career-ops Is an AI Job Search Tool Whose Best Answer Is Don't Apply
The open-source repo turns your coding CLI into a job pipeline that scores every listing 1.0 to 5.0 and tells you to skip anything under 4.0, and the rubric doing the scoring is a markdown file you own
Every AI job tool shipped in the last two years optimizes for the same number: applications sent. career-ops puts a line in bold near the top of its README telling you not to apply to anything scoring below 4.0 out of 5.
That inversion is the entire design. The README calls it out directly: "This is NOT a spray-and-pray tool. Career-ops is a filter." The FAQ on the project site says the same thing in the scoring language: "Anything below 4.0 the agent recommends against applying."
A tool whose headline output is a refusal is a strange thing to build, and a stranger thing for 58,000 people to star. It is also the most interesting piece of agent design I have read this month, and it has a hole in it that the README describes without seeming to notice.
The threshold is the product
Strip away the PDF generator, the Go dashboard, and the portal scanner and what remains is a number and a rule about the number.
The scoring runs against your CV rather than against keywords. The project site describes a five-dimension rubric covering match, north-star alignment, compensation, cultural signals, and red flags, producing a holistic score from 1.0 to 5.0 with citations back to specific CV lines and specific job description requirements. The README describes the same evaluation differently, as an "A-F scoring system (10 weighted dimensions)."
Those are two official surfaces giving two different accounts of the same mechanism, which I will come back to.
Worth clearing up one thing the trend coverage keeps repeating: the "A to F" in career-ops is not a letter grade. It is the block structure of the report the agent writes. Block A through F cover role summary, CV match, level strategy, compensation research, personalization, and interview preparation in STAR plus Reflection format. There is now a Block G that checks whether the posting is legitimate at all, flagging scams and ghost jobs. The grade you act on is the numeric one.
Say the position plainly. The valuable engineering here is not that an agent can read a job description. Every model does that. It is that somebody built a system whose default answer is no, and then put a specific number on where no becomes yes.
How it actually runs
career-ops is not an app. It is a directory of markdown files that your existing coding CLI reads.
The skill lives at .agents/skills/career-ops/SKILL.md under the open agent skill standard, and gets symlinked or referenced into per-CLI directories: .claude/, .qwen/, .antigravitycli/, .grok/. Instructions live in AGENTS.md, with thin wrappers in CLAUDE.md, CODEX.md, and OPENCODE.md that import it. The evaluation logic sits in modes/*.md as plain prompt files. Your data sits in markdown tables, YAML config, and TSV batch files.
Nothing is hosted. There is no account and no telemetry. The only thing that leaves your machine is whatever your chosen CLI sends to its own provider, which is a meaningfully different privacy posture from every resume tool that asks you to upload a PDF.
The install is one command:
npx @santifer/career-ops init
Then you open your CLI in that directory and talk to it. In Claude Code, Antigravity, or Grok you get /career-ops with subcommands. In Codex, slash commands are not guaranteed, so you ask in plain language: "Run the career-ops scan mode and summarize new matches." Same mode files, different invocation.
The piece of engineering I liked most is the least glamorous. node scan.mjs hits applicant tracking system feeds directly and spends zero model tokens doing discovery. Then --verify launches Playwright afterward to check whether each new posting is actually still live, because companies routinely leave closed roles in their public API and those stale entries would otherwise flow straight into your pipeline. The verification runs sequentially and only against new offers after dedup, so the cost stays bounded.
That is somebody who ran the system on a real job search and got tired of chasing dead listings. The repo's case study says 740 listings evaluated, 68 applications, 12 interviews, one offer, which is where a detail like that comes from.
The human-in-the-loop parts are constraints rather than fallbacks. The email mode drafts recruiter outreach and explicitly never sends, submits, or clicks. The apply mode fills open-ended application answers on Greenhouse, Ashby, and Lever forms and hands them back paste-ready. You submit. The system does not.
The hole
Here is the sentence in the README that undoes the threshold:
The system is designed to be customized by your AI coding CLI itself. Modes, archetypes, scoring weights, just ask it to change them. It reads the same files it uses, so it knows exactly what to edit.
That is offered as a feature, and as a feature it is excellent. Customization by conversation is exactly how skill-based tooling should work, and it is why the same modes run unchanged across eight CLIs.
It also means the gate and the gatekeeper are the same file, and the agent enforcing the gate will rewrite it on request, in the same session where you just got a 3.7 on the role you actually want.
Nobody edits a rubric maliciously. You edit it reasonably. You notice the compensation dimension is weighted too heavily for a career pivot. You decide "cultural signals" is penalizing early-stage companies unfairly. Each edit is defensible on its own, and the aggregate is a threshold that drifts toward whatever you already wanted to do. A filter you can edit mid-search is a filter you will edit mid-search.
The fix is not complicated, and it is not in the repo. It is version control and a rule.
Put this into practice
If you want to try this, here is the order that avoids the obvious failure modes.
-
Install and let it interview you.
npx @santifer/career-ops init,cd career-ops, then openclaudeorcodexoropencodein that directory. First launch walks you through your CV, profile, and target roles by chatting. Nothing to hand-edit. -
Feed it context before you feed it jobs. The README's own warning is worth repeating: "the first evaluations won't be great. The system doesn't know you yet." Give it your CV, your career story, your proof points, and what you are trying to avoid. Treat the first week the way you would treat onboarding a recruiter.
-
Commit the rubric before you score anything real.
git init, commitmodes/andconfig/profile.yml. This is the step that makes everything after it honest, and it takes thirty seconds. -
Calibrate against listings you already have opinions about. Pick three roles you know are wrong for you and three you would take tomorrow. Run all six. If the numbers disagree with your judgment, that is information about the rubric, and the right time to tune it is now, before any real listing is in play.
-
Log every rubric edit with a reason, and refuse edits you can't justify abstractly. If you cannot explain a weighting change without naming the specific job that prompted it, do not make the change. Write it in a
RUBRIC-LOG.mdnext to the commits. This is the discipline the tool does not enforce for you. -
Use
node scan.mjs --verify, not plainscan. The extra Playwright pass costs a few minutes and keeps dead postings out of your pipeline.
Where it breaks, honestly
The star numbers do not agree. When I loaded the GitHub repo page it served 58.5k stars and 11.5k forks. The project site claims "65K+ stars · 12K+ forks." A search result the same day reported 60K. Shields.io returned nothing at all for this repo on every query I ran. Any specific number you see for this project, including mine, is a snapshot from one surface.
The rubric description does not agree either. README says A-F with 10 weighted dimensions. The site FAQ says five dimensions plus a holistic global score. Both are official. For a tool whose whole value rests on the rubric, that is the documentation gap I would want closed first.
Neither does the portal count. The README says 45+ companies pre-configured and 19 search queries. The site says 150+ company portals. The docs page listing supported job boards mentions 21 provider modules. Pick a number and you will be quoting one of three.
LLM fit scoring has no ground truth. A 4.2 means the model produced 4.2 given your rubric and your CV. It does not mean you have a 4.2 chance of anything. There is no calibration set, no held-out validation, and no feedback loop from outcomes back into the weights. The score is a structured opinion, and it is worth having, but it is not a measurement.
The case study is n=1. One person, one search, one offer, in one field, in one market, in 2026. 740 listings evaluated is a usage figure, not an efficacy figure. Nobody has published a comparison against just reading the listings yourself.
Terms of service risk is yours. The repo says so directly in its disclaimer: you must comply with the ToS of every portal you touch, and Playwright-driven scraping of career sites is a place people get accounts restricted. The tool hands you that liability along with the capability.
Batch mode costs real money. Parallel evaluation spawns headless CLI workers with claude -p or opencode run. That is your subscription and your rate limits, evaluating ten listings at a time.
What I'd take from it even if I never used it
The transferable idea is not the job search. It is that a system built around a refusal threshold produces different behavior than a system built around throughput, and the threshold has to live somewhere the user cannot casually move.
career-ops gets the first half right and leaves the second half to you. If you are building any agent that gates a decision, whether that is a code review bot, a lead qualifier, or an alert triager, the question to answer early is who can edit the gate and how you would know if they did. Putting the rubric in git and requiring a written reason for each change costs nothing and makes the number mean something a month later.
Clone it, score six listings you already have opinions about, and see whether the rubric agrees with you. If it does, you have a filter. If it doesn't, you have learned something about your own criteria that a spreadsheet was never going to tell you.
Then commit the file before you touch it.
Sources: santifer/career-ops on GitHub, the project README, career-ops.org, and the maintainer's case study.