MrTechKing

6 min read

career-ops: The AI Job Search Agent Where You Still Press Submit (GitHub, Scanned)

Scores each job against your CV, tells you when to skip, and drafts the CV and answers. It never applies for you.

career-ops logo
✅
Scan: safe. Nothing malicious, and it does not auto-apply. Know that installing downloads Chromium, your CV goes to the AI provider you pick, and an optional scheduled scan stays on your machine until you remove it. Scanned Nov 7, 2026; the full report is below.

career-ops is an AI job search tool built to make you apply to fewer jobs, not more. Paste a job link or description into your AI coding tool and it checks the posting is still live, scores it from 1 to 5 against your real CV, tells you to skip a weak fit, then tailors your CV as a PDF, drafts a cover letter and form answers, and adds a row to your tracker. It can also scan job boards through more than 55 provider modules, find who to contact and draft the note, and prepare you for interviews.

Santiago Fernández built it for his own search and reports 740 listings evaluated, 68 applications, 12 interviews and 1 offer; those are his numbers. It has since grown to about 81,000 stars, 616 contributors and 59 releases, with v1.39.0 current. It runs in Claude Code, Codex, OpenCode and other AI command-line tools, and free or local models work too.

The line it holds is the reason to trust it with a job search: it does not submit. The apply mode fills in a form with you field by field and stops before the Submit button, the email mode only writes drafts, and there is no mail code in the project. It also has a check that stops a tailored CV whose numbers or facts are not in your source material, though the project still tells you to read every CV before sending it.

Who it is for

Job seekers comfortable with a terminal who already use or are willing to try an AI coding tool, especially people in tech applying to many roles who want a filter and a tracker rather than a spray-and-pray bot. If you avoid terminals, the web version is only a waitlist so far.

Getting started

1. Create the project (Node.js needed). This also downloads Chromium, used for PDFs and browser checks

npx @santifer/career-ops init

2. Open your AI tool in the new folder and it walks you through setup by chat

cd career-ops && claude

3. Then paste a job into the chat

Evaluate this job: <paste the link or description>

Swap claude for codex, opencode or another supported tool. Set PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 before installing to skip the Chromium download. Read everything it writes before you send it.

Safety scan

We cloned career-ops-hq/career-ops at commit 0339acb on Nov 7, 2026 and ran the checks described on the GitHub Tools page: credential patterns, decode-and-execute code, install-time scripts, committed binaries, risky CI workflows, every host the code talks to, known vulnerabilities in pinned dependencies, and project hygiene. A person read every hit. This is what we found.

  • It does not apply for you. The README says the script never POSTs and the system never submits, and modes/apply.md tells the agent to stop before Submit, Send or Apply. The project's own legal note adds the fair caveat that this rests partly on prompts and AI models can behave unpredictably, so watch it when it is filling in a live form.
  • Your CV leaves your machine, by design. Evaluations send your whole CV, profile and job descriptions to the AI provider you chose, and the README says nowhere else: no telemetry and no backend of theirs. An optional privacy guard can block requests containing values you list. Use a local model through Ollama if you want nothing sent at all.
  • All 7 pattern hits are one optional feature: web/scripts/install-scan-schedule.sh and its uninstaller writing a macOS LaunchAgent or a crontab line. Scheduled scans belong to the alpha web UI and you have to run the script yourself. The worker wakes every 15 minutes, runs as your user and never as root, and per the docs never evaluates roles, applies or calls an AI model. The uninstall script removes it.
  • One install hook: the postinstall in package.json downloads Chromium through Playwright, for PDFs and for checking postings. It fetches nothing else, and PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 turns it off. The 4 installer scripts the scanner counted are the install and uninstall scripts for the schedule, for shell and PowerShell.
  • 2 unrated advisories across 268 packages, both in golang.org/x/text 0.3.8, an indirect dependency of the terminal dashboard; they cover crafted input, and that dashboard is not a network service. The web UI's lockfile has none. The root Node project has no lockfile we could check, so its dependencies are not covered by this count.
  • No secret hits and no binaries. The 9 bare-IP URLs are all in test-all.mjs, testing that its network code refuses unsafe redirects. Most of the top hosts are job boards such as Greenhouse, Lever and Ashby, plus test placeholders.
  • 28 workflows and strong hygiene: security policy, Dependabot, Renovate, CodeQL, licence and contributing guide all present, and all 3 third-party actions pinned to a commit. The scanner flagged 5 files for pull_request_target and none checks out pull request code: one, plugin-registry-validate, only mentions it in a comment, two use event metadata alone, and two check out the base branch.

What the scanner counted

CheckResult
SecretsNone found.
Suspicious code7 pattern hits found and read; every one is listed under the raw findings.
Install-time code1 npm lifecycle script. 4 installer scripts
Committed binariesNone.
CI workflows28 workflows. 5 use pull_request_target, none check out the pull request head. 0 of 3 third-party actions pinned to a tag rather than a commit.
Network hosts40 distinct hosts referenced from source; most often jobs.example.com, boards.greenhouse.io, jobs.lever.co, job-boards.greenhouse.io. 9 URLs to a bare IP address, listed under the raw findings.
Known vulnerabilities2 advisories across 268 pinned packages: 0 critical, 0 high, 0 moderate, 0 low, 2 unrated. dashboard/go.mod: 21 packages, 2 advisories; web/package-lock.json: 248 packages, 0 advisories.
Project hygieneHas security policy, automated dependency updates, CodeQL, licence file, contributing guide.
OpenSSF ScorecardNot scored: the project is not in Scorecard's weekly index.

