resume manages a personal resume as version-controlled structured data (JSON Resume v1 plus an x_linkedin extension). Storage is one YAML file per entity; git is a first-class primitive, so every mutation auto-commits. The sections below group the commands by task. For each command you get its purpose, the key usage, and the flags that matter most.
The CLI is agent-first and deterministic — no prompts, no editor, no network calls. Every command accepts a global --json flag (or set RESUME_JSON=1 in the environment) to switch output to a structured envelope: {"ok":true,"data":{...}} on stdout for success, {"ok":false,"error":{"code":"...","message":"..."}} on stderr for failure. Run resume cheatsheet to print the exhaustive in-binary reference (the same content an agent harness loads).

Conventions

  • A <section> is one of the 13 section names: basics, x_linkedin (singletons), work, education, volunteer, awards, publications, certificates, projects (ordered, newest-first), and skills, languages, interests, references (unordered, alphabetical).
  • A <spec> is <section> for singletons (e.g. basics) or <section>/<slug> for multi-entity sections (e.g. work/anthropic).
  • --require-clean (global) refuses any mutation when the working tree is dirty.
  • Mutations serialize via an advisory lock at .resume-as-code/lock; tune the wait with RESUME_LOCK_WAIT_MS (default 5000ms).
Stable exit codes: 0 success; 1 VALIDATION / ALREADY_EXISTS / E_ENTITLEMENT / E_LICENSE_INVALID; 2 NOT_FOUND; 3 GIT; 4 IO; 64 USAGE; 70 INTERNAL. The envelope’s error.code disambiguates the codes that share an exit number.

Setup & first run

resume bootstrap

A read-only first-run readiness report: it inspects the environment and the current repo and tells you whether everything for the workflow is in place — whether you are inside a resume repo, whether git is initialized, whether this is a fresh first run, and which optional rendering/import dependencies are available. It performs no mutation and always exits 0; findings live in the envelope, not the exit code. It also works outside any repo (reports inRepo: false).
# Readiness report at the start of a first session (what an agent runs)
RESUME_JSON=1 resume bootstrap

# Human-readable check
resume bootstrap

# Pre-download the pinned Chrome used by PDF export (the only networked action; idempotent)
resume bootstrap --ensure-chrome
Key envelope fields: inRepo, repoRoot, gitInitialized, firstRun, a dependencies block (pdfImport, pdfExport, docx, ocr), healthy, and recommendations. pdfImport.ok is always true — the binary bundles an unpdf fallback when pdftotext (Poppler) is absent.

resume init

Scaffold a new resume-as-code repo. Creates resume/, a starter resume/basics.yaml, .gitignore, and the agent-bootstrap files CLAUDE.md + AGENTS.md; initializes a git repo if one does not exist and makes the seed commit. Refuses to scaffold into a non-empty directory (only pre-existing .git* entries are allowed).
resume init                  # scaffold in the current directory
resume init ~/code/my-resume # scaffold in a subdirectory
The seed entity is basics: { name: "Your Name" } — edit it with resume edit basics --field name="...".

resume init-skill

Install the bundled agent skill (SKILL.md + companion docs + examples/) into an agent harness so the agent automatically knows how to drive the CLI. Human-facing setup — the agent never calls this itself.
resume init-skill                                   # install for Claude Code (default target)
resume init-skill --force                            # refresh an existing install
resume init-skill --dir ./my-agent/skills/resume-as-code  # custom destination
Flags: --target <harness> (default claude-code, which installs to ~/.claude/skills/resume-as-code), --dir <path> (explicit destination, overrides --target), --force (overwrite a non-empty destination).

resume cheatsheet

Print the embedded comprehensive usage doc to stdout — the canonical agent-facing reference baked into the binary. With --json, the text is wrapped in an envelope under data.cheatsheet. Does not require a repo.
resume cheatsheet
resume cheatsheet | less
resume cheatsheet --json | jq -r '.data.cheatsheet'

Resume data

resume add

