catherd-cli 0.2.1 → 1.1.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 (207) hide show
  1. package/README.md +141 -33
  2. package/THIRD_PARTY_NOTICES.md +37 -0
  3. package/catalog/jev.json +89 -0
  4. package/catalog/models.json +236 -0
  5. package/catalog/scores.json +366 -0
  6. package/package.json +24 -8
  7. package/plugin/.claude-plugin/plugin.json +2 -2
  8. package/plugin/.mcp.json +2 -2
  9. package/plugin/bin/catherd-mcp +30 -0
  10. package/plugin/commands/catherd.md +2 -2
  11. package/plugin/skills/catherd/SKILL.md +160 -97
  12. package/plugin/skills/catherd-setup/SKILL.md +34 -24
  13. package/src/adapters/access.ts +63 -0
  14. package/src/adapters/all.ts +8 -0
  15. package/src/adapters/backend.ts +159 -0
  16. package/src/adapters/claude-code/events.ts +86 -0
  17. package/src/adapters/claude-code/index.ts +317 -0
  18. package/src/adapters/claude-code/models-api.ts +65 -0
  19. package/src/adapters/claude-code/models.ts +26 -0
  20. package/src/adapters/cli.ts +74 -0
  21. package/src/adapters/codex/events.ts +65 -0
  22. package/src/adapters/codex/home.ts +47 -0
  23. package/src/adapters/codex/index.ts +282 -0
  24. package/src/adapters/discovery.ts +87 -0
  25. package/src/adapters/opencode/agents.ts +84 -0
  26. package/src/adapters/opencode/events.ts +109 -0
  27. package/src/adapters/opencode/index.ts +333 -0
  28. package/src/adapters/registry.ts +14 -0
  29. package/src/cli.ts +159 -19
  30. package/src/domain/agents.ts +60 -0
  31. package/src/domain/budget.ts +45 -0
  32. package/src/domain/catalog.ts +262 -0
  33. package/src/domain/changes.ts +52 -0
  34. package/src/domain/cost.ts +89 -0
  35. package/src/domain/errors.ts +60 -0
  36. package/src/domain/failover.ts +119 -0
  37. package/src/domain/hints.ts +22 -0
  38. package/src/domain/ids.ts +97 -0
  39. package/src/domain/jev.ts +283 -0
  40. package/src/domain/lane.ts +92 -0
  41. package/src/domain/notice.ts +95 -0
  42. package/src/domain/profile-rules.ts +239 -0
  43. package/src/domain/profile.ts +389 -0
  44. package/src/domain/record.ts +86 -0
  45. package/src/{profile → domain}/role-prompts.ts +75 -27
  46. package/src/domain/roles.ts +25 -0
  47. package/src/domain/route.ts +126 -0
  48. package/src/domain/runtime.ts +21 -0
  49. package/src/domain/sanitize.ts +29 -0
  50. package/src/domain/secrets.ts +57 -0
  51. package/src/domain/select.ts +136 -0
  52. package/src/domain/state.ts +49 -0
  53. package/src/domain/util.ts +18 -0
  54. package/src/entry/capture-fixtures-command.ts +57 -0
  55. package/src/entry/catalog-command.ts +106 -0
  56. package/src/entry/cli-kit.ts +36 -0
  57. package/src/entry/deps.ts +17 -0
  58. package/src/entry/doctor-command.ts +51 -0
  59. package/src/entry/glyphs.ts +34 -0
  60. package/src/entry/init-command.ts +182 -0
  61. package/src/entry/lock-command.ts +123 -0
  62. package/src/{mcp/commands.ts → entry/mcp/command.ts} +5 -1
  63. package/src/entry/mcp/dispatch-tools.ts +52 -0
  64. package/src/entry/mcp/handshake.ts +58 -0
  65. package/src/entry/mcp/lane-tools.ts +84 -0
  66. package/src/entry/mcp/protocol-tools.ts +57 -0
  67. package/src/entry/mcp/result.ts +51 -0
  68. package/src/entry/mcp/run-tools.ts +145 -0
  69. package/src/entry/mcp/server.ts +88 -0
  70. package/src/entry/mcp/setup-tools.ts +81 -0
  71. package/src/entry/profile-command.ts +318 -0
  72. package/src/entry/prompt.ts +103 -0
  73. package/src/entry/runs-command.ts +260 -0
  74. package/src/entry/supervise-bin.ts +18 -0
  75. package/src/entry/supervise-command.ts +37 -0
  76. package/src/entry/tui/commands.ts +545 -0
  77. package/src/entry/tui/effects.ts +312 -0
  78. package/src/entry/tui/fixtures.ts +585 -0
  79. package/src/entry/tui/profile-edits.ts +138 -0
  80. package/src/entry/tui/profile-tree.ts +420 -0
  81. package/src/entry/tui/providers/app.tsx +162 -0
  82. package/src/entry/tui/providers/boundary.tsx +57 -0
  83. package/src/entry/tui/providers/data.tsx +160 -0
  84. package/src/entry/tui/providers/keymap.tsx +166 -0
  85. package/src/entry/tui/providers/root.tsx +45 -0
  86. package/src/entry/tui/providers/theme.tsx +17 -0
  87. package/src/entry/tui/providers/toast.tsx +77 -0
  88. package/src/entry/tui/run.tsx +98 -0
  89. package/src/entry/tui/state.ts +356 -0
  90. package/src/entry/tui/stories.ts +107 -0
  91. package/src/entry/tui/text.ts +130 -0
  92. package/src/entry/tui/theme.ts +140 -0
  93. package/src/entry/tui/views/app.tsx +208 -0
  94. package/src/entry/tui/views/dialogs.tsx +41 -0
  95. package/src/entry/tui/views/profile-actions.ts +321 -0
  96. package/src/entry/tui/views/profiles.tsx +440 -0
  97. package/src/entry/tui/views/runs.tsx +573 -0
  98. package/src/entry/tui/views/save-dialog.tsx +332 -0
  99. package/src/entry/tui/views/status.tsx +209 -0
  100. package/src/entry/tui/widgets/chrome.tsx +114 -0
  101. package/src/entry/tui/widgets/dialog-confirm.tsx +58 -0
  102. package/src/entry/tui/widgets/dialog-prompt.tsx +52 -0
  103. package/src/entry/tui/widgets/dialog-select.tsx +212 -0
  104. package/src/entry/tui/widgets/dialog.tsx +68 -0
  105. package/src/entry/tui/widgets/line.tsx +92 -0
  106. package/src/entry/tui/widgets/list.tsx +197 -0
  107. package/src/entry/tui/widgets/spinner.tsx +49 -0
  108. package/src/entry/tui/widgets/toast.tsx +62 -0
  109. package/src/infra/assets.ts +4 -0
  110. package/src/infra/claude-session.ts +98 -0
  111. package/src/infra/dispatch-dir.ts +286 -0
  112. package/src/infra/env.ts +67 -0
  113. package/src/infra/filelock.ts +159 -0
  114. package/src/infra/git.ts +91 -0
  115. package/src/infra/heavy-lock.ts +36 -0
  116. package/src/infra/jev-client.ts +162 -0
  117. package/src/infra/launch.ts +25 -0
  118. package/src/infra/log.ts +84 -0
  119. package/src/infra/paths.ts +38 -0
  120. package/src/infra/peer-inbox.ts +92 -0
  121. package/src/infra/proc.ts +110 -0
  122. package/src/infra/store.ts +229 -0
  123. package/src/infra/supervisor.ts +287 -0
  124. package/src/infra/version.ts +6 -0
  125. package/src/services/admission.ts +303 -0
  126. package/src/services/agent-links.ts +139 -0
  127. package/src/services/backends.ts +44 -0
  128. package/src/services/budget.ts +47 -0
  129. package/src/services/capture.ts +233 -0
  130. package/src/services/catalog-service.ts +382 -0
  131. package/src/services/dispatch-service.ts +620 -0
  132. package/src/services/dispatches.ts +174 -0
  133. package/src/services/doctor-access.ts +231 -0
  134. package/src/services/doctor-backends.ts +170 -0
  135. package/src/services/doctor-checks.ts +148 -0
  136. package/src/services/doctor-push.ts +109 -0
  137. package/src/services/doctor.ts +338 -0
  138. package/src/services/finalize.ts +377 -0
  139. package/src/services/gate-service.ts +288 -0
  140. package/src/services/global-install.ts +99 -0
  141. package/src/services/jev-service.ts +183 -0
  142. package/src/services/lane-service.ts +390 -0
  143. package/src/services/milestones.ts +208 -0
  144. package/src/services/notifier.ts +296 -0
  145. package/src/services/peek.ts +103 -0
  146. package/src/services/ports.ts +135 -0
  147. package/src/services/preflight.ts +221 -0
  148. package/src/services/profile-service.ts +292 -0
  149. package/src/services/profile-store.ts +141 -0
  150. package/src/services/protocol.ts +212 -0
  151. package/src/services/questions.ts +102 -0
  152. package/src/services/reconcile.ts +130 -0
  153. package/src/services/reentry.ts +22 -0
  154. package/src/services/routing-service.ts +190 -0
  155. package/src/services/run-debug.ts +79 -0
  156. package/src/services/run-service.ts +194 -0
  157. package/src/services/run-store.ts +334 -0
  158. package/src/services/runs-page.ts +254 -0
  159. package/src/services/session-view.ts +152 -0
  160. package/src/services/sessions.ts +92 -0
  161. package/src/services/setup.ts +87 -0
  162. package/src/services/state.ts +111 -0
  163. package/src/services/summary.ts +241 -0
  164. package/catalog/catalog.json +0 -112
  165. package/catalog/models-dev.json +0 -56453
  166. package/src/core/codex-events.ts +0 -49
  167. package/src/core/codex.ts +0 -166
  168. package/src/core/harness.ts +0 -89
  169. package/src/core/lanes.ts +0 -67
  170. package/src/core/lock.ts +0 -111
  171. package/src/core/opencode.ts +0 -207
  172. package/src/core/reconcile.ts +0 -26
  173. package/src/core/reply.ts +0 -54
  174. package/src/core/runstore.ts +0 -163
  175. package/src/core/spawn.ts +0 -89
  176. package/src/core/status.ts +0 -193
  177. package/src/files.ts +0 -19
  178. package/src/mcp/dispatch.ts +0 -154
  179. package/src/mcp/lane-tools.ts +0 -200
  180. package/src/mcp/out.ts +0 -6
  181. package/src/mcp/preflight.ts +0 -84
  182. package/src/mcp/run-tools.ts +0 -121
  183. package/src/mcp/runs.ts +0 -118
  184. package/src/mcp/server.ts +0 -26
  185. package/src/mcp/setup-tools.ts +0 -175
  186. package/src/paths.ts +0 -20
  187. package/src/profile/agents.ts +0 -130
  188. package/src/profile/profile.ts +0 -228
  189. package/src/routing/catalog.ts +0 -227
  190. package/src/routing/commands.ts +0 -22
  191. package/src/routing/jev.ts +0 -185
  192. package/src/routing/route.ts +0 -155
  193. package/src/routing/select.ts +0 -90
  194. package/src/tui/backends.ts +0 -57
  195. package/src/tui/commands.ts +0 -53
  196. package/src/tui/dashboard.tsx +0 -245
  197. package/src/tui/editor.tsx +0 -214
  198. package/src/tui/init.tsx +0 -208
  199. package/src/tui/matrix-model.ts +0 -279
  200. package/src/tui/matrix.tsx +0 -512
  201. package/src/tui/profiles.ts +0 -38
  202. package/src/tui/theme.ts +0 -177
  203. package/src/tui/ui.tsx +0 -267
  204. package/src/tui/watch-model.ts +0 -83
  205. package/src/tui/watch.tsx +0 -236
  206. package/src/types.ts +0 -184
  207. package/src/version.ts +0 -7
