Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .claude/skills/intake-profile/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
name: intake-profile
description: Use when a user has no profile/master.json yet and needs to create one — bootstraps the canonical master profile either by interviewing the user (no existing resume) or by importing an old PDF/CV/LinkedIn export. Triggers on "set up my resume", "I'm starting from scratch", "import my old CV".
---

# Intake Profile

Produce `profile/master.json`: the candidate's full, truthful career history in
JSON Resume shape. This is the source of truth the `tailor-resume` skill draws
from.

## Choose a path

Ask the user which they have:
- **Nothing written down** → follow
`.claude/skills/intake-profile/references/interview-guide.md`.
- **An old resume/CV (PDF or DOCX) or a LinkedIn export** → follow
`.claude/skills/intake-profile/references/import-guide.md`.

## Rules

- Capture the FULL history (every role, project, skill) — `master.json` is the
superset; tailoring prunes later. Bias toward including more.
- Record only what the user actually did. Do not embellish. If a detail is
unclear (a date, a title), ask rather than guess.
- Match the field shapes in
`.claude/skills/tailor-resume/references/jsonresume-fields.md`.
- The honesty policy in
`.claude/skills/tailor-resume/references/tailoring-rules.md` also applies:
record only real facts, and never use `[VERIFY]` in the master — that marker
is only for tailored resumes.

## Finish

1. Write `profile/master.json`.
2. Run `npm run validate`. Fix any schema errors.
3. Tell the user the master is ready and they can now run `tailor-resume` with a
job description.
26 changes: 26 additions & 0 deletions .claude/skills/intake-profile/references/import-guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Import Guide (build master.json from an old CV / LinkedIn)

You can read PDFs and documents directly. Use that to extract a structured
master profile, then confirm with the user.

## Steps

1. **Read the source.** Open the user-provided PDF/DOCX resume or LinkedIn
"Export" (PDF or the `Profile.pdf`/CSV). Extract every role, project, skill,
and education entry you can find.
2. **Map to JSON Resume.** Populate `basics`, `work`, `projects`, `skills`,
`education`, `awards`, `volunteer` per
`.claude/skills/tailor-resume/references/jsonresume-fields.md`. Use `company`
and `website` field names to match the repo sample.
3. **Preserve facts exactly.** Copy dates, titles, employers, and metrics
verbatim from the source. Do NOT round, infer, or upgrade titles.
4. **Flag uncertainty.** If the source is ambiguous (e.g. a missing end date, an
unclear month), ask the user instead of guessing. Do not use `[VERIFY]` in
the master — that marker is only for tailored resumes.
5. **Fill gaps by asking.** If sections are thin (few bullets, no summary),
prompt the user to expand, the same way the interview guide does.

## Finish

Write `profile/master.json`, run `npm run validate`, and show the user a concise
summary of what you imported so they can correct anything.
25 changes: 25 additions & 0 deletions .claude/skills/intake-profile/references/interview-guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Interview Guide (build master.json from scratch)

Ask one topic at a time. Keep it conversational; don't dump every question at
once. Fill `profile/master.json` as you go.

## Order

1. **Basics** — full name, professional label/title, email, phone, website,
city + country, any profile links (GitHub, LinkedIn).
2. **Summary** — 2–3 sentences: who they are professionally and their focus.
3. **Work** — for each role (most recent first): company, position, start/end
dates (YYYY-MM), and 2–5 concrete accomplishments. Push for specifics:
numbers, scale, impact, technologies. One role at a time.
4. **Projects** (optional) — name, what it does, their role, tech, links.
5. **Skills** — group into named buckets (e.g. "Backend", "Cloud") with
keywords and an honest level.
6. **Education** — institution, area, study type, dates, notable courses/GPA.
7. **Awards / Volunteer** (optional) — title/org, date, one-line summary.

## Quality bar

