job-application-agent

A deep-dive technical review of Vaibhav Arora's privacy-first Agent Skill for autonomous job hunting โ€” version 3.1.1

MIT License v3.1.1 Node 20+ Agent Skill ESM SLSA Provenance
GitHub Repo npm Package Live Dashboard

1. What It Does (TL;DR)

An Agent Skill + CLI that lets a coding agent (like me ๐Ÿ—ก๏ธ) discover, evaluate, fill out, submit, and track job applications on a candidate's behalf. It packages:

The agent is told to use it via natural language ("search jobs", "apply https://...", "show attention queue") and the CLI handles all the deterministic bookkeeping. The agent itself only does browser interaction + discovery.

2. Repository Stats

142
files in repo
9
versions in 5 days
~218KB
unpacked size
27
files in npm tarball

๐Ÿ“‚ File Layout (excluding site/ and telemetry-worker/)

job-application-agent/ โ”œโ”€โ”€ bin/ โ”‚ โ””โ”€โ”€ job-application-agent.mjs โ† npm bin entry โ”œโ”€โ”€ installer/ โ”‚ โ””โ”€โ”€ src/ โ”‚ โ”œโ”€โ”€ cli.mjs โ† install/update/status commands โ”‚ โ”œโ”€โ”€ installer.mjs โ† staging, rollback, vendor copy โ”‚ โ”œโ”€โ”€ runner.mjs โ† auto-update shell script gen โ”‚ โ””โ”€โ”€ scheduler.mjs โ† launchd / systemd / Task Scheduler โ”œโ”€โ”€ job-application-agent/ โ”‚ โ”œโ”€โ”€ SKILL.md โ† agent-facing spec โ”‚ โ”œโ”€โ”€ references/ โ”‚ โ”‚ โ”œโ”€โ”€ ANALYTICS.md โ”‚ โ”‚ โ”œโ”€โ”€ APPLICATION_GUIDANCE.md โ”‚ โ”‚ โ”œโ”€โ”€ AUTONOMY.md โ”‚ โ”‚ โ”œโ”€โ”€ BROWSER_UPLOADS.md โ”‚ โ”‚ โ”œโ”€โ”€ RUNS.md โ”‚ โ”‚ โ””โ”€โ”€ SCHEMAS.md โ”‚ โ”œโ”€โ”€ scripts/ โ”‚ โ”‚ โ”œโ”€โ”€ job-application.mjs โ† main CLI (commands router) โ”‚ โ”‚ โ”œโ”€โ”€ secret-store.mjs โ† keychain/cred manager wrapper โ”‚ โ”‚ โ”œโ”€โ”€ telemetry-client.mjs โ† outbound telemetry โ”‚ โ”‚ โ”œโ”€โ”€ telemetry-schema.mjs โ† event validation โ”‚ โ”‚ โ””โ”€โ”€ windows-profile-store.ps1 โ† DPAPI wrapper for Win โ”‚ โ””โ”€โ”€ tests/ โ† 7 test files โ”œโ”€โ”€ LICENSE โ”œโ”€โ”€ package.json โ”œโ”€โ”€ README.md โ”œโ”€โ”€ SECURITY.md โ””โ”€โ”€ scripts/ โ”œโ”€โ”€ ci/ โ”œโ”€โ”€ smoke-package.mjs โ””โ”€โ”€ ...

3. Architecture & Components

System Architecture Candidate (You) natural language commands Coding Agent (e.g., OpenClaw) reads SKILL.md, calls CLI handles browser interaction Browser (Chrome/Playwright) candidates existing session filechooser for resume upload job-application.mjs CLI command router + validation Profile Storage macOS Keychain / Win Cred Mgr Local Ledgers (NDJSON) applications, outcomes, rounds Resume PDF imported, canonical, 0600 perms Autonomy Grant routine-auto permission
Diagram 1: Component relationships โ€” agent drives the CLI which manages local state

Key components

FileLinesPurpose
job-application.mjs~1100Single mega-CLI with command router for profile/resume/score/ledger/autonomy/round/attention/friction/telemetry
secret-store.mjs~130OS keychain abstraction โ€” macOS uses security CLI, Win uses PowerShell + DPAPI, Linux throws
telemetry-client.mjs~150Outbound telemetry client with grace period, install ID, HMAC envelope
telemetry-schema.mjs~160Event schemas with enum validation, identity-stripping checks
installer/installer.mjs~180Skill staging, atomic replace, rollback to previous/, vendor sync
installer/scheduler.mjs~110launchd plist / systemd timer / Win Task Scheduler for hourly auto-updates

4. Workflow: How a Job Gets Applied To

