LaTeX Documents skill for Claude Code

Clean builds, resolved citations, no ?? in the PDF. Claude sets up LaTeX projects that build with one latexmk command: a sane preamble with packages in the right order, biblatex with biber, booktabs tables, siunitx units, and cleveref cross-references. After each build a bundled log parser lists errors by line, undefined references and citations, and wide overfull boxes, so fixes target causes instead of rerunning and hoping.

  • namelatex-documents
  • size211 lines · ≈2,100 tokens
  • files2
  • licenseMIT
  • updated

What Claude sees before loading it

This description is the only part of the skill that sits in context all the time. Claude reads it to decide when to load the full playbook below.

description: Writes and builds LaTeX papers, theses, and reports that compile cleanly — project structure, engine choice (pdfLaTeX vs LuaLaTeX), latexmk builds with .latexmkrc, biblatex with biber bibliographies, figures and booktabs tables, cross-references with cleveref, siunitx units, and reading the log to fix errors, undefined references, and overfull boxes. Use when the user works with .tex or .bib files, asks for a LaTeX paper or report template, hits a compile error, has citations showing as question marks, or needs a journal or university class configured.

Try asking

  • Set up a LaTeX paper template with biblatex, figures and cleveref
  • My citations show up as question marks — fix the bibliography build
  • Clean up the overfull hbox warnings in my thesis before submission

Use it when

  • Starting a LaTeX paper, report, or thesis
  • A .tex build fails or citations and references show ?? or bold keys
  • Configuring latexmk, biber, or a journal class
  • Polishing tables, figures, and layout warnings before submission

Skip it when

How to check the result

A clean latexmk build from scratch exits zero and the log summary shows no errors, no undefined references or citations, and no overfull boxes above the agreed width.

The common mistake it prevents

Placing the label before the caption in a figure or table, so every reference silently points at the section number instead of the figure.

~/.claude/skills/latex-documents/SKILL.md

--- name: latex-documents description: Writes and builds LaTeX papers, theses, and reports that compile cleanly — project structure, engine choice (pdfLaTeX vs LuaLaTeX), latexmk… license: MIT ---

Done well means: the document builds from a clean checkout with one command, the log has no errors, no undefined references or citations, and no overfull boxes worth fixing; structure lives in sectioning and labels rather than manual numbering; and you read the log rather than guessing from the PDF.

Toolchain

NeedChoice
Default engine for new documentsLuaLaTeX (Unicode, system/OpenType fonts via fontspec)
Journal/arXiv template requires it, or fastest buildspdfLaTeX
Build orchestrationlatexmk (reruns LaTeX/biber until references settle)
Bibliographybiblatex + biber (unless the journal class mandates BibTeX .bst styles)
Single-binary alternativeTectonic (auto-downloads packages; good for CI)
DistributionTeX Live 2025+ (full scheme avoids missing packages) or MacTeX; texlive/texlive Docker image in CI

Check what is installed: latexmk -v, lualatex --version, biber --version, kpsewhich biblatex.sty (prints a path if the package exists).

Project layout

paper/
├── main.tex
├── .latexmkrc
├── refs.bib
├── sections/  intro.tex  method.tex  results.tex
├── figures/   architecture.pdf  latency.pdf
└── build/     (generated; gitignored)

.gitignore: build/, *.aux, *.log, *.bbl, *.bcf, *.blg, *.fdb_latexmk, *.fls, *.out, *.run.xml, *.synctex.gz, *.toc.

Minimal dependable preamble (LuaLaTeX)

\documentclass[11pt,a4paper]{article}
\usepackage{fontspec}              % LuaLaTeX/XeLaTeX only; pdfLaTeX: \usepackage[T1]{fontenc}
\usepackage[english]{babel}
\usepackage{microtype}
\usepackage{amsmath,amssymb}
\usepackage{graphicx}
\usepackage{booktabs}
\usepackage{siunitx}
\usepackage[style=authoryear,backend=biber,maxcitenames=2]{biblatex}
\addbibresource{refs.bib}
\usepackage[hidelinks]{hyperref}   % load late
\usepackage[capitalise,noabbrev]{cleveref}   % after hyperref
\graphicspath{{figures/}}

