jobhunter-cli 0.0.post1.dev85__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. jobhunter_cli-0.0.post1.dev85/.env.example +6 -0
  2. jobhunter_cli-0.0.post1.dev85/.github/workflows/job-maintenance.yml +78 -0
  3. jobhunter_cli-0.0.post1.dev85/.github/workflows/job-pipeline.yml +139 -0
  4. jobhunter_cli-0.0.post1.dev85/.github/workflows/publish.yml +52 -0
  5. jobhunter_cli-0.0.post1.dev85/.gitignore +24 -0
  6. jobhunter_cli-0.0.post1.dev85/.python-version +1 -0
  7. jobhunter_cli-0.0.post1.dev85/LICENSE +21 -0
  8. jobhunter_cli-0.0.post1.dev85/PKG-INFO +136 -0
  9. jobhunter_cli-0.0.post1.dev85/README.md +119 -0
  10. jobhunter_cli-0.0.post1.dev85/config/.gitkeep +0 -0
  11. jobhunter_cli-0.0.post1.dev85/data/.gitkeep +0 -0
  12. jobhunter_cli-0.0.post1.dev85/docs/architecture.md +116 -0
  13. jobhunter_cli-0.0.post1.dev85/docs/configuration.md +308 -0
  14. jobhunter_cli-0.0.post1.dev85/examples/config.example.json +96 -0
  15. jobhunter_cli-0.0.post1.dev85/examples/cv.example.md +24 -0
  16. jobhunter_cli-0.0.post1.dev85/examples/preferences.example.md +19 -0
  17. jobhunter_cli-0.0.post1.dev85/pyproject.toml +58 -0
  18. jobhunter_cli-0.0.post1.dev85/src/jobhunter/__init__.py +0 -0
  19. jobhunter_cli-0.0.post1.dev85/src/jobhunter/__main__.py +4 -0
  20. jobhunter_cli-0.0.post1.dev85/src/jobhunter/_version.py +24 -0
  21. jobhunter_cli-0.0.post1.dev85/src/jobhunter/core/__init__.py +0 -0
  22. jobhunter_cli-0.0.post1.dev85/src/jobhunter/core/filters.py +110 -0
  23. jobhunter_cli-0.0.post1.dev85/src/jobhunter/core/http.py +57 -0
  24. jobhunter_cli-0.0.post1.dev85/src/jobhunter/core/logger.py +54 -0
  25. jobhunter_cli-0.0.post1.dev85/src/jobhunter/core/paths.py +124 -0
  26. jobhunter_cli-0.0.post1.dev85/src/jobhunter/core/settings.py +123 -0
  27. jobhunter_cli-0.0.post1.dev85/src/jobhunter/core/textutil.py +33 -0
  28. jobhunter_cli-0.0.post1.dev85/src/jobhunter/main.py +101 -0
  29. jobhunter_cli-0.0.post1.dev85/src/jobhunter/models.py +81 -0
  30. jobhunter_cli-0.0.post1.dev85/src/jobhunter/pipeline/__init__.py +0 -0
  31. jobhunter_cli-0.0.post1.dev85/src/jobhunter/pipeline/evaluate.py +284 -0
  32. jobhunter_cli-0.0.post1.dev85/src/jobhunter/pipeline/fetch.py +233 -0
  33. jobhunter_cli-0.0.post1.dev85/src/jobhunter/pipeline/send.py +191 -0
  34. jobhunter_cli-0.0.post1.dev85/src/jobhunter/sources/__init__.py +21 -0
  35. jobhunter_cli-0.0.post1.dev85/src/jobhunter/sources/arbeitnow.py +77 -0
  36. jobhunter_cli-0.0.post1.dev85/src/jobhunter/sources/ashby.py +75 -0
  37. jobhunter_cli-0.0.post1.dev85/src/jobhunter/sources/base.py +108 -0
  38. jobhunter_cli-0.0.post1.dev85/src/jobhunter/sources/greenhouse.py +70 -0
  39. jobhunter_cli-0.0.post1.dev85/src/jobhunter/sources/lever.py +85 -0
  40. jobhunter_cli-0.0.post1.dev85/src/jobhunter/sources/personio.py +124 -0
  41. jobhunter_cli-0.0.post1.dev85/src/jobhunter/sources/recruitee.py +80 -0
  42. jobhunter_cli-0.0.post1.dev85/src/jobhunter/sources/remoteok.py +69 -0
  43. jobhunter_cli-0.0.post1.dev85/src/jobhunter/sources/remotive.py +61 -0
  44. jobhunter_cli-0.0.post1.dev85/src/jobhunter/sources/workable.py +94 -0
  45. jobhunter_cli-0.0.post1.dev85/src/jobhunter/tools/__init__.py +0 -0
  46. jobhunter_cli-0.0.post1.dev85/src/jobhunter/tools/discover.py +275 -0
  47. jobhunter_cli-0.0.post1.dev85/src/jobhunter/tools/init.py +74 -0
  48. jobhunter_cli-0.0.post1.dev85/src/jobhunter/tools/prompt.py +106 -0
  49. jobhunter_cli-0.0.post1.dev85/src/jobhunter/tools/prune.py +124 -0
  50. jobhunter_cli-0.0.post1.dev85/uv.lock +636 -0
