catherd-cli 1.1.1 → 1.2.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.
Files changed (62) hide show
  1. package/README.md +40 -14
  2. package/catalog/ATTRIBUTION.md +36 -0
  3. package/catalog/models.json +30 -0
  4. package/catalog/scores.json +3006 -194
  5. package/catalog/sources.json +87 -0
  6. package/package.json +1 -1
  7. package/plugin/.claude-plugin/plugin.json +1 -1
  8. package/plugin/bin/catherd-mcp +1 -1
  9. package/plugin/skills/catherd/SKILL.md +2 -2
  10. package/src/adapters/claude-code/models.ts +2 -1
  11. package/src/domain/bars.ts +119 -0
  12. package/src/domain/calibration.ts +125 -0
  13. package/src/domain/catalog.ts +210 -23
  14. package/src/domain/failover.ts +14 -1
  15. package/src/domain/profile-rules.ts +108 -18
  16. package/src/domain/sources.ts +144 -0
  17. package/src/entry/catalog-command.ts +163 -8
  18. package/src/entry/init-command.ts +72 -2
  19. package/src/entry/mcp/lane-tools.ts +1 -1
  20. package/src/entry/mcp/server.ts +13 -3
  21. package/src/entry/mcp/setup-tools.ts +24 -2
  22. package/src/entry/profile-command.ts +10 -4
  23. package/src/entry/tui/commands.ts +20 -2
  24. package/src/entry/tui/effects.ts +50 -1
  25. package/src/entry/tui/fixtures.ts +31 -3
  26. package/src/entry/tui/profile-edits.ts +17 -9
  27. package/src/entry/tui/profile-tree.ts +2 -2
  28. package/src/entry/tui/state.ts +4 -0
  29. package/src/entry/tui/views/profile-actions.ts +9 -1
  30. package/src/entry/tui/views/profiles.tsx +81 -14
  31. package/src/entry/tui/views/save-dialog.tsx +12 -4
  32. package/src/infra/env.ts +2 -0
  33. package/src/infra/jev-client.ts +88 -40
  34. package/src/infra/paths.ts +2 -0
  35. package/src/infra/sources/arena.ts +87 -0
  36. package/src/infra/sources/artificial-analysis.ts +112 -0
  37. package/src/infra/sources/cache.ts +124 -0
  38. package/src/infra/sources/csv.ts +38 -0
  39. package/src/infra/sources/epoch.ts +82 -0
  40. package/src/infra/sources/http.ts +75 -0
  41. package/src/infra/sources/litellm.ts +58 -0
  42. package/src/infra/sources/models-dev.ts +59 -0
  43. package/src/infra/sources/openrouter-endpoints.ts +52 -0
  44. package/src/infra/sources/openrouter-models.ts +60 -0
  45. package/src/infra/sources/rows.ts +38 -0
  46. package/src/infra/sources/vectara.ts +69 -0
  47. package/src/infra/sources/zip.ts +48 -0
  48. package/src/services/catalog-refresh.ts +200 -0
  49. package/src/services/catalog-service.ts +83 -23
  50. package/src/services/credentials.ts +69 -0
  51. package/src/services/doctor-sources.ts +76 -0
  52. package/src/services/doctor.ts +10 -0
  53. package/src/services/jev-service.ts +14 -41
  54. package/src/services/ports.ts +6 -0
  55. package/src/services/profile-service.ts +8 -6
  56. package/src/services/provenance.ts +115 -0
  57. package/src/services/routing-service.ts +7 -2
  58. package/src/services/run-evidence.ts +86 -0
  59. package/src/services/source-derive.ts +349 -0
  60. package/src/services/source-sync.ts +305 -0
  61. package/src/services/standins.ts +204 -0
  62. package/src/services/treat-likes.ts +84 -0
package/README.md CHANGED
@@ -35,6 +35,8 @@ of work, climbing a ladder only when a cheaper rung falls short.
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
37
  - Optional: a TypeSafe API key for Jev, in `TYPESAFE_API_KEY` or saved by `catherd init`
