一键导入
apply
Apply to a single job (URL or pasted page) with fit review, or drain the pending queue when no argument is given.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Apply to a single job (URL or pasted page) with fit review, or drain the pending queue when no argument is given.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | apply |
| description | Apply to a single job (URL or pasted page) with fit review, or drain the pending queue when no argument is given. |
| argument-hint | [job_url_or_pasted_job_page] (omit to drain the queue) |
Two modes, one shared apply loop:
/api/queue/pending → score → ranked table approval → apply all.User approves once up front. No per-job confirmation after that.
Follow ../../shared/setup.md to load profile, resume, credentials.
JOBPILOT_API="${JOBPILOT_API:-https://jobpilot.suxrobgm.net}"
Read autoApply for config (defaults applied per field):
| Setting | Default | Notes |
|---|---|---|
minMatchScore | 60 | Batch-mode threshold (0-100). Ignored in single-job mode. |
maxApplicationsPerCampaign | null (unlimited) | Sent as config.maxApplications when set; omit for unlimited batch. Single-job mode forces 1. |
defaultStartDate | "2 weeks notice" | Default start-date answer. |
For ATS portals (Greenhouse, Lever, Workday, etc.) the apply step lands on a domain that isn't in /api/job-boards; the job-worker handles login/registration there per ../../shared/auth.md.
campaign <campaign-id> → re-apply mode: set CAMPAIGN_ID=<campaign-id>, set config.maxApplications = null (unlimited - the user hand-selected these jobs), skip Phases 1-4, and run the Phase 5 loop over its current approved jobs. (The campaign viewer - or the rescan-skipped skill - promotes the chosen skipped/failed jobs to approved before injecting this.)If the argument is pasted content (HTML / text), extract description, Apply URL, company, title. If no Apply URL can be found, stop: "I need either a job URL or content with a visible Apply link."
URL input → delegate to the job-worker subagent with mode:"review" so the posting snapshot stays out of this conversation: { "mode":"review", "url":"<job-url>", "resumeId":"<primary-or-empty>" }. Use its returned matchScore/strongMatches/partialMatches/gaps/blockers/visaRisk/verdict to fill the review below; keep its digest as DIGEST for 1.4.
Pasted input → parse the fields yourself (the content is already in hand), build the digest (../../shared/digest-schema.md), and POST /api/score-fit for the score. Keep the digest in DIGEST=... for 1.4.
## Job Fit Review: [Title] at [Company]
**Match Score: X/100**
**Strong Matches:** [skill - evidence]
**Partial Matches:** [skill - what's adjacent]
**Gaps:** [skill - what's missing]
**Visa/Sponsorship Risk:** [if mentioned]
**Verdict:** [1-2 sentence recommendation]
Ask: "Want me to proceed with the application?" - yes/go continue, anything else stop.
URL_ENCODED=$(jq -rn --arg v "<job-url>" '$v|@uri')
TITLE_ENCODED=$(jq -rn --arg v "<title>" '$v|@uri')
COMPANY_ENCODED=$(jq -rn --arg v "<company>" '$v|@uri')
curl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" "$JOBPILOT_API/api/applied/check?url=$URL_ENCODED&title=$TITLE_ENCODED&company=$COMPANY_ENCODED"
If .applied === true, surface the match (title + company + appliedAt + .match.kind) and ask whether to proceed anyway. Stop on no.
CAMPAIGN_ID=$(date -u +%Y-%m-%dT%H-%M-%S_apply)
curl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" -X POST "$JOBPILOT_API/api/campaigns" \
-H 'content-type: application/json' \
-d "$(jq -n --arg campaignId "$CAMPAIGN_ID" --arg query "<title> at <company>" \
'{campaignId:$campaignId, query:$query, source:"apply", config:{maxApplications:1}}')"
JOB_KEY=$(date -u +%s)-single
curl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" -X POST "$JOBPILOT_API/api/campaigns/$CAMPAIGN_ID/jobs" \
-H 'content-type: application/json' \
-d "$(jq -n --arg key "$JOB_KEY" --arg title "<title>" --arg company "<company>" \
--arg location "<location>" --arg url "<job-url>" --arg board "<board>" \
--arg matchReason "<one-line verdict>" --argjson score <0-100> \
--arg digest "$DIGEST" --arg desc "<posting text>" \
'{key:$key, title:$title, company:$company, location:$location, url:$url, board:$board, matchScore:$score, matchReason:$matchReason, status:"approved", digest:$digest, description:$desc}')"
Keep $CAMPAIGN_ID and $JOB_KEY. Live view: $JOBPILOT_WEB/campaigns/<CAMPAIGN_ID>. Jump to Phase 5.
curl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" "$JOBPILOT_API/api/queue/pending"
data is [{ id, url, note, status }]. If empty, tell user to open $JOBPILOT_WEB/queue to add URLs and stop. Otherwise: "Found N URLs in the queue. Visiting each to gather details..."
CAMPAIGN_ID=$(date -u +%Y-%m-%dT%H-%M-%S_apply)
curl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" -X POST "$JOBPILOT_API/api/campaigns" \
-H 'content-type: application/json' \
-d "$(jq -n --arg campaignId "$CAMPAIGN_ID" \
'{campaignId:$campaignId, query:"apply queue", source:"apply", config:{minScore:6, maxApplications:10}}')"
For each queue URL:
URL_ENCODED=$(jq -rn --arg v "<job-url>" '$v|@uri')
curl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" "$JOBPILOT_API/api/applied/check?url=$URL_ENCODED"
If applied, mark the queue entry consumed (status:"skipped") and add a skipped Job with skipReason:"Already applied (<kind>)", then continue - don't spawn a worker.
job-worker)Delegate the visit + score to the job-worker subagent - it opens its own tab, reads the posting, fuzzy-dedupes by title+company, scores, applies eligibility (../../shared/eligibility.md), and saves the Job row itself (so the full posting/digest never enters this conversation). One worker at a time. Input JSON:
{ "mode": "score", "campaignId": "<CAMPAIGN_ID>", "jobKey": "<entry-id>", "url": "<job-url>",
"board": "<domain>", "minMatchScore": <minMatchScore>, "resumeId": "<RESUME_ID>" }
It returns { outcome:"scored", jobKey, title, company, location, matchScore, confidence, eligible, skipReason, matchReason }. Collect these summaries for the ranked table (Phase 4) - the worker has already written each Job as pending (eligible) or skipped (with skipReason).
Auto mode (confirmMode: "auto" AND every qualified job ≥ threshold): PATCH all to approved, go to Phase 5.
Batch mode (default): present ranked table.
## Batch Apply
Visited <total> jobs. <qualified> qualify (score >= minMatchScore/100).
| # | Score | Title | Company | Location | Board |
|---|--------|-------|---------|----------|-------|
**Commands:** "go" | "go 1,3,5" | "remove 3" | "details 2" | "stop"
PATCH Job.status accordingly:
go → all qualified to approvedgo N,M → selected to approved; rest to skipped ("Not selected by user")remove N → that job to skipped ("Removed by user"); re-present tablestop → PATCH campaign status:"paused" and stopcurl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" -X PATCH "$JOBPILOT_API/api/campaigns/$CAMPAIGN_ID/jobs/<key>" \
-H 'content-type: application/json' -d '{"status":"approved"}'
For each approved job, score-descending:
curl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" -X PATCH "$JOBPILOT_API/api/campaigns/$CAMPAIGN_ID/jobs/<key>" \
-H 'content-type: application/json' -d '{"status":"applying"}'
job-worker)Delegate to the job-worker subagent and wait for its compact result - it navigates, authenticates, tailors, fills, and submits in its own tab/context (keeping the form snapshots out of this conversation). One worker at a time. Input JSON:
{ "mode": "apply", "campaignId": "<CAMPAIGN_ID>", "jobKey": "<key>", "url": "<job-url>",
"board": "<domain>", "resumeId": "<RESUME_ID>", "defaultStartDate": "<autoApply.defaultStartDate>",
"salaryExpectation": <remembered-or-null>, "preSubmitReview": <true when config.maxApplications === 1, else false> }
(digest omitted → the worker fetches it from the saved Job.)
Single-job pre-submit review: when preSubmitReview is true the worker fills everything, leaves the form open, and returns needs_user reason:"review" with a field summary. Present:
## Ready to Submit: [Title] at [Company]
| Name | Email | Phone | Resume | Salary | Start date | Cover letter | Custom Qs |
<total> fields across <P> page(s). Submit? (yes / no / edit <field>)
yes → re-delegate 5.2 with preSubmitReview:false (the worker submits the already-filled form). no → POST /result outcome:"skipped", skipReason:"User cancelled at pre-submit review". edit <field> → tell the worker what to change on re-delegation.
Map the worker's outcome to /result (the worker never writes it). The server atomically updates the Job, creates the Application (on applied), marks the queue, and recomputes the summary.
NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
# applied
jq -n --arg t "$NOW" --argjson score <0-100> '{outcome:"applied", appliedAt:$t, matchScore:$score}'
# failed
jq -n --arg r "<failReason>" --arg notes "<retryNotes>" '{outcome:"failed", failReason:$r, retryNotes:$notes}'
# skipped (e.g. CAPTCHA, user cancelled, max-apps cap)
jq -n --arg r "<skipReason>" '{outcome:"skipped", skipReason:$r}'
needs_user: reason:"salary" → ask once, remember for the campaign, re-delegate 5.2 with salaryExpectation; reason:"2FA" or email verification → pause and ask; reason:"payment" → POST /result failed "Payment required". The worker closed its tabs; re-select tab 0, then continue.
If config.maxApplications is set and applied >= config.maxApplications, stop the loop. Leave remaining approved jobs as-is.
NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
curl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" -X PATCH "$JOBPILOT_API/api/campaigns/$CAMPAIGN_ID" \
-H 'content-type: application/json' \
-d "$(jq -n --arg t "$NOW" '{status:"completed", completedAt:$t}')"
Print a summary table and link to $JOBPILOT_WEB/campaigns/<CAMPAIGN_ID>.
job-worker follows ../../shared/auth.md: register when no account exists (without asking), run forgot-password when the stored password is stale.needs_user reason:"payment", POST /result outcome:"failed", failReason:"Payment required".solve-captcha and returns skipped when unsolved; for needs_user reason:"2FA" or email verification, pause and ask (see ../../shared/auth.md)./result for terminal outcomes.skipped write carries a non-empty skipReason. No valid reason → not a skip.Search a job board and autonomously apply to matching jobs one at a time, until paused, exhausted, or the max-applications cap is hit.
Write a tailored one-page cover letter from a job description and the user's resume, humanized for natural tone.
Parse a resume's uploaded PDF into structured JSON (basics, experience, projects, skills, education) and save it to the editor.
Fetch the latest verification code or magic link from the connected mailbox for a given board domain. Called by apply / auto-apply for 2FA and account-creation flows.
Remove signs of AI-generated writing from text. Use when editing or reviewing text to make it sound more natural and human-written. Based on Wikipedia's comprehensive "Signs of AI writing" guide. Detects and fixes patterns including: inflated symbolism, promotional language, superficial -ing analyses, vague attributions, em dash overuse, rule of three, AI vocabulary words, negative parallelisms, and excessive conjunctive phrases.
Produce a tailored interview prep sheet (behavioral, technical, system design, company) from a job description and the user's resume.