
Job stack
A job-search assistant that finds the right roles, tracks every application from Gmail, and reports my progress every Sunday.
Contents
TL;DR
Job stack is my personal job-search assistant. It collects openings from five job boards and Telegram channels, ranks each one against my profile, tracks every application through the replies in my Gmail, and tells me each week how my coding practice and job switch are going. I designed and built it on my own, as eight Claude Code skills (reusable workflows that Claude Code runs on command), four helper AI agents and a local dashboard. It's in use today, with two tasks running on a schedule on their own.
The problem
A job search is scattered. Openings sit across LinkedIn, Indeed, Naukri, Glassdoor, Wellfound and Telegram channels, and many of them are the wrong level, the wrong stack or not remote. Replies come back as loose emails that are easy to lose track of.
I wanted one ranked feed of entry-to-mid-level remote Software, Full Stack, Frontend and Backend roles, and one place that shows where every application stands.
What I built
Find jobs
- One search across five boards: a single
/job-searchcommand sends one scraper agent per board, all in parallel, to LinkedIn, Indeed, Glassdoor, Wellfound and Naukri through Apify (a web-scraping service). - Boards as config: each board is one small config entry that maps its fields into a shared format, so adding a board is a config edit with no code change.
- Telegram channels: a second source reads job posts from chosen Telegram channels into the same format, and does nothing when none are set.
Rank jobs
- Scoring against my profile: each job earns points for matching my core stack, being remote and having a matching title, and loses points for seniority signals, eligibility gates such as US citizenship, or a crowd of over 200 applicants. Jobs land in Strongest, Stretch, Safest or Remaining tiers.
- Filters and duplicates: senior and intern titles, wrong-stack roles and postings older than 12 days are dropped, and the same role found on two boards is merged into one.
- AI shortlist: a curator agent reads the real descriptions of the top jobs and picks 10 to 15, each marked apply now, tailor first or skip.
Apply and track
- Tailored resumes: an agent writes a tailored resume and cover letter for each role as separate copies. It never touches the master documents and never invents experience.
- Status from Gmail: every night the stack reads the day's emails, sorts each one into received, assessment, interview, offer or rejected, and matches it to the job I applied for. A status only moves forward, never back.
- Follow-up drafts: stalled applications get a follow-up email drafted for me to review. Nothing is sent automatically.
Improve
- Resume audit: it compares my applications with rejections, ghosted applications (no movement after 14 days) and the best-fit roles, then writes a section-by-section critique of my resume and cover letter.
- Progress log and weekly report: I log practice in one sentence ("solved Two Sum, arrays, easy"). Every Sunday at 19:00 IST a report combines that with my public LeetCode stats to show weak topics, questions to revisit, my streak, the per-company application funnel and my pace toward a target date.
See it
- Local dashboard: a small web app on my laptop with Jobs, Applied, Shortlist, Resume Audit, Progress and Runs tabs. Jobs shows each role's tier, score and matched skills, and a red banner warns me when a run came back incomplete.
How it works
Claude Code runs each step in order, pre-written Python scripts do the work, and everything meets in shared data files that the dashboard reads.
flowchart TB
A(["You start a search"])
B["One scraper per board"]
T["Telegram channels"]
C["Filter, merge and score"]
D["AI shortlist"]
E["Tailored resume copies"]
F["You apply"]
G["Nightly Gmail check"]
H["Audit and weekly report"]
I["Local dashboard"]
A e1@-->|"five boards in parallel"| B
B e2@--> C
T --> C
C e3@-->|"top tiers"| D
D e4@--> E
E e5@--> F
F e6@-->|"replies in Gmail"| G
G --> H
H --> I
C --> I
e1@{ animate: true }
e2@{ animate: true }
e3@{ animate: true }
e4@{ animate: true }
e5@{ animate: true }
e6@{ animate: true }
%% walkthrough "Find remote Full Stack roles": A > B > C > D > E > F > G
%% walkthrough "Pull jobs from my Telegram channels": T > C > I
%% walkthrough "A rejection lands in Gmail": G > H > I
The scripts are in Python, mostly standard library. Scraping goes through Apify via its MCP server (a way for AI assistants like Claude to use outside tools), Telegram through Telethon (a Telegram library), email by reading my Gmail inbox over IMAP (the standard way mail apps fetch email), and practice stats through LeetCode's public stats API. The dashboard is a standard-library Python server with plain HTML, CSS and JavaScript, so there is no build step.
Key decisions & trade-offs
- Scripts do the work, the AI gives judgement: every step is a pre-written script. The model only returns decisions as data (a sorted email, a shortlist, an audit), never code written on the fly, so unattended runs behave the same every time. The cost: new behaviour needs a script change, not just a prompt edit.
- Config over code: scoring weights, filters, boards and paths live in config files. The cost: some keyword lists and display labels still live in the scoring script.
- Safe writes, one owner per file: a scrape can run while I click Apply, so files are written to a temp copy and swapped in, and each data file has exactly one writer. The cost: agents must route their writes through the owning script.
- Checking coverage mechanically: the main quiet failure was a search that fell back to LinkedIn only and still looked fine. A coverage check now marks the run incomplete when an enabled board returns nothing. The cost: an extra check step in every run.
- Least-privilege agents: each helper agent gets only the tools it needs. The scraper can't edit config, and the curator and tracker can't use any outside tools. The cost: the main session has to run each stage itself.
Results & status
- Moved to a new machine: the stack originally depended on fixed D:\ drive paths from a work laptop and stopped working on a machine without one. I rebuilt it to find its own folder, wrote a migration script that previews by default, can safely run twice and refuses to overwrite files, and rebuilt the dashboard against the existing data formats.
- Running unattended: the Gmail status check runs every night and the progress report every Sunday at 19:00 IST, both on Claude Code's scheduler.
- Health checks built in: one script checks paths, folders, dependencies, the skills and that required keys are present (without printing them). Another validates all eight skill files against the Agent Skills specification.
What this shows
- Agentic workflow design: one orchestrating workflow over four least-privilege agents, a parallel search per board, and AI judgement returned as data.
- Data pipeline engineering: per-board field mapping, filters, duplicate merging and weighted scoring into tiers.
- Reliability for unattended runs: safe writes, single-owner files and a coverage check that catches a half-empty search.
- Integrations: Apify, Telegram, Gmail and LeetCode in one pipeline.
- Portability and tooling: one path resolver, a re-runnable migration script, a health checker and a spec validator.