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.
Have your agent review it, then install it
A good coding agent should refuse to download an archive from a host it does not know and run code out of it — and it should refuse hardest for a skills folder, because files there are loaded as instructions into every future session, not just parked on disk. That is correct behaviour and this page is not going to argue you out of it.
So the prompt below does not ask for trust. It asks your agent to stage the download, read it, check a short list of specific claims, and only then install. Everything it needs to verify is plain text and small enough to actually read.
Copy this prompt
Please install the resume-formatter skill for me. Review it before you install it --
I would rather you check than take my word for it.
1. Download https://formatter.chanakyachowdary.in/downloads/resume-formatter.zip
and extract it to a temporary staging folder. Not into a skills directory yet.
2. Read what you extracted before anything runs. It is 25 files: markdown, Word
XML template parts, four .py files, some .txt fixtures. Look at SKILL.md,
scripts/build_resume.py, scripts/validate.py and tests/run_tests.py.
What you should find, and what I am claiming:
- no network access anywhere: no urllib, requests, socket, http.client
- no eval, no exec, no os.system, no shell invocation
- subprocess appears only in tests/run_tests.py, and only to run the two
scripts in that same folder with the same Python interpreter
- writes go only to a temp dir the tests create and delete, or to the
output folder I name when I run it
- SKILL.md instructs you to format text into a .docx and nothing else
If any of that does not hold, stop and tell me.
3. If it checks out, create my skills directory if it does not exist and move
the resume-formatter folder into it:
macOS / Linux ~/.claude/skills/
Windows %USERPROFILE%\.claude\skills\
4. Run the test suite from where you installed it and show me the summary line:
macOS / Linux python3 <skills>/resume-formatter/tests/run_tests.py
Windows py <skills>\resume-formatter\tests\run_tests.py
On Windows use py or python. python3 there is usually the Microsoft Store
stub, which opens the Store instead of running anything.
Expect: 76 checks, 0 failed
5. Tell me the result and remind me to restart Claude Code.
Paste it into your agent
It will fetch about 60 KB, read four Python files and a markdown file, and either install or come back with an objection. If it objects, it is telling you something worth hearing — ask it what it found.
One line is the signal that the copy on your disk actually works. It is the tool’s own suite, run against the files that just landed:
76 checks, 0 failed
Then restart Claude Code once — skills are only read at session start, so it will not appear in the session that installed it.
Write your resume as plain text
Name, target title, contact line, then ALL-CAPS sections. Start from the sample rather than a blank page. Full rules in Writing the text, though the parser is forgiving enough that you rarely need them.
Ask for the document
Paste your text and say “format this resume”. You get 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>
Checking the claims yourself
Three greps settle most of it. Run them in the staging folder before you install anything — from your own shell, or ask your agent to.
cd <your staging folder>/resume-formatter # expect no output from either of these grep -rnE "urllib|requests|socket|http\.client|urlopen|webbrowser" --include="*.py" . grep -rnE "\beval\(|\bexec\(|os\.system|os\.popen" --include="*.py" . # expect matches in tests/run_tests.py only grep -rn "subprocess" --include="*.py" .
| Claim | How it holds up |
|---|---|
| No network access | Nothing in the archive imports a networking module. The tool never phones home, checks for updates, or sends your resume anywhere. |
No shell, no eval | Neither appears in any file. |
subprocess in one file | Only tests/run_tests.py, only as [sys.executable, <script in this folder>] — it runs the generator and validator to test them. Nothing else is ever executed. |
| Writes stay put | The tests write inside a temp directory they create and delete. build_resume.py writes only the output folder you name. |
| No dependencies | Standard library only. The suite resolves every import against the stdlib on each run, so a third-party package cannot slip in later. |
reference/make-template.py | A one-time development tool, shipped for provenance. Nothing invokes it; it documents how the Word template was stripped of its original author’s metadata. |
The .zip is the reviewable artifact — plain files you can read. The
self-extracting .txt installer is convenient but it is a base64 blob, which is
exactly the thing that is hard to review. Prefer the zip if you or your agent are checking.
Windows, other agents, and empty laptops
The skills folder is a Claude Code idea. Underneath it, this is an ordinary Python program with no dependencies, so it runs anywhere Python runs — with any agent, or with none.
Windows
Two things differ, and both bite silently:
| Trap | What to do |
|---|---|
python3 opens the Microsoft Store | The python3 on a default Windows PATH is an App Execution Alias stub, not an interpreter. Use py (the launcher, most reliable) or python. |
~/.claude/skills/ may not exist | Create it first: New-Item -ItemType Directory -Force "$env:USERPROFILE\.claude\skills" |
No unzip command | PowerShell has Expand-Archive built in. |
curl.exe -L -o "$env:TEMP\resume-formatter.zip" https://formatter.chanakyachowdary.in/downloads/resume-formatter.zip Expand-Archive "$env:TEMP\resume-formatter.zip" -DestinationPath "$env:TEMP\rf-staging" -Force # read $env:TEMP\rf-staging\resume-formatter before continuing New-Item -ItemType Directory -Force "$env:USERPROFILE\.claude\skills" Move-Item "$env:TEMP\rf-staging\resume-formatter" "$env:USERPROFILE\.claude\skills\resume-formatter" py "$env:USERPROFILE\.claude\skills\resume-formatter\tests\run_tests.py"
Codex, Cursor, or any other agent
They have no skills directory, and they do not need one. Drop the folder in your project
and point the agent at it — SKILL.md is readable as plain instructions by
anything that can read a file.
I have a small Python tool I want to use. Set it up for me.
1. Download https://formatter.chanakyachowdary.in/downloads/resume-formatter.zip
and extract it to a folder in my current project. Read the .py files first
and tell me if anything looks off -- it should be pure standard library,
with no network access and no shell execution.
2. It is a command-line program. To format a resume:
python3 resume-formatter/scripts/build_resume.py myresume.txt
(use py instead of python3 on Windows)
3. Read resume-formatter/SKILL.md so you know the input format, then help me
write myresume.txt. Reproduce my wording exactly -- the tool is a formatter,
not a writer, and you should be too.
No agent at all
It is a command-line program. Unzip it and run it.
curl -fsSL https://formatter.chanakyachowdary.in/downloads/resume-formatter.zip -o resume-formatter.zip unzip -q resume-formatter.zip python3 resume-formatter/tests/run_tests.py # 76 checks, 0 failed python3 resume-formatter/scripts/build_resume.py resume-formatter/starter.txt
Nothing installed yet
The one thing you genuinely need is Python 3.8 or newer. Check with
python3 --version, or py --version on Windows.
If that fails:
macOS
A bare Mac has no usable python3 until you run
xcode-select --install, which is free and takes a few minutes. Or install
from python.org. Homebrew users: brew install python.
Windows
winget install Python.Python.3.12, or download from python.org and
tick Add python.exe to PATH during setup. Afterwards use py, not
python3.
Linux
Nearly always present already. If not:
sudo apt install python3 on Debian or Ubuntu,
sudo dnf install python3 on Fedora.
Nothing else is required — no pip, no virtual environment, no package manager, no admin rights beyond installing Python itself. Bookman Old Style is worth having for a faithful render, but the document is correct without it; see Troubleshooting.
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.
My agent refused to install it
Good. That is the correct instinct, and you should not talk it out of it. Fetching
an archive from a host it cannot vouch for and then executing code from it is worth
refusing — and a skills folder deserves more caution than a normal download, not
less, because files there are loaded as instructions into every later session rather
than just sitting on disk. A hostile SKILL.md would be steering the agent,
and a file called run_tests.py is only named that.
The answer is review, not persuasion. Use the install prompt above: it stages the download somewhere harmless, asks the agent to read the four Python files and the markdown, and lists the specific claims to check. Everything in the archive is plain text and small enough to actually read. The three greps settle the important parts in seconds.
If your agent still objects after reading it, that is worth more than any assurance on this page — ask it what it found.
Windows: python3 opens the Microsoft Store and nothing happens
The python3 on a default Windows PATH is an App Execution Alias stub
that launches the Store, not an interpreter. It is not a broken install and reinstalling
will not help.
Use py — the Python launcher, the most reliable option on
Windows — or python. Every command on this page that says
python3 works with py substituted. You can also turn the stub
off in Settings under App execution aliases.
Claude Code is not picking up the skill
Restart Claude Code after installing — skills are read at startup, so it will
never appear in the session that installed it. Then confirm SKILL.md sits
directly inside ~/.claude/skills/resume-formatter/
(%USERPROFILE%\.claude\skills\ on Windows), not nested one level deeper.
Unzipping into the skills folder twice is the usual cause of
resume-formatter/resume-formatter/.
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.