← Back to Blog

career-ops Passed 67,000 Stars In Under Five Months. Here Is What It Actually Does, What One Job Evaluation Costs, And How To Run The Whole Thing For Nothing.

· 16 min read career-ops Passed 67,000 Stars In Under Five Months. Here Is What It Actually Does, What One Job Evaluation Costs, And How To Run The Whole Thing For Nothing.

Somebody spent months applying to jobs the slow way, got tired of the spreadsheet, and built the system they wished they had. Then they published it. Four and a half months later it is one of the most-starred repositories on GitHub.

That is the short version of career-ops. The long version is more interesting, because the thing is genuinely useful and it is also genuinely easy to run badly. This post is the map: what it does, what each part costs, where the money leaks, and five ways an ordinary person can put it to work this week.

The numbers, read off the API this morning

I pulled these from the GitHub and npm APIs on August 24, 2026, rather than copying them out of the README.

Measure Value
Stars 67,975
Forks 12,923
Open issues 313
Watchers 259
Repository created April 4, 2026
Last push August 23, 2026
License MIT
npm package @santifer/career-ops, latest 1.28.0, 20 published versions
npm first published June 9, 2026
README translations 17 languages

Four and a half months from first commit to 67,975 stars is not a normal growth curve, and it is worth saying plainly that star counts measure attention, not usefulness. The 12,923 forks are a better signal, and 313 open issues tell you the maintainer is not pretending everything is fine. The author says the repo is maintained in about four hours a week with a fleet of agents doing the routine work, which is a claim I find plausible having run something similar.

What it actually is

career-ops is not a website and not a service. It is a folder of markdown files, YAML config, and Node scripts that you clone onto your own machine and then open inside an AI coding CLI. The CLI does the thinking. The folder tells it how.

npx @santifer/career-ops init
cd career-ops
claude   # or codex / opencode / agy / grok / qwen

That is the entire install. On first launch it walks you through setup by chatting: your CV, your profile, the roles you are targeting. Nothing to edit by hand unless you want to.

