# How the Cover Letter Generator Creates Keyword-Mirrored Letters Using Four Angle Prompts

> Learn how the Career Ops cover letter generator uses four prompts to mirror keywords from job descriptions into your ATS friendly letter. Get your perfect application today.

- Repository: [Santiago Fernández de Valderrama/career-ops](https://github.com/santifer/career-ops)
- Tags: how-to-guide
- Published: 2026-06-12

---

**The Career-Ops cover letter generator extracts 8–10 exact phrases from a job description and weaves them into a candidate-specific letter through four structured angle prompts, ensuring every mirrored keyword appears exactly once in the final ATS-friendly PDF.**

The `santifer/career-ops` repository provides a CLI-driven pipeline that automates the creation of highly personalized cover letters. Its **cover letter generator** uses a two-stage workflow defined in [[`modes/cover.md`](https://github.com/santifer/career-ops/blob/main/modes/cover.md)](https://github.com/santifer/career-ops/blob/main/modes/cover.md): first it parses a job description for **mirrored vocabulary**, then it forces the user to supply four narrative angles before any text is drafted. This design guarantees that the output reflects the employer’s exact wording while remaining genuinely candidate-driven.

## JD Parsing and Keyword Mirroring

When a user runs the `career-ops cover` command, the generator executes **Step 4 – Keyword extraction** from [`modes/cover.md`](https://github.com/santifer/career-ops/blob/main/modes/cover.md). The script scans the pasted job description and isolates 8–10 exact phrases the employer uses.

### ATS-Critical vs. Human Trust Signals

The extracted keywords are split into two distinct groups. **ATS-critical** keywords include titles, tools, and methodologies that applicant-tracking systems scan for. **Human trust signals** consist of verbs, domain nouns, outcome language, and team framing that resonate with hiring managers.

### The Once-Only Placement Rule

The generator enforces the rule "Mirror their vocabulary, not their structure" as defined in lines 12–18 of [`modes/cover.md`](https://github.com/santifer/career-ops/blob/main/modes/cover.md). Every confirmed keyword may be used **once** and only in one of four sections: the opening, profile intro, achievements, or problems sections. This prevents mechanical repetition while preserving natural flow.

## The Four Mandatory Angle Prompts

After keyword confirmation, **Step 6 – Four prompts** in [`modes/cover.md`](https://github.com/santifer/career-ops/blob/main/modes/cover.md) collects mandatory narrative input. The generator refuses to draft any letter until all four angles are provided.

### Prompt A – Why This Role or Company?

This angle captures the candidate’s motivation. It might reference a funding round, company mission, or market position that explains why the applicant is interested.

### Prompt B – What Problem Would You Solve?

This prompt converts the research synthesis from **Step 3** into a concrete problem statement. It identifies a specific business or technical challenge the candidate would address.

### Prompt C – How Would You Approach It?

The most differentiated angle, this prompt requires a day-one action plan. The candidate must outline concrete steps they would take to solve the problem identified in Prompt B.

### Prompt D – Tone Selection

This final prompt sets the voice for the entire letter. Options include formal, direct, conversational, or mirror JD, ensuring the tone aligns with the employer’s communication style.

## Draft Assembly and PDF Rendering

Once the four angles are locked, the generator moves to **Step 9** and merges all collected data into a single payload.

### Merging Data into a JSON Payload

The system combines the candidate profile from [`config/profile.yml`](https://github.com/santifer/career-ops/blob/main/config/profile.yml), achievements from [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md), the confirmed keyword lists, and the four angle responses into a JSON object. This payload is passed to the rendering engine.

### HTML Template Substitution

The script `generate-cover-letter.mjs` reads [[`templates/cover-letter-template.html`](https://github.com/santifer/career-ops/blob/main/templates/cover-letter-template.html)](https://github.com/santifer/career-ops/blob/main/templates/cover-letter-template.html) and substitutes placeholders such as `{{NAME}}` and `{{ACHIEVEMENTS_BLOCK}}` with the payload values. The keywords are injected into their designated textual slots according to the once-only placement rule.

### Playwright PDF Conversion

The rendered HTML is handed to [`generate-pdf.mjs`](https://github.com/santifer/career-ops/blob/main/generate-pdf.mjs), which uses Playwright to generate an A4 PDF. This is the same rendering pipeline used for CVs, ensuring visual consistency across application documents.

## Post-Generation Verification

After PDF creation, **Step 10** logs any keywords that could not be incorporated into the draft. It also records any gap acknowledgments the user chose to include. This audit trail guarantees traceability and lets the user verify that the letter truly mirrors the original job description.

## Running the Generator

The CLI supports two invocation modes. You can generate from a previously evaluated job using its slug, or paste a fresh job description interactively.

```bash

# From a previously evaluated job (slug mode)

career-ops cover my‑company‑2024‑03‑15

# From a newly pasted JD (paste mode)

career-ops cover

```

The following JSON payload illustrates the data structure fed into the rendering script:

```json
{
  "candidate": {
    "name": "Alice Example",
    "email": "alice@example.com",
    "phone": "+1‑555‑123‑4567",
    "location": "Seattle, WA",
    "linkedin": "https://linkedin.com/in/aliceexample",
    "github": "https://github.com/aliceexample",
    "credentials": ["M.S. Computer Science", "AWS Certified Solutions Architect"]
  },
  "letter": {
    "role_title": "Senior Backend Engineer",
    "company": "FinTechCo",
    "city": "San Francisco",
    "date": "2026‑06‑12",
    "opening": "FinTechCo’s recent Series B round signals an ambitious path to reshape payments.",
    "profile_intro": "With 8 years building scalable APIs, I’ve led migration projects that cut latency by 70 %.",
    "achievements": [
      {"lead":"Reduced order‑processing latency","impact":"from 2 s to 350 ms, saving $1.2 M annually"},
      {"lead":"Built a fault‑tolerant micro‑service platform","impact":"supporting 1.5 M daily transactions"}
    ],
    "problems_section": "I will modernise your transaction pipeline to cut latency and improve reliability.",
    "closing": "I am available to start immediately and look forward to discussing how I can contribute.",
    "language_closing": null
  },
  "output_path": "output/fintechco-senior-backend-engineer-cover.pdf"
}

```

Invoke the rendering script with the payload path:

```bash
node generate-cover-letter.mjs --payload /tmp/cover-payload-fintechco.json

```

## Summary

- The Career-Ops cover letter generator in `santifer/career-ops` extracts 8–10 exact phrases from a job description and splits them into ATS-critical and human trust signal categories.
- Keywords are placed exactly once across four sections according to the once-only rule defined in [`modes/cover.md`](https://github.com/santifer/career-ops/blob/main/modes/cover.md).
- Four mandatory angle prompts—motivation, problem, approach, and tone—must be answered before any drafting occurs.
- The rendering pipeline in `generate-cover-letter.mjs` and `generate-pdf.mjs` produces an A4 PDF via Playwright, with post-generation logging in **Step 10** to verify keyword coverage.

## Frequently Asked Questions

### What is the Career-Ops cover letter generator?

The Career-Ops cover letter generator is a CLI tool in the `santifer/career-ops` repository that automates the creation of personalized cover letters. It mirrors exact vocabulary from a job description while forcing the candidate to supply four unique narrative angles, ensuring the output is both ATS-optimized and genuinely custom.

### How does keyword mirroring work without copying the employer's structure?

The system enforces the rule "Mirror their vocabulary, not their structure" in [`modes/cover.md`](https://github.com/santifer/career-ops/blob/main/modes/cover.md). It extracts 8–10 exact phrases from the job description but scatters each keyword only once into candidate-driven sections such as the opening, profile intro, achievements, and problems sections.

### What are the four angle prompts?

The four mandatory angle prompts are defined in **Step 6** of [`modes/cover.md`](https://github.com/santifer/career-ops/blob/main/modes/cover.md): **A** asks why the candidate wants the role or company, **B** identifies a specific problem to solve, **C** outlines a concrete day-one approach, and **D** sets the letter's tone. All four must be completed before the generator assembles the draft.

### How is the final cover letter rendered to PDF?

After the JSON payload is assembled in **Step 9**, `generate-cover-letter.mjs` injects the data into [[`templates/cover-letter-template.html`](https://github.com/santifer/career-ops/blob/main/templates/cover-letter-template.html)](https://github.com/santifer/career-ops/blob/main/templates/cover-letter-template.html). The resulting HTML is then passed to [`generate-pdf.mjs`](https://github.com/santifer/career-ops/blob/main/generate-pdf.mjs), which uses Playwright to render an A4 PDF. Unused keywords are logged in **Step 10** for verification.