How this skill is triggered — by the user, by Claude, or both
Slash command
/cowork-job-search:job-searchThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> **Priority hierarchy**: See `shared/references/priority-hierarchy.md` for conflict resolution.
Priority hierarchy: See
shared/references/priority-hierarchy.mdfor conflict resolution.
Search all supported job boards, return the top 10 matches with tailored resumes ready for manual application.
/cowork:job-search - Search with default terms from preferences/cowork:job-search AI infrastructure - Search with specific keywordsscripts/
evaluate-jobs.md # Subagent for parallel job evaluation
assets/
templates/ # Format templates (committed)
Resolve the data directory using shared/references/data-directory.md.
Resolve the data directory, then check prerequisites per shared/references/prerequisites.md. Resume and preferences are both required.
Read these files:
DATA_DIR/resume/* (candidate profile)DATA_DIR/preferences.md (preferences)DATA_DIR/profile.md (work history, if exists)DATA_DIR/job-history.md (to avoid duplicates)DATA_DIR/linkedin-contacts.csv (if it exists — for network matching)Extract search terms from:
$ARGUMENTS if providedExtract location from preferences (for URL substitution).
Refer to shared/references/job-boards.md for the full board list and URL patterns.
Search every board in the list. Do not skip boards or pick a subset. The goal is maximum coverage — more sources means better results.
Use Claude in Chrome MCP tools per shared/references/browser-setup.md.
For each board:
shared/references/job-boards.md, substituting {q} with the search query and {l} with the location. URL-encode both values.evaluate_script — see extraction strategy in shared/references/job-boards.md.IMPORTANT: Do NOT use get_page_text on any job listing page. Always use targeted JS extraction or take_snapshot.
Deduplication across boards: Track jobs by (company + title). If the same job appears on multiple boards, keep the first occurrence and note which boards listed it.
Score each job against the candidate's resume and preferences using the criteria in shared/references/fit-scoring.md.
Rank all results by fit score. Select the top 10 (High and Medium fits, prioritizing High). If there aren't 10 new results, include however many were found.
Exclude any jobs already in DATA_DIR/job-history.md.
Hard rule: every job presented in Step 7 MUST be verified live in this step. Unverified jobs do not appear in the top 10 — full stop. Expired, 404, or unverifiable postings waste the user's time and damage trust in the skill.
For each of the top 10 jobs:
If the listing links directly to the employer careers page, use that URL.
If the listing is on an aggregator, click through to reach the actual employer posting.
Verify the posting is still live. Use the primary path if Claude in Chrome is connected, otherwise use the fallback.
Primary path (Claude in Chrome available). Use evaluate_script:
(() => {
const text = document.body?.innerText?.toLowerCase() || "";
const title = document.title?.toLowerCase() || "";
const deadSignals = [
"no longer available",
"no longer accepting",
"position has been filled",
"this job has been removed",
"job not found",
"page not found",
"this posting has expired",
"this position is closed",
"no longer active",
"has been closed",
"job has expired",
"this role has been filled",
"sorry, this job is no longer available",
"this job is no longer posted",
"requisition is no longer open",
"this opportunity is no longer open",
"applications are closed",
];
const found = deadSignals.find(
(s) => text.includes(s) || title.includes(s),
);
return {
alive: !found,
signal: found || null,
httpStatus: document.querySelector("h1")?.innerText?.substring(0, 50),
finalUrl: window.location.href,
};
})();
alive is false, treat as expired.<h1> contains "404", "not found", or "error", treat as expired.finalUrl redirected to a generic careers/jobs index (no specific job slug / ID), treat as expired.Fallback path (Chrome unavailable). Use WebFetch with a verification-only prompt:
Does this URL still show a specific, currently-open job posting? Look for the job title, description, and an "Apply" affordance. If the page redirects to a generic careers index, shows "this posting is no longer available / has expired / has been filled / 404 / not found", or does not show a specific live posting, reply exactly "EXPIRED". Otherwise reply exactly "LIVE: <job title> | <company>".
LIVE: ... as expired.WebFetch is blocked by the egress proxy for that domain, the posting is unverifiable — treat the same as expired for presentation purposes. Do not present unverified postings as active opportunities.Extract the full job description (Chrome: evaluate_script selector [class*="description"], [class*="content"], article, main; fallback: WebFetch with an extraction prompt). Do NOT use get_page_text.
Save to DATA_DIR/jobs/[company-slug]-[date]/posting.md with the employer URL + a verified: YYYY-MM-DD stamp at the top.
Backfill from ranked list: If any top-10 jobs are dead or unverifiable, pull the next-highest-ranked candidates to replace them until you have 10 verified-live postings, or until the ranked pool is exhausted.
If the ranked pool is exhausted before reaching 10 verified postings, present however many you have and clearly state "N live matches (M additional candidates excluded as expired/unverifiable)". Never pad the list with unverified jobs.
Aggregator listings on Levels.fyi, Remotive, Remote Rocketship, Wellfound, etc. are especially prone to staleness — they often keep closed postings visible for weeks. Always click through to the employer ATS (Greenhouse, Lever, Ashby, Workday) before trusting liveness.
Never show aggregator URLs to the user — always resolve to the employer's own posting.
For each of the top 10 jobs, automatically run the resume tailoring process:
DATA_DIR/jobs/[company-slug]-[date]/posting.mdtailor-resume skillDATA_DIR/jobs/[company-slug]-[date]/resume.mdThe tailored resume should:
Append ALL evaluated jobs (not just top 10) to DATA_DIR/job-history.md. Include a Status column so expired/unverifiable postings are tracked over time:
## [DATE] - Search: "[terms]"
| # | Job Title | Company | Location | Salary | Fit | Status | Apply Link |
| --- | --------- | ------- | -------- | ------ | ------- | --------------- | ---------- |
| 1 | ... | ... | ... | ... | High | Live (verified) | [link] |
| 2 | ... | ... | ... | ... | High | Live (verified) | [link] |
| 3 | ... | ... | ... | ... | High | Expired | — |
| 4 | ... | ... | ... | ... | Medium | Unverifiable | — |
Show the top 10 matches. Only verified-live postings appear here. If fewer than 10 verified live postings exist, show the count and note how many were excluded:
## Top 10 Matches — [DATE]
Searched [N] boards | [M] total jobs found | [K] new matches | [L] verified live
### 1. [Title] at [Company]
- **Fit**: High
- **Salary**: $XXXk
- **Location**: Remote
- **Source**: [board name]
- **Verified**: [YYYY-MM-DD] ✅
- **Why**: [1-line reason]
- **Network**: You know [First Last] ([Position]) at [Company]
- **Apply**: [direct employer URL]
- **Resume**: `~/.cowork/jobs/[company-slug]-[date]/resume.md`
Omit the "Network" line if there are no contacts at that company.
If LinkedIn contacts were loaded, cross-reference each result's company name against the "Company" column in the CSV. Use fuzzy matching (e.g. "Google" matches "Google LLC", "Alphabet/Google").
After the list:
Ready to apply? Each job folder has a tailored resume.
- /cowork:cover-letter [job URL] — write a cover letter
- /cowork:app-questions [job URL] — help with application questions
Built with Cowork Job Search — https://github.com/whyuascii/cowork-job-search
If user provides feedback, update DATA_DIR/preferences.md:
Structure user-facing output with these sections:
/cowork:cover-letter and /cowork:app-questions for top matchesAdd to ~/.claude/settings.json:
{
"permissions": {
"allow": [
"Read(~/.claude/skills/**)",
"Read(~/.cowork/**)",
"Write(~/.cowork/**)",
"Edit(~/.cowork/**)",
"Bash(crontab *)",
"mcp__claude-in-chrome__*"
]
}
}
npx claudepluginhub whyuascii/cowork-job-search --plugin cowork-job-searchCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.