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 short, targeted Upwork proposal from a job description and the user's resume, humanized for natural tone.
$ npx -y skills add suxrobGM/jobpilot --skill upwork-proposal --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/upwork-proposalContext preview
The summary Claude sees to decide when to auto-load this skill.
Write a short, targeted Upwork proposal from a job description and the user's resume, humanized for natural tone.
name: upwork-proposal description: Write a short, targeted Upwork proposal from a job description and the user's resume, humanized for natural tone. argument-hint: "<proposal_id | job_description>"
Write a concise, winning Upwork proposal that directly addresses the client's needs. Clients skim proposals on mobile, between meetings - if it takes more than ~30 seconds to read, it's gone. Optimize every line for a fast skim.
Follow `../_shared/setup.md`. Then `Read` the resume at `primaryResumeSourceAbsolutePath` for full context (identity, skills, experience, projects, research).
The argument is either a **proposal id** (an integer, when launched from the JobPilot UI) or a raw **job description** (manual use). Detect which:
curl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" "$JOBPILOT_API/api/upwork/proposals/$ARG"
Use `jobDescription` as the posting, plus `jobTitle` / `clientName` / `jobUrl` for context. Remember the id - you will `PATCH` the result back to it in Step 7. (A draft launched from an Upwork **search recommendation** already has these fields filled and `source:"search"` - same flow, no extra work.)
Identify: what the client needs built/fixed, required tech and skills, scope and timeline clues, pain points/challenges, and any specific questions the client asks. Pull out one concrete detail unique to _this_ posting - you'll reference it in the hook so the client can tell the proposal isn't a mass send.
Pick the SINGLE most relevant project from the resume - one that matches their problem, not just their tech stack. One specific, on-point case study beats five generic ones. Don't list everything. If a portfolio/GitHub/live link exists for it, keep that link ready.
Order it the way it gets read - them first, you second, never the reverse.
**Hook (line 1, bolded):** Open with a bold line that names _their_ problem or goal in their words. Upwork renders `**text**` as bold - use it on this line so it stands out instantly. This is the single most important line; if it reads like "Hello, I'm excited to apply," the proposal is deleted. No "Hi" / "Dear client" / "I'm excited to apply" / "I came across your posting."
**Case study (1–2 lines):** One relevant project, stated specifically - what you built, the outcome with a real metric (users, perf gain, revenue), and a link if available. Tie it directly to what they need. Don't narrate your career; the client cares about their problem, not your journey.
**Specific question (1 line):** Ask ONE sharp question about their project that proves you read the posting and thought about it (scope, an edge case, a decision they'll need to make). This both shows engagement and opens a conversation.
**CTA (1 line):** End with a real next step - the specific question above, or a calendar link if the resume/profile provides one. Never end with a dead phrase like "Looking forward to hearing from you." Create the next step.
If the posting has screening questions, answer each one short, direct, and specific. "How many years with React?" → "4 years, including [project]." Never write a 300-word essay for a one-line question - long answers read as AI padding.
Invoke the `humanizer` skill on the full text in **embedded mode** (final rewrite only).
Save the result so it appears on the Upwork page. `screeningAnswers` is a JSON array of `{ "question", "answer" }` objects (empty `[]` if the posting had none).
curl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" -X PATCH "$JOBPILOT_API/api/upwork/proposals/$ARG" \
-H 'content-type: application/json' \
-d '{ "proposalText": "...", "screeningAnswers": [] }' curl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" -X POST "$JOBPILOT_API/api/upwork/proposals" \
-H 'content-type: application/json' \
-d '{ "jobTitle": "...", "clientName": "...", "jobUrl": "...", "jobDescription": "...", "proposalText": "...", "screeningAnswers": [] }'`jobTitle` is required; derive it from the posting. Include `clientName` / `jobUrl` when the posting provides them.
Then print the proposal (and any screening answers, each labeled with its question) to the terminal so the user can paste it into Upwork.
1. **Under 150 words for the body.** Shorter wins on mobile. Brevity reads as confidence. 2. **Them before you.** Their problem leads; your background supports. Never open with 3 paragraphs about yourself. 3. **No AI tells.** No "Certainly, here is..." preambles, no robotic symmetry, no functional emoji bullets (✅ 📌 🔹 ☑) - those scream AI. If an emoji appears at all, at most one, used the way a person would. 4. **No fluff** - drop "passionate", "dedicated", "committed", "excited", "thrilled", "leverage", "utilize", "innovative", "cutting-edge", "seamless", "robust". 5. **No generic openings** ("I came across your job posting" / "I'm a senior developer with X years"). 6. **Be specific.** Real project names, metrics, tech, and links from the resume - referencing their actual job, not a template. 7. **One matched case study, not five.** Relevance beats volume. 8. **One CTA**, and make it a real next step (a question or a calendar link). 9. **Match tone.** Casual posting → casual; formal → professional. 10. **No fabrication.** Only reference resume content. Don't invent metrics or links. 11. **Don't mention freelance status
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".