A Chrome extension that analyzes job-resume fit directly on LinkedIn and other job boards. Click the extension on any job posting to see your match score, gap analysis, and personalized recommendations — all in seconds.
Status: Phase 1 MVP in development. Targeting public Chrome Web Store launch in 6-7 weeks.
- Install the Chrome extension
- Sign in with Google and upload your resume once (one-time setup, ~2 minutes)
- Open any job posting on LinkedIn, Greenhouse, or a company careers page
- Click the extension to get an instant fit score and decision
Above 65 → worth applying, see strengths and resume edits. Below 65 → the extension tells you why not, so you can move on.
Save hours of "should I apply?" deliberation. Spend that time on the roles that actually fit.
You click the extension on any job posting. In ~30 seconds, you see:
Verdict — Apply / Apply with caveats / Don't apply, with a one-sentence reason
Fit Score (0-100) — explainable reasoning, broken down by:
- Role match
- Skills overlap
- Seniority fit
Strengths — your experiences that already answer the JD's requirements, with the specific JD line each one answers
Gaps — what's missing, separated into critical vs nice-to-have
(Phase 2) Resume edit suggestions (gated behind a fabrication validator), tailored cover letter draft, and likely interview questions
After installing the extension:
- Click the extension icon → opens the JD-Resume Fit Agent setup page
- Sign in with Google
- Upload your resume (PDF or Markdown)
- (Optional) Add your LinkedIn profile URL, target roles, and location preferences
- Done — return to LinkedIn and click the extension on any job posting
Your resume is stored securely on our servers (encrypted at rest) so you can use the extension across multiple devices. We never share your data with employers or third parties.
A filter, not an amplifier. Most job tools optimize for volume: apply faster, apply to more. JD-Resume Fit Agent optimizes for the opposite — helping you say "no" to roles that aren't worth your time, so you can put real energy into the ones that are.
If your fit score is below 65, the extension says so plainly and tells you why. The goal isn't to maximize applications submitted. It's to maximize the signal-to-noise ratio of your job search.
┌─────────────────────────────────────────────────────────────┐
│ Chrome Extension (Manifest V3) │
│ ┌──────────────────┐ ┌─────────────────────────────┐ │
│ │ Content Script │ │ Popup UI │ │
│ │ (LinkedIn DOM │ │ - Verdict + Fit score │ │
│ │ extraction) │ │ - Strengths & gaps │ │
│ └────────┬─────────┘ │ - Resume edit suggestions │ │
│ │ │ - "Generate Cover Letter" │ │
│ │ │ - "Interview Prep" │ │
│ │ └──────────┬──────────────────┘ │
│ └─────────────────────────┘ │
│ │ │
│ Auth token stored in chrome.storage │
└─────────────────────────┬───────────────────────────────────┘
│ HTTPS (Bearer token)
▼
┌─────────────────────────────────────────────────────────────┐
│ Web App + FastAPI Backend (Railway) │
│ │
│ Web (setup page): │
│ - Google OAuth sign-in │
│ - Resume upload / view / replace │
│ - Profile preferences │
│ │
│ API: │
│ POST /analyze ← Phase 1 (default) │
│ POST /cover-letter ← Phase 2 (on-demand button) │
│ POST /interview-prep ← Phase 2 (on-demand button) │
│ GET /resume ← fetch user's resume │
│ PUT /resume ← update user's resume │
│ │
│ Per-user rate limiting + Prompt caching │
└─────────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ PostgreSQL (Railway) │
│ - users (id, email, google_sub, created_at) │
│ - resumes (user_id, content, format, updated_at) │
└─────────────────────────────────────────────────────────────┘
│
▼
Agent Orchestration (Claude Agent SDK)
Default analysis pipeline (sequential)
┌─────────────┐ ┌──────────────┐ ┌───────────┐
│ JD Parser │ ▸ │ Resume │ ▸ │ Strategy │
│ │ │ Matcher │ │ │
└─────────────┘ └──────────────┘ └─────┬─────┘
│
▼
┌──────────────┐
│Resume Tailor │ ◀ Phase 2
└──────────────┘
On-demand generators (parallel after strategy)
┌──────────────┐ ┌──────────────┐
│Cover Letter │ │Interview Prep│ ◀ Phase 2
└──────────────┘ └──────────────┘
│
▼
Anthropic API
(Claude Sonnet 4.7)
The architecture is scaffolded for all six agents from day one. Phase 1 ships four; Phase 2 fills the remaining two without changing the architecture.
| Agent | Phase 1 | Phase 2 | Purpose |
|---|---|---|---|
| jd_parser_agent | ✅ | — | Extracts must-haves, nice-to-haves, seniority signals from raw JD |
| resume_matcher_agent | ✅ | — | Compares resume to parsed JD, produces fit score with reasoning |
| strategy_agent | ✅ | — | Synthesizes parsed JD + gap report into application strategy |
| resume_tailor_agent | 🟡 scaffold | ✅ | Suggests bullet-level resume edits — deferred to Phase 2 pending fabrication validator |
| cover_letter_agent | 🟡 stub | ✅ | On-demand: drafts cover letter following strategy |
| interview_prep_agent | 🟡 stub | ✅ | On-demand: generates likely interview questions and approaches |
Why scaffolding for the full vision now: Adding agents later means revisiting the routing, state schema, and API contract. Designing for six up front means Phase 2 is "fill in two files" rather than "rearchitect."
Backend:
- Python 3.11+
- Claude Agent SDK (agent orchestration, subagents, hooks)
- Anthropic API (
claude-sonnet-4-7for analysis,claude-haiku-4-5for lightweight tasks) - FastAPI (HTTP API layer)
- PostgreSQL (user accounts, resume storage)
- Google OAuth (authentication, via
authlib) - JWT (extension ↔ backend token exchange)
- Prompt Caching (cuts resume re-processing cost by ~90%)
Frontend (extension):
- Chrome Extension (Manifest V3)
- Vanilla JS + Tailwind CSS (no framework — keeps the popup lightweight)
chrome.storagefor auth token persistence
Frontend (setup web app):
- Next.js (App Router) + Tailwind CSS
- Hosted alongside the API on Railway
Infrastructure:
- Railway (FastAPI + PostgreSQL deployment)
- Chrome Web Store (extension distribution)
- Sentry (error monitoring)
Planned for Phase 2+:
- Model Context Protocol (MCP) servers for GitHub integration (verify project claims) and filesystem (PDF resume parsing pipeline)
jd-resume-fit-agent/
├── README.md
├── backend/
│ ├── pyproject.toml
│ ├── .env.example
│ ├── docker/
│ │ └── Dockerfile
│ ├── alembic/ # database migrations
│ ├── src/
│ │ ├── agents/
│ │ │ ├── __init__.py
│ │ │ ├── jd_parser.py # ✅ Phase 1
│ │ │ ├── resume_matcher.py # ✅ Phase 1
│ │ │ ├── strategy.py # ✅ Phase 1
│ │ │ ├── resume_tailor.py # 🟡 Phase 2 scaffold (implemented; deferred for safety)
│ │ │ ├── cover_letter.py # 🟡 Phase 2 stub
│ │ │ └── interview_prep.py # 🟡 Phase 2 stub
│ │ ├── auth/
│ │ │ ├── google_oauth.py # OAuth flow
│ │ │ └── jwt.py # token issue / verify
│ │ ├── models/
│ │ │ ├── user.py # SQLAlchemy: users table
│ │ │ └── resume.py # SQLAlchemy: resumes table
│ │ ├── orchestrator.py # routes requests to agent pipelines
│ │ ├── api.py # FastAPI app
│ │ ├── state.py # shared dataclasses
│ │ ├── rate_limit.py # per-user rate limiting
│ │ └── caching.py # prompt caching helpers
│ └── tests/
├── extension/
│ ├── manifest.json # Manifest V3
│ ├── popup/
│ │ ├── popup.html
│ │ ├── popup.css
│ │ └── popup.js # main UI logic
│ ├── content/
│ │ ├── linkedin.js # LinkedIn DOM extraction
│ │ └── (future: greenhouse.js, lever.js, etc.)
│ ├── background/
│ │ └── service-worker.js # message routing, token mgmt
│ ├── pages/
│ │ ├── cover-letter.html # 🟡 Phase 2 detail page
│ │ └── interview-prep.html # 🟡 Phase 2 detail page
│ ├── assets/
│ │ └── icons/
│ └── lib/
│ ├── api-client.js # talks to FastAPI backend
│ └── auth.js # token storage / refresh
├── web/ # setup + account web app
│ ├── package.json
│ └── app/
│ ├── (auth)/
│ │ └── sign-in/page.tsx # Google OAuth entry
│ ├── setup/page.tsx # resume upload + preferences
│ └── account/page.tsx # view / replace resume
└── docs/
├── architecture.md
└── product-spec.md
First-time user
- Install extension from Chrome Web Store
- Extension shows onboarding tooltip: "Upload your resume to start scoring jobs — save hours of guesswork"
- User clicks → opens setup web app in new tab
- User signs in with Google → uploads resume → done
- Extension receives auth token via
chrome.storage, ready to use
Daily use
- User opens a job posting on LinkedIn, Greenhouse, or company careers page
- User clicks the extension icon
- Content script (
linkedin.js) extracts the JD text from the page DOM - Popup sends
POST /analyzewith the JD; backend looks up the user's resume from PostgreSQL using the auth token - Backend runs the Phase 1 pipeline:
- jd_parser → parsed_jd
- resume_matcher → gap_report (with fit score)
- strategy → application strategy and verdict
- Response renders in popup: verdict, score, strengths, gaps
- (Phase 2) User clicks "Generate Cover Letter" → opens new tab → triggers
POST /cover-letter→ tab displays editable result
Phase 1 is funded out-of-pocket. Cost controls in place from day one:
- Prompt caching: the resume content is cached server-side per user, reducing per-request token cost by ~85%
- Per-user rate limiting: each free user gets 5 analyses per day (signed-in, since Phase 1 has accounts)
- Model tiering:
claude-haiku-4-5for lightweight steps,claude-sonnet-4-7only where reasoning quality matters
Projected Phase 1 cost: $25-55/month assuming 50-100 active users. Breakdown: ~$15-35 Anthropic API, ~$5-10 Railway (compute), ~$5-10 Railway PostgreSQL.
Phase 3 introduces a Pro tier ($9.99/month) to cover sustained costs at scale.
cd backend
python -m venv .venv
source .venv/bin/activate
pip install -e .
cp .env.example .env
# Add ANTHROPIC_API_KEY, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, JWT_SECRET
# DATABASE_URL points to local Postgres or Railway-provided URL
alembic upgrade head # run migrations
uvicorn src.api:app --reload --port 8080cd web
npm install
cp .env.example .env.local # NEXT_PUBLIC_API_URL, GOOGLE_CLIENT_ID
npm run dev # http://localhost:3000# Open Chrome → chrome://extensions/
# Toggle "Developer mode"
# Click "Load unpacked"
# Select the `extension/` directoryThe extension talks to http://localhost:8080 in development mode. Production builds point to https://api.jdfit.app (TBD).
Phase 1 uses a layered test approach matched to the risk profile of an LLM pipeline: fast, free tests on every push; expensive LLM eval tests run on demand before merging prompt changes.
| Layer | Catches | Tool | Runs |
|---|---|---|---|
| Unit | Pure-function bugs (caching helpers, schema construction) | pytest |
CI |
| Mocked agent | Tool-use parsing, request shape, error handling | pytest + unittest.mock |
CI |
| API contract | Route wiring, request/response schema, rate limits | fastapi.testclient |
CI |
| LLM eval | Prompt regressions (score drift after a prompt tweak) | pytest -m eval |
Manual, before merging prompt changes |
| Lint | Style, imports, common mistakes | ruff |
CI |
cd backend
pip install -e ".[dev]"
pytest # unit + mocked agent + API contract (free, fast)
pytest -m eval # real Anthropic calls (costs credits)
ruff check src testsbackend/tests/
├── conftest.py # shared fixtures: mock Anthropic client, sample JD + resume
├── test_caching.py # unit
├── test_jd_parser.py # mocked agent
├── test_resume_matcher.py # mocked agent
├── test_strategy.py # mocked agent
├── test_resume_tailor.py # mocked agent
├── test_api.py # FastAPI TestClient
└── evals/
├── cases.jsonl # hand-labeled JD + resume + expected score range
└── test_eval.py # @pytest.mark.eval — real Anthropic calls
- Chrome extension UI — covered by manual click-testing during Phase 1. A Playwright + loaded-extension setup is planned for Phase 2, once the DOM extractors stabilize across LinkedIn, Greenhouse, Lever, and Workday.
- Live LinkedIn pages — too brittle (anti-automation). Phase 2 drives Playwright against fixture HTML pages served from
localhostinstead.
A GitHub Actions workflow runs ruff check + pytest -m "not eval" on every push and on every pull request. LLM eval tests are run manually before merging changes to any agent's system prompt.
- 3-agent analysis pipeline (JD parser, resume matcher, strategy)
- Chrome extension with LinkedIn support
- Google OAuth sign-in + resume upload web app
- PostgreSQL storage for users and resumes
- Per-user rate limiting (5 analyses/day on free tier)
- FastAPI + Next.js deployed on Railway
- Resume edit suggestions, gated behind a programmatic fabrication validator
- Cover letter generation (button-triggered)
- Interview prep generation (button-triggered)
- Support for Greenhouse, Lever, Workday JD pages
- PDF resume upload (filesystem MCP server for parsing)
- Posting legitimacy check (flag suspicious JDs)
- Account page: replace resume, view past analyses
- Free tier (current limits) + Pro tier ($9.99/month, unlimited)
- Stripe integration
- LLC formed, payment processing live
- GitHub MCP integration (verify project claims, surface portfolio in cover letters)
- Multi-resume A/B testing (e.g., "frontend-leaning" vs "platform-leaning")
- Browser support beyond Chrome (Edge, Firefox)
- Application tracker integration (read-only)
- Team / coaching features
I'm a recent CS grad who came from four years in design. In my own 2026 job search, I kept asking the same question: "Is this role worth the 30 minutes it takes to tailor my resume and apply?"
ChatGPT gave bland answers. Keyword-matching tools missed the point. So I built the thing I wished existed — something that reads the JD and my resume the way a thoughtful friend would, and tells me honestly whether to spend the time.
MIT.
0 comments
log in to comment.