Skip to content

About

Finds relevant roles, scores them, tailors your resume & cover letter, reviews applications, and tracks your pipeline in Notion.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Job Wizard 🧙‍♂️

An end-to-end, self-hostable job-search assistant. It finds roles, scores them against your profile, tracks them in Notion, and — from a simple local dashboard — tailors a résumé + cover letter + a candid 3-lens review for each job you decide to pursue.

Built for any role and market: everything about you lives in config.json, generated by a setup wizard. Clone it, run setup, and it's your job search, not anyone else's.

Privacy first. Your résumé, your Notion data, your API keys, and every generated application stay on your machine. Nothing personal is ever committed to git.


What it does

        ┌─────────────┐   ┌──────────────┐   ┌─────────────────┐   ┌────────────────────┐
        │  1. SEARCH  │──▶│  2. SCORE +  │──▶│  3. TRIAGE on   │──▶│ 4. GENERATE via    │
        │ career sites│   │  DEDUPE      │   │  the dashboard  │   │ /job-wizard        │
        │ + LinkedIn  │   │              │   │                 │   │                    │
        └─────────────┘   └──────────────┘   └─────────────────┘   └────────────────────┘
         fresh postings    Fit + Priority      you flip good jobs    résumé + cover + 3-lens
         → Notion          (0–100 each)        to "Applying"         review → files on the
                                               (or add a link)       dashboard, ready to send
  1. Search (Mon/Wed/Fri, or "Run Search Now") — reads the career sites of the companies on your watchlist directly (Workday, Oracle, Greenhouse, Lever, Ashby, SmartRecruiters, Workable, Amazon), your LinkedIn job-alert emails (read-only Gmail), the CareerJet API for the Gulf and Southeast Asia, and a small, curated JSearch top-up (senior roles that fit you, posted in the last 3 days; the rest are logged, not saved). Every product role is kept, wherever it is — India and Outside India jobs get separate tabs.
  2. Score & dedupe — every job gets Fit (how well you match: skills 40 / experience 30 / domain 30) and Priority (how much you want it: company tier 40 / location 30 / freshness 20 / direct employer 10), both 0–100 and adjustable in Settings. The same job from several sources becomes one row; a job you've already seen is never added again; jobs that vanish from a career site are marked Expired; New jobs older than 5 days and dismissed ones are archived, then trashed after 30 days (reposts come back with a 🔁 tag). Everything lands in Notion.
  3. Triage — a local dashboard (http://localhost:4321) lets you sort, filter, and set each job's status. Flip the ones you want to Applying; mark the rest Rejected / Not Relevant. You can also paste a link to a job search didn't find.
  4. Generate — run /job-wizard in Claude Code opened in this project folder. It picks up every Applying job, tailors your résumé, writes a cover letter grounded in a real, searched company fact, runs a 3-lens review (ATS · recruiter · hiring-manager), and drops the .tex résumé and cover letter into each job's Details on the dashboard — ready for you to compile and submit.

It drafts; you send. Job Wizard never auto-submits an application or sends an email, and it never invents a metric or skill you don't have — gaps are surfaced for interview prep, not papered over.

📖 For the full step-by-step dashboard walkthrough, see USER_GUIDE.md.


Two ways to generate applications

Mode How it runs Cost When to use
Cowork /job-wizard (default) You run /job-wizard in a Claude session; Claude does the writing on your subscription Free — no API key The recommended path. Private, high-quality, on demand.
Headless API (optional) The dashboard server calls the Anthropic API itself the moment a job flips to Applying ~a few ¢/app Only if you want fully hands-off generation with no session open. Set auto_generate.enabled: true + add an Anthropic key.

The default is the free Cowork path (auto_generate.enabled: false). The headless path is fully built and gated behind that one flag.


Quick start (≈5 minutes)

Prerequisites: Node.js 18+, Python 3 with python-docx (pip3 install python-docx), and LibreOffice (soffice) for PDF rendering.

# 1. Clone + install
git clone https://github.com/dludhani-max/Job-Wizard.git
cd Job-Wizard
npm install

# 2. Configure your profile + credentials (creates config.json, gitignored)
npm run setup

# 3. Add the Notion fields + create your Companies database
npm run init-notion

# 4. Find jobs (try a dry run first — it writes nothing)
npm run search -- --dry-run
npm run search

# 5. Open the dashboard
npm run dashboard        # → http://localhost:4321

Then add your target companies on the dashboard's Companies tab (paste one job link from each company's careers site), optionally Connect Gmail in Settings for LinkedIn alerts, triage on the India Jobs / Outside India Jobs tabs, and run /job-wizard in your Claude session to generate applications for everything you set to Applying.


Running /job-wizard (how generation actually happens)

Generation runs inside a Claude session on your own subscription — no API key, no per-application cost. Open Claude Code in this Job-Wizard folder (the terminal, or the desktop app's Code tab with this folder selected — a chat can't see your files), and once you've flipped the jobs you want to Applying on the dashboard, simply tell Claude:

run job-wizard (or type /job-wizard)

Claude then follows the runbook in commands/job-wizard.md and, for every Applying job:

  1. Reads the job description (for ➕ Add External Job links, it fetches the JD from the URL first and scores the job).
  2. Tailors your résumé to that JD — honestly, never inventing a metric or a skill you don't have.
  3. Writes a cover letter grounded in one real, searched fact about the company.
  4. Runs a 3-lens review (ATS · recruiter · hiring-manager) with a candid verdict.
  5. Writes the .tex résumé and cover letter (starting from resume/master-resume.tex) into that job's Details ▾ on the dashboard.

You download and submit them yourself — it never applies or emails on your behalf.

Prerequisites for a run: the dashboard running (npm run dashboard) so results surface, resume/master-resume.tex present, and — optionally — a Tavily key in config.json for the cover-letter fact.

Note: /job-wizard is registered for Claude Code via .claude/commands/job-wizard.md (a link to commands/job-wizard.md), so it appears only in sessions opened in this folder. Anywhere else, saying "run job-wizard" in this folder does the same thing.


Credentials

All keys live in config.json (gitignored). config.example.json is the template.

Credential Required? What it's for
Notion integration token ✅ Required The pipeline database — read/write, system of record
JSearch API key (OpenWeb Ninja) ➖ Recommended Top-up search + finding JDs for LinkedIn jobs (free tier 200 req/month; budget tracked in Settings)
Google OAuth client (Gmail, read-only) ➖ Recommended Reads your LinkedIn job-alert emails. Stored in the macOS Keychain, not config.json — see Connect Gmail below
CareerJet partner ID ➖ Optional (free) Gulf / Southeast Asia job search (UAE, Saudi, Qatar, Singapore, Malaysia by default — config.careerjet.locales). Sign up as a CareerJet partner and put the ID in careerjet.affid
Tavily API key ➖ Optional (free tier) The one verified company fact in each cover letter; without it, that line is omitted
Anthropic API key ➖ Optional Only for the headless auto-generate mode; the default /job-wizard path needs no key

Prefer OAuth/connector credentials over raw keys where a tool offers both. One credential per project; never reuse across projects. Career-site feeds need no credentials.

Connect Gmail

