add-api-route
Add a new JobPilot API route or module the standard way - contracts schema, controller, service, response schema, optional web hook. Use for "add an endpoint",…
Choose the best existing resume base/variant for a job, or create a new tailored variant when nothing fits.
$ npx -y skills add suxrobGM/jobpilot --skill tailor-resume --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/tailor-resumeContext 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.
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>]"
Choose or produce a resume for a specific job. You decide reuse vs create; the user does not pre-select.
Follow `../_shared/setup.md`. The profile response includes `resumes` (every base with `label`, `hasData`, `variantCount`, `isPrimary`).
Detect the argument shape:
From the digest (`title`, `requirements[]`, `responsibilities[]`, `skills[]`, `yearsExperience`, `descriptionExcerpt`), assemble:
**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.
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}
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
An AI agent that applies to jobs for you, on the Claude or Codex subscription you already have.
Repo: suxrobGM/jobpilot
Add a new JobPilot API route or module the standard way - contracts schema, controller, service, response schema, optional web hook. Use for "add an endpoint",…
Review and clean up the given file(s)/folder(s)/module(s): rate organization, find dead code, duplication, coupling, over-engineering, deep nesting, structural…
Create and apply a Prisma migration safely against the remote (tunneled) PostgreSQL. Use for schema changes - "create a migration", "apply migrations", "add a…
Add knip to a TypeScript project and use it to remove dead exports, collapse pass-through barrels, and narrow every export to what another file actually…
Bump the unified JobPilot version (host + plugin), update the changelog, commit, and tag a new release
Rebuild the .NET terminal host, restart it, and wait for /healthz. Use after any C# change under apps/terminal, or when asked to "restart the terminal host".