Use this skill when the user asks about job hunting, job applications, scraping job boards, tracking application stages, or filling out job application forms. Triggers include mentions of LinkedIn, Gupy, RemoteOK, Job na Gringa, application tracking, cover letter drafting tied to a tracked job, or any request to apply to a job listing URL. The skill discovers senior mobile / Android / Kotlin Multiplatform openings across BR and international remote markets, tracks each through stages (discove...
Scanned 5/27/2026
Install via CLI
openskills install lomartins/job-hunter-skill---
name: job-hunter
description: "Use this skill when the user asks about job hunting, job applications, scraping job boards, tracking application stages, or filling out job application forms. Triggers include mentions of LinkedIn, Gupy, RemoteOK, Job na Gringa, application tracking, cover letter drafting tied to a tracked job, or any request to apply to a job listing URL. The skill discovers senior mobile / Android / Kotlin Multiplatform openings across BR and international remote markets, tracks each through stages (discovered, queued, applying, applied, screening, technical, behavioral, offer, rejected, withdrawn), and assists with form filling in two modes (shadow and auto) while keeping PII out of model context. Do NOT use for general career advice, resume writing from scratch, or interview coaching unrelated to a tracked job."
license: Apache-2.0
version: 0.13.1
---
# job-hunter
Discover, track, and assist with senior mobile / Android / Kotlin Multiplatform job applications across LinkedIn, Gupy, RemoteOK, Job na Gringa, and seven more sources. Tracks every opportunity from `discovered` through `offer`/`rejected` in a local SQLite DB mirrored to human-readable Markdown. Fills application forms in two modes — `shadow` (default; pauses before submit for your review) and `auto` (gated by adapter reliability score and explicit consent flags) — using a YAML adapter system that learns from unknown forms and improves over time.
**When this skill is the right tool:** the user mentions a specific job board or posting URL; asks to track an application; wants to fill a form on a known ATS (Gupy, Greenhouse, Lever, Workday, Ashby); asks for a weekly application report; asks to add a new source or fix a broken adapter. **When it is not:** generic career coaching, resume writing from a blank page, mock interviews not tied to a tracked job.
## Forbidden actions (PII isolation)
This skill keeps personally identifiable information (CPF, RG, phone, full address, birth date, salary expectations, bank info, LinkedIn session cookie) entirely out of the model's context window. Violating this voids the safety guarantee. Treat the secrets file as if reading it would corrupt the database.
**Never run any of these commands against `$XDG_CONFIG_HOME/job-hunter/secrets/personal.env`** (default `~/.config/job-hunter/secrets/personal.env`):
```
cat head tail less more bat view
grep rg ag awk sed
printenv env source
strings od xxd hexdump
cp <secrets> <anywhere-the-model-might-read>
```
Also never:
- Read the file via the `Read` tool.
- Pipe it through any command whose output enters the conversation.
- Suggest the user paste its contents into chat for debugging.
- Decode base64 / hex / etc. that you suspect contains secrets.
**What you CAN do:** read `assets/personal.env.example` (empty-value template — that's the schema you write against), generate scripts that call `os.environ` after `python-dotenv` has loaded the file at runtime in a child process, and run `job-hunter lint` which reports presence/absence by key name without ever printing values.
If a user pastes a real CPF or other PII into chat, stop, warn them, and instruct them to move it into `personal.env`.
## Slash commands (when installed as a Claude Code plugin)
```
/job-hunter:discover [source] # pull new jobs (default: remoteok)
/job-hunter:list [filters] # show the pipeline as a Rich table
/job-hunter:track <url> # track a specific posting + queue it
/job-hunter:apply <id> # preview the form-fill plan (dry-run)
/job-hunter:status # pipeline summary + suggested next steps
/job-hunter:review # paused adapters + inbox drafts
/job-hunter:sync # regenerate tracking.md
/job-hunter:doctor # validate install / perms / deps
/job-hunter:web # launch local triage webapp (FastAPI + HTMX)
/job-hunter:dig <id> # re-fetch JD + brief: fit / friction / angles / next move
/job-hunter:tailor-resume <id> # Reactive Resume JSON tailored to a JD
```
These are thin wrappers around the CLI — Claude executes the relevant `job-hunter` command via `Bash` and formats the result.
## CLI surface
The package installs two console scripts: `job-hunter` (canonical) and `job` (alias). All commands below work with either name.
```
job init # idempotent setup, creates XDG dirs, copies templates
job discover [--source SOURCE] [--query QUERY] [--watch]
job list [--stage STAGE] [--source SOURCE] [--since DATE]
job show <id>
job queue <id>
job apply <id> [--mode shadow|auto] [--dry-run] [--no-wait] [--i-understand]
job apply --batch --stage queued [--mode ...]
job approve <fill_attempt_id> [--letter]
job review
job stage <id> --to <stage> [--note "..."]
job adapter list
job adapter promote <signature>
job adapter test <signature> --url URL
job adapter mark-auto-eligible <signature>
job adapter contribute <signature> # PRs adapter back upstream via gh
job sync [--no-md-sync]
job report [--weekly]
job lint # runs lint_secret_leaks against runtime dirs
job doctor # validates install, paths, perms
job web [--host 127.0.0.1] [--port 8765] [--no-open] # local webapp for triage
```
Defaults:
- `--mode shadow` if omitted.
- `--watch` interval: 6 hours.
- `--no-md-sync` defers tracking.md regeneration for batch operations.
- `--i-understand` is required once per session for auto mode.
## Runtime layout
The repo is read-only after install. All mutable state lives in XDG paths in the user's home.
```
$XDG_CONFIG_HOME/job-hunter/ # default ~/.config/job-hunter/
├── config.yaml # XDG path overrides, defaults
├── profile.yaml # non-PII profile (roles, locations, links)
├── field_labels.yaml # user overrides for the bundled label dictionary
└── secrets/
├── personal.env # chmod 600, NEVER READ BY THIS SKILL
└── README.md
$XDG_DATA_HOME/job-hunter/ # default ~/.local/share/job-hunter/
├── jobs.db # SQLite, source of truth
├── tracking.md # human-readable mirror
├── tracking/ # per-job markdown files
├── adapters_inbox/ # newly-learned adapters awaiting review
├── adapters_user/ # user-customized adapters (override bundled by platform_signature)
├── files/ # resumes, cover letter base, attachments
└── runs/<iso-timestamp>/ # per-run artifacts (report.json, screenshots, HAR)
$XDG_STATE_HOME/job-hunter/ # default ~/.local/state/job-hunter/
└── logs/ # rotating logs + ratelimit.json token buckets
```
For tests: `JOB_HUNTER_HOME_OVERRIDE=/tmp/job-hunter-test` redirects all three XDG roots under one tmp dir. Documented for test fixtures only — never use in prod.
## File map under `scripts/job_hunter/`
Navigate without reading everything. One line per file.
- `cli.py` — Typer + Rich CLI entrypoint. Subcommands wire to functions in the other modules.
- `paths.py` — XDG path resolution; honors `JOB_HUNTER_HOME_OVERRIDE` for tests.
- `models.py` — SQLModel definitions: `Job`, `Application`, `StageHistory`, `SiteAdapter`, `FillAttempt`, `CoverLetterApproval`.
- `db.py` — Engine + session factory. Runs migrations from `../migrations/` on `job init`.
- `tracking_md.py` — Deterministic markdown sync (`tracking.md` + per-job files). Atomic write-temp-rename. Preserves `<!-- notes:start -->...<!-- notes:end -->` blocks.
- `sources/` — One module per data source. All implement the `Source` protocol (`discover()`, `fetch_detail()`).
- `adapters/` — Adapter resolver, YAML schema, source dispatcher (`profile.*`, `secret.*`, `file.*`, `generate.*`).
- `apply.py` — Playwright runner. Shadow blocks for `y/N/edit`; auto runs cooldown + submits if all 5 gates hold.
- `learn.py` — Inspects unknown forms, hashes platform signature, drafts adapter into `adapters_inbox/`.
- `webapp/` — Local FastAPI + HTMX triage UI. `app.py` (factory), `routes.py`, `scoring.py`, `i18n.py`, `templates/`, `static/`, `i18n_data/` (en + pt_BR). Launched via `job-hunter web`. Localhost-only by default.
At `scripts/` root (sibling of the package):
- `lint_secret_leaks.py` — Scans runtime dirs for CPF/CNPJ/RG/phone regex matches. Exits non-zero on hit.
- `healthcheck.py` — Used by `job doctor`. Validates pyproject install, Playwright browser, XDG perms, gh auth (if `adapter contribute` is to work).
- `install_hook.sh` — Idempotent bootstrap: creates XDG dirs, copies templates from `assets/` if absent, prompts user to `chmod 600` the secrets file.
DB migrations live at `scripts/migrations/NNN_description.sql` and are applied in lexical order.
## Loading references on demand
Read these only when working on that specific area. They are NOT part of the always-loaded SKILL.md context.
| File | Read when |
|------|-----------|
| `references/sources/linkedin.md` | Implementing or debugging LinkedIn scraping; cookie expired |
| `references/sources/gupy.md` | Adding a Gupy company subdomain; fixing parser |
| `references/sources/job_na_gringa.md` | Maintaining the Job na Gringa scraper |
| `references/sources/remoteok.md` | RemoteOK JSON shape changes |
| `references/sources/remotive.md`, `wwr.md`, `himalayas.md`, `programathor.md`, `coodesh.md`, `trampos.md`, `arcdev.md` | Implementing source 5–11 |
| `references/adapters/gupy.md`, `greenhouse.md`, `lever.md`, `workday.md`, `ashby.md` | Editing an adapter or learning its quirks |
| `references/apply_modes.md` | Changing shadow/auto behavior; tuning auto gates |
| `references/markdown_sync.md` | Touching `tracking_md.py`; determinism failures |
| `references/self_improvement.md` | Working on `learn.py`; debugging platform signatures |
| `references/decisions.md` | Understanding why something diverges from the spec |
| `references/troubleshooting.md` | Install, perms, Playwright, gh auth, cookie issues |
## Failure modes and recovery
| Symptom | Likely cause | Recovery |
|---------|--------------|---------|
| `job doctor` reports "secrets file world-readable" | Forgot `chmod 600 ~/.config/job-hunter/secrets/personal.env` | `chmod 600` the file; doctor exits clean |
| LinkedIn discover returns 0 results unexpectedly | Cookie expired or LinkedIn flagged the session | Refresh `LINKEDIN_LI_AT` from browser devtools; `references/sources/linkedin.md` has step-by-step |
| `apply` reports `aborted_for_review` immediately | No adapter matches the URL signature; `learn.py` drafted a new one | Edit the draft in `adapters_inbox/`, run `job adapter test <sig> --url ...`, then `job adapter promote <sig>` |
| Auto mode refuses to submit | One of 5 gates failed (success rate <90%, missing `--i-understand`, `generate.*` without approval, etc.) | `job apply <id>` (without `--mode auto`) submits via shadow; or fix the gate per error message |
| `tracking.md` shows churn on every regen | Non-deterministic input (e.g. unstable ordering) | File a bug; the determinism test should have caught this — see `references/markdown_sync.md` |
| `job lint` flags a CPF match in `runs/` | A scraper or apply run leaked PII into an artifact | Open the file, remove the value, fix the leak source (likely a log statement); `lint` exits non-zero until clean |
| Playwright launch fails on Wayland | `playwright install chromium` not run, or Chromium can't find a display in headed mode | `job doctor` reports both; `playwright install chromium` and/or set `BROWSER_WS_ENDPOINT` to a Browserless container |
| `apply` hangs in `claude -p` | Shadow mode needs a TTY; headless detected, auto-aborts | Run from an interactive shell, or pre-approve and use `--mode auto` |
For anything else, attach the relevant `runs/<iso-timestamp>/report.json` to a GitHub issue.
No comments yet. Be the first to comment!