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.
- package/CONTRIBUTING.md +26 -0
- package/LICENSE +21 -0
- package/README.md +161 -0
- package/SECURITY.md +26 -0
- package/bin/cursor-route +24 -0
- package/bin/cursor-route.js +26 -0
- package/docs/DEMO_GIF.md +22 -0
- package/docs/audit-2026-08-10-sol-grok-kimi.md +64 -0
- package/docs/demo-notes.md +25 -0
- package/docs/fixtures/claude-ds-smoke.log +5 -0
- package/package.json +54 -0
- package/skills/route-orch/SKILL.md +62 -0
- package/src/adapters/claude-ds.ts +154 -0
- package/src/adapters/grok.ts +54 -0
- package/src/adapters/index.ts +21 -0
- package/src/adapters/types.ts +26 -0
- package/src/cli.test.ts +85 -0
- package/src/cli.ts +370 -0
- package/src/config.ts +32 -0
- package/src/health.ts +120 -0
- package/src/jobs.ts +394 -0
- package/src/mark-complete.ts +42 -0
- package/src/runtime.ts +23 -0
- package/src/secrets.ts +26 -0
- package/src/tmux.ts +123 -0
- package/src/util.ts +21 -0
package/CONTRIBUTING.md
ADDED
|
@@ -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.
|
package/bin/cursor-route
ADDED
|
@@ -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);
|
package/docs/DEMO_GIF.md
ADDED
|
@@ -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.
|
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
|
+
};
|