package/src/tui/watch.tsx DELETED
@@ -1,236 +0,0 @@
1
- import { join } from "node:path";
2
- import { TextAttributes } from "@opentui/core";
3
- import { useKeyboard, useRenderer } from "@opentui/react";
4
- import { useEffect, useRef, useState } from "react";
5
- import { type BudgetStatus, formatBudget, type RunSummary, summarizeRun } from "../core/status.ts";
6
- import { listRuns, readJsonl, readRunRecords, type Run } from "../core/runstore.ts";
7
- import type { RunRecord, RungId } from "../types.ts";
8
- import { face, glyph, tint, type Ui } from "./theme.ts";
9
- import { CatSpinner, DetailPane, Frame, gradientLetters, ListLine } from "./ui.tsx";
10
- import {
11
- budgetTone,
12
- climbs,
13
- jevLine,
14
- type JevLine,
15
- liveLine,
16
- plainBudgetBar,
17
- runLine,
18
- } from "./watch-model.ts";
19
-
20
- export interface WatchDeps {
21
- listRuns: () => Run[];
22
- summarizeRun: (run: Run) => RunSummary;
23
- readRunRecords: (dir: string) => RunRecord[];
24
- readJev: (dir: string) => JevLine[];
25
- }
26
-
27
- const watchDeps: WatchDeps = {
28
- listRuns,
29
- summarizeRun,
30
- readRunRecords,
31
- readJev: (dir) => readJsonl<JevLine>(join(dir, "jev.jsonl")),
32
- };
33
-
34
- // ponytail: only the 20 newest runs are summarized each poll; add paging when someone keeps more than that live
35
- const MAX_RUNS = 20;
36
- const LEFT_HEIGHT = 12;
37
-
38
- interface Item {
39
- run: Run;
40
- summary: RunSummary | null;
41
- error: string | null;
42
- }
43
-
44
- function poll(d: WatchDeps): { items: Item[]; problem: string | null } {
45
- try {
46
- const items = d
47
- .listRuns()
48
- .slice(0, MAX_RUNS)
49
- .map((run): Item => {
50
- try {
51
- return { run, summary: d.summarizeRun(run), error: null };
52
- } catch (e) {
53
- return { run, summary: null, error: (e as Error).message };
54
- }
55
- });
56
- return { items, problem: null };
57
- } catch (e) {
58
- return { items: [], problem: (e as Error).message };
59
- }
60
- }
61
-
62
- /** spec §11b: a bar in the ginger-to-pink gradient, solid ginger from 80% spent and solid pink once
63
- * the budget is exhausted (the palette has no dedicated warning/error tones, see watch-model.ts). */
64
- function BudgetBar({ ui, budget }: { ui: Ui; budget: BudgetStatus }) {
65
- const width = 10;
66
- if (ui.plain) {
67
- return (
68
- <text
69
- wrapMode="none"
70
- truncate
71
- >{`${plainBudgetBar(budget.fraction, width)} ${formatBudget(budget)}`}</text>
72
- );
73
- }
74
- const filled = Math.round(Math.max(0, Math.min(1, budget.fraction)) * width);
75
- const tone = budgetTone(budget.fraction);
76
- const solid =
77
- tone === "error" ? tint("pink", ui.depth) : tone === "warning" ? tint("ginger", ui.depth) : null;
78
- const cells = solid
79
- ? Array.from({ length: filled }, () => ({ ch: "█", fg: solid }))
80
- : gradientLetters("█".repeat(filled));
81
- return (
82
- <text wrapMode="none" truncate>
83
- {cells.map((cell, i) => (
84
- // biome-ignore lint: index key is stable, the bar never reorders
85
- <span key={i} fg={cell.fg}>
86
- {cell.ch}
87
- </span>
88
- ))}
89
- <span attributes={TextAttributes.DIM}>{"░".repeat(width - filled)}</span>
90
- {` ${formatBudget(budget)}`}
91
- </text>
92
- );
93
- }
94
-
95
- /** The right pane for the highlighted run: its live lanes, recent climbs, Jev decisions and the
96
- * budget bar — the run's records and raw state text stay out (they were noise, spec §item 4). */
97
- function RunDetail({ ui, it, climbed, deps }: { ui: Ui; it: Item; climbed: string[]; deps: WatchDeps }) {
98
- const pink = tint("pink", ui.depth);
99
- if (!it.summary) {
100
- return (
101
- <DetailPane ui={ui} title={it.run.id} subtitle="unreadable">
102
- <text fg={pink} wrapMode="none" truncate>
103
- {`${face("failed", ui.plain)} ${it.error}`}
104
- </text>
105
- </DetailPane>
106
- );
107
- }
108
- const s = it.summary;
109
- const jev = deps.readJev(it.run.dir);
110
- return (
111
- <DetailPane ui={ui} title={s.title} subtitle={it.run.meta.repo}>
112
- {s.live.length > 0 ? (
113
- s.live.map((l) => (
114
- <text key={l.name} wrapMode="none" truncate>
115
- {liveLine(l, ui.plain)}
116
- </text>
117
- ))
118
- ) : (
119
- <text attributes={TextAttributes.DIM}>no lane is running right now</text>
120
- )}
121
- {climbed.length > 0 ? (
122
- <>
123
- <text> </text>
124
- {climbed.map((c, i) => (
125
- <text key={`c${i}`} fg={pink} wrapMode="none" truncate>
126
- {c}
127
- </text>
128
- ))}
129
- </>
130
- ) : null}
131
- <text> </text>
132
- <text attributes={TextAttributes.DIM}>
133
- {`jev ${glyph("dot", ui.plain)} ${s.jev.decisions} decisions ${glyph("dot", ui.plain)} ${s.jev.fallbacks} fell back`}
134
- </text>
135
- {jev.slice(-4).map((e, i) => (
136
- <text key={`j${i}`} wrapMode="none" truncate>
137
- {jevLine(e, ui.plain)}
138
- </text>
139
- ))}
140
- {s.budget ? (
141
- <>
142
- <text> </text>
143
- <BudgetBar ui={ui} budget={s.budget} />
144
- </>
145
- ) : null}
146
- </DetailPane>
147
- );
148
- }
149
-
150
- export function Watch({
151
- ui,
152
- deps = {},
153
- intervalMs = 2000,
154
- }: {
155
- ui: Ui;
156
- deps?: Partial<WatchDeps>;
157
- intervalMs?: number;
158
- }) {
159
- const d = { ...watchDeps, ...deps };
160
- const renderer = useRenderer();
161
- const seen = useRef(new Map<string, RungId>());
162
- const [state, setState] = useState<{ items: Item[]; problem: string | null } | null>(null);
163
- const [climbed, setClimbed] = useState<string[]>([]);
164
- const [cursor, setCursor] = useState(0);
165
-
166
- useEffect(() => {
167
- const refresh = () => {
168
- const next = poll(d);
169
- const fresh = next.items.flatMap((it) =>
170
- it.summary ? climbs(seen.current, it.summary, ui.plain) : [],
171
- );
172
- if (fresh.length > 0) setClimbed((c) => [...fresh, ...c].slice(0, 3));
173
- setState(next);
174
- };
175
- refresh();
176
- const t = setInterval(refresh, intervalMs);
177
- return () => clearInterval(t);
178
- }, []);
179
-
180
- const items = state?.items ?? [];
181
- const at = Math.min(cursor, Math.max(0, items.length - 1));
182
- const leftStart = Math.max(0, Math.min(at - Math.floor(LEFT_HEIGHT / 2), items.length - LEFT_HEIGHT));
183
-
184
- useKeyboard((key) => {
185
- if (key.sequence === "q") {
186
- renderer.destroy();
187
- return;
188
- }
189
- if (key.name === "up") setCursor(Math.max(0, at - 1));
190
- else if (key.name === "down") setCursor(Math.min(items.length - 1, at + 1));
191
- });
192
-
193
- const pink = tint("pink", ui.depth);
194
- const title = `Watch ${glyph("dot", ui.plain)} ${items.length} runs`;
195
- const hint = `${glyph("keys", ui.plain)} navigate q quit refreshes every ${intervalMs / 1000} s`;
196
-
197
- if (state?.problem) {
198
- return (
199
- <Frame ui={ui} title={title} hint="q quit">
200
- <text fg={pink} wrapMode="none" truncate>{`${face("failed", ui.plain)} ${state.problem}`}</text>
201
- </Frame>
202
- );
203
- }
204
-
205
- return (
206
- <Frame ui={ui} title={title} hint={hint}>
207
- {state === null ? (
208
- <CatSpinner ui={ui} label="reading the runs" />
209
- ) : items.length === 0 ? (
210
- <text>No runs yet. Start one with /catherd in Claude Code.</text>
211
- ) : (
212
- <box style={{ flexDirection: "row" }}>
213
- <box style={{ flexDirection: "column", width: 24 }}>
214
- <text fg={pink} attributes={TextAttributes.BOLD}>
215
- RUNS
216
- </text>
217
- {items.slice(leftStart, leftStart + LEFT_HEIGHT).map((it, i) => (
218
- <ListLine
219
- key={it.run.dir}
220
- ui={ui}
221
- selected={leftStart + i === at}
222
- dim={!it.summary}
223
- text={
224
- it.summary
225
- ? runLine(it.summary, ui.plain)
226
- : `${face("failed", ui.plain)} ${it.run.id} ${glyph("dot", ui.plain)} unreadable: ${it.error}`
227
- }
228
- />
229
- ))}
230
- </box>
231
- {items[at] ? <RunDetail ui={ui} it={items[at]} climbed={climbed} deps={d} /> : null}
232
- </box>
233
- )}
234
- </Frame>
235
- );
236
- }
package/src/types.ts DELETED
@@ -1,184 +0,0 @@
1
- export const ROLES = [
2
- "architect",
3
- "verifier",
4
- "worker",
5
- "reviewer",
6
- "ui-reviewer",
7
- "artist",
8
- "writer",
9
- "researcher",
10
- ] as const;
11
- export type Role = (typeof ROLES)[number];
12
-
13
- export type Backend = "codex" | "opencode" | "claude";
14
-
15
- /** "<model-id>#<effort>". Model ids may contain "/" (opencode providers); split at the LAST "#". */
16
- export type RungId = string;
17
-
18
- export function splitRung(rung: RungId): { model: string; effort: string } {
19
- const i = rung.lastIndexOf("#");
20
- if (i < 1 || i === rung.length - 1) throw new Error(`catherd: bad rung "${rung}", expected model#effort`);
21
- return { model: rung.slice(0, i), effort: rung.slice(i + 1) };
22
- }
23
-
24
- export const rungOf = (model: string, effort: string): RungId => `${model}#${effort}`;
25
-
26
- export type Sandbox = "read-only" | "workspace-write" | "danger-full-access";
27
-
28
- /** Spec §4: the sandbox a Codex or opencode run of each role gets. */
29
- export const ROLE_SANDBOX: Record<Role, Sandbox> = {
30
- architect: "read-only",
31
- verifier: "danger-full-access",
32
- worker: "workspace-write",
33
- reviewer: "read-only",
34
- "ui-reviewer": "danger-full-access",
35
- artist: "workspace-write",
36
- writer: "workspace-write",
37
- researcher: "read-only",
38
- };
39
-
40
- export interface Capabilities {
41
- toolCall: boolean;
42
- imageIn: boolean;
43
- imageOut: boolean;
44
- reasoning: boolean;
45
- context: number;
46
- }
47
-
48
- /** Spec §4: what a model must have to be offered for a role. */
49
- export const ROLE_NEEDS: Record<Role, Partial<Capabilities>> = {
50
- architect: { toolCall: true },
51
- verifier: { toolCall: true },
52
- worker: { toolCall: true },
53
- reviewer: { toolCall: true },
54
- "ui-reviewer": { toolCall: true, imageIn: true },
55
- artist: { imageOut: true },
56
- writer: { toolCall: true },
57
- researcher: { toolCall: true },
58
- };
59
-
60
- export const KINDS = ["repo_code", "terminal", "ui", "prose", "research"] as const;
61
- export type Kind = (typeof KINDS)[number];
62
-
63
- export const DIFFICULTIES = ["copy", "build", "logic", "hard"] as const;
64
- export type Difficulty = (typeof DIFFICULTIES)[number];
65
-
66
- export type ScoreDim = "repo_code" | "terminal" | "honesty";
67
-
68
- export interface Scores {
69
- repo_code?: number;
70
- terminal?: number;
71
- honesty?: number;
72
- secs_per_task?: number;
73
- }
74
-
75
- export interface CatalogModel {
76
- id: string;
77
- backend: Backend;
78
- efforts: string[];
79
- capabilities: Capabilities;
80
- }
81
-
82
- export interface CatalogEntry {
83
- rung: RungId;
84
- scores: Scores;
85
- costRank: number;
86
- }
87
-
88
- export type Bars = Record<Kind, Record<Difficulty, Partial<Record<ScoreDim, number>>>>;
89
-
90
- export interface Catalog {
91
- version: string;
92
- models: CatalogModel[];
93
- entries: CatalogEntry[];
94
- bars: Bars;
95
- /** unscored rung -> scored rung whose scores it borrows */
96
- treatLike: Record<RungId, RungId>;
97
- }
98
-
99
- export interface RoleConfig {
100
- enabled: boolean;
101
- /** model id -> enabled efforts */
102
- models: Record<string, string[]>;
103
- defaultRung?: RungId;
104
- }
105
-
106
- export type NotifyMoment = "milestone" | "finish" | "blocked";
107
-
108
- /** Spec §8.2: false (the default) runs the vendor harness exactly as the user configured it. */
109
- export interface HarnessConfig {
110
- isolated: boolean;
111
- }
112
-
113
- export interface Profile {
114
- name: string;
115
- objective: "cost" | "speed";
116
- roles: Record<Role, RoleConfig>;
117
- harness: { codex: HarnessConfig; opencode: HarnessConfig };
118
- lock: { heavy: number | "cpus/2" };
119
- notify: NotifyMoment[];
120
- /** spec §11b: a rung's stand-in on another backend, used on a `limit` result */
121
- failover?: Record<RungId, RungId>;
122
- /** spec §11b: the run budget; `status` shows spend against it, `route` and `dispatch` react at 80%/100% */
123
- budget?: { minutes?: number; tokens?: number; usd?: number };
124
- }
125
-
126
- export interface RouteDecision {
127
- kind: Kind | null;
128
- difficulty: Difficulty | null;
129
- confidence: { kind: number | null; difficulty: number | null };
130
- source: "jev" | "default";
131
- rung: RungId;
132
- ladder: RungId[];
133
- }
134
-
135
- export type ReplyStatus = "complete" | "partial" | "blocked" | "refused";
136
- export type RunStatus = "ok" | "failed" | "limit" | "cli-too-old" | "timeout" | "interrupted";
137
-
138
- export interface Tokens {
139
- input: number;
140
- cached: number;
141
- output: number;
142
- }
143
-
144
- /** One Codex or opencode role run. Appended to <run>/runs.jsonl; the only record of it. */
145
- export interface RunRecord {
146
- name: string;
147
- role: Role;
148
- backend: "codex" | "opencode";
149
- rung: RungId;
150
- thread: string | null;
151
- status: RunStatus;
152
- startedAt: string;
153
- secs: number;
154
- pid: number | null;
155
- tokens: Tokens;
156
- costUsd: number | null;
157
- changedOwned: string[];
158
- replyStatus: ReplyStatus | null;
159
- replyWhy: string | null;
160
- threadHeavy: boolean;
161
- /** the harness ran isolated (spec §8.2); lets reports measure what the user's customizations cost */
162
- isolated: boolean;
163
- images: string[];
164
- error: string | null;
165
- replyPath: string;
166
- }
167
-
168
- export interface RunMeta {
169
- id: string;
170
- repo: string;
171
- title: string;
172
- aLines: string[];
173
- createdAt: string;
174
- }
175
-
176
- export interface StateSnapshot {
177
- head: string;
178
- dirty: { path: string; owner: string | null }[];
179
- running: { name: string; rung: RungId; thread: string | null; brief: string; since: string }[];
180
- lastCheck: string | null;
181
- next: string;
182
- }
183
-
184
- export const THREAD_HEAVY_INPUT = 8_000_000;
package/src/version.ts DELETED
@@ -1,7 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
-
3
- const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")) as {
4
- version: string;
5
- };
6
-
7
- export const VERSION: string = pkg.version;