Because the logic lives in modes/*.md rather than in code, it is vendor-neutral by construction. The repo ships wrappers for Claude Code, Codex, OpenCode, Antigravity CLI, Grok Build CLI, Qwen, Kimi, GitHub Copilot CLI and Cursor, all pointing at the same AGENTS.md. If you already pay for one AI CLI, that is the only subscription involved.

What happens when you paste a job link

You paste a URL or the raw text of a job description. The system classifies the role into an archetype, then produces a structured report in blocks A through H:

  • Role summary and what the company is actually asking for
  • CV match, with the gaps named rather than smoothed over
  • Level strategy, meaning whether you should be applying at the advertised level at all
  • Compensation research
  • Personalization angles
  • Interview stories in STAR plus a reflection beat
  • Block G, a separate posting-legitimacy assessment that flags scams, ghost jobs and reposts, and which deliberately does not affect the score
  • Block H, drafted only when the role scores 4.5 or above

Then it gives the role a single 1.0 to 5.0 score. Two design choices in that scoring are worth copying even if you never install this.

The score is reached by holistic judgement across five dimensions, not by an arithmetic formula. Weighted-average scoring quietly rewards a role that is mediocre at everything over a role that is excellent at three things and wrong on one. Holistic judgement does not.

And the system tells you not to apply below 4.0. A tool whose whole business is generating applications, telling you to send fewer of them, is an unusual and correct instinct. As the README puts it, your time is valuable and so is the recruiter's.

It never submits anything. The README states that as an absolute rather than a hedge, and there is even an HTML comment in the feature table instructing translators not to soften that row. AI evaluates and recommends; you decide and act.

The parts that cost you nothing

This is the section most people skip, and it is where most of the value sits.

The portal scanner is free. node scan.mjs reads public ATS APIs directly, covering Greenhouse, Ashby, Lever, Wellfound and around 55 provider modules that also handle board-wide feeds, XML and RSS, and local parsers. No language model is involved. It writes new URLs into data/pipeline.md and dedupes anything it has seen. It ships with 100-plus companies pre-configured and 45-plus search queries, and you add your own in portals.yml.

PDF generation is free. The ATS-optimized CV is HTML plus Playwright, rendered locally. Chromium only launches when you pass --verify to a scan or when you generate a PDF.

The triage pass is nearly free. The automation guide ships a Read/Write-only prompt that reads only config/profile.yml and data/pipeline.md, judges each pending posting on title and location alone, and writes a shortlist to data/shortlist.md. It opens no URLs, fetches no job descriptions, spawns no subagents. One small prompt.

That last one is the whole trick. Scan on a schedule for nothing, triage for a fraction of a cent, and only spend real tokens on the handful of postings that already cleared a free filter.

The triage prompt also does something I wish more agent prompts did: it declares the pipeline file untrusted. Job postings are third-party text, and third-party text can contain "ignore previous instructions." The prompt says so explicitly and pins the allowed reads and writes before the data arrives. Copy that pattern into anything you build that reads scraped text.

What an evaluation really costs

Here is the number the project is refreshingly honest about, taken from its own budget guide.

If you paste a job URL into your CLI, the agent loads AGENTS.md at 8,285 tokens, modes/_shared.md at 4,127, and modes/oferta.md at 13,601. That is roughly 26,000 tokens of instructions per evaluation, before your CV, before the job description, before any tool output. That is the floor of an interactive evaluation, and it is why an afternoon of pasting URLs eats a quota that a batch run would not.

The standalone script path is lighter: a static prefix of 17,728 tokens, about 20,000 input and 1,000 output all in, which at DeepSeek V3 rates works out to roughly $0.003 per evaluation. Tailoring the CV afterward adds about 3,000 tokens. Generating the PDF adds none.

Two consequences follow, and they are the cheapest wins available.

  1. Batch beats pasting. batch/batch-runner.sh reuses one worker instead of resending the instruction block per role.
  2. One evaluation is not the risk. Unbounded research is. The budget guide records that a single evaluation used to be able to fan out into dozens of subagents and burn tens of millions of tokens, tracked as issue #1235. The modes now cap web research at five queries and forbid spawning subagents for it. If you write your own mode, keep that cap. It is the difference between a third of a cent and an exhausted five-hour limit.

The guide also tells you to measure it yourself rather than trust the paragraph, because those files grow with every release, and it hands you the one-liner to do it. That is the correct posture for any cost claim in any documentation, including mine.

The billing trap that costs people actual money

This one is worth the price of the whole post if it applies to you.

If you pay for a plan and you are also being charged per token, the usual cause is an API key sitting in your environment. Most CLIs prefer an explicit key over your logged-in subscription, and the tool cannot tell that you would rather spend the plan you already bought.

echo $ANTHROPIC_API_KEY   # anything printed here is being billed per token

Unsetting it in the current shell is not enough. It is exported from ~/.zshrc, ~/.bashrc, ~/.profile, a project .env, or another tool that sets it for you, so edit the file or it comes back on the next terminal. ANTHROPIC_AUTH_TOKEN and the cloud-provider switches take precedence the same way.

Then there is the one almost nobody finds. ~/.claude/settings.json can define apiKeyHelper, a script that produces an API key on every request. When it is set, that key wins over your subscription login and cleaning your environment fixes nothing.

grep -n "apiKeyHelper" ~/.claude/settings.json 2>/dev/null

Any match means a helper is configured. Project-level .claude/settings.json and ~/.claude/settings.local.json can set the same thing.

Batch mode is the deliberate exception, because headless workers do not use the interactive login at all. Run claude setup-token once and export the result as CLAUDE_CODE_OAUTH_TOKEN in the environment the batch runs in. It is a credential. Treat it like one.

Running the whole thing for nothing

career-ops runs on Antigravity CLI's free tier with no API key and no paid subscription. Google moved consumer Gemini CLI access there, and the repo supports it natively.

Resource Approximate daily cap
Requests 1,000
Input tokens about 1 million
Output tokens about 100,000

Caps reset at midnight Pacific. On the free tier the guidance is to keep batch parallelism at 1, and to expect a 50-offer batch to span multiple days, resuming with --start-from. Evaluation, report generation, portal scanning and PDF generation all work; only heavy batching is constrained.

Beyond that, the pipeline is genuinely provider-agnostic. node openai-eval.mjs speaks to any OpenAI-compatible endpoint, so DeepSeek, Qwen, GLM, Together, Groq, OpenRouter, LM Studio, llama.cpp, vLLM and NVIDIA NIM all work by setting a base URL, a model and a key. node ollama-eval.mjs runs it fully local.

One warning from the budget guide that I would underline: do not run this on an 8B model. Small models fail to follow the A-H schema, produce invalid structure, and write generic resume text that is worse than no resume text. The floor for reliable output is 32B, which wants 16 to 24 GB of VRAM, and 70B wants 48 GB. For nearly everyone, a few cents on a hosted cheap model beats buying a graphics card.

There is also a spend_tier key in config/profile.yml with three settings: economy, standard and premium. Economy uses the cheapest model with no extended thinking and is the right choice for high-volume scanning. On standard and premium a pre-screen gate skips obvious mismatches before the full evaluation runs.

Five ways to actually use this

1. The one hour a week operation

Schedule node scan.mjs on cron, launchd, or Windows Task Scheduler. It is idempotent and costs nothing, so daily is fine and the dedup absorbs the days you did not need it. Once a week, run the triage prompt, open data/shortlist.md, and run a real evaluation on the "Worth a look" rows only. Everything expensive is pointed at postings that already passed a free filter.

Note the cron subtlety the docs flag: 0 9 */3 * * runs on the 1st, 4th, 7th and so on of each month, and resets at the month boundary, so the gap across month-end is 1 to 3 days rather than a strict 72 hours. If you want an even cadence, run it daily.

