Step-by-step instructions for getting the AI Job Search framework running.
Install Claude Code (Anthropic's CLI for Claude):
npm install -g @anthropic-ai/claude-codeYou'll need an Anthropic API key or a Claude Pro/Team subscription. See the Claude Code docs for details.
Python 3.10+ is required for the salary lookup tool. Check with:
python --versionOn Windows, py --version is often the most reliable check if python is not on your PATH.
The job portal CLIs (four Danish portals plus the country-agnostic LinkedIn tool) are written in TypeScript and run with Bun.
- macOS/Linux:
curl -fsSL https://bun.sh/install | bash- Windows PowerShell:
powershell -ExecutionPolicy Bypass -c "irm https://bun.sh/install.ps1 | iex"If you prefer a package manager, winget install Oven-sh.Bun also works on Windows.
Install a LaTeX distribution to compile the generated .tex files to PDF:
- Windows: MiKTeX
- macOS: MacTeX
- Linux:
sudo apt install texlive-fullorsudo dnf install texlive-scheme-full
The CV compiles with lualatex (pdflatex often fails on modern MiKTeX installs with fontawesome5 font-expansion errors). The cover letter compiles with xelatex because cover.cls requires fontspec for its custom Lato/Raleway fonts.
Full TeX distributions work out of the box, but minimal distributions need a few extra packages before the stock templates compile.
On macOS, a user-level TinyTeX install avoids a system-wide installer and does not require sudo:
curl -fsSL https://yihui.org/tinytex/install-bin-unix.sh -o /tmp/tinytex-install-bin-unix.sh
sh /tmp/tinytex-install-bin-unix.sh /tmp --no-path
export PATH="$HOME/Library/TinyTeX/bin/universal-darwin:$PATH"Then install the template dependencies:
tlmgr install \
moderncv fontawesome5 fontawesome6 academicons import luatexbase pgf \
titlesec textpos xltxtra xunicode cite realscriptsFor BasicTeX/MacTeX, make sure the TeX binary directory is on PATH first (for example via /Library/TeX/texbin), then run the same tlmgr install ... command.
Quick smoke tests after setup:
cd cv && lualatex -interaction=nonstopmode -halt-on-error main_example.tex && cd ..
SMOKE_DIR="$(mktemp -d /tmp/ai-job-cover-smoke.XXXXXX)"
cp -R cover_letters/cover.cls cover_letters/OpenFonts "$SMOKE_DIR/"
cat >"$SMOKE_DIR/cover_smoke.tex" <<'EOF'
\documentclass[]{cover}
\begin{document}
\namesection{Test}{Candidate}{test@example.com}
\companyname{Example Company}
\companyaddress{123 Hiring Street\\Example City}
\currentdate{\today}
\lettercontent{Dear Hiring Manager,}
\lettercontent{This smoke test verifies that xelatex can load cover.cls and the bundled fonts.}
\closing{Sincerely,}
\signature{Test Candidate}
\end{document}
EOF
(cd "$SMOKE_DIR" && xelatex -interaction=nonstopmode -halt-on-error cover_smoke.tex)/apply runs an ATS parseability check on the compiled CV: it extracts the PDF's text layer and verifies contact details, reading order, and keyword coverage the way an applicant-tracking system sees them. This uses pdftotext from poppler, which is not part of TeX distributions:
- macOS:
brew install poppler - Debian/Ubuntu:
sudo apt install poppler-utils - Windows:
choco install poppler
If pdftotext is missing, /apply skips the mechanical check with a warning and falls back to a visual keyword review — everything else works normally.
gh repo fork MadsLorentzen/ai-job-search --clone
cd ai-job-searchOr manually: fork on GitHub, then clone your fork.
Run these from the repository root.
- PowerShell:
$tools = @("jobbank-search", "jobdanmark-search", "jobindex-search", "jobnet-search", "linkedin-search")
foreach ($tool in $tools) {
Set-Location ".agents/skills/$tool/cli"
bun install
Set-Location "..\..\..\.."
}- Bash / zsh / Git Bash:
for tool in jobbank-search jobdanmark-search jobindex-search jobnet-search linkedin-search; do
cd .agents/skills/$tool/cli && bun install && cd ../../../..
doneFor linkedin-search the install is optional: it has zero runtime dependencies and runs with plain bun; bun install only pulls TypeScript dev types.
If you're outside Denmark, you can generate an equivalent search skill for your local job board with /add-portal — it scaffolds the same CLI structure for any public portal and test-runs a live query before registering. See the "Job search tools" section in the README.
Start Claude Code in the repository:
claudeThen run the onboarding:
/setup
Claude will offer three paths:
- Path A (documents folder): Add your CV, LinkedIn export, diplomas, references, or past applications under
documents/. Claude reads and cross-references them before proposing profile updates. This is best when you have several source files. - Path B (single CV import): Share one CV/resume by mentioning the file with
@or pasting the text. Claude extracts it and asks follow-up questions for anything missing. - Path C (interview mode): Answer structured interview questions section by section.
All three paths produce the same result: fully populated profile files.
| File | Content |
|---|---|
CLAUDE.md |
Your full candidate profile |
01-candidate-profile.md |
Structured education, experience, skills |
02-behavioral-profile.md |
Behavioral assessment |
04-job-evaluation.md |
Personalized skill match areas and career goals |
05-cv-templates.md |
Profile statement templates for your background |
07-interview-prep.md |
STAR examples from your experience |
cv/main_example.tex |
Your LaTeX CV with actual details |
search-queries.md |
Job search queries for /scrape |
You can update specific sections later:
/setup --section skills
/setup --section experience
/setup --section search
The --section search option is especially useful as your priorities evolve. It re-runs the search configuration interview and suggests role types you may not have considered based on your full profile.
If you have salary data (from a union, salary survey, Glassdoor, or personal research):
- Option A: Create
salary_data.jsonmanually in the repo root (seetools/README_SALARY_TOOL.mdfor the format) - Option B: Convert from Excel:
pip install openpyxl python tools/convert_salary_excel.py path/to/salary-data.xlsx --source "My Salary Data 2025"
This creates salary_data.json which the /apply workflow uses for salary benchmarking. If you skip this step, salary lookup is simply omitted.
Find a job posting you're interested in, then:
/apply https://jobindex.dk/job/1234567
Or paste the job description directly:
/apply [paste job posting text here]
Claude will:
- Evaluate the fit against your profile
- Ask if you want to proceed
- Draft a tailored CV and cover letter
- Have a reviewer agent critique the drafts
- Revise and present the final output
After /apply creates the LaTeX files:
# Bash / zsh / Git Bash
cd cv && lualatex main_<company>.tex && cd ..
cd cover_letters && xelatex cover_<company>_<role>.tex && cd ..# PowerShell
Set-Location cv; lualatex main_<company>.tex; Set-Location ..
Set-Location cover_letters; xelatex cover_<company>_<role>.tex; Set-Location ..These commands apply to the stock templates (moderncv CV, cover.cls cover letter). If you'd rather use your own LaTeX template, run /add-template — it captures the template's compile engine, fonts, style rules, and page limit, test-compiles it, and wires it into /apply. See the "LaTeX templates" section in the README.
This is expected if you haven't set up salary benchmarking. The /apply workflow skips this step automatically.
Make sure Bun is installed and you ran bun install in each CLI directory. The tools require network access to fetch job listings.
- CV: uses
lualatex(pdflatex often fails on modern MiKTeX withfontawesome5font-expansion errors; lualatex handles the same sources cleanly) - Cover letter: uses
xelatex(for custom fonts inOpenFonts/fonts/) - Make sure your LaTeX distribution includes the
moderncvpackage
The cover letter template expects fonts in cover_letters/OpenFonts/fonts/. Make sure this directory exists and contains the Lato and Raleway font files.
Shared Claude Code permissions now live in .claude/settings.json (scoped to bun run and python salary_lookup.py). Earlier versions of this repo committed a broader .claude/settings.local.json that pre-approved Bash(curl:*), Bash(python:*) and Bash(bun:*). If you cloned before that change, git leaves the old file behind in your working copy, and its permissions still apply on top of settings.json. Delete it (or trim it to your own personal overrides):
rm .claude/settings.local.json