Skip to content

devpyle/job-search-profile

Repository files navigation

Job Radar — AI-Powered Job Search & Application Tracker

A Claude Code-powered job search system that runs automated daily searches across multiple job APIs, emails you results twice a day, and provides a full web dashboard for tracking applications and generating tailored resumes and cover letters.

Built and maintained using Claude Code. Released under the MIT License.


Screenshots

Kanban Board — drag jobs through columns as you work them Board

Job Radar — tier-coded cards (green = Apply Now · blue = Worth a Look · gray = Skip) Radar

Job Detail — reviewing checklist, application URL field, and document generation Job Detail


What It Does

Automated Job Radar

  • Queries Adzuna, Brave Search, Tavily, LinkedIn, Remotive, WeWorkRemotely, Himalayas, RemoteOK, Jobicy, JSearch, Greenhouse, Lever, and Ashby ATS twice a day (9am and 4pm via cron)
  • LinkedIn scraping uses full filter params (full-time, newest first, remote+hybrid), fetches up to 2 pages per query with randomized delays, and retrieves full job descriptions for every job that passes pre-filtering — so Claude rates on real content, not just title
  • Deduplicates results across runs using multi-key dedup (company+title and URL) so you only see new postings
  • Filters out wrong titles, non-US locations, onsite/hybrid roles outside your area, staffing agencies, closed listings, broken URLs, salary below floor, and aggregate/category pages
  • Broad title targeting: PO, PM, BA, systems/functional/solutions/integration analyst, scrum master, agile delivery, platform/service/feature owner, pre-sales engineer, customer success (technical/enterprise), and more
  • Rates each job with Claude Haiku — Apply Now / Worth a Look / Weak Match / Skip — with a one-sentence reason
  • Within each tier, companies local to your configured metro area sort to the top — even for remote roles, proximity to the company is an advantage
  • Saves a dated markdown report to output/job-radar/ with each tier in its own section
  • Emails the full report in the email body and attaches the .md file

Web Dashboard (scripts/dashboard.py)

  • Local Flask app — neon dark UI, run it on your always-on machine, open at http://localhost:5000
  • Kanban board — drag jobs through: New → Reviewing → Drafting → Ready → Applied → Phone Screen → Interview → Offer → Accepted / Rejected / Passed
    • Status is drag-only — no dropdowns to accidentally click
    • Reviewing cards show a checklist badge (☑) as a reminder to work through the checklist before moving on
  • Radar view — browse reports in-browser, save jobs to the board with one click
    • Job cards have a color-coded right-border stripe: green = Apply Now, blue = Worth a Look, yellow = Weak Match, gray = Skip
    • Posting button is electric blue to distinguish it from the green Save button
    • Comment box on every card to note why a job isn't a fit or flag search tweaks
    • Dismiss jobs you've reviewed — stays hidden on future visits, toggle to show dismissed
    • Mark entire reports as reviewed — ✓ appears in the report dropdown
  • Portal Scanner — on-demand scanning of 230+ company career pages across Greenhouse, Lever, and Ashby ATS platforms
    • Filter by source (Greenhouse / Lever / Ashby) and location (Remote / Local / Other US)
    • Filters out non-US jobs and on-site roles outside your metro area
    • Save results directly to the kanban board
  • Fit Analysis — AI-powered evaluation of how well your experience matches a job posting
    • Match score (1-10, color-coded), experience matches mapped to specific JD requirements
    • Gap analysis with mitigation strategies for missing requirements
    • Surfaces 3-5 relevant STAR interview stories from your work history
    • Auto-moves job from New → Reviewing when analysis runs
  • Interview Story Bank — persistent view of all STAR stories from your job docs
    • Cross-references fit analyses to show which jobs each story was surfaced for
    • Ready reference for behavioral interview prep
  • Job detail — full description, timestamped notes, application URL field
    • Reviewing checklist: 7-step checklist (read JD, verify remote, check salary, research company, find hiring manager, paste apply URL, drag to Drafting) appears when card is in Reviewing
    • Paste and save a direct application URL — shows as a separate Apply Now button at the top
  • Document generation — generates tailored resume + cover letter via Claude (uses your Pro subscription via claude -p, no API cost)
    • ATS keyword optimization — extracts 15-20 key terms from the JD and reformulates existing experience to match the posting's vocabulary without inventing experience
  • Two-panel editor — edit resume and cover letter side by side, regenerate with custom instructions, version history, mark final
  • DOCX export — download resume or cover letter as a Word file
  • Source Health — tracks per-source job counts, errors, and latency for every radar run
    • Trend arrows show whether each source returned more or fewer jobs than the previous run
    • Filter stats show how many jobs each filter caught — helps tune search queries
    • 14-day run history at a glance
  • Filter audit trail — every filtered job is logged with the reason it was dropped
    • Collapsible "Filtered" section on each radar report page shows what was removed and why
    • Grouped by filter (wrong title, non-US, staffing, etc.) so you can spot patterns
  • Company memory — tracks dismiss/reject/boost signals per company across runs
    • Dismissing radar jobs or rejecting board jobs automatically records a signal for that company
    • Boost button on radar cards for companies you want to prioritize
    • Signal badges show on radar cards (e.g. "3x dismissed", "rejected before", "boosted")
  • Application completeness gate — prevents moving jobs to Ready without requirements met
    • Requires a final document (resume/cover letter marked final) and an apply URL
    • Server-side enforcement with clear error messages
  • Stale job alerts — highlights jobs stuck in a column too long
    • Per-status thresholds (New=3d, Reviewing=5d, Drafting=7d, Ready=3d, Applied=14d, Interviewing=7d)
    • Orange "stale" badge and border on board cards
  • SQLite database — all jobs, notes, documents, comments, dismissed state, and application URLs persist locally

