Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9a76346
feat: add OpenCode backend support with multi-backend adapter
rothnic Feb 27, 2026
29b9874
style: fix linting errors in tests
rothnic Feb 27, 2026
4770dde
chore: clean .env.example and add OpenCode test script
rothnic Feb 27, 2026
1cc9402
fix: add resume/profile checks to test script
rothnic Feb 27, 2026
cc14e8b
feat: add --backend option to apply command
rothnic Feb 27, 2026
5b3efba
fix: handle NULL apply_status in job acquisition query
rothnic Feb 27, 2026
f7693ec
fix: run OpenCode from ~/.applypilot directory to find MCP config
rothnic Feb 27, 2026
a850fd0
fix: check default opencode installation location in _find_binary
rothnic Feb 27, 2026
2632ad4
fix: check default opencode installation location in _find_binary
rothnic Feb 27, 2026
66ecaab
fix: correct indentation and remove duplicate method in backends.py
rothnic Feb 27, 2026
7656f76
fix(opencode): pass prompt as positional arg and add OPENCODE_CONFIG_…
rothnic Feb 27, 2026
a892221
fix(opencode): don't write prompt to stdin since it's a positional arg
rothnic Feb 27, 2026
e38ab75
fix(opencode): remove desktop env vars that break CLI mode
rothnic Feb 27, 2026
034a940
feat(prompt): add debug logging and better apply button guidance
rothnic Feb 27, 2026
839f365
feat(prompt): use programmatic element discovery instead of visual sc…
rothnic Feb 27, 2026
332cbe9
feat(prompt): improve file upload instructions with explicit workflow
rothnic Feb 28, 2026
cc86621
feat(prompt): add explicit MCP tool usage examples
rothnic Feb 28, 2026
978ff39
fix(prompt): simplify MCP tool examples, fix scroll guidance
rothnic Feb 28, 2026
73a0238
feat(opencode): Add comprehensive OpenCode support with instrumentati…
rothnic Mar 1, 2026
63146a5
Merge remote changes and resolve conflicts
rothnic Mar 1, 2026
03ed88e
chore: remove debugging test script from PR
rothnic Mar 1, 2026
9716fd8
docs: remove backend preference language, treat both equally
rothnic Mar 1, 2026
1ee7c00
docs: fix remaining backend preference in init wizard
rothnic Mar 1, 2026
f56e45d
docs: add OpenCode configuration details and XDG isolation explanation
rothnic Mar 1, 2026
8e6bb6a
docs: show complete opencode.jsonc with all playwright and gmail tools
rothnic Mar 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,21 @@ GEMINI_API_KEY= # Gemini 2.0 Flash (recommended, cheapest)
# LLM_URL=http://127.0.0.1:8080/v1 # Local LLM (llama.cpp, Ollama)
# LLM_MODEL= # Override model name

# Backend selection for auto-apply (APPLY_BACKEND)
# Options: claude (default), opencode
# To use OpenCode: set APPLY_BACKEND=opencode and register MCPs
# APPLY_BACKEND=opencode

# Backend-specific defaults for apply command (optional)
# APPLY_CLAUDE_MODEL= # Claude backend default model (default: haiku)
# APPLY_OPENCODE_MODEL= # OpenCode backend default model (fallback: LLM_MODEL or gpt-4o-mini)
# APPLY_OPENCODE_AGENT= # OpenCode --agent value (optional)

# OpenCode MCP baseline:
# Ensure these MCP servers exist in `opencode mcp list` before applying:
# - playwright
# - gmail

# Auto-Apply (optional)
CAPSOLVER_API_KEY= # For CAPTCHA solving during auto-apply

Expand Down
185 changes: 179 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ applypilot apply --dry-run # fill forms without submitting
## Two Paths

### Full Pipeline (recommended)
**Requires:** Python 3.11+, Node.js (for npx), Gemini API key (free), Claude Code CLI, Chrome
**Requires:** Python 3.11+, Node.js (for npx), Gemini API key (free), Claude Code CLI or OpenCode CLI, Chrome

Runs all 6 stages, from job discovery to autonomous application submission. This is the full power of ApplyPilot.

Expand All @@ -63,7 +63,7 @@ Runs stages 1-5: discovers jobs, scores them, tailors your resume, generates cov
| **3. Score** | AI rates every job 1-10 based on your resume and preferences. Only high-fit jobs proceed |
| **4. Tailor** | AI rewrites your resume per job: reorganizes, emphasizes relevant experience, adds keywords. Never fabricates |
| **5. Cover Letter** | AI generates a targeted cover letter per job |
| **6. Auto-Apply** | Claude Code navigates application forms, fills fields, uploads documents, answers questions, and submits |
| **6. Auto-Apply** | Orchestrates browser-driven submission using an external backend (Claude or OpenCode). The backend launches a browser, detects the form type, fills personal information and work history, uploads the tailored resume and cover letter, answers screening questions with AI, and submits. |