\title{Reducing Tail Latency in Queue-Backed APIs}
\author{Jane Doe}
\date{\today}

\begin{document}
\maketitle
\begin{abstract}
...
\end{abstract}
\input{sections/intro}
\input{sections/method}
\printbibliography
\end{document}

Package order rules: hyperref near the end; cleveref after hyperref; fontspec never with pdfLaTeX; do not load inputenc on LuaLaTeX (UTF-8 is native; pdfLaTeX has defaulted to UTF-8 since 2018 too).

Build with latexmk

.latexmkrc:

$pdf_mode = 4;            # 1 = pdflatex, 4 = lualatex, 5 = xelatex
$out_dir = 'build';
$bibtex_use = 2;          # run biber/bibtex as needed; delete .bbl on clean
@default_files = ('main.tex');
set_tex_cmds('-interaction=nonstopmode -halt-on-error -file-line-error -synctex=1 %O %S');
latexmk                 # build using .latexmkrc
latexmk -pvc            # rebuild on save (preview continuously)
latexmk -c              # remove auxiliary files, keep PDF
latexmk -C              # remove everything generated, including PDF
latexmk -lualatex -interaction=nonstopmode -halt-on-error main.tex   # without an rc file

After every build, summarize the log:

python3 scripts/latex_log_summary.py build/main.log
python3 scripts/latex_log_summary.py build/main.log --boxes 5

Standard library only. It lists errors with file:line, missing files, undefined references and citations, overfull boxes above a width threshold, badness-10000 underfull boxes, font and missing-character warnings, and whether a rerun is pending. Exit code 1 when errors, missing files, or undefined refs/citations remain.

Bibliography (biblatex + biber)

