catherd-cli 1.2.1 → 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.
Files changed (59) hide show
  1. package/README.md +74 -3
  2. package/catalog/models.json +148 -5
  3. package/catalog/scores.json +24 -0
  4. package/catalog/sources.json +12 -2
  5. package/package.json +3 -2
  6. package/plugin/.claude-plugin/plugin.json +1 -1
  7. package/plugin/bin/catherd-mcp +1 -1
  8. package/plugin/skills/catherd/SKILL.md +3 -3
  9. package/plugin/skills/catherd-setup/SKILL.md +3 -3
  10. package/src/adapters/access.ts +54 -17
  11. package/src/adapters/all.ts +6 -0
  12. package/src/adapters/antigravity/events.ts +131 -0
  13. package/src/adapters/antigravity/home.ts +46 -0
  14. package/src/adapters/antigravity/index.ts +320 -0
  15. package/src/adapters/antigravity/models.ts +37 -0
  16. package/src/adapters/backend.ts +34 -1
  17. package/src/adapters/claude-code/index.ts +2 -0
  18. package/src/adapters/codex/index.ts +2 -0
  19. package/src/adapters/cursor/events.ts +120 -0
  20. package/src/adapters/cursor/home.ts +79 -0
  21. package/src/adapters/cursor/index.ts +331 -0
  22. package/src/adapters/cursor/models.ts +30 -0
  23. package/src/adapters/discovery.ts +21 -0
  24. package/src/adapters/grok/events.ts +112 -0
  25. package/src/adapters/grok/home.ts +132 -0
  26. package/src/adapters/grok/index.ts +339 -0
  27. package/src/adapters/grok/models.ts +48 -0
  28. package/src/adapters/opencode/index.ts +2 -0
  29. package/src/cli.ts +1 -0
  30. package/src/domain/catalog.ts +10 -1
  31. package/src/domain/cost.ts +1 -0
  32. package/src/domain/errors.ts +1 -0
  33. package/src/domain/failover.ts +5 -2
  34. package/src/domain/ids.ts +2 -2
  35. package/src/domain/profile.ts +57 -0
  36. package/src/domain/record.ts +2 -0
  37. package/src/domain/util.ts +3 -0
  38. package/src/entry/knowledge-command.ts +50 -0
  39. package/src/entry/tui/effects.ts +12 -3
  40. package/src/entry/tui/fixtures.ts +1 -0
  41. package/src/entry/tui/profile-tree.ts +10 -1
  42. package/src/entry/tui/views/profiles.tsx +14 -1
  43. package/src/infra/paths.ts +3 -1
  44. package/src/services/admission.ts +27 -4
  45. package/src/services/backends.ts +42 -4
  46. package/src/services/capture.ts +112 -54
  47. package/src/services/catalog-refresh.ts +2 -1
  48. package/src/services/dispatches.ts +2 -0
  49. package/src/services/doctor-access.ts +14 -7
  50. package/src/services/doctor-backends.ts +10 -1
  51. package/src/services/doctor-checks.ts +25 -0
  52. package/src/services/doctor.ts +3 -0
  53. package/src/services/finalize.ts +1 -0
  54. package/src/services/lane-service.ts +3 -10
  55. package/src/services/profile-service.ts +3 -3
  56. package/src/services/profile-store.ts +106 -2
  57. package/src/services/run-service.ts +37 -4
  58. package/src/services/run-store.ts +11 -1
  59. 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 or
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`) |
@@ -1,10 +1,12 @@
1
1
  {
2
2
  "schema": 1,
3
- "version": "2026-09-25",
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,6 +227,11 @@
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" }
@@ -261,6 +288,122 @@
261
288
  "opencode": { "id": "opencode/claude-haiku-4-5", "efforts": ["high", "max"], "context": 200000 }
262
289
  },
263
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
+ }
264
407
  }
265
408
  ]
266
409
  }
@@ -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": {
@@ -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",
@@ -82,6 +84,14 @@
82
84
  "claude-opus-5-5": "high",
83
85
  "claude-sonnet-5-5": "high",
84
86
  "claude-sonnet-5": "high",
85
- "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"
86
96
  }
87
97
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "catherd-cli",
3
- "version": "1.2.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.2.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",
@@ -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.2.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.2.1`. If it differs, stop and tell the user to restart Claude Code so the plugin and its server match.
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.2.1 lock -- <command>`": other lanes share the machine.
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 opencode changes, access and isolation included, apply at the next dispatch, even in a run already under way.
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`).
@@ -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
- runCli(bin as string, [...rest, ...sh, "-c", script, "_", ...args], { ...probeShell, cwd }),
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 mac = process.platform === "darwin";
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.
@@ -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);