Skip to content
Automation
Skill

/tailor-resume

Choose the best existing resume base/variant for a job, or create a new tailored variant when nothing fits.

From plugin
jobpilot
7331 skills2 agents2 MCP
Install
$ npx -y skills add suxrobGM/jobpilot --skill tailor-resume --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/tailor-resume

Context preview

The summary Claude sees to decide when to auto-load this skill.

Choose the best existing resume base/variant for a job, or create a new tailored variant when nothing fits.

SKILL.md

tailor-resume.SKILL.md
name: tailor-resume
description: Choose the best existing resume base/variant for a job, or create a new tailored variant when nothing fits.
argument-hint: "<digest-json | job-url | pasted-jd-text> [--base <resumeId>]"

Tailor Resume - Reuse or Create

Choose or produce a resume for a specific job. You decide reuse vs create; the user does not pre-select.

Setup

Follow `../_shared/setup.md`. The profile response includes `resumes` (every base with `label`, `hasData`, `variantCount`, `isPrimary`).

Step 1: Build the JD object

Detect the argument shape:

  • Starts with `{` → parse as digest JSON. **No navigation, no snapshot.**
  • Starts with `http` → `browser_navigate`, then `browser_snapshot` the posting body (per `../_shared/browser-tips.md`) and build the digest (`../_shared/digest-schema.md`) from it.
  • Otherwise → pasted JD text; parse the same fields manually.

From the digest (`title`, `requirements[]`, `responsibilities[]`, `skills[]`, `yearsExperience`, `descriptionExcerpt`), assemble:

  • `title`, `domain` (fintech/healthtech/devtools/…), `standouts` (clearance, on-call, on-site, …).
  • `roleFamily` ∈ `frontend | backend | fullstack | mobile | data | ml | devops | qa | other` - match `title` + `descriptionExcerpt` against: frontend (`frontend`, `ui`, `react`, `vue`, `angular`), backend (`backend`, `api`, `services`), fullstack (`full-stack`), mobile (`ios`, `android`, `react native`, `flutter`), data (`data engineer/scientist`, `analytics`, `etl`), ml (`ml`, `ai engineer`, `mlops`), devops (`devops`, `sre`, `platform`, `infrastructure`), qa (`qa`, `sdet`, `test engineer`).
  • `seniority` ∈ `junior | mid | senior | staff | lead` - from title (`junior`/`entry` → junior; `senior`/`sr.` → senior; `staff` → staff; `lead`/`principal` → lead; else mid). Cross-check `yearsExperience`: 0-2 junior, 3-5 mid, 6-9 senior, 10+ staff/lead.
  • `keywords` - top 10 required skill terms from `skills` ∪ extracted from `requirements`. Lowercase, deduped, must-have ranked above nice-to-have.
  • `responsibilityTerms` - top 5 verbs/nouns from `responsibilities` (`design`, `mentor`, `migrate`, `on-call`, …).

Step 2: Pick the Base

**Campaign choice wins.** If `--base <resumeId>` was passed (the campaign's selected resume) and that resume has `hasData` or a `sourceFilename`, use it as `BASE_ID` (skip scoring). Else **primary wins**: if `primaryResumeId` is set and that resume has `hasData` or a `sourceFilename`, use it as `BASE_ID` (skip scoring; Step 3 extracts content if missing). Otherwise score each `resumes` entry (max 10):

| Signal | Points | Rule | | ------------------- | ------ | ----------------------------------------------------------------------------------- | | Exact role-family | +4 | `label` maps to `JD.roleFamily`. | | Adjacent family | +2 | frontend↔fullstack, backend↔fullstack, ml↔data, devops↔backend. Not both. | | `hasData: true` | +1 | Enables content scoring; cheaper to tailor. | | `isPrimary: true` | +1 | | | JD keyword coverage | +0..+3 | If `hasData`, fetch base; `round(3 × matched/10)` over skills + projects + summary. | | Recency | +1 | `updatedAt` within 90 days. |

Highest wins. Tie-break: primary → most recent → lowest id. If no candidate has `hasData` AND no `sourceFilename`, stop:

> No usable base resume. Upload a PDF at <$JOBPILOT_WEB/resumes>, or fill a resume's editor manually, then re-run.

Let `BASE_ID` be the chosen id.

Step 3: Extract Structure if Missing

curl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" "$JOBPILOT_API/api/resumes/$BASE_ID"

If `content` is `null`, delegate to extract-resume so the logic stays in one place:

> Run the `extract-resume` skill for `$BASE_ID` and wait for it to finish.

Refetch the base row afterward - Step 5 needs the saved `content`. If extract-resume stops because there's no `sourceFilename`, surface the same message and stop.

Skip this step when `hasData: true`.

**Check `profileMismatches` on the base response.** Non-empty means the recruiter reads one address and the form submits another. Echo once, don't block the apply:

> ⚠ resume disagrees with your profile: {field} says "{resume}", profile says "{profile}". Fix at $JOBPILOT_WEB/resumes/{baseId}

Step 4: Decide Reuse vs Create

curl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" "$JOBPILOT_API/api/resumes/$BASE_ID/variants"

**Shortlist first.** Rank the list response by title similarity and fetch `GET /api/resumes/variants/<id>` for the **top 5** only - a base with 60 variants would otherwise cost 60 fetches per job.

Compute `reuseScore` (0-100) for the shortlist. Variants failing the role-family gate (different family AND not adjacent) score 0. Skip `Suggested rewrite` variants - they are not tailored for any job.

| Component | Max | Calculation | | -------------------- | --- | ---------------------------------------------------------------------------------------------------- | | Keyword coverage | 40 | `40 × matched/10` of `JD.keywords` across skills + project keywords + summary + bullets. | | Title similarity | 20 | `20 ×` Jaccard token overlap of `JD.title` vs `variant.label`, stripping `engineer/senior/the/at/-`. | | Responsibility cover | 15 | `15 × matched/5` of `JD.responsibilityTerms` in summary + bullets. | | Seniority alignment | 15 | Exact 15; one step off (mid↔senior, senior↔staff) 8; further 0. | | Domain match | 5 | `JD.domai

Read more
Ships withjobpilot

An AI agent that applies to jobs for you, on the Claude or Codex subscription you already have.

Get the whole plugin

Other skills on jobpilot.