Apply
AI-driven job applier. Claude reads form questions at runtime, generates answers from your profile, Puppeteer fills the form in a…
---
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-emptyoptionsarray: Your answer MUST exactly match one of theoptionsvalues (case-sensitive). Pick the best match. If none fit, usenull.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)