# tarunlnmiit/autopilot-jobhunt architecture

> Architecture diagram of the GitHub repository tarunlnmiit/autopilot-jobhunt, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-06-24). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/tarunlnmiit/autopilot-jobhunt
- Repository: https://github.com/tarunlnmiit/autopilot-jobhunt

## Overview

1. **Overall shape**
   This is a **local-first Python 3.11+ job-hunting automation package**. It has two main entry surfaces: a **CLI** (`job_hunt.main`) and an optional **MCP server** (`job_hunt.mcp_server`) for Claude Code/Desktop. Most logic lives in a shared internal package so both interfaces drive the same backend.

2. **Runtime boundary: packaged code vs user workspace**
   The repo ships **templates and defaults** in `job_hunt/data/` and at the root (`config.example.json`, `companies.json`, `.env.example`). Real runtime data lives in a user-owned workspace:
   - `config.json` / `.env` for secrets and preferences
   - `resume/YOUR_RESUME.md` for the candidate profile
   - `state/` for scan state / deduplication
   - `output/` for exported or drafted artifacts  
   This is a file-based system, not a database-backed app.

3. **Company discovery input**
   `companies.json` is the main catalog of target employers and careers pages. The scanner reads this list to decide which sites to query. The README indicates 130+ preconfigured companies, and users can customize this list to change the search universe.

4. **Job scanning subsystem**
   `job_hunt.scanner` orchestrates the nightly scan flow:
   - iterates through company careers pages
   - discovers new job URLs
   - fetches job details
   - deduplicates against prior state
   - hands jobs off for scoring  
   The README says this uses **TinyFish API** as the discovery/fetch layer, so external scraping/discovery is an architectural dependency, not an implementation detail.

5. **LLM scoring boundary**
   `job_hunt.llm_utils` abstracts the LLM backend. The scanner uses it to score jobs against the full resume and job description, producing a **0–100 fit score** plus a short rationale. The architecture supports multiple providers:
   - **OpenRouter** with fallback models
   - **Claude CLI**
   - **Anthropic API**  
   This backend abstraction matters because scoring is the main intelligence layer and is designed to degrade gracefully if one model is unavailable.

6. **Filtering and prioritization**
   Scored jobs are filtered by a configurable `min_score`. Only top matches are surfaced. This is the point where “all discovered jobs” become “actionable matches,” and it’s the main decision boundary between ingestion and notification/drafting.

7. **Notification path**
   `job_hunt.notifier` sends the best matches to **Telegram**. The README shows a top-N summary with scores, locations, and job URLs. This is an outbound-only notification channel: scan results are pushed to the user without requiring a browser UI or server-hosted dashboard.

8. **Drafting subsystem**
   `job_hunt.drafter` handles the on-demand workflow for a specific job:
   - reads the selected job and the user resume
   - generates tailored resume bullets
   - generates a cover letter
   - writes generated application materials to `output/`  
   This is distinct from scanning: scanning is broad and periodic; drafting is narrow and user-triggered.

9. **Shared tool layer**
   `job_hunt.tools` appears to be the protocol-agnostic service layer used by both the CLI and MCP server. This is the boundary that keeps business logic reusable regardless of whether the caller is a shell user or an AI assistant session.

10. **MCP integration**
   `job_hunt.mcp_server` exposes the same core actions through the Model Context Protocol, enabling natural-language control from Claude Code/Desktop. Architecturally, this is a thin adapter over the shared tool layer rather than a separate application.

11. **Operational automation**
   `setup_cron.sh` and the README’s nightly-scan flow show the system is meant to run unattended on a schedule. The repo also includes a `Dockerfile` and GitHub Actions CI, which support reproducible execution and validation, but the application itself remains a local workflow.

12. **Demo and test surface**
   `demo/` contains scripted fixtures for a deterministic product demo, separate from the real scan pipeline. The tests cover configuration precedence, scan output, and export smoke behavior, which signals that configuration loading and file outputs are key architectural contracts.

13. **External service dependencies that materially shape the design**
   The system depends on three external service classes:
   - **TinyFish** for careers-page discovery/fetching
   - **LLM providers** for scoring and drafting
   - **Telegram** for alert delivery  
   Because the app is orchestrating these services, the main architecture is an automation pipeline with service adapters, not a monolith with local-only logic.

14. **Data flow summary**
   The core flow is:
   **companies catalog + resume + config → scan careers pages → fetch jobs → score with LLM → persist/filter → Telegram notify → optional draft application artifacts**.  
   That is the central architecture diagram to draw: a local orchestrator coordinating external fetch, inference, and messaging services, with file-based state and outputs on disk.