2. The career changer who feeds it context first

The README is blunt about this: the first evaluations will not be great, because the system does not know you yet. Feed it your CV, your career story, your proof points, what you are good at, what you want to avoid. The framing that makes it click is onboarding a new recruiter. The first week they learn about you; after that they are worth having.

The customization loop is the unusual part. You do not edit config files. You ask the CLI to edit them, because it reads the same files it uses.

Change the archetypes to backend engineering roles
Translate the modes to English
Add these 5 companies to portals.yml
Update my profile with this CV I am pasting

3. The person who does not want to be double-submitted

The scanner computes a 64-bit SimHash fingerprint of each job description body and stores it in the eighth column of data/scan-history.tsv. When two listings from different companies come back nearly identical, you get a cross-listing warning with a similarity score.

That usually means a recruitment agency has reposted a direct employer's role with the employer's name stripped. If you apply through both, the agency and the employer each see your application independently, which is a double submission and it damages your standing with the hiring team. Read both listings, pick one channel, and apply once. Direct is usually the safer pick.

Fingerprints are computed locally from text the ATS already returned, so no extra network request happens, and postings with no usable description are never flagged.

4. The freelancer between contracts

Two modes are useful well beyond the job search. deep produces a structured six-axis research brief on a company covering AI strategy, recent moves, engineering culture, challenges, competitors, and the angle your profile should take. contacto identifies the hiring manager, recruiter, or a team peer and drafts a message under 300 characters tuned to each contact type.

The README's own framing is the right one: applications get you in the queue, research gets you a conversation.

If you point these at prospects rather than employers, you are doing outbound sales with a research assistant, and the same rules apply that always apply. Read the terms of service of anything you are reading, send one message rather than a sequence, and do not automate the sending. The repo does not send anything, and neither should you.

5. The person who wants a paper trail

templates/states.yml draws a distinction most trackers miss. SKIP means it never fit and you never considered it. Discarded means you considered it and stopped, or the posting closed. They land in different dashboard groups and count differently in your funnel, so at the end of three months you can see whether your problem is filtering or dropping out.

Around that sit analyze-patterns.mjs for rejection patterns and per-ATS advance rates, stats.mjs for lifetime funnel numbers, detect-reposts.mjs for ghost-job detection, followup-cadence.mjs for follow-up timing, and salary-gap.mjs for the gap between what you wanted, what was advertised, and what you got. There is a Go terminal dashboard with six filter tabs and four sort modes, plus an opt-in experimental web UI that runs nothing unless you start it.

Windows notes, because I run Windows

The repo ships docs/WINDOWS.md and it is unusually candid. Four items matter.

  • The bash on your PATH is probably not Git Bash. The batch/ scripts need Git Bash specifically. cygpath and bash must come from the same install, because Git Bash mounts drives at /c/... and WSL mounts them at /mnt/c/..., and mixing them produces a path the receiving shell cannot resolve, silently.
  • Scoop does not put cygpath on PATH, so checking C:\Program Files\... is not a reliable way to decide whether Git is installed.
  • Skill entrypoints check out as plain text files, because Windows does not create symlinks by default. node update-system.mjs apply replaces the pointer files with real content. No Developer Mode changes needed.
  • Task Scheduler discards stdout and stderr, so a scheduled run that failed and a scheduled run that hung look identical on disk. Redirect to a file, and use -NonInteractive in the PowerShell action so nothing can prompt and hang until the execution time limit.

Shell scripts must also keep LF line endings, and there is a renormalization step if you cloned before v1.26.0.

The honest limits

It is four and a half months old. 313 open issues is healthy transparency, not a red flag, but it does mean you will hit rough edges. The maintainer asks for an issue first on new features, modes and architecture changes, and takes pull requests directly for bug fixes, zero-auth scanner providers, docs and translations.

AI models hallucinate skills and experience. The disclaimer says so, and it is the single most important sentence in the repo. Read every generated CV line before it goes anywhere. A tailored resume that credits you with something you have not done is worse than a generic one.

You are responsible for third-party terms. Greenhouse, Lever, Workday and LinkedIn each have their own rules and this tool reads their public surfaces on your behalf. Do not use it to spam employers.

Some ATS feeds leak closed roles. Companies leave stale postings in their public API after the role is filled. node scan.mjs --verify launches Playwright after the API pass and drops expired postings before they reach your pipeline. The verification is sequential and only runs against new offers after dedup, so the cost stays bounded.

There is no cloud component and no device sync. Everything is files in your checkout. People who want sync put the folder in a synced directory. There is a Dockerfile and a compose file, but Docker packages the environment; it does not turn this into a service that runs on its own.

MIT covers the code, not the name. The trademark policy is a separate file, permissive for community use and reserved for commercial product naming and endorsement. If you fork this into a product, read that file before you pick a name.

Who should skip it

If you are applying to fewer than ten roles, the setup will cost you more time than it saves. If you want something that fires off 200 applications while you sleep, this is the wrong tool and it will tell you so. And if you have no AI CLI at all and no interest in installing one, the free tier is genuinely free but the terminal is not optional.

Everyone else, particularly anyone who has been running a job search out of a spreadsheet for more than a month, should spend the twenty minutes.

The pattern underneath is the real lesson

Strip out the job search and look at what is left. Markdown files that describe modes of work. A YAML profile. Node scripts that do the deterministic, zero-cost parts. A model that does only the judgement, called with a hard cap on how far it can wander. A tracker that is a plain markdown table. Nothing proprietary anywhere in the stack.

That shape works for a lot more than hiring. Vendor evaluation, lead qualification, grant screening, property triage, RFP review. Anything where you face more candidates than you have attention, and the expensive step should only ever run on the ones that survived a free filter.

If you are running a one-person business on a single AI subscription and wondering how far that goes, that is the exact argument in The $20 Dollar Agency, which is about replacing a thousand-dollar-a-month retainer with a twenty-dollar-a-month plan and a set of repeatable playbooks. career-ops is what that looks like when somebody builds it in public.

Related reading

Fact-check notes and sources

  • Stars 67,975, forks 12,923, open issues 313, watchers 259, created April 4 2026, last push August 23 2026, MIT license: read from https://api.github.com/repos/santifer/career-ops on August 24, 2026. These move daily, so re-check before quoting.
  • npm package @santifer/career-ops, latest 1.28.0, 20 versions, first published June 9 2026, modified August 20 2026: read from the npm registry API the same morning.
  • Token floor of about 26,000 per interactive evaluation and the component figures 8,285 / 4,127 / 13,601, plus the 17,728-token standalone static prefix and the roughly $0.003 per evaluation at DeepSeek V3 rates: docs/RUNNING_ON_A_BUDGET.md. The same file supplies the local-model VRAM guidance and the spend_tier table.
  • Issue #1235 and the five-query research cap: same budget guide, "What the default path costs" section.
  • ANTHROPIC_API_KEY precedence, the apiKeyHelper override, and claude setup-token for batch workers: same guide, section 2b. Vendor documentation is the authority on auth precedence: Claude Code authentication and managing costs.
  • Antigravity CLI free-tier caps of roughly 1,000 requests, 1M input tokens and 100K output tokens per day, resetting midnight Pacific: docs/FREE_TIER.md. The doc itself labels these approximate and subject to change by Google.
  • Zero-token scan, the triage prompt, and the cron month-boundary caveat: docs/AUTOMATION.md.
  • SimHash cross-listing detection, the eighth column of data/scan-history.tsv, scan versus scan:full, the blacklist, and the SKIP versus Discarded distinction: docs/FAQ.md.
  • Windows PATH, cygpath, Scoop, symlinked skill entrypoints, LF line endings and Task Scheduler behavior: docs/WINDOWS.md.
  • A-H block structure, Block G legitimacy scoring, Block H at 4.5 and above, the sub-4.0 recommendation, the never-submits guarantee, the 17 README translations, the supported-CLI list, the trademark policy, and the author's own 740-listings case study: the repository README and docs/SUPPORTED_CLIS.md.
  • Press placements referenced by the project: a WIRED Greece feature and a Business Insider first-person piece. Both are linked from the README; I have not independently verified their contents.

This post is informational, not career, legal, or financial advice. career-ops is an independent open-source project. Mentions of it, of the AI CLI vendors named, and of the job boards listed are nominative fair use. No affiliation is implied, and nothing here is sponsored.

← Back to Blog

Accessibility Options

Text Size
High Contrast
Reduce Motion
Reading Guide
Link Highlighting
Accessibility Statement

J.A. Watte is committed to ensuring digital accessibility for people with disabilities. This site conforms to WCAG 2.1 and 2.2 Level AA guidelines.

Measures Taken

  • Semantic HTML with proper heading hierarchy
  • ARIA labels and roles for interactive components
  • Color contrast ratios meeting WCAG AA (4.5:1)
  • Full keyboard navigation support
  • Skip navigation link
  • Visible focus indicators (3:1 contrast)
  • 44px minimum touch/click targets
  • Dark/light theme with system preference detection
  • Responsive design for all devices
  • Reduced motion support (CSS + toggle)
  • Text size customization (14px–20px)
  • Print stylesheet

Feedback

Contact: jwatte.com/contact

Full Accessibility StatementPrivacy Policy

Last updated: April 2026