38
+ - Optional: a free [Artificial Analysis](https://artificialanalysis.ai) API key for more scores, in
39
+ `ARTIFICIAL_ANALYSIS_API_KEY` or saved by `catherd init`; its numbers are read for you alone and never shipped
38
40
 
39
41
  `catherd doctor` checks each backend's version and login and prints the fix for anything missing. The default
40
42
  profile runs its workers on Codex; without Codex, doctor's fix also names how to move those roles to a backend
@@ -50,10 +52,11 @@ catherd init
50
52
  The npm package is `catherd-cli`; the command it installs is `catherd`. `bunx catherd-cli init` works too: `init`
51
53
  installs the global command at its own version (`--no-global` skips it) and says `installing catherd…` before
52
54
  it does, though the first `bunx` resolve itself prints nothing for up to half a minute. `init` asks for the
53
- optional Jev key, writes the default profile and links its Claude agents, lists your backends' models, and ends
54
- with a readiness report (`--no-input` asks nothing and keeps what exists; `--profile <name>` sets up and activates
55
- that profile instead of `default`; piped, it reads one answer per line once stdin closes: the Jev key, the
56
- profile, whether to replace it, each on its own line even when a question is skipped). Then add the plugin to
55
+ optional Jev key and the optional Artificial Analysis key, syncs the public model sources, writes the default
56
+ profile and links its Claude agents, lists your backends' models, and ends with a readiness report (`--no-input`
57
+ asks nothing and keeps what exists; `--profile <name>` sets up and activates that profile instead of `default`;
58
+ piped, it reads one answer per line once stdin closes: the Jev key, the Artificial Analysis key, the profile,
59
+ whether to replace it, each on its own line even when a question is skipped). Then add the plugin to
57
60
  Claude Code:
58
61
 
59
62
  ```sh
@@ -87,7 +90,9 @@ In a terminal:
87
90
  | `catherd status [run]`, `catherd watch [--once] [--interval <s>]` | Where runs stand, grouped by the Claude Code session that drove them |
88
91
  | `catherd runs list [--repo <path>]\|show <id> [--debug [--name <n>]]\|cancel <id> <name>` | Past runs, by session; `--debug` adds exit.json and the stderr and event tails, `--name` one role's |
89
92
  | `catherd catalog refresh\|list [--backend <b>] [--role <r>] [--text <t>] [--scored]` | The models catherd can place, filtered |
93
+ | `catherd catalog sync [--force] [--unmatched]` | Fetches the public model facts and scores now (below); `--unmatched` lists ids no model matched |
90
94
  | `catherd catalog treat-like <rung> <like>` | Scores an unscored rung as a scored one |
95
+ | `catherd catalog treat-like --suggest <rung>\|--clear <rung>\|--reset` | The three nearest stand-ins for a rung; removes one or every mapping of yours |
91
96
  | `catherd lock [--slots N] -- <cmd>` | Runs a heavy command behind the machine-wide semaphore, in its own session (no /dev/tty) |
92
97
  | `catherd mcp` | The MCP server on stdio; the plugin starts it, you never need to |
93
98
  | `catherd capture-fixtures [--backend <b>] [--out <dir>]` | Contributors: records sanitized test fixtures from real runs (see CONTRIBUTING.md) |
@@ -99,21 +104,42 @@ on the profile the repo you are in runs on: the one bound to it, else the active
99
104
  `~/.local/share/catherd/logs/`, kept for 7 days with secrets redacted. The dashboard takes `--plain` (ASCII, no colour)
100
105
  and `--reduced-motion`; `doctor` and `init` take `--plain` for ASCII glyphs too. `NO_COLOR` drops colour, never glyphs.
101
106
 
107
+ Model facts and scores also come from public sources: models.dev, OpenRouter, LiteLLM, Arena (LMArena), Vectara's
108
+ hallucination leaderboard and Epoch AI, plus Artificial Analysis when you give `catherd init` a free key. The MCP
109
+ server syncs them in the background when a Claude Code session starts (each source at most every 12 hours, never
110
+ delaying the session), `init` syncs them, and `catherd catalog sync` (or the `catalog_sync` tool) does it on demand.
111
+ Each answer is kept in `~/.local/share/catherd/sources/`; a source that fails keeps its last good answer, and with no
112
+ network and no sync at all catherd routes on the scores it ships. Those are the keyless sources' values, refreshed
113
+ weekly (`catalog/ATTRIBUTION.md` credits each source); Artificial Analysis values are read with your key only and
114
+ never shipped.
115
+
116
+ A lane's kind and difficulty pick a bar: a threshold on each dimension it spans (`repo_code`, `terminal`,
117
+ `honesty`, `agentic`, `frontend`; `steer` is shown but has no default bar), and the lane starts on the cheapest
118
+ rung that clears them all. A rung with no value on a dimension takes its nearest stand-in's as `inferred`, and
119
+ `profile validate` and `doctor` list it as a "stand-in to confirm": confirm or replace it with `catherd catalog
120
+ treat-like --suggest <rung>`, then `treat-like <rung> <like>`. `route` says, for the rung it picks, each threshold,
121
+ the value used, its confidence and source, and catherd's own runs on it ("12 lanes, 2 climbed, 1 partial"), which
122
+ it shows but never routes on. A bar of your own goes in `~/.config/catherd/catalog.override.json`, per dimension
123
+ (`"bars": { "ui": { "hard": { "frontend": 1700, "honesty": null } } }`: a number sets a threshold, `null` removes
124
+ the default's).
125
+
102
126
  Run data lives in `~/.local/share/catherd/`, config in `~/.config/catherd/` (both follow
103
127
  `XDG_*`).
104
128
 
105
129
  ### Environment variables
106
130
 
107
- | Variable | What it does |
108
- | --------------------------- | ----------------------------------------------------------------------------------------------------- |
109
- | `TYPESAFE_API_KEY` | The Jev key, instead of the one `catherd init` saves |
110
- | `CATHERD_HOME` | Puts config and data under `$CATHERD_HOME/config` and `$CATHERD_HOME/data` instead of XDG |
111
- | `CATHERD_LOG` | Log level: `off`, `error`, `warn`, `info` (default) or `debug` (what `--verbose` sets) |
112
- | `CATHERD_LOCK_SLOTS` | `catherd lock`'s slot count when `--slots` is not given (before the profile's `lock.heavy`) |
113
- | `CATHERD_REDUCED_MOTION` | Any value: the dashboard's `--reduced-motion` |
114
- | `CATHERD_NO_KITTY` | Any value: turns off the kitty keyboard protocol in the dashboard, for terminals it breaks |
115
- | `CATHERD_CLAUDE_AGENTS_DIR` | Where catherd links its Claude agents (default: `$CLAUDE_CONFIG_DIR/agents`, else `~/.claude/agents`) |
116
- | `NO_COLOR` | Drops colour (the dashboard's and `--help`'s; piped `--help` has none either) |
131
+ | Variable | What it does |
132
+ | ----------------------------- | ----------------------------------------------------------------------------------------------------------- |
133
+ | `TYPESAFE_API_KEY` | The Jev key, instead of the one `catherd init` saves |
134
+ | `ARTIFICIAL_ANALYSIS_API_KEY` | An Artificial Analysis key for `catalog sync`, instead of the one `catherd init` saves |
135
+ | `CATHERD_NO_SYNC` | `1`: no automatic sync of the public sources (at MCP server start and in `init`); `catalog sync` still runs |
136
+ | `CATHERD_HOME` | Puts config and data under `$CATHERD_HOME/config` and `$CATHERD_HOME/data` instead of XDG |
137
+ | `CATHERD_LOG` | Log level: `off`, `error`, `warn`, `info` (default) or `debug` (what `--verbose` sets) |
138
+ | `CATHERD_LOCK_SLOTS` | `catherd lock`'s slot count when `--slots` is not given (before the profile's `lock.heavy`) |
139
+ | `CATHERD_REDUCED_MOTION` | Any value: the dashboard's `--reduced-motion` |
140
+ | `CATHERD_NO_KITTY` | Any value: turns off the kitty keyboard protocol in the dashboard, for terminals it breaks |
141
+ | `CATHERD_CLAUDE_AGENTS_DIR` | Where catherd links its Claude agents (default: `$CLAUDE_CONFIG_DIR/agents`, else `~/.claude/agents`) |
142
+ | `NO_COLOR` | Drops colour (the dashboard's and `--help`'s; piped `--help` has none either) |
117
143
 
118
144
  For development only: `CATHERD_STORY=1` opens the dashboard's storybook, and `CATHERD_LIVE=1` enables the live
119
145
  tests (CONTRIBUTING.md has the rest).
@@ -0,0 +1,36 @@
1
+ # Attribution for the shipped catalog data
2
+
3
+ `catalog/scores.json` carries model scores from public sources. Each value names its source (`source`), the page
4
+ it came from (`url`), its date and its confidence. The weekly `catalog-refresh` workflow rebuilds the values that
5
+ come from the keyless sources below; the hand-typed values (no `source`) cite their own vendor or benchmark page in
6
+ their `url`.
7
+
8
+ ## Sources whose values are shipped
9
+
10
+ ### Arena (LMArena)
11
+
12
+ - Data: `lmarena-ai/leaderboard-dataset` on Hugging Face, configs `agent`, `agent_task_outcome_explicit`,
13
+ `agent_bash_recovery_steps`, `agent_steerability`, `agent_tool_hallucination`, `webdev`
14
+ - License: CC BY 4.0
15
+ - Attribution: Leaderboard data by LMArena, CC BY 4.0, https://huggingface.co/datasets/lmarena-ai/leaderboard-dataset
16
+
17
+ ### Epoch AI benchmarks
18
+
19
+ - Data: https://epoch.ai/data/benchmark_data.zip (FrontierCode, Terminal-Bench and WebDev Arena tables)
20
+ - License: CC BY 4.0; external tables keep their own license
21
+ - Attribution: Epoch AI, 'Capabilities & benchmarking'. Published online at epoch.ai. Retrieved from 'https://epoch.ai/benchmarks' (CC BY 4.0)
22
+
23
+ ### Vectara hallucination leaderboard
24
+
25
+ - Data: the README table of https://github.com/vectara/hallucination-leaderboard
26
+ - License: Apache License 2.0
27
+ - Attribution: Hallucination Leaderboard by Vectara (Apache License 2.0), https://github.com/vectara/hallucination-leaderboard
28
+
29
+ ## Sources read at run time only
30
+
31
+ catherd reads these on the user's machine and ships none of their data: models.dev (MIT License,
32
+ https://models.dev), the OpenRouter API (https://openrouter.ai) and LiteLLM's
33
+ `model_prices_and_context_window.json` (MIT License, https://github.com/BerriAI/litellm).
34
+
35
+ Artificial Analysis (https://artificialanalysis.ai) is read only with the user's own key, and its values are never
36
+ shipped.
@@ -20,6 +20,7 @@
20
20
  "id": "gpt-6-astra",
21
21
  "name": "GPT-6 Astra",
22
22
  "vendor": "openai",
23
+ "releaseDate": "2026-09-04",
23
24
  "capabilities": { "toolUse": true, "imageIn": true, "reasoning": true },
24
25
  "price": { "input": 10, "cached": 1, "output": 50 },
25
26
  "planWeight": 60,
@@ -43,6 +44,7 @@
43
44
  "id": "gpt-6-sol",
44
45
  "name": "GPT-6 Sol",
45
46
  "vendor": "openai",
47
+ "releaseDate": "2026-09-22",
46
48
  "capabilities": { "toolUse": true, "imageIn": true, "reasoning": true },
47
49
  "price": { "input": 2, "cached": 0.2, "output": 10 },
48
50
  "planWeight": 20,
@@ -66,6 +68,7 @@
66
68
  "id": "gpt-6-luna",
67
69
  "name": "GPT-6 Luna",
68
70
  "vendor": "openai",
71
+ "releaseDate": "2026-09-22",
69
72
  "capabilities": { "toolUse": true, "imageIn": true, "reasoning": true },
70
73
  "price": { "input": 0.1, "cached": 0.01, "output": 0.5 },
71
74
  "planWeight": 1,
@@ -91,6 +94,7 @@
91
94
  "id": "gpt-5.6-sol",
92
95
  "name": "GPT-5.6 Sol",
93
96
  "vendor": "openai",
97
+ "releaseDate": "2026-07-09",
94
98
  "status": "legacy",
95
99
  "capabilities": { "toolUse": true, "imageIn": true, "reasoning": true },
96
100
  "price": { "input": 4, "cached": 0.4, "output": 20 },
@@ -113,6 +117,7 @@
113
117
  "id": "gpt-5.6-terra",
114
118
  "name": "GPT-5.6 Terra",
115
119
  "vendor": "openai",
120
+ "releaseDate": "2026-07-09",
116
121
  "status": "legacy",
117
122
  "capabilities": { "toolUse": true, "imageIn": true, "reasoning": true },
118
123
  "price": { "input": 2, "cached": 0.2, "output": 12 },
@@ -135,6 +140,7 @@
135
140
  "id": "gpt-5.6-luna",
136
141
  "name": "GPT-5.6 Luna",
137
142
  "vendor": "openai",
143
+ "releaseDate": "2026-07-09",
138
144
  "status": "legacy",
139
145
  "capabilities": { "toolUse": true, "imageIn": true, "reasoning": true },
140
146
  "price": { "input": 0.2, "cached": 0.02, "output": 1.2 },
@@ -162,6 +168,7 @@
162
168
  "id": "claude-fable-5-1",
163
169
  "name": "Claude Fable 5.1",
164
170
  "vendor": "anthropic",
171
+ "releaseDate": "2026-09-01",
165
172
  "capabilities": { "toolUse": true, "imageIn": true, "reasoning": true },
166
173
  "price": { "input": 10, "cached": 0.25, "output": 50 },
167
174
  "meteredOnPlan": true,
@@ -185,6 +192,7 @@
185
192
  "id": "claude-opus-5-5",
186
193
  "name": "Claude Opus 5.5",
187
194
  "vendor": "anthropic",
195
+ "releaseDate": "2026-09-22",
188
196
  "capabilities": { "toolUse": true, "imageIn": true, "reasoning": true },
189
197
  "price": { "input": 4, "cached": 0.2, "output": 20 },
190
198
  "on": {
@@ -201,10 +209,31 @@
201
209
  },
202
210
  "notes": { "effort": "the API default effort is medium" }
203
211
  },
212
+ {
213
+ "id": "claude-sonnet-5-5",
214
+ "name": "Claude Sonnet 5.5",
215
+ "vendor": "anthropic",
216
+ "releaseDate": "2026-09-28",
217
+ "capabilities": { "toolUse": true, "imageIn": true, "reasoning": true },
218
+ "price": { "input": 2, "cached": 0.2, "output": 10 },
219
+ "on": {
220
+ "claude-code": {
221
+ "id": "claude-sonnet-5-5",
222
+ "efforts": ["low", "medium", "high", "xhigh", "max"],
223
+ "context": 1000000
224
+ },
225
+ "opencode": {
226
+ "id": "opencode/claude-sonnet-5-5",
227
+ "efforts": ["low", "medium", "high", "xhigh", "max"],
228
+ "context": 1000000
229
+ }
230
+ }
231
+ },
204
232
  {
205
233
  "id": "claude-sonnet-5",
206
234
  "name": "Claude Sonnet 5",
207
235
  "vendor": "anthropic",
236
+ "releaseDate": "2026-06-29",
208
237
  "capabilities": { "toolUse": true, "imageIn": true, "reasoning": true },
209
238
  "price": { "input": 2, "cached": 0.2, "output": 10 },
210
239
  "on": {
@@ -224,6 +253,7 @@
224
253
  "id": "claude-haiku-4-5",
225
254
  "name": "Claude Haiku 4.5",
226
255
  "vendor": "anthropic",
256
+ "releaseDate": "2025-10-15",
227
257
  "capabilities": { "toolUse": true, "imageIn": true, "reasoning": true },
228
258
  "price": { "input": 1, "cached": 0.1, "output": 5 },
229
259
  "on": {