How the AI-Job-Search Framework Generates Tailored CVs and Cover Letters

The framework generates tailored CVs and cover letters through an 8-step deterministic workflow orchestrated by the /apply command, using template-driven generation, automated PDF verification, and relevance-weighted content pruning to produce ATS-friendly documents grounded in a single source of truth.

The MadsLorentzen/ai-job-search repository provides a deterministic system for generating job application materials. It treats candidate data as immutable facts while dynamically assembling reusable content blocks to match specific job postings. This approach ensures factual consistency across applications while maximizing relevance to each target role.

The 8-Step Deterministic Workflow

The generation process follows a strict pipeline defined in .claude/commands/apply.md, triggered when a user invokes /apply <URL> or pastes a job description.

1. Job Posting Ingestion

The system extracts required skills, language requirements, deadlines, and company identifiers from the provided URL or text. This raw posting data drives all subsequent tailoring decisions and template selection.

2. Grounding Audit

The framework enforces a single source of truth consisting of three canonical files: 01-candidate-profile.md, cv/main_example.tex, and the Candidate Profile section in CLAUDE.md. Line 77 of apply.md explicitly prohibits introducing new facts not present in these sources; every bullet inserted into a draft must match the master candidate profile exactly.

3. Template Block Selection

Pre-written profile-statement templates from .claude/skills/job-application-assistant/05-cv-templates.md and cover-letter templates from 06-cover-letter-templates.md contain reusable phrasing blocks. The system selects blocks based on keyword matching against the job posting and internal relevance scoring algorithms.

4. LaTeX Draft Generation

The framework fills placeholders to generate two concrete files:

  • cv/main_<company>_<role>.tex (using the moderncv format)
  • cover_letters/cover_<company>_<role>.tex (using the custom cover.cls class)

These files embed selected statements, adapt bullet ordering, and respect strict page budgets (two pages for CVs, one for cover letters).

5. PDF Compilation

The CV compiles via lualatex while cover letters require xelatex. If compilation fails, the system automatically rewrites the LaTeX source—inserting spacing commands like \needspace or \enlargethispage—and retries until successful.

6. PDF Verification Loop

The tools/verify_pdf.py script examines each PDF for layout issues (orphans, font mismatches) and ATS parseability using the pypdf library (falling back to pdftotext). If the ATS check fails, the framework either applies LaTeX fixes or degrades to manual keyword review.


# tools/verify_pdf.py implementation

from verify_pdf import verify_pdf

pdf_path = 'cv/main_acme_software_engineer.pdf'
ok, issues = verify_pdf(pdf_path)

if not ok:
    # Apply LaTeX fixes and recompile

    fix_latex_and_recompile(pdf_path)

7. Relevance-Weighted Cutting

When a CV exceeds the two-page limit, the system scores every line using relevance to posting keywords, content uniqueness, and dependency on the cover letter narrative. The lowest-scoring lines are pruned first, preserving the most impactful experience rather than simply cutting oldest entries.


# Logic excerpt from apply.md step 7

lines = extract_lines('cv/main_example.tex')
scores = {
    line: relevance_score(line, posting) + uniqueness_score(line)
    for line in lines
}

while pdf_page_count(pdf_path) > 2:
    line_to_drop = min(scores, key=scores.get)
    remove_line(line_to_drop)
    recompile_and_verify()

8. Output and Archiving

Final PDFs are delivered to the user. Simultaneously, the framework archives the job posting to documents/applications/<company>_<role>/job_posting.md and appends the application record to job_search_tracker.csv.

Template-Driven Content Architecture

The framework separates immutable facts from flexible phrasing. While candidate data remains static in 01-candidate-profile.md, reusable language blocks reside in dedicated template files.

CV Templates

File: .claude/skills/job-application-assistant/05-cv-templates.md

These templates store achievement statements and skill descriptions as modular blocks. During generation, the system injects these phrases into the base LaTeX template at cv/main_example.tex, ensuring consistent professional tone while allowing role-specific customization.

Cover Letter Templates

File: .claude/skills/job-application-assistant/06-cover-letter-templates.md

This file contains structural templates for salutations, opening hooks, value propositions, and closings. The framework assembles these components based on company culture signals detected in the job posting, generating unique narratives without inventing new biographical facts.

Automated PDF Verification and ATS Optimization

The verification pipeline ensures machine readability before final delivery. The verify_pdf.py tool checks text extraction integrity, visual layout consistency, and file metadata integrity.

Key verification criteria include:

  • ATS parseability: Confirming that pypdf or pdftotext can extract readable text without encoding errors
  • Visual layout: Detecting orphans, improper spacing, or font embedding issues
  • Compilation integrity: Verifying that forced recompilation did not corrupt the PDF structure

Summary

  • The /apply command triggers an 8-step deterministic workflow defined in .claude/commands/apply.md to generate tailored CVs and cover letters for each job posting.
  • Single source of truth ensures all factual claims come from 01-candidate-profile.md and related master files, preventing hallucinated qualifications.
  • Template-driven generation uses modular content blocks from 05-cv-templates.md and 06-cover-letter-templates.md to maintain consistent phrasing across applications.
  • Automated verification via tools/verify_pdf.py guarantees ATS parseability and visual correctness through lualatex and xelatex compilation loops.
  • Relevance-weighted cutting optimizes two-page CV constraints by scoring content against job posting keywords rather than using date-based truncation.

Frequently Asked Questions

How does the framework ensure factual accuracy in generated documents?

The system enforces a grounding audit against three canonical sources: 01-candidate-profile.md, cv/main_example.tex, and the Candidate Profile section in CLAUDE.md. Per line 77 of .claude/commands/apply.md, the framework prohibits introducing any facts not present in these master files, ensuring tailored drafts match the verified candidate history exactly.

What happens if the generated PDF fails ATS parsing?

If tools/verify_pdf.py detects ATS parseability issues using pypdf (or pdftotext as fallback), the framework enters a repair loop. It either applies LaTeX fixes—such as adjusting spacing commands or font packages—or degrades to manual keyword review. The system retries compilation until the PDF passes both visual layout checks and text extraction validation.

Can the framework handle different compilers for CVs and cover letters?

Yes. The framework specifically compiles CVs using lualatex for moderncv compatibility, while cover letters require xelatex to support the custom cover.cls class defined in cover_letters/cover.cls. The orchestration logic in apply.md automatically selects the correct compiler for each document type and handles cross-platform compilation errors through automated LaTeX rewriting.

How does relevance-weighted cutting differ from traditional resume truncation?

Traditional truncation removes oldest entries chronologically. The ai-job-search framework instead scores every line by relevance to the job posting's keywords, content uniqueness, and dependency on the cover letter narrative. This relevance-weighted algorithm prunes the lowest-scoring lines first, ensuring the most impactful, role-specific experience remains visible within the two-page CV constraint.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →