End-to-end PM job hunt for AI companies: find relevant roles, score them against your CV, then generate a candidate-tailored LLM interview prep doc — and an investor-grade company review — for any one with a single click.
Three workflows in one tool:
- Discovery — pulls live job postings from ~120 curated AI companies (frontier labs, AI infra, AI apps, dev tools) and VC-backed startups across Greenhouse, Lever, Ashby, SmartRecruiters, Workday, and a generic HTML fallback. Scores each role 0–100 against your CV across title/seniority, skills, domains, location, and company preference. Reads from a local SQLite cache, so you can replay scoring without re-fetching.
- Interview prep — for any job in the latest report, generate a comprehensive prep report covering the company, the role, and the likely interview questions for it (pulled from public signals about how that company interviews), then auto-adjust which of your CV stories to tell so each one maps to what the role actually wants. Built on Claude Opus 4.7 with adaptive thinking, structured Pydantic output, and prompt-cached static context (frameworks, calibrations, per-company playbooks). Streams live progress to the UI button (parsing → calling Claude → reviewing → writing files), runs a second-pass critic that scores the doc 1–10 with severity-tagged findings, and auto-opens the result as a styled HTML view + downloadable Word file.
- Company review — for any company on a job card, generate an investor-grade analysis doc covering valuation, funding rounds (with timeline chart), growth trajectory (headcount, ARR), competitive position (with quadrant chart vs the 3-5 most credible rivals), team & leadership, press sentiment, employee sentiment (Glassdoor / Levels / Blind), Reddit/HN community signal, risks, and a verdict (Strong apply / Apply with caveats / Pass / Inconclusive). Uses Claude Opus 4.7 with the server-side
web_searchtool to research live (15-25 queries), cites every numeric claim inline, and renders Mermaid charts + Markdown tables in the styled HTML view.
This repo ships clean — no CV, no name, no personal data. See Personalize this for your own use for the 5-minute setup.
Forking or cloning? Do these steps in order before running anything:
-
Drop your CV into the repo root. Any filename matching
*_CV.pdf,*_Resume.pdf, or*_resume.pdfis auto-gitignored (see.gitignorelines 52–54). Example:Alex_Kalyvas_CV.pdf. For DOCX/TXT, add the filename to.gitignoremanually. -
Create your
.envfrom the template:cp .env.example .env
Then add these three lines (the email credentials are also required if you want
--sendto work):ROLE_RADAR_CV_PATH=/absolute/path/to/Your_Name_CV.pdf ROLE_RADAR_CANDIDATE_NAME=Your Name ANTHROPIC_API_KEY=sk-ant-... # only needed for the interview prep generatorGet an Anthropic key at https://console.anthropic.com/settings/keys.
-
Edit
preferences.yamlfor your search — setlocation,include_remote,seniority,allowed_titles, andexcluded_keywords. The shipped defaults target SF Bay Area PM roles; change them to match yours. -
(Optional) Edit
data/portfolios.csvto add custom companies you want tracked beyond the curated AI Top 20 / VC portfolios. Schema:company_name,homepage_url,careers_url,vc_backers,notes. -
(Windows users) Wire up the local run scripts. Copy the templates and edit the paths inside:
cp run-scrape.bat.example run-scrape.bat # edit cd path + CV_PATH cp start-ui.bat.example start-ui.bat # edit cd path
Both
.batfiles are gitignored (see.gitignorelines 58–59) so your local paths never get committed. Point Windows Task Scheduler atrun-scrape.batfor weekly automated scrapes. -
What stays local (none of this is ever committed): your CV PDF, your
.env, generated prep docs inoutputs/prep/, learned like/dislike feedback in~/.role_radar/feedback.db(lives outside the repo, in your home dir), and your*.batscripts.
You're done. Run role-radar run "$ROLE_RADAR_CV_PATH" for a dry run, or jump to Installation below.
- Curated company lists — transparent scoring methodology for Top 20 AI companies and Top VCs
- Multi-ATS support — connectors for Greenhouse, Lever, Ashby, SmartRecruiters, Workday, generic HTML
- Smart matching — CV-based scoring across title/seniority, skills, domains, location, with a learned-preferences layer driven by like/dislike feedback
- Web review UI — Flask UI to browse matches, like/dislike to train the scorer, take notes, mark applied
- Interview prep generator — Claude Opus 4.7 with adaptive thinking, prompt-cached static context (frameworks, calibrations, per-company playbooks), structured Pydantic output, second-pass review critic, live SSE progress streaming, Markdown + DOCX output
- Company review generator — Claude Opus 4.7 with the server-side
web_searchtool researches each company live (15-25 queries) and emits an investor-grade Markdown doc with valuation timeline chart, ARR growth chart, competitor quadrant chart, funding-rounds table, comp tables, and inline-cited press / Glassdoor / Reddit sentiment - Email reports — HTML emails with match rationale and score breakdowns
- Local caching — SQLite for jobs/companies, prompt cache for the LLM
- Observability — structured JSON logging and run summaries
- Python 3.11 or higher
- pip or pipx
cd role-radar
pip install -e ".[dev]"# Initialize configuration files
role-radar init
# Edit .env with your email credentials
# Edit preferences.yaml for your job search
# Run in test mode (no email sent)
role-radar run path/to/your/cv.pdf
# Run and send email
role-radar run path/to/your/cv.pdf --sendCopy .env.example to .env and configure:
# Email settings (choose one provider)
ROLE_RADAR_EMAIL_PROVIDER=smtp # smtp or sendgrid
# SMTP settings (for Gmail, use an App Password)
ROLE_RADAR_SMTP_HOST=smtp.gmail.com
ROLE_RADAR_SMTP_PORT=587
ROLE_RADAR_SMTP_USERNAME=your-email@gmail.com
ROLE_RADAR_SMTP_PASSWORD=your-app-password
# Email addresses
ROLE_RADAR_EMAIL_FROM=your-email@gmail.com
ROLE_RADAR_EMAIL_TO=your-email@gmail.com
# Test mode (prints email instead of sending)
ROLE_RADAR_EMAIL_TEST_MODE=trueCustomize your job search:
# Single region:
location: "San Francisco Bay Area"
# Or a list — jobs match if ANY region matches:
location:
- "San Francisco Bay Area"
- "New York"
include_remote: true
seniority:
- "PM"
- "Senior PM"
- "Staff PM"
allowed_titles:
- "Product Manager"
- "Technical Product Manager"
- "AI Product Manager"
- "Senior Product Manager"
excluded_keywords:
- "Sales"
- "Marketing"
max_roles_per_email: 15Supported regions with smart matching: San Francisco Bay Area (covers SF, Palo Alto, Mountain View, etc.) and New York (covers NYC boroughs plus Jersey City / Hoboken / Newark). Any other string falls back to substring matching against the job location.
Add custom VC-backed companies:
company_name,homepage_url,careers_url,vc_backers,notes
Glean,https://www.glean.com,https://www.glean.com/careers,"Sequoia, Kleiner Perkins",Enterprise AI search
Harvey,https://www.harvey.ai,https://www.harvey.ai/careers,"Sequoia",Legal AI# Initialize configuration files
role-radar init
# Run job search (test mode - no email sent)
role-radar run path/to/cv.pdf
# Run with custom preferences
role-radar run path/to/cv.pdf --prefs my-preferences.yaml
# Run and send email
role-radar run path/to/cv.pdf --send
# Daily run mode (for cron)
role-radar run path/to/cv.pdf --daily
# Use cached jobs (skip fetching)
role-radar run path/to/cv.pdf --skip-fetch
# Show company lists
role-radar companies
# Debug a specific company
role-radar debug "OpenAI"Add to your crontab (crontab -e):
# Run Role Radar daily at 9 AM
0 9 * * * cd /path/to/role-radar && /path/to/venv/bin/role-radar run /path/to/cv.pdf --daily >> /var/log/role-radar.log 2>&1| Dimension | Max Points | Description |
|---|---|---|
| Company Category | 25 | Frontier Lab (25), AI Infra (20), AI Apps (15) |
| Technical Reputation | 20 | GitHub stars, benchmarks, research output |
| Funding/Scale | 20 | Total funding or public company status |
| Hiring Velocity | 15 | Open roles count, growth signals |
| Developer Adoption | 10 | API usage, community size |
| Recent Momentum | 10 | Product launches, major announcements |
| Dimension | Max Points | Description |
|---|---|---|
| Track Record | 35 | Unicorn count, notable exits |
| Fund Size/AUM | 25 | Assets under management |
| Stage Focus | 20 | Seed/Early (20), Growth (12) |
| SF Concentration | 10 | Portfolio focus on Bay Area |
| Recent Activity | 10 | Deals per year |
| Dimension | Max Points | Description |
|---|---|---|
| Title/Seniority | 25 | Match between CV and job seniority |
| Skills Overlap | 35 | Technical and product skills match |
| Domain Overlap | 25 | Industry/domain expertise alignment |
| Location Fit | 10 | Geographic and remote preferences |
| Company Preference | 5 | Boost for AI/VC-backed companies |
After each run, find these files in outputs/:
report_YYYYMMDD_HHMMSS.json- Detailed JSON reportreport_YYYYMMDD_HHMMSS.html- Visual HTML reportemail_RUNID.html- Email preview (test mode)ai_top20_scoring.md- AI company scoring breakdowntop_vcs_scoring.md- VC scoring breakdown
Role Radar is designed to be respectful of websites and APIs:
- No LinkedIn scraping - We don't scrape LinkedIn or any site that prohibits it
- robots.txt compliance - All HTML parsing respects robots.txt
- Rate limiting - Built-in rate limiter (2 req/sec default)
- Official APIs only - Uses official ATS APIs (Greenhouse, Lever, etc.)
- User-agent identification - Identifies itself properly
- Caching - SQLite cache reduces redundant requests
- Check that companies have valid ATS identifiers in the seed data
- Verify your network can reach the job board APIs
- Try
role-radar debug "CompanyName"to test a specific company
- Ensure
.envhas correct credentials - For Gmail, use an App Password
- Test with
ROLE_RADAR_EMAIL_TEST_MODE=truefirst
- Review your CV to ensure skills are clearly listed
- Adjust
preferences.yamlto match your experience level - Check that domains in your CV align with target companies
# Install dev dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Run with coverage
pytest --cov=role_radar --cov-report=html
# Format code
black src tests
ruff check src tests
# Type checking
mypy srcsrc/role_radar/
├── main.py # Typer CLI entry point
├── agent.py # Stateful agent: classify, draft, follow up, decide
├── agent_cli.py # `role-radar agent {triage,status,draft}` subcommands
├── config.py # Settings and preferences
├── cv_parser.py # CV/resume parsing
├── company_sources/ # Company list generation
│ ├── ai_top20.py # AI company scoring
│ ├── top_vcs.py # VC scoring
│ └── vc_portfolios.py # Portfolio discovery
├── connectors/ # Job board connectors
│ ├── greenhouse.py
│ ├── lever.py
│ ├── smartrecruiters.py
│ └── generic_html.py
├── interview_prep/ # Claude-powered interview prep doc generator
├── outreach/ # Claude-powered cold outreach drafter
├── scoring.py # Job-CV matching
├── dedupe.py # Deduplication
├── storage.py # SQLite (jobs, companies, applications, drafts, events)
├── emailer.py # Email sending
├── reporting.py # Report generation
└── utils/
├── http.py # HTTP client with rate limiting
└── logging.py # Structured logging
Once Role Radar finds a relevant role, generate a comprehensive interview prep report with one click. The report covers: a senior advisor's read of the company and role, the technical topics you need to know cold (sourced from the company's public engineering surface), the 5-7 likely interview questions for this role (with sample answers that re-map your CV stories to what this role actually wants), an honest read of how your background maps to the role and where the gaps are, a multi-day prep plan, and the strongest single move to make in the room. Markdown + DOCX output, plus a styled in-browser view.
-
Install the optional
interviewextra:pip install -e ".[interview]" # adds the anthropic SDK
-
Add your Anthropic API key to
.env:ANTHROPIC_API_KEY=sk-ant-... ROLE_RADAR_CV_PATH=/absolute/path/to/your/cv.pdf ROLE_RADAR_CANDIDATE_NAME=Your Name
Get a key at https://console.anthropic.com/settings/keys.
From the Web UI — click the 📄 Generate prep button on any job card. The button streams live progress via SSE — 📄 Parsing CV → 🤖 Calling Claude (~60s) · Ns → 🔍 Reviewing doc (~30s) · Ns → 💾 Writing files — and once done transforms into clickable 📖 Open · ⬇ DOCX links to the just-generated files. The Markdown also auto-opens in a new tab. Total time: 60-120s.
From the CLI — run against the latest report:
# Top match
role-radar prep cv.pdf --rank 1
# Top 3 matches (one doc each)
role-radar prep cv.pdf --top 3
# Specific job by ID
role-radar prep cv.pdf --job-id cursor_66e67c2e
# Custom output dir, skip DOCX
role-radar prep cv.pdf --rank 1 --output-dir ./my-prep --no-docx
# Generate + run a second-pass critic (gstack-style review)
role-radar prep cv.pdf --rank 1 --review- Prompt context: a static system prompt (~5K tokens) bundles the candidate's interview frameworks (DASME, SIGNAL metric cascade, model selection, anti-patterns, safety checklist, the 3 Laws of delivery), 4 calibrated weak/strong answer exemplars, and the company-specific playbook if available. Marked
cache_control: ephemeralso subsequent jobs at the same company hit the cache. - Job + CV: the per-job user message includes the full job posting, extracted CV signals, and a CV excerpt. Volatile, uncached.
- Structured output: Pydantic schema with Markdown leaf strings → renders cleanly to both
.mdand.docx. - Model: Claude Opus 4.7 with adaptive thinking,
effort=high,max_tokens=16000.
The static context (data/interview_prep/) is mirrored from kalyvask/interview-prep. To refresh it after the source repo updates, re-export the TypeScript content files to JSON and replace the snapshots in data/interview_prep/.
The batch pipeline (role-radar run) is stateless: it scrapes, scores, emails, and is done. The agent layer adds state — it remembers which jobs you've already triaged, which are in active outreach, which need a follow-up, and which you skipped. Each agent capability is a discrete method (fetch_new_matches, classify_match, draft_outreach, surface_followups, record_decision, pipeline_summary), so the same code path serves both the interactive CLI and a future autonomous loop.
- Pipeline state — three new tables in the local SQLite DB:
applications(per-job status, contact info, timestamps),outreach_drafts(subject, body, self-rating, sent flag),feedback_events(append-only audit log). - Outreach drafts — Claude generates a 100–200 word cold email per job, scored 1–10 for honesty about quality. Same Anthropic SDK pattern as the interview prep generator (Opus 4.7, adaptive thinking, prompt-cached system block).
- Follow-up surfacing — applications older than
--stale-days(default 14) since last contact get flagged for follow-up. - Bridge to existing scoring — every decision is mirrored to
~/.role_radar/feedback.dbso the existinglearned_adjustmentfield in the scorer picks up your saves and skips on the next run.
# Triage the latest report interactively (save / skip / draft / apply / note)
role-radar agent triage cv.pdf
# Triage with a custom score threshold and surface borderline matches too
role-radar agent triage cv.pdf --threshold 65 --show-borderline
# Snapshot of pipeline state (counts by status, follow-ups due)
role-radar agent status
# Draft outreach for one specific job
role-radar agent draft cv.pdf --rank 1
role-radar agent draft cv.pdf --job-id anthropic_pm-claude --contact-name "Jane" --contact-role recruiterThe outreach generator ships with sensible default voice rules (no em-dashes, no hedging, S.H.I.T. mechanics, 100–200 words). To override, drop two Markdown files into data/:
data/outreach_voice.md— voice and style rules. Replaces the default.data/outreach_profile.md— your career arc, distinctive credentials, signature stories. Used to ground the email's hook in something real.
Or point at files anywhere via env vars:
ROLE_RADAR_OUTREACH_VOICE=/path/to/voice.md
ROLE_RADAR_OUTREACH_PROFILE=/path/to/profile.mdBoth are optional. The generator only uses them when present.
# 1. Refresh the report
role-radar run cv.pdf --skip-fetch # or fully fetch if it's been a few days
# 2. Triage
role-radar agent triage cv.pdf
# For each match: [s]ave / [a]pply / [d]raft / [k]skip / [n]otes / [q]uit
# Drafts persist; follow-ups surface at the end of each session.
# 3. Anytime: pipeline check
role-radar agent statusThe agent never sends email on its own. It generates drafts and persists them; sending is manual (or a future feature). All decisions are written to local SQLite — no remote state.
MIT
Contributions welcome! Please open an issue first to discuss proposed changes.