Resume Formatter
v1.0  ·  python 3.8+  ·  zero dependencies
Write the words. Never touch the formatting again.

Your resume, rebuilt from plain text — identical every time.

Rewrite the content as often as a job posting demands. Run one command. Get back a Word document whose every margin, indent, bullet glyph and line break matches your reference file exactly — because the formatting is cloned from it, not recreated. A companion skill scores it before you send it.

Download INSTALL.md 7 KB See how it works then just type /resume-formatter

Download one file, paste one sentence, and your coding agent installs it — Claude Code, Codex, Cursor, whichever you use. No commands, no dependencies, nothing to configure.

A formatted single-page resume for the fictional Jordan Rivera, set in Bookman Old Style at 10 point with justified bullets and underlined section headers.
Real output · rendered through Microsoft Word · fictional content
What it does

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.

resume.txt — what you write
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.
the document — what it produces
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
It formats. It does not write.

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.

Install

Two steps, whichever agent you use

Download one small text file and paste one sentence. That file is the installer — readable instructions your coding agent follows: fetch the tool, check it over, put it where that particular agent looks for it, run its tests, and report back. You do not unzip anything, run any commands, or install Python.

Download INSTALL.md

Plain markdown. Read it first if you like — that is rather the point of shipping an installer as a document instead of a binary.

Paste this to your agent

works in Claude Code, Codex, Cursor, or anything else that reads files
Read the INSTALL.md file in my Downloads folder and follow it.

It reads the file and follows it. Expect it to check the package before installing — the instructions tell it to, and to stop if anything looks wrong.

Or skip the download.

If your agent can fetch a URL, one sentence is the whole install:

nothing to download
Read https://formatter.chanakyachowdary.in/install.md and follow it.

Where it ends up, per agent

There is no shared convention for this, so INSTALL.md branches on whichever agent is reading it. For the record:

AgentWhere the folder goesHow it gets discovered
Claude Code
or Claude Desktop
~/.claude/skills/
%USERPROFILE%\.claude\skills\ on Windows
Loaded automatically as a skill. Restart once after installing — skills are read at startup.
Codex In your project, e.g. ./resume-formatter/ Codex reads AGENTS.md. A short block is appended there pointing at the tool — see below.
Cursor
and similar
In your project Same block, in whichever instructions file it reads — AGENTS.md or its own rules file.
No agent Anywhere It is a plain command-line program. See Command line.
The AGENTS.md block, if you want to add it by hand

This is what INSTALL.md appends for Codex and Cursor. Append it to an existing AGENTS.md rather than replacing the file.

append to AGENTS.md
## Resume formatting

`resume-formatter/` turns a plain-text resume into a formatted .docx.

- Read `resume-formatter/SKILL.md` before using it.
- Build with: `python3 resume-formatter/scripts/build_resume.py <input.txt>`
  (use `py` on Windows). It writes a dated folder containing the .docx and a
  copy of the source text.
- Check a result with: `python3 resume-formatter/scripts/validate.py <file.docx> -i <input.txt>`
- It is a formatter, not a writer. Reproduce the user's wording exactly -- never
  reword, add or remove anything. If they want the wording changed, do that as a
  separate, explicit step first.
What INSTALL.md tells it to do

No surprises — it is a document, so you can read the whole thing first. In order:

  1. Download the zip and extract it to a staging folder, deliberately not to its final home yet.
  2. Check it: three greps proving no network access, no eval or shell, and subprocess only in the test runner. It is told to stop and report if any of that fails.
  3. Install it to the right place for whichever agent is reading — skills folder, or project plus AGENTS.md.
  4. Run the test suite and confirm 76 checks, 0 failed.
  5. Report back, and pass on how to use it.

Covers macOS, Linux and Windows, including the Windows python3-opens-the-Microsoft-Store trap. Same claims as Is it safe to install? below.

Just give me the files

Download the zip and put the resume-formatter folder wherever the table above says, so that SKILL.md sits directly inside it. There is no setup step to skip — copying the folder is the install.

Using it

You never run a command. You just ask.

Paste your resume into the chat, or point at a text file. Then say what you want. Whichever agent you use runs the tool for you — the Python underneath is not something you need to think about, or even know is there.

Claude Code

/resume-formatter

The skill is a slash command. Type it on its own and it asks for your resume, or paste your text straight after it and it goes. Tab-completes after the slash, so you do not have to remember the name.

Any agent

“format this resume”

Plain English works everywhere — Claude Code picks the skill up from the words alone, and Codex or Cursor use it once AGENTS.md points at the folder. No command to memorise.

three ways in, same result
/resume-formatter
    → asks for your resume, then formats it

/resume-formatter ~/Documents/resume.txt
    → formats that file

/resume-formatter
Jordan Rivera
Senior Platform Engineer
…
    → formats the text you pasted after it
a normal exchange
You
  /resume-formatter

  Jordan Rivera
  Senior Platform Engineer
  jordan.rivera@example.com | +1 555-0100

  PROFESSIONAL SUMMARY:
  • Platform engineer with 9 years building internal tooling.
  …

Your agent
  Done. Jordan Rivera’s resume 18 August 2026 09-14/
    Jordan Rivera - Senior Platform Engineer.docx
    source.txt

  4 sections, 16 bullets, A4, justified. Validator: 26 checks, 0 failed.

What lands on your disk

A dated folder per run, holding the document and a copy of the text that made it. Nothing is ever overwritten, so every version you have sent out stays recoverable.

one folder per run
Jordan Rivera’s resume 18 August 2026 09-14/
├── Jordan Rivera - Senior Platform Engineer.docx   open it, send it
└── source.txt                                      exactly what you fed in

The folder is {Name}’s resume {date} {time} and the document is {Full Name} - {Target Title}.docx — both read off the first two lines of your text. Two runs in the same minute get (2), (3).

Tailoring for each application

This is the workflow the tool exists for. Keep one master text file. For each posting, edit the wording — lead with what that job asks for, cut what it does not — and ask again. You get a fresh dated folder; the previous one stays untouched. Because the formatting is identical every single time, the only thing you are ever thinking about is the words.

Things worth asking for

Say thisYou get
/resume-formatter
“format this resume”
The default: A4, justified, Bookman Old Style 10 pt.
“format this on US Letter”Letter instead of A4 — about four fewer lines per page.
“format this, left-aligned”Ragged right instead of justified.
“check the docx matches my text”Runs the validator: reports any character dropped, added or altered.
“save it in ~/Documents/applications”The dated folder created wherever you want it.
“turn this docx back into plain text”Your text recovered out of a document you built earlier.
It will not rewrite your words.

The tool reproduces your text exactly and has no text-generation path at all. If you want the wording improved, ask for that as a separate step, read what comes back, and format the version you actually agreed to. Keeping those two jobs apart is the whole point — it is why the output never surprises you.

Never done this before?

Start from the sample instead of a blank page. It is a complete, correctly shaped resume for a fictional person: overwrite it line by line and you cannot get the structure wrong. A copy is already inside the skill at resume-formatter/starter.txt.

Download starter-resume.txt 3 KB
Companion skill

resume-grader — score it before you send it

A separate skill, installed the same way and on its own. It scores a resume against a rubric you can read: six mechanical checks, each worth a stated number of points, each deduction naming the exact line it came from. Neither skill needs the other.

Install

Two steps, same as above

Download the file, paste the sentence. It stages, reviews, installs and runs the grader’s own 50-check suite.

Then

/resume-grader

Or plain English: “grade my resume”, “score this”, “how well does this match the posting?”

paste this after downloading
Read the INSTALL-grader.md file in my Downloads folder and follow it.
or skip the download entirely
Read https://formatter.chanakyachowdary.in/install-grader.md and follow it.

What it gives you

real output, on a real resume
  RESUME SCORE   84 / 100   (SOLID)

  Quantified impact            10.7 / 22
      16 of 66 experience bullets carry a number (24%)
      -11.3 Below the half-of-bullets convention. Numbers are the
            fastest way to turn a duty into evidence.
            • Developed RAG pipelines combining document ingestion…
            • Developed conversational AI capabilities supporting…

  Verb variety                  7.6 / 12
      21 distinct opening verbs across 66 experience bullets
      -4.4  Openers repeat heavily.
            • "Developed" starts 15 bullets
            • "Implemented" starts 11 bullets

  Strong openings   18.0 / 18     Clean language   16.0 / 16
  Bullet length     12.0 / 12     Structure        20.0 / 20