Telegram Bot (scripts/telegram_bot.py, optional)

  • /radar — trigger a job search from your phone
  • /latest — pull up the most recent report
  • /status — last run summary
  • Chat with any supported AI model using your full career profile as context
  • Supports Claude, GPT-4o, Gemini, Kimi K2, DeepSeek, and any OpenRouter or Nvidia NIM model

Repo Structure

job-search-profile/
├── CLAUDE.md                          # Claude Code instructions
├── .env                               # create locally — your API keys (gitignored)
├── config.py                          # create locally — copy from config.example.py (gitignored)
├── config.example.py                  # template — copy this to config.py to get started
├── docs/                              # create locally — your personal profile docs (gitignored)
│   ├── personal-info.md               # contact, headline, summary
│   ├── technical-skills.md            # master skills list
│   ├── education.md                   # degrees and formatting
│   ├── resume-generation-rules.md     # rules Claude follows for resume/cover letter work
│   ├── YYYY-YYYY-company-title.md     # one file per role
│   └── templates/                     # starter templates for each doc type
├── input/
│   ├── job-postings/                  # drop JDs here before generating a resume
│   ├── old-resumes/                   # source material for backfilling docs
│   └── raw-notes/                     # informal role notes to clean up
├── output/
│   ├── documents/                     # DOCX resume/cover letter exports
│   └── job-radar/                     # daily search reports + dedup state
├── dashboard/
│   ├── templates/                     # Jinja2 HTML templates
│   │   ├── base.html
│   │   ├── board.html
│   │   ├── radar.html
│   │   ├── portals.html               # portal scanner results
│   │   ├── health.html                # source health + filter stats
│   │   ├── stories.html               # interview story bank
│   │   ├── job_detail.html
│   │   ├── draft.html
│   │   └── _card.html
│   └── static/
│       ├── style.css                  # dark mode UI
│       └── app.js
├── tests/
│   ├── conftest.py                    # shared fixtures, mock config
│   ├── test_job_radar.py              # filter, dedup, dataclass tests
│   ├── test_source_parsers.py         # fixture-based API response parsing
│   ├── test_dashboard.py              # Flask route smoke tests
│   ├── test_report_parser.py          # radar report markdown parsing
│   ├── test_startup.py                # config validation tests
│   ├── test_log.py                    # logging helper tests
│   ├── test_telegram_bot.py           # bot token redaction tests
│   └── fixtures/                      # JSON/HTML/XML API response fixtures
└── scripts/
    ├── job_radar.py                   # orchestrator — main(), seen file, dedup
    ├── models.py                      # Job dataclass
    ├── filters.py                     # 12 filter functions + regex constants
    ├── rating.py                      # Claude AI job rating + salary extraction
    ├── report.py                      # report building, debug log, email
    ├── normalize.py                   # shared cleanup: HTML strip, salary format
    ├── sources/                       # one module per source or source group
    │   ├── adzuna.py                  # Adzuna REST API
    │   ├── brave.py                   # Brave web search
    │   ├── tavily.py                  # Tavily web search + company extraction
    │   ├── linkedin.py                # LinkedIn HTML scraping + enrichment
    │   ├── remote_boards.py           # Remotive, WWR, Himalayas, RemoteOK, Jobicy
    │   ├── jsearch.py                 # JSearch / RapidAPI
    │   └── ats.py                     # Greenhouse, Lever, Ashby direct APIs
    ├── dashboard.py                   # Flask dashboard — kanban, radar, doc gen
    ├── db.py                          # SQLite access layer (named query functions)
    ├── portal_scanner.py              # on-demand ATS career page scanner
    ├── telegram_bot.py                # Telegram bot with multi-model AI chat
    ├── startup.py                     # env/config validation at boot
    └── log.py                         # log() helper with [source] prefix

Daily Workflow

Job radar runs automatically. Check your email at 9am and 4pm.

Reviewing the radar:

  1. Open the dashboard at http://localhost:5000 and go to Radar
  2. Review each job — save promising ones to the board with + Save, dismiss the rest with
  3. Add comments to any job to note why it's not a fit (helps tune the search over time)
  4. Click Mark Reviewed when done with the report

Scanning career portals:

  1. Go to Portals and click Scan All Portals — checks 230+ companies across Greenhouse, Lever, and Ashby
  2. Filter results by source and location (Remote / Local / Other US)
  3. Save interesting jobs to the board with + Save

Working a saved job:

  1. Go to Board — saved jobs land in the New column
  2. Click View and run Analyze Fit — get a match score, experience mapping, gap analysis, and relevant interview stories
  3. Drag the card to Reviewing — a checklist (☑) badge appears as a reminder
  4. Work through the Reviewing checklist:
    • Read the full job description
    • Verify remote / location works
    • Check salary range (listed or Glassdoor)
    • Research the company (size, funding, culture)
    • Find the hiring manager on LinkedIn
    • Paste the application URL into the field and save
  5. Drag the card to Drafting, then click ViewGenerate Resume + Cover Letter
  6. Edit in the two-panel editor, add regeneration instructions if needed
  7. Download DOCX, Mark Final — drag card to Ready
  8. Move through Applied → Phone Screen → Interview → Offer as you progress

Preparing for interviews:

  • Check the Stories tab for your STAR story bank — all signature stories from your work history in one place
  • Run a fit analysis on the job to see which stories are most relevant

To generate a resume without the dashboard:

  1. Save the job description to input/job-postings/company-title.txt
  2. Open Claude Code in the repo and say: generate resume for input/job-postings/company-title.txt

Setup

Prerequisites:

  • Claude Code with a Pro or Max subscription
  • Python 3.10+
  • Free API keys: Adzuna · Tavily
  • (Optional) Brave Search API — paid, but adds broader web search coverage
  • (Optional) Gmail account with an App Password — for email delivery of reports. Skip if you'll use the dashboard only.
  • Anthropic API key — used for Claude Haiku job rating in job_radar.py
  • (Optional) Telegram bot token for mobile access

Run the setup script (handles deps, .env, config.py, docs, and cron for you):

python setup.py

Or set things up manually:

Install dependencies:

pip install flask python-docx anthropic openai google-genai requests beautifulsoup4 python-dotenv markdown

Create your .env file:

ANTHROPIC_API_KEY=...
ADZUNA_APP_ID=...
ADZUNA_APP_KEY=...
BRAVE_API_KEY=...           # optional (paid API)
TAVILY_API_KEY=...
JSEARCH_API_KEY=...         # optional
GMAIL_FROM=you@gmail.com    # optional — skip if using dashboard only
GMAIL_TO=you@gmail.com      # optional
GMAIL_APP_PW=...            # optional
TELEGRAM_BOT_TOKEN=...      # optional
TELEGRAM_USER_ID=...        # optional

Configure for yourself: Copy config.example.py to config.py (gitignored) and fill it out:

cp config.example.py config.py

Key settings in config.py:

  • CANDIDATE_NAME, CANDIDATE_BACKGROUND — your name and a short profile summary used for job rating
  • HOME_CITY, HOME_STATE, HOME_METRO_TERMS — your location for local job filtering
  • JOB_DOCS — list of your docs/ job files for the dashboard
  • Search query lists — ADZUNA_QUERIES, LI_REMOTE_QUERIES, LI_LOCAL_QUERIES, etc. — customize for your target roles
  • BLOCKED_COMPANIES — add companies as you find ones that keep slipping through filters
  • DOMAIN_COMPANY_MAP — add domains for better company name extraction from Workday/ATS URLs

Run the dashboard:

cd job-search-profile
python scripts/dashboard.py
# Open http://localhost:5000

Schedule the radar (cron):

0 9,16 * * 1-5 cd /path/to/job-search-profile && python scripts/job_radar.py

Profile docs: Most setup time is writing your docs/ files. Template files showing the expected structure for each doc type are in docs/templates/. Once those are solid, Claude can generate a tailored resume in under 2 minutes.


Changelog

v1.6.0 — Workflow wins (2026-04-07)

  • Company memory: dismiss/reject/boost signals per company with radar badges
  • Application completeness gate: blocks Ready status without final doc + apply URL
  • Stale job alerts: orange badges on board cards past per-status time thresholds
  • 139 tests passing

v1.5.0 — Operational visibility (2026-04-07)

  • Source health tracking: per-source job counts, errors, latency, and trend arrows on a new Health dashboard tab
  • Filter audit trail: every filtered job logged with reason, shown in a collapsible section on each radar report
  • Per-run filter stats (how many jobs each filter caught) on Health page
  • 14-day run history table
  • 128 tests passing

v1.4.0 — Internal cleanup (2026-04-07)

  • Refactored job_radar.py from 1842 lines into focused modules: models.py, filters.py, rating.py, report.py, normalize.py, and 7 source modules under sources/
  • Extracted ~70 inline SQL queries from dashboard.py into db.py with named functions
  • Deduplicated salary formatting, keyword matching, and HTML cleanup into shared normalize.py
  • 125 tests passing

v1.3.0 — Foundation hardening (2026-04-07)

  • Added scripts/log.pylog() helper with [source] prefix and --verbose flag
  • Added scripts/startup.py — env/config validation with clear error messages at boot
  • Added 13 source parser fixtures + 26 tests (125 total)

v1.2.0 — Security audit fixes (2026-04-07)

  • Fixed Adzuna env var mismatch, XSS via innerHTML, URL validation, Flask debug mode
  • Added Telegram token redaction, lazy provider init, pinned requirements.txt
  • Added test suite (81 tests)

v1.1.0 — Feature release (2026-04-06)

  • Fit Analysis, Interview Story Bank, Portal Scanner, ATS keyword optimization

Acknowledgements

Built in collaboration with Casey Chalfant, who contributed to the initial architecture and feature design.

About

Stop manually searching job boards. This searches for you, rates every result with Claude, and handles the whole pipeline from radar to offer.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors