AI Job Search on Your Machine: A Claude Code Framework
A local Claude Code workflow that evaluates jobs, tailors applications, verifies PDFs for ATS systems, and prepares candidates for interviews.
AI Job Search on Your Machine: A Claude Code Framework
Job searching is often treated as a writing problem: find a posting, edit a resume, write a cover letter, and repeat. In practice, the difficult part is managing the entire funnel without losing context or accidentally sending generic, inaccurate materials.
ai-job-search approaches the process as an engineering workflow. It turns Claude Code into a local job application assistant that can build a structured candidate profile, search job portals, evaluate fit, generate tailored documents, check their PDF output, and prepare interview materials. Because the workflow runs in your own repository, your career data stays under your control and can be adapted to your market.
The project is also unusually easy to evaluate: its author reports using the workflow after losing a position in late 2025. The process produced 69 tailored applications, 20 first interviews, and one signed contract. The author began working as an AI engineer in June 2026. Those figures do not guarantee the same result for every candidate, but they provide a concrete example of the system being used as an actual job-search tool rather than a demo.
The Core Workflow
The main workflow is deliberately short:
/setup -> /scrape -> /apply <url>
| | |
profile search evaluate fit
files portals draft documents
review and revise
/setup creates the foundation. It can read a populated documents/ directory containing a CV, LinkedIn export, diplomas, references, and previous applications. It can also import a single CV pasted into the chat or collect information through an interview. The documents-folder path is idempotent, so it can be run again as more source material becomes available.
/scrape searches the configured job portals, removes duplicate postings, and presents results sorted by fit. If the result set is too large to inspect manually, /rank batch-scores the postings and returns a ranked shortlist. The evaluation considers skills, experience, culture, location, and career alignment rather than matching keywords alone.
Finally, /apply accepts a job URL or pasted job description and runs the complete application pipeline. This separation matters: job discovery, prioritization, and document generation are different decisions, and combining them into one unstructured prompt makes it harder to review the system's reasoning.
What /apply Does Differently
The application command follows a drafter-reviewer pattern:
- Parse the job posting.
- Evaluate the candidate's fit across the configured criteria.
- Draft a tailored CV and cover letter.
- Spawn a second Claude agent with a fresh context to research the company and critique the drafts.
- Revise the documents based on that review.
- Compile and inspect the final PDFs.
- Run an ATS-oriented text-layer check and present a verification checklist.
The second-agent design is important. A single pass can produce fluent but generic writing while missing a requirement, weakly framing a transferable skill, or making the company research sound superficial. The reviewer receives the drafts inline instead of re-reading them, which keeps dispatch relatively token-efficient. Verification runs once at the end, although the newer PDF inspection stage spends some of that saved budget on rendering and layout iteration.
The workflow also enforces a factuality rule: claims in the CV and cover letter must be supported by the candidate profile. A missing skill remains a gap. The system does not add unsupported keywords simply to improve an apparent match.
PDF Layout Is Part of Correctness
The repository treats generated documents as build artifacts that need testing. The CV is compiled with lualatex, while the cover letter uses xelatex because the custom cover.cls depends on fontspec. The workflow keeps iterating until the CV is exactly two pages and the cover letter is exactly one page with a visible signature and consistent fonts.
That catches failures that are invisible in source files: an entry title stranded at the bottom of a page, a cover letter spilling onto a second page, or list-item icons silently falling back to the body font. The project uses targeted LaTeX fixes such as \\needspace and \\enlargethispage, along with font-matching wrappers for list items.
When a CV exceeds two pages, content is not removed mechanically from the oldest role. Candidate lines are scored by their relevance to the target posting, uniqueness in the document, and whether the cover letter depends on them. A relevant bullet from an older role can therefore survive ahead of a recent but unrelated bullet.
The ATS check examines the PDF's embedded text rather than its visual appearance. With pypdf or pdftotext, it verifies that contact details are literal text, glyphs are not garbled, reading order is sensible, and supported posting keywords appear in the extracted document. If neither extraction tool is available, the process falls back to a visual keyword review.
Installation and Setup
The framework requires:
- Claude Code CLI, although the portal skills can also be used with other agent tools through
AGENTS.md. - Python 3.10 or newer.
- Bun for the job-search CLI tools.
- A LaTeX distribution with
lualatexandxelatex, such as TeX Live, MacTeX, TinyTeX, or MiKTeX.
For the optional ATS parser, install pypdf:
pip install pypdf
Poppler's pdftotext remains a fallback. On macOS it can be installed with brew install poppler; on Debian or Ubuntu, use apt install poppler-utils; on Windows, use choco install poppler. Minimal TeX distributions may also require the additional packages listed in SETUP.md.
After cloning the repository, install the portal tools:
gh repo fork MadsLorentzen/ai-job-search --clone
cd ai-job-search
for tool in jobbank-search jobdanmark-search jobindex-search jobnet-search linkedin-search freehire-search; do
(cd .agents/skills/$tool/cli && bun install)
done
The linkedin-search and freehire-search tools have zero runtime dependencies, so their install step is optional and only retrieves TypeScript development types.
A significant privacy detail appears in the setup instructions: a public GitHub fork cannot be made private, while /setup writes personal information such as contact details, employment history, and salary expectations into tracked files. For a personal job search, the recommended arrangement is a private repository with this project configured as an upstream remote. Fork publicly only when contributing changes back.
Extending Beyond Denmark
The shipped Jobindex, Jobnet, Jobdanmark, and Akademikernes Jobbank integrations are designed for the Danish market. The architecture is intended to be portable, however. Each portal skill is a self-contained folder under .agents/skills/ with a common search and detail CLI, --format json|table|plain output, tests, and an enabled: flag in its skill definition.
/add-portal can investigate a local job board's URL patterns, result structure, robots rules, and access restrictions, then scaffold and test a new integration. Auth-walled portals are declined, and restrictive terms receive a personal-use warning. LinkedIn provides a country-agnostic starting point through its public jobs-guest endpoints, with an explicit location flag such as -l "Berlin, Germany" or -l "Remote". The repository also includes freehire-search, which uses the freehire.me public REST API and supports region, country, and remote filters.
Portal skills copied from another fork should be read and tested before use. The project specifically recommends checking network destinations, dependencies, lifecycle scripts, filesystem access, tests, terms-of-service notes, and the enabled: setting. Since installed tools may run under pre-approved Claude Code permissions, this manual inspection is a deliberate security control.
Tracking, Interviews, and Career Planning
The framework continues after an application is generated. /outcome archives the exact posting, submitted CV, and cover letter under documents/applications/<company>_<role>/, while recording interviews, offers, rejections, or silence in the tracker. /outcome followup finds applications that have been quiet for the default 10 days and drafts a channel-appropriate follow-up. It never sends the message and limits follow-ups to two per application.
/interview builds preparation material from the documents the interviewer actually saw, previous-round feedback, company research, and interviewer research. Likely questions are mapped to real STAR examples, and missing experience is handled with honest bridge answers rather than invented stories.
For reporting, /html-report generates a self-contained offline dashboard with status, sector, channel, and funnel charts plus a filterable application table. /notion-sync offers a one-way read-only view in Notion using the official MCP server, while keeping repository files as the system of record. /gmail-sync can propose status changes from Gmail, but requires approval and cites the source email for each change.
The project also supports /expand for enriching a profile from linked public sources, /upskill for producing a prioritized skill-gap heatmap and learning plan, and a salary benchmarking script that accepts user-provided data from union statistics, Glassdoor exports, or personal research.
The Quality Multiplier Is Profile Depth
The system cannot tailor material that the profile does not contain. “Python” is weak input; “built ML pipelines for customer churn prediction in Python with scikit-learn” gives the workflow a concrete achievement, toolchain, and context to reuse.
Detailed role descriptions, measurable outcomes, project specifics, preferences, deal-breakers, and reflections on what energized or drained you all affect recommendations. The framework can support explicit targeting, but it can also discover adjacent career paths by analyzing the actual work behind job titles. Language requirements receive dedicated handling: undeclared languages can trigger a hard rejection, while a posting asking for a higher level than declared is flagged for human judgment.
That combination of structured evaluation, document generation, PDF validation, application tracking, and interview preparation is what makes ai-job-search more than a resume prompt. It is a local, inspectable operating system for a job search, with clear extension points for portals, document templates, and evaluation criteria. The project is MIT-licensed and available at github.com/MadsLorentzen/ai-job-search.