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",…
Write a tailored, concise cover letter (150-250 words) from a job description and the user's resume, humanized for natural tone.
$ npx -y skills add suxrobGM/jobpilot --skill cover-letter --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/cover-letterContext preview
The summary Claude sees to decide when to auto-load this skill.
Write a tailored, concise cover letter (150-250 words) from a job description and the user's resume, humanized for natural tone.
name: cover-letter description: Write a tailored, concise cover letter (150-250 words) from a job description and the user's resume, humanized for natural tone. argument-hint: "<job_description>"
Write a short, tailored cover letter connecting the candidate's resume to a specific role.
Follow `../_shared/setup.md` to load profile and resume. That structured resume is what you write from - experience, projects, skills, education, publications, awards, certifications and any custom sections are all in it. Don't also `Read` the source PDF: it's the same content again at several times the size, and inside an apply flow that cost is paid per job.
From the argument, identify: company + what they do, role title and level, key responsibilities, required/preferred qualifications, tech stack and domain, culture cues.
Do not skip. Letters that are individually fine and collectively identical are what reads as AI - not word choice.
curl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" "$JOBPILOT_API/api/cover-letters?page=1&pageSize=5" | jq -r '.items[].id'
The list is metadata only, so `GET /api/cover-letters/<id>` each for the body. No history (first letter, or the call fails) → Step 3.
Take three things from each and drop the rest: which experience it **led** with, its metric phrasings, and its closing sentence. Those are what rules 14-16 compare against; keeping five whole letters in context buys nothing beyond them.
From the resume, pick the most relevant: 2–3 work experiences, 2–3 projects, research (if AI/ML/CV), education (if relevant to level).
**Apply rules 14-16 here**, against Step 2's letters. Rotating the lead is a selection decision - make it before drafting, not by patching a draft that already opened with the usual paragraph.
**Header** - values strictly from `user.*`, never from the resume file (resumes carry stale addresses). Omit any line whose fields are empty:
[Full Name] [City, State] | [Phone] | [Email] [LinkedIn] | [GitHub] | [Website]
**Body - 150–250 words, 3–4 paragraphs of uneven length.**
Pick ONE lead angle - the strongest for this JD, not a stack of all three:
Then write:
If the draft came out as opening → experience → tech depth → why-company → closing, that's the template every AI letter follows - merge or cut a section and vary where the company mention lands.
**Sign-off:**
Best regards, [Full Name]
Invoke the `humanizer` skill on the full text in **embedded mode** - final rewrite only, no draft or audit bullets.
Then re-check against Step 2's letters: no shared sentence, different lead, different closing. The humanizer sees one document at a time, so this check is yours.
Persist the final letter so it's reviewable in the web app. Best-effort - if the call fails, continue:
curl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" -X POST "$JOBPILOT_API/api/cover-letters" -H 'content-type: application/json' \
-d "$(jq -n --arg c "<final letter text>" --arg u "<job url>" --arg t "<role title>" --arg co "<company>" --arg s "<source>" \
'{content:$c, jobUrl:($u|select(.!="")), jobTitle:($t|select(.!="")), company:($co|select(.!="")), source:$s}')"`jobUrl`/`jobTitle`/`company` come from the JD argument (`$DIGEST` fields when present). `source` is the invoking context - `apply`, `auto_apply`, or `manual` (default `manual` when the caller didn't specify).
1. **150–250 words for the body.** Short reads as confident. 2. **No fluff** - drop "passionate", "dedicated", "committed", "excited", "thrilled", "leverage", "utilize", "innovative", "cutting-edge", "eager", "dynamic". 3. **No generic openings.** 4. **Be specific.** Real project names, metrics, technologies from the resume. 5. **Tailor by selection, not narration.** Prove fit by choosing the right material. Quote back at most ONE short JD phrase in the whole letter. Never end a paragraph by mapping yourself onto the JD - no "which is exactly what your team needs", "the same kind of X that Y requires", "which is basically the job description". 6. **End paragraphs on the fact** - what you did or why you're interested - not a tie-back or a flourish. 7. **One dash clause max** per letter (em dash or " - "); restructure the rest into commas or separate sentences. 8. **One contrast max** per letter - "X rather than Y" / "not X, but Y" is a crutch; usually delete the negative half. 9. **No rule-of-three.** Don't default to three-item lists; name one thing, or two, or four. 10. **Show, don't tell.** No "I'm a strong communicator" - demonstrate it through the writing. 11. **Match tone.** Startup → conversational; enterprise/gov → formal. 12. **No fabrication.** Only reference projects/skills from the resume. 13. **First person** as the candidate.
14. **Rotate the lead.** Don't open with the experience either of the last two letters opened with, unless the JD makes it the only honest choice (a healthcare role genuinely wants the healthcar
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".