LinkedIn has no job API, but its job-alert emails carry fresh, personalised postings. Job Wizard reads them with read-only Gmail access (it can't send, delete, label or mark anything read).

  1. Set up a few LinkedIn job alerts (e.g. your target titles in Bengaluru, Dubai, London).
  2. At console.cloud.google.com: create a project → APIs & Services → Library → Gmail API → Enable.
  3. OAuth consent screen: user type External, add your own address under Test users, then Publish app (status In production). Apps left in Testing lose access after 7 days. Google will show "unverified app" once when you connect — it's your own app.
  4. Credentials → Create credentials → OAuth client ID → Desktop app. Copy the Client ID and Client secret.
  5. Dashboard → ⚙ Settings → Connections → Gmail → Set up, paste both, click Save & connect, approve the read-only permission.

The client secret and refresh token go into your macOS Keychain (service job-wizard). Disconnect in Settings removes the token and revokes it; you can also revoke at myaccount.google.com/permissions.


Project structure

Job-Wizard/
├── server.js                 # local dashboard server (Notion-backed, zero personal data leaves the machine)
├── dashboard.html            # the dashboard UI (triage, statuses, Add External Job, Paste JD, Details)
├── render_application.py     # deterministic ATS-clean PDF renderer (python-docx → LibreOffice)
├── init-notion-fields.js     # upgrades the Jobs schema, creates + seeds the Companies database
├── config.example.json       # config template (copy → config.json via `npm run setup`)
├── agents/
│   └── job-search-agent.js   # the search run: sources → keep → merge → score → Notion → expire
├── lib/
│   ├── sources/              # one module per source: workday · oracle · greenhouse · lever · ashby ·
│   │                         #   smartrecruiters · workable · amazon · linkedin (alert emails) · jsearch
│   ├── job.js                # the common job shape, regions, product-role rule
│   ├── job-dedup.js          # same-job matching within a run and against Notion
│   ├── score.js              # Fit + Priority (also served to the dashboard)
│   ├── store.js · notion.js  # Notion mapping + small REST client with retries
│   ├── gmail.js              # read-only Gmail (OAuth, Keychain) for LinkedIn alerts
│   ├── detect-ats.js         # pasted job link → job system + board
│   ├── jsearch-client.js     # JSearch HTTP client
│   ├── generate-application.js   # headless-mode orchestrator (optional API path)
│   ├── llm-client.js         # LLM driver (Anthropic; provider-agnostic seam)
│   └── web-search-client.js  # Tavily/Brave client for the cover-letter fact
├── commands/
│   └── job-wizard.md         # the /job-wizard runbook (the free Cowork generator)
├── skills/                   # tailor-resume · write-cover-letter · job-search · job-wizard-setup · linkedin-growth
├── test/                     # npm test (node --test, no network)
├── diagrams/                 # workflow / architecture / lifecycle / dataflow (SVG)
├── USER_GUIDE.md             # full step-by-step operator's manual
├── README.md                 # this file
└── LICENSE

# Never committed (gitignored): config.json · resume/ · applications/ · memory/ · data/ · *.pdf · *.docx

Scoring & pipeline

Fit (0–100) — skills 40 · experience 30 · domain 30. Without a job description it's an estimate (shown as ~); 🔍 Find JD or 📋 Paste JD on the row makes it exact. Tiers: High ≥ 80, Medium ≥ 65, Stretch ≥ 50. Skills = how many of candidate.skills the JD names (whole words; "A/B/C" matches any of A, B or C), with scoring.skills_full matches (default 6) counting as full marks — so list the skills that set you apart, not words every PM posting uses ("roadmap", "stakeholders").

Priority (0–100) — company tier 40 · location 30 · freshness 20 · direct employer 10. Entry-level titles and your excluded titles/companies get a penalty. Nothing is hidden for scoring reasons — weak matches sink to the bottom. All weights are editable in Settings.

Notion status lifecycle:

New ──┬─→ Watchlist ──┐
      └──────→ Applying ─→ Applied ─┬─→ In Conversation ─→ Interviewing ─→ Offer ─→ Accepted
                                    └─→ No Response (suggested at 21 days)
Exits from most states: Withdrawn · Rejected · Expired · Not Relevant

Duplicates: each job is identified by its source ID (e.g. the Workday requisition) and matched across sources by company + title + city. Re-running search never re-adds a job, never touches your status or notes, and a job you closed stays closed. A job that vanishes from a company's career site in two successful runs is marked Expired (and restored if it's reposted).


Security & privacy

Never committed (enforced by .gitignore): config.json, resume/, applications/, memory/, data/, logs/, CLAUDE.local.md, and any *.pdf / *.docx.

Design choices: the Notion token stays server-side (never exposed to the browser page); the PDF renderer is deterministic, so a model never controls document formatting; and the generators produce drafts only — no auto-submit, no auto-email.

Treat this repo as if it were public even if it's private: personal data belongs in Notion or a gitignored local file, never in a committed file.


Documentation

  • USER_GUIDE.md — the complete step-by-step manual: every dashboard control, the /job-wizard flow, adding external jobs, and troubleshooting.
  • diagrams/ — workflow, architecture, lifecycle, and dataflow visuals.

Contributing

Improvements welcome — this is built to be shared and adapted. Good areas:

  • Additional job sources (Adzuna, Jooble, company career-page feeds)
  • More LLM providers in lib/llm-client.js (OpenAI, Gemini, OpenRouter)
  • Better résumé-tailoring heuristics
  • Support for other markets and role types

Please keep the golden rule intact: no personal data in the repo, and drafts only — never auto-submit.


License

MIT — fork it and make it your own job search.

About

Finds relevant roles, scores them, tailors your resume & cover letter, reviews applications, and tracks your pipeline in Notion.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages