---
name: resume-formatter
description: Format a plain-text resume into a .docx that exactly matches a stored reference layout (Bookman Old Style 10pt, A4, justified bullets, bold+underlined section headers). Use when the user pastes or attaches resume text and asks to format it, make it a Word/docx file, apply "my format" or "the usual format", tailor a resume for a job application, or when they paste resume-shaped text (name, contact line, PROFESSIONAL SUMMARY / TECHNICAL SKILLS / EXPERIENCE / EDUCATION sections) and mention a resume or CV. Not for general Word document creation, letters, reports, or editing an existing .docx.
---

# Resume formatter

Turns plain-text resume content into a `.docx` byte-compatible with a stored
reference layout. The formatting is fixed; only the words change.

## The one rule that matters

**This skill is a formatter, not a writer.**

Reproduce the user's text verbatim. Do not invent, embellish, reword, shorten,
"improve", or reorder a single bullet. Do not add companies, job titles, dates,
metrics, skills, certifications or degrees that are not in the input. If the
input has no EDUCATION section, the output has no EDUCATION section. If a bullet
has a typo, the typo ships.

The script enforces this mechanically — it copies input strings into XML and
has no text-generation path at all. Your job is not to undermine it by
"helpfully" editing the text before you feed it in. If the user asks you to
*improve* the wording, that is a separate task: do the rewrite, show it to them,
get agreement, and only then format the agreed text.

## How you get invoked

In Claude Code this skill is also a slash command, `/resume-formatter`. Handle
each of these without making the user repeat themselves:

| What arrives | What to do |
|---|---|
| `/resume-formatter` on its own | Ask for the resume text, in one short line. Offer two ways: paste it, or give a path to a `.txt`. Mention `starter.txt` in this folder as a template if they have nothing yet. Do not dump the format spec at them. |
| `/resume-formatter` followed by pasted resume text | That is the input. Format it. |
| `/resume-formatter path/to/resume.txt` | Format that file. |
| `/resume-formatter letter` or `--page-size letter`, `left-aligned`, `no justify`, an output path | Treat trailing words as options and map them to the flags below. |
| "format this resume", "make this a docx", pasted resume-shaped text | Same as above; no slash command needed. |

If the text they give is clearly incomplete — no contact line, or no ALL-CAPS
sections — say what is missing and show the four-line shape rather than guessing
or inventing content.

## Procedure

1. Save the user's resume text to a `.txt` file exactly as given. Do not edit it.
   (`starter.txt` in the skill folder is a correctly shaped fictional example.)
2. Build. The script prints the path it wrote to on stdout:
   ```
   python3 scripts/build_resume.py resume.txt
   ```
   This creates a timestamped folder in the current directory and puts the
   document plus a copy of the source text inside it:
   ```
   Jordan Rivera's resume 17 August 2026 19-52/
   ├── Jordan Rivera - Platform Engineer.docx
   └── source.txt
   ```
   The name comes from line 1 of the input and the title from line 2 — never
   hardcode either. Use `-d DIR` to put the folder somewhere else, `--no-folder`
   for a bare `.docx`, or `-o PATH` for an exact filename.
3. Validate — always, it is fast and it is the guard against silent mangling:
   ```
   python3 scripts/validate.py "<the path build_resume.py printed>" -i resume.txt
   ```
4. Report the folder that was created, the page count if you rendered it, and
   anything the parser normalized (date separators, smart quotes, stripped
   bullet markers).
5. If validation fails, fix the cause and rebuild. Never hand over a document
   that failed validation.

No dependencies to install. Standard library Python 3.8+.

## Input contract

```
Full Name                          <- line 1
Target Job Title                   <- line 2
email@example.com | +1 555-0100    <- line 3 (email becomes a mailto: link)

PROFESSIONAL SUMMARY:              <- ALL-CAPS header, colon optional
•  bullet text
•  bullet text

TECHNICAL SKILLS:
Category: item, item, item         <- bold label, regular list, NOT a bullet

PROFESSIONAL EXPERIENCE:
Company, City ST | Month Year – Month Year
Job Title
Responsibilities:                  <- optional in input, always emitted
•  bullet text

EDUCATION:
•  Degree - Institution.
```

Tolerated automatically: `•  -  *  ·  tab` bullet markers, missing trailing
colons on headers, lowercase headers, `-` `--` `–` `—` `to` in date ranges
(all normalized to EN DASH), `Present` / `Current` / `Till Date`, missing
`Responsibilities:`, CRLF, smart quotes, non-breaking spaces and hyphens,
trailing whitespace, `&` `<` `>` in any text.

Section aliases recognized: `SUMMARY`, `WORK EXPERIENCE`, `EMPLOYMENT HISTORY`,
`SKILLS` and friends normalize to the four canonical headers. Any other
ALL-CAPS header (`CERTIFICATIONS`, `PROJECTS`, `SUMMARY OF QUALIFICATIONS`) is
kept, formatted as a section header, with its lines rendered as bullets.

## CLI

| Flag | Default | Effect |
|---|---|---|
| `input` | stdin | plain-text resume; `-` or omitted reads stdin |
| `-d, --outdir` | `.` | where the dated folder is created |
| `-o, --output` | — | exact `.docx` path; suppresses the dated folder |
| `--no-folder` | off | write the `.docx` straight into `--outdir` |
| `--no-source-copy` | off | skip the `source.txt` snapshot |
| `--page-size` | `a4` | `a4` (11906×16838) or `letter` (12240×15840) |
| `--no-justify` | off | left-align instead of `jc="both"` |
| `--font` | `Bookman Old Style` | body font |
| `--size` | `10` | body size in points |

### Output naming

- Folder: `{Name}'s resume {D Month YYYY} {HH-MM}` — e.g.
  `Jordan Rivera's resume 17 August 2026 19-52`
- Document: `{Full Name} - {Target Title}.docx`

Both take the name verbatim from line 1 of the input. The clock is written
`HH-MM`, not `HH:MM`: Windows forbids `:` in filenames and macOS Finder renders
it as `/`. Running twice in the same minute appends ` (2)`, ` (3)` — a previous
run is never overwritten.

Defaults reproduce the reference exactly. `validate.py` takes `--no-justify`
and `--indent` so its structural checks match a non-default build.

## Failure modes

The build exits non-zero, prints `error: ...`, and writes no file when:

- the input is empty, or has fewer than 3 non-empty lines before the first section
- a section header appears before the 3 header lines are complete
- content appears after the header block but before any section header
- there are no ALL-CAPS section headers at all
- `--size` is outside 4–72 pt

A half-written document is never left behind — output is written to a `.part`
file and renamed only on success.

If the user is on a machine without **Bookman Old Style** (it ships with
Microsoft Office; most Linux boxes and some Macs lack it), the `.docx` is still
correct — Word will use the real font wherever it exists. Viewers without it
substitute, which shifts metrics and can change the page count. Say so rather
than reporting it as a bug.

## Reference material

Load only when you need it:

- `reference/format-spec.md` — every formatting property with the XML that
  proves it. Read this before changing `build_resume.py`.
- `reference/resume-notes.md` — ATS safety, justification, A4 vs Letter, font
  availability, page-count behavior.
- `reference/make-template.py` — how `assets/template/` was derived from the
  original document and de-identified.

`assets/template/` is the reference `.docx` unpacked. `styles.xml`,
`numbering.xml`, `settings.xml`, `fontTable.xml` and `theme/` are copied into
every build untouched — never regenerate them.
