A serverless, GitOps-driven job application and tracking system. GitHub Issues are your pipeline, GitHub Projects is your Kanban board, and GitHub Actions is the automation plane that scrapes roles, AI-triages fit, and tailors your resume — all running free on GitHub's infrastructure.
- Replace the placeholder resume in
resumes/resume.yaml(a YAML file in JSON Resume format) with your real work history, then commit and push tomain. The daily cron takes over from there.
Per-user options live in config/settings.yaml and are edited directly in that file — same workflow as replacing the placeholder resume. Notable defaults:
search.work_preferencedefaults tohybrid(also acceptsremoteoronsite)- Your resume's
basics.locationfields (city,state,countryCode) drive the search location — include all three for best results withhybrid/onsitemodes search.enabled(set tofalseto pause daily scraping)fit_threshold(minimum fit score,1.0–5.0, default3.5)
theme controls how your tailored resume is rendered to PDF. It accepts any theme from the JSON Resume theme ecosystem — browse npm or GitHub for options — in one of two pinned forms:
- An npm package pinned to an exact version:
"<package>@<version>", e.g."@jsonresume/jsonresume-theme-professional@1.0.22" - A GitHub-only theme pinned to a full 40-character commit SHA:
"github:<owner>/<repo>#<sha>"
GitEmployed installs the theme package — running its own code in the process — and later renders with it, so always pin to an exact version or commit SHA, never a floating branch, tag, or dist-tag like latest, since a theme maintainer could otherwise push new code that runs unreviewed in your repo's Actions. Omit this key to use the built-in default theme.
Full setup, configuration, and workflow documentation: https://github.com/menil/gitemployed
Each scraped job listing becomes a GitHub Issue. The AI triage engine scores every listing against your resume, tailors a fit resume for strong matches, and closes mismatches with a reasons comment.
An optional Kanban board for tracking your application lifecycle (Triage Pending → Ready to Apply → Applied → In Loop → Rejected). Requires Project V2 setup; without it, issue labels track state instead.
Configure these under Settings > Secrets and variables > Actions:
| Secret | Purpose |
|---|---|
GEMINI_API_KEY |
Gemini LLM key for triage and tailoring (Google AI Studio) — one of Gemini, OpenRouter, or Claude is required |
OPENROUTER_API_KEY |
OpenRouter LLM key — also powers automated PR reviews |
CLAUDE_CODE_OAUTH_TOKEN |
Claude provider credentials: Claude Code subscription token (claude setup-token) or standard Anthropic API key |
GH_PAT |
Optional. GitHub Personal Access Token. Enables status badges via Gist, and optionally Projects V2 Kanban board automation; falls back to GITHUB_TOKEN without it |
TAVILY_API_KEY |
Optional. Enables the Tavily search provider for the Issue Assistant's web research |
BRAVE_API_KEY |
Optional. Enables the Brave search provider for the Issue Assistant's web research |
JINA_API_KEY |
Optional. Raises page-fetch rate limits from 20 to 500 RPM for JS-heavy job boards |
GMAIL_CLIENT_ID |
Optional. Required only to enable Gmail Sync — see DEVELOPMENT.md for the one-time OAuth setup |
GMAIL_CLIENT_SECRET |
Optional. Required only to enable Gmail Sync (paired with GMAIL_CLIENT_ID) |
GMAIL_REFRESH_TOKEN |
Optional. Required only to enable Gmail Sync; long-lived, read-only (gmail.readonly) |
Configure these under the Variables tab:
| Variable | Default | Purpose |
|---|---|---|
LLM_PROVIDER |
auto-detected | gemini, openrouter, or claude (auto-detected from keys when unset) |
OPENROUTER_MODEL |
openrouter/free |
OpenRouter model (requires provider prefix, e.g., openrouter/free or google/gemini-2.5-flash). |
GEMINI_MODEL |
models/gemini-2.5-flash |
Gemini model (e.g., models/gemini-2.5-flash or gemini-2.5-flash) |
CLAUDE_MODEL |
claude-sonnet-5 |
Claude model (e.g., claude-sonnet-5 or claude-haiku-4-5-20251001) |
| Workflow | What it does |
|---|---|
scrape-jobs.yml |
Daily cron that discovers new roles from LinkedIn, Indeed, and ZipRecruiter, then opens issues |
triage-issue.yml |
AI-scores each listing against your resume and tailors a fit resume if it passes the threshold |
respond-issue.yml |
Answers questions on issue threads via live web research, and applies status labels from conversation |
status-transition.yml |
Moves your board card when you apply a lifecycle label (applied, in-loop, ...), and closes the issue for terminal labels (rejected, triage-mismatched) while keeping their columns distinct |
project-status-sync.yml |
Reverse sync: applies the matching label when you drag a card to a new column |
gmail-sync.yml |
Optional, off by default. Hourly cron that matches lifecycle emails in a label-scoped slice of your Gmail inbox to open applications and applies the same status updates as the Issue Assistant |