Lifecycle: From Discovery to Outcome 1. Discover agent searches career pages, ATS, X, YC 2. scoreJob() deterministic scoring + gates decision? exclude / ask / skip / review EXCLUDE closed, ineligible, excluded ASK need candidate judgment SKIP low fit, salary below floor REVIEW candidate ready to apply 3. ledger check canonical URL / employerJobID 4. Fill application verified facts only, no demographics 5. Submit + ledger add only on visible confirmation 6. Record outcome (days later) Attention Queue MFA, CAPTCHA, legal, demographic agent pauses, returns later to resolve
Diagram 2: Full job-application lifecycle from discovery to outcome tracking

Round-based execution model

For batches, the agent starts a "round" with a target count (e.g. {requestedCount: 30}). Each roundId gets attached to ledger entries, and round complete rejects if you didn't hit the target. This makes resumable workflows across agent sessions and crash recoveries possible.

5. Scoring Engine Deep Dive

The core scoreJob(input, target) function is a deterministic gate-decision engine. It's invoked via node scripts/job-application.mjs score --stdin.

Decision gates (in order)

  1. Company exclusion โ€” reject if in profile.excludedCompanies
  2. Eligibility โ€” must be eligible (not unclear/ineligible)
  3. Posting status โ€” must be active
  4. Location/work-mode โ€” matches profile.targetLocations and workModes
  5. Seniority โ€” matches profile target list
  6. Must-have coverage โ€” at least minMustHaveCoverage% of requirements met or partial (weighted: met=1, partial=0.5)
  7. Compensation floor โ€” if stated, must meet profile minimum

Score components (after gates pass)

ComponentMax PointsCondition
Role family match+25job.roleFamily โˆˆ profile target
Seniority match+15always added if past gate
Must-have coverage+40round(coverage% ร— 0.4)
Location/remote match+10locations or remote: true
Industry match+5keyword overlap in title/description
Compensation pass+5meets floor

The engine is purely string matching + lookup tables. No embeddings, no LLM. Decisions are reproducible.

Auto-submit guard

autoEligible requires ALL of: decision = review, seniority = senior/staff, no experience mismatch, score โ‰ฅ autoSubmitMinScore (default 80), must-have coverage โ‰ฅ minMustHaveCoverage (default 70%), AND all gates passed.

6. Local Storage & Privacy Boundaries