The raw findings

Every hit the scanner wrote out, with a link to the exact line at the scanned commit. Secrets candidates are redacted.

Pattern hits (7)
WhereRuleMatch
web/scripts/install-scan-schedule.sh:94persistencePLIST="$HOME/Library/LaunchAgents/$LABEL.plist"
web/scripts/install-scan-schedule.sh:160persistenceif ! crontab -l >"$TMP" 2>"$ERR"; then
web/scripts/install-scan-schedule.sh:174persistencecrontab - <"$NEW"
web/scripts/uninstall-scan-schedule.sh:11persistencePLIST="$HOME/Library/LaunchAgents/$LABEL.plist"
web/scripts/uninstall-scan-schedule.sh:27persistenceif ! crontab -l >"$TMP" 2>"$ERR"; then
web/scripts/uninstall-scan-schedule.sh:44persistencecrontab - <"$NEW"
web/scripts/uninstall-scan-schedule.sh:46persistencecrontab -r
URLs to bare IP addresses (9)
WhereRuleMatch
test-all.mjs:16263ip-literal-urlif (u === 'https://93.184.216.34/start') return new Response(null, { status: 302, headers: { location: 'https://93.184.216.35/final' } });
test-all.mjs:16263ip-literal-urlif (u === 'https://93.184.216.34/start') return new Response(null, { status: 302, headers: { location: 'https://93.184.216.35/final' } });
test-all.mjs:16264ip-literal-urlif (u === 'https://93.184.216.35/final') return new Response(JSON.stringify({ ok: 1 }), { status: 200 });
test-all.mjs:16265ip-literal-urlif (u === 'https://93.184.216.34/bad') return new Response(null, { status: 302, headers: { location: 'https://10.0.0.1/x' } });
test-all.mjs:16269ip-literal-urllet httpRej = false; try { await gctx.fetch('http://93.184.216.34/x'); } catch { httpRej = true; }
test-all.mjs:16272ip-literal-urllet outRej = false; try { await gctx.fetch('https://8.8.8.8/x'); } catch { outRej = true; }
test-all.mjs:16276ip-literal-urlconst r = await gctx.fetch('https://93.184.216.34/start', { headers: { Authorization: 'Bearer secret' } });
test-all.mjs:16277ip-literal-urlconst cross = fetchCalls.find(c => c.url === 'https://93.184.216.35/final');
test-all.mjs:16283ip-literal-urllet ssrfRej = false; try { await gctx.fetch('https://93.184.216.34/bad'); } catch { ssrfRej = true; }
npm lifecycle scripts (1)
  • package.json postinstall: node -e "process.exit(['','0','false'].includes((process.env.PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD||'').toLowerCase())?1:0)" || npx playwright install chromium || npx playwright install chromium || node -e…
Installer scripts (4)
Worst known vulnerabilities (2 of 2)
AdvisorySeverityPackageSummary
CVE-2026-56852unknowngolang.org/x/text@0.3.8Infinite loop on invalid input in golang.org/x/text
CVE-2026-56851unknowngolang.org/x/text@0.3.8Panic parsing crafted input in x/text/secure/precis in golang.org/x/text
Workflows worth a look

By the numbers

Stars80.8K
Forks14.9K
Contributors616
Commits3,935
Open issues267
Open pull requests166
Releases59
Latest releasecareer-ops-v1.39.0
LicenceMIT
Main languageJavaScript
Project age7 months
Last pushNov 7, 2026
Tracked files3,206
Lines of code626.3K
Checkout size50 MB

Lines by language: JavaScript 476.1K, Markdown 74.3K, TypeScript 30.8K, Go 20.7K, YAML 10K, HTML 6,336.

Questions

Is career-ops free?

Yes, MIT-licensed, and the project says it stays free for candidates. Your only cost is the AI model, and it also runs on free and local models through OpenRouter or Ollama. SerpApi sponsors the project, and the README says sponsors never appear in evaluations or rankings.

Does career-ops apply to jobs automatically?

No. It evaluates, ranks and drafts, and you press Submit. The apply mode fills a form with you and stops before the final button, and email mode only writes drafts. It is built as a filter, and it advises against applying to roles scoring below 4 out of 5.

Where does my CV go?

It stays in files on your machine and is sent to the AI provider you choose whenever a job is evaluated. The README says the project collects nothing and has no backend. If you do not want your CV with a cloud provider, run a local model.

Will it leave anything running on my computer?

Not by default. A background task only exists if you run the scheduled scan install script from the web UI folder. It checks for due scans every 15 minutes until you run the matching uninstall script.


This post is part of GitHub Tools, where every repository is cloned and scanned before it is written up. The scan is a snapshot of one commit on one day; the repository has moved on since, so check it before you install.