Your local-first, AI-native desktop assistant for intelligent job searching, résumé & cover-letter generation, and assisted applications. Run it fully offline with Ollama, plug in your own OpenAI, Anthropic, or Gemini key, or route it through an AI CLI agent (Claude Code, Codex, Gemini CLI).
⬇️ Download the latest release · 🌐 Live site · 🎬 Short film · 📦 Install · ✨ Features · 📚 Docs
📑 Table of contents
# 🚀 Try it → download a build for your OS:
# https://github.com/saeedkolivand/ai-job-hunter-app/releases
#
# 🛠️ Develop it → run the full desktop app from source:
git clone https://github.com/saeedkolivand/ai-job-hunter-app.git
cd ai-job-hunter-app && pnpm install
ollama pull mistral # optional: a local model for the offline Ollama provider
pnpm dev # launches the Tauri app with hot reloadNo API key required to start. Run fully offline with Ollama, or add a cloud key later in Settings → AI. New here? See Installation for prerequisites and per-OS notes.
AI Job Hunter is a desktop application built with Tauri (a Rust core with a React renderer) that brings AI-driven job hunting to your local machine. It scrapes 24 job boards (including an Adzuna/JSearch aggregator that covers walled boards like Indeed, Glassdoor, and Xing, plus company-scoped ATS boards like Greenhouse and Lever), semantically matches postings to your résumé, generates tailored cover letters and résumés with your AI provider of choice, drafts grounded answers to application questions, and tracks everything you apply to, all while keeping your data and credentials on your device.
Your résumés, generations, applications, and tracked job data live in a local SQLite database on your device (credentials live in the OS keychain); there is no app-operated backend collecting them, and no behavioural analytics. Two things happen automatically, without you asking: the app checks for updates (GitHub, 10 s after launch then every 4 h, transmitting only your app version and OS/architecture), and sends crash reports to Sentry (error + stack trace, OS, app version) when the app breaks — the latter is on by default, asked during setup, switchable off in Settings → Privacy, and redacted of paths, links, hosts, e-mails and credentials before sending. The app's other outbound calls go to services you configure or invoke: your AI provider (which receives the résumé and job text you ask it to generate from), job boards you scrape (including the Adzuna, JSearch/RapidAPI, Jooble, Apify and freehire aggregator tiers — freehire being the one that needs no key, tried last), an optional web search you explicitly enable (your provider's own, or Exa if you add a key), location autocomplete (Photon, with offline fallback), opt-in company-logo enrichment (Clearbit, default off), opt-in email-confirmation watching via IMAP (credential user-supplied and OS-keychain-backed; email content never leaves the device), and your optional profile import from GitHub or LinkedIn. Neither the update check nor the crash report ever includes your documents or job data, and the browser extension is excluded entirely from all reporting; see ADR 0005.
Twelve résumé templates in two tiers: ATS-Safe (Classic · Swiss Minimal · Academic · Meridian · Throughline · Cadence · Regent) and Design (Atelier · Portrait · Lebenslauf · Aria · Saffron)
📝 Résumé & cover-letter generation
- Streaming generation with 12 professional templates (7 ATS-Safe tier + 5 Design tier), DOCX / PDF / TXT export, ATS-safe formatting.
- Universal "thinking" view: see the model's reasoning stream live across every provider (Anthropic, OpenAI, Gemini, Ollama, CLI agents), not just one.
- Background generation: switch tabs, close the modal, or navigate away; generation keeps running and the result is there when you come back.
- Smaller PDFs: fonts are glyph-subsetted per export (only the characters you actually use), shrinking a typical résumé PDF from ~3 MB to ~120 KB.
🎯 Matching, ATS & analysis
- Job search & matching: Keyword search over your scraped postings, with vector similarity and LLM reranking re-ordering the results when semantic ranking is enabled (off by default); separately, each posting is scored against your résumé.
- Résumé analysis: ATS scoring, skill-gap detection, language-mismatch warnings, and improvement recommendations.
🤖 Autopilot & application tracking
- Autopilot workflows: define a search (board, query, location, schedule, filters); it finds and scores matching jobs.
- Dedup + New/Applied badges: re-running a workflow merges results by URL: prior finds are kept, genuinely new ones are badged New, and jobs you've generated for are badged Applied (derived automatically from your saved generations).
- One-click tailoring: from any found job, click Tailor to seed the AI Generate workspace, open the posting, and mark it applied on any board. Generate a tailored résumé + cover letter and résumé-grounded answers to common application questions, with optional company research.
- Documents / Activity: every generated application is stored as a single per-job record (résumé, cover, answers, brief, board, date) and browsable in the Documents page (Résumés / Cover Letters / Activity tabs).
🌐 Browser extension (save jobs one-click)
- Now on the Chrome Web Store and Firefox Add-ons: Install for Chrome · Install for Firefox.
- MV3 extension for Chrome & Firefox: while browsing any job board, click the extension button to import the job into your saved applications.
- One-click import: click "Import this job" on any board page; the extension automatically captures the rendered DOM when possible (bypassing bot-walls on logged-in boards like LinkedIn/Indeed) and falls back to URL-only on restricted pages.
- Assisted autofill (opt-in, default off): fill empty contact fields on any application with your saved profile; enabled only in Settings → Accounts → Browser extension.
- Transport: native messaging (primary) with loopback WebSocket fallback. Paired with mutual HMAC-SHA256 authentication (token used only as an HMAC key, never transmitted). Zero remote backend, zero analytics.
- See apps/extension/README.md for setup, dev pairing, and architecture. Try it locally: see the extension's Local development & testing guide.
🔎 Company research (opt-in)
- Before writing a cover letter or answers, optionally research the company on the web, using your active AI provider's own web search (or the free Ollama Web Search API on Ollama) → a concise, factual brief: what they do, size/stage, products, mission, recent news. Providers with no web search of their own can fall back to Exa with your own key.
- Default off, cached for a week, and treated as untrusted reference context: it never becomes a candidate fact, and the no-fabrication grounding rule still governs every claim.
🧠 AI providers & local tuning
- Multi-provider: Ollama (local), OpenAI, Anthropic, Gemini, any OpenAI-compatible server (LM Studio, vLLM, remote Ollama), plus headless CLI agents (Claude Code, Codex, Gemini CLI).
- Per-model local limits: analyze a local model's real context window via Ollama's
/api/show, then set the context window + max output tokens per model, with a hardware-lag warning so large prompts aren't silently truncated.
🔒 Privacy & data
- Credentials in the OS keychain: encrypted, never in plain text or config files.
- All data local: jobs, résumés, generations, applications in a local SQLite database; no behavioural analytics. Two things go out automatically: an update check and an opt-out crash report.
- Full reset: one action wipes every store (documents, generations, autopilots, contact/job preferences, caches, keychain entries) back to a clean install.
- Multilingual: UI in English & German; generation in 11 languages: en, de, fr, es, it, tr, pt, ru, zh, ja, ko.
Resumé generation uses a staged pipeline where the model decides how to present verified candidate evidence, never what the candidate has done. Three structural enforcements make this real:
-
Grounding rule, enforced in Rust. Every claim in the output is traced back to the source resumé or job ad; the evidence map is selected by Rust from the source résumé itself, with no model call, so a quote is always source text. See
apps/desktop/src-tauri/src/pipeline/resume/(quality pipeline orchestration) andstages::evidence(deterministic evidence selection). -
Every Critical is deterministic, never a model opinion. The validators in
apps/desktop/src-tauri/src/validate/content/check factual, structural, and language alignment deterministically — dropped roles, unsourced metrics, unsupported dates, language misalignment. A model cannot emit a Critical; a Critical means something is provably wrong in the source material. Warnings (unsourced terms, keyword density) are advisory; only Criticals park a run. SeeCONTENT_ISSUE_CODESfor the registry andADR-032for the design. -
Fabrication-review gate: refuse to save rather than save with a flag. When validation finds Criticals, the run parks at
needsReview. The UI shows each flagged claim; you decide Remove (delete the line) or Keep. Nothing is silently dropped, and nothing is saved with an unresolved flag — every claim awaiting your verdict blocks export until you act. SeeFabricationReview(the review UI) andSaveVerdict::Refused(the gate). -
Untrusted-text fencing: anything you did not type gets wrapped. The principle is: every blob entering a prompt without your fingerprint is fenced as untrusted — job ads (
<job_posting>), prior-stage model outputs (strategy artifact, evidence map), web-sourced company research, and rerank candidates. Each fence instructs the model to ignore instructions within it and treat the text as reference only. The fencing mechanism and patterns are inprompt_fence.rs(the OWASP LLM01 chokepoint); implementations includebuildJobAdBlockfor job ads and pipeline prompts for artifacts. SeeADR-010for the design.
What is measured and what is not. The offline eval harness (tests/eval.rs) measures the deterministic validators against planted-defect fixtures: every planted issue must be recalled at its claimed severity, and truthful documents must raise zero Criticals. This is in CI. What is not measured: generation quality needs a live model (a live corpus of A/B runs); retrieval quality has no labelled dataset; search's dense arm re-orders keyword hits and only retrieves when keyword search finds nothing (see ADR-039 for search's exact scope). This repo's posture is: state which is which, measure the deterministic layer, and keep improving where the data allows.
Switch providers at any time in Settings → AI:
| Provider | Models | Notes |
|---|---|---|
| Ollama (local) | mistral, llama3.2, deepseek-r1, any Ollama model | No API key needed; fully offline; per-model context/output limits |
| OpenAI | GPT-4o, o-series, GPT-4 Turbo | Requires API key |
| Anthropic | Claude (Sonnet / Opus), extended thinking | Requires API key; reasoning streamed to the thinking view |
| Google Gemini | Gemini 2.5 / 1.5 (Pro, Flash) | Requires API key; thinking models supported |
| OpenAI-compatible | Any (LM Studio, vLLM, remote Ollama, …) | Custom base URL |
| CLI agents (local) | Claude Code, Codex, Gemini CLI | Run headless via the installed CLI: no API key (uses the CLI's own login) |
API keys are stored encrypted in the OS keychain. CLI agents run as a headless subprocess and reuse whatever login that CLI already has, so they need no key in the app.
Download a released build (recommended)
Grab the latest installer for your OS from the Releases page.
macOS: open the .dmg and drag the app into Applications. Because the app isn't notarized by Apple, Gatekeeper may refuse to open it the first time ("app is damaged and can't be opened"). Clear the quarantine attribute once:
xattr -cr "/Applications/AI Job Hunter.app"Windows / Linux: run the installer / AppImage from the Releases page.
Homebrew (macOS)
Releases ship signed macOS .dmgs, and the repo doubles as its own Homebrew tap (the Casks/ai-job-hunter.rb cask). Install with:
brew tap saeedkolivand/ai-job-hunter-app https://github.com/saeedkolivand/ai-job-hunter-app
brew install --cask ai-job-hunterThe cask clears the Gatekeeper quarantine flag for you (the app isn't notarized). Prefer a one-off download? Grab the .dmg straight from the Releases page.
The cask pins per-arch
sha256checksums for a verified install, tracking the latest release that ships macOS.dmgs. Since the installer build is manual, bump the caskversion+ both checksums when a newer build publishes dmgs (brew bump-cask-pr).
Build from source
Prerequisites
| Requirement | Version | Notes |
|---|---|---|
| Node.js | 20+ | LTS recommended |
| pnpm | 11+ | npm install -g pnpm |
| Rust toolchain | stable | rustup install stable |
| Ollama | latest | ollama.com, for local AI |
git clone https://github.com/saeedkolivand/ai-job-hunter-app.git
cd ai-job-hunter-app
pnpm install
# Pull a local model (optional — only for the Ollama provider)
ollama pull mistral # or: ollama pull llama3.2
# Start the full Tauri desktop app with hot reload
pnpm devTroubleshooting
| Symptom | Fix |
|---|---|
| macOS: "app is damaged and can't be opened" | Not notarized. Clear quarantine once: xattr -cr "/Applications/AI Job Hunter.app" |
| No models in the picker / "select a model" | Start Ollama and ollama pull <model>, or add a cloud key in Settings → AI |
| Company research does nothing | It's opt-in and uses your AI provider's own web search; on Ollama add the free Ollama key (Settings → AI). For a provider with no search of its own, add an Exa key in the same place. Without a usable search the toggle reads off and generation proceeds without a brief |
| Scraping can't find a browser | The app auto-detects an installed Chromium browser (detect_system_chrome). If none is found, install Chrome or Edge, or set the CHROME env var to the browser executable path. |
pnpm dev fails to build the Rust core |
Ensure the stable Rust toolchain is installed (rustup install stable) and re-run |
| Need to report a crash or bug | Open Support → Contact/Feedback → Export Bundle: saves ajh-diagnostics-<date>.zip (system info plus redacted crash/app logs; no résumés, API keys, job data, or database). Attach it to a new GitHub issue. For security vulnerabilities use SECURITY.md instead; do not post them publicly. |
Generate a tailored résumé / cover letter
1. Open the app → AI Generate
2. Paste your résumé text, or upload a PDF/DOCX/TXT file
3. Paste the job ad text, or upload a job description file
4. Click Continue → the app detects languages, role, company, top requirements
5. Choose a template + style; optionally enable "Research the company"
6. Generate → watch streaming output (with live reasoning) → export as DOCX / PDF / TXT
Run Autopilot & answer application questions
1. Autopilot → New → set board, query, location, schedule, filters
2. Run it → found jobs appear, scored and deduped (New badges on fresh results)
3. Open a found job → Tailor:
• generate a tailored résumé + cover letter (target: Both)
• pick application questions → get résumé-grounded answers
• the job flips to "Applied" and is saved in Documents → Activity
Scrape boards & search semantically
1. Jobs → Scrape → select boards (e.g. LinkedIn + Greenhouse + Aggregator)
2. Query + location; for company-scoped ATS boards (Greenhouse, Lever, etc.), enter company slugs
3. Click Start → results stream into the jobs table
4. Ranked search over the scraped postings — keyword by default, with vector similarity and LLM reranking *re-ordering* the results when semantic ranking is enabled
Note: Company-scoped ATS boards require company slugs instead of free-text keywords. The Aggregator (Adzuna + JSearch) replaces direct scraping of anti-bot boards (Indeed, Glassdoor, Xing); free Adzuna keys available at https://developer.adzuna.com. See docs/knowledge/scraping-domain.md for the full board list and configuration details.
CLI agent — `ajh-tauri agent ` (headless mode)
Run commands from your shell or an LLM agent, with the desktop app already running:
# Find the best matches across all autopilots
ajh-tauri agent best-matches --limit 10
# Get full details for one job by URL
ajh-tauri agent job "https://example.com/job/123"
# Export your profile for autofill (requires opt-in consent)
ajh-tauri agent profile
# List all autopilots and their status
ajh-tauri agent automations
# Generic command dispatch (advanced: read/reversible commands, irreversible with proof)
ajh-tauri agent call "namespace:command" --input '{"key":"value"}'
# Show help
ajh-tauri agent --helpPlatform-specific paths: The CLI is the same binary as the desktop app, no separate install needed. On Linux and Homebrew/macOS it is already on PATH. On macOS with dmg drag-install, add the path to your shell profile:
export PATH="/Applications/AI Job Hunter.app/Contents/MacOS:$PATH"On Windows, the NSIS installer adds its per-user install directory to your PATH. If ajh-tauri still isn't found, invoke it by full path — ~/.ajh-agent/agent.json carries it as exePath (the app rewrites that file on every launch).
In the app: Settings → Developer shows this binary's path and ready-to-copy registration commands for Claude Code and Codex with that path already filled in.
Requirements: Bridge-backed calls need the app running; --help, MCP startup (initialize) and the local commands tool do not. The CLI communicates over a local loopback bridge with mutual HMAC-SHA256 authentication (the pairing token is used only as an HMAC key and is never sent on the wire). Run ajh-tauri agent --help to see all verbs, exit codes, and error sentinels. For the design rationale see ADR-037 and ADR-038; for the full policy table, use the MCP commands tool or read apps/desktop/src-tauri/src/extension_bridge/agent_cli/policy.rs (agent schema lists only the five curated resources).
LLM agents (MCP mode): The CLI can run as a standard MCP stdio server — any MCP client can run it, not just the two documented below:
Claude Code (recommended)
Use claude mcp add --scope user to register the server (user scope = per-machine, runs only when you authorize):
claude mcp add --scope user ai-job-hunter -- "/path/to/ajh-tauri" agent mcpOn Linux/macOS with Homebrew, ajh-tauri is already on PATH. On Windows and macOS dmg, substitute the exePath from ~/.ajh-agent/agent.json — keep the double quotes, because the default install directory contains a space on both platforms and an unquoted path registers a server that never connects. The in-app Settings → Developer card generates the same command already quoted (shellDoubleQuoted in apps/desktop/src/renderer/features/settings/lib/agent-cli-snippets.ts).
By default the server is read-only: the model can search postings, read your profile and automations, and enumerate the command table. Two launch flags open the write tiers, each a superset of the last:
# undoable state changes — track an application, edit your profile, import a document
claude mcp add --scope user ai-job-hunter-write -- "/path/to/ajh-tauri" agent mcp --allow-reversible
# additionally: destructive actions and AI spend — delete documents, remove provider keys, run generation against your API budget
claude mcp add --scope user ai-job-hunter-unrestricted -- "/path/to/ajh-tauri" agent mcp --allow-irreversibleThe flags only decide which tools the model can see; every write still goes through the app's own policy table and, for destructive commands, the confirm ceremony (ADR-038).
Codex
Add to your ~/.codex/config.toml (or create it):
[mcp_servers.ai-job-hunter]
command = 'ajh-tauri'
args = ["agent", "mcp"]If ajh-tauri is not on your PATH, set command to the exePath from ~/.ajh-agent/agent.json — keep it single-quoted, because a TOML literal string has no escape sequences and a Windows path is full of backslashes (in a double-quoted basic string they read as invalid escapes and the whole file stops parsing) — unless the path contains an apostrophe, in which case copy the Settings card's output verbatim (it falls back to an escaped basic string). The Settings → Developer card emits the correctly-quoted form for you (tomlString in apps/desktop/src/renderer/features/settings/lib/agent-cli-snippets.ts). The same --allow-reversible / --allow-irreversible flags go in args.
Any MCP client
Most other MCP clients (Claude Desktop, Cursor, Windsurf, Gemini CLI, LM Studio, Jan, etc.) read a mcpServers JSON block from their own config file rather than taking a CLI command:
{
"mcpServers": {
"ai-job-hunter": {
"command": "/path/to/ajh-tauri",
"args": ["agent", "mcp"]
}
}
}The same tier flags as above go at the end of args — --allow-reversible (server name ai-job-hunter-write) or --allow-irreversible (server name ai-job-hunter-unrestricted). The Settings → Developer card copies this block with your real path already filled in — on Windows the backslashes must be doubled (C:\\Users\\…) — or just copy the block from the Settings card, which does it for you.
URL-only clients: agent mcp --http <port> runs the same server over MCP Streamable HTTP instead of stdio, for a client that only takes a URL rather than launching a subprocess. It binds 127.0.0.1 only — no flag can name a different host, so there is nothing to configure there — and prints one {"transport":"http","url":"...","token":"..."} line to stdout, once, before it starts serving; that token is never written to a file, so a client that loses it has to relaunch the server. Every POST /mcp needs Authorization: Bearer <token>, and any request carrying an Origin header is refused outright (this is for local scripts and agent frameworks, never a browser page's fetch). It answers one JSON-RPC request per POST — no streaming, no sessions, GET/DELETE both 405 — and shuts down the same way the stdio mode does, on Ctrl-C or stdin EOF.
Tool results are sent to the model and stored in the client's transcript. Queries like profile and documents_get_text return PII; the model can access job postings, your résumé, and application records. Use these tools only when you ask the agent to retrieve that data.
Important: The first argument to the MCP server must be agent (e.g., agent mcp), or the desktop app launches instead.
The app uses the OS keychain for secrets instead of .env files. Keys and credentials are set in the UI and encrypted via Tauri's keychain plugin.
| Setting | Location | Description |
|---|---|---|
| AI provider + key | Settings → AI | Ollama / OpenAI / Anthropic / Gemini / compatible |
| Local model limits | Settings → AI | Context window + max output, per Ollama model |
| Ollama account key | Settings → AI | Optional: Ollama Cloud models + company research |
| Adzuna/JSearch keys | Settings → Jobs | Optional: provider registry with Adzuna (primary, free) and JSearch (paid fallback). Covers anti-bot boards like Indeed, Glassdoor, Xing, and more. See docs/knowledge/scraping-domain.md for details. |
| Performance mode | Settings → Performance | Low / Balanced / Performance |
| Language | Settings → General | UI and generation locale |
| Layer | Technology |
|---|---|
| Desktop shell | Tauri 2.x: Rust core + React renderer |
| UI framework | React 19, TypeScript 7 |
| Routing | TanStack Router 1.x (file-based) |
| Server state | TanStack Query 5.x |
| Client state | Zustand 5 |
| Styling | Tailwind CSS v4 + CSS custom properties |
| Animations | motion/react |
| Build system | Vite 8 + Turborepo (monorepo) |
| Package manager | pnpm 11 (workspaces) |
| Local AI | Ollama |
| Relational DB | SQLite via rusqlite (Rust core) |
| Search & ranking | SQLite FTS5 (BM25) + optional vector similarity, fused with RRF |
| Browser automation | chromiumoxide (Rust); Playwright for e2e only |
| Document generation | Typst engine (export/typst_engine/) + docx-rs (Rust) |
| Validation | Zod (shared schemas → generated Rust structs) |
Architecture in one line: the React renderer never calls the OS directly; it talks to the Rust core over a typed IPC contract.
React renderer → service hook (React Query) → tauri-client → Rust #[tauri::command] → core (scrape · AI · export · DB)
apps/desktop/src/renderer/services apps/desktop/src-tauri/src
IPC request shapes have a single source of truth: Zod schemas in packages/shared, from which pnpm gen:ipc generates the matching Rust structs, so the TypeScript and Rust sides can't drift.
Add a new IPC capability (5 hand-synced touchpoints)
packages/shared/src/ipc/contracts/*.ts: add the method signature.apps/desktop/src-tauri/src/commands/*.rs: implement the#[tauri::command]and register it inshell/handler.rs.apps/desktop/src/tauri-client/namespaces/*: wire theinvoke(...)call.apps/desktop/src/renderer/services/*: add the React Query service hook.- If the request has a new shape: add a Zod schema and run
pnpm gen:ipc.
Add an AI provider / a job board (config + adapter, no business-logic changes)
- AI provider: implement the provider adapter and register it; the rest of the app routes through the centralized provider abstraction (
Completer/ streaming contract). Reasoning, limits, and job state are normalized at the adapter boundary, so the renderer holds one contract and zero per-provider branching. - Job board: add an entry to the scraper registry (
scraping/boards/mod.rsSCRAPERS); discovery is registry-driven, so no caller changes.
Conventions & guardrails
- PRs only: never push to
main; Conventional Commits; ESLint + commitlint + architecture tests gate every change. - Ports & adapters: UI imports
@ajh/uiprimitives and service hooks, neverwindow.apidirectly; design-system tokens (text-brand, motion tokens) over hardcoded values. - Backend owns business logic: Rust-first; the renderer is a thin client.
- See CLAUDE.md for the enforced rules and docs/PATTERNS.md for the patterns.
Knowledge base & AI agent system
This repo ships a knowledge base under docs/knowledge/: domain notes plus architecture decision records (ADRs), plus a Claude Code agent system under .claude/ (23 agents, a write-capable author + an independent critic per domain, plus commands). When in doubt about why something is built a certain way, the ADRs are the fastest answer.
For a visual walkthrough of the agent system (the fleet map, how a prompt is routed to an agent, the author→critic pipeline, and a with/without-agents comparison), open the interactive apps/landing/agent-system.html.
A graphify knowledge graph (graphify-out/) can also be queried directly or wired as an optional, opt-in local MCP server; see docs/DEVELOPMENT.md.
ai-job-hunter-app/
├── apps/
│ ├── desktop/ # Main desktop app (Tauri shell: Rust core + React renderer)
│ │ ├── src-tauri/ # Rust core (commands, scraping, AI, export, DB, extension bridge)
│ │ └── src/renderer/ # React frontend
│ │ ├── features/ # Feature-scoped components
│ │ ├── routes/ # TanStack Router pages
│ │ ├── services/ # React Query IPC hooks
│ │ ├── lib/ # Utilities (generate, motion, i18n, machines)
│ │ ├── store/ # Zustand stores
│ │ └── providers/ # React context providers
│ └── extension/ # Browser extension (MV3, Chrome + Firefox) — job import via loopback WS
├── packages/
│ ├── shared/ # IPC contracts, Zod schemas, shared types, extension protocol
│ ├── ui/ # @ajh/ui — React component library
│ ├── prompts/ # Provider-aware, locale-driven AI prompt templates
│ ├── translations/ # i18n config + locale strings (en, de, …)
│ └── test-ids/ # @ajh/test-ids — central TEST_IDS map
├── docs/ # Documentation + knowledge base (ADRs)
├── turbo.json # Turbo build configuration
├── pnpm-workspace.yaml # pnpm workspaces
└── package.json # Root scripts
pnpm dev # Start the Tauri dev app (full stack)
pnpm build # Build everything (Turbo)
pnpm build:packages # Build shared packages only (excludes Tauri)
pnpm build:chrome # Build the browser extension (Chrome / MV3)
pnpm build:firefox # Build the browser extension (Firefox / MV3)
pnpm package # Package the desktop installers
pnpm typecheck # TypeScript check across the monorepo
pnpm test # Run the Vitest suite (test:watch / test:coverage variants)
pnpm lint:strict # Lint with --max-warnings 0 (CI mode); lint:fix to autofix
pnpm format # Prettier format (format:check to verify)
pnpm gen:ipc # Regenerate Rust IPC structs from the shared Zod schemas
pnpm gen:workflows # Regenerate the CI workflow catalog + status badges
pnpm clean # Remove dist / out / .turbo + caches
pnpm storybook # Run the @ajh/ui StorybookLive status of every GitHub Actions workflow. See
.github/workflows/README.md for the full catalog:
what each one does, its triggers, and whether it gates merges (only ✅ CI OK does).
Both are generated from the workflow files by pnpm gen:workflows.
📊 Mission Control: a verdict-first, full-repo dashboard (delivery, work, quality, community) updated nightly from a GitHub API snapshot (with live fallback when signed in with a fine-grained PAT).
See CONTRIBUTING.md for branching, commit conventions, code style, and PR guidelines. Quick rules:
- All changes go through PRs; never push directly to
main. - Use Conventional Commits (
feat:,fix:,chore:, …). - Run
pnpm lint:fix && pnpm typecheckbefore pushing; ESLint errors block commits.
| Document | Description |
|---|---|
| apps/desktop/README.md | Desktop app architecture, directory map, Rust/React setup |
| apps/extension/README.md | Browser extension (MV3): job import, local dev pairing, permissions |
| docs/ARCHITECTURE.md | System design, data flow, diagrams |
| docs/PATTERNS.md | IPC, state machines, AI streaming, search patterns |
| docs/API.md | IPC namespaces + commands |
| docs/EXPORT_TEMPLATES.md | Templates, theming, PDF/DOCX export |
| docs/DESIGN_SYSTEM.md | Tokens, components, motion, theming |
| docs/DEVELOPMENT.md | Local dev environment setup |
| docs/DEPLOYMENT.md | Building and releasing installers |
| docs/ARCHITECTURE_STATUS.md | Implementation status tracker |
| docs/SCRAPING_ENDPOINTS.md | Job-board scraping endpoint reconnaissance snapshot (21 boards, 2026-07-01) |
| docs/knowledge/ | Knowledge base + architecture decision records (ADRs) |
| SECURITY.md | Security policy & vulnerability reporting |
| CONTRIBUTING.md | Code style, branching, PR process |
| How it Works | How the AI Job Hunter works end-to-end (interactive walkthrough) |
| Architecture Map | Interactive architecture map of the AI Job Hunter |
| Agent System | Interactive agent-fleet walkthrough: 23 paired author+critic agents, intake→delegation routing, per-task pipeline |
| THE CREATURE | THE CREATURE: a hand-drawn doodle about the recruiter you summon |
This project is built by a guy who is, himself, still unemployed, a cry for help made between rejections. If it's saving you time or sanity, consider a voluntary gift:
GitHub Sponsors · Ko-fi · PayPal
- Respect each site's Terms of Service. Some job boards allow only manual access or their official APIs; automated collection (or signing in with your own account to scrape) may breach their terms and can get your account suspended. You choose which boards to enable and accept that responsibility.
- Not affiliated. AI Job Hunter is an independent project, not affiliated with, endorsed by, or sponsored by LinkedIn, Indeed, Glassdoor, Xing, StepStone, or any other job board or company named in this repository. Product names and trademarks belong to their respective owners.
- Your data stays yours. Processing is local-first and single-user; the project maintainers do not receive, store, or process your data. Bring-your-own-key means AI prompts go only to the provider you configure.
- No warranty. Provided "as is" under the Apache License 2.0, without warranty of any kind and without liability for how it is used. This is not legal advice.
Found a vulnerability? Please report it privately: see SECURITY.md. Don't open a public issue for security reports.
Apache License 2.0 (SPDX: Apache-2.0): open source, free for any use including commercial, with an explicit patent grant. Effective 2026-09-07, replacing PolyForm Noncommercial 1.0.0; reason: the free code-signing programmes for open-source projects (SignPath Foundation, Certum) require an OSI-approved license, and PolyForm Noncommercial is not one.
One carve-out: the vendored ATS company datasets under apps/desktop/src-tauri/ats-slugs/ are third-party data under CC BY-NC 4.0, not Apache-2.0 — see their README before redistributing commercially.
Contributions welcome: see CONTRIBUTING.md.
Both charts are generated in-repo by 📈 Repo Charts and served from the badges branch — no third-party service.
