Five tools in one clean, self-hosted dashboard: search remote jobs across multiple boards, track your pipeline, get instant AI salary & company intel, auto-draft your application kit, score your rΓ©sumΓ© against a job, rehearse the interview, and coach your salary negotiation.
| Tool | What it does | Needs AI? |
|---|---|---|
| π Job Search | Search 5+ free boards at once, ranked by a keyword match score. AI company/salary intel + auto-drafted email, cover letter & CV prompt. | for intel/drafts |
| π Application Tracker | Save jobs and move them through Saved β Applied β Interview β Offer β Rejected, with notes. Persists to local JSON. | no |
| π Resume Analyzer | Paste your CV against a job β ATS match score, matched vs missing keywords, concrete rewrite suggestions, and a stronger tailored summary. | yes |
| π€ Interview Prep | Generate the questions you'll likely face β what each really assesses, plus STAR-format model answers tailored to you. | yes |
| π° Salary Coach | A realistic market range, an assessment of your offer, a ready-to-send counter-offer script, and negotiation tips. | yes |
- π Multi-board search β pulls from 5 free job APIs at once, remote-first with a
Remote | Anytoggle. - β Instant match score β every card shows how well it fits your skills (keyword overlap, zero AI cost).
- π§ AI company & salary intel β one click gives you a company brief, reputation summary, fit score, recommended salary (real or AI-estimated), and the best way to apply.
- π Deep research links β jump straight to the company's Glassdoor, Levels.fyi, Blind, and LinkedIn pages.
- βοΈ One-click application kit β tailored outreach email, cover letter, and a CV-tailoring prompt, each editable with copy +
mailto:. - π Local pipeline tracker β save any job and track it to offer; notes and status live in
applications.json. - π Provider-agnostic AI β works with any OpenAI-compatible endpoint: OmniRoute, Groq, OpenRouter, OpenAI, or local Ollama. Built-in model picker + connection tester.
- ποΈ Clean multi-page UI β one shared theme across five tools. No database, no build step, no accounts, no telemetry.
flowchart LR
U([You]) -->|title Β· skills Β· Remote/Any| S[Search form]
S --> J["jobs.search_jobs()"]
J --> R[Remotive]
J --> RO[RemoteOK]
J --> AB[Arbeitnow]
J --> JC[Jobicy]
J --> TM[The Muse]
J -. optional key .-> AD[Adzuna]
J -. optional key .-> JS[JSearch]
R & RO & AB & JC & TM --> N[Normalize Β· de-dupe Β· match score]
N --> C[[Job cards]]
C -->|Select| D[Detail panel]
D -->|Analyze| E["ai.enrich()"]
D -->|Generate| G["ai.generate()"]
E & G --> AI{{Any OpenAI-compatible endpoint}}
AI --> OMNI[OmniRoute / Groq / OpenAI / Ollama]
The job boards find the jobs; the AI enriches them (salary, company, fit, drafts). The LLM never scrapes β it only reasons over what the APIs return.
Tech stack: Python Β· Streamlit Β· requests Β· openai SDK Β· python-dotenv. No database β results live in session state, your profile saves to a local profile.json.
# 1. Clone
git clone https://github.com/hassanannajjar/Job-Pilot.git
cd Job-Pilot
# 2. Create a virtual env + install
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
# 3. (Optional) configure AI + keys
cp .env.example .env # edit AI_BASE_URL / AI_API_KEY / AI_MODEL
# 4. Run
streamlit run Home.pyOpen the URL Streamlit prints (default http://localhost:8501). Search & the tracker work with zero config β you only need an AI provider for the intel, rΓ©sumΓ©, interview, salary & drafting features (set it live in the sidebar).
Run the offline self-checks and the line-cap gate anytime (same as CI):
python scripts/check_lines.py # β "β every source file is β€ 150 lines"
python -m scripts.selftest_all # β "β all self-checks passed"Pick a preset in the sidebar, paste a key, and go. Any OpenAI-compatible endpoint works.
| Preset | Base URL | Example model | Key needed? |
|---|---|---|---|
| OmniRoute | http://localhost:20128/v1 |
auto/best-free |
Local key (see below) |
| Groq (free) | https://api.groq.com/openai/v1 |
llama-3.3-70b-versatile |
Free β console.groq.com |
| OpenRouter | https://openrouter.ai/api/v1 |
openai/gpt-4o-mini |
openrouter.ai/keys |
| OpenAI | https://api.openai.com/v1 |
gpt-4o-mini |
Paid β platform.openai.com |
| Ollama (local) | http://localhost:11434/v1 |
llama3.1 |
None (runs on your machine) |
π‘ New to this? Groq is the fastest free path (sign up, paste key, done). Ollama runs fully offline.
OmniRoute is a local, open-source AI gateway β one OpenAI-compatible endpoint (http://localhost:20128/v1) that routes across 290+ providers with free-tier fallback. Job-Pilot connects to it like any other provider.
π Full step-by-step guide: docs/omniroute.md.
The OmniRoute (local) preset is selected by default, so the sidebar already points at http://localhost:20128/v1 with model auto/best-free β you just paste your key and load models.
Setup:
- Install & run OmniRoute β its dashboard opens at
http://localhost:20128. - Providers β connect at least one (e.g. Kimi, DeepSeek, Gemini). The Provider Topology must show β₯ 1 active β a router with 0 active providers can't answer.
- API Keys β Create a key.
- In Job-Pilot's sidebar: choose the OmniRoute (local) preset β paste your key β click π Load models β pick a model β click π§ͺ Test connection (should show β + the model name).
sequenceDiagram
participant JP as Job-Pilot
participant OR as OmniRoute (:20128)
participant P as Provider (DeepSeek / Kimi / β¦)
JP->>OR: POST /v1/chat/completions (model = auto/best-free)
OR->>P: routes to a healthy provider
P-->>OR: completion
OR-->>JP: OpenAI-shaped response
OmniRoute exposes namespaced model routes β there is no bare auto. Sending auto (or auto/best-chat) can fall through to a weak provider (e.g. felo-chat) that returns garbage like .com.
| Use these β | Avoid these β |
|---|---|
auto/best-free, auto/smart (route to DeepSeek) |
bare auto, auto/best-chat (β felo-chat junk) |
Use π Load models to see everything your OmniRoute exposes, and π§ͺ Test connection to confirm a model actually replies before you rely on it. If output ever comes back as a fragment, Job-Pilot detects it and tells you to switch models. Note: some providers may return errors independent of Job-Pilot (e.g. Kimi 402 if its quota/billing is unset, occasional 502s) β just pick another working route.
| Source | Coverage | Salary data | Key required |
|---|---|---|---|
| Remotive | Remote (tech + non-tech) | sometimes | β none |
| RemoteOK | Remote (tech) | sometimes | β none |
| Arbeitnow | EU + remote | rarely | β none |
| Jobicy | Remote | often | β none |
| The Muse | Global (many on-site) | rarely | β none |
| Adzuna | Broad incl. non-remote | β real | ADZUNA_APP_ID + ADZUNA_APP_KEY |
| JSearch | Aggregates LinkedIn/Indeed/Glassdoor/ZipRecruiter | β real | RAPIDAPI_KEY |
The five no-key sources are always on. Adzuna and JSearch activate automatically only when their keys are present in .env.
| Variable | Purpose | Default |
|---|---|---|
AI_BASE_URL |
AI endpoint | http://localhost:20128/v1 |
AI_API_KEY |
Key for the endpoint | β |
AI_MODEL |
Model id | auto (override to a valid one, e.g. auto/best-free) |
ADZUNA_APP_ID / ADZUNA_APP_KEY |
Enable Adzuna | β |
ADZUNA_COUNTRY |
Adzuna country code | us |
RAPIDAPI_KEY |
Enable JSearch | β |
All AI settings are also editable live in the sidebar β .env just sets the defaults.
- Sidebar β My profile β add your name, email, target role, key skills, and a short rΓ©sumΓ© summary. (Skills power the match score & AI drafts.) Hit πΎ Save profile.
- Search β type a title, choose
RemoteorAny, hit π Search. - Scan β cards are sorted by β match %; salary/source/remote chips at a glance.
- Select a job β the right panel fills in.
- π’ Company & Salary β π Analyze β get the AI brief, fit score, recommended salary, apply strategy, and research deep-links.
- βοΈ Apply β pick Email / Cover Letter / CV Prompt β β¨ Generate β edit β copy or open in your mail client.
Job-Pilot/
βββ Home.py # dashboard entry β `streamlit run Home.py`
βββ pages/ # one file per tool (Streamlit-native multipage nav)
β βββ 1_π_Job_Search.py
β βββ 2_π_Application_Tracker.py
β βββ 3_π_Resume_Analyzer.py
β βββ 4_π€_Interview_Prep.py
β βββ 5_π°_Salary_Coach.py
βββ lib/
β βββ ai/ # client Β· prompts Β· generate Β· analyze (+ barrel)
β βββ jobs/ # normalize Β· sources Β· search (+ barrel)
β βββ ui.py Β· ui_css.py # shared components + the single CSS kit
β βββ storage.py # profile.json + applications.json
βββ scripts/ # check_lines.py (β€150 cap) Β· selftest_all.py
βββ docs/ # per-tool documentation
βββ .github/ # CI workflow + screenshots
βββ CLAUDE.md Β· CONTRIBUTING.md # engineering rules (CI-enforced)
βββ requirements.txt Β· .env.example Β· .streamlit/config.toml
Every file stays β€ 150 lines, one responsibility each, with strict page β lib barrel β service/engine layering. See CONTRIBUTING.md.
- Saved-applications tracker (
applications.json+ a pipeline page) - RΓ©sumΓ© analyzer, interview prep, salary coach
- More keyed boards (USAJobs, etc.)
- Optional one-click send via Gmail SMTP (currently draft +
mailto:) - Export the tracker to CSV
- No live Glassdoor ratings β Glassdoor has no free/legal API, so reputation is an AI summary + deep links, not scraped scores. Salary is real when a board provides it, otherwise a clearly-labeled AI estimate.
- Output quality tracks the model you route to β a weak/free model gives weaker text. Use the model picker + connection tester to land on a good one.
- Not a scraper β it uses official/public job APIs; it does not bypass any site's login or anti-bot.
Issues and PRs welcome. This is a small codebase with a deliberately strict, tiny rule set
(β€150 lines/file, one responsibility, zero heavy deps, a self-check per module) β read
CONTRIBUTING.md first. CI enforces the line cap and self-checks on every PR.
Adding a tool? Drop a file in pages/, reuse lib.ai.chat() + the lib.ui kit, and add a page under docs/.
MIT Β© 2026 Hassan Al-Najjar
A personal productivity tool. Respect each job board's Terms of Service and rate limits, and review every AI-generated draft before sending it.