Add a new entity to a section, validate it against the section schema, write it as YAML, and auto-commit. The slug is derived from a name-like field (name, title, organization, institution, or language) unless overridden with --slug. For ordered sections, the start-date field becomes a <YYYY>- or <YYYY-MM>- filename prefix so listings stay chronological.
resume add work --data '{"name":"Anthropic","position":"Founder","startDate":"2024-01"}'
echo '{"name":"TypeScript","level":"Expert"}' | resume add skills --from-stdin --json
resume add languages --field language=Spanish --field fluency=Conversational
resume add work --slug stripe-platform --data '{"name":"Stripe","startDate":"2021-03"}'
Exactly one input mode is required: --data <json>, --from-stdin, or one-or-more --field key=value. --field values starting with [ or { are JSON-parsed (arrays/objects); all others are strings — for booleans or numbers use --data or --from-stdin. Adding a duplicate slug, or a singleton that already exists, fails with ALREADY_EXISTS.

resume edit

Edit an existing entity. --data / --from-stdin replace the stored entity entirely (omitted fields are removed); --field shallow-merges the supplied keys (other fields preserved). The merged result is validated, written atomically, and auto-committed. edit is idempotent — a byte-identical result makes no commit and returns changed: false.
resume edit work/anthropic --field position="Member of Technical Staff"   # shallow merge
resume edit basics --field name="Yev Getman" --field email="y@example.com"
echo '<full-json>' | resume edit work/anthropic --from-stdin               # full replacement
Never edit YAML files by hand — always use resume edit or resume add. The CLI’s writer auto-quotes strings containing colon-space (: ); a hand-edit of such a string makes YAML parse it as a mapping, breaking validation and the preview server.
Editing the start-date of an ordered section does not rename the file (auto-rename is deferred); when the <YYYY[-MM]>- prefix goes stale the envelope carries a warnings array explaining how to re-sort (resume mv, or remove + re-add).

resume rm

Remove an entity from disk and auto-commit the deletion.
resume rm work/anthropic
resume rm skills/typescript --json
basics is required by the schema and cannot be removed (rm basicsUSAGE); the optional x_linkedin singleton is removable.

resume show

Print a single entity. Human mode renders YAML to stdout; JSON mode puts the entity in data with the absolute file path as a sibling path field. Read-only — no commit, no schema validation.
resume show basics
resume show work/anthropic --json

resume list

List entities. Without an argument, lists every entity in every section; with a section name, lists only that section. Ordered sections are newest-first; unordered are alphabetical. Human mode skips empty sections; JSON mode includes every section (empty arrays for those with none).
resume list
resume list work --json | jq '.data.work[].slug'

resume mv

Rename a non-singleton entity in place — same section, new slug, with the existing <YYYY[-MM]>- prefix preserved for ordered sections. The rename uses git mv so history follows the file. If the normalized new slug equals the current one, it is a no-op (renamed: false, commit: null).
resume mv work/anthropic claude
Singletons cannot be renamed; cross-section moves are not supported (the second argument is a bare slug; the section is fixed by <spec>). A collision with an existing entity fails with ALREADY_EXISTS.

resume validate

Walk the entire resume/ tree, parse every YAML file, and validate the assembled document against the root schema (which requires basics). Reports per-section entity counts. The right command to gate a commit in CI.
resume validate
resume validate --json   # {"valid":true,"ok":true,"data":{"counts":{"basics":1,"work":3}}}

resume schema

Emit a JSON Schema for the whole resume (no argument) or a single section’s entity shape. This is the self-discovery surface — the schema is the contract add and edit enforce. schema always produces JSON; --json only controls whether it is wrapped in the success envelope.
resume schema             # full root schema
resume schema work        # one section
resume schema work --json | jq '.data'
An agent should call resume schema <section> before constructing a --data payload. The output may be wrapped under definitions.<name> (a zod-to-json-schema quirk) — tolerate both shapes with schema.definitions?.[name] ?? schema.

resume build

Assemble the canonical JSON Resume document: load every section, validate against the root schema, and write pretty-printed JSON. Default output is dist/resume.json (gitignored). build does not auto-commit the artifact, and empty sections are omitted from the document.
resume build
resume build -o /tmp/me.json --json
Flag: -o, --output <path> (default <repo>/dist/resume.json).

Import & export

resume import

Three modes, dispatched by the detected (or --format-overridden) input type and flags:
  1. Text extraction (PDF/DOCX/HTML/TXT/MD) — extract text into a structured envelope with sectionHints for the agent to consume. PDFs use pdftotext (Poppler) with an unpdf fallback, auto-falling-back to tesseract OCR for thin-text PDFs.
  2. Structured import (.json matching JSON Resume v1, or a LinkedIn data-export .zip containing Profile.csv) — write entities directly to the tree, one commit per entity.
  3. Mechanical structuring (--structure on a text format) — extract, then deterministically structure the text into schema entities (no LLM) and write them.
resume import resume.pdf
RESUME_JSON=1 resume import scanned.pdf --stage
RESUME_JSON=1 resume import resume.json                 # structured import
RESUME_JSON=1 resume import linkedin-export.zip --prefix linkedin
RESUME_JSON=1 resume import resume.json --dry-run        # preview without writing
RESUME_JSON=1 resume import resume.txt --structure --overwrite
Key flags: --stage (also write the full envelope to .resume-as-code/staging/<id>.json), --no-ocr / --ocr-threshold <n> (control the OCR auto-fallback; 0 disables it), --format <fmt> (override detection: pdf, docx, html, txt, md, json-resume, linkedin-export), and for structured/--structure writes: --overwrite, --dry-run, --prefix <slug>. Structured imports require a resume/ repo; per-entity failures are reported in data.failed and do not abort the run.

resume export

Emit the canonical document, optionally filtered, in several formats. Read-only — no commit, no lock, --require-clean is a no-op. export is the surface for “give me a tailored slice”; build always emits the full doc.
resume export                                         # full doc as JSON to stdout
resume export --since 2020 --json
resume export --audience tech --json
resume export --include basics,work --exclude skills --json
resume export --format yaml -o /tmp/me.yaml
resume export --format html --theme compact -o ~/Desktop/resume.html
resume export --format pdf                             # default theme -> dist/<First>_<Last>_Resume.pdf
resume export --format pdf --theme compact --page-size a4
resume export --format docx -o ~/Desktop/resume.docx   # via pandoc (must be on PATH)
resume export --format html --branch tailor/acme-corp  # render a branch read-only (HEAD unchanged)
Flags: --include <list> / --exclude <list> (comma-separated sections), --since <YYYY[-MM]> (drops earlier entries from ordered sections only), --audience <tag> (keeps entities whose audiences[] contains the tag; untagged entities are universal), --format json|yaml|html|pdf|docx (default json), --theme <name> (HTML/PDF/DOCX; bundled default/compact, an jsonresume-theme-* npm package, or a local path), --page-size letter|a4|legal (PDF only), --branch <ref> (render from a git ref without checkout), and -o, --output <path>. The first PDF render downloads pinned Chrome for Testing (~170MB to ~/.cache/puppeteer/); progress goes to stderr.
Tier behavior: Free PDF/HTML exports carry a discreet footer watermark (Pro removes it). On Free, DOCX export, --page-size, and the filter flags (--include/--exclude/--since/--audience) are Pro-only and return a non-fatal E_ENTITLEMENT envelope rather than being silently ignored. The watermark is applied to exported artifacts only — never the live preview server.

resume themes

List themes available for export: local forks (<repo>/themes/<name>/), bundled (default, compact), and jsonresume-theme-* npm packages discoverable from the cwd. Read-only; works from any directory. Local forks shadow same-named bundled/npm themes during --theme resolution.
resume themes
RESUME_JSON=1 resume themes | jq '.data.themes[].name'

resume theme fork

Copy a theme into the resume repo at <repo>/themes/<slug>/ so it can be edited locally. The fork is version-controlled with the resume, shadows same-named bundled/npm themes, carries a .resume-fork.json provenance marker, and auto-commits.
resume theme fork default                  # copy bundled default to <repo>/themes/default/
resume theme fork default --as my-default  # copy to a different slug
resume theme fork elegant --as cv          # fork an npm theme (jsonresume-theme-elegant)
Flags: --as <slug> (local slug, normalized; defaults to the source name), --force (overwrite an existing fork). After forking, edit index.js (HTML) and style.css directly, then render with resume export --theme <slug>.

resume preview

Run a local HTTP preview server bound to 127.0.0.1 (default port 4444, not 4000). Foreground process — Ctrl+C to stop. The theme/resume views are read-only, but the /jobs tracker page is editable in the browser (add a job, change status, edit fields, append notes, delete) — those edits commit to the orphan jobs branch exactly like the CLI.
resume preview
resume preview --port 5000 --no-open
RESUME_JSON=1 resume preview --no-open --no-watch
Flags: --port <n>, --no-open (don’t open a browser tab), --no-watch (disable live reload), --theme <name>. Routes include /, /preview/:theme, /pdf/:theme, /resume.json, /jobs, /api/branches, /api/jobs, and /events (SSE). Filter query params (audience, since, include, exclude, pageSize, branch) apply to the render routes.
The preview server does not hot-reload its own source. After changing src/preview/, restart the server and hard-refresh the browser.

Versions & history

resume branch

Create a new git branch for a resume variant: normalize the name, create and check out the branch, write lineage metadata to .resume-as-code/branch-meta.yaml, and auto-commit. Use the role/<role-type> convention for a reusable role-positioned variant.
resume branch role/engineering-leadership
resume branch tailor/acme-staff-eng --from main
Flag: --from <ref> (source branch; default is the current branch). Always branch variants --from main so global changes can be merged down into each variant.

resume branches

List the resume variant branches (main plus tailor/* and other variants) as a hierarchy, each with its role label (resolved from basics.label, falling back to basics.name). Read-only — never checks out, creates, or modifies a branch. The orphan jobs store branch is excluded.
resume branches
RESUME_JSON=1 resume branches

resume history

Print the git commit log for one entity, scoped to its file and following renames (git log --follow). Read-only. Newest-first; SHAs are abbreviated to 7 chars and can be passed straight to resume revert.
resume history work/anthropic
resume history work/anthropic --limit 5 --json
resume history basics --json
Flag: --limit <n> (default 20; must be a positive integer).

resume revert

Restore a single entity to its content at a prior git ref: read git show <ref>:<path>, validate against the current schema, write atomically, and auto-commit. Idempotent — reverting to the on-disk state makes no commit. If the schema has evolved and the historical content no longer parses, it refuses with VALIDATION (reconcile manually via resume edit <spec> --from-stdin).
resume revert work/anthropic              # default ref: HEAD~1
resume revert work/anthropic abc1234 --json
resume revert basics resume-baseline
Argument: <git-ref> (optional; default HEAD~1; accepts a SHA, tag, HEAD~N, or branch).

resume snapshot

Create an annotated git tag at HEAD. The label is normalized and prefixed with resume- to namespace it from other tags. Refuses with ALREADY_EXISTS on a duplicate. Does not honor --require-clean — tagging is independent of working-tree state.
resume snapshot baseline
resume snapshot before-acme -m 'before applying to ACME role' --json
Flag: -m, --message <msg> (default snapshot at <ISO-date>). Tags are local until pushed (git push --tags); list them with git tag --list 'resume-*'.

Jobs & matching

Job applications live on a dedicated orphan jobs git branch, checked out in a linked worktree at .resume-as-code/jobs-wt/jobs/<slug>/ — durable across every resume branch. Manage them through the jobs subcommands; do not read or write the YAML directly. Unlike resume add, jobs add auto-disambiguates slugs on collision rather than failing.

resume jobs add

Save a new job application (company + role, plus an optional job description and a rich set of metadata fields). When a JD is supplied, the CLI parses it deterministically (keyword extraction, requirement classification) and stores the result.
resume jobs add --company "Acme" --role "Staff Engineer"
resume jobs add --company "Acme" --role "Eng" --jd path/to/jd.txt
resume jobs add --company "Acme" --role "Staff Eng" --seniority staff \
  --employment-type full_time --work-arrangement remote
Required: --company, --role. JD input (mutually exclusive): --jd <path>, --jd-text <text>, --stdin. Common extras: --location, --tags <tags...>, --source-url, --seniority, --department, --team, --employment-type, --work-arrangement (with --work-detail/--timezone/--office-days), --posted-at, --expires-at, --headcount, --visa-sponsorship/--no-visa-sponsorship, --travel, --interest, --metadata <json>, and --resume-branch (usually set automatically by jobs tailor). See resume cheatsheet or the full reference for every flag.

resume jobs edit

Edit a job application — partial merge of the specified fields. Idempotent (no-op if nothing changes); auto-commits.
resume jobs edit acme-staff-engineer --role "Staff Software Engineer (L6)"
resume jobs edit acme-staff-engineer --add-tag remote-ok
resume jobs edit acme-staff-engineer --seniority staff --work-arrangement hybrid --office-days 3
resume jobs edit acme-staff-engineer --data '{"location":"Remote","source_url":"https://jobs.example/123"}'
Accepts the same field flags as jobs add, plus --add-tag/--remove-tag. --data <json> applies a JSON patch via the shared merge path (mutually exclusive with the field flags; can set fields the flags cannot, notably source_url). See resume cheatsheet for the full flag table.

resume jobs delete

Delete a job application and all its files. Two-pass: without --confirm it previews what would be deleted; with --confirm it deletes and auto-commits.
resume jobs delete acme-staff-engineer            # preview
resume jobs delete acme-staff-engineer --confirm  # actually delete

resume jobs list

List tracked jobs. Read-only.
resume jobs list
resume jobs list --status active
resume jobs list --tag remote --added-within 30d
resume jobs list --format pipeline
Filters: --status <state> (a state name, or the meta-filters active / terminal), --tag <tag>, --added-within <dur>, --updated-within <dur>. Format: --format brief|full|json|pipeline (pipeline groups jobs by state in state-machine order).

resume jobs show

Show full detail on one job (record, status history, notes). Read-only.
resume jobs show acme-staff-engineer

resume jobs status

Show or update a job’s status. Without --set, read-only; with --set, transition the state machine and auto-commit. States: saved → applied → screening → interviewing → offer → accepted (with declined off offer); any non-terminal can go to withdrawn / rejected / closed.
resume jobs status acme-staff-engineer                                  # read
resume jobs status acme-staff-engineer --set applied --note "Submitted"
resume jobs status acme-staff-engineer --set interviewing
Flags: --set <state>, --note <text>, --variant <branch> (records the variant sent on the history entry), --file <path>.

resume jobs note

Append a timestamped note to a job. Auto-commits to the jobs branch.
resume jobs note acme-staff-engineer "Recruiter mentioned 2-week timeline"

resume jobs remind

Add, list, or clear reminders for a job. Adding requires --note plus one of --at or --in.
resume jobs remind acme-staff-engineer --in 7d --note "Follow up"
resume jobs remind acme-staff-engineer --at "2026-06-01T09:00:00Z" --note "Check in"
resume jobs remind acme-staff-engineer --list
resume jobs remind acme-staff-engineer --clear
Flags: --at <timestamp>, --in <duration>, --note <text> (required on add), --list, --clear.

resume jobs history

Show a job’s git commit history on the jobs branch (changes to job.yaml / status.yaml / notes.md / analyses/). The jobs-branch analogue of resume history. Read-only.
resume jobs history acme-staff-engineer
resume jobs history acme-staff-engineer --limit 10
Flag: --limit <n> (default 50).

resume jobs tailor

Create (or adopt) a tailored resume branch for a job, link it (set resume_branch), and check it out — in one step. This is the blessed way to start tailoring: because the link is established at tailor time, resume match --job <slug> automatically scores against the tailored resume.
resume jobs tailor acme-staff-engineer
resume jobs tailor acme-staff-engineer --from main --name tailor/acme-v2
Flags: --from <ref> (base; default current branch), --name <branch> (default tailor/<slug>). If the branch exists it is adopted (no error); either way job.resume_branch is set.
Never tailor a resume on the current branch — always branch first (jobs tailor for a tracked job, or resume branch for a variant not tied to a job). After tailoring, confirm whether the user actually applied before setting status to applied.

resume jobs sync

Sync job applications with the cloud queue. Not yet implemented — requires a resume-as-code cloud account.
resume jobs sync

resume match

Match a resume against a job description via deterministic keyword extraction, classification, and coverage analysis. The CLI emits a structured envelope (keyword_score, keywords_present, keywords_missing, near_misses); the agent performs the semantic pass on top.
resume match --job acme-staff-engineer            # scores the job's linked tailored resume
resume match --jd path/to/jd.txt --no-save        # ad-hoc JD from a file
resume match --all                                # rescore every tracked job with stored JD text
JD source (exactly one): --job <slug> (uses stored JD; scores the linked resume_branchtailor/<slug> → base HEAD), --jd <path>, --stdin, or --all. Options: --branch <name> (default HEAD), --label <name>, --no-save, --render (human-readable output). With --job, the analysis is saved under the job’s analyses/ dir and the job record’s latest_analysis is updated.

Licensing & bridge

resume license

Inspect or change the Free/Pro tier. resume runs Free by default and unlocks Pro with a license; daily verification is offline (an embedded Ed25519 public key — no network call during normal use). Human-facing setup — an agent doesn’t call license itself, but should recognize the non-fatal E_ENTITLEMENT envelope and offer the upgrade at https://resume-as-code.ai/pro.
resume license status
resume license activate POLAR-XXXX-...   # store key from checkout (exchanged for a token)
resume license activate eyJ...           # or a raw signed Pro token (verified offline)
resume license refresh                   # re-validate (online if activated from a store key)
resume license deactivate                # back to Free
Subcommands: status (tier, sku, expiry, source env/file/none; always exits 0), activate <key> (auto-detects a store key vs. a raw token; writes ~/.config/resume-as-code/license.json; invalid → E_LICENSE_INVALID), deactivate (revert to Free; idempotent), refresh (re-validate; online when activated from a store key). Source precedence: a RESUME_LICENSE_KEY env token overrides the cached file, which overrides Free.

resume serve —bridge

Run a localhost (127.0.0.1) token-gated bridge for a paired browser extension that fills job-application forms from resume-as-code data. Reuses the preview-server core and adds three token-gated /api/* endpoints. Foreground (Ctrl+C); read-only with respect to resume/.
resume serve --bridge --print-token       # print the pairing token and exit (no port bound)
resume serve --bridge                       # start on 127.0.0.1:4444 (falls back if busy)
resume serve --bridge --port 4571 --no-open
Flags: --port <n>, --no-open, --print-token. A stable pairing token is generated once at ~/.config/resume-as-code/bridge.json; every /api/* request requires the X-Bridge-Token header. Endpoints: GET /api/profile?branch=<ref> (resume JSON for a variant), GET /api/apply-profile (the application profile; sensitive — never served without the token), and POST /api/map-fields (a 3-tier field-to-value mapper: ATS adapters live in the extension, a deterministic heuristic in the CLI, and an opt-in Claude fallback that uses ANTHROPIC_API_KEY set only in the CLI).

resume apply-profile

Manage the application profile — reusable answers to the boilerplate a job-application form asks that are not part of the resume: work authorization, sponsorship needs, voluntary EEO self-ID, salary expectation, references, availability, and site-specific answers. Stored as one versioned YAML file, validated, auto-committed, and branch-aware.
resume apply-profile set --field work_authorization='US citizen' --field requires_sponsorship=false
resume apply-profile set --data '{"salary_expectation":{"min":180000,"max":220000,"currency":"USD"}}'
RESUME_JSON=1 resume apply-profile get
Subcommands: get (print the profile; {} when unset) and set (merge fields, validate, write, auto-commit — via --field k=v with dot-paths, --data <json> deep-merge, or --from-stdin).
The application profile is never exported. It is not a resume section — resume build and resume export read only resume/** and never include this data, so these sensitive answers cannot leak into a resume you send out. It reaches a consumer only via the token-gated GET /api/apply-profile on the bridge.
New to resume-as-code? Start with Getting started for the guided first-run setup. For the exhaustive reference — every flag and every envelope shape — run resume cheatsheet.