- Prefer outcome-driven bullets ("cut build time 40%") over duties ("responsible
for builds").
- Never invent metrics. If they don't know a number, leave it qualitative.
- Capture everything now; relevance is decided later during tailoring.
43 changes: 43 additions & 0 deletions .claude/skills/tailor-resume/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
name: tailor-resume
description: Use when tailoring a resume to a specific job description — reads profile/master.json plus a JD and writes a pruned, JD-ranked resumes/<company>/resume.json with a suggest-but-mark [VERIFY] honesty policy. Triggers on "tailor my resume", "resume for this job", "apply to <company>".
---

# Tailor Resume

Turn a job description into a tailored resume that is a faithful, JD-ranked view
of the candidate's master profile.

## Required reading

Before producing anything, read these and follow them exactly:
- `.claude/skills/tailor-resume/references/tailoring-rules.md` (canonical behavior)
- `.claude/skills/tailor-resume/references/jsonresume-fields.md` (field shapes)

## Steps

1. **Locate the master.** Read `profile/master.json`. If it does not exist, stop
and tell the user to run the `intake-profile` skill first.
2. **Get the JD.** Accept pasted text or a file path. Identify the target
company/role and the key requirements (skills, seniority, domain, keywords).
3. **Pick a target folder.** `resumes/<company>/` using a kebab-cased
company-or-role name (e.g. `resumes/google/`). Confirm the name with the user
if ambiguous.
4. **Select & rank.** Choose the master entries (`work`, `projects`, `skills`,
etc.) most relevant to the JD; reorder so the strongest JD-relevant items lead.
5. **Rephrase truthfully.** Rewrite selected bullets toward the JD's language
without changing facts. Apply the suggest-but-mark `[VERIFY]` rule for any
plausible-but-unconfirmed addition.
6. **Write the files.** Create `resumes/<company>/resume.json` and
`resumes/<company>/config.json` (`{ "visibility": "public" }` by default).
7. **Validate.** Run `npm run validate`. Fix schema errors. If `[VERIFY]`
markers remain, list each to the user and explain they must confirm or delete
before pushing.
8. **Report gaps.** In chat, give a short "Gaps vs this JD" list of JD
requirements absent from the master — never write these into the resume.

## Done when

`resumes/<company>/resume.json` exists, `npm run validate` passes (or the only
blockers are `[VERIFY]` markers explicitly surfaced to the user), and the gaps
report has been delivered.
34 changes: 34 additions & 0 deletions .claude/skills/tailor-resume/references/jsonresume-fields.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Resume Fields Reference (engineering theme)

Resume documents follow the JSON Resume shape, validated by
`schema/resume.schema.json`. This repo's theme (`jsonresume-theme-engineering`)
accepts both legacy and modern field names — prefer the names already used in
`resumes/sample/resume.json` for consistency.

## Sections the theme renders

- `basics`: `name` (required), `label`, `email`, `phone`, `website`, `summary`,
`location { address, postalCode, city, countryCode, region }`, `profiles[]`.
- `work[]`: `company` (legacy) or `name` (modern), `position`, `website`/`url`,
`startDate`, `endDate`, `summary`, `highlights[]`.
- `volunteer[]`: `organization`, `position`, `startDate`, `endDate`, `summary`,
`highlights[]`.
- `education[]`: `institution`, `area`, `studyType`, `location`,
`specialization`, `startDate`, `endDate`, `gpa`, `courses[]`.
- `skills[]`: `name`, `level`, `keywords[]`.
- `awards[]`: `title`, `date`, `awarder`, `summary`.
- `projects[]`, `languages[]`, `interests[]` — supported, optional.

## Dates

Strings in `YYYY`, `YYYY-MM`, or `YYYY-MM-DD`. Omit `endDate` for current roles.

## HTML in text fields

`summary` and `highlights` strings may contain inline HTML — the theme renders
it. The sample uses `<strong>`, `<em>`, and `<a href="...">`. Use sparingly for
emphasis and links; never to fabricate structure.

## Validate before finishing

Always run `npm run validate` after writing a resume document.
63 changes: 63 additions & 0 deletions .claude/skills/tailor-resume/references/tailoring-rules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Tailoring Rules (canonical)

These rules govern how a tailored `resume.json` is produced from a master
profile and a job description (JD). Both the `tailor-resume` and
`intake-profile` skills follow them. They are the single source of behavioral
truth — do not restate or fork them elsewhere.

## Inputs

- `profile/master.json` — the candidate's full, truthful career history.
- A job description (pasted text or a file path).
- Target output directory `resumes/<company>/` (kebab-cased company/role).

## What you MAY do

- Select a subset of `work`, `projects`, `skills`, `volunteer`, `education`,
and `awards` entries from the master that are relevant to the JD.
- Reorder entries and bullet `highlights` so the most JD-relevant items come
first.
- Rephrase existing bullets to use the JD's terminology, provided the meaning
and the underlying fact are unchanged (e.g. "made services faster" →
"reduced p99 latency" ONLY if the master already states a latency result).
- Tighten the `basics.summary` to foreground JD-relevant strengths drawn from
the master.

## What you MUST NOT do

- Never invent or alter: employer names, job titles, dates, education,
certifications, or any metric/number not present in the master.
- Never silently add a skill, tool, or achievement the master does not contain.
- Never copy JD requirements into the resume as if they were the candidate's
experience.

## Suggest-but-mark (the one exception)

If the JD strongly implies an addition that is *plausibly* true but NOT in the
master, you may include it as a suggestion — but you MUST append the literal
marker ` [VERIFY]` to that string. Example:

"Led migration of core services to Kubernetes [VERIFY]"

`scripts/validate.mjs` fails the build if any `[VERIFY]` marker survives, so the
candidate is forced to confirm (remove the marker) or delete the line before it
can deploy. This is the only sanctioned way to introduce content not in the
master.

## Gaps report (not in the JSON)

When the JD asks for things the master lacks, do NOT put them in `resume.json`.
Instead, after writing the file, report them to the user in chat as a short
"Gaps vs this JD" list (e.g. "JD wants Kubernetes + Go; your master shows
neither — add them to master.json if you have them"). The resume file must
contain only resume content, never commentary.

## Output contract

- Write `resumes/<company>/resume.json` — schema-valid per
`schema/resume.schema.json` (see `jsonresume-fields.md`).
- Write `resumes/<company>/config.json` as `{ "visibility": "public" }` unless
the user asks for `private`.
- After writing, run `npm run validate` and report the result. If it fails,
fix the file (or surface remaining `[VERIFY]` markers to the user) before
finishing.
16 changes: 15 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,23 @@ jobs:
"${{ github.event.after }}" \
"${{ github.event.inputs.file_path }}" >> $GITHUB_OUTPUT

validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
- name: Install dependencies
run: npm ci
- name: Validate resume documents
run: npm run validate
- name: Run unit tests
run: npm test

build:
runs-on: ubuntu-latest
needs: detect-changes
needs: [detect-changes, validate]
if: ${{ needs.detect-changes.outputs.build_resumes != '[]' }}
strategy:
matrix:
Expand Down
20 changes: 20 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Validate Resumes

on:
pull_request:
workflow_dispatch:

jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
- name: Install dependencies
run: npm ci
- name: Validate resume documents
run: npm run validate
- name: Run unit tests
run: npm test
43 changes: 43 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# CLAUDE.md — resume-as-code conventions

This repo is a git-driven multi-resume publisher. AI authoring sits on top of it
and must not change the build/deploy pipeline.

## Source of truth

- `profile/master.json` is the candidate's full, truthful history. It is the
ONLY place facts originate.
- Each `resumes/<company>/resume.json` is a tailored, JD-ranked VIEW of the
master. Tailored resumes are derived, never the source.

## Skills

- `intake-profile` — build `profile/master.json` (interview or import a PDF/CV/
LinkedIn export).
- `tailor-resume` — turn a JD + the master into `resumes/<company>/resume.json`.
- Canonical behavior lives in
`.claude/skills/tailor-resume/references/tailoring-rules.md`. Read it before
editing resume content.

## Honesty policy (enforced)

- Never fabricate employers, titles, dates, education, or metrics.
- Plausible-but-unconfirmed additions must be tagged ` [VERIFY]`.
- `npm run validate` fails on schema errors and on any surviving `[VERIFY]`
marker, so unverified claims cannot deploy.
- The gate runs on the standard push/PR path (`build` depends on `validate`); it
assumes resumes reach `main` through that flow, not via out-of-band manual job
runs.

## Layout

- `schema/resume.schema.json` — validation schema (lenient; accepts legacy
`company`/`website` and modern `name`/`url`).
- `scripts/validate.mjs` + `scripts/lib/` — validator (run `npm run validate`).
- `scripts/build-resume.sh`, `scripts/deploy-resume.sh`,
`scripts/delete-repo.sh`, `.github/workflows/ci.yml` — the publish pipeline.
Do not modify when working on authoring features.

## Before finishing any resume change

Run `npm run validate` and `npm test`.
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,31 @@ Check your Actions tab to see if its running correctly.

![resume](assets/resume.gif)

## AI Authoring (Optional)

Generate a tailored resume from a job description using Claude Code (works great
in Codespaces — no API key needed).

1. **Build your master profile once.** Run the `intake-profile` skill. It either
interviews you or imports an old PDF/CV/LinkedIn export into
`profile/master.json` — your full, truthful career history. (See
`profile/master.example.json` for the shape.)

2. **Tailor to a job.** Run the `tailor-resume` skill with the job description.
It writes `resumes/<company>/resume.json` as a ranked, JD-focused view of your
master, and reports any gaps between the JD and your experience.

3. **Honesty gate.** The skills never invent facts. Anything plausible but
unconfirmed is tagged `[VERIFY]`; `npm run validate` (run automatically in CI)
fails until you confirm or remove it — so nothing unverified is ever
published.

4. **Push as usual.** Pushing `resumes/<company>/resume.json` triggers the normal
build and deploy.

See `examples/` for a full worked example (master profile → JD → tailored
resume). Validate any resume locally with `npm run validate`.

## Advanced (Optional)

Want to preview your resume before pushing?
Expand Down
Loading
Loading