CV Tailor
Turns a job description into a one-page LaTeX CV and a matching cover letter, compiled and
verified. Every claim traces to facts.md.
Prerequisites
tectonic — the LaTeX engine. brew install tectonic, or see tectonic-typesetting.github.io.
It is a single binary with no TeX Live install, and it runs with shell-escape disabled, which
is why this skill uses it rather than pdflatex.
pdftotext (poppler) — for the ATS verification pass. brew install poppler.
facts.md and profile.yml in the working directory. Scaffold them from examples/
if missing. Do not proceed without them.
Load first, every build
- The
jd-analyser output. Build from its positioning brief, never from your own read of
the JD. Skipping this is how a trainee role gets mis-built as a specialist role.
facts.md, including its Evidence Index and every caveat in it.
profile.yml for constraints and exclusions.
- The
human-voice skill — load it, every build, before writing a single bullet. It covers
CVs as well as prose. Working from memory is the documented failure mode here, not a
theoretical one.
Non-negotiables
- Never fabricate. Only facts present in
facts.md. No invented metrics, employers, or dates.
- Never write a gap-disclosure sentence into the CV or cover letter. Gaps get tracked
internally, in the report, for the user's own judgement. They never appear on the page the
employer reads. When a JD exposes a real gap, the fix is to find a genuine stronger proof
point, not to write an apologetic paragraph.
- Never submit. This skill does not click Apply or Send, ever.
- One page unless the user explicitly asks otherwise.
- No hidden text. No white text, no zero-opacity layers, no off-page keyword stuffing.
Current ATS platforms flag this as fraud, and it will end the application.
- Compile with
tectonic. Never \uppercase inside titlesec — it breaks under tectonic.
Use \scshape.
- Result over template. Every bullet needs a real outcome and a real number where
facts.md
supports one. What is not required is that every bullet wear the same grammar. Applying one
sentence formula literally across a whole role produces exactly the uniform cadence that reads
as machine-written. Within each role, at least one bullet must break the shape.
- Keyword placement, not just presence. The JD's top two or three terms belong in the profile
line and the first bullet of the most relevant role. ATS scoring weights that placement higher
than the same keyword buried in the last line of page one.
Workflow
- Quick score first. One line, out of 5, with the reason. Wait for a go-ahead before building.
- Run
jd-analyser. Take its positioning brief as the spec.
- Copy the template, do not start from scratch:
cp assets/cv-template.tex cv-<company-slug>.tex
cp assets/cover-letter-template.tex cl-<company-slug>.tex
- Fill from
facts.md. Mirror the JD's exact wording only where real evidence backs it.
Mark anything unsupported as a gap in your report to the user; never invent evidence to close one.
- Compile:
tectonic cv-<company-slug>.tex
tectonic cl-<company-slug>.tex
- Verify with the pass below. Never ship a document that fails it.
- Report exact filenames, compiler result, each verification result, and any honest gaps.
State the numbers: "1 page, all six headings found, 0 hidden characters, 4/4 JD terms backed."
ATS verification pass
An ATS does not see your layout. It sees pdftotext output. Verify against that, not against
how the PDF looks.
PDF=cv-<company-slug>.pdf
# 0. Page count — must be 1
pdfinfo "$PDF" | grep '^Pages:'
# 1. Extraction must not be empty (a CV built from images scores zero)
pdftotext "$PDF" - | wc -w
# 2. Name, email and phone must appear in the first few lines
pdftotext "$PDF" - | head -5
# 3. Standard headings present and in order
pdftotext "$PDF" - | grep -inE '^(education|experience|projects|skills|languages|certifications)'
# 4. Hidden or zero-width characters — must be 0
pdftotext "$PDF" - | grep -cP '[\x{200B}-\x{200D}\x{FEFF}\x{00AD}]'
# 5. Layout vs plain extraction must agree in word count (a big gap means
# multi-column or table layout the parser will scramble)
A=$(pdftotext "$PDF" - | wc -w); B=$(pdftotext -layout "$PDF" - | wc -w)
echo "plain=$A layout=$B delta=$((A-B))"
# 6. No language claimed above the level recorded in facts.md
pdftotext "$PDF" - | grep -inE '(fluent|bilingual|native|C1|C2|B2)'
Then run the human-voice Part 2 pre-ship scan on the same PDF.
Read the matches, never the exit code. grep | sort -u exits 0 on empty input, so chaining
&& echo FAIL fires either way. Look at the output.
Interpreting check 5: a large delta means the parser is reading your columns as interleaved
text. Tab-aligned dates are fine for text search but can confuse portal autofill widgets that
infer structured fields from layout. If a portal is Workday- or SuccessFactors-shaped, consider
supplying a plain .docx alongside the PDF, with dates stacked rather than tab-aligned.
Design pattern
The bundled template follows the conventions that survive ATS parsing:
lmodern, single column, no tables in the body, no text boxes, no headers or footers
- Contact row hyperlinked, but never a bare URL printed as visible text
- Small-caps section headings with a rule underneath, standard heading names
- Dark, mid, and light greys only — colour that survives a black-and-white print
- Tight but not cramped spacing; one page is a constraint, not a target to overflow
Edit the template's variables at the top. Do not restructure the body unless you have a reason
you can state.
Assets
assets/cv-template.tex — one-page CV skeleton
assets/cover-letter-template.tex — matching cover letter
Both are plain LaTeX source. Read them before running them, as you should with any file that
goes through a typesetter.
1---2name: cv-tailor3description: Build a tailored one-page LaTeX CV and matching cover letter from a job description, compile them with tectonic, and verify the PDF the way an ATS parser reads it. Use for any resume, CV, cover letter, LaTeX, or ATS document request. Never fabricates a fact that is not in the facts file.4---56# CV Tailor78Turns a job description into a one-page LaTeX CV and a matching cover letter, compiled and9verified. Every claim traces to `facts.md`.1011## Prerequisites1213- **`tectonic`** — the LaTeX engine. `brew install tectonic`, or see tectonic-typesetting.github.io.14 It is a single binary with no TeX Live install, and it runs with shell-escape disabled, which15 is why this skill uses it rather than `pdflatex`.16- **`pdftotext`** (poppler) — for the ATS verification pass. `brew install poppler`.17- **`facts.md` and `profile.yml`** in the working directory. Scaffold them from `examples/`18 if missing. Do not proceed without them.1920## Load first, every build21221. **The `jd-analyser` output.** Build from its positioning brief, never from your own read of23 the JD. Skipping this is how a trainee role gets mis-built as a specialist role.242. **`facts.md`**, including its Evidence Index and every caveat in it.253. **`profile.yml`** for constraints and exclusions.264. **The `human-voice` skill — load it, every build, before writing a single bullet.** It covers27 CVs as well as prose. Working from memory is the documented failure mode here, not a28 theoretical one.2930## Non-negotiables3132- **Never fabricate.** Only facts present in `facts.md`. No invented metrics, employers, or dates.33- **Never write a gap-disclosure sentence into the CV or cover letter.** Gaps get tracked34 internally, in the report, for the user's own judgement. They never appear on the page the35 employer reads. When a JD exposes a real gap, the fix is to find a genuine stronger proof36 point, not to write an apologetic paragraph.37- **Never submit.** This skill does not click Apply or Send, ever.38- **One page** unless the user explicitly asks otherwise.39- **No hidden text.** No white text, no zero-opacity layers, no off-page keyword stuffing.40 Current ATS platforms flag this as fraud, and it will end the application.41- **Compile with `tectonic`.** Never `\uppercase` inside `titlesec` — it breaks under tectonic.42 Use `\scshape`.43- **Result over template.** Every bullet needs a real outcome and a real number where `facts.md`44 supports one. What is *not* required is that every bullet wear the same grammar. Applying one45 sentence formula literally across a whole role produces exactly the uniform cadence that reads46 as machine-written. **Within each role, at least one bullet must break the shape.**47- **Keyword placement, not just presence.** The JD's top two or three terms belong in the profile48 line and the first bullet of the most relevant role. ATS scoring weights that placement higher49 than the same keyword buried in the last line of page one.5051## Workflow52531. **Quick score first.** One line, out of 5, with the reason. Wait for a go-ahead before building.542. **Run `jd-analyser`.** Take its positioning brief as the spec.553. **Copy the template**, do not start from scratch:56 ```bash57 cp assets/cv-template.tex cv-<company-slug>.tex58 cp assets/cover-letter-template.tex cl-<company-slug>.tex59 ```604. **Fill from `facts.md`.** Mirror the JD's exact wording only where real evidence backs it.61 Mark anything unsupported as a gap in your report to the user; never invent evidence to close one.625. **Compile:**63 ```bash64 tectonic cv-<company-slug>.tex65 tectonic cl-<company-slug>.tex66 ```676. **Verify** with the pass below. Never ship a document that fails it.687. **Report** exact filenames, compiler result, each verification result, and any honest gaps.69 State the numbers: "1 page, all six headings found, 0 hidden characters, 4/4 JD terms backed."7071## ATS verification pass7273An ATS does not see your layout. It sees `pdftotext` output. Verify against that, not against74how the PDF looks.7576```bash77PDF=cv-<company-slug>.pdf7879# 0. Page count — must be 180pdfinfo "$PDF" | grep '^Pages:'8182# 1. Extraction must not be empty (a CV built from images scores zero)83pdftotext "$PDF" - | wc -w8485# 2. Name, email and phone must appear in the first few lines86pdftotext "$PDF" - | head -58788# 3. Standard headings present and in order89pdftotext "$PDF" - | grep -inE '^(education|experience|projects|skills|languages|certifications)'9091# 4. Hidden or zero-width characters — must be 092pdftotext "$PDF" - | grep -cP '[\x{200B}-\x{200D}\x{FEFF}\x{00AD}]'9394# 5. Layout vs plain extraction must agree in word count (a big gap means95# multi-column or table layout the parser will scramble)96A=$(pdftotext "$PDF" - | wc -w); B=$(pdftotext -layout "$PDF" - | wc -w)97echo "plain=$A layout=$B delta=$((A-B))"9899# 6. No language claimed above the level recorded in facts.md100pdftotext "$PDF" - | grep -inE '(fluent|bilingual|native|C1|C2|B2)'101```102103Then run the `human-voice` Part 2 pre-ship scan on the same PDF.104105**Read the matches, never the exit code.** `grep | sort -u` exits 0 on empty input, so chaining106`&& echo FAIL` fires either way. Look at the output.107108**Interpreting check 5:** a large delta means the parser is reading your columns as interleaved109text. Tab-aligned dates are fine for text search but can confuse portal autofill widgets that110infer structured fields from layout. If a portal is Workday- or SuccessFactors-shaped, consider111supplying a plain `.docx` alongside the PDF, with dates stacked rather than tab-aligned.112113## Design pattern114115The bundled template follows the conventions that survive ATS parsing:116117- `lmodern`, single column, no tables in the body, no text boxes, no headers or footers118- Contact row hyperlinked, but never a bare URL printed as visible text119- Small-caps section headings with a rule underneath, standard heading names120- Dark, mid, and light greys only — colour that survives a black-and-white print121- Tight but not cramped spacing; one page is a constraint, not a target to overflow122123Edit the template's variables at the top. Do not restructure the body unless you have a reason124you can state.125126## Assets127128- `assets/cv-template.tex` — one-page CV skeleton129- `assets/cover-letter-template.tex` — matching cover letter130131Both are plain LaTeX source. Read them before running them, as you should with any file that132goes through a typesetter.