catherd-cli 1.2.0 → 1.3.0
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/README.md +74 -3
- package/catalog/models.json +168 -5
- package/catalog/scores.json +24 -0
- package/catalog/sources.json +13 -2
- package/package.json +3 -2
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/bin/catherd-mcp +1 -1
- package/plugin/skills/catherd/SKILL.md +3 -3
- package/plugin/skills/catherd-setup/SKILL.md +3 -3
- package/src/adapters/access.ts +54 -17
- package/src/adapters/all.ts +6 -0
- package/src/adapters/antigravity/events.ts +131 -0
- package/src/adapters/antigravity/home.ts +46 -0
- package/src/adapters/antigravity/index.ts +320 -0
- package/src/adapters/antigravity/models.ts +37 -0
- package/src/adapters/backend.ts +34 -1
- package/src/adapters/claude-code/index.ts +2 -0
- package/src/adapters/claude-code/models.ts +2 -1
- package/src/adapters/codex/index.ts +2 -0
- package/src/adapters/cursor/events.ts +120 -0
- package/src/adapters/cursor/home.ts +79 -0
- package/src/adapters/cursor/index.ts +331 -0
- package/src/adapters/cursor/models.ts +30 -0
- package/src/adapters/discovery.ts +21 -0
- package/src/adapters/grok/events.ts +112 -0
- package/src/adapters/grok/home.ts +132 -0
- package/src/adapters/grok/index.ts +339 -0
- package/src/adapters/grok/models.ts +48 -0
- package/src/adapters/opencode/index.ts +2 -0
- package/src/cli.ts +1 -0
- package/src/domain/catalog.ts +10 -1
- package/src/domain/cost.ts +1 -0
- package/src/domain/errors.ts +1 -0
- package/src/domain/failover.ts +5 -2
- package/src/domain/ids.ts +2 -2
- package/src/domain/profile.ts +57 -0
- package/src/domain/record.ts +2 -0
- package/src/domain/util.ts +3 -0
- package/src/entry/knowledge-command.ts +50 -0
- package/src/entry/tui/effects.ts +12 -3
- package/src/entry/tui/fixtures.ts +1 -0
- package/src/entry/tui/profile-tree.ts +10 -1
- package/src/entry/tui/views/profiles.tsx +14 -1
- package/src/infra/paths.ts +3 -1
- package/src/services/admission.ts +27 -4
- package/src/services/backends.ts +42 -4
- package/src/services/capture.ts +112 -54
- package/src/services/catalog-refresh.ts +2 -1
- package/src/services/dispatches.ts +2 -0
- package/src/services/doctor-access.ts +14 -7
- package/src/services/doctor-backends.ts +10 -1
- package/src/services/doctor-checks.ts +25 -0
- package/src/services/doctor.ts +3 -0
- package/src/services/finalize.ts +1 -0
- package/src/services/lane-service.ts +3 -10
- package/src/services/profile-service.ts +3 -3
- package/src/services/profile-store.ts +106 -2
- package/src/services/run-service.ts +37 -4
- package/src/services/run-store.ts +11 -1
- package/src/services/source-derive.ts +32 -7
package/README.md
CHANGED
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|
(")(")
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
Autopilot builds from your own Claude Code session. Claude plans and verifies, Codex, opencode
|
|
10
|
-
headless Claude Code workers write the code, and [Jev](https://typesafe.ai) picks the model and effort for each piece
|
|
9
|
+
Autopilot builds from your own Claude Code session. Claude plans and verifies, Codex, opencode, Cursor, Grok Build,
|
|
10
|
+
Antigravity or headless Claude Code workers write the code, and [Jev](https://typesafe.ai) picks the model and effort for each piece
|
|
11
11
|
of work, climbing a ladder only when a cheaper rung falls short.
|
|
12
12
|
|
|
13
13
|
- **Your harness, as you set it up.** Every role runs in its vendor's own CLI with your config,
|
|
14
14
|
hooks, skills and `AGENTS.md`. Isolation is an opt-in toggle per profile and harness, for when
|
|
15
15
|
you'd rather save the tokens your customizations cost.
|
|
16
16
|
- **Claude roles stay native.** `claude:` rungs run as ordinary Claude Code subagents; Codex,
|
|
17
|
-
opencode and headless `claude-code:` rungs run through catherd's MCP server.
|
|
17
|
+
opencode, Cursor, Grok Build, Antigravity and headless `claude-code:` rungs run through catherd's MCP server.
|
|
18
18
|
- **Survives restarts.** Workers are detached processes writing straight to disk, so a dropped
|
|
19
19
|
MCP server never loses a run.
|
|
20
20
|
- **Results come to you.** Roles run side by side while you keep talking to Claude; each one that finishes
|
|
@@ -34,6 +34,13 @@ of work, climbing a ladder only when a cheaper rung falls short.
|
|
|
34
34
|
- [opencode](https://opencode.ai) **v2**, 2.0.16 or newer: `curl -fsSL https://opencode.ai/v2/install | bash`
|
|
35
35
|
(the npm package `opencode-ai` is v1 and is not supported)
|
|
36
36
|
- Claude Code's `claude` CLI 2.1.282 or newer, for headless `claude-code:` rungs
|
|
37
|
+
- Cursor's CLI, `cursor-agent` 2026.09.28 or newer: `curl https://cursor.com/install -fsS | bash`, then
|
|
38
|
+
`cursor-agent login` (below)
|
|
39
|
+
- Grok Build's `grok` 1.0.44 or newer: `curl -fsSL https://x.ai/cli/install.sh | bash`, then `grok login`
|
|
40
|
+
(below)
|
|
41
|
+
- Google's Antigravity CLI, `agy` 1.2.13 or newer: `brew install --cask antigravity-cli`, or
|
|
42
|
+
`curl -fsSL https://antigravity.google/cli/install.sh | bash` (with `~/.local/bin` on PATH), then run `agy` once
|
|
43
|
+
to sign in (below)
|
|
37
44
|
- Optional: a TypeSafe API key for Jev, in `TYPESAFE_API_KEY` or saved by `catherd init`
|
|
38
45
|
- Optional: a free [Artificial Analysis](https://artificialanalysis.ai) API key for more scores, in
|
|
39
46
|
`ARTIFICIAL_ANALYSIS_API_KEY` or saved by `catherd init`; its numbers are read for you alone and never shipped
|
|
@@ -42,6 +49,66 @@ of work, climbing a ladder only when a cheaper rung falls short.
|
|
|
42
49
|
profile runs its workers on Codex; without Codex, doctor's fix also names how to move those roles to a backend
|
|
43
50
|
you have (`/catherd-setup` in Claude Code, or `catherd profile set roles.<role>.rungs <rung>`).
|
|
44
51
|
|
|
52
|
+
### Cursor
|
|
53
|
+
|
|
54
|
+
A Cursor rung names Cursor's own model slug, with the effort as the slug's suffix: `cursor:gpt-6-sol#xhigh` runs
|
|
55
|
+
`gpt-6-sol-xhigh`, and `#default` runs the bare slug (`cursor:composer-2.5#default`). `cursor-agent models` lists
|
|
56
|
+
what your account offers; `catherd catalog refresh` reads it. No profile uses Cursor until you put a rung on it.
|
|
57
|
+
Its Composer and Grok models bill from Cursor's own pool, the other models at API rates after your plan's included
|
|
58
|
+
usage, so a Cursor rung ranks as `metered`.
|
|
59
|
+
|
|
60
|
+
- **Native** (the default) runs with your login and your Cursor setup. It also loads your Claude Code hooks,
|
|
61
|
+
skills and plugins, which Cursor reads from `~/.claude`. `workspace-write` runs in Cursor's sandbox under your
|
|
62
|
+
`~/.cursor/sandbox.json`, which catherd never edits: `catherd doctor` shows which of a worker's checks (the lock
|
|
63
|
+
dir, temp, loopback, the network, Docker) it allows. `read-only` runs Cursor's ask mode, which asks the model not
|
|
64
|
+
to write but cannot stop it (`advisory`).
|
|
65
|
+
- **Isolated** (`catherd profile set harness.cursor.isolated true`) needs `CURSOR_API_KEY`, since a login cannot
|
|
66
|
+
move to another home. catherd runs Cursor under its own HOME with its own `sandbox.json`, which gives a worker
|
|
67
|
+
catherd's writable roots.
|
|
68
|
+
|
|
69
|
+
### Grok Build
|
|
70
|
+
|
|
71
|
+
A Grok rung names grok's model and effort: `grok:grok-4.6#high` runs `grok -m grok-4.6 --effort high`, and
|
|
72
|
+
`#default` passes no effort. `grok models` lists your models but no efforts, so catherd offers Grok Build's `low`
|
|
73
|
+
to `xhigh`; `catherd catalog refresh` reads the list. No profile uses grok until you put a rung on it. A Grok login
|
|
74
|
+
(SuperGrok, X Premium+) bills your subscription and `XAI_API_KEY` bills per token: `catherd doctor` says when a
|
|
75
|
+
profile's `billing.grok` differs from your login. On a usage limit, a Grok rung fails over to the same model on
|
|
76
|
+
Cursor, and a Cursor Grok rung to grok, unless the profile names another stand-in.
|
|
77
|
+
|
|
78
|
+
- **Native** (the default) runs with your login and your grok setup. It also loads your Claude Code plugins,
|
|
79
|
+
agents, skills, hooks and permission rules, and your Cursor rules and MCP. `workspace-write` runs in grok's
|
|
80
|
+
sandbox under catherd's own profile, `catherd-ws`, which lets a worker write the lock dir, temp and the
|
|
81
|
+
toolchain caches: catherd adds its marked `[profiles.catherd-*]` tables to `~/.grok/sandbox.toml` and leaves the
|
|
82
|
+
rest of that file alone. `read-only` runs grok's kernel `read-only` profile. On a Mac whose
|
|
83
|
+
`/var/run/docker.sock` is a link (Docker Desktop, OrbStack, Colima) grok refuses that profile, so catherd gives
|
|
84
|
+
the role only the read tools instead (`advisory`); `catherd doctor` says which applies. On macOS no grok profile
|
|
85
|
+
controls the network.
|
|
86
|
+
- **Isolated** (`catherd profile set harness.grok.isolated true`) needs `XAI_API_KEY`: catherd never copies your
|
|
87
|
+
grok login, whose refresh token rotates. catherd runs grok under its own HOME with its own `sandbox.toml`.
|
|
88
|
+
|
|
89
|
+
grok keeps a session on the access it started with, so catherd refuses to resume one under another access.
|
|
90
|
+
|
|
91
|
+
### Antigravity
|
|
92
|
+
|
|
93
|
+
An Antigravity rung names the model `agy models` lists, without an effort suffix; the effort is agy's `--effort`
|
|
94
|
+
(`low`, `medium`, `high` or `max`), and `#default` passes none: `antigravity:gemini-3.8-flash#low`. No profile uses
|
|
95
|
+
Antigravity until you put a rung on it. catherd never runs `agy -p` while agy is signed out, since agy would open a
|
|
96
|
+
browser and wait; `catherd doctor` says so, with the fix. A Google login draws on your plan's quota (doctor shows
|
|
97
|
+
what is left); `GEMINI_API_KEY` bills the Gemini API project. Gemini rungs fail over between Antigravity and Cursor
|
|
98
|
+
when either runs out, unless your profile names another stand-in.
|
|
99
|
+
|
|
100
|
+
- **Native** (the default) runs with your Google login and your `~/.gemini` settings, which the Antigravity desktop
|
|
101
|
+
app shares and catherd never edits. `workspace-write` runs shell commands in agy's sandbox, which reaches the
|
|
102
|
+
workspace, temp and build caches but no network unless your own `read_url` rules grant it; the file tools are not
|
|
103
|
+
confined (`advisory`). agy has no read-only mode, so a read-only role (the reviewer, the architect) cannot run on
|
|
104
|
+
native Antigravity: `profile validate` refuses it.
|
|
105
|
+
- **Isolated** (`catherd profile set harness.antigravity.isolated true`) needs `GEMINI_API_KEY`. catherd runs agy
|
|
106
|
+
under its own HOME, with its own settings: the Gemini API as the provider, write and command deny rules for
|
|
107
|
+
read-only roles, and catherd's writable roots and the network for `workspace-write`.
|
|
108
|
+
|
|
109
|
+
Whether Google's plan terms allow an orchestrator to drive `agy` on a consumer plan is not settled here. The API-key
|
|
110
|
+
route is the one meant for automation; check the terms before you rely on a plan login.
|
|
111
|
+
|
|
45
112
|
## Install
|
|
46
113
|
|
|
47
114
|
```sh
|
|
@@ -93,6 +160,7 @@ In a terminal:
|
|
|
93
160
|
| `catherd catalog sync [--force] [--unmatched]` | Fetches the public model facts and scores now (below); `--unmatched` lists ids no model matched |
|
|
94
161
|
| `catherd catalog treat-like <rung> <like>` | Scores an unscored rung as a scored one |
|
|
95
162
|
| `catherd catalog treat-like --suggest <rung>\|--clear <rung>\|--reset` | The three nearest stand-ins for a rung; removes one or every mapping of yours |
|
|
163
|
+
| `catherd knowledge show\|add "<line>"\|path [--repo <path>]` | The repo's knowledge.md, which new runs read; `add` appends a fact of yours, marked "by hand" |
|
|
96
164
|
| `catherd lock [--slots N] -- <cmd>` | Runs a heavy command behind the machine-wide semaphore, in its own session (no /dev/tty) |
|
|
97
165
|
| `catherd mcp` | The MCP server on stdio; the plugin starts it, you never need to |
|
|
98
166
|
| `catherd capture-fixtures [--backend <b>] [--out <dir>]` | Contributors: records sanitized test fixtures from real runs (see CONTRIBUTING.md) |
|
|
@@ -133,6 +201,9 @@ Run data lives in `~/.local/share/catherd/`, config in `~/.config/catherd/` (bot
|
|
|
133
201
|
| `TYPESAFE_API_KEY` | The Jev key, instead of the one `catherd init` saves |
|
|
134
202
|
| `ARTIFICIAL_ANALYSIS_API_KEY` | An Artificial Analysis key for `catalog sync`, instead of the one `catherd init` saves |
|
|
135
203
|
| `CATHERD_NO_SYNC` | `1`: no automatic sync of the public sources (at MCP server start and in `init`); `catalog sync` still runs |
|
|
204
|
+
| `CURSOR_API_KEY` | Cursor's API key: logs `cursor-agent` in, and is what an isolated Cursor role runs on |
|
|
205
|
+
| `XAI_API_KEY` | xAI's API key: logs `grok` in when no Grok login is active, and is what an isolated grok role runs on |
|
|
206
|
+
| `GEMINI_API_KEY` | The Gemini API key an isolated Antigravity role runs on (agy reads it only with its Gemini API provider) |
|
|
136
207
|
| `CATHERD_HOME` | Puts config and data under `$CATHERD_HOME/config` and `$CATHERD_HOME/data` instead of XDG |
|
|
137
208
|
| `CATHERD_LOG` | Log level: `off`, `error`, `warn`, `info` (default) or `debug` (what `--verbose` sets) |
|
|
138
209
|
| `CATHERD_LOCK_SLOTS` | `catherd lock`'s slot count when `--slots` is not given (before the profile's `lock.heavy`) |
|
package/catalog/models.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": 1,
|
|
3
|
-
"version": "2026-
|
|
3
|
+
"version": "2026-10-01",
|
|
4
4
|
"sources": {
|
|
5
5
|
"claude": "https://platform.claude.com/docs/en/models/overview (2026-09-25); efforts https://platform.claude.com/docs/en/build-with-claude/effort; prices https://platform.claude.com/docs/en/about-claude/pricing",
|
|
6
|
-
"codex": "codex debug models --bundled, Codex CLI 0.157.0 (2026-09-25); prices https://developers.openai.com/api/docs/pricing; plan weights https://learn.chatgpt.com/docs/pricing",
|
|
7
|
-
"opencode": "opencode models <provider> --verbose on 1.18.32 and the v2 model.list (2026-09-25); https://opencode.ai/docs/zen, https://opencode.ai/docs/go"
|
|
6
|
+
"codex": "codex debug models --bundled, Codex CLI 0.157.0 (2026-09-25), gpt-6.1-sol from Codex CLI 0.159.2 (2026-10-01); prices https://developers.openai.com/api/docs/pricing; plan weights https://learn.chatgpt.com/docs/pricing",
|
|
7
|
+
"opencode": "opencode models <provider> --verbose on 1.18.32 and the v2 model.list (2026-09-25); https://opencode.ai/docs/zen, https://opencode.ai/docs/go",
|
|
8
|
+
"cursor": "cursor-agent models on 2026.09.28-64d2043 (research 2026-09-29 §2.4; ids to confirm in the live kit, docs/dev/live-verification.md §11); prices https://openrouter.ai/api/v1/models and https://models.dev/api.json (2026-09-28)",
|
|
9
|
+
"antigravity": "agy 1.2.13 docs (models, and slugs such as gemini-3.8-flash-high; research 2026-09-29 §4.5) and the Gemini API's thinking levels; ids and efforts to confirm in the live kit, docs/dev/live-verification.md §13"
|
|
8
10
|
},
|
|
9
11
|
"backends": {
|
|
10
12
|
"codex": {
|
|
@@ -40,6 +42,24 @@
|
|
|
40
42
|
"ultra": "maximum reasoning with automatic task delegation to parallel subagents; never in a default ladder"
|
|
41
43
|
}
|
|
42
44
|
},
|
|
45
|
+
{
|
|
46
|
+
"id": "gpt-6.1-sol",
|
|
47
|
+
"name": "GPT-6.1 Sol",
|
|
48
|
+
"vendor": "openai",
|
|
49
|
+
"releaseDate": "2026-09-29",
|
|
50
|
+
"capabilities": { "toolUse": true, "imageIn": true, "reasoning": true },
|
|
51
|
+
"price": { "input": 2, "cached": 0.1, "output": 10 },
|
|
52
|
+
"on": {
|
|
53
|
+
"codex": {
|
|
54
|
+
"id": "gpt-6.1-sol",
|
|
55
|
+
"efforts": ["low", "medium", "high", "xhigh", "max", "ultra"],
|
|
56
|
+
"context": 272000
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"notes": {
|
|
60
|
+
"ultra": "maximum reasoning with automatic task delegation to parallel subagents; never in a default ladder"
|
|
61
|
+
}
|
|
62
|
+
},
|
|
43
63
|
{
|
|
44
64
|
"id": "gpt-6-sol",
|
|
45
65
|
"name": "GPT-6 Sol",
|
|
@@ -58,7 +78,8 @@
|
|
|
58
78
|
"id": "opencode/gpt-6-sol",
|
|
59
79
|
"efforts": ["none", "low", "medium", "high", "xhigh", "max"],
|
|
60
80
|
"context": 1050000
|
|
61
|
-
}
|
|
81
|
+
},
|
|
82
|
+
"cursor": { "id": "gpt-6-sol", "efforts": ["low", "high", "xhigh"], "context": 200000 }
|
|
62
83
|
},
|
|
63
84
|
"notes": {
|
|
64
85
|
"ultra": "maximum reasoning with automatic task delegation to parallel subagents; never in a default ladder"
|
|
@@ -87,7 +108,8 @@
|
|
|
87
108
|
"id": "opencode-go/gpt-6-luna",
|
|
88
109
|
"efforts": ["none", "low", "medium", "high", "xhigh", "max"],
|
|
89
110
|
"context": 1050000
|
|
90
|
-
}
|
|
111
|
+
},
|
|
112
|
+
"cursor": { "id": "gpt-6-luna", "efforts": ["high"], "context": 200000 }
|
|
91
113
|
}
|
|
92
114
|
},
|
|
93
115
|
{
|
|
@@ -205,10 +227,35 @@
|
|
|
205
227
|
"id": "opencode/claude-opus-5-5",
|
|
206
228
|
"efforts": ["low", "medium", "high", "xhigh", "max"],
|
|
207
229
|
"context": 1000000
|
|
230
|
+
},
|
|
231
|
+
"cursor": {
|
|
232
|
+
"id": "claude-opus-5-5",
|
|
233
|
+
"efforts": ["low", "medium", "high", "xhigh", "max"],
|
|
234
|
+
"context": 200000
|
|
208
235
|
}
|
|
209
236
|
},
|
|
210
237
|
"notes": { "effort": "the API default effort is medium" }
|
|
211
238
|
},
|
|
239
|
+
{
|
|
240
|
+
"id": "claude-sonnet-5-5",
|
|
241
|
+
"name": "Claude Sonnet 5.5",
|
|
242
|
+
"vendor": "anthropic",
|
|
243
|
+
"releaseDate": "2026-09-28",
|
|
244
|
+
"capabilities": { "toolUse": true, "imageIn": true, "reasoning": true },
|
|
245
|
+
"price": { "input": 2, "cached": 0.2, "output": 10 },
|
|
246
|
+
"on": {
|
|
247
|
+
"claude-code": {
|
|
248
|
+
"id": "claude-sonnet-5-5",
|
|
249
|
+
"efforts": ["low", "medium", "high", "xhigh", "max"],
|
|
250
|
+
"context": 1000000
|
|
251
|
+
},
|
|
252
|
+
"opencode": {
|
|
253
|
+
"id": "opencode/claude-sonnet-5-5",
|
|
254
|
+
"efforts": ["low", "medium", "high", "xhigh", "max"],
|
|
255
|
+
"context": 1000000
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
},
|
|
212
259
|
{
|
|
213
260
|
"id": "claude-sonnet-5",
|
|
214
261
|
"name": "Claude Sonnet 5",
|
|
@@ -241,6 +288,122 @@
|
|
|
241
288
|
"opencode": { "id": "opencode/claude-haiku-4-5", "efforts": ["high", "max"], "context": 200000 }
|
|
242
289
|
},
|
|
243
290
|
"notes": { "effort": "no effort parameter in Claude Code: its only rung there is #default" }
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
"id": "grok-4-7",
|
|
294
|
+
"name": "Grok 4.7",
|
|
295
|
+
"vendor": "xai",
|
|
296
|
+
"releaseDate": "2026-09-21",
|
|
297
|
+
"capabilities": { "toolUse": true, "imageIn": true, "reasoning": true },
|
|
298
|
+
"price": { "input": 2, "cached": 0.5, "output": 6 },
|
|
299
|
+
"on": {
|
|
300
|
+
"grok": { "id": "grok-4.7", "efforts": ["low", "medium", "high", "xhigh"], "context": 500000 },
|
|
301
|
+
"cursor": { "id": "grok-4.7", "efforts": ["low", "medium", "high", "xhigh"], "context": 200000 }
|
|
302
|
+
},
|
|
303
|
+
"notes": {
|
|
304
|
+
"pool": "a Cursor Model in Cursor: billed from Cursor's own pool (research §2.4)",
|
|
305
|
+
"effort": "grok lists no efforts: Grok Build's effort names, low to xhigh (research §3.4, §5; live-verification §12)"
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
"id": "grok-4-6",
|
|
310
|
+
"name": "Grok 4.6",
|
|
311
|
+
"vendor": "xai",
|
|
312
|
+
"releaseDate": "2026-08-12",
|
|
313
|
+
"capabilities": { "toolUse": true, "imageIn": true, "reasoning": true },
|
|
314
|
+
"price": { "input": 2, "cached": 0.5, "output": 6 },
|
|
315
|
+
"on": {
|
|
316
|
+
"grok": { "id": "grok-4.6", "efforts": ["low", "medium", "high", "xhigh"], "context": 500000 },
|
|
317
|
+
"cursor": {
|
|
318
|
+
"id": "cursor-grok-4.6",
|
|
319
|
+
"efforts": ["low", "medium", "high", "xhigh"],
|
|
320
|
+
"context": 200000
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
"notes": {
|
|
324
|
+
"pool": "a Cursor Model in Cursor: billed from Cursor's own pool (research §2.4)",
|
|
325
|
+
"effort": "grok lists no efforts: Grok Build's effort names, low to xhigh (research §3.4, §5; live-verification §12)"
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
"id": "grok-4-5",
|
|
330
|
+
"name": "Grok 4.5",
|
|
331
|
+
"vendor": "xai",
|
|
332
|
+
"releaseDate": "2026-07-08",
|
|
333
|
+
"capabilities": { "toolUse": true, "imageIn": true, "reasoning": true },
|
|
334
|
+
"price": { "input": 2, "cached": 0.3, "output": 6 },
|
|
335
|
+
"on": {
|
|
336
|
+
"grok": { "id": "grok-4.5", "efforts": ["low", "medium", "high", "xhigh"], "context": 500000 },
|
|
337
|
+
"cursor": { "id": "cursor-grok-4.5", "efforts": ["low", "medium", "high"], "context": 200000 }
|
|
338
|
+
},
|
|
339
|
+
"notes": {
|
|
340
|
+
"pool": "a Cursor Model in Cursor: billed from Cursor's own pool (research §2.4)",
|
|
341
|
+
"effort": "grok lists no efforts: Grok Build's effort names, low to xhigh (research §3.4, §5; live-verification §12)"
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
"id": "composer-2-5",
|
|
346
|
+
"name": "Composer 2.5",
|
|
347
|
+
"vendor": "cursor",
|
|
348
|
+
"capabilities": { "toolUse": true, "imageIn": true, "reasoning": true },
|
|
349
|
+
"price": { "input": 2, "cached": 0.5, "output": 6 },
|
|
350
|
+
"on": { "cursor": { "id": "composer-2.5", "efforts": [], "context": 200000 } },
|
|
351
|
+
"notes": {
|
|
352
|
+
"price": "no keyless source prices Composer (research §5); Grok 4.7's, the same Cursor pool, stands in",
|
|
353
|
+
"effort": "no effort: its only rung is #default"
|
|
354
|
+
}
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
"id": "gemini-3-8-flash",
|
|
358
|
+
"name": "Gemini 3.8 Flash",
|
|
359
|
+
"vendor": "google",
|
|
360
|
+
"releaseDate": "2026-09-02",
|
|
361
|
+
"capabilities": { "toolUse": true, "imageIn": true, "reasoning": true },
|
|
362
|
+
"price": { "input": 0.75, "cached": 0.075, "output": 3.75 },
|
|
363
|
+
"on": {
|
|
364
|
+
"cursor": { "id": "gemini-3.8-flash", "efforts": ["low", "medium", "high"], "context": 200000 },
|
|
365
|
+
"antigravity": { "id": "gemini-3.8-flash", "efforts": ["low", "medium", "high"], "context": 1048576 }
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
"id": "gemini-3-7-flash",
|
|
370
|
+
"name": "Gemini 3.7 Flash",
|
|
371
|
+
"vendor": "google",
|
|
372
|
+
"releaseDate": "2026-08-13",
|
|
373
|
+
"capabilities": { "toolUse": true, "imageIn": true, "reasoning": true },
|
|
374
|
+
"price": { "input": 0.75, "cached": 0.075, "output": 3.75 },
|
|
375
|
+
"on": {
|
|
376
|
+
"cursor": { "id": "gemini-3.7-flash", "efforts": ["low", "medium", "high"], "context": 200000 },
|
|
377
|
+
"antigravity": { "id": "gemini-3.7-flash", "efforts": ["low", "medium", "high"], "context": 1048576 }
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
"id": "gemini-3-6-flash",
|
|
382
|
+
"name": "Gemini 3.6 Flash",
|
|
383
|
+
"vendor": "google",
|
|
384
|
+
"releaseDate": "2026-07-21",
|
|
385
|
+
"capabilities": { "toolUse": true, "imageIn": true, "reasoning": true },
|
|
386
|
+
"price": { "input": 0.75, "cached": 0.075, "output": 3.75 },
|
|
387
|
+
"on": {
|
|
388
|
+
"cursor": {
|
|
389
|
+
"id": "gemini-3.6-flash",
|
|
390
|
+
"efforts": ["minimal", "low", "medium", "high"],
|
|
391
|
+
"context": 200000
|
|
392
|
+
},
|
|
393
|
+
"antigravity": { "id": "gemini-3.6-flash", "efforts": ["low", "medium", "high"], "context": 1048576 }
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
"id": "gemini-3-1-pro",
|
|
398
|
+
"name": "Gemini 3.1 Pro",
|
|
399
|
+
"vendor": "google",
|
|
400
|
+
"releaseDate": "2026-02-19",
|
|
401
|
+
"capabilities": { "toolUse": true, "imageIn": true, "reasoning": true },
|
|
402
|
+
"price": { "input": 2, "cached": 0.2, "output": 12 },
|
|
403
|
+
"on": {
|
|
404
|
+
"cursor": { "id": "gemini-3.1-pro", "efforts": [], "context": 200000 },
|
|
405
|
+
"antigravity": { "id": "gemini-3.1-pro", "efforts": ["low", "high"], "context": 1048576 }
|
|
406
|
+
}
|
|
244
407
|
}
|
|
245
408
|
]
|
|
246
409
|
}
|
package/catalog/scores.json
CHANGED
|
@@ -3053,6 +3053,30 @@
|
|
|
3053
3053
|
"gpt-6-luna#max": {
|
|
3054
3054
|
"like": "gpt-5.6-luna#max",
|
|
3055
3055
|
"note": "no Arena agent score is published for GPT-6 Luna; GPT-5.6 Luna at the same effort stands in for agentic and steer (the nearest stand-in, spec 1.2 §6.3)"
|
|
3056
|
+
},
|
|
3057
|
+
"gpt-6.1-sol#low": {
|
|
3058
|
+
"like": "gpt-6-sol#low",
|
|
3059
|
+
"note": "no DeepSWE 1.1 or Arena score is published for GPT-6.1 Sol yet, and its only Terminal-Bench 4.0 value (vals.ai, 55.05 ± 1.82 at max) comes from a harness not comparable with GPT-6 Sol's Artificial Analysis one; the addendum's Broken Search Tool figure (2.08 % failure, https://deploymentsafety.openai.com/gpt-6-1-sol) is left out because a shipped honesty row would become the nearest honesty stand-in for unrelated sparse rungs; GPT-6 Sol at the same effort stands in for every dimension (the nearest stand-in, spec 1.2 §6.3)"
|
|
3060
|
+
},
|
|
3061
|
+
"gpt-6.1-sol#medium": {
|
|
3062
|
+
"like": "gpt-6-sol#medium",
|
|
3063
|
+
"note": "no DeepSWE 1.1 or Arena score is published for GPT-6.1 Sol yet, and its only Terminal-Bench 4.0 value (vals.ai, 55.05 ± 1.82 at max) comes from a harness not comparable with GPT-6 Sol's Artificial Analysis one; the addendum's Broken Search Tool figure (2.08 % failure, https://deploymentsafety.openai.com/gpt-6-1-sol) is left out because a shipped honesty row would become the nearest honesty stand-in for unrelated sparse rungs; GPT-6 Sol at the same effort stands in for every dimension (the nearest stand-in, spec 1.2 §6.3)"
|
|
3064
|
+
},
|
|
3065
|
+
"gpt-6.1-sol#high": {
|
|
3066
|
+
"like": "gpt-6-sol#high",
|
|
3067
|
+
"note": "no DeepSWE 1.1 or Arena score is published for GPT-6.1 Sol yet, and its only Terminal-Bench 4.0 value (vals.ai, 55.05 ± 1.82 at max) comes from a harness not comparable with GPT-6 Sol's Artificial Analysis one; the addendum's Broken Search Tool figure (2.08 % failure, https://deploymentsafety.openai.com/gpt-6-1-sol) is left out because a shipped honesty row would become the nearest honesty stand-in for unrelated sparse rungs; GPT-6 Sol at the same effort stands in for every dimension (the nearest stand-in, spec 1.2 §6.3)"
|
|
3068
|
+
},
|
|
3069
|
+
"gpt-6.1-sol#xhigh": {
|
|
3070
|
+
"like": "gpt-6-sol#xhigh",
|
|
3071
|
+
"note": "no DeepSWE 1.1 or Arena score is published for GPT-6.1 Sol yet, and its only Terminal-Bench 4.0 value (vals.ai, 55.05 ± 1.82 at max) comes from a harness not comparable with GPT-6 Sol's Artificial Analysis one; the addendum's Broken Search Tool figure (2.08 % failure, https://deploymentsafety.openai.com/gpt-6-1-sol) is left out because a shipped honesty row would become the nearest honesty stand-in for unrelated sparse rungs; GPT-6 Sol at the same effort stands in for every dimension (the nearest stand-in, spec 1.2 §6.3)"
|
|
3072
|
+
},
|
|
3073
|
+
"gpt-6.1-sol#max": {
|
|
3074
|
+
"like": "gpt-6-sol#max",
|
|
3075
|
+
"note": "no DeepSWE 1.1 or Arena score is published for GPT-6.1 Sol yet, and its only Terminal-Bench 4.0 value (vals.ai, 55.05 ± 1.82 at max) comes from a harness not comparable with GPT-6 Sol's Artificial Analysis one; the addendum's Broken Search Tool figure (2.08 % failure, https://deploymentsafety.openai.com/gpt-6-1-sol) is left out because a shipped honesty row would become the nearest honesty stand-in for unrelated sparse rungs; GPT-6 Sol at the same effort stands in for every dimension (the nearest stand-in, spec 1.2 §6.3)"
|
|
3076
|
+
},
|
|
3077
|
+
"gpt-6.1-sol#ultra": {
|
|
3078
|
+
"like": "gpt-6-sol#ultra",
|
|
3079
|
+
"note": "no DeepSWE 1.1 or Arena score is published for GPT-6.1 Sol yet, and its only Terminal-Bench 4.0 value (vals.ai, 55.05 ± 1.82 at max) comes from a harness not comparable with GPT-6 Sol's Artificial Analysis one; the addendum's Broken Search Tool figure (2.08 % failure, https://deploymentsafety.openai.com/gpt-6-1-sol) is left out because a shipped honesty row would become the nearest honesty stand-in for unrelated sparse rungs; GPT-6 Sol at the same effort stands in for every dimension (the nearest stand-in, spec 1.2 §6.3)"
|
|
3056
3080
|
}
|
|
3057
3081
|
},
|
|
3058
3082
|
"bars": {
|
package/catalog/sources.json
CHANGED
|
@@ -69,10 +69,12 @@
|
|
|
69
69
|
],
|
|
70
70
|
"aliases": {
|
|
71
71
|
"claude-4-5-haiku": "claude-haiku-4-5",
|
|
72
|
-
"claude-haiku-4-5-20251001": "claude-haiku-4-5"
|
|
72
|
+
"claude-haiku-4-5-20251001": "claude-haiku-4-5",
|
|
73
|
+
"gemini-3.1-pro-preview": "gemini-3-1-pro"
|
|
73
74
|
},
|
|
74
75
|
"defaultEffort": {
|
|
75
76
|
"gpt-6-astra": "medium",
|
|
77
|
+
"gpt-6.1-sol": "medium",
|
|
76
78
|
"gpt-6-sol": "medium",
|
|
77
79
|
"gpt-6-luna": "medium",
|
|
78
80
|
"gpt-5.6-sol": "medium",
|
|
@@ -80,7 +82,16 @@
|
|
|
80
82
|
"gpt-5.6-luna": "medium",
|
|
81
83
|
"claude-fable-5-1": "high",
|
|
82
84
|
"claude-opus-5-5": "high",
|
|
85
|
+
"claude-sonnet-5-5": "high",
|
|
83
86
|
"claude-sonnet-5": "high",
|
|
84
|
-
"claude-haiku-4-5": "default"
|
|
87
|
+
"claude-haiku-4-5": "default",
|
|
88
|
+
"grok-4-7": "high",
|
|
89
|
+
"grok-4-6": "high",
|
|
90
|
+
"grok-4-5": "high",
|
|
91
|
+
"composer-2-5": "default",
|
|
92
|
+
"gemini-3-8-flash": "high",
|
|
93
|
+
"gemini-3-7-flash": "high",
|
|
94
|
+
"gemini-3-6-flash": "high",
|
|
95
|
+
"gemini-3-1-pro": "high"
|
|
85
96
|
}
|
|
86
97
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "catherd-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Herds coding agents: autopilot builds from your own Claude Code session.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"autopilot",
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"format": "oxfmt",
|
|
41
41
|
"format:check": "oxfmt --check",
|
|
42
42
|
"tui-frames": "CATHERD_WRITE_FRAMES=1 bun test test/entry/tui/frames.test.tsx",
|
|
43
|
-
"version-packages": "changeset version && bun scripts/stamp-plugin-version.mjs"
|
|
43
|
+
"version-packages": "changeset version && bun scripts/stamp-plugin-version.mjs",
|
|
44
|
+
"release:publish": "npm view catherd-cli@$npm_package_version version >/dev/null 2>&1 || bun test/pack-smoke.ts && changeset publish"
|
|
44
45
|
},
|
|
45
46
|
"dependencies": {
|
|
46
47
|
"@modelcontextprotocol/sdk": "^1.30.1",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "catherd",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Herds coding agents: Claude plans and verifies, Codex, opencode or headless Claude Code workers write the code, and Jev picks the model and effort for each piece.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "47vigen",
|
package/plugin/bin/catherd-mcp
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
2
|
# catherd's MCP launcher (spec 1.1 §12): the plugin's .mcp.json runs it. The release stamps VERSION
|
|
3
3
|
# (scripts/stamp-plugin-version.mjs), so the plugin always starts the server it was released with.
|
|
4
|
-
VERSION="1.
|
|
4
|
+
VERSION="1.3.0"
|
|
5
5
|
|
|
6
6
|
# a global catherd at this version (catherd init installs one): start it, no resolve at all
|
|
7
7
|
if command -v catherd >/dev/null 2>&1 && [ "$(catherd --version 2>/dev/null)" = "$VERSION" ]; then
|
|
@@ -23,7 +23,7 @@ Your job:
|
|
|
23
23
|
|
|
24
24
|
The catherd server keeps `state.md` true: it rewrites it on every dispatch, climb and landing.
|
|
25
25
|
|
|
26
|
-
**Your first call is `status()`.** Its `version` must be the one this plugin pins, `catherd-cli@1.
|
|
26
|
+
**Your first call is `status()`.** Its `version` must be the one this plugin pins, `catherd-cli@1.3.0`. If it differs, stop and tell the user to restart Claude Code so the plugin and its server match.
|
|
27
27
|
|
|
28
28
|
**You never edit product files.** Every line of code, tests or docs comes from a role, including a one-line fix.
|
|
29
29
|
|
|
@@ -65,7 +65,7 @@ The catherd MCP tools ship with this plugin. They appear as `mcp__plugin_catherd
|
|
|
65
65
|
- `E_ADMIT_DUPLICATE`: that role name is already running. It reports through a catherd message when it finishes; `peek(run, name)` shows it now, and `cancel` stops it.
|
|
66
66
|
- `E_ADMIT_RUNG`: the rung is not on that role's ladder, it is a `claude:` rung, or the profile turns the role off. Use the rung `route` returned; run a `claude:` rung as its agent; skip a role that is off.
|
|
67
67
|
- `E_RUN_BUDGET`: the run's budget is spent (a soft cap: roles already running finish). Pause, report and push.
|
|
68
|
-
- `E_BACKEND_MISSING`, `E_BACKEND_NOT_LOGGED_IN`, `E_BACKEND_TOO_OLD`: tell the user the `fix`, word for word, then pause.
|
|
68
|
+
- `E_BACKEND_MISSING`, `E_BACKEND_NOT_LOGGED_IN`, `E_BACKEND_TOO_OLD`, `E_BACKEND_CANNOT_RUN`: tell the user the `fix`, word for word, then pause.
|
|
69
69
|
- `E_LANE_INVALID`: a lane file's `Kind:` or `Difficulty:` is missing or not one the catalog knows. Fix the header (the `fix` lists the values), or have the architect fix it, then call again.
|
|
70
70
|
- `E_LAND_GATE`: the milestone has no reviewer record or no verifier verdict since its lanes started, it is parked, or its `skip` does not hold. Run what the message names, then land again.
|
|
71
71
|
- `E_CLIMB_DESIGN`: the evidence points at the plan, not the rung. Send it to the architect (an `ask` finding, then an architect delta), not up the ladder.
|
|
@@ -274,7 +274,7 @@ The brief is the `brief` text you pass to `dispatch` (catherd writes it to the d
|
|
|
274
274
|
4. For a worker:
|
|
275
275
|
- "Read `<R>/lanes/Mx.Ly.md`": decisions, signatures, data shapes;
|
|
276
276
|
- its fast check (targeted tests, lint and type check), to run until it passes;
|
|
277
|
-
- "Run the full suite only if this brief says so", and "Wrap any full build or full test suite in `bunx catherd-cli@1.
|
|
277
|
+
- "Run the full suite only if this brief says so", and "Wrap any full build or full test suite in `bunx catherd-cli@1.3.0 lock -- <command>`": other lanes share the machine.
|
|
278
278
|
5. For a reviewer: the A-lines and the changed files, with new files read in full. It reports every finding as `BLOCKER|BUG|NIT file:line — problem — fix`, covering:
|
|
279
279
|
- unmet A-lines and edge cases;
|
|
280
280
|
- code or abstractions nobody needs;
|
|
@@ -21,7 +21,7 @@ You tune the user's catherd profile in conversation. A profile says, per role, w
|
|
|
21
21
|
Ask these, one at a time, each with its recommended answer:
|
|
22
22
|
|
|
23
23
|
1. **Their order of speed, cost and quality.** Recommend cost first, the default `objective`: catherd climbs a rung when a cheap one cannot do the work, so the lanes that need speed get it anyway, and the reviewer and the verifier hold quality either way.
|
|
24
|
-
2. **The subscriptions they hold:** a ChatGPT plan (Codex), a Claude plan, OpenCode Go, Zen credit or API keys. They set `billing` per key (`codex`, `claude`, `claude-code`, `opencode-go`, `opencode`): `chatgpt-plan`, `claude-plan`, `subscription` or `metered`. Recommend leaning on subscriptions before metered spend, and on Claude last among the workers, since it spends the same quota as this conversation.
|
|
24
|
+
2. **The subscriptions they hold:** a ChatGPT plan (Codex), a Claude plan, OpenCode Go, Zen credit or API keys. They set `billing` per key (`codex`, `claude`, `claude-code`, `opencode-go`, `opencode`, `cursor`, `grok`, `antigravity`): `chatgpt-plan`, `claude-plan`, `subscription` or `metered`. Recommend leaning on subscriptions before metered spend, and on Claude last among the workers, since it spends the same quota as this conversation.
|
|
25
25
|
3. **The kind of work they orchestrate:** front-end screens, back-end services, terminal and ops work, docs. Recommend from their own runs when `runs_summary` has any.
|
|
26
26
|
|
|
27
27
|
## 2. Read the facts before proposing
|
|
@@ -59,7 +59,7 @@ Each proposal has three parts: the change, a worked example from their facts, an
|
|
|
59
59
|
|
|
60
60
|
**Budget and timeouts.** `budget` (`minutes`, `tokens`, `usd`) is a soft cap: from 80 % routing starts at the cheapest rung that clears the bar, and at 100 % no new role starts. `timeouts.idleMin` (15) stops a role that has gone quiet, `timeouts.wallMin` (90) one that runs too long. `preflight.confirm: true` makes `preflight` show its commands for the user to approve first.
|
|
61
61
|
|
|
62
|
-
**Harness isolation.** For each harness they use (`codex`, `claude-code`, `opencode`), offer `harness.<name>.isolated` with its harness line from `runs_summary` (Codex has none: it reports no per-request input, so say there is no figure for it instead of offering one) and this tradeoff: "native keeps your hooks, skills and AGENTS.md; isolated saves ~N tokens per run, but the role loses them." Recommend native. When they have no isolated runs yet, the number is the median first-turn input of their native runs: say that isolation would save some part of it, not all of it. When there are no runs at all, say there is no number yet, and recommend native until there is.
|
|
62
|
+
**Harness isolation.** For each harness they use (`codex`, `claude-code`, `opencode`, `cursor`, `grok`, `antigravity`), offer `harness.<name>.isolated` with its harness line from `runs_summary` (Codex has none: it reports no per-request input, so say there is no figure for it instead of offering one) and this tradeoff: "native keeps your hooks, skills and AGENTS.md; isolated saves ~N tokens per run, but the role loses them." Recommend native. When they have no isolated runs yet, the number is the median first-turn input of their native runs: say that isolation would save some part of it, not all of it. When there are no runs at all, say there is no number yet, and recommend native until there is.
|
|
63
63
|
|
|
64
64
|
## 4. Write it
|
|
65
65
|
|
|
@@ -70,6 +70,6 @@ Each proposal has three parts: the change, a worked example from their facts, an
|
|
|
70
70
|
|
|
71
71
|
## 5. Say what applies when
|
|
72
72
|
|
|
73
|
-
- Codex, claude-code and
|
|
73
|
+
- Codex, claude-code, opencode, Cursor, grok and Antigravity changes, access and isolation included, apply at the next dispatch, even in a run already under way. Isolating Cursor needs `CURSOR_API_KEY` in the environment catherd runs in, isolating grok `XAI_API_KEY`, and isolating Antigravity `GEMINI_API_KEY`; `profile_validate` refuses each without. A read-only role (reviewer, architect, researcher) can run on Antigravity only isolated: agy has no read-only mode.
|
|
74
74
|
- An agent listed in `newSessionNeededFor` applies from the next Claude Code session: Claude Code reads agent files when a session starts. Other Claude changes apply now.
|
|
75
75
|
- Editing a profile that is not the active one writes its agent files but links none; they apply once it becomes active (`catherd profile use <name>`), or in a repo it is bound to (`catherd profile use <name> --repo`).
|
package/src/adapters/access.ts
CHANGED
|
@@ -1,25 +1,35 @@
|
|
|
1
1
|
import { existsSync, mkdtempSync, realpathSync, rmSync } from "node:fs";
|
|
2
2
|
import { homedir, tmpdir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import { locksDir } from "../infra/paths.ts";
|
|
4
|
+
import { configDir, dataDir, locksDir } from "../infra/paths.ts";
|
|
5
5
|
import type { AccessShell } from "./backend.ts";
|
|
6
6
|
import { runCli } from "./cli.ts";
|
|
7
7
|
|
|
8
8
|
/** How long one access probe may run: an HTTPS fetch through a slow proxy included. */
|
|
9
9
|
export const probeShell = { timeoutMs: 20_000 };
|
|
10
10
|
|
|
11
|
+
/** `argv` as one POSIX shell command line: each piece single-quoted, an embedded `'` as `'\''`. */
|
|
12
|
+
export const shellLine = (argv: string[]): string =>
|
|
13
|
+
argv.map((a) => `'${a.replaceAll("'", "'\\''")}'`).join(" ");
|
|
14
|
+
|
|
11
15
|
/**
|
|
12
16
|
* A probe shell from a fresh scratch dir: `prefix` is the argv before `sh -c <script> _ <args…>` (empty for
|
|
13
|
-
* a backend whose worker shell runs unsandboxed).
|
|
17
|
+
* a backend whose worker shell runs unsandboxed). `oneLine`: the runner joins its args with spaces and runs
|
|
18
|
+
* them in a shell, so `sh -c …` goes after `prefix` as one quoted command line.
|
|
14
19
|
*/
|
|
15
|
-
export function scratchShell(how: string, prefix: string[]): AccessShell {
|
|
20
|
+
export function scratchShell(how: string, prefix: string[], o: { oneLine?: boolean } = {}): AccessShell {
|
|
16
21
|
const cwd = mkdtempSync(join(realTmpdir(), "catherd-probe-"));
|
|
17
22
|
const [bin, ...rest] = prefix.length ? prefix : ["sh"];
|
|
18
23
|
const sh = prefix.length ? ["sh"] : [];
|
|
19
24
|
return {
|
|
20
25
|
how,
|
|
21
|
-
run: (script, args) =>
|
|
22
|
-
|
|
26
|
+
run: (script, args) => {
|
|
27
|
+
const cmd = [...sh, "-c", script, "_", ...args];
|
|
28
|
+
return runCli(bin as string, [...rest, ...(o.oneLine ? [shellLine(cmd)] : cmd)], {
|
|
29
|
+
...probeShell,
|
|
30
|
+
cwd,
|
|
31
|
+
});
|
|
32
|
+
},
|
|
23
33
|
close: () => rmSync(cwd, { recursive: true, force: true }),
|
|
24
34
|
};
|
|
25
35
|
}
|
|
@@ -63,6 +73,23 @@ const effectiveCaches = (query: ToolQuery): string[] =>
|
|
|
63
73
|
? (asked ??= [...query("go", ["env", "GOCACHE", "GOMODCACHE"]), ...query("pnpm", ["store", "path"])])
|
|
64
74
|
: [...query("go", ["env", "GOCACHE", "GOMODCACHE"]), ...query("pnpm", ["store", "path"])];
|
|
65
75
|
|
|
76
|
+
/** Each toolchain cache's env variable and its value: the env's, else the tool's default under `home`. */
|
|
77
|
+
function cacheVars(env: Record<string, string | undefined>, home: string): Record<string, string> {
|
|
78
|
+
const mac = process.platform === "darwin";
|
|
79
|
+
const cache = env.XDG_CACHE_HOME || join(home, ".cache");
|
|
80
|
+
const data = env.XDG_DATA_HOME || join(home, ".local", "share");
|
|
81
|
+
const gopath = (env.GOPATH || join(home, "go")).split(":")[0] as string;
|
|
82
|
+
return {
|
|
83
|
+
GOCACHE: env.GOCACHE || (mac ? join(home, "Library", "Caches", "go-build") : join(cache, "go-build")),
|
|
84
|
+
GOMODCACHE: env.GOMODCACHE || join(gopath, "pkg", "mod"),
|
|
85
|
+
npm_config_store_dir:
|
|
86
|
+
env.npm_config_store_dir ||
|
|
87
|
+
(mac ? join(home, "Library", "pnpm", "store") : join(data, "pnpm", "store")),
|
|
88
|
+
BUN_INSTALL_CACHE_DIR: env.BUN_INSTALL_CACHE_DIR || join(home, ".bun", "install", "cache"),
|
|
89
|
+
npm_config_cache: env.npm_config_cache || join(home, ".npm"),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
66
93
|
/**
|
|
67
94
|
* The toolchain caches a worker's checks write, where they exist: Go's build and module caches and the pnpm store
|
|
68
95
|
* (as the tools themselves report them, else their defaults), Bun's install cache and npm's cache. Without them a
|
|
@@ -73,21 +100,31 @@ export function toolchainCaches(
|
|
|
73
100
|
home = env.HOME || homedir(),
|
|
74
101
|
query: ToolQuery = queryTool,
|
|
75
102
|
): string[] {
|
|
76
|
-
const
|
|
77
|
-
const cache = env.XDG_CACHE_HOME || join(home, ".cache");
|
|
78
|
-
const data = env.XDG_DATA_HOME || join(home, ".local", "share");
|
|
79
|
-
const gopath = (env.GOPATH || join(home, "go")).split(":")[0] as string;
|
|
80
|
-
const paths = [
|
|
81
|
-
...effectiveCaches(query),
|
|
82
|
-
env.GOCACHE || (mac ? join(home, "Library", "Caches", "go-build") : join(cache, "go-build")),
|
|
83
|
-
env.GOMODCACHE || join(gopath, "pkg", "mod"),
|
|
84
|
-
env.npm_config_store_dir || (mac ? join(home, "Library", "pnpm", "store") : join(data, "pnpm", "store")),
|
|
85
|
-
env.BUN_INSTALL_CACHE_DIR || join(home, ".bun", "install", "cache"),
|
|
86
|
-
env.npm_config_cache || join(home, ".npm"),
|
|
87
|
-
];
|
|
103
|
+
const paths = [...effectiveCaches(query), ...Object.values(cacheVars(env, home))];
|
|
88
104
|
return [...new Set(paths.filter((p) => existsSync(p)).map(realOr))];
|
|
89
105
|
}
|
|
90
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Spec 1.3 §4.4: the env of an isolated worker whose CLI is isolated by a HOME of catherd's (Cursor reads
|
|
109
|
+
* `~/.claude` whatever its own config dir says). catherd's own config and data stay put, so a `catherd lock`
|
|
110
|
+
* in the worker takes the same lock dir, and so do the toolchain caches `writableRoots` grants, which would
|
|
111
|
+
* otherwise start cold under the new HOME, where no sandbox lets the worker write.
|
|
112
|
+
* ponytail: only these caches follow; other dotfiles (~/.npmrc, ~/.gitconfig, ~/.cargo) stay behind.
|
|
113
|
+
*/
|
|
114
|
+
export function movedHomeEnv(
|
|
115
|
+
home: string,
|
|
116
|
+
env: Record<string, string | undefined> = process.env,
|
|
117
|
+
realHome = env.HOME || homedir(),
|
|
118
|
+
): Record<string, string> {
|
|
119
|
+
return {
|
|
120
|
+
HOME: home,
|
|
121
|
+
CATHERD_CONFIG_DIR: configDir(),
|
|
122
|
+
CATHERD_DATA_DIR: dataDir(),
|
|
123
|
+
GOPATH: env.GOPATH || join(realHome, "go"),
|
|
124
|
+
...cacheVars(env, realHome),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
91
128
|
/**
|
|
92
129
|
* The directories a workspace-write worker writes besides the repo: the heavy-lock dir, the temp dir and the
|
|
93
130
|
* toolchain caches that exist, real paths.
|
package/src/adapters/all.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
import { antigravityAdapter } from "./antigravity/index.ts";
|
|
1
2
|
import { claudeCodeAdapter } from "./claude-code/index.ts";
|
|
2
3
|
import { codexAdapter } from "./codex/index.ts";
|
|
4
|
+
import { cursorAdapter } from "./cursor/index.ts";
|
|
5
|
+
import { grokAdapter } from "./grok/index.ts";
|
|
3
6
|
import { opencodeAdapter } from "./opencode/index.ts";
|
|
4
7
|
import { registerAdapter } from "./registry.ts";
|
|
5
8
|
|
|
6
9
|
registerAdapter(codexAdapter);
|
|
7
10
|
registerAdapter(claudeCodeAdapter);
|
|
8
11
|
registerAdapter(opencodeAdapter);
|
|
12
|
+
registerAdapter(cursorAdapter);
|
|
13
|
+
registerAdapter(grokAdapter);
|
|
14
|
+
registerAdapter(antigravityAdapter);
|