Where Your Data Lives OS Keychain macOS: security CLI Win: Credential Manager + DPAPI State Directory ~/.local/share/job-application-agent/ (all files chmod 0o600) Skill Directory ~/.agents/skills/job-application-agent/ replaceable on update profile (keychain) โœ“ name โœ“ email โœ“ phone โœ“ workAuthorization โœ“ targetCompensation โœ“ roleFamilies, seniority โœ— passwords (never stored) State files (NDJSON) โ€ข resume.pdf + resume.json โ€ข applications.ndjson โ€ข outcomes.ndjson โ€ข rounds.ndjson โ€ข attention.ndjson โ€ข friction.ndjson โ€ข reviews.ndjson Skill dir contents SKILL.md (agent reads) scripts/job-application.mjs scripts/secret-store.mjs scripts/telemetry-client.mjs references/*.md (6 files) tests/*.test.mjs (7 files) telemetry.json (config only) โš ๏ธ Linux Caveat createSecretStore() throws LINUX_PROFILE_ERROR on Linux โ€” "Secure profile storage requires macOS or Windows." (scheduler still installs via systemd though)
Diagram 3: Storage layout โ€” what's encrypted, what's append-only, what's replaceable
๐Ÿ”‘ Secret store implementation details
  • macOS: security find-generic-password -s com.vaibhavarora.job-application-agent -a profile -w โ€” also tries the legacy com.openai.codex.job-application-agent service on read
  • Windows: PowerShell + DPAPI. A 32-byte AES key is stored in Credential Manager, then used to AES-encrypt the profile JSON which is additionally DPAPI-protected
  • Linux: throws. Hard. No workaround in code.

7. Telemetry Pipeline

Telemetry Flow Your machine telemetry-client.mjs validates against schema, strips identity, hashes URL Cloudflare Worker /v1/install โ†’ token mint /v1/events โ†’ verify HMAC, rate-limit, forward PostHog US Cloud $process_person_profile: false $geoip_disable: true raw events (24mo) Cloudflare D1 daily counters HMAC-derived install hashes no PostHog credentials Public Dashboard stats.jobappagent.com aggregates only segments <3 โ†’ "other"
Diagram 4: Telemetry data flow โ€” your machine โ†’ CF Worker โ†’ PostHog + D1

What's actually sent

13 documented event types, all with strict enum validation, max 4KB payload. Examples:

What's never sent

Identity-stripping checks in telemetry-schema.mjs: Name, email, phone, exact address, profile URLs, candidate location, work authorization, personal compensation, target profile or thresholds, resume or attachments, must-have evidence, rejection reasons, prompts, responses, job descriptions, form questions, drafted answers, notes, passwords, MFA, CAPTCHA, legal/demographic answers, browser data, IP address, request headers, user agent, raw error messages.

The containsDirectIdentity() regex rejects any string that looks like an email, phone number, URL, LinkedIn profile, or GitHub profile.

โš ๏ธ Telemetry is ON by default. Disabled via node scripts/job-application.mjs telemetry disable. There's a one-command grace period for new installs.

8. Installer & Auto-Updates

The npx job-application-agent@latest install command does a lot more than a typical npm package:

  1. Validates the packaged skill โ€” checks for SKILL.md + the CLI script
  2. Stages to a random temp dir in ~/.agents/job-application-agent/staging-*
  3. Moves current to previous/ for rollback
  4. Atomic rename of staging โ†’ ~/.agents/skills/job-application-agent/
  5. Vendor sync: if any of ~/.codex/skills/, ~/.claude/skills/, ~/.cursor/skills/, ~/.copilot/skills/, ~/.gemini/skills/ exist, copies the skill there too
  6. Installs an auto-update scheduler:
    • macOS: LaunchAgent plist with RunAtLoad=true + StartInterval=3600 (hourly)
    • Linux: systemd user timer with OnBootSec=2m + OnUnitActiveSec=1h
    • Windows: Scheduled Task with logon trigger + hourly repetition
Default behavior: auto-updates enabled. The update script runs npm exec --yes --package=job-application-agent@latest -- job-application-agent auto-update which downloads the latest version and replaces your install. Disable with npx job-application-agent@latest updates disable.

9. Safety Guardrails

The package is unusually explicit about what it won't do. From SKILL.md and code:

Hard stops (never bypasses)

๐Ÿ” Authentication

Passwords, SSO, MFA, CAPTCHA

๐Ÿ“œ Legal attestations

Government IDs, e-verify

๐Ÿ‘ฅ Demographics

EEOC self-ID, voluntary disclosures

โ“ Ambiguous claims

Authorization, compensation, unverifiable facts

Browser session rules

Submission rules

10. Final Verdict

โœ… What it does well

  • Architecture is thoughtful. Clear separation: agent handles browser, CLI handles state. Deterministic scoring means reproducible decisions.
  • Privacy claims are backed by code. Identity-stripping regex, enum-only schemas, 4KB payload cap, schema validation on both client and server, PostHog person-profile off, GeoIP disabled.
  • Credential handling is real. macOS Keychain / Win DPAPI / Linux errors explicitly. No plaintext secrets to disk.
  • Append-only ledgers with idempotency, duplicate detection, and round accounting โ€” proper audit trail.
  • Auto-update is opt-out (and reversible), with staging + rollback.
  • Honest about scope. Won't bypass auth, won't fabricate, hard stops are baked in.

โš ๏ธ Caveats

  • 9 versions in 5 days. Aggressive iteration โ€” could be instability, could be active development. Pin a version if you care.
  • Linux is unsupported for the profile store. The CLI throws. Scheduler installs fine, but no secure secret storage.
  • Telemetry on by default. Anonymous, but you need to run telemetry disable if you don't want it. There's no out-of-the-box opt-out at install.
  • PostHog retention caveat: the README admits the free PostHog plan doesn't expose raw-event TTL, so the documented 24-month retention depends on the owner getting plan-level access or arranging deletion with PostHog.
  • Single-author project. Vaibhav Arora is doing this solo โ€” bus factor of 1. No CODEOWNERS community, 1 contributor.
  • The browser-uploads flow still requires the agent to do the actual form-filling via Playwright/Chrome DevTools. The CLI just bookkeeping.

๐ŸŽฏ Use it if...

  • You're on macOS or Windows (Linux users: profile store throws)
  • You want a privacy-respecting, deterministic agent skill rather than a black-box "AI applies for jobs" SaaS
  • You're comfortable with Node, browser automation, and append-only local state
  • You don't mind a fresh project with rapid version bumps

๐Ÿ›‘ Don't use it if...

  • You want a hosted SaaS โ€” this is purely local + Cloudflare Worker
  • You need multi-user / team features
  • You're on Linux (wait for an OS keyring impl)
  • You want a battle-tested project โ€” pin v3.1.1 and watch for issues

Recommendation: โœ… Worth trying on macOS, pin v3.1.1, disable telemetry first