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:
- Source
.texfile — the new master CV structure - Compile command — overriding the default
lualatexinvocation - 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:
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— Template rules, language handling, and compile commands.claude/commands/apply.md— Complete/applyworkflow specification including ATS verificationdocuments/README.md— CV folder structure and the role ofcv_draft.texfor manual experimentationSETUP.md— LaTeX installation requirements and verification commands
Summary
- CV generation is triggered by the
/applycommand, creatingcv/main_<company>_<role>.texfrom job metadata - The template uses moderncv's banking style with blue accents, compiled via
lualatex - The master source resides in
cv/main_example.texand 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →