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

> Learn how the AI-Job-Search framework compiles LaTeX PDFs using LuaLaTeX and XeLaTeX via Python subprocesses for robust CV and cover letter generation in local and CI/CD environments.

- Repository: [Mads Lorentzen/ai-job-search](https://github.com/MadsLorentzen/ai-job-search)
- Tags: internals
- Published: 2026-08-26

---

**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`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SETUP.md) and reinforced in [`README.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/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`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/apply.md) command implementation. The process changes into the appropriate directory and executes the compile command directly:

```bash

# 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:

```python
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`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.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`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.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`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.github/workflows/ci.yml) guarantees build reliability across environments
- **Template customization** via [`.claude/commands/add-template.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.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`](https://github.com/MadsLorentzen/ai-job-search/blob/main/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`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.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`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tools/verify_pdf.py) for ATS-readability verification.