Each stage is independent. Run them all or pick what you need.

Expand All @@ -90,7 +90,7 @@ Each stage is independent. Run them all or pick what you need.
| Node.js 18+ | Auto-apply | Needed for `npx` to run Playwright MCP server |
| Gemini API key | Scoring, tailoring, cover letters | Free tier (15 RPM / 1M tokens/day) is enough |
| Chrome/Chromium | Auto-apply | Auto-detected on most systems |
| Claude Code CLI | Auto-apply | Install from [claude.ai/code](https://claude.ai/code) |
| Claude Code CLI or OpenCode CLI | Auto-apply | Claude: install from https://claude.ai/code; OpenCode: install from https://opencode.ai and register MCPs |

**Gemini API key is free.** Get one at [aistudio.google.com](https://aistudio.google.com). OpenAI and local models (Ollama/llama.cpp) are also supported.

Expand All @@ -115,7 +115,175 @@ Your personal data in one structured file: contact info, work authorization, com
Job search queries, target titles, locations, boards. Run multiple searches with different parameters.

### `.env`
API keys and runtime config: `GEMINI_API_KEY`, `LLM_MODEL`, `CAPSOLVER_API_KEY` (optional).
API keys and runtime config: `GEMINI_API_KEY`, `LLM_MODEL`, `CAPSOLVER_API_KEY` (optional). See Backend and Gateway configuration for details on multi-backend selection and gateway compatibility.

---

## Backend and Gateway configuration (Gemini first, OpenCode backend)

ApplyPilot supports multiple LLM backends. The baseline-first approach for LLMs is Gemini. For the auto-apply orchestration, the code's runtime default backend is Claude (APPLY_BACKEND unset => "claude"). OpenCode (opencode) is the recommended production path and is supported as an alternative; set APPLY_BACKEND=opencode to use it. Configure your environment carefully and never commit real keys.

1) Baseline LLM (Gemini)
- Set GEMINI_API_KEY to use Google Gemini for scoring, tailoring, and cover letters. This is the recommended default and is used automatically when present.

2) Gateway compatibility (9router / OpenAI-compatible gateways)
- If you need a proxy or gateway that speaks the OpenAI-compatible API (for example, 9router, self-hosted gateways, or Ollama with a REST wrapper), set these env vars in your `.env` or runtime environment:

- LLM_URL: Base URL of your gateway, for example `https://my-9router.example.com/v1`
- LLM_API_KEY: API key for that gateway (keep secret)
- LLM_MODEL: Model name exposed by the gateway, for example `gpt-4o-mini`

- Example (do not paste real keys):

export LLM_URL="https://my-9router.example.com/v1"
export LLM_API_KEY="sk-xxxxxxxx"
export LLM_MODEL="gpt-4o-mini"

3) Backend selection for auto-apply and orchestration
- Use APPLY_BACKEND to select which orchestration backend the system will auto-apply with. Supported values:
- opencode: Use the OpenCode backend and its MCP integrations (recommended)
- claude: Use Claude Code CLI for auto-apply (current code default when APPLY_BACKEND is not set)

- Backend defaults are configurable:
- `APPLY_CLAUDE_MODEL` (default: `haiku`)
- `APPLY_OPENCODE_MODEL` (fallback: `LLM_MODEL`, then `gpt-4o-mini`)
- `APPLY_OPENCODE_AGENT` (passed as `--agent` to `opencode run`)

Example (use OpenCode):

export APPLY_BACKEND=opencode
export APPLY_OPENCODE_MODEL="gh/claude-sonnet-4.5"
export APPLY_OPENCODE_AGENT="coder"

4) OpenCode MCP prerequisite
- When using the opencode backend you must register the OpenCode MCP provider before first run. Run:

opencode mcp add my-mcp --provider=openai --url "$LLM_URL" --api-key "$LLM_API_KEY" --model "$LLM_MODEL"

- Replace the provider and flags according to your MCP. This registers the gateway so OpenCode can reach it at runtime. Note: OpenCode manages MCP servers globally in its own config; you cannot pass an MCP config file per invocation.
- For parity with Claude apply flow, ensure `opencode mcp list` contains both MCP server names:
- `playwright`
- `gmail`
ApplyPilot validates this baseline before running the OpenCode backend.

5) Claude fallback / code default
- The code default backend when APPLY_BACKEND is not set is `claude`. If you plan to rely on the default behavior or explicitly set APPLY_BACKEND=claude, ensure Claude Code CLI is installed and configured. Claude remains supported as a fallback orchestration backend.

6) Security and secret handling
- Never add API keys to git. Use a local file outside the repo (for example `~/.applypilot/.env`) or a secret manager.
- Add `.env` or `~/.applypilot/.env` to your `.gitignore`.
- Rotate keys regularly and treat gateway keys like production secrets.
- When sharing examples, replace any keys with `sk-xxxxxxxx` or `GEMINI_API_KEY=xxxxx` placeholders.