@@ -0,0 +1,6 @@
1
+ # Telegram: create a bot at https://t.me/BotFather and get the bot token, then start a chat with your bot and get the chat ID.
2
+ TELEGRAM_BOT_TOKEN=
3
+ TELEGRAM_CHAT_ID=
4
+
5
+ # OpenAI: create an account at https://platform.openai.com/ and generate an API key.
6
+ LLM_API_KEY=
@@ -0,0 +1,78 @@
1
+ name: Job Maintenance & Discovery
2
+
3
+ on:
4
+ schedule:
5
+ # Runs once a week: every Sunday at 3:00 AM UTC
6
+ - cron: '0 3 * * 0'
7
+ workflow_dispatch:
8
+ inputs:
9
+ source:
10
+ description: 'Run discovery/prune for a specific source only (optional)'
11
+ type: string
12
+ required: false
13
+ default: ''
14
+
15
+ jobs:
16
+ maintenance:
17
+ runs-on: ubuntu-latest
18
+ permissions:
19
+ contents: write
20
+
21
+ steps:
22
+ - name: Checkout repository
23
+ uses: actions/checkout@v4
24
+
25
+ - name: Set up UV and Python
26
+ uses: astral-sh/setup-uv@v5
27
+ with:
28
+ enable-cache: true
29
+ version: "latest"
30
+
31
+ - name: Set up Python
32
+ uses: actions/setup-python@v5
33
+ with:
34
+ python-version-file: ".python-version"
35
+ cache: "uv"
36
+
37
+ - name: Install dependencies
38
+ run: uv sync
39
+
40
+ - name: Run Discover and Prune if config.json exists
41
+ run: |
42
+ if [ ! -f "config/config.json" ]; then
43
+ echo "config/config.json does not exist. Skipping maintenance tasks."
44
+ exit 0
45
+ fi
46
+
47
+ SOURCE_FLAG=""
48
+ if [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ -n "${{ inputs.source }}" ]; then
49
+ SOURCE_FLAG="--source ${{ inputs.source }}"
50
+ fi
51
+
52
+ echo "Running discovery..."
53
+ uv run jobhunter discover --write $SOURCE_FLAG
54
+
55
+ echo "Running pruning..."
56
+ uv run jobhunter prune --write $SOURCE_FLAG
57
+
58
+ - name: Commit and push updated config.json
59
+ run: |
60
+ if [ ! -f "config/config.json" ]; then
61
+ exit 0
62
+ fi
63
+
64
+ git config --global user.name "github-actions[bot]"
65
+ git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
66
+
67
+ git status
68
+
69
+ if [ -f "config/config.json" ]; then
70
+ git add config/config.json
71
+ fi
72
+
73
+ if git diff --staged --quiet; then
74
+ echo "No changes in config.json to commit."
75
+ else
76
+ git commit -m "chore(maintenance): update company board configuration [skip ci]"
77
+ git push
78
+ fi
@@ -0,0 +1,139 @@
1
+ name: Job Pipeline
2
+
3
+ on:
4
+ schedule:
5
+ # 1. First daily run (Aggregators ENABLED)
6
+ - cron: "0 6 * * *"
7
+ # 2. Subsequent daily runs (Aggregators DISABLED)
8
+ - cron: "0 14,22 * * *"
9
+ workflow_dispatch:
10
+ inputs:
11
+ dry_run:
12
+ description: "Run pipeline in dry-run mode (no telegram notifications sent)"
13
+ type: boolean
14
+ required: false
15
+ default: false
16
+ ignore_seen:
17
+ description: "Ignore seen history cache and re-process recent jobs"
18
+ type: boolean
19
+ required: false
20
+ default: false
21
+ no_aggregators:
22
+ description: "Disable aggregator sources (Remotive, RemoteOK, Arbeitnow)"
23
+ type: boolean
24
+ required: false
25
+ default: true
26
+ source:
27
+ description: "Run for a specific source/company only (optional)"
28
+ type: string
29
+ required: false
30
+ default: ""
31
+ limit:
32
+ description: "Limit maximum candidates per run (optional)"
33
+ type: number
34
+ required: false
35
+ default: 0
36
+
37
+ jobs:
38
+ run-pipeline:
39
+ runs-on: ubuntu-latest
40
+ permissions:
41
+ contents: write
42
+
43
+ steps:
44
+ - name: Checkout repository
45
+ uses: actions/checkout@v4
46
+
47
+ - name: Set up UV and Python
48
+ uses: astral-sh/setup-uv@v5
49
+ with:
50
+ enable-cache: true
51
+ version: "latest"
52
+
53
+ - name: Set up Python
54
+ uses: actions/setup-python@v5
55
+ with:
56
+ python-version-file: ".python-version"
57
+
58
+ - name: Install dependencies
59
+ run: uv sync
60
+
61
+ - name: Check existing state files before run
62
+ id: check-state
63
+ run: |
64
+ if [ -f "data/candidates.json" ]; then
65
+ echo "candidates_exists=true" >> $GITHUB_OUTPUT
66
+ else
67
+ echo "candidates_exists=false" >> $GITHUB_OUTPUT
68
+ fi
69
+ if [ -f "data/evaluated.json" ]; then
70
+ echo "evaluated_exists=true" >> $GITHUB_OUTPUT
71
+ else
72
+ echo "evaluated_exists=false" >> $GITHUB_OUTPUT
73
+ fi
74
+
75
+ - name: Run JobHunter Pipeline
76
+ env:
77
+ TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
78
+ TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
79
+ LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
80
+ CONFIG_JSON: ${{ secrets.CONFIG_JSON }}
81
+ CV: ${{ secrets.CV }}
82
+ PREFERENCES: ${{ secrets.PREFERENCES }}
83
+ run: |
84
+ CMD="uv run jobhunter run"
85
+
86
+ if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
87
+ if [ "${{ inputs.no_aggregators }}" == "true" ]; then
88
+ CMD="$CMD --no-aggregators"
89
+ fi
90
+ if [ "${{ inputs.dry_run }}" == "true" ]; then
91
+ CMD="$CMD --dry-run"
92
+ fi
93
+ if [ "${{ inputs.ignore_seen }}" == "true" ]; then
94
+ CMD="$CMD --ignore-seen"
95
+ fi
96
+ if [ -n "${{ inputs.source }}" ]; then
97
+ CMD="$CMD --source ${{ inputs.source }}"
98
+ fi
99
+ if [ -n "${{ inputs.limit }}" ] && [ "${{ inputs.limit }}" -gt 0 ]; then
100
+ CMD="$CMD --limit ${{ inputs.limit }}"
101
+ fi
102
+ elif [ "${{ github.event_name }}" == "schedule" ]; then
103
+ if [ "${{ github.event.schedule }}" == "0 6 * * *" ]; then
104
+ echo "Morning schedule triggered: Aggregators ENABLED"
105
+ else
106
+ echo "Subsequent schedule triggered: Aggregators DISABLED"
107
+ CMD="$CMD --no-aggregators"
108
+ fi
109
+ fi
110
+
111
+ echo "Executing: $CMD"
112
+ eval $CMD
113
+
114
+ - name: Commit and push state files
115
+ env:
116
+ CANDIDATES_EXISTS: ${{ steps.check-state.outputs.candidates_exists }}
117
+ EVALUATED_EXISTS: ${{ steps.check-state.outputs.evaluated_exists }}
118
+ run: |
119
+ git config --global user.name "github-actions[bot]"
120
+ git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
121
+
122
+ git status
123
+
124
+ git add data/seen.json
125
+
126
+ if [ "$CANDIDATES_EXISTS" == "true" ] && [ -f "data/candidates.json" ]; then
127
+ git add data/candidates.json
128
+ fi
129
+
130
+ if [ "$EVALUATED_EXISTS" == "true" ] && [ -f "data/evaluated.json" ]; then
131
+ git add data/evaluated.json
132
+ fi
133
+
134
+ if git diff --staged --quiet; then
135
+ echo "No state file changes to commit."
136
+ else
137
+ git commit -m "chore(pipeline): update jobhunter state files [skip ci]"
138
+ git push
139
+ fi
@@ -0,0 +1,52 @@
1
+ name: Auto Publish & Release
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ publish:
11
+ name: Build, Release, and Publish
12
+ runs-on: ubuntu-latest
13
+
14
+ environment:
15
+ name: pypi
16
+ url: https://pypi.org/p/jobhunter-cli
17
+
18
+ permissions:
19
+ id-token: write
20
+ contents: write
21
+
22
+ steps:
23
+ - name: Checkout repository
24
+ uses: actions/checkout@v4
25
+ with:
26
+ fetch-depth: 0
27
+
28
+ - name: Install uv
29
+ uses: astral-sh/setup-uv@v3
30
+ with:
31
+ version: "latest"
32
+
33
+ - name: Set up Python
34
+ uses: actions/setup-python@v5
35
+ with:
36
+ python-version-file: "pyproject.toml"
37
+
38
+ - name: Build package
39
+ run: uv build
40
+
41
+ - name: Publish to PyPI
42
+ uses: pypa/gh-action-pypi-publish@release/v1
43
+ with:
44
+ attestations: false
45
+
46
+ - name: Create GitHub Release
47
+ uses: softprops/action-gh-release@v2
48
+ with:
49
+ tag_name: v1.0-${{ github.run_number }}
50
+ name: Release 1.0 (Build ${{ github.run_number }})
51
+ generate_release_notes: true
52
+ files: dist/*
@@ -0,0 +1,24 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ _version.py
8
+ *.egg-info
9
+ .locks
10
+ logs
11
+ todo.txt
12
+
13
+ # Virtual environments
14
+ .venv
15
+
16
+ # Secrets and personal files
17
+ .env
18
+ config/*
19
+ !config/.gitkeep
20
+
21
+ # Generated by the tool. Only data/seen.json is meant to be committed (the GitHub Actions workflow does it).
22
+ data/*
23
+ !data/.gitkeep
24
+ !data/seen.json
@@ -0,0 +1 @@
1
+ 3.14
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ziad Aboughaleb
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,136 @@
1
+ Metadata-Version: 2.5
2
+ Name: jobhunter-cli
3
+ Version: 0.0.post1.dev85
4
+ Summary: An AI-powered, zero-database Python CLI that automates job hunting. It scrapes ATS boards, evaluates candidate-job fit using LLMs against your CV, and dispatches real-time Telegram alerts.
5
+ Author-email: ziad-aboughaleb <ziadaboughaleb@gmail.com>
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.14
8
+ Requires-Dist: ddgs>=9.16.1
9
+ Requires-Dist: filelock>=4.2.4
10
+ Requires-Dist: httpx>=0.28.1
11
+ Requires-Dist: openai>=3.41.0
12
+ Requires-Dist: pydantic-settings>=2.16.1
13
+ Requires-Dist: pydantic>=2.14.1
14
+ Requires-Dist: rich>=15.0.0
15
+ Requires-Dist: typer>=0.27.3
16
+ Description-Content-Type: text/markdown
17
+
18
+ # JobHunter 🎯
19
+
20
+ > **AI-Powered, Highly Concurrent, Zero-Database Automated Job Hunting Pipeline**
21
+
22
+ `JobHunter` is a robust, production-grade Python CLI application designed to fully automate the job search and filtering lifecycle. It continuously scrapes Applicant Tracking Systems (ATS) and job boards, filters opportunities against precise regex and keyword criteria, evaluates candidate-job fit using Large Language Models (LLMs) with strict JSON schemas, and dispatches rich real-time alerts directly to Telegram.
23
+
24
+ ---
25
+
26
+ ## 🚀 Key Features
27
+
28
+ - **High Concurrency & Rate Limiting**: Powered by `concurrent.futures` with dynamic worker bounds, exponential backoff retries on transient network errors, and intelligent request pacing.
29
+ - **ATS WAF & API Bypass**: Bypasses heavy frontend anti-bot walls by querying official public underlying JSON/XML board APIs directly (Greenhouse, Lever, Personio, Workable, Recruitee, Ashby, plus aggregators like Remotive, RemoteOK, and Arbeitnow).
30
+ - **Multi-Stage Filtering**: Filters jobs through regex word-boundary matching for titles, excluded companies, location rules, remote requirements, posting age, and minimum tech stack keyword hits (`stack_hits`).
31
+ - **AI-Driven Evaluation**: Evaluates shortlisted candidates against your exact CV (`config/cv.md`) and preferences (`config/preferences.md`) using OpenAI-compatible LLMs, enforcing hard gates, scoring rubrics, and anti-hallucination guardrails.
32
+ - **Telegram Rich Alerts**: Dispatches beautifully formatted HTML notifications featuring VIP priority badges, score color-coding, AI reasoning, score breakdowns, professional pitches, and direct links.
33
+ - **Autonomous Maintenance Tools**: Includes built-in CLI commands to automatically discover new company career boards via DuckDuckGo and prune dead or broken board URLs.
34
+ - **Zero-Database State Management**: Uses atomic file writes with file locking (`filelock`) and a rolling 90-day retention cache (`seen.json`) to prevent duplicate processing without requiring an external database.
35
+
36
+ ---
37
+
38
+ ## 🛠️ Quickstart Guide
39
+
40
+ ### Prerequisites
41
+
42
+ - **Python >= 3.14**
43
+ - **uv** (recommended Python package and project manager)
44
+
45
+ ### 1. Install
46
+
47
+ Install the CLI globally via PyPI using `uv` (recommended) or `pip`:
48
+
49
+ ```bash
50
+ uv tool install jobhunter-cli
51
+ # OR
52
+ pip install jobhunter-cli
53
+ ```
54
+
55
+ ### 2. Initialize Your Workspace
56
+
57
+ Create a dedicated folder for your job search and run the `init` command to automatically download the configuration templates:
58
+
59
+ ```bash
60
+ mkdir my-job-search && cd my-job-search
61
+ jobhunter init
62
+ ```
63
+
64
+ ### 3. Configure
65
+
66
+ The `init` command creates a `.env` file and a `config/` directory.
67
+
68
+ 1. Open `.env` and add your API keys:
69
+ ```env
70
+ TELEGRAM_BOT_TOKEN=your_telegram_bot_token_here
71
+ TELEGRAM_CHAT_ID=your_telegram_chat_id_here
72
+ LLM_API_KEY=your_openai_or_compatible_api_key_here
73
+ ```
74
+ 2. Edit the templates in the `config/` folder (`config.json`, `cv.md`, `preferences.md`) with your specific details.
75
+
76
+ > **💡 Note on GitHub Actions:** `JobHunter` automatically falls back to environment variables (`CONFIG_JSON`, `CV`, `PREFERENCES`) if physical files do not exist on disk. This allows deploying via GitHub Actions on public repositories using GitHub Secrets.
77
+
78
+ ### 4. Run the Pipeline
79
+
80
+ Execute the full pipeline (Fetch -> Evaluate -> Send) from within your workspace directory:
81
+
82
+ ```bash
83
+ jobhunter run
84
+ ```
85
+
86
+ ---
87
+
88
+ ## 🔄 The 5-Stage Pipeline
89
+
90
+ ```
91
+ [Discover / Configure] ──> [Fetch & Filter] ──> [AI Evaluate] ──> [Send to Telegram] ──> [State Purge & Seen Sync]
92
+ ```
93
+
94
+ 1. **Discover / Configure**: Resolve targets from `config.json` or discover new company boards using `jobhunter discover`.
95
+ 2. **Fetch**: Scrape raw job postings concurrently across configured ATS boards and aggregators.
96
+ 3. **Filter & Prune**: Discard duplicates, previously seen jobs, and postings failing title, location, age, or stack keyword criteria. Rank candidates by VIP priority and stack keyword hits.
97
+ 4. **Evaluate**: Submit shortlisted candidates to the LLM. Enforce hard gates and score against your CV and preferences.
98
+ 5. **Send & Purge**: Dispatch passing job alerts to Telegram. Log processed jobs to `seen.json` with a 90-day retention window and purge completed items from active state files.
99
+
100
+ ---
101
+
102
+ ## 📖 Documentation Deep Dives
103
+
104
+ For comprehensive technical documentation, consult the downloaded `docs/` folder or view them on GitHub:
105
+
106
+ - **[Architecture Guide](docs/architecture.md)**: Detailed breakdown of the decoupled pipeline modules, concurrency model, and zero-database state lifecycle.
107
+ - **[Configuration & Settings](docs/configuration.md)**: Comprehensive guide to `config.json`, Pydantic settings, environment variables, and prompt engineering setup (`cv.md` / `preferences.md`).
108
+
109
+ ---
110
+
111
+ ## 💻 CLI Commands Reference
112
+
113
+ | Command | Description | Key Options |
114
+ | :------------------- | :---------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
115
+ | `jobhunter init` | Scaffolds a new workspace by downloading templates and docs | `[TARGET_DIR]` (Target directory to initialize) |
116
+ | `jobhunter run` | Runs the full pipeline (fetch -> evaluate -> send) | `-s, --source` (Specific ATS/aggregator to fetch from)<br>`--no-aggregators` (Skip aggregators)<br>`--ignore-seen` (Ignore seen cache)<br>`-l, --limit` (Override max candidates)<br>`--dry-run` (Alert without cache updates) |
117
+ | `jobhunter fetch` | Scrapes and filters jobs from configured boards | `-s, --source`, `--no-aggregators`, `--ignore-seen`, `-l, --limit` |
118
+ | `jobhunter evaluate` | Scores and evaluates pending candidates using AI | `-j, --job` (Evaluate specific job UID), `-l, --limit` |
119
+ | `jobhunter send` | Dispatches passing job evaluations to Telegram | `--dry-run` |
120
+ | `jobhunter discover` | Discovers new company ATS slugs via DuckDuckGo | `-w, --write` (Persist to config), `-s, --source`, `-t, --titles` (Comma-separated titles) |
121
+ | `jobhunter prune` | Validates and prunes broken company slugs from config | `-w, --write`, `-s, --source` |
122
+ | `jobhunter prompt` | Prints the compiled AI evaluation system prompt | None |
123
+
124
+ ---
125
+
126
+ ## 🏗️ Building from Source (Development)
127
+
128
+ If you want to contribute or modify the source code directly:
129
+
130
+ ```bash
131
+ git clone https://github.com/ziad-aboughaleb/job-hunter.git
132
+ cd job-hunter
133
+ uv sync
134
+ uv run jobhunter init
135
+ uv run jobhunter run
136
+ ```
@@ -0,0 +1,119 @@
1
+ # JobHunter 🎯
2
+
3
+ > **AI-Powered, Highly Concurrent, Zero-Database Automated Job Hunting Pipeline**
4
+
5
+ `JobHunter` is a robust, production-grade Python CLI application designed to fully automate the job search and filtering lifecycle. It continuously scrapes Applicant Tracking Systems (ATS) and job boards, filters opportunities against precise regex and keyword criteria, evaluates candidate-job fit using Large Language Models (LLMs) with strict JSON schemas, and dispatches rich real-time alerts directly to Telegram.
6
+
7
+ ---
8
+
9
+ ## 🚀 Key Features
10
+
11
+ - **High Concurrency & Rate Limiting**: Powered by `concurrent.futures` with dynamic worker bounds, exponential backoff retries on transient network errors, and intelligent request pacing.
12
+ - **ATS WAF & API Bypass**: Bypasses heavy frontend anti-bot walls by querying official public underlying JSON/XML board APIs directly (Greenhouse, Lever, Personio, Workable, Recruitee, Ashby, plus aggregators like Remotive, RemoteOK, and Arbeitnow).
13
+ - **Multi-Stage Filtering**: Filters jobs through regex word-boundary matching for titles, excluded companies, location rules, remote requirements, posting age, and minimum tech stack keyword hits (`stack_hits`).
14
+ - **AI-Driven Evaluation**: Evaluates shortlisted candidates against your exact CV (`config/cv.md`) and preferences (`config/preferences.md`) using OpenAI-compatible LLMs, enforcing hard gates, scoring rubrics, and anti-hallucination guardrails.
15
+ - **Telegram Rich Alerts**: Dispatches beautifully formatted HTML notifications featuring VIP priority badges, score color-coding, AI reasoning, score breakdowns, professional pitches, and direct links.
16
+ - **Autonomous Maintenance Tools**: Includes built-in CLI commands to automatically discover new company career boards via DuckDuckGo and prune dead or broken board URLs.
17
+ - **Zero-Database State Management**: Uses atomic file writes with file locking (`filelock`) and a rolling 90-day retention cache (`seen.json`) to prevent duplicate processing without requiring an external database.
18
+
19
+ ---
20
+
21
+ ## 🛠️ Quickstart Guide
22
+
23
+ ### Prerequisites
24
+
25
+ - **Python >= 3.14**
26
+ - **uv** (recommended Python package and project manager)
27
+
28
+ ### 1. Install
29
+
30
+ Install the CLI globally via PyPI using `uv` (recommended) or `pip`:
31
+
32
+ ```bash
33
+ uv tool install jobhunter-cli
34
+ # OR
35
+ pip install jobhunter-cli
36
+ ```
37
+
38
+ ### 2. Initialize Your Workspace
39
+
40
+ Create a dedicated folder for your job search and run the `init` command to automatically download the configuration templates:
41
+
42
+ ```bash
43
+ mkdir my-job-search && cd my-job-search
44
+ jobhunter init
45
+ ```
46
+
47
+ ### 3. Configure
48
+
49
+ The `init` command creates a `.env` file and a `config/` directory.
50
+
51
+ 1. Open `.env` and add your API keys:
52
+ ```env
53
+ TELEGRAM_BOT_TOKEN=your_telegram_bot_token_here
54
+ TELEGRAM_CHAT_ID=your_telegram_chat_id_here
55
+ LLM_API_KEY=your_openai_or_compatible_api_key_here
56
+ ```
57
+ 2. Edit the templates in the `config/` folder (`config.json`, `cv.md`, `preferences.md`) with your specific details.
58
+
59
+ > **💡 Note on GitHub Actions:** `JobHunter` automatically falls back to environment variables (`CONFIG_JSON`, `CV`, `PREFERENCES`) if physical files do not exist on disk. This allows deploying via GitHub Actions on public repositories using GitHub Secrets.
60
+
61
+ ### 4. Run the Pipeline
62
+
63
+ Execute the full pipeline (Fetch -> Evaluate -> Send) from within your workspace directory:
64
+
65
+ ```bash
66
+ jobhunter run
67
+ ```
68
+
69
+ ---
70
+
71
+ ## 🔄 The 5-Stage Pipeline
72
+
73
+ ```
74
+ [Discover / Configure] ──> [Fetch & Filter] ──> [AI Evaluate] ──> [Send to Telegram] ──> [State Purge & Seen Sync]
75
+ ```
76
+
77
+ 1. **Discover / Configure**: Resolve targets from `config.json` or discover new company boards using `jobhunter discover`.
78
+ 2. **Fetch**: Scrape raw job postings concurrently across configured ATS boards and aggregators.
79
+ 3. **Filter & Prune**: Discard duplicates, previously seen jobs, and postings failing title, location, age, or stack keyword criteria. Rank candidates by VIP priority and stack keyword hits.
80
+ 4. **Evaluate**: Submit shortlisted candidates to the LLM. Enforce hard gates and score against your CV and preferences.
81
+ 5. **Send & Purge**: Dispatch passing job alerts to Telegram. Log processed jobs to `seen.json` with a 90-day retention window and purge completed items from active state files.
82
+
83
+ ---
84
+
85
+ ## 📖 Documentation Deep Dives
86
+
87
+ For comprehensive technical documentation, consult the downloaded `docs/` folder or view them on GitHub:
88
+
89
+ - **[Architecture Guide](docs/architecture.md)**: Detailed breakdown of the decoupled pipeline modules, concurrency model, and zero-database state lifecycle.
90
+ - **[Configuration & Settings](docs/configuration.md)**: Comprehensive guide to `config.json`, Pydantic settings, environment variables, and prompt engineering setup (`cv.md` / `preferences.md`).
91
+
92
+ ---
93
+
94
+ ## 💻 CLI Commands Reference
95
+
96
+ | Command | Description | Key Options |
97
+ | :------------------- | :---------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
98
+ | `jobhunter init` | Scaffolds a new workspace by downloading templates and docs | `[TARGET_DIR]` (Target directory to initialize) |
99
+ | `jobhunter run` | Runs the full pipeline (fetch -> evaluate -> send) | `-s, --source` (Specific ATS/aggregator to fetch from)<br>`--no-aggregators` (Skip aggregators)<br>`--ignore-seen` (Ignore seen cache)<br>`-l, --limit` (Override max candidates)<br>`--dry-run` (Alert without cache updates) |
100
+ | `jobhunter fetch` | Scrapes and filters jobs from configured boards | `-s, --source`, `--no-aggregators`, `--ignore-seen`, `-l, --limit` |
101
+ | `jobhunter evaluate` | Scores and evaluates pending candidates using AI | `-j, --job` (Evaluate specific job UID), `-l, --limit` |
102
+ | `jobhunter send` | Dispatches passing job evaluations to Telegram | `--dry-run` |
103
+ | `jobhunter discover` | Discovers new company ATS slugs via DuckDuckGo | `-w, --write` (Persist to config), `-s, --source`, `-t, --titles` (Comma-separated titles) |
104
+ | `jobhunter prune` | Validates and prunes broken company slugs from config | `-w, --write`, `-s, --source` |
105
+ | `jobhunter prompt` | Prints the compiled AI evaluation system prompt | None |
106
+
107
+ ---
108
+
109
+ ## 🏗️ Building from Source (Development)
110
+
111
+ If you want to contribute or modify the source code directly:
112
+
113
+ ```bash
114
+ git clone https://github.com/ziad-aboughaleb/job-hunter.git
115
+ cd job-hunter
116
+ uv sync
117
+ uv run jobhunter init
118
+ uv run jobhunter run
119
+ ```
File without changes
File without changes