cursor-route 0.1.9 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,15 @@
4
4
 
5
5
  _(none)_
6
6
 
7
+ ## 0.1.10 — 2026-08-21
8
+
9
+ - Opt-in `--worker opencode`: OpenCode `run` as a coding agent that defaults to Zen free model `opencode/big-pickle` (alias `--model free`). Not a lane default — mid stays `claude-ds`, easy stays OpenRouter chat.
10
+ - Always-approve → `opencode run --auto` (still honors explicit deny rules); `--ask` / `CURSOR_ROUTE_ASK=1` omits `--auto`. Never rewrites `~/.config/opencode/opencode.json`.
11
+ - `--model provider/model` for opencode (env `CURSOR_ROUTE_OPENCODE_MODEL`); `flash|pro` still DeepSeek-only.
12
+ - Health: `worker:opencode` ✓ when `opencode` is on PATH (override `CURSOR_ROUTE_OPENCODE_BIN`). Auth is `opencode auth login` at first start (same pattern as grok).
13
+ - Dry-run works without opencode installed (falls back to plain `opencode` in the printed command); real starts stay gated by health.
14
+ - Docs / `route-orch`: OpenCode is an opt-in usage-reduction worker, not a second mid harness. Free Zen models may log/train — non-secret prompts only.
15
+
7
16
  ## 0.1.9 — 2026-08-18
8
17
 
9
18
  - Health proves mid DeepSeek: dedicated `lane:mid` check is ✓ only when mid is a `claude-ds` / `deepseek-claude` shim or stock `claude` with DeepSeek `ANTHROPIC_BASE_URL`. `CURSOR_ROUTE_ALLOW_ANTHROPIC=1` is not proof. Overall `ok` still uses the existing OR-gate (any worker + `CURSOR_ROUTE_RELAXED=1`); grok-only installs stay OK with `lane:mid` ✗ plus a fix tip.
package/CONTRIBUTING.md CHANGED
@@ -12,6 +12,7 @@ CURSOR_ROUTE_RELAXED=1 ./bin/cursor-route health # without tmux
12
12
  ./bin/cursor-route start --dry-run --lane mid --model flash "ping"
13
13
  ./bin/cursor-route start --dry-run --lane mid --model pro "ping"
14
14
  ./bin/cursor-route start --dry-run --worker deepseek --model flash "ping" # needs dsh + DEEPSEEK_API_KEY for real starts
15
+ ./bin/cursor-route start --dry-run --worker opencode --model free "ping" # needs opencode for real starts
15
16
  ```
16
17
 
17
18
  Keep `skills/route-orch/SKILL.md` and `.cursor/skills/route-orch/SKILL.md` identical when editing the skill.
@@ -23,6 +24,7 @@ Keep `skills/route-orch/SKILL.md` and `.cursor/skills/route-orch/SKILL.md` ident
23
24
  - Prefer adapters over rewriting the job core
24
25
  - Always-approve defaults stay documented + opt-out (`--ask` / `CURSOR_ROUTE_ASK=1`)
25
26
  - Mid default is Flash; document Pro only as harder mid / Grok **usage** stand-in (not missing `grok login`)
27
+ - OpenCode is opt-in (`--worker opencode`); do not make it a lane default — mid stays claude-ds
26
28
 
27
29
  ## Release checklist
28
30
 
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # cursor-route
2
2
 
3
- **Cursor stays the brain. Grok CLI + DeepSeek (claude-ds) + OpenRouter (easy) are the parallel army.**
3
+ **Cursor stays the brain. Grok CLI + DeepSeek (claude-ds) + OpenRouter (easy) + OpenCode (opt-in free) are the parallel army.**
4
4
 
5
5
  Lane-aware `/route` orchestration in tmux — not another multi-provider fleet, not a Codex clone.
6
6
 
@@ -17,7 +17,7 @@ cursor-route capture <jobId>
17
17
  |---------|-------|---------|------|
18
18
  | [codex-orchestrator](https://github.com/kingbootoshi/codex-orchestrator) | Claude Code | Codex | Viral tmux panes |
19
19
  | [CAO](https://github.com/awslabs/cli-agent-orchestrator) | Supervisor CLI | Many (incl. Cursor CLI) | Enterprise MCP + Web UI |
20
- | **cursor-route** | **Cursor Agent** | **Grok CLI + claude-ds + OpenRouter (easy)** | Cost-aware lanes you already pay for |
20
+ | **cursor-route** | **Cursor Agent** | **Grok CLI + claude-ds + OpenRouter (easy) + OpenCode (opt-in)** | Cost-aware lanes you already pay for |
21
21
 
22
22
  If you already live in Cursor, X Premium (Grok CLI), and DeepSeek — stop paying a third coding agent just to parallelize.
23
23
 
@@ -29,6 +29,7 @@ If you already live in Cursor, X Premium (Grok CLI), and DeepSeek — stop payin
29
29
  | Grok CLI | X Premium | `--lane hard` implement |
30
30
  | DeepSeek via claude-ds | DeepSeek API / plan | `--lane mid` implement (**Flash** default; `--model pro` when needed) |
31
31
  | OpenRouter free models | OpenRouter API (free tier) | `--lane easy` wording/drafts — non-secret prompts only (see Security) |
32
+ | OpenCode (opt-in) | OpenCode Zen free models | `--worker opencode` implement on free Zen (default `opencode/big-pickle`) |
32
33
  | Codex / extra Claude | Optional | Not required for v0 |
33
34
 
34
35
  Exact dollars vary — the point is **reuse subscriptions you already have**.
@@ -43,6 +44,7 @@ Exact dollars vary — the point is **reuse subscriptions you already have**.
43
44
  | [Bun](https://bun.sh) *(or Node 20+)* | Runs the CLI |
44
45
  | [Grok CLI](https://x.ai/cli) and/or Claude Code + DeepSeek (`claude-ds`) | Workers |
45
46
  | OpenRouter API key (`OPENROUTER_API_KEY`) | Easy lane (`--lane easy` / `--worker openrouter`) |
47
+ | [OpenCode](https://opencode.ai) (`opencode` on PATH) | Opt-in `--worker opencode` (free Zen models) |
46
48
  | `script(1)` | Job logs (macOS/Linux) |
47
49
 
48
50
  ```bash
@@ -57,6 +59,7 @@ npm i -g cursor-route
57
59
  grok login # if using Grok
58
60
  # configure claude-ds — see DeepSeek setup below
59
61
  export OPENROUTER_API_KEY=... # if using the easy lane (see OpenRouter setup below)
62
+ # optional: npm i -g opencode-ai && opencode auth login # --worker opencode
60
63
 
61
64
  cursor-route health
62
65
  # without tmux / workers (CI / headless infra smoke):
@@ -221,6 +224,36 @@ cursor-route start --lane easy "Rewrite this FAQ answer in 3 sentences"
221
224
  **wording/drafts without credentials**. The same refuse gate as every lane blocks
222
225
  key-shaped material in `start` / `send`, and the runner re-checks the prompt file.
223
226
 
227
+ ## OpenCode setup (opt-in free coding worker)
228
+
229
+ `--worker opencode` runs [OpenCode](https://opencode.ai) as a **coding agent** on
230
+ OpenCode Zen free models (default `opencode/big-pickle`). This is **not** a lane
231
+ default — mid stays `claude-ds`; easy stays OpenRouter chat (no tools). Use it
232
+ to burn fewer Grok / DeepSeek tokens on implement work.
233
+
234
+ ```bash
235
+ npm i -g opencode-ai # or: brew install opencode
236
+ opencode auth login # connect OpenCode Zen (or another provider)
237
+ # optional:
238
+ export CURSOR_ROUTE_OPENCODE_MODEL=opencode/big-pickle # default; alias --model free
239
+
240
+ cursor-route health # worker:opencode should be ✓
241
+ cursor-route start --worker opencode "…" # Big Pickle (free)
242
+ cursor-route start --worker opencode --model free "…"
243
+ cursor-route start --worker opencode --model opencode/hy3-free "…"
244
+ ```
245
+
246
+ The adapter launches `opencode run --dir <cwd> --model <id>` with the prompt via
247
+ `cat` (never interpolated). Always-approve maps to `--auto` (explicit `"deny"`
248
+ rules still apply); `--ask` omits `--auto`. It never rewrites
249
+ `~/.config/opencode/opencode.json`, so parallel jobs don't race. Override the
250
+ binary with `CURSOR_ROUTE_OPENCODE_BIN`.
251
+
252
+ **Non-secret prompts:** several Zen free models may log or train on prompts during
253
+ their free period (see [OpenCode Zen](https://opencode.ai/docs/zen/) privacy notes).
254
+ The same refuse gate as every lane still applies. `opencode/x-preview-f-free`
255
+ (Ox Alpha) is the zero-retention free option if you need it.
256
+
224
257
  ## Jobs directory
225
258
 
226
259
  Jobs default to `~/.local/share/cursor-route/jobs` (override with `CURSOR_ROUTE_JOBS_DIR`).
@@ -235,7 +268,7 @@ This is **not** inside a git clone of this repo.
235
268
  ## FAQ
236
269
 
237
270
  **What is cursor-route?**
238
- cursor-route is a public MIT CLI and Cursor skill that runs parallel coding workers in tmux while Cursor remains the planner. DeepSeek handles the mid lane, Grok CLI handles the hard lane, and OpenRouter free models handle the easy lane for wording/drafts.
271
+ cursor-route is a public MIT CLI and Cursor skill that runs parallel coding workers in tmux while Cursor remains the planner. DeepSeek handles the mid lane, Grok CLI handles the hard lane, OpenRouter free models handle the easy lane for wording/drafts, and `--worker opencode` is an opt-in coding agent on OpenCode Zen free models.
239
272
 
240
273
  **How is this different from Codex orchestrator?**
241
274
  It uses the familiar strategist and worker-pane shape, but it is not a Codex clone. cursor-route uses Cursor as the planner and DeepSeek plus Grok CLI as workers. Codex is not required.
@@ -247,7 +280,7 @@ No. The mid worker is DeepSeek. Claude Code is the harness, configured with `ANT
247
280
  Run `npm i -g cursor-route`, install tmux if needed, then run `cursor-route health`. The package is available at https://www.npmjs.com/package/cursor-route, and the source is at https://github.com/cemini23/cursor-route.
248
281
 
249
282
  **Is it free?**
250
- The cursor-route code is open source under MIT. It does not make the worker services free. Your costs depend on Cursor, DeepSeek API usage, and the Grok access or balance available to you. The easy lane can be free on OpenRouter's free-model route (`openrouter/free`).
283
+ The cursor-route code is open source under MIT. It does not make the worker services free. Your costs depend on Cursor, DeepSeek API usage, and the Grok access or balance available to you. The easy lane can be free on OpenRouter's free-model route (`openrouter/free`). `--worker opencode` can run OpenCode Zen free models (default `opencode/big-pickle`).
251
284
 
252
285
  ## Related
253
286
 
@@ -280,6 +313,7 @@ If you’d like to tip, use the **donation-only** addresses below (not trading o
280
313
  | **EVM** (Ethereum, Polygon, Base, Arbitrum, …) | `0x444C5C2eC439E0382aa5a17F70313c536BcC5D58` |
281
314
  | **Solana / SVM** | `J4zNn4hK9jTrKBFY8sbAGJHLoZvXvQf4B9pQSbSrocZE` |
282
315
  | **Polymarket** (referral) | [polymarket.com/?r=Cemini23](https://polymarket.com/?r=Cemini23) |
316
+ | **Hyperliquid** (referral) | [app.hyperliquid.xyz/join/CEMINI23](https://app.hyperliquid.xyz/join/CEMINI23) |
283
317
 
284
318
  Full wallet note: [SUPPORT.md](SUPPORT.md) · canon also in [CCC SUPPORT.md](https://github.com/cemini23/cemini-claude-code-CCC/blob/main/SUPPORT.md).
285
319
 
@@ -293,6 +327,7 @@ MIT © Cemini — see [LICENSE](LICENSE).
293
327
 
294
328
  - Homebrew tap
295
329
  - Stabilize the DeepSeek harness adapter (experimental `--worker deepseek` since 0.1.8)
330
+ - Stabilize the OpenCode adapter (`--worker opencode` since 0.1.10)
296
331
  - Codebase map injection (`--map`)
297
332
  - Cursor CLI `agent` as alternate supervisor
298
333
  - Web UI / CAO-style MCP supervisor
package/SECURITY.md CHANGED
@@ -15,14 +15,15 @@ Do **not** open a public issue for credential leaks or RCE-class bugs.
15
15
 
16
16
  1. **No secrets in prompts or job files.** Do not paste API keys, SSH private keys, Discord tokens, or `.env` contents into `cursor-route start` / `send` prompts.
17
17
  2. **Free OpenRouter easy-lane models may log prompts.** `--lane easy` / `--worker openrouter` is for **non-secret** wording/drafts only. The refuse gate (`start` / `send`, plus the runner's re-check) applies to every lane — never send key-shaped material to the easy lane.
18
- 3. **Always-approve is powerful.** Default headless workers skip interactive permission prompts (`--always-approve` / `--dangerously-skip-permissions`). Opt out with `--ask` / `CURSOR_ROUTE_ASK=1` when you need HITL.
19
- 4. **Install from known channels only.** Prefer `npm i -g cursor-route` or a git clone of this repo. Do not pipe unknown `curl | bash` installers as the primary path.
20
- 5. **Workers inherit your auth.** Grok CLI and Claude Code / claude-ds use *your* local login. Treat worker panes like you would treat your own shell.
21
- 6. **No LIVE Discord / trading egress demos** from this tool. Orchestration is local.
18
+ 3. **OpenCode Zen free models may log or train during their free period.** `--worker opencode` defaults to `opencode/big-pickle`. Treat it like the easy lane for secrets. Prefer `opencode/x-preview-f-free` (Ox Alpha) when you need the zero-retention free option.
19
+ 4. **Always-approve is powerful.** Default headless workers skip interactive permission prompts (`--always-approve` / `--dangerously-skip-permissions` / OpenCode `--auto`). Opt out with `--ask` / `CURSOR_ROUTE_ASK=1` when you need HITL.
20
+ 5. **Install from known channels only.** Prefer `npm i -g cursor-route` or a git clone of this repo. Do not pipe unknown `curl | bash` installers as the primary path.
21
+ 6. **Workers inherit your auth.** Grok CLI, Claude Code / claude-ds, and OpenCode use *your* local login. Treat worker panes like you would treat your own shell.
22
+ 7. **No LIVE Discord / trading egress demos** from this tool. Orchestration is local.
22
23
 
23
24
  ## Supply chain
24
25
 
25
- - Declared runtime npm dependencies: **none**. System needs: Node ≥20 or Bun, `tmux`, worker CLIs (Grok / claude-ds), and an OpenRouter API key for the easy lane.
26
+ - Declared runtime npm dependencies: **none**. System needs: Node ≥20 or Bun, `tmux`, worker CLIs (Grok / claude-ds / optional OpenCode), and an OpenRouter API key for the easy lane.
26
27
  - The npm package ships a compiled `dist/` (plain JS, no loader). The bin shim and the completion hook run the compiled JS via `node`; there is **no network fetch of `tsx` on the happy path**.
27
28
  - Running from a git clone without a build uses Bun to execute `src/` directly (Bun runs TypeScript natively) — also offline-friendly.
28
29
  - Review `package.json` before upgrading.
package/SUPPORT.md CHANGED
@@ -10,6 +10,7 @@ Voluntary tips fund wikis, OSS tooling, and public research. **Donation-only add
10
10
  | **EVM** (Ethereum, Polygon, Base, Arbitrum, …) | `0x444C5C2eC439E0382aa5a17F70313c536BcC5D58` |
11
11
  | **Solana / SVM** | `J4zNn4hK9jTrKBFY8sbAGJHLoZvXvQf4B9pQSbSrocZE` |
12
12
  | **Polymarket** (referral) | [polymarket.com/?r=Cemini23](https://polymarket.com/?r=Cemini23) |
13
+ | **Hyperliquid** (referral) | [app.hyperliquid.xyz/join/CEMINI23](https://app.hyperliquid.xyz/join/CEMINI23) |
13
14
 
14
15
  If you’d rather follow along or try something we ship:
15
16
 
@@ -2,11 +2,13 @@ import { grokAdapter } from "./grok.js";
2
2
  import { claudeDsAdapter } from "./claude-ds.js";
3
3
  import { openRouterAdapter } from "./openrouter.js";
4
4
  import { deepseekAdapter } from "./deepseek.js";
5
+ import { opencodeAdapter } from "./opencode.js";
5
6
  const registry = {
6
7
  grok: grokAdapter,
7
8
  "claude-ds": claudeDsAdapter,
8
9
  openrouter: openRouterAdapter,
9
10
  deepseek: deepseekAdapter,
11
+ opencode: opencodeAdapter,
10
12
  };
11
13
  export function getAdapter(worker) {
12
14
  const a = registry[worker];
@@ -0,0 +1,83 @@
1
+ import { execSync } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import { shellQuote } from "../util.js";
4
+ import { openCodeModel, OPENCODE_DEFAULT_MODEL } from "../config.js";
5
+ /**
6
+ * Opt-in OpenCode worker (`opencode run`) — a coding agent that can use
7
+ * OpenCode Zen free models (default `opencode/big-pickle`) to cut Grok /
8
+ * DeepSeek usage. Not a lane default: mid stays claude-ds; easy stays
9
+ * OpenRouter chat (no tools).
10
+ *
11
+ * We never rewrite ~/.config/opencode/opencode.json (parallel jobs would
12
+ * race). Always-approve maps to `opencode run --auto` (still honors explicit
13
+ * deny rules). Prompt is inlined via cat — never interpolated.
14
+ */
15
+ function findOpencode() {
16
+ // Env override lets tests pin a fake opencode — but it must exist, so a
17
+ // stale override cannot pass health with a dangling path.
18
+ const override = process.env.CURSOR_ROUTE_OPENCODE_BIN;
19
+ if (override)
20
+ return existsSync(override) ? override : null;
21
+ try {
22
+ return (execSync("command -v opencode", {
23
+ encoding: "utf8",
24
+ stdio: ["ignore", "pipe", "ignore"],
25
+ env: { ...process.env },
26
+ }).trim() || null);
27
+ }
28
+ catch {
29
+ return null;
30
+ }
31
+ }
32
+ export const opencodeAdapter = {
33
+ kind: "opencode",
34
+ label: "OpenCode (free Zen / other models)",
35
+ health() {
36
+ const binary = findOpencode();
37
+ if (!binary) {
38
+ return {
39
+ worker: "opencode",
40
+ ok: false,
41
+ binary: null,
42
+ detail: "opencode not found — install: npm i -g opencode-ai (or brew install opencode). Then: opencode auth login. Mid default remains claude-ds.",
43
+ };
44
+ }
45
+ let defaultModel = OPENCODE_DEFAULT_MODEL;
46
+ try {
47
+ defaultModel = openCodeModel();
48
+ }
49
+ catch {
50
+ /* invalid env — health still ok; startJob will fail loud */
51
+ }
52
+ return {
53
+ worker: "opencode",
54
+ ok: true,
55
+ binary,
56
+ detail: `ok (opencode run; default model ${defaultModel}; auth at first start — opencode auth login if jobs fail; mid default remains claude-ds)`,
57
+ };
58
+ },
59
+ buildLaunch({ promptFile, cwd, alwaysApprove, modelId }) {
60
+ // Missing binary is tolerated here so `--dry-run` can still print the
61
+ // command; real starts are gated by the health preflight.
62
+ const binary = findOpencode() || "opencode";
63
+ const id = openCodeModel(modelId);
64
+ const ask = process.env.CURSOR_ROUTE_ASK === "1";
65
+ const skip = alwaysApprove && !ask;
66
+ const parts = [
67
+ shellQuote(binary),
68
+ "run",
69
+ "--dir",
70
+ shellQuote(cwd),
71
+ "--model",
72
+ shellQuote(id),
73
+ ];
74
+ if (skip)
75
+ parts.push("--auto");
76
+ parts.push(`"$(cat ${shellQuote(promptFile)})"`);
77
+ return {
78
+ worker: "opencode",
79
+ command: `cd ${shellQuote(cwd)} && ${parts.join(" ")}`,
80
+ alwaysApprove: skip,
81
+ };
82
+ },
83
+ };
package/dist/cli.js CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env bun
2
2
  /**
3
- * cursor-route CLI — Cursor brain, Grok + DeepSeek + OpenRouter (easy) workers in tmux.
3
+ * cursor-route CLI — Cursor brain, Grok + DeepSeek + OpenRouter (easy) + OpenCode (opt-in) workers in tmux.
4
4
  */
5
5
  import { readFileSync, existsSync, realpathSync, statSync } from "node:fs";
6
6
  import { resolve, basename } from "node:path";
7
- import { config, WORKERS, LANES, resolveDsModel, } from "./config.js";
7
+ import { config, WORKERS, LANES, resolveDsModel, openCodeModel, } from "./config.js";
8
8
  import { runHealth, printHealth } from "./health.js";
9
9
  import { startJob, listJobs, readJob, killJob, cleanJobs, jobPaths, refreshStatus, jobEvidence, } from "./jobs.js";
10
10
  import { capturePane, sendKeys, attachHint, listManagedSessions, sessionExists, } from "./tmux.js";
@@ -12,7 +12,7 @@ import { looksLikeSecretMaterial, redactSecrets } from "./secrets.js";
12
12
  function usage(exitCode = 0) {
13
13
  console.log(`cursor-route v${config.version}
14
14
 
15
- Cursor stays the brain. Grok CLI + DeepSeek (claude-ds) + OpenRouter (easy) are the parallel army.
15
+ Cursor stays the brain. Grok CLI + DeepSeek (claude-ds) + OpenRouter (easy) + OpenCode (opt-in free) are the parallel army.
16
16
 
17
17
  Usage:
18
18
  cursor-route --version
@@ -29,9 +29,9 @@ Usage:
29
29
  cursor-route clean [--days N]
30
30
 
31
31
  Start options:
32
- --worker <grok|claude-ds|openrouter|deepseek> Worker adapter (default: grok; deepseek = experimental official dsh)
32
+ --worker <grok|claude-ds|openrouter|deepseek|opencode> Worker adapter (default: grok; deepseek/opencode = opt-in)
33
33
  --lane <easy|mid|hard> Lane → worker (easy=openrouter, mid=claude-ds, hard=grok)
34
- --model <flash|pro> Mid DeepSeek only (default: flash / CURSOR_ROUTE_DS_MODEL). Parsed for claude-ds + deepseek
34
+ --model <flash|pro|free|provider/model> claude-ds/deepseek: flash|pro. opencode: free (default opencode/big-pickle) or provider/model
35
35
  --dir <path> Working directory (default: cwd)
36
36
  --ask Disable always-approve for this job
37
37
  --dry-run Print launch command; do not start
@@ -48,10 +48,12 @@ Env:
48
48
  CURSOR_ROUTE_GROK_BIN Override the grok binary path (tests / power users)
49
49
  CURSOR_ROUTE_CLAUDE_DS_BIN Override the claude-ds binary path (tests / power users)
50
50
  CURSOR_ROUTE_DSH_BIN Override the dsh binary path (tests / power users)
51
+ CURSOR_ROUTE_OPENCODE_BIN Override the opencode binary path (tests / power users)
51
52
  DEEPSEEK_API_KEY DeepSeek API key (required for --worker deepseek)
52
53
  OPENROUTER_API_KEY OpenRouter key (required for --worker openrouter / --lane easy)
53
54
  CURSOR_ROUTE_OPENROUTER_MODEL OpenRouter model (default: openrouter/free)
54
55
  OPENROUTER_BASE_URL OpenRouter API base (default: https://openrouter.ai/api/v1)
56
+ CURSOR_ROUTE_OPENCODE_MODEL OpenCode model (default: opencode/big-pickle); --model overrides
55
57
  `);
56
58
  process.exit(exitCode);
57
59
  }
@@ -150,6 +152,14 @@ function asDsModelChoice(v) {
150
152
  throw new Error(`Invalid --model; expected flash|pro`);
151
153
  return resolveDsModel(v);
152
154
  }
155
+ function asOpenCodeModel(v) {
156
+ if (v === undefined || v === true)
157
+ return undefined;
158
+ if (typeof v !== "string") {
159
+ throw new Error(`Invalid --model; expected provider/model (e.g. opencode/big-pickle) or free`);
160
+ }
161
+ return openCodeModel(v);
162
+ }
153
163
  function refuseSecrets(text, context) {
154
164
  if (looksLikeSecretMaterial(text)) {
155
165
  console.error(`Refusing ${context}: looks like secret key material. Remove tokens/keys and retry.`);
@@ -223,8 +233,8 @@ async function main() {
223
233
  const resolvedWorker = worker ?? (lane ? config.laneWorkers[lane] : config.defaultWorker);
224
234
  let model;
225
235
  let modelId;
226
- // --model is DeepSeek-only (claude-ds mid lane + experimental deepseek worker);
227
- // ignore (do not validate) for other workers
236
+ // --model: DeepSeek flash|pro for claude-ds/deepseek; provider/model (or free) for opencode;
237
+ // ignore (do not validate) for grok/openrouter
228
238
  if (f.model !== undefined && (resolvedWorker === "claude-ds" || resolvedWorker === "deepseek")) {
229
239
  try {
230
240
  const choice = asDsModelChoice(f.model);
@@ -238,6 +248,15 @@ async function main() {
238
248
  process.exit(2);
239
249
  }
240
250
  }
251
+ else if (f.model !== undefined && resolvedWorker === "opencode") {
252
+ try {
253
+ modelId = asOpenCodeModel(f.model);
254
+ }
255
+ catch (e) {
256
+ console.error(e.message);
257
+ process.exit(2);
258
+ }
259
+ }
241
260
  let prompt = "";
242
261
  if (promptFile) {
243
262
  const p = resolve(promptFile);
@@ -327,7 +346,7 @@ async function main() {
327
346
  else {
328
347
  for (const j of jobs) {
329
348
  const age = j.startedAt || j.createdAt;
330
- const modelCol = j.model ? j.model.padEnd(6) : "".padEnd(6);
349
+ const modelCol = j.model ? j.model.padEnd(22) : "".padEnd(22);
331
350
  console.log(`${j.id} ${j.status.padEnd(10)} ${j.worker.padEnd(10)} ${modelCol} ${age} ${j.prompt.slice(0, 48).replace(/\n/g, " ")}`);
332
351
  }
333
352
  }
package/dist/config.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { homedir } from "node:os";
2
2
  import { join } from "node:path";
3
3
  import { defaultJobsDir } from "./runtime.js";
4
- export const WORKERS = ["grok", "claude-ds", "openrouter", "deepseek"];
4
+ export const WORKERS = ["grok", "claude-ds", "openrouter", "deepseek", "opencode"];
5
5
  export const LANES = ["easy", "mid", "hard"];
6
6
  export const DS_MODELS = ["flash", "pro"];
7
7
  export const DS_MODEL_IDS = {
@@ -49,6 +49,33 @@ export function openRouterModel() {
49
49
  export function openRouterBaseUrl() {
50
50
  return process.env.OPENROUTER_BASE_URL || "https://openrouter.ai/api/v1";
51
51
  }
52
+ /**
53
+ * Default OpenCode model: OpenCode Zen Big Pickle (free, limited-time).
54
+ * Override: CURSOR_ROUTE_OPENCODE_MODEL or `--model provider/model`.
55
+ * Alias `free` resolves to this default (or the env override).
56
+ */
57
+ export const OPENCODE_DEFAULT_MODEL = "opencode/big-pickle";
58
+ /** Whitelist OpenCode `provider/model` ids (no spaces / injection). */
59
+ export function assertOpenCodeModel(id) {
60
+ if (!/^[a-z0-9][a-z0-9._-]*(?:\/[a-z0-9][a-z0-9._\-:]+)+$/i.test(id)) {
61
+ throw new Error(`Invalid OpenCode model ${id}; expected provider/model (e.g. opencode/big-pickle) or free`);
62
+ }
63
+ return id;
64
+ }
65
+ /**
66
+ * Resolve OpenCode `--model` / env to a concrete `provider/model` id.
67
+ * Empty or `free` → CURSOR_ROUTE_OPENCODE_MODEL, else OPENCODE_DEFAULT_MODEL.
68
+ */
69
+ export function openCodeModel(raw) {
70
+ const v = (raw ?? "").trim();
71
+ if (!v || v.toLowerCase() === "free") {
72
+ const env = (process.env.CURSOR_ROUTE_OPENCODE_MODEL ?? "").trim();
73
+ if (!env || env.toLowerCase() === "free")
74
+ return OPENCODE_DEFAULT_MODEL;
75
+ return assertOpenCodeModel(env);
76
+ }
77
+ return assertOpenCodeModel(v);
78
+ }
52
79
  function maxConcurrentJobsFromEnv() {
53
80
  const raw = process.env.CURSOR_ROUTE_MAX_JOBS;
54
81
  if (raw) {
@@ -65,7 +92,7 @@ function maxConcurrentJobsFromEnv() {
65
92
  */
66
93
  export const config = {
67
94
  product: "cursor-route",
68
- version: "0.1.9",
95
+ version: "0.1.10",
69
96
  get jobsDir() {
70
97
  return defaultJobsDir();
71
98
  },
@@ -73,7 +100,7 @@ export const config = {
73
100
  defaultWorker: "grok",
74
101
  /**
75
102
  * Lane → default worker (Cemini /route public core).
76
- * `deepseek` is experimental only — mid stays on claude-ds.
103
+ * `deepseek` and `opencode` are opt-in only — mid stays on claude-ds.
77
104
  */
78
105
  laneWorkers: {
79
106
  easy: "openrouter",
package/dist/health.js CHANGED
@@ -107,7 +107,7 @@ export function printHealth(report, asJson) {
107
107
  if (!report.ok) {
108
108
  console.log("");
109
109
  console.log("Fix the ✗ items, then re-run: cursor-route health");
110
- console.log("Tip: start with one worker (grok OR claude-ds) before parallel demos.");
110
+ console.log("Tip: start with one worker (grok OR claude-ds OR opencode) before parallel demos.");
111
111
  }
112
112
  else if (report.checks.some((c) => c.name === "lane:mid" && !c.ok)) {
113
113
  console.log("");
package/dist/jobs.js CHANGED
@@ -2,7 +2,7 @@ import { mkdirSync, writeFileSync, readFileSync, readdirSync, existsSync, unlink
2
2
  import { join, resolve } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { dirname } from "node:path";
5
- import { config, sessionName, defaultDsModelFromEnv, DS_MODEL_IDS, } from "./config.js";
5
+ import { config, sessionName, defaultDsModelFromEnv, DS_MODEL_IDS, openCodeModel, } from "./config.js";
6
6
  import { getAdapter } from "./adapters/index.js";
7
7
  import { spawn } from "node:child_process";
8
8
  import { spawnSync } from "node:child_process";
@@ -245,14 +245,17 @@ export function startJob(opts) {
245
245
  writeSecure(paths.prompt, opts.prompt);
246
246
  let model;
247
247
  let modelId;
248
+ let dsAlias;
248
249
  if (worker === "claude-ds" || worker === "deepseek") {
249
250
  if (opts.model) {
251
+ dsAlias = opts.model;
250
252
  model = opts.model;
251
253
  modelId = opts.modelId ?? DS_MODEL_IDS[opts.model];
252
254
  }
253
255
  else {
254
256
  try {
255
257
  const choice = defaultDsModelFromEnv();
258
+ dsAlias = choice.alias;
256
259
  model = choice.alias;
257
260
  modelId = opts.modelId ?? choice.id;
258
261
  }
@@ -267,13 +270,29 @@ export function startJob(opts) {
267
270
  }
268
271
  }
269
272
  }
273
+ else if (worker === "opencode") {
274
+ try {
275
+ const ocModel = openCodeModel(opts.modelId);
276
+ model = ocModel;
277
+ modelId = ocModel;
278
+ }
279
+ catch (e) {
280
+ try {
281
+ unlinkSync(paths.prompt);
282
+ }
283
+ catch {
284
+ /* ignore */
285
+ }
286
+ return { ok: false, error: e.message };
287
+ }
288
+ }
270
289
  let plan;
271
290
  try {
272
291
  plan = adapter.buildLaunch({
273
292
  promptFile: paths.prompt,
274
293
  cwd,
275
294
  alwaysApprove,
276
- model,
295
+ model: dsAlias,
277
296
  modelId,
278
297
  dryRun: Boolean(opts.dryRun),
279
298
  });
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  title: cursor-route workspace — working brief (edit in place)
3
3
  repo: ~/Projects/cursor-route
4
- npm: cursor-route@0.1.9 (not published this WorkDir only)
4
+ npm: cursor-route@0.1.10 (unreleasedOpenCode worker)
5
5
  created: 2026-08-12
6
- updated: 2026-08-18
6
+ updated: 2026-08-21
7
7
  ---
8
8
 
9
9
  # cursor-route — living brief
@@ -21,10 +21,11 @@ Cursor Agent plans. Workers run in tmux via `cursor-route`:
21
21
  | `easy` | OpenRouter free | Wording / drafts — non-secret prompts only |
22
22
  | `mid` | claude-ds (DeepSeek behind Claude Code) | Default implement (**Flash**; `--model pro` when needed) |
23
23
  | `hard` | Grok CLI | Hard implement |
24
+ | opt-in | OpenCode | `--worker opencode` coding agent on Zen free models (default `opencode/big-pickle`) |
24
25
 
25
26
  Always-approve on for coding worktrees (`--ask` / `CURSOR_ROUTE_ASK=1` to opt out) — not LIVE Discord/trading. Jobs live in `~/.local/share/cursor-route/jobs`, not in this clone.
26
27
 
27
- Install: `npm i -g cursor-route` → **0.1.9** once published. Release notes: [CHANGELOG.md](../../CHANGELOG.md).
28
+ Install: `npm i -g cursor-route` → **0.1.9** live; **0.1.10** unreleased (OpenCode worker). Release notes: [CHANGELOG.md](../../CHANGELOG.md).
28
29
 
29
30
  ## Open (edit / check off)
30
31
 
@@ -38,6 +39,7 @@ Install: `npm i -g cursor-route` → **0.1.9** once published. Release notes: [C
38
39
  - [x] **Experimental `--worker deepseek`** — 0.1.8: real dsh adapter (`dsh --profile headless` + per-job Cordis patch pins the model; never writes `~/.dsh/settings.yaml`). Always-approve → `DSH_PERMISSION_MODE=danger-full-access`, `--ask` → `workspace-write`; key via env only. Health ✓ needs `dsh` + `DEEPSEEK_API_KEY` (override `CURSOR_ROUTE_DSH_BIN`). Mid stays **claude-ds**.
39
40
  - [x] **route-orch brief steals (2026-08-14)** — AutoDesign / misevolution / Vero habits into the public skill: **Verify / claim closeout** (external eval contract; activity ≠ verification), **Eval & skill hygiene** (mid-run Verify-rewrite ban; skill misevolution HITL — no auto-promotion of worker-trajectory variants; verify-fail → reconsider plan/definition + stage attribution spawn/execute/verify); handoff shape Success criteria + Verify + NEVER; skills synced; mid stays claude-ds.
40
41
  - [x] **Health proves mid DeepSeek + evidence tree** — 0.1.9: `lane:mid` ✓ only when DeepSeek is proven (shim or DeepSeek `ANTHROPIC_BASE_URL`; Anthropic hatch is not proof); health JSON `lanes.mid`; `status --json` evidence tree (`spawn` / `execute` / `verify.claim=unverified`); skill health-before-mid + evidence-tree closeout. Mid stays **claude-ds**.
42
+ - [x] **Opt-in `--worker opencode`** — 0.1.10: `opencode run --dir` + `--model` (default Zen free `opencode/big-pickle` / `--model free`); always-approve → `--auto`; `--ask` omits it; never rewrites `~/.config/opencode/opencode.json`. Health ✓ needs `opencode` on PATH (`CURSOR_ROUTE_OPENCODE_BIN`). Mid stays **claude-ds**; easy stays OpenRouter chat. Free Zen may log/train — non-secret prompts.
41
43
  - [ ] **Hero GIF** — still outstanding; dry-run fixture ships as the substitute for now (`docs/fixtures/hero-demo.log` — see `docs/DEMO_GIF.md`)
42
44
  - [x] **Do not** paste private `ROUTE_KIT`, SIP, prod paths, or hang-watchdog env into this public repo
43
45
 
@@ -50,6 +52,7 @@ Install: `npm i -g cursor-route` → **0.1.9** once published. Release notes: [C
50
52
  | `src/adapters/deepseek.ts` | Experimental dsh worker (`--worker deepseek`; mid stays claude-ds) |
51
53
  | `src/adapters/grok.ts` | Hard worker |
52
54
  | `src/adapters/openrouter.ts` | Easy worker |
55
+ | `src/adapters/opencode.ts` | Opt-in OpenCode worker (`--worker opencode`; mid stays claude-ds) |
53
56
  | `skills/route-orch/SKILL.md` | Cursor skill — spawn CLI, do not implement in-session |
54
57
  | `CHANGELOG.md` | Release notes |
55
58
  | `SECURITY.md` | Secret refuse gate |
@@ -67,4 +70,5 @@ Install: `npm i -g cursor-route` → **0.1.9** once published. Release notes: [C
67
70
  | 2026-08-14 | DeepSeek Harness eval: `@deepseek-ai/dsh` 0.1.0-rc.6 is a developer-preview plugin kernel, not a mid replacement; `--worker deepseek` stays unhealthy; mid remains claude-ds (docs-only, no version bump). |
68
71
  | 2026-08-14 | Experimental `--worker deepseek` wired to official dsh (headless + per-job patch + `DSH_PERMISSION_MODE` + key-via-env); `--model` applies to claude-ds + deepseek; mid stays claude-ds → 0.1.8 LIVE. |
69
72
  | 2026-08-14 | route-orch brief steals (AutoDesign / misevolution / Vero): **Verify / claim closeout** + **Eval & skill hygiene**; handoff Success criteria + Verify + NEVER; both skill copies synced; mid stays claude-ds. Docs-only, no version bump. |
70
- | 2026-08-18 | Health `lane:mid` + `lanes.mid` prove DeepSeek; status evidence tree (`verify.claim` stays unverified); skill health-before-mid + closeout tree; mid stays claude-ds → 0.1.9 (not published). |
73
+ | 2026-08-18 | Health `lane:mid` + `lanes.mid` prove DeepSeek; status evidence tree (`verify.claim` stays unverified); skill health-before-mid + closeout tree; mid stays claude-ds → 0.1.9 LIVE. |
74
+ | 2026-08-21 | Opt-in `--worker opencode` (Zen free `opencode/big-pickle`, `--auto`, no config rewrite); mid stays claude-ds → 0.1.10. |
package/llms.txt CHANGED
@@ -1,14 +1,14 @@
1
1
  # cursor-route
2
2
 
3
- > Cursor stays the planner. DeepSeek (mid), Grok CLI (hard), and OpenRouter free models (easy) run parallel coding workers in tmux.
3
+ > Cursor stays the planner. DeepSeek (mid), Grok CLI (hard), and OpenRouter free models (easy) run parallel coding workers in tmux. `--worker opencode` is an opt-in coding agent on OpenCode Zen free models.
4
4
 
5
- MIT CLI + Cursor skill. npm: https://www.npmjs.com/package/cursor-route (latest **0.1.9**)
5
+ MIT CLI + Cursor skill. npm: https://www.npmjs.com/package/cursor-route (latest **0.1.10**)
6
6
  GitHub: https://github.com/cemini23/cursor-route
7
7
 
8
8
  ## FAQ
9
9
 
10
10
  ### What is cursor-route?
11
- cursor-route is a public MIT CLI and Cursor skill that runs parallel coding workers in tmux while Cursor remains the planner. DeepSeek handles the mid lane, Grok CLI handles the hard lane, and OpenRouter free models handle the easy lane (wording/drafts, non-secret prompts only).
11
+ cursor-route is a public MIT CLI and Cursor skill that runs parallel coding workers in tmux while Cursor remains the planner. DeepSeek handles the mid lane, Grok CLI handles the hard lane, and OpenRouter free models handle the easy lane (wording/drafts, non-secret prompts only). `--worker opencode` is an opt-in coding agent (default `opencode/big-pickle`) — not a lane default.
12
12
 
13
13
  ### How is this different from Codex orchestrator?
14
14
  It uses the familiar strategist and worker-pane shape, but it is not a Codex clone. cursor-route uses Cursor as the planner and DeepSeek plus Grok CLI as workers. Codex is not required.
@@ -22,15 +22,19 @@ No. The mid worker is DeepSeek. Claude Code is the harness, configured with `ANT
22
22
  | `--model flash` (default) | `deepseek-v4-flash` | Cheap mid execute |
23
23
  | `--model pro` | `deepseek-v4-pro` | Harder mid / Grok usage stand-in |
24
24
 
25
+ ### How do I use OpenCode free models?
26
+ Install OpenCode (`npm i -g opencode-ai`), run `opencode auth login`, then `cursor-route start --worker opencode "…"`. Default model is `opencode/big-pickle` (`--model free`). Mid stays `claude-ds`.
27
+
25
28
  ### How do I install?
26
29
  Run `npm i -g cursor-route`, install tmux if needed, then run `cursor-route health`. Copy the Cursor skill from `$(npm root -g)/cursor-route/skills/route-orch`. Source: https://github.com/cemini23/cursor-route
27
30
 
28
31
  ### Is it free?
29
- The cursor-route code is open source under MIT. It does not make the worker services free. Your costs depend on Cursor, DeepSeek API usage, and the Grok access or balance available to you. The easy lane can be free on OpenRouter's free-model route (`openrouter/free`).
32
+ The cursor-route code is open source under MIT. It does not make the worker services free. Your costs depend on Cursor, DeepSeek API usage, and the Grok access or balance available to you. The easy lane can be free on OpenRouter's free-model route (`openrouter/free`). `--worker opencode` can use OpenCode Zen free models.
30
33
 
31
34
  ## Install
32
35
 
33
36
  - npm: `npm i -g cursor-route`
34
37
  - Health: `cursor-route health`
35
38
  - Mid: `cursor-route start --lane mid "…"` (Flash) · `--model pro` when needed
39
+ - OpenCode (opt-in): `cursor-route start --worker opencode "…"`
36
40
  - Skill: `/route-orch` (Cursor)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cursor-route",
3
- "version": "0.1.9",
4
- "description": "Cursor stays the brain. Grok CLI + DeepSeek (claude-ds) + OpenRouter easy lane are the parallel army \u2014 lane-aware /route orchestration in tmux.",
3
+ "version": "0.1.10",
4
+ "description": "Cursor stays the brain. Grok CLI + DeepSeek (claude-ds) + OpenRouter easy lane + OpenCode (opt-in free) are the parallel army \u2014 lane-aware /route orchestration in tmux.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -41,6 +41,7 @@
41
41
  "deepseek",
42
42
  "claude-ds",
43
43
  "openrouter",
44
+ "opencode",
44
45
  "tmux",
45
46
  "orchestrator",
46
47
  "agents",