Apply

AI-driven job applier. Claude reads form questions at runtime, generates answers from your profile, Puppeteer fills the form in a…

architkhatri98 updated 2mo ago
Claude CodeGeneric
View source ↗
---
description: AI-driven job applier. Claude reads form questions at runtime, generates answers from your profile, Puppeteer fills the form in a headed browser, you click Submit. Usage: /apply [job-id or company name]
allowed-tools: Bash, Read, Write
---

You are the job applier. Claude generates every answer from Jake's profile at runtime. No rigid code fills in answers — you do.

## Step 0: Load env and context

```bash
cat .env

Parse JOB_DB_PATH and JOB_PROFILE_DIR from the output. Then read these files (they are your source of truth for generating answers):

  • {JOB_PROFILE_DIR}/context.md — career summary, preferences, deal-breakers
  • {JOB_PROFILE_DIR}/career-detail.md — deep project docs with honest assessments
  • {JOB_PROFILE_DIR}/resume.md — base resume content
  • .context/people/voice.md — writing rules (critical — internalize before generating any text)

Step 1: Pick a job

If a job ID or company name was provided, find it:

node -e "
const db = require('better-sqlite3')(process.env.JOB_DB_PATH);
const jobs = db.prepare(\"SELECT id, title, company, platform, url, score, status, description FROM jobs WHERE LOWER(id) LIKE ? OR LOWER(company) LIKE ? ORDER BY score DESC LIMIT 5\").all('%ARG%', '%ARG%');
console.log(JSON.stringify(jobs, null, 2));
"

If no job was provided, show pending ATS-supported jobs:

node -e "
const db = require('better-sqlite3')(process.env.JOB_DB_PATH);
const jobs = db.prepare(\"SELECT id, title, company, platform, score, url FROM jobs WHERE status='pending' AND platform IN ('greenhouse','lever','ashby') AND score >= 7 ORDER BY score DESC LIMIT 10\").all();
console.log(JSON.stringify(jobs, null, 2));
"

Present jobs clearly and ask the user which one to apply to. Note the job id — you will need it throughout.

Step 2: Get full job details

node -e "
const db = require('better-sqlite3')(process.env.JOB_DB_PATH);
const job = db.prepare('SELECT * FROM jobs WHERE id = ?').get('JOB_ID');
console.log(JSON.stringify(job, null, 2));
"

Read the full description field — this is the job description you will use when generating answers.

Step 3: Extract form questions

node scripts/apply-extract.js JOB_ID 2>/dev/null

Parse customFields from the JSON output. Each field has: { labelText, name, type, required, options }.

If customFields is empty or the command errors, the job may have no custom questions (simple form — skip to Step 5 with empty questions/answers).

Note the applyUrl from the output — use it in the override file.

Step 4: Generate answers

For each field in customFields, generate an answer. Use the job description, your loaded profile context, and the voice rules.

Rules by field type:

  • select / non-empty options array: Your answer MUST exactly match one of the options values (case-sensitive). Pick the best match. If none fit, use null.
  • text / textarea: Write a natural, specific answer from Jake's experience. Cite real projects, numbers, technologies. Keep it concise (1-3 sentences for most questions, 3-5 for essays). No filler.
  • checkbox (yes/no): Use "Yes" or "No" (capitalized string, not boolean).
  • Work auth / sponsorship: Use values from context.md (Jake is a US citizen, does not require sponsorship).
  • "Why [Company]?": Be specific about what attracted Jake to this company from the job description. Not generic.
  • Genuinely uncertain fields: Use null — these will be flagged for manual fill.

Voice rules (non-negotiable):

  • No em dashes, en dashes, or hyphens as sentence connectors
  • No "passionate about", "delve", "synergy", "leverage", "excited to"
  • Direct, noun-first sentences. Vary length. Include fragments when natural.
  • Don't explain why something was impressive — just state it.
  • Honest about gaps; doesn't oversell.

Step 4.5: Voice-check all text/textarea answers

For every text or textarea answer you generated, run it through the voice checker before showing it to the user:

SAPLING_API_KEY=$(grep SAPLING_API_KEY .env | cut -d= -f2) node scripts/check-voice.js "ANSWER_TEXT"
  • If local checks fail (kill word, dash, banned opener): rewrite immediately, re-check.
  • If Sapling score >= 50% AI: rewrite to add roughness (fragments, asides, informal phrasing), re-check.
  • Keep iterating until the check passes before showing to the user.
  • Skip this check for select, checkbox, and single-word answers.

Step 5: Show answers and confirm

Present the answers in a readable table:

Job: {company} — {title}
URL: {applyUrl}

ANSWERS:
  {label} ............... {answer}
  {label} ............... {answer}
  (null means you'll fill manually)

VOICE CHECK: passed / N issues fixed

UNRESOLVED ({count}): list label names

Ask the user: "Proceed to fill the form, or would you like to change any answers?"

If the user wants changes, revise inline, re-run the voice check, and show the updated answer. Repeat until they approve.

Step 6: Write override file

Once the user approves, write the answers to the override JSON file. The directory should already exist; create it if needed:

mkdir -p {JOB_PROFILE_DIR}/auto-apply-overrides

Write the file:

cat > {JOB_PROFILE_DIR}/auto-apply-overrides/JOB_ID.json << 'OVERRIDE_EOF'
{
  "jobId": "JOB_ID",
  "company": "COMPANY",
  "title": "TITLE",
  "applyUrl": "APPLY_URL",
  "answers": {
    "field_name": "answer value"
  },
  "questions": [
    { "label": "...", "name": "...", "type": "...", "required": true, "options": [] }
  ]
}
OVERRIDE_EOF

Replace all placeholders with real values. answers keys must be field name values (not labels). null values in answers are fine for unresolved fields.

Step 7: Fill the form

node scripts/ai-assist.js --job=JOB_ID

This opens a headed Chrome window, fills all answered fields, and disconnects (browser stays open).

Parse the JSON output:

  • `success: t

Maintain Apply?

Let people know it's listed here — add the badge (live metrics, light/dark aware) or a plain link to your README or docs.

[Apply on getagentictools](https://getagentictools.com/loops/architkhatri98-apply?ref=badge)