Main entry points: [`job_hunt/scanner.py`](https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/job_hunt/scanner.py) (Scanner), [`job_hunt/drafter.py`](https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/job_hunt/drafter.py) (Drafter), [`job_hunt/tools.py`](https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/job_hunt/tools.py) (Shared tools), [`demo/demo_run.py`](https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/demo/demo_run.py) (Demo harness)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_app["Packaged app"]
  node_cli["CLI<br/>entrypoint<br/>[main.py]"]
  node_mcp["MCP server<br/>entrypoint<br/>[mcp_server.py]"]
  node_tools["Shared tools<br/>service layer<br/>[tools.py]"]
  node_scanner["Scanner<br/>pipeline<br/>[scanner.py]"]
  node_drafter["Drafter<br/>workflow<br/>[drafter.py]"]
  node_notifier["Notifier<br/>delivery<br/>[notifier.py]"]
  node_llm["LLM utils<br/>adapter<br/>[llm_utils.py]"]
  node_log["Logging<br/>support<br/>[log.py]"]
end

subgraph group_workspace["User workspace"]
  node_companies["Company catalog<br/>input data<br/>[companies.json]"]
  node_config["Runtime config<br/>workspace config"]
  node_env["Env vars<br/>workspace secret<br/>[.env.example]"]
  node_resume["Resume<br/>candidate profile<br/>[YOUR_RESUME.md]"]
  node_state[("Scan state<br/>workspace state")]
  node_output["Output files<br/>workspace output"]
end

subgraph group_external["External services"]
  node_tinyfish(("TinyFish<br/>scraping api"))
  node_providers(("LLM providers<br/>model api"))
  node_telegram(("Telegram<br/>messaging api"))
end

subgraph group_ops["Automation"]
  node_cron{{"Cron setup<br/>scheduler<br/>[setup_cron.sh]"}}
  node_demo["Demo harness<br/>fixture runner<br/>[demo_run.py]"]
  node_ci{{"CI workflow<br/>validation<br/>[ci.yml]"}}
end

node_cli -->|"uses"| node_tools
node_mcp -->|"uses"| node_tools
node_tools -->|"drives"| node_scanner
node_tools -->|"drives"| node_drafter
node_scanner -->|"reads"| node_companies
node_scanner -->|"reads"| node_config
node_scanner -->|"reads"| node_env
node_scanner -->|"fetches"| node_tinyfish
node_scanner -->|"scores"| node_llm
node_scanner -->|"persists"| node_state
node_scanner -->|"sends matches"| node_notifier
node_notifier -->|"delivers"| node_telegram
node_drafter -->|"reads"| node_resume
node_drafter -->|"generates"| node_llm
node_drafter -->|"writes"| node_output
node_llm -->|"routes to"| node_providers
node_cron -->|"schedules"| node_cli
node_demo -->|"uses fixtures"| node_companies
node_demo -->|"uses fixtures"| node_resume
node_demo -->|"simulates"| node_scanner
node_ci -->|"validates"| node_cli
node_ci -->|"tests"| node_scanner