@article{dean2013tail,
  author  = {Dean, Jeffrey and Barroso, Luiz Andr{\'e}},
  title   = {The Tail at Scale},
  journal = {Communications of the ACM},
  volume  = {56}, number = {2}, pages = {74--80}, year = {2013},
  doi     = {10.1145/2408776.2408794}
}
  • Cite with \textcite{dean2013tail} (narrative) and \parencite{...} (parenthetical); \autocite follows the style’s default.
  • Styles: authoryear, numeric-comp, ieee (via biblatex-ieee), apa (via biblatex-apa, requires \DeclareLanguageMapping only on old versions).
  • Protect capitalization in titles with braces: {GPU}, {Kubernetes}.
  • Use -- for page ranges, full author names in Last, First and Last, First form.
  • Citations print as bold keys or [?]: biber did not run or failed. Check build/main.blg, confirm backend=biber, and that keys match exactly (case-sensitive).
  • Never fabricate references. If the user asks for citations, use only sources they provide or that you can verify, and flag any entry you could not confirm.

Figures and tables

\begin{figure}[tbp]
  \centering
  \includegraphics[width=0.8\linewidth]{latency}
  \caption{p99 latency before and after admission control.}
  \label{fig:latency}
\end{figure}

\begin{table}[tbp]
  \centering
  \caption{Latency by configuration.}
  \label{tab:latency}
  \begin{tabular}{l S[table-format=3.1] S[table-format=3.1]}
    \toprule
    Configuration & {p50 (\unit{\milli\second})} & {p99 (\unit{\milli\second})} \\
    \midrule
    Baseline      & 12.4 & 310.0 \\
    Admission     & 11.9 &  84.2 \\
    \bottomrule
  \end{tabular}
\end{table}
  • \label after \caption, or references point at the section number.
  • Vector figures as PDF (matplotlib savefig("x.pdf")), photos as PNG/JPEG. Omit the extension in \includegraphics so the engine picks the best available.
  • Refer with \cref{fig:latency} → “Figure 3”; never hard-code numbers.
  • No vertical rules in tables; booktabs rules only. siunitx S columns align decimals; wrap non-numeric header cells in braces.
  • Units: \qty{3.2}{\milli\second}, \num{12345} (siunitx v3 syntax; \SI is the old form).
  • Floats drifting far away: use [tbp], reduce float size, or \FloatBarrier (placeins); avoid [H] except for short documents.

Fixing common errors

Log messageUsual cause and fix
Undefined control sequence at l.NTypo or missing package for that command; check the line shown
File 'x.sty' not foundPackage not installed: tlmgr install x (TeX Live) or install the full scheme
Missing $ insertedMath character (_, ^, &) in text; escape as \_ or wrap in $...$
Misplaced alignment tab character && in text; use \&
Runaway argument / File ended while scanningUnbalanced braces; check the start of the reported paragraph
There's no line here to end\\ in vertical mode (after a blank line); remove it, use paragraph spacing
Option clash for package xLoaded twice with different options; load once, or pass via \PassOptionsToPackage before the class
Missing character: There is no ü in fontpdfLaTeX without T1 fontenc, or font lacks glyph; switch to LuaLaTeX with a Unicode font
Citations [?] / references ??Rerun needed or biber failed; run latexmk, read .blg

-halt-on-error stops at the first error, which is the one to fix; later errors are often consequences.

Overfull boxes

Fix ones wider than ~1–2 pt in final output. Options in order: let microtype work; rephrase; allow hyphenation of a long word (\hyphenation{data-base}); break URLs with \url{} (hyperref) or xurl; for code use listings/minted with breaklines. Do not blanket-set \sloppy for the whole document.

Gotchas

  • Journal classes (IEEEtran, acmart, elsarticle, llncs) dictate bibliography tooling and fonts; follow their templates instead of the preamble above, and do not add geometry or font changes they forbid.
  • arXiv uses pdfLaTeX by default and requires the .bbl for BibTeX; for biblatex, include the .bbl produced by a matching biber version or switch to BibTeX for submission.
  • Spaces after macros: \LaTeX is swallows the space; write \LaTeX{} is.
  • Quotes: use ``text'' or \enquote{} (csquotes), never straight ".
  • Non-breaking space before references and citations: Figure~\ref{...} (cleveref handles this for you).

Verification

  • latexmk -C && latexmk builds from scratch with exit code 0.
  • scripts/latex_log_summary.py reports zero errors, zero undefined references and citations, and no overfull boxes above the agreed threshold.
  • Spot-check the PDF: table of contents, figure/table numbering, bibliography entries, and links resolve.

Bundled files

Loaded only when a step in the skill needs them. Review them here before installing.

scripts/latex_log_summary.py 103 lines · 4.5 KB
#!/usr/bin/env python3
"""Summarize a LaTeX .log file: errors with line numbers, undefined references and
citations, missing files, overfull/underfull boxes, font and package warnings, and
whether a rerun is still needed.

Standard library only (Python 3.10+).
Usage:
  python3 latex_log_summary.py build/main.log
  python3 latex_log_summary.py build/main.log --boxes 5     # report overfull boxes wider than 5pt

Exit codes: 0 = no errors and no undefined refs/citations, 1 = problems found, 2 = cannot read log.
"""
import argparse
import re
import sys

ERR = re.compile(r"^(?:! (?P<msg>.+)|(?P<file>[^:\s]+\.\w+):(?P<line>\d+): (?P<msg2>.+))$")
UNDEF_REF = re.compile(r"LaTeX Warning: Reference `([^']+)' on page \d+ undefined")
UNDEF_CIT = re.compile(r"(?:LaTeX|Package natbib) Warning: Citation `([^']+)'.* undefined")
BIBLATEX_UNDEF = re.compile(r"Package biblatex Warning: The following entry could not be found")
MISSING = re.compile(r"(?:LaTeX Error: File `([^']+)' not found|! LaTeX Error: Unknown graphics extension)")
OVERFULL = re.compile(r"^Overfull \\hbox \(([\d.]+)pt too wide\).*?lines? (\d+)")
UNDERFULL = re.compile(r"^Underfull \\[hv]box \(badness (\d+)\).*?lines? (\d+)")
RERUN = re.compile(r"(Rerun to get|Label\(s\) may have changed|Please \(re\)run Biber|Please rerun LaTeX)")
FONT = re.compile(r"(LaTeX Font Warning: .+|Missing character: There is no .+)")
LINE_NO = re.compile(r"^l\.(\d+)")


def main() -> int:
    ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    ap.add_argument("log")
    ap.add_argument("--boxes", type=float, default=1.0, help="min overfull width in pt to report (default 1.0)")
    args = ap.parse_args()
    try:
        # TeX wraps log lines at 79 chars; join wrapped lines conservatively for matching
        raw = open(args.log, encoding="utf-8", errors="replace").read()
    except OSError as exc:
        print(f"error: {exc}", file=sys.stderr)
        return 2
    lines = raw.splitlines()

    errors, refs, cites, missing, overfull, underfull, fonts = [], set(), set(), set(), [], [], set()
    rerun = False
    for i, line in enumerate(lines):
        m = ERR.match(line)
        if m:
            msg = m.group("msg") or m.group("msg2")
            where = f"{m.group('file')}:{m.group('line')}" if m.group("file") else ""
            if not where:
                for nxt in lines[i + 1:i + 8]:
                    lm = LINE_NO.match(nxt)
                    if lm:
                        where = f"line {lm.group(1)}"
                        break
            errors.append(f"{where} {msg}".strip())
        if (m := UNDEF_REF.search(line)):
            refs.add(m.group(1))
        if (m := UNDEF_CIT.search(line)):
            cites.add(m.group(1))
        if BIBLATEX_UNDEF.search(line):
            nxt = lines[i + 1] if i + 1 < len(lines) else ""
            key = re.search(r"'([^']+)'", nxt)
            cites.add(key.group(1) if key else "(see log)")
        if (m := MISSING.search(line)):
            missing.add(m.group(1) or line.strip())
        if (m := OVERFULL.match(line)) and float(m.group(1)) >= args.boxes:
            overfull.append((float(m.group(1)), int(m.group(2))))
        if (m := UNDERFULL.match(line)) and int(m.group(1)) >= 10000:
            underfull.append(int(m.group(2)))
        if RERUN.search(line):
            rerun = True
        if (m := FONT.search(line)):
            fonts.add(m.group(1)[:120])

    print(f"{args.log}")
    print(f"  errors: {len(errors)}")
    for e in errors[:25]:
        print(f"    ! {e}")
    if missing:
        print(f"  missing files: {sorted(missing)}")
    if refs:
        print(f"  undefined references ({len(refs)}): {sorted(refs)[:30]}")
    if cites:
        print(f"  undefined citations ({len(cites)}): {sorted(cites)[:30]}")
    if overfull:
        worst = sorted(overfull, reverse=True)[:15]
        print(f"  overfull hboxes >= {args.boxes}pt ({len(overfull)}): " +
              ", ".join(f"{w:.1f}pt@line {ln}" for w, ln in worst))
    if underfull:
        print(f"  underfull boxes at badness 10000 ({len(underfull)}): lines {underfull[:15]}")
    if fonts:
        print("  font warnings:")
        for f in sorted(fonts)[:10]:
            print(f"    - {f}")
    if rerun:
        print("  rerun needed: yes (latexmk normally handles this; run it again or check for oscillating labels)")
    problems = bool(errors or refs or cites or missing)
    print("  status: " + ("PROBLEMS" if problems else "clean (warnings above are cosmetic)"))
    return 1 if problems else 0


if __name__ == "__main__":
    sys.exit(main())

About this skill

Written and maintained by the Claude Skills editors to the same editorial standard as every skill in the directory: a selective trigger, a real workflow, known pitfalls and a verification step. First published , last updated . MIT licensed.

esc