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

> Discover how ai-job-search automatically generates CVs using the moderncv LaTeX package and lualatex. Learn about the banking style template and the `/apply` workflow.

- Repository: [Mads Lorentzen/ai-job-search](https://github.com/MadsLorentzen/ai-job-search)
- Tags: how-to-guide
- Published: 2026-08-30

---

**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`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.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:

```bash
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`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/skills/job-application-assistant/05-cv-templates.md).

### Template Configuration

The master CV begins with these document class options:

```latex
\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:

```latex
\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:

```python
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`](https://github.com/MadsLorentzen/ai-job-search/blob/main/05-cv-templates.md).

## Key Source Files

Understanding these paths enables deep customization:

- **`cv/main_example.tex`** — Master CV source; edit this to change your baseline content and styling
- **[`.claude/skills/job-application-assistant/05-cv-templates.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/skills/job-application-assistant/05-cv-templates.md)** — Template rules, language handling, and compile commands
- **[`.claude/commands/apply.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/apply.md)** — Complete `/apply` workflow specification including ATS verification
- **[`documents/README.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/documents/README.md)** — CV folder structure and the role of `cv_draft.tex` for manual experimentation
- **[`SETUP.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SETUP.md)** — LaTeX installation requirements and verification commands

## 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`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/apply.md) and [`05-cv-templates.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/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.