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.
- jobhunter_cli-0.0.post1.dev85/.env.example +6 -0
- jobhunter_cli-0.0.post1.dev85/.github/workflows/job-maintenance.yml +78 -0
- jobhunter_cli-0.0.post1.dev85/.github/workflows/job-pipeline.yml +139 -0
- jobhunter_cli-0.0.post1.dev85/.github/workflows/publish.yml +52 -0
- jobhunter_cli-0.0.post1.dev85/.gitignore +24 -0
- jobhunter_cli-0.0.post1.dev85/.python-version +1 -0
- jobhunter_cli-0.0.post1.dev85/LICENSE +21 -0
- jobhunter_cli-0.0.post1.dev85/PKG-INFO +136 -0
- jobhunter_cli-0.0.post1.dev85/README.md +119 -0
- jobhunter_cli-0.0.post1.dev85/config/.gitkeep +0 -0
- jobhunter_cli-0.0.post1.dev85/data/.gitkeep +0 -0
- jobhunter_cli-0.0.post1.dev85/docs/architecture.md +116 -0
- jobhunter_cli-0.0.post1.dev85/docs/configuration.md +308 -0
- jobhunter_cli-0.0.post1.dev85/examples/config.example.json +96 -0
- jobhunter_cli-0.0.post1.dev85/examples/cv.example.md +24 -0
- jobhunter_cli-0.0.post1.dev85/examples/preferences.example.md +19 -0
- jobhunter_cli-0.0.post1.dev85/pyproject.toml +58 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/__init__.py +0 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/__main__.py +4 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/_version.py +24 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/core/__init__.py +0 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/core/filters.py +110 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/core/http.py +57 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/core/logger.py +54 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/core/paths.py +124 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/core/settings.py +123 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/core/textutil.py +33 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/main.py +101 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/models.py +81 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/pipeline/__init__.py +0 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/pipeline/evaluate.py +284 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/pipeline/fetch.py +233 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/pipeline/send.py +191 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/sources/__init__.py +21 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/sources/arbeitnow.py +77 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/sources/ashby.py +75 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/sources/base.py +108 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/sources/greenhouse.py +70 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/sources/lever.py +85 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/sources/personio.py +124 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/sources/recruitee.py +80 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/sources/remoteok.py +69 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/sources/remotive.py +61 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/sources/workable.py +94 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/tools/__init__.py +0 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/tools/discover.py +275 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/tools/init.py +74 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/tools/prompt.py +106 -0
- jobhunter_cli-0.0.post1.dev85/src/jobhunter/tools/prune.py +124 -0
- 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
|