click node_cli "https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/job_hunt/main.py"
click node_mcp "https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/job_hunt/mcp_server.py"
click node_tools "https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/job_hunt/tools.py"
click node_scanner "https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/job_hunt/scanner.py"
click node_drafter "https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/job_hunt/drafter.py"
click node_notifier "https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/job_hunt/notifier.py"
click node_llm "https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/job_hunt/llm_utils.py"
click node_log "https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/job_hunt/log.py"
click node_companies "https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/companies.json"
click node_config "https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/config.example.json"
click node_env "https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/.env.example"
click node_resume "https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/resume/YOUR_RESUME.md"
click node_state "https://github.com/tarunlnmiit/autopilot-jobhunt/tree/main/state"
click node_output "https://github.com/tarunlnmiit/autopilot-jobhunt/tree/main/output"
click node_cron "https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/setup_cron.sh"
click node_demo "https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/demo/demo_run.py"
click node_ci "https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/.github/workflows/ci.yml"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_cli,node_mcp,node_tools,node_scanner,node_drafter,node_notifier,node_llm,node_log toneBlue
class node_companies,node_config,node_env,node_resume,node_state,node_output toneAmber
class node_tinyfish,node_providers,node_telegram toneMint
class node_cron,node_demo,node_ci toneRose
```

## Components

### Packaged app

Reusable Python package and entrypoints shipped with the repo.

- [CLI](https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/job_hunt/main.py): entrypoint · Command-line entry surface for running scans and drafting. · `job_hunt/main.py`
- [MCP server](https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/job_hunt/mcp_server.py): entrypoint · Model Context Protocol adapter for Claude Code/Desktop. · `job_hunt/mcp_server.py`
- [Shared tools](https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/job_hunt/tools.py): service layer · Protocol-agnostic actions used by both CLI and MCP. · `job_hunt/tools.py`
- [Scanner](https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/job_hunt/scanner.py): pipeline · Discovers jobs, fetches details, deduplicates, and scores matches. · `job_hunt/scanner.py`
- [Drafter](https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/job_hunt/drafter.py): workflow · Generates tailored resume bullets and cover letters for a selected job. · `job_hunt/drafter.py`
- [Notifier](https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/job_hunt/notifier.py): delivery · Formats and sends top matches to Telegram. · `job_hunt/notifier.py`
- [LLM utils](https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/job_hunt/llm_utils.py): adapter · Abstraction over model providers for scoring and drafting. · `job_hunt/llm_utils.py`
- [Logging](https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/job_hunt/log.py): support · Shared logging setup for the runtime. · `job_hunt/log.py`

### User workspace

Files and directories that hold runtime config, state, inputs, and outputs.

- [Company catalog](https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/companies.json): input data · Target employer and careers-page list used by the scanner. · `companies.json`
- [Runtime config](https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/config.example.json): workspace config · User-owned configuration and secrets that override package defaults. · `config.example.json`
- [Env vars](https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/.env.example): workspace secret · Environment-based secrets and overrides for local execution. · `.env.example`
- [Resume](https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/resume/YOUR_RESUME.md): candidate profile · User resume used for scoring and drafting. · `resume/YOUR_RESUME.md`
- [Scan state](https://github.com/tarunlnmiit/autopilot-jobhunt/tree/main/state): workspace state · Persistent deduplication and prior-scan state. · `state`
- [Output files](https://github.com/tarunlnmiit/autopilot-jobhunt/tree/main/output): workspace output · Generated application materials and exported artifacts. · `output`

### External services

Third-party services the automation orchestrates.

- **TinyFish**: scraping api · External discovery and fetch layer for careers pages and job details.
- **LLM providers**: model api · OpenRouter, Claude CLI, and Anthropic backends used for scoring and drafting.
- **Telegram**: messaging api · Outbound notification channel for top job matches.

### Automation

Packaging, scheduling, demo, and validation surfaces.

- [Cron setup](https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/setup_cron.sh): scheduler · Unattended nightly execution path for the automation. · `setup_cron.sh`
- [Demo harness](https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/demo/demo_run.py): fixture runner · Deterministic demo flow using canned companies, jobs, and resume data. · `demo/demo_run.py`
- [CI workflow](https://github.com/tarunlnmiit/autopilot-jobhunt/blob/main/.github/workflows/ci.yml): validation · GitHub Actions validation for the package and tests. · `.github/workflows/ci.yml`

## Connections

- CLI → Shared tools: uses · CLI invokes shared actions.
- MCP server → Shared tools: uses · MCP server exposes the same shared actions.
- Shared tools → Scanner: drives · Shared layer triggers the scan workflow.
- Shared tools → Drafter: drives · Shared layer triggers drafting workflow.
- Scanner → Company catalog: reads · Uses target company catalog to decide what to scan.
- Scanner → Runtime config: reads · Loads runtime preferences such as thresholds and providers.
- Scanner → Env vars: reads · Consumes secrets and environment overrides.
- Scanner → TinyFish: fetches · Discovers and retrieves job data from careers pages.
- Scanner → LLM utils: scores · Scores jobs against the resume and job description.
- Scanner → Scan state: persists · Stores deduplication and prior-scan state.
- Scanner → Notifier: sends matches · Passes top matches to outbound notification.
- Notifier → Telegram: delivers · Pushes summaries and links to the user.
- Drafter → Resume: reads · Uses the candidate profile as drafting input.
- Drafter → LLM utils: generates · Creates tailored bullets and cover letters.
- Drafter → Output files: writes · Exports generated application materials.
- LLM utils → LLM providers: routes to · Abstracts over multiple model backends and fallbacks.
- Cron setup → CLI: schedules · Runs the local automation unattended.
- Demo harness → Company catalog: uses fixtures · Drives deterministic demo inputs.
- Demo harness → Resume: uses fixtures · Drives deterministic demo inputs.
- Demo harness → Scanner: simulates · Exercises the scan pipeline with canned data.
- CI workflow → CLI: validates · Checks runtime and packaging behavior.
- CI workflow → Scanner: tests · Covers scan output and core workflow behavior.
