Repository

cvfit

Tailors a one-page resume to one posting, then fails the build if it inflated a date or a title. Any agent, or none.

Where
How
Claude Code
Claude Code
Codex, Cursor, Gemini CLI, Amp, Zed, Jules
Codex, Cursor, Gemini CLI, Amp, Zed, Jules
ChatGPT, Claude.ai, any chat window
ChatGPT, Claude.ai, any chat window

Specs

  • WhereHow
  • Claude CodeClaude Code
  • Codex, Cursor, Gemini CLI, Amp, Zed, JulesCodex, Cursor, Gemini CLI, Amp, Zed, Jules
  • ChatGPT, Claude.ai, any chat windowChatGPT, Claude.ai, any chat window
  • No agent at allNo agent at all

README

cvfit

Point it at a job posting, give it whatever you've got about yourself, and it
writes a one-page resume aimed at that posting. Then it checks the result and
won't hand it over if something's wrong.

Two halves. SKILL.md is the judgement, written down so it's done the same way
every time, and any agent can follow it: Claude Code, Codex, Cursor, Gemini
CLI, or a chat window where you paste the file. The two scripts do the part
that shouldn't be improvised: rendering and checking.

The idea

A model should decide what goes on the page, because that's judgement and nobody
has automated it well. Code should decide how the page looks and whether it's
allowed to ship, because those are the parts where improvising costs you.

So every bullet in your profile carries a priority. When the page overflows, the
lowest ones go first; then the tool walks the dropped ones back in from the top
and keeps whatever still fits. It prints every drop. When the page comes up short
it tells you how many lines are missing and won't stretch the whitespace to hide
it, since a page that's 41% full with big gaps reads worse than one that's
honestly short.

And there's a guard. If tailoring moved a date, grew a job title, or added an
employer that isn't in your master profile, the build fails. Tailoring gets to
choose and reorder. It doesn't get to promote you.

Install

Python 3.9 or newer, plus:

pip install typst pyyaml pypdf

No system packages, so this is the same on macOS, Linux and Windows. If you'd
rather use the native tools, brew install typst poppler (or your package
manager) works too and is a little faster; cvfit takes whichever it finds.

git clone https://github.com/MartinPuli/cvfit.git
cd cvfit && python3 tests/run.py

Green means the pipeline works on your machine.

Use it from

Where How
Claude Code /plugin marketplace add MartinPuli/cvfit then /plugin install cvfit@cvfit. Or copy the repo to ~/.claude/skills/cvfit/
Codex, Cursor, Gemini CLI, Amp, Zed, Jules Clone it into the workspace. They read AGENTS.md on their own, which points at SKILL.md
ChatGPT, Claude.ai, any chat window Paste SKILL.md and TAILORING.md, paste the posting, answer its questions, then run the two commands yourself
No agent at all Copy examples/ada-lovelace.yaml, put your own history in it, run the two commands. The fitting, the measuring and the guard are all in the scripts

Two commands

python3 scripts/build_cv.py examples/ada-lovelace.northwind.yaml -o out/cv.pdf --kind job --preview
python3 scripts/verify_cv.py out/cv.pdf --profile out/cv.profile.json --master examples/ada-lovelace.yaml --target examples/posting-northwind.json

Build writes the PDF, a PNG of page one so you can actually look at it, and the
effective profile: what landed on the page after caps and fitting. Verify exits
non-zero when anything's off.

The file you edit

One YAML per person. Open examples/ada-lovelace.yaml; every field has a
comment next to it. Don't have a section? Leave it out. Empty sections get
dropped at render time and the tool never invents one. Skills go in items,
which render as a bold label followed by plain text, so a person can scan them by
label and an ATS reads them as ordinary lines.

Kinds

--kind job, hackathon or competition. Same career, three different
documents, because a hiring manager wants to know whether you've done the job
before, a hackathon organiser wants proof a past project survived past Sunday
night, and a selection committee compares ranks. kinds.json has each one's
section order, priority shifts, caps, and the reasoning in a notes field.

If there's no Experience section at all, Education goes first no matter the
kind. Projects above Education on a student's resume reads like hiding something.

Language

Write the profile in whatever language the resume should be in. Headings are
matched by alias (Experiencia, Formación, Compétences all count) so
ordering and caps still work, and the page keeps the words you wrote.
verify_cv.py --lang es swaps in the Spanish pronoun check.
examples/tomas-rivera.es.yaml is the student example in Spanish.

Format

US Letter, 0.55 inch margins, Georgia at 10.5pt with Palatino and Times New
Roman behind it. Georgia wasn't the first pick. Charter was, until rendering the
same profile in eight faces showed it running 63pt longer than Times, about five
lines, which on a one-pager is a whole bullet. One column, no tables, no
graphics, so an ATS gets plain text. Name centred, one contact line, upper-case
headings with a rule under them, dates in grey on the right.

The spacing is all explicit, and it took four tries to get there. Typst adds
implicit spacing between blocks, and a section whose first entry had no title row
(a skills list, say) sat 12pt lower under its rule than every other section. A
negative v() did nothing; an empty grid did nothing. Zeroing every implicit gap
and owning them with named constants is what finally worked. The numbers are in
the comments in templates/harvard.typ.

How it reads the page back

Page count and page fill come from measuring the PDF, never from an estimate.
With poppler installed that's exact. Without it, pypdf reads the same numbers
in pure Python off each line's baseline, which lands within 0.2 of a percentage
point on the example resumes. scripts/toolchain.py holds both paths, and a
test compares them.

Examples

Four files, three invented people. ada-lovelace.yaml is a standard master
profile with roles and side projects. ada-lovelace.northwind.yaml is her
tailored to posting-northwind.json, dates and titles untouched, which is what
the guard checks. tomas-rivera.yaml is a student with no work history, and
tomas-rivera.es.yaml is him in Spanish.

Prior art

dabydat/resume-builder-skill
packages the Harvard and ATS rules as prose for an agent; this adds the code.
RenderCV is a far better typesetter and
does no tailoring at all. Resume Forge
is where the keyword coverage came from, minus the score, since a score turns
into something people write toward.
silver-dev-cv, by a
recruiter who places Argentine engineers in US startups, and its parent blog
supplied the rule in TAILORING.md about employers the reader has never heard of.

MIT.