How the AI-Job-Search Framework Compiles LaTeX PDFs for Job Applications

The AI-Job-Search framework compiles LaTeX PDFs by invoking LuaLaTeX for CVs and XeLaTeX for cover letters through shell commands wrapped in Python subprocess calls, ensuring reliable document generation both locally and in CI/CD pipelines.

The MadsLorentzen/ai-job-search repository automates job application workflows, including the generation of professional PDF documents from LaTeX templates. To compile LaTeX PDFs for job applications, the framework relies on direct TeX engine execution rather than Python LaTeX libraries, delegating rendering to locally installed distributions like TeX Live, MacTeX, TinyTeX, or MiKTeX.

Default TeX Engine Configuration

The framework assigns specific engines to different document types to optimize typographic output. According to SETUP.md and reinforced in README.md, the defaults are:

  • CV compilation: Uses lualatex for modern font handling and Lua scripting capabilities
  • Cover letter compilation: Uses xelatex for system font integration

Both engines are invoked with strict error handling flags: -interaction=nonstopmode -halt-on-error. This configuration ensures that compilation failures terminate immediately rather than hanging on interactive prompts, which is critical for automated workflows.

The Compilation Workflow

Source File Organization

Template files follow a predictable naming convention within dedicated directories. The repository includes example templates at cv/main_example.tex and cover_letters/cover_example.tex. During the application process, files are generated following the pattern main_<company>_<role>.tex for CVs and cover_<company>_<role>.tex for cover letters, placed in their respective cv/ and cover_letters/ directories.

Command Execution via the apply Command

The compilation is orchestrated by the .claude/commands/apply.md command implementation. The process changes into the appropriate directory and executes the compile command directly:


# CV compilation pattern

cd cv && lualatex -interaction=nonstopmode -halt-on-error main_<company>_<role>.tex

# Cover letter compilation pattern  

cd cover_letters && xelatex -interaction=nonstopmode -halt-on-error cover_<company>_<role>.tex

Python Subprocess Implementation

The framework wraps shell execution in a run_tool function that encapsulates subprocess.run. The compilation logic follows this pattern as implemented in the apply workflow:

def compile_tex(dir_path: Path, tex_file: str, engine: str) -> None:
    """
    Compile a LaTeX file using the specified engine.
    
    Args:
        dir_path: Either cv/ or cover_letters/
        tex_file: e.g., "main_example.tex" or "cover_example.tex"
        engine: "lualatex" or "xelatex"
    """
    cmd = [
        engine,
        "-interaction=nonstopmode",
        "-halt-on-error",
        tex_file,
    ]
    run_tool(cmd, cwd=dir_path)  # run_tool wraps subprocess.run

This implementation ensures that working directories are isolated (preventing auxiliary file conflicts) and that exit codes propagate correctly to the calling process.

CI/CD Verification Pipeline

The .github/workflows/ci.yml workflow file automates PDF generation verification on every push. The CI pipeline executes the same lualatex and xelatex commands used in local development, capturing LaTeX errors and failing the job if compilation does not succeed. This ensures that template changes or dependency updates do not break PDF generation before they reach production use.

Customizing Compilation Commands

Custom templates can override default engines through the .claude/commands/add-template.md command structure. When adding a template via /add-template, the configuration can specify:

  • Source file extension: Usually .tex
  • Custom compile command: Alternative engine or additional flags

This extensibility allows users to invoke pdflatex or add custom preprocessing steps while maintaining the same run_tool execution wrapper and directory management patterns.

Summary

  • The framework uses engine-specific compilation: lualatex for CVs and xelatex for cover letters
  • Shell command execution via run_tool wrappers ensures compatibility with any standard TeX distribution
  • Strict error handling through -halt-on-error flags prevents automation deadlocks
  • CI verification in .github/workflows/ci.yml guarantees build reliability across environments
  • Template customization via .claude/commands/add-template.md supports alternate compile workflows without modifying core Python code

Frequently Asked Questions

What TeX distributions are compatible with the framework?

The framework requires any standard distribution that provides lualatex and xelatex executables in the system PATH. Supported options include TeX Live, MacTeX, TinyTeX, and MiKTeX. The SETUP.md file documents specific installation requirements and PATH configuration for each platform.

Can I use pdfLaTeX instead of LuaLaTeX or XeLaTeX?

Yes, though it requires template customization. When adding a custom template via .claude/commands/add-template.md, you can specify pdflatex as the compile command or modify the engine parameter in the compilation logic. The run_tool wrapper is engine-agnostic and will execute any valid TeX command provided.

How does the framework handle compilation errors?

The -interaction=nonstopmode -halt-on-error flags ensure that LaTeX stops immediately upon encountering errors rather than prompting for user input. When subprocess.run (wrapped by run_tool) detects a non-zero exit code, the error propagates up the call stack, causing the apply command to fail visibly. This behavior is mirrored in the CI pipeline, where LaTeX errors result in immediate job failure.

Where are the generated PDF files stored?

PDFs are generated in the same directories as their source .tex files—specifically within cv/ for resumes and cover_letters/ for application letters. The files share the base name of their source (e.g., main_example.pdf from main_example.tex) and are subsequently processed by downstream tools such as tools/verify_pdf.py for ATS-readability verification.

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 →