7) 9router example variables
- 9router and similar gateways expect the following env variables for compatibility with ApplyPilot's AI stages: `LLM_URL`, `LLM_API_KEY`, `LLM_MODEL`. Make sure the gateway exposes an OpenAI-compatible v1 completions/chat endpoint.

8) Verification
- After setting env vars and optionally registering MCPs for opencode, run `applypilot doctor`. It will report configured providers and flag missing MCP registration or missing CLI binaries. If doctor reports issues, follow its guidance.

### OpenCode Configuration Details

ApplyPilot uses OpenCode in **project mode** with isolated configuration to prevent conflicts with your personal OpenCode setup:

**Configuration File:** `~/.applypilot/.opencode/opencode.jsonc`

This file is where you define:
- The custom `applypilot-apply` agent
- MCP server configurations
- Permission scopes
- Model defaults

**XDG Directory Isolation:**
ApplyPilot sets `XDG_CONFIG_HOME=~/.applypilot` when running OpenCode, which means:
- OpenCode loads **only** `~/.applypilot/.opencode/opencode.jsonc`
- Your global `~/.config/opencode/` is ignored
- Auth credentials (`~/.opencode/auth.json`) still work (different XDG var)
- State and sessions use default locations

This isolation ensures ApplyPilot's agent configuration doesn't interfere with your personal OpenCode workflows.

**Example `~/.applypilot/.opencode/opencode.jsonc`:**
```jsonc
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"*": "allow",
"question": "deny"
},
"tools": {
"question": false
},
"agent": {
"applypilot-apply": {
"description": "Autonomous job application agent",
"mode": "primary",
"model": "github-copilot/gpt-5-mini",
"prompt": "{file:../prompts/apply-agent.md}",
"permission": {
// Safety: deny file editing and bash
"task": "deny",
"edit": "deny",
"write": "deny",
"bash": "deny",
// Read-only tools allowed
"read": "allow",
"grep": "allow",
"glob": "allow",
"lsp": "allow",
// All Playwright browser tools enabled
"playwright_browser_navigate": "allow",
"playwright_browser_click": "allow",
"playwright_browser_fill_form": "allow",
"playwright_browser_snapshot": "allow",
"playwright_browser_evaluate": "allow",
"playwright_browser_file_upload": "allow",
"playwright_browser_tabs": "allow",
"playwright_browser_wait_for": "allow",
"playwright_browser_screenshot": "allow",
// All Gmail tools enabled
"gmail_search_emails": "allow",
"gmail_read_email": "allow",
"gmail_send_email": "allow",
"gmail_create_draft": "allow"
}
}
},
"mcp": {
"playwright": {
"type": "local",
"enabled": true,
"command": [
"npx", "@playwright/mcp@latest",
"--cdp-endpoint=http://localhost:9222"
]
},
"gmail": {
"type": "local",
"enabled": true,
"command": [
"npx", "-y", "@gongrzhe/server-gmail-autoauth-mcp"
]
}
}
}
```

**MCP Server Registration:**
```bash
# Register Playwright MCP for browser automation
opencode mcp add playwright --provider=openai --url="$LLM_URL" --api-key="$LLM_API_KEY"

# Verify registration
opencode mcp list
```

**Key Points:**
- The agent name `applypilot-apply` is referenced in the backend code
- The prompt file path is relative to the `~/.applypilot/` directory
- Browser tools are explicitly allowed; file editing is denied for safety
- Model can be overridden via `APPLY_OPENCODE_MODEL` environment variable

---

### Package configs (shipped with ApplyPilot)
- `config/employers.yaml` - Workday employer registry (48 preconfigured)
Expand All @@ -142,9 +310,14 @@ Generates a custom resume per job: reorders experience, emphasizes relevant skil
Writes a targeted cover letter per job referencing the specific company, role, and how your experience maps to their requirements.

### Auto-Apply
Claude Code launches a Chrome instance, navigates to each application page, detects the form type, fills personal information and work history, uploads the tailored resume and cover letter, answers screening questions with AI, and submits. A live dashboard shows progress in real-time.
Auto-apply is implemented via a pluggable backend with two supported options:

- **Claude**: uses the Claude Code CLI (default when APPLY_BACKEND is unset)
- **OpenCode**: uses the OpenCode CLI with pre-configured MCP servers

Both backends perform the same high-level tasks: launch a browser, detect form types, fill personal details, upload tailored documents, answer screening questions, and submit applications. A live dashboard shows progress in real-time.

The Playwright MCP server is configured automatically at runtime per worker. No manual MCP setup needed.
Choose your backend by setting APPLY_BACKEND=claude or APPLY_BACKEND=opencode. Each requires the respective CLI to be installed and configured.

```bash
# Utility modes (no Chrome/Claude needed)
Expand Down
Loading