cursor-route 0.1.1

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.
@@ -0,0 +1,26 @@
1
+ # Contributing
2
+
3
+ ## Dev
4
+
5
+ ```bash
6
+ bun install
7
+ bun test
8
+ ./bin/cursor-route health
9
+ CURSOR_ROUTE_RELAXED=1 ./bin/cursor-route health # without tmux
10
+ ./bin/cursor-route start --dry-run --worker grok "ping"
11
+ ```
12
+
13
+ ## Rules
14
+
15
+ - No secrets in prompts, fixtures, or commits
16
+ - Do not vendor private `agent-toolkit` paths
17
+ - Prefer adapters over rewriting the job core
18
+ - Always-approve defaults stay documented + opt-out (`--ask` / `CURSOR_ROUTE_ASK=1`)
19
+
20
+ ## Release checklist
21
+
22
+ 1. `bun test`
23
+ 2. `cursor-route health` (tmux + ≥1 worker)
24
+ 3. One real `start` smoke (grok or claude-ds)
25
+ 4. `npm pack --dry-run` — no secrets, no `node_modules`
26
+ 5. Tag `v0.x.y` then `npm publish --access public` (maintainers)
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Cemini
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,161 @@
1
+ # cursor-route
2
+
3
+ **Cursor stays the brain. Grok CLI + DeepSeek (claude-ds) are the parallel army.**
4
+
5
+ Lane-aware `/route` orchestration in tmux — not another multi-provider fleet, not a Codex clone.
6
+
7
+ ```bash
8
+ cursor-route health
9
+ cursor-route start --lane hard "Refactor auth module; run tests; report verify evidence"
10
+ cursor-route jobs --json
11
+ cursor-route capture <jobId>
12
+ ```
13
+
14
+ ## Why this exists
15
+
16
+ | Product | Brain | Workers | Feel |
17
+ |---------|-------|---------|------|
18
+ | [codex-orchestrator](https://github.com/kingbootoshi/codex-orchestrator) | Claude Code | Codex | Viral tmux panes |
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** | Cost-aware lanes you already pay for |
21
+
22
+ If you already live in Cursor, X Premium (Grok CLI), and DeepSeek — stop paying a third coding agent just to parallelize.
23
+
24
+ ## Cost table (orienting)
25
+
26
+ | Role | Typical bill | cursor-route use |
27
+ |------|----------------|------------------|
28
+ | Cursor | Premium plan | Plan / synthesize / verify (orchestrator) |
29
+ | Grok CLI | X Premium | `--lane hard` implement |
30
+ | DeepSeek via claude-ds | DeepSeek API / plan | `--lane mid` implement |
31
+ | Codex / extra Claude | Optional | Not required for v0 |
32
+
33
+ Exact dollars vary — the point is **reuse subscriptions you already have**.
34
+
35
+ ## Install (60 seconds)
36
+
37
+ **Prerequisites**
38
+
39
+ | Dep | Why |
40
+ |-----|-----|
41
+ | [tmux](https://github.com/tmux/tmux) | Worker panes |
42
+ | [Bun](https://bun.sh) *(or Node 20+)* | Runs the CLI |
43
+ | [Grok CLI](https://x.ai/cli) and/or Claude Code + DeepSeek (`claude-ds`) | Workers |
44
+ | `script(1)` | Job logs (macOS/Linux) |
45
+
46
+ ```bash
47
+ # Prerequisites
48
+ brew install tmux # or build tmux into ~/.local
49
+ # Bun optional — Node 20+ works via pinned tsx
50
+
51
+ # Preferred install
52
+ npm i -g cursor-route
53
+
54
+ # Auth workers
55
+ grok login # if using Grok
56
+ # configure claude-ds — see DeepSeek setup below
57
+
58
+ cursor-route health
59
+ # without tmux (CI / headless):
60
+ CURSOR_ROUTE_RELAXED=1 cursor-route health
61
+ ```
62
+
63
+ **From source (dev):**
64
+
65
+ ```bash
66
+ git clone https://github.com/cemini23/cursor-route.git
67
+ cd cursor-route && bun install
68
+ ./bin/cursor-route health
69
+ ```
70
+
71
+ ### Cursor skill
72
+
73
+ Copy the skill into your project or user skills:
74
+
75
+ ```bash
76
+ mkdir -p .cursor/skills
77
+ cp -R ~/.cursor-route-src/skills/route-orch .cursor/skills/
78
+ ```
79
+
80
+ Then say **`/route-orch`** or **spawn workers** in Cursor — the skill delegates to `cursor-route` (does not replace private Cemini `/route`).
81
+
82
+ ## Commands
83
+
84
+ | Command | Description |
85
+ |---------|-------------|
86
+ | `health` | Gate #1 — tmux, runtime, workers |
87
+ | `start <prompt>` | Spawn worker job (`--worker` / `--lane` / `--dir` / `--ask` / `--dry-run` / `--no-tmux`) |
88
+ | `jobs [--json]` | List jobs |
89
+ | `status <id>` | Job + sessionAlive |
90
+ | `capture <id> [n]` | Last n pane/log lines |
91
+ | `send <id> <msg>` | Mid-task redirect (tmux send-keys) |
92
+ | `attach <id>` | Print `tmux attach` hint |
93
+ | `kill <id>` | Stop session |
94
+ | `sessions` | Managed tmux sessions |
95
+ | `clean [--days N]` | Drop old job files |
96
+
97
+ ## Lanes
98
+
99
+ | Lane | Worker | Intent |
100
+ |------|--------|--------|
101
+ | `mid` | `claude-ds` | Default implement on DeepSeek |
102
+ | `hard` | `grok` | Hard implement on Grok CLI |
103
+
104
+ Always-approve is **on** by default. Opt out: `--ask` or `CURSOR_ROUTE_ASK=1`.
105
+
106
+ ## DeepSeek setup (the cheap mid-lane — this is the point)
107
+
108
+ `cursor-route` mid lane runs **DeepSeek**, not Anthropic. Claude Code is only the terminal harness; bills go to DeepSeek when `ANTHROPIC_BASE_URL` points at them.
109
+
110
+ **Recommended (official DeepSeek → Claude Code):**
111
+
112
+ ```bash
113
+ npm i -g @anthropic-ai/claude-code
114
+
115
+ export ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic
116
+ export ANTHROPIC_AUTH_TOKEN=YOUR_DEEPSEEK_API_KEY # from platform.deepseek.com
117
+ export ANTHROPIC_MODEL=deepseek-v4-pro[1m]
118
+ export ANTHROPIC_DEFAULT_HAIKU_MODEL=deepseek-v4-flash
119
+ export CLAUDE_CODE_SUBAGENT_MODEL=deepseek-v4-flash
120
+
121
+ cursor-route health # worker:claude-ds should be ✓
122
+ cursor-route start --lane mid "…"
123
+ ```
124
+
125
+ Persist the same vars under `~/.claude/settings.json` → `"env": { … }` if you want them every shell.
126
+
127
+ **Also accepted:** `claude-ds` or `deepseek-claude` on PATH (Cemini shims).
128
+
129
+ **Not the default:** bare `claude` still talking to Anthropic. Health refuses that so a misconfigured install cannot silently burn frontier $ rates. Escape hatch only: `CURSOR_ROUTE_ALLOW_ANTHROPIC=1`.
130
+
131
+ No DeepSeek yet? Use `--lane hard` / `--worker grok` (X Premium).
132
+
133
+ ## Jobs directory
134
+
135
+ Jobs default to `~/.local/share/cursor-route/jobs` (override with `CURSOR_ROUTE_JOBS_DIR`).
136
+ This is **not** inside a git clone of this repo.
137
+
138
+ ## Safety
139
+
140
+ - No secrets in prompts (CLI soft-refuses common key patterns)
141
+ - See [SECURITY.md](./SECURITY.md)
142
+ - Local orchestration only — no LIVE Discord / trading egress demos
143
+
144
+ ## Also from Cemini
145
+
146
+ - [Atto](https://youratto.com) — genealogy / archival agents
147
+ - [GuruWatcher](https://guruwatcher.com) — alert workflows
148
+ - Cemini research & trading tooling (private)
149
+
150
+ ## License
151
+
152
+ MIT © Cemini
153
+
154
+ ## Roadmap (explicitly later)
155
+
156
+ - npm publish + Homebrew tap
157
+ - Native DeepSeek harness adapter
158
+ - Codebase map injection (`--map`)
159
+ - Cursor CLI `agent` as alternate supervisor
160
+ - Web UI / CAO-style MCP supervisor
161
+ - OpenRouter easy lane (secrets-safe)
package/SECURITY.md ADDED
@@ -0,0 +1,26 @@
1
+ # Security Policy
2
+
3
+ ## Supported versions
4
+
5
+ | Version | Supported |
6
+ |---------|-----------|
7
+ | 0.x | Yes |
8
+
9
+ ## Reporting a vulnerability
10
+
11
+ Email security reports to the maintainers via GitHub Security Advisories on this repo.
12
+ Do **not** open a public issue for credential leaks or RCE-class bugs.
13
+
14
+ ## Hard rules for users and agents
15
+
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
+ 2. **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.
18
+ 3. **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.
19
+ 4. **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.
20
+ 5. **No LIVE Discord / trading egress demos** from this tool. Orchestration is local.
21
+
22
+ ## Supply chain
23
+
24
+ - Runtime dependencies: **none** (Node/Bun + system `tmux` + worker CLIs).
25
+ - Review `package.json` before upgrading.
26
+ - Prefer pinned git tags for production installs.
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env bash
2
+ # cursor-route — thin launcher (Bun preferred, Node+tsx fallback)
3
+ set -euo pipefail
4
+ SOURCE="${BASH_SOURCE[0]}"
5
+ while [ -L "$SOURCE" ]; do
6
+ DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"
7
+ SOURCE="$(readlink "$SOURCE")"
8
+ [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE"
9
+ done
10
+ SCRIPT_DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"
11
+ ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
12
+ CLI="$ROOT/src/cli.ts"
13
+
14
+ if command -v bun >/dev/null 2>&1; then
15
+ exec bun "$CLI" "$@"
16
+ fi
17
+
18
+ if command -v npx >/dev/null 2>&1; then
19
+ # Pin tsx for supply-chain predictability (no floating --yes latest)
20
+ exec npx --yes tsx@4.19.4 "$CLI" "$@"
21
+ fi
22
+
23
+ echo "cursor-route: need Bun (https://bun.sh) or Node 20+ with npx" >&2
24
+ exit 127
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * npm bin entry — prefers Bun, falls back to pinned tsx.
4
+ */
5
+ import { spawnSync } from "node:child_process";
6
+ import { fileURLToPath } from "node:url";
7
+ import { dirname, join } from "node:path";
8
+
9
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
10
+ const cli = join(root, "src", "cli.ts");
11
+ const args = process.argv.slice(2);
12
+
13
+ function run(cmd, cmdArgs) {
14
+ const r = spawnSync(cmd, cmdArgs, { stdio: "inherit", env: process.env });
15
+ if (r.error && r.error.code === "ENOENT") return null;
16
+ process.exit(r.status ?? 1);
17
+ }
18
+
19
+ const bun = spawnSync("bun", ["--version"], { encoding: "utf8" });
20
+ if (bun.status === 0) {
21
+ run("bun", [cli, ...args]);
22
+ }
23
+
24
+ run("npx", ["--yes", "tsx@4.19.4", cli, ...args]);
25
+ console.error("cursor-route: need Bun (https://bun.sh) or Node 20+ with npx");
26
+ process.exit(127);
@@ -0,0 +1,22 @@
1
+ # Hero demo recording (GIF)
2
+
3
+ tmux is required for the viral attach/send demo. On this laptop brew needs sudo — record after:
4
+
5
+ ```bash
6
+ brew install tmux
7
+ export PATH="$HOME/.cursor-route/bin:$PATH"
8
+ cursor-route health
9
+
10
+ # Terminal A — three parallel jobs
11
+ cursor-route start --lane hard --dir "$PWD" "Add README section Demo"
12
+ cursor-route start --lane mid --dir "$PWD" "Add a unit test for shellQuote"
13
+ cursor-route start --worker grok --dir "$PWD" "List open TODOs in src/"
14
+
15
+ # Terminal B — watch
16
+ cursor-route jobs --json
17
+ # Attach one pane for the GIF: tmux attach -t cursor-route-<id>
18
+ ```
19
+
20
+ Record with [asciinema](https://asciinema.org/) or CleanShot → export GIF → `docs/fixtures/hero.gif`.
21
+
22
+ Until then, use `docs/fixtures/claude-ds-smoke.log` as the committed proof fixture.
@@ -0,0 +1,64 @@
1
+ # Cursor audit — cursor-route v0.1.0→0.1.1
2
+
3
+ **Mode:** `code-debug` · **Roles → models:** code-implementation→`gpt-5.6-sol-medium`, adversarial→`cursor-grok-4.5-high-fast`, third-lens→`kimi-k3-max`
4
+
5
+ Auditors: [Sol](fad461d6-9be9-421e-b42e-081cd7888a56) · [Grok](79e70632-1de7-456e-871b-cdb66b7b72f1) · [Kimi](9a0b224f-eacf-4a2f-969c-6854db1d6876)
6
+
7
+ ## Verdict rollup (pre-fix)
8
+
9
+ | Model | Verdict |
10
+ |-------|---------|
11
+ | GPTSOL | FAIL |
12
+ | Cursor Grok | FAIL |
13
+ | Kimi 3 | WARN |
14
+
15
+ **Overall (pre-fix):** REWORK — then SHIP-WITH-FIXES after 0.1.1.
16
+
17
+ ## Consensus (≥2 auditors)
18
+
19
+ 1. Headless `kill` did not kill (no PID) — phantom killed
20
+ 2. `refreshStatus` inferred `completed` from missing tmux — lies on `--no-tmux`
21
+ 3. Completion hooks hard-coded `bun` vs Node fallback
22
+ 4. macOS `script` left `cd && …` unquoted
23
+ 5. Stock `claude` silent fallback for “claude-ds”
24
+ 6. Secret deny false-positive on prose “API key”; `send` bypass
25
+ 7. Missing DeepSeek setup docs / mid-lane dead end for public users
26
+ 8. Skill `/route` collision with federation `/route`
27
+
28
+ ## Unique
29
+
30
+ - [Sol] Job-id path traversal; dry-run side effects; race before `running` write
31
+ - [Grok] Always-approve + skill contract burns private SIP/verify; Grok sandbox missing
32
+ - [Kimi] No hero.gif; typecheck red; jobsDir collided with clone path; no CI
33
+
34
+ ## Conflicts
35
+
36
+ | Topic | Sol | Grok | Kimi | Resolution |
37
+ |-------|-----|------|------|------------|
38
+ | Overall ship | FAIL | FAIL | WARN | Fix criticals then soft-launch; GIF still deferred |
39
+ | Skill name `/route` | — | critical rename | — | Renamed triggers to `/route-orch` |
40
+
41
+ ## Fixes shipped in `20b1580` (v0.1.1)
42
+
43
+ - PID-tracked headless spawn + polled terminate on `kill`
44
+ - No phantom `completed` from session absence; exit via mark-complete / failed unknown
45
+ - `markCompleteInvoker` (bun or pinned `tsx@4.19.4`)
46
+ - macOS/Linux `script` always via `/bin/sh -c`
47
+ - Stock Claude only with `CURSOR_ROUTE_ALLOW_STOCK_CLAUDE=1`
48
+ - Material-only secret deny on `start` + `send`
49
+ - XDG jobs dir `~/.local/share/cursor-route/jobs`
50
+ - README DeepSeek setup; skill deconflict; CI workflow; `--version`; typecheck green
51
+
52
+ ## Verify after fix
53
+
54
+ - 11/11 tests · `tsc --noEmit` clean
55
+ - Headless claude-ds → `CURSOR_ROUTE_SMOKE_OK2` · `exitCode: 0`
56
+ - Headless `kill` → `status: killed`
57
+ - Prose “API key” dry-run allowed; `sk-…` refused exit 3
58
+
59
+ ## Still open (not tweet-blockers for soft launch)
60
+
61
+ - Real tmux attach/send GIF (`docs/DEMO_GIF.md`) — needs `brew install tmux` (sudo)
62
+ - npm publish
63
+ - Grok Build balance for live grok demos
64
+ - Optional: grok `--sandbox`, `wait` command, remove skill file duplicate via symlink
@@ -0,0 +1,25 @@
1
+ # Demo fixture log (no secrets)
2
+
3
+ Simulated capture output for README / tweet assets — replace with a real GIF once tmux is available.
4
+
5
+ ```
6
+ $ cursor-route health
7
+ cursor-route v0.1.0
8
+ health: OK
9
+ ✓ tmux
10
+ ✓ runtime bun ok
11
+ ✓ script(1)
12
+ ✓ worker:grok
13
+ ✓ worker:claude-ds
14
+
15
+ $ cursor-route start --lane mid "Add a failing test then make it pass"
16
+ started a1b2c3d4 (claude-ds)
17
+ session: cursor-route-a1b2c3d4
18
+ attach: tmux attach -t cursor-route-a1b2c3d4
19
+
20
+ $ cursor-route jobs --json
21
+ [{ "id": "a1b2c3d4", "status": "running", "worker": "claude-ds", ... }]
22
+ ```
23
+
24
+ Verified locally (2026-08-10): headless `claude-ds` smoke returned `CURSOR_ROUTE_SMOKE_OK`.
25
+ Grok smoke hit 402 (Build usage balance exhausted) — auth/PATH wiring works; top up Grok Build for live demos.
@@ -0,0 +1,5 @@
1
+ claude-ds - DeepSeek Claude Code (model=deepseek-v4-flash)
2
+ Install: ~/.deepseek-claude
3
+ PromptFile: ~/.cursor-route/jobs/e7567d1f.prompt
4
+ WorkDir: ~/Projects/cursor-route
5
+ CURSOR_ROUTE_SMOKE_OK
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "cursor-route",
3
+ "version": "0.1.1",
4
+ "description": "Cursor stays the brain. Grok CLI + DeepSeek (claude-ds) are the parallel army — lane-aware /route orchestration in tmux.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "bin": {
8
+ "cursor-route": "./bin/cursor-route.js"
9
+ },
10
+ "engines": {
11
+ "node": ">=20"
12
+ },
13
+ "files": [
14
+ "bin",
15
+ "src",
16
+ "skills",
17
+ "docs",
18
+ "LICENSE",
19
+ "SECURITY.md",
20
+ "CONTRIBUTING.md",
21
+ "README.md"
22
+ ],
23
+ "scripts": {
24
+ "start": "bun run src/cli.ts",
25
+ "health": "bun run src/cli.ts health",
26
+ "test": "bun test",
27
+ "typecheck": "tsc --noEmit",
28
+ "prepublishOnly": "bun test && bun run typecheck"
29
+ },
30
+ "keywords": [
31
+ "cursor",
32
+ "grok",
33
+ "deepseek",
34
+ "claude-ds",
35
+ "tmux",
36
+ "orchestrator",
37
+ "agents",
38
+ "route"
39
+ ],
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/cemini23/cursor-route.git"
43
+ },
44
+ "bugs": {
45
+ "url": "https://github.com/cemini23/cursor-route/issues"
46
+ },
47
+ "homepage": "https://github.com/cemini23/cursor-route#readme",
48
+ "devDependencies": {
49
+ "@types/bun": "^1.2.0",
50
+ "@types/node": "^22.15.0",
51
+ "typescript": "^5.8.0"
52
+ },
53
+ "dependencies": {}
54
+ }
@@ -0,0 +1,62 @@
1
+ ---
2
+ name: route-orch
3
+ description: >-
4
+ Delegate coding work from Cursor to parallel Grok CLI / claude-ds (DeepSeek)
5
+ workers via cursor-route. Use when the user says /route-orch, spawn workers,
6
+ parallel agents with cursor-route, or explicitly asks to outsource
7
+ implementation to Grok/DeepSeek panes — not for private Cemini /route.
8
+ ---
9
+
10
+ # route-orch (cursor-route)
11
+
12
+ You are the **orchestrator**. Do **not** implement bulk code in this Cursor session when delegation fits.
13
+
14
+ ## When to activate
15
+
16
+ - User says `/route-orch`, `spawn workers`, or asks for parallel Grok/DeepSeek via **cursor-route**
17
+ - Mid/hard implementation that should run on a subscription worker (Grok CLI / claude-ds)
18
+ - Multi-file investigation that benefits from parallel panes
19
+
20
+ **Do not steal federation `/route`.** Private Cemini `/route` (route-task → SIP → verify → Grok/claude-ds chain) is a different skill. This public skill only drives the `cursor-route` CLI.
21
+
22
+ ## Lanes (public core)
23
+
24
+ | Lane | Worker | Use when |
25
+ |------|--------|----------|
26
+ | `mid` | `claude-ds` (DeepSeek via Claude Code harness) | Standard implement / refactor |
27
+ | `hard` | `grok` | Premium plan in Cursor → Grok implement |
28
+
29
+ Easy/OpenRouter is **not** in v0 — keep secrets off free models.
30
+
31
+ ## Workflow
32
+
33
+ 1. Run `cursor-route health` (or `CURSOR_ROUTE_RELAXED=1` for headless). If the **target worker** is unhealthy, fix before spawning.
34
+ 2. Write a clear handoff prompt with **verify criteria** (no secrets, no LIVE Discord).
35
+ 3. Spawn:
36
+
37
+ ```bash
38
+ cursor-route start --lane hard --dir "$PWD" "$(cat <<'EOF'
39
+ ## Task
40
+ ...
41
+
42
+ ## Verify
43
+ - [ ] ...
44
+ EOF
45
+ )"
46
+ ```
47
+
48
+ Or `--worker grok` / `--worker claude-ds`. Use `--no-tmux` only when tmux is unavailable.
49
+
50
+ 4. Monitor: `cursor-route jobs --json` · `cursor-route capture <id>` · `cursor-route send <id> "…"` (tmux only).
51
+ 5. Summarize worker results with **verify evidence** — no status-only “done”. If verify fails, `send` a correction or spawn a follow-up — do not invent success.
52
+
53
+ ## Always-approve
54
+
55
+ Defaults on for workers. Opt out: `cursor-route start … --ask` or `CURSOR_ROUTE_ASK=1`.
56
+
57
+ ## Anti-patterns
58
+
59
+ - Do not paste API keys / private keys into prompts or `send`
60
+ - Do not claim DeepSeek-native harness until that adapter ships — today is **claude-ds**
61
+ - Do not open-source or dump private Cemini `agent-toolkit` paths into public handoffs
62
+ - Do not mark done without reading `capture` / exit status
@@ -0,0 +1,154 @@
1
+ import { execSync } from "node:child_process";
2
+ import { readFileSync, existsSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { join } from "node:path";
5
+ import type { Adapter, WorkerHealth } from "./types.ts";
6
+ import { shellQuote } from "../util.ts";
7
+
8
+ /**
9
+ * Mid-lane = DeepSeek (cheap). The Claude Code binary is only a harness —
10
+ * requests must hit DeepSeek, not Anthropic.
11
+ *
12
+ * Resolution order:
13
+ * 1. claude-ds / deepseek-claude shims
14
+ * 2. stock `claude` when ANTHROPIC_BASE_URL (env or ~/.claude/settings.json)
15
+ * points at api.deepseek.com — auto-ok, no opt-in
16
+ * 3. stock `claude` on Anthropic only if CURSOR_ROUTE_ALLOW_ANTHROPIC=1
17
+ * (explicit expensive escape hatch — not the product default)
18
+ */
19
+
20
+ function which(cmd: string): string | null {
21
+ try {
22
+ return (
23
+ execSync(`command -v ${cmd}`, {
24
+ encoding: "utf8",
25
+ stdio: ["ignore", "pipe", "ignore"],
26
+ }).trim() || null
27
+ );
28
+ } catch {
29
+ return null;
30
+ }
31
+ }
32
+
33
+ function deepseekBaseFromSettings(): string | null {
34
+ const candidates = [
35
+ join(homedir(), ".claude", "settings.json"),
36
+ join(process.cwd(), ".claude", "settings.json"),
37
+ ];
38
+ for (const p of candidates) {
39
+ if (!existsSync(p)) continue;
40
+ try {
41
+ const j = JSON.parse(readFileSync(p, "utf8")) as {
42
+ env?: Record<string, string>;
43
+ };
44
+ const url = j.env?.ANTHROPIC_BASE_URL;
45
+ if (url) return url;
46
+ } catch {
47
+ /* ignore */
48
+ }
49
+ }
50
+ return null;
51
+ }
52
+
53
+ /** True when Claude Code harness is routed to DeepSeek (cheap path). */
54
+ export function isDeepSeekRouted(): boolean {
55
+ const url =
56
+ process.env.ANTHROPIC_BASE_URL ||
57
+ process.env.CURSOR_ROUTE_ANTHROPIC_BASE_URL ||
58
+ deepseekBaseFromSettings() ||
59
+ "";
60
+ return /deepseek\.com/i.test(url);
61
+ }
62
+
63
+ function resolveClaudeDs(): { binary: string; mode: string } | null {
64
+ for (const c of [
65
+ { cmd: "claude-ds", mode: "claude-ds (DeepSeek shim)" },
66
+ { cmd: "deepseek-claude", mode: "deepseek-claude" },
67
+ ] as const) {
68
+ const path = which(c.cmd);
69
+ if (path) return { binary: path, mode: c.mode };
70
+ }
71
+
72
+ const claude = which("claude");
73
+ if (!claude) return null;
74
+
75
+ if (isDeepSeekRouted()) {
76
+ return {
77
+ binary: claude,
78
+ mode: "claude → DeepSeek (ANTHROPIC_BASE_URL)",
79
+ };
80
+ }
81
+
82
+ // Expensive Anthropic path — opt-in only so we never silently bill frontier rates
83
+ if (
84
+ process.env.CURSOR_ROUTE_ALLOW_ANTHROPIC === "1" ||
85
+ process.env.CURSOR_ROUTE_ALLOW_STOCK_CLAUDE === "1" // legacy alias
86
+ ) {
87
+ return {
88
+ binary: claude,
89
+ mode: "claude → Anthropic (CURSOR_ROUTE_ALLOW_ANTHROPIC=1)",
90
+ };
91
+ }
92
+
93
+ return null;
94
+ }
95
+
96
+ export const claudeDsAdapter: Adapter = {
97
+ kind: "claude-ds",
98
+ label: "DeepSeek (via Claude Code harness)",
99
+ health(): WorkerHealth {
100
+ const resolved = resolveClaudeDs();
101
+ if (!resolved) {
102
+ const hasClaude = Boolean(which("claude"));
103
+ return {
104
+ worker: "claude-ds",
105
+ ok: false,
106
+ binary: null,
107
+ detail: hasClaude
108
+ ? "claude on PATH but ANTHROPIC_BASE_URL is not DeepSeek — set https://api.deepseek.com/anthropic (see README). Refusing Anthropic default so mid-lane stays cheap."
109
+ : "need claude-ds, deepseek-claude, or claude + DeepSeek ANTHROPIC_BASE_URL — see README DeepSeek setup",
110
+ };
111
+ }
112
+ return {
113
+ worker: "claude-ds",
114
+ ok: true,
115
+ binary: resolved.binary,
116
+ detail: `ok (${resolved.mode})`,
117
+ };
118
+ },
119
+ buildLaunch({ promptFile, cwd, alwaysApprove }) {
120
+ const resolved = resolveClaudeDs();
121
+ if (!resolved) {
122
+ throw new Error("DeepSeek worker not available — run: cursor-route health");
123
+ }
124
+
125
+ const ask = process.env.CURSOR_ROUTE_ASK === "1" || process.env.CLAUDE_DS_ASK === "1";
126
+ const skip = alwaysApprove && !ask;
127
+
128
+ if (resolved.mode.startsWith("claude-ds")) {
129
+ const parts = [
130
+ shellQuote(resolved.binary),
131
+ "-PromptFile",
132
+ shellQuote(promptFile),
133
+ ];
134
+ if (skip) parts.push("--dangerously-skip-permissions");
135
+ return {
136
+ worker: "claude-ds",
137
+ command: `cd ${shellQuote(cwd)} && ${parts.join(" ")}`,
138
+ alwaysApprove: skip,
139
+ };
140
+ }
141
+
142
+ const parts = [
143
+ shellQuote(resolved.binary),
144
+ "-p",
145
+ `"$(cat ${shellQuote(promptFile)})"`,
146
+ ];
147
+ if (skip) parts.push("--dangerously-skip-permissions");
148
+ return {
149
+ worker: "claude-ds",
150
+ command: `cd ${shellQuote(cwd)} && ${parts.join(" ")}`,
151
+ alwaysApprove: skip,
152
+ };
153
+ },
154
+ };