Skip to main content
Glama

Job Agent

AI-powered job search and application assistant: multi-source hunting, OpenAI matching, cover letters, and hybrid apply (Playwright ATS → Chrome CDP → optional screen OCR → manual assist).

Safety first: read DISCLAIMER.md. Keep require_submit_confirmation: true and prefer --dry-run until you trust the flow.

Architecture

┌─────────────┐   ┌──────────────┐   ┌─────────────────────────────┐
│ Job sources │ → │ OpenAI match │ → │ today.json + daily_report   │
│ LinkedIn    │   │ gpt-4o-mini  │   │ + optional Canvas sidecars  │
│ JobsDB      │   └──────────────┘   └──────────────┬──────────────┘
│ Adzuna …    │                                     │ approve
└─────────────┘                                     ▼
                                         ┌─────────────────────┐
                                         │ ApplyRouter         │
                                         │  Playwright ATS     │
                                         │  LinkedIn CDP       │
                                         │  Screen OCR (macOS) │
                                         │  Manual assist pack │
                                         └─────────────────────┘

Requirements

  • Python 3.11+

  • OpenAI API key (matching + cover letters)

  • Optional: Adzuna App ID/Key

  • Chrome (LinkedIn Easy Apply via CDP)

  • macOS (Screen OCR fallback; Accessibility + Screen Recording permissions)

Setup

git clone https://github.com/<you>/job-agent.git ~/job-agent
cd ~/job-agent
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
playwright install chromium
cp .env.example .env          # OPENAI_API_KEY, optional ADZUNA_*
cp profile/profile.example.json profile/profile.json
cp profile/answers.example.json profile/answers.json
python -m src.cli onboard     # or edit profile JSON directly

Edit config.yaml for search sources, match threshold, Chrome CDP URL, and paths. Optional Canvas sync: set canvas_dir (or env CURSOR_CANVAS_DIR) to your Cursor canvases folder.

Four ways to use Job Agent

1. CLI

python -m src.cli launch              # tune (if needed) + hunt
python -m src.cli list                # markdown report
./scripts/approve_and_apply.sh <id>   # approve + apply + cover letter
python -m src.cli apply <id> --dry-run
./scripts/start_chrome_debug.sh       # LinkedIn Easy Apply via CDP
python -m src.cli cdp-status

2. Web Dashboard

python -m src.cli dashboard
# or macOS Desktop shortcut:
./scripts/install_desktop_shortcut.sh

Open http://127.0.0.1:8787 — run hunts, batch-approve, paste ATS URLs, manage applied history.

3. Cursor Agent + MCP

  1. Open this folder as the Cursor workspace

  2. Create the venv and install deps (MCP uses .venv/bin/python — see .cursor/mcp.json)

  3. Run ./scripts/verify_mcp.sh

  4. Use the job-hunt skill (.cursor/skills/job-hunt/SKILL.md)

MCP server

Role

job-search

Hunt, match, list jobs

playwright-agent

Browser automation

screen-agent

macOS screen OCR fallback

Example chat prompts:

  • "Run today's job hunt and show top 3 matches"

  • "Approve job <id> with cover letter, dry-run only"

  • "Tune my profile — ask about missing salary and notice period"

Optional Canvas UI samples live in canvases/. Sync sidecars with python scripts/sync_canvas.py after setting canvas_dir.

4. Cursor Automation

Import automation/daily-job-hunt.yaml:

  1. Open Automations in Cursor

  2. Import the YAML (cron: weekdays 08:00)

  3. Point gitConfig.repo at your clone (~/job-agent)

  4. Ensure .env is available to the agent runtime

The automation runs ./scripts/daily_hunt.sh only — no automatic submit. Review matches in the dashboard.

Learning loop (review before it changes your profile)

Nothing is written to your profile automatically. After each apply the agent files proposals into a review inbox; you approve or reject them in the dashboard's Learning tab (or via CLI). Password-like fields are never proposed.

Proposal

Approving it does

Source

question_answer

Merges the (editable) answer into profile/answers.jsoncustom_answers, so the next apply auto-fills it

form_answers.json of a successful apply

fixture

Copies page.html into tests/fixtures/ats/ + writes a pytest stub

a failed apply that left a snapshot

retry_policy

Records a channel-specific retry hint

repeated failures of one failure_type

hunt_signal

Feeds a boost/skip signal into scoring

CRM outcome or the "bad match" button

python -m src.cli learn list              # pending proposals (JSON)
python -m src.cli learn approve <id>      # apply it
python -m src.cli learn reject <id>       # drop it

CRM statuses close the loop on their own: interview / offer boost similar companies and titles in future scoring, while rejected / withdrawn down-weight them.

Reliability & ops CLI

python -m src.cli apply-report --days 7    # success rates, failure types, timings
python -m src.cli crm list
python -m src.cli source-eval              # which search sources actually convert
python -m src.cli export-applications --output ~/Desktop/applications.csv
python -m src.cli profile list             # multi-profile switching

Failures are bucketed into login_gate, empty_required, resume_upload, captcha, timeout, and unsupported so apply-report can tell you what to fix next. ATS account walls (Workday and friends) are reported as login_gate with the credential key to add — not as "unsupported ATS".

Config highlights

Key

Purpose

match_threshold

Minimum OpenAI match score

search_sources

e.g. linkedin, jobsdb, adzuna, remotive

linkedin_mode

hybrid / playwright / screen / manual

require_submit_confirmation

Skip final Submit until confirmed (default true)

chrome_cdp_url

Debug Chrome endpoint (default http://127.0.0.1:9222)

canvas_dir

Optional Cursor Canvas sidecar directory

applications_dir

Where cover letters / apply artefacts are written

Tests

pytest

License

MIT — see LICENSE.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/PARK0301/job-agent'

If you have feedback or need assistance with the MCP directory API, please join our Discord server