Plain text in. The same document out. Forever.
Tailoring a resume means rewriting the words, not the layout. But every rewrite in Word is a chance for a stray indent, a 10.5 pt run or a lost bullet to creep in. This tool removes that risk by separating the two jobs completely: you own the text, it owns the formatting, and the formatting cannot drift because it is copied from your reference document rather than rebuilt from a description of it.
Jordan Rivera Senior Platform Engineer jordan.rivera@example.com | +1 555-0100 PROFESSIONAL SUMMARY: • Platform engineer with 9 years building the internal tooling other engineers depend on. TECHNICAL SKILLS: Languages & Runtimes: Go, Python, Bash PROFESSIONAL EXPERIENCE: Northwind Logistics, Denver CO | March 2022 - Present Staff Platform Engineer • Rebuilt the deployment pipeline, cutting median release time from 41 to 9 minutes.
Bookman Old Style 10 pt every run, pinned A4, 0.5″ margins w:pgMar 720 Bullets Symbol U+F0B7 real list numbering Indent glyph 0″, text .25″ w:ind left=360 Body justified w:jc both Headers bold + underlined ALL CAPS, colon Email live mailto: link rStyle Hyperlink Skills bold label, then one paragraph, the list plain not a bullet Spacing one blank line before each exactly section + job Dates normalised to EN DASH U+2013
Your text is reproduced verbatim. The tool never invents, rewords, shortens or “improves” a bullet, and never adds an employer, date, metric or credential you did not supply. If your input has no education section, neither does the output. A built-in validator diffs the finished document against your text and fails the build on any character that was dropped, added or altered.
Let your agent install it
You already have an agent that can download a file and put it in a folder. Hand it the prompt below and it will do the whole install, then run the tool’s own test suite so you know the result is sound before you trust it with anything.
Copy this prompt
Install the resume-formatter skill for me. 1. Download https://formatter.chanakyachowdary.in/downloads/resume-formatter.zip 2. Unzip it into ~/.claude/skills/ so that ~/.claude/skills/resume-formatter/SKILL.md exists 3. Run: python3 ~/.claude/skills/resume-formatter/tests/run_tests.py and show me the summary line so I know the install is sound 4. Confirm it worked and remind me to restart Claude Code It is a zero-dependency Python skill that turns a plain-text resume into a formatted Word document. There is nothing to install beyond the folder itself.
Paste it into Claude Code
That is the whole install. It downloads about 60 KB, writes one folder, and runs 76 checks against it. Nothing is installed system-wide, no package manager is involved, and nothing touches your existing configuration.
Your agent will narrate this its own way, but one line is the one that matters — it is the tool’s own test suite reporting on the copy that just landed on your disk:
76 checks, 0 failed
Anything else means the install is not sound — say so to your agent and it can retry. Then restart Claude Code once; skills are read at startup.
Write your resume as plain text
Name, target title, contact line, then ALL-CAPS sections. Start from the sample rather than a blank page — it is a complete, correctly shaped resume you can overwrite line by line. The full rules are in Writing the text, but the parser is forgiving enough that you rarely need them.
Ask for the document
Paste your text and say “format this resume”. You get back a dated folder
holding the .docx and a copy of the text that produced it.
Format this resume with the resume-formatter skill. Reproduce my text exactly -- do not reword, add or remove anything. <paste your resume text here>
Rather do it yourself?
The agent route is only a convenience — nothing about the tool needs it. Two manual paths, both equivalent:
curl -fsSL https://formatter.chanakyachowdary.in/downloads/resume-formatter-installer.txt \ -o resume-formatter-installer.txt && python3 resume-formatter-installer.txt
curl -fsSL https://formatter.chanakyachowdary.in/downloads/resume-formatter.zip -o resume-formatter.zip && \ unzip -q resume-formatter.zip -d ~/.claude/skills/
Use .claude/skills/ inside a project instead of ~/.claude/skills/
to scope it to one repository. Or skip Claude entirely and run
scripts/build_resume.py straight from the unzipped folder — it is an
ordinary command-line program. See Command reference.
The input contract
Three header lines, then ALL-CAPS sections. That is the whole structure. Everything else the parser is deliberately forgiving about, because a resume you paste out of an old document is never clean.
Full Name ← line 1, becomes the document title Target Job Title ← line 2 email@example.com | +1 555-0100 ← line 3, email becomes a mailto: link PROFESSIONAL SUMMARY: ← ALL CAPS, colon optional • A bullet. • Another bullet. TECHNICAL SKILLS: Category: item, item, item ← bold label, plain list, NOT a bullet PROFESSIONAL EXPERIENCE: Company, City ST | Month Year – Month Year Job Title Responsibilities: ← optional; always emitted anyway • A bullet. EDUCATION: • Degree – Institution.
Things you do not have to get right
| You write | It does |
|---|---|
• - * · or a tab | Recognises any of them as a bullet marker and strips it. The real bullet glyph comes from Word’s list numbering, so a typed one never reaches the document. |
professional summary | Matches case-insensitively, normalises to PROFESSIONAL SUMMARY:. SUMMARY, WORK EXPERIENCE, EMPLOYMENT HISTORY and SKILLS map to the canonical four. |
2022 - Present | Normalises to an EN DASH: 2022 – Present. Accepts -, --, –, — and to, and passes Present, Current, Till Date straight through. |
No Responsibilities: line | Adds it. Every job block gets one. |
| Curly quotes, CRLF line endings, non-breaking spaces and hyphens, trailing spaces | All folded to their plain equivalents. Real en and em dashes in your prose are left alone. |
Programming & Data, <10 minutes | Escaped correctly in the XML and rendered as the characters you typed. |
CERTIFICATIONS, PROJECTS, anything else in caps | Kept, given the same section-header formatting, with its lines as bullets. |
| Blank lines wherever you like | Treated as hints, not content. Vertical spacing follows the document’s own rule, not your blank lines. |
The tool stops with a plain-English error and writes nothing at all if the input is empty, has fewer than three lines before the first section, puts a section header before the contact line, has stray content before any section, or has no ALL-CAPS headers. A half-written document is never left behind: output goes to a temporary file and is renamed only once the build has fully succeeded.
A dated folder per run, so nothing is ever overwritten
Every build makes its own timestamped folder holding the document and a copy of the exact text that produced it. Six months and thirty applications later you can still see which words went with which file.
Jordan Rivera’s resume 17 August 2026 19-52/ ├── Jordan Rivera - Senior Platform Engineer.docx the document └── source.txt exactly what you fed in
Folder name
{Name}’s resume {D Month YYYY} {HH-MM} — the name is taken verbatim
from line 1 of your text, never hardcoded.
Document name
{Full Name} - {Target Title}.docx — line 1 and line 2 of your text,
matching the reference file’s own naming style.
Why 19-52 and not 19:52
Windows forbids a colon in filenames and macOS Finder renders it as a slash. A hyphen is the only spelling that survives being emailed to someone else.
Two runs in one minute
The second gets (2) appended, the third (3). An earlier run
is never silently replaced.
Prefer a bare file? --no-folder writes the .docx on its own,
-o PATH gives it an exact name and location, -d DIR puts the
dated folder somewhere other than the current directory, and
--no-source-copy skips the text snapshot.
Every flag
The defaults reproduce the reference document exactly. You only reach for a flag when you deliberately want to depart from it.
| Flag | Default | What it does |
|---|---|---|
input | stdin | Path to your plain-text resume. Omit it, or pass -, to pipe text in instead. |
-d, --outdir DIR | . | Where the dated folder is created. |
-o, --output PATH | — | Exact output path. Suppresses the dated folder entirely. |
--no-folder | off | Write the .docx straight into the output directory. |
--no-source-copy | off | Skip the source.txt snapshot. |
--page-size | a4 | a4 (11906×16838 twips) or letter (12240×15840). |
--no-justify | off | Left-align the body instead of justifying it. |
--font "NAME" | Bookman Old Style | Swap the body typeface. |
--size PT | 10 | Body size in points. Accepts halves. |
Checking a document
The validator is the safety net. Run it after every build — it takes a fraction of a second and it is what stands between you and a silently mangled bullet.
python3 ~/.claude/skills/resume-formatter/scripts/validate.py "Your Name - Your Title.docx" -i resume.txt
It runs 26 checks in four families — round-trip (no character dropped, added or
altered), structure (every bullet carries the right numbering and indent, headers are bold
and underlined, blank paragraphs sit where they should, no literal bullet character in any
text run), schema (the package is well-formed and every reference resolves, so Word never
offers to repair it), and privacy (no author metadata or editing fingerprints).
--extract dumps the document back to plain text if you ever need to recover it.
What is actually being reproduced
The reference document was taken apart — a .docx is a ZIP of XML —
and every formatting property read out of the markup rather than guessed at from how it
looks on screen. These are the numbers that matter.
| Property | Value | Where it lives |
|---|---|---|
| Page | A4, 11906 × 16838 twips | w:pgSz |
| Margins | 720 twips (0.5″) on all four sides | w:pgMar |
| Typeface | Bookman Old Style, complex-script fallback minorHAnsi | direct run formatting, all 400 property blocks |
| Size | 10 pt (w:sz 20) | direct run formatting — not the style |
| Line spacing | Single, no paragraph gap | Normal: after=0 line=240 |
| Bullet glyph | U+F0B7 in Symbol | numbering.xml, numId 1 |
| Bullet indent | glyph at 0″, text at 0.25″ | w:ind left=360 over the style’s 720, with hanging=360 |
| Body alignment | Justified | w:jc both |
| Section headers | Bold + single underline, ALL CAPS, trailing colon | direct run formatting |
| Vertical rhythm | Exactly one empty paragraph before each section header and each job block after the first | seven empty paragraphs, nothing else |
Three deliberate departures
The reference document carries a few artefacts of having been edited by hand in Word. All three are invisible when rendered, and the tool emits the majority form rather than reproducing the inconsistency:
The stray justification
Three of the four section headers are left-aligned; EDUCATION: also carries
jc="both". On a line that never wraps, the two render identically.
The bold space
Seven of eight skills lines put the space after the label in the plain run; the eighth puts it in the bold run. Difference: well under one pixel of justification.
Five kinds of blank line
The seven empty paragraphs have five different property sets, each inherited from whatever preceded it. All seven render as the same empty 10 pt line.
Measured, not asserted
The strongest test of a formatter is whether it can rebuild the document it learned from. The reference was extracted back to plain text, fed through the tool, and the result compared against the original at three levels.
At the XML level
Seven diff hunks across 3,215 lines of markup, every one of them in the documented set of invisible artefacts above. Same paragraph count, same text in the same order.
At the rendered level
Both files put through Microsoft Word to PDF on a machine with the real font installed. Layout-preserving text extraction is byte-identical, and both are five pages.
At the pixel level
Rasterised at 100 dpi and compared: 2,339 differing pixels out of 4.8 million, maximum delta 65 of 255 — the signature of anti-aliasing, not of moved type.
Why it holds up
The style definitions are never regenerated. styles.xml,
numbering.xml, settings.xml, fontTable.xml and the
theme are copied into every build byte-for-byte; only the document body and the
mailto: link are written fresh. That means properties nobody thought to write
down — the kerning default, the OpenType ligature setting, the inherited language tag,
the 376 latent style entries — survive untouched. A generator that rebuilt the styles
from a description would silently lose every one of them it failed to notice.
Builds are deterministic. Running the tool twice on the same text produces two byte-identical files, which makes any change genuinely diffable.
Everything, individually
The installer contains all of this already. These are here for reading offline, or for dropping into a repository.
python3.The things that actually come up
The document looks different on my machine than in the screenshot
Almost certainly the font. Bookman Old Style ships with Microsoft Office — it is not part of Windows or macOS themselves, and it is not on most Linux systems. Where it is missing, the viewer substitutes a serif using the metrics declared in the document, so lines rewrap, and the page count can change.
The document itself is still correct: it names the real font, and anyone opening it in Word on a machine that has it sees the intended layout. To freeze the layout for everyone regardless, export to PDF from a machine that has the font — the font is then embedded in the PDF.
Claude Code is not picking up the skill
Restart Claude Code after installing — skills are read at startup. Then confirm
the folder is at ~/.claude/skills/resume-formatter with
SKILL.md directly inside it, not nested one level deeper.
Failing that, the command-line path works identically and needs no skill discovery
at all: python3 ~/.claude/skills/resume-formatter/scripts/build_resume.py resume.txt.
My last page is nearly empty
An A4 page at these metrics holds about 63 lines, and a typical bullet takes two of them — so roughly 30 bullets per page, plus three lines of overhead per job block. US Letter holds about 59.
If your final page carries fewer than about 15 lines it reads as an accident rather than a choice. Cutting three or four two-line bullets usually pulls it back. The tool will not do this for you — editing your words is not its job.
Will this get through applicant tracking systems?
The layout is close to the safest shape there is: a single column, no tables, no text boxes, no images, no headers or footers, contact details in the body, conventional section names, and real list numbering rather than typed bullet characters. Those are the specific things that scramble or silently drop text in a parser that reads a document linearly.
The validator asserts the absence of tables, drawings, text boxes, frames, content controls and headers on every single build, so a future change cannot quietly reintroduce one.
Can I use US Letter instead of A4?
Yes: --page-size letter. The reference is A4, which is very likely an
accident of the machine it was first written on, so A4 is the default in order to
reproduce it exactly. A4 is 5 mm narrower and 18 mm taller than Letter; the
practical difference is about four lines per page.
Why is the text justified? Can I turn it off?
Because the reference is justified, and reproducing the reference is the point. It is worth knowing the trade: justification stretches word spacing to force both margins flush, which can produce pale “rivers” running down a paragraph and makes it harder for some readers to track from line to line. Accessibility guidance is generally against it for body text.
This layout is a relatively forgiving case — the measure is wide, so each space
stretches very little. If you would rather not, --no-justify left-aligns
the body and the validator adjusts its checks to match.
Does it need pip, a virtual environment, or an internet connection?
None of the three. Everything is Python standard library — the document is
assembled with zipfile and string templating. The test suite verifies this
by resolving every import against the standard library on each run, so a third-party
dependency cannot creep in unnoticed.
Something in my resume came out wrong
Run the validator with -i pointing at your text file. It compares the
finished document against your input character by character and names anything that
was dropped, added or altered, so you get a specific answer rather than a hunch.
--extract on its own dumps the document back to plain text, which is the
quickest way to see exactly what the tool thinks your content is.