DimensionPointsWhat it checks
Quantified impact22Do at least half your experience bullets carry a number, percentage or amount?
Strong openings18Concrete verbs, not “Responsible for” or “Helped with”.
Verb variety12Six bullets starting “Developed” make six different things read as one.
Bullet length12Roughly 12–32 words. Longer stops being skimmable; shorter is usually a duty with the result missing.
Clean language16No “team player”, “detail-oriented”, “results-driven”. These assert a trait instead of evidencing it.
Structure20Reachable contact details, expected sections, every role dated.

Against a specific posting

Give it the job ad too and it reports keyword coverage as a separate score — deliberately not folded into the first, because “well written” and “matches this job” are different questions and averaging them answers neither. It ranks the posting by prominence rather than raw frequency, so the missing-terms list contains technologies instead of ideally and comfortable. It also flags terms you have used too often, because current systems penalise stuffing rather than reward it.

What the number is, and what it is not.

It scores how the resume is written. It cannot tell whether an achievement is impressive or whether a claim is true, and it does not predict interviews — no honest tool can. Every threshold is a widely repeated convention, not a measured optimum, which is why the rubric is published rather than hidden: a score with a visible derivation is a checklist, one without is a horoscope. And it never rewrites your bullets. It points at lines and explains why.

One thing it will not check: employment gaps.

Trivial to implement — parse the dates, subtract. Left out deliberately. Flagging gaps quietly penalises career breaks, illness, caring responsibilities and layoffs, and a tool has no business applying that on your behalf while calling it objectivity.

Every dimension with its source, an honest confidence rating, and the full list of things left out on purpose:

grading-rubric.md 9 KB resume-grader.zip 29 KB
Writing the text

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.

the shape
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 writeIt does
- * · or a tabRecognises 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 summaryMatches case-insensitively, normalises to PROFESSIONAL SUMMARY:. SUMMARY, WORK EXPERIENCE, EMPLOYMENT HISTORY and SKILLS map to the canonical four.
2022 - PresentNormalises to an EN DASH: 2022 – Present. Accepts -, --, , and to, and passes Present, Current, Till Date straight through.
No Responsibilities: lineAdds it. Every job block gets one.
Curly quotes, CRLF line endings, non-breaking spaces and hyphens, trailing spacesAll folded to their plain equivalents. Real en and em dashes in your prose are left alone.
Programming & Data, <10 minutesEscaped correctly in the XML and rendered as the characters you typed.
CERTIFICATIONS, PROJECTS, anything else in capsKept, given the same section-header formatting, with its lines as bullets.
Blank lines wherever you likeTreated as hints, not content. Vertical spacing follows the document’s own rule, not your blank lines.
When it refuses to build

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.

The format

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.

PropertyValueWhere it lives
PageA4, 11906 × 16838 twipsw:pgSz
Margins720 twips (0.5″) on all four sidesw:pgMar
TypefaceBookman Old Style, complex-script fallback minorHAnsidirect run formatting, all 400 property blocks
Size10 pt (w:sz 20)direct run formatting — not the style
Line spacingSingle, no paragraph gapNormal: after=0 line=240
Bullet glyphU+F0B7 in Symbolnumbering.xml, numId 1
Bullet indentglyph at 0″, text at 0.25″w:ind left=360 over the style’s 720, with hanging=360
Body alignmentJustifiedw:jc both
Section headersBold + single underline, ALL CAPS, trailing colondirect run formatting
Vertical rhythmExactly one empty paragraph before each section header and each job block after the firstseven empty paragraphs, nothing else
0″ 0.25″ 0.5″ 0.75″ 1″ Rebuilt the deployment pipeline, cutting median release time from 41 minutes to 9. 360 twips w:ind left="360"  +  w:hanging="360"   →   glyph pulled back to the margin 1440 twips = 1 inch  ·  the ListParagraph style says 720; the paragraph overrides it
Bullet geometry, to scale

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.

Proof of fidelity

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.

0
unexplained differences in the document body XML
289
of 289 rendered lines break at exactly the same word
0.048%
of pixels differ, all of it anti-aliasing
81
automated checks, all passing

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.

Same input, same bytes.

Builds are deterministic. Running the tool twice on the same text produces two byte-identical files, which makes any change genuinely diffable.

Before you install

Is it safe to install?

Worth asking, and worth checking rather than taking on faith. Wherever this ends up — a Claude Code skills folder, or a project your agent reads via AGENTS.md — it is being read as instructions in every later session, not just parked on disk. That earns more scrutiny than an ordinary download, not less. If your agent hesitates, that is the right instinct.

It is 25 files, all plain text, small enough to actually read. Three commands settle the parts that matter:

check it yourself
cd resume-formatter

# both should print nothing
grep -rnE "urllib|requests|socket|http\.client|urlopen" --include="*.py" .
grep -rnE "\beval\(|\bexec\(|os\.system|os\.popen" --include="*.py" .

# should list tests/run_tests.py and nothing else
grep -rln "subprocess" --include="*.py" .
ClaimWhat it rests on
It cannot reach the networkNothing imports a networking module. It never phones home, checks for updates, or sends your resume anywhere.
No shell, no evalNeither appears in any file.
subprocess is used onceOnly in tests/run_tests.py, only as [sys.executable, <script in the same folder>] — it runs the generator to test it. Nothing else is executed, ever.
Writes stay where you put themThe tests write in a temp directory they create and delete. The generator writes only the output folder you name.
No dependenciesStandard library only, so nothing is fetched at install or run time. The test suite resolves every import against the stdlib, so a package cannot creep in later.
reference/make-template.pyA one-time development tool, shipped for provenance. Nothing invokes it. It documents how the Word template was stripped of its original author’s metadata.

Prefer the .zip if you or your agent are reviewing — it is plain files you can read. The self-extracting .txt installer is convenient but it is a base64 blob, which is precisely the thing that is hard to check.

All downloads

Everything, individually

The installer contains all of this already. These are here for reading offline, or for dropping into a repository.

resume-formatter-installer.txtThe whole tool, self-extracting. Run it with python3.
Download 83 KB
resume-formatter.zipThe same skill as a plain folder, if you would rather unzip it yourself.
Download 57 KB
starter-resume.txtA complete, correctly shaped sample resume to overwrite.
Download 3 KB
README.mdInstall, usage, folder layout, sharing.
Download 7 KB
SKILL.mdThe operating procedure Claude Code follows.
Download 8 KB
format-spec.mdEvery formatting property with the XML that proves it.
Download 12 KB
resume-notes.mdApplicant tracking systems, justification, A4 vs Letter, fonts, page counts.
Download 12 KB
Command reference

Every flag

The defaults reproduce the reference document exactly. You only reach for a flag when you deliberately want to depart from it.

FlagDefaultWhat it does
inputstdinPath 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 PATHExact output path. Suppresses the dated folder entirely.
--no-folderoffWrite the .docx straight into the output directory.
--no-source-copyoffSkip the source.txt snapshot.
--page-sizea4a4 (11906×16838 twips) or letter (12240×15840).
--no-justifyoffLeft-align the body instead of justifying it.
--font "NAME"Bookman Old StyleSwap the body typeface.
--size PT10Body 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.

Terminal
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.

Send it to a friend

One file, nothing to configure

Send them resume-formatter-installer.txt. That is the entire handover. They run it and get a working install with the identical formatting, because the reference document’s style definitions travel inside the bundle.

Nothing personal travels with it

No name, email, phone number or employer appears anywhere in the tool — not in a default, not in an example, not in the packaged template. Author metadata, revision identifiers and editing fingerprints were all stripped from the stored template. The sample data is openly fictional, and the test suite asserts all of this on every run.

Or share it as a repository

The unpacked folder is self-contained and dependency-free. Commit it, push it, and anyone can drop it into ~/.claude/skills/, or into a project and reference it from AGENTS.md.

Troubleshooting

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 an instructions folder, whether that is Claude Code’s skills directory or a project’s AGENTS.md, deserves more caution than a normal download rather than less: what lands there is read as instructions in every later session, not just parked 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.

My agent is not picking up the tool

Claude Code: restart it. Skills are read at startup, so the tool never appears in the session that installed it. Then confirm SKILL.md sits directly inside ~/.claude/skills/resume-formatter/ (%USERPROFILE%\.claude\skills\ on Windows), not one level deeper — unzipping into the skills folder twice gives you resume-formatter/resume-formatter/, which is the usual cause.

Codex or Cursor: there is nothing to restart, but there is nothing automatic either — they only know about the tool if your instructions file says so. Check that AGENTS.md exists at the repo root and contains the resume-formatting block, and that the path in it matches where you actually put the folder.

Failing either, the command-line path works identically and needs no discovery at all — point your agent at resume-formatter/scripts/build_resume.py, or run it yourself. See Command line.

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.