How a CV Is Generated and What Template Is Used in ai-job-search

The ai-job-search repository automatically generates tailored CVs using the moderncv LaTeX package with the banking style, compiling each customized document with lualatex during the /apply workflow.

Every job application in the MadsLorentzen/ai-job-search project produces a unique, ATS-optimized PDF résumé derived from a master LaTeX source. The system leverages Claude's agent capabilities to rewrite and compile CVs on demand, ensuring each submission matches the target role while maintaining consistent visual formatting. This article explains the complete generation pipeline, the template architecture, and how to customize the output.

The CV Generation Pipeline

The /apply command orchestrates a four-step process defined in .claude/commands/apply.md. When you accept a job posting, the agent executes this workflow automatically.

Step 1: Derive the Output Filename

The system constructs a unique identifier from the job metadata:


cv/main_<company>_<role>.tex

For example, applying to "Acme Corp" for a "Data Engineer" position generates cv/main_Acme_DataEngineer.tex. This naming convention prevents collisions and enables easy retrieval of historical applications.

Step 2: Copy and Tailor the Master CV

The agent reads cv/main_example.tex — the master CV containing your complete professional history — then trims and rewrites sections for relevance. The tailoring logic prioritizes experience bullets that match keywords extracted from the job description.

Step 3: Compile with LuaLaTeX

The generated .tex file is compiled using:

lualatex -interaction=nonstopmode main_<company>_<role>.tex

The -interaction=nonstopmode flag ensures compilation completes without halting on minor warnings, which is essential for automated batch processing.

Step 4: Verify ATS Compatibility

After PDF generation, the workflow extracts the text layer using pypdf or pdftotext to validate:

  • Contact detail integrity (email, phone, LinkedIn URL reachable)
  • Keyword coverage against the original job posting

Failed verifications trigger regeneration with adjusted content density.

The moderncv Banking Template

All CVs default to the stock moderncv package with specific styling parameters locked in cv/main_example.tex and documented in .claude/skills/job-application-assistant/05-cv-templates.md.

Template Configuration

The master CV begins with these document class options:

\documentclass[11pt,a4paper,banking]{moderncv}
\moderncvstyle{banking}
\moderncvcolor{blue}
Parameter Value Purpose
Engine LuaLaTeX Modern Unicode and font handling
Style banking Horizontal section rules, professional compact layout
Color blue Subtle accent for links and section headers
Paper A4 European standard (adjustable)

Minimal Working Example

Extracted from cv/main_example.tex, this skeleton shows the required structure:

\usepackage[utf8]{inputenc}
\name{[YOUR_NAME]}{[YOUR_SURNAME]}
\address{[YOUR_ADDRESS]}{}{[YOUR_COUNTRY]}
\phone[mobile]{[YOUR_PHONE]}
\email{[YOUR_EMAIL]}
\social[linkedin]{[YOUR_LINKEDIN_URL]}

\begin{document}
\makecvtitle

\section{Experience}
% Tailored entries inserted here

\section{Education}
% Static or tailored entries

\section{Skills}
% Keyword-optimized for target role
\end{document}

Placeholders in brackets are replaced during generation; the agent preserves LaTeX syntax while injecting role-specific content.

Programmatic Generation Pattern

The agent's internal logic follows this Pythonic flow, substituting shell commands where appropriate:

import pathlib
import subprocess

def generate_cv(company: str, role: str, master_path: str = "cv/main_example.tex") -> pathlib.Path:
    """
    Generate a tailored CV from the master template.
    """
    source = pathlib.Path(master_path)
    destination = pathlib.Path(f"cv/main_{company}_{role}.tex")
    
    # Read master and apply tailoring logic (simplified)

    master_content = source.read_text(encoding="utf-8")
    tailored_content = tailor_for_role(master_content, company, role)
    destination.write_text(tailored_content, encoding="utf-8")
    
    # Compile with lualatex

    subprocess.run(
        ["lualatex", "-interaction=nonstopmode", destination.name],
        cwd=destination.parent,
        check=True,
        capture_output=True,
    )
    
    return destination.with_suffix(".pdf")


def tailor_for_role(content: str, company: str, role: str) -> str:
    """
    Rewrites experience sections for relevance.
    Actual implementation uses LLM-based section scoring.
    """
    # Placeholder for relevance-based trimming

    return content.replace("[COMPANY_NAME]", company).replace("[ROLE_NAME]", role)

Custom Template Registration

While the moderncv banking template is the default, the system supports alternative designs via the /add-template command. Custom templates must specify:

  1. Source .tex file — the new master CV structure
  2. Compile command — overriding the default lualatex invocation
  3. Validation rules — ATS checks specific to the template's text layout

If no active template is registered, the agent falls back to the moderncv banking configuration defined in 05-cv-templates.md.

Key Source Files

Understanding these paths enables deep customization:

Summary

  • CV generation is triggered by the /apply command, creating cv/main_<company>_<role>.tex from job metadata
  • The template uses moderncv's banking style with blue accents, compiled via lualatex
  • The master source resides in cv/main_example.tex and serves as the foundation for all tailored versions
  • ATS verification extracts PDF text to validate contact details and keyword coverage
  • Custom templates can replace the default via /add-template, falling back to moderncv if none specified

Frequently Asked Questions

What LaTeX engine is required for CV compilation?

LuaLaTeX is mandatory. The compile command lualatex -interaction=nonstopmode is hardcoded in .claude/commands/apply.md and 05-cv-templates.md. This engine was chosen for superior Unicode support and modern font handling compared to pdfLaTeX or XeLaTeX.

Can I modify the default template styling?

Yes, by editing cv/main_example.tex directly. Changes to \moderncvstyle or \moderncvcolor propagate to all generated CVs. For persistent alternatives, register a custom template with /add-template pointing to your modified .tex master file.

How does the system handle company names with spaces or special characters?

File naming sanitizes inputs. The agent transforms "Some Company LLC!" into a safe filename like main_SomeCompanyLLC_RoleName.tex. Original punctuation is preserved in the document content but stripped from paths to prevent shell injection and filesystem errors.

Is the generated PDF editable after compilation?

No — regenerate instead. The workflow treats PDFs as build artifacts. To revise a submitted CV, re-run /apply for the same posting; the agent overwrites the previous .tex and PDF with updated tailoring.

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 →