catherd-cli 1.5.0 → 2.0.0-beta.20261022113309

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 (275) hide show
  1. package/README.md +164 -208
  2. package/THIRD_PARTY_NOTICES.md +217 -35
  3. package/catalog/jev.json +27 -1
  4. package/catalog/models.json +56 -247
  5. package/package.json +6 -8
  6. package/plugin/.claude-plugin/plugin.json +1 -1
  7. package/plugin/.codex-plugin/plugin.json +1 -1
  8. package/plugin/bin/catherd-mcp +1 -1
  9. package/plugin/commands/catherd.md +2 -2
  10. package/plugin/skills/catherd/SKILL.md +97 -421
  11. package/plugin/skills/catherd-setup/SKILL.md +20 -19
  12. package/src/adapters/all.ts +0 -4
  13. package/src/adapters/backend.ts +1 -2
  14. package/src/adapters/cursor/home.ts +1 -1
  15. package/src/adapters/cursor/index.ts +4 -4
  16. package/src/cli.ts +22 -27
  17. package/src/domain/bars.ts +33 -107
  18. package/src/domain/blocked.ts +72 -0
  19. package/src/domain/branch.ts +51 -0
  20. package/src/domain/brief.ts +21 -6
  21. package/src/domain/catalog.ts +69 -324
  22. package/src/domain/cost.ts +15 -30
  23. package/src/domain/errors.ts +2 -2
  24. package/src/domain/fail-lines.ts +26 -0
  25. package/src/domain/failover.ts +37 -87
  26. package/src/domain/findings.ts +47 -0
  27. package/src/domain/gate-brief.ts +1 -0
  28. package/src/domain/ids.ts +13 -2
  29. package/src/domain/jev.ts +47 -2
  30. package/src/domain/judge.ts +124 -0
  31. package/src/domain/lane.ts +22 -3
  32. package/src/domain/milestone-memory.ts +53 -0
  33. package/src/domain/notice.ts +25 -8
  34. package/src/domain/notify.ts +32 -0
  35. package/src/domain/plan-doc.ts +198 -0
  36. package/src/domain/plan-lint.ts +141 -0
  37. package/src/domain/profile-rules.ts +101 -160
  38. package/src/domain/profile.ts +116 -63
  39. package/src/domain/record.ts +53 -2
  40. package/src/domain/role-prompts.ts +131 -45
  41. package/src/domain/role-scope.ts +4 -34
  42. package/src/domain/role-tools.ts +2 -2
  43. package/src/domain/roles.ts +2 -0
  44. package/src/domain/route.ts +17 -0
  45. package/src/domain/rules.ts +33 -0
  46. package/src/domain/run-events.ts +298 -0
  47. package/src/domain/run-machine.ts +668 -0
  48. package/src/domain/run-state.ts +1310 -0
  49. package/src/domain/scorecard.ts +197 -0
  50. package/src/domain/select.ts +151 -146
  51. package/src/domain/slopalytics.ts +72 -0
  52. package/src/entry/answer-command.ts +39 -0
  53. package/src/entry/catalog-command.ts +25 -174
  54. package/src/entry/check-bin.ts +17 -0
  55. package/src/entry/cli-kit.ts +1 -1
  56. package/src/entry/daemon-bin.ts +22 -0
  57. package/src/entry/glyphs.ts +15 -2
  58. package/src/entry/init-command.ts +18 -68
  59. package/src/entry/knowledge-command.ts +45 -4
  60. package/src/entry/lock-command.ts +6 -7
  61. package/src/entry/mcp/role-server.ts +18 -0
  62. package/src/entry/mcp/server.ts +13 -40
  63. package/src/entry/mcp/session-tools.ts +149 -0
  64. package/src/entry/panel/openapi.ts +189 -0
  65. package/src/entry/panel/server.ts +410 -0
  66. package/src/entry/panel-command.ts +68 -0
  67. package/src/entry/profile-command.ts +10 -14
  68. package/src/entry/role-cli-command.ts +47 -3
  69. package/src/entry/runs-command.ts +3 -19
  70. package/src/entry/scorecard-command.ts +187 -0
  71. package/src/entry/supervise-bin.ts +1 -1
  72. package/src/infra/env.ts +0 -2
  73. package/src/infra/git.ts +64 -0
  74. package/src/infra/heavy-lock.ts +45 -7
  75. package/src/infra/launch.ts +63 -7
  76. package/src/infra/memory.ts +45 -0
  77. package/src/infra/ntfy.ts +40 -0
  78. package/src/infra/open-url.ts +42 -0
  79. package/src/infra/paths.ts +2 -2
  80. package/src/infra/slopalytics.ts +30 -0
  81. package/src/infra/store.ts +6 -1
  82. package/src/services/admission.ts +24 -9
  83. package/src/services/backends.ts +4 -6
  84. package/src/services/capture.ts +1 -22
  85. package/src/services/catalog-service.ts +155 -262
  86. package/src/services/credentials.ts +4 -20
  87. package/src/services/daemon.ts +181 -0
  88. package/src/services/dispatch-service.ts +5 -1
  89. package/src/services/doctor-backends.ts +1 -1
  90. package/src/services/doctor-checks.ts +47 -0
  91. package/src/services/doctor.ts +4 -9
  92. package/src/services/failover-rungs.ts +109 -0
  93. package/src/services/finalize.ts +12 -0
  94. package/src/services/gate-env.ts +1 -2
  95. package/src/services/gate-service.ts +21 -6
  96. package/src/services/jev-service.ts +2 -4
  97. package/src/services/lane-service.ts +34 -3
  98. package/src/services/milestone-check.ts +433 -0
  99. package/src/services/milestone-tree.ts +170 -0
  100. package/src/services/milestones.ts +35 -8
  101. package/src/services/notifier.ts +69 -2
  102. package/src/services/notify-service.ts +26 -0
  103. package/src/services/panel-setup.ts +116 -0
  104. package/src/services/plan-input.ts +151 -0
  105. package/src/services/ports.ts +30 -4
  106. package/src/services/preflight.ts +82 -8
  107. package/src/services/profile-service.ts +30 -3
  108. package/src/services/profile-store.ts +8 -3
  109. package/src/services/protocol.ts +4 -0
  110. package/src/services/provenance.ts +31 -90
  111. package/src/services/questions.ts +9 -0
  112. package/src/services/reconcile.ts +11 -0
  113. package/src/services/routing-service.ts +26 -15
  114. package/src/services/run-briefs.ts +477 -0
  115. package/src/services/run-client.ts +217 -0
  116. package/src/services/run-effects.ts +837 -0
  117. package/src/services/run-events-store.ts +174 -0
  118. package/src/services/run-evidence.ts +1 -1
  119. package/src/services/run-read.ts +221 -0
  120. package/src/services/run-report.ts +176 -0
  121. package/src/services/run-service.ts +55 -6
  122. package/src/services/run-store.ts +54 -7
  123. package/src/services/scorecard-metrics.ts +86 -0
  124. package/src/services/scorecard.ts +382 -0
  125. package/src/services/summary.ts +27 -3
  126. package/src/services/tree-path.ts +27 -0
  127. package/src/services/workspace-admission.ts +5 -13
  128. package/src/services/workspace-service.ts +5 -0
  129. package/web/dist/assets/Brand-PKlUX5TE.js +1 -0
  130. package/web/dist/assets/Brand-PKlUX5TE.js.br +0 -0
  131. package/web/dist/assets/Brand-PKlUX5TE.js.gz +0 -0
  132. package/web/dist/assets/Modal-Dxc5so53.js +1 -0
  133. package/web/dist/assets/Modal-Dxc5so53.js.br +0 -0
  134. package/web/dist/assets/Modal-Dxc5so53.js.gz +0 -0
  135. package/web/dist/assets/Pressable-C8-T6Ed_.js +1 -0
  136. package/web/dist/assets/StatusChip-B5xfI_Mn.js +153 -0
  137. package/web/dist/assets/StatusChip-B5xfI_Mn.js.br +0 -0
  138. package/web/dist/assets/StatusChip-B5xfI_Mn.js.gz +0 -0
  139. package/web/dist/assets/_app-Ea5cpyKe.js +1 -0
  140. package/web/dist/assets/_app-Ea5cpyKe.js.br +0 -0
  141. package/web/dist/assets/_app-Ea5cpyKe.js.gz +0 -0
  142. package/web/dist/assets/_runId-pTG0fXVo.js +1 -0
  143. package/web/dist/assets/_runId-pTG0fXVo.js.br +0 -0
  144. package/web/dist/assets/_runId-pTG0fXVo.js.gz +0 -0
  145. package/web/dist/assets/drawer-Du3cdlok.js +1 -0
  146. package/web/dist/assets/drawer-Du3cdlok.js.br +0 -0
  147. package/web/dist/assets/drawer-Du3cdlok.js.gz +0 -0
  148. package/web/dist/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  149. package/web/dist/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  150. package/web/dist/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  151. package/web/dist/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  152. package/web/dist/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
  153. package/web/dist/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
  154. package/web/dist/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
  155. package/web/dist/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
  156. package/web/dist/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
  157. package/web/dist/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
  158. package/web/dist/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  159. package/web/dist/assets/index-B6GiwDkh.js +11 -0
  160. package/web/dist/assets/index-B6GiwDkh.js.br +0 -0
  161. package/web/dist/assets/index-B6GiwDkh.js.gz +0 -0
  162. package/web/dist/assets/index-C5IbylvM.css +2 -0
  163. package/web/dist/assets/index-C5IbylvM.css.br +0 -0
  164. package/web/dist/assets/index-C5IbylvM.css.gz +0 -0
  165. package/web/dist/assets/label-DFSYBc62.js +1 -0
  166. package/web/dist/assets/label-DFSYBc62.js.br +0 -0
  167. package/web/dist/assets/label-DFSYBc62.js.gz +0 -0
  168. package/web/dist/assets/link-D8EcKZUt.js +1 -0
  169. package/web/dist/assets/link-D8EcKZUt.js.br +0 -0
  170. package/web/dist/assets/link-D8EcKZUt.js.gz +0 -0
  171. package/web/dist/assets/login-DaN5HPPW.js +1 -0
  172. package/web/dist/assets/login-DaN5HPPW.js.br +0 -0
  173. package/web/dist/assets/login-DaN5HPPW.js.gz +0 -0
  174. package/web/dist/assets/query-Cu--ccT3.js +1 -0
  175. package/web/dist/assets/questions-shoO9OVj.js +1 -0
  176. package/web/dist/assets/questions-shoO9OVj.js.br +0 -0
  177. package/web/dist/assets/questions-shoO9OVj.js.gz +0 -0
  178. package/web/dist/assets/react-query.gen-Bu2yV-qJ.js +12 -0
  179. package/web/dist/assets/react-query.gen-Bu2yV-qJ.js.br +0 -0
  180. package/web/dist/assets/react-query.gen-Bu2yV-qJ.js.gz +0 -0
  181. package/web/dist/assets/rolldown-runtime-XKyaHtl9.js +1 -0
  182. package/web/dist/assets/run-status-C0DRuV7U.js +1 -0
  183. package/web/dist/assets/runs-C3rg2zZc.js +1 -0
  184. package/web/dist/assets/runs-C3rg2zZc.js.br +0 -0
  185. package/web/dist/assets/runs-C3rg2zZc.js.gz +0 -0
  186. package/web/dist/assets/setup-5I4Vl9fR.js +1 -0
  187. package/web/dist/assets/setup-5I4Vl9fR.js.br +0 -0
  188. package/web/dist/assets/setup-5I4Vl9fR.js.gz +0 -0
  189. package/web/dist/assets/table-BOxXhqS4.js +1 -0
  190. package/web/dist/assets/table-BOxXhqS4.js.br +0 -0
  191. package/web/dist/assets/table-BOxXhqS4.js.gz +0 -0
  192. package/web/dist/assets/textfield-BzRFkxBF.js +1 -0
  193. package/web/dist/assets/textfield-BzRFkxBF.js.br +0 -0
  194. package/web/dist/assets/textfield-BzRFkxBF.js.gz +0 -0
  195. package/web/dist/assets/time--9kSQbbp.js +1 -0
  196. package/web/dist/assets/use-css-variable-CpnN9WtA.js +5 -0
  197. package/web/dist/assets/use-css-variable-CpnN9WtA.js.br +0 -0
  198. package/web/dist/assets/use-css-variable-CpnN9WtA.js.gz +0 -0
  199. package/web/dist/assets/useMutation-CVPKlR34.js +1 -0
  200. package/web/dist/assets/useMutation-CVPKlR34.js.br +0 -0
  201. package/web/dist/assets/useMutation-CVPKlR34.js.gz +0 -0
  202. package/web/dist/index.html +15 -0
  203. package/catalog/ATTRIBUTION.md +0 -44
  204. package/catalog/scores.json +0 -3202
  205. package/catalog/sources.json +0 -97
  206. package/src/adapters/antigravity/events.ts +0 -131
  207. package/src/adapters/antigravity/home.ts +0 -46
  208. package/src/adapters/antigravity/index.ts +0 -335
  209. package/src/adapters/antigravity/models.ts +0 -37
  210. package/src/adapters/grok/events.ts +0 -112
  211. package/src/adapters/grok/home.ts +0 -187
  212. package/src/adapters/grok/index.ts +0 -358
  213. package/src/adapters/grok/models.ts +0 -48
  214. package/src/domain/calibration.ts +0 -132
  215. package/src/domain/sources.ts +0 -144
  216. package/src/entry/mcp/dispatch-tools.ts +0 -52
  217. package/src/entry/mcp/lane-tools.ts +0 -131
  218. package/src/entry/mcp/protocol-tools.ts +0 -63
  219. package/src/entry/mcp/push-tools.ts +0 -16
  220. package/src/entry/mcp/run-tools.ts +0 -164
  221. package/src/entry/mcp/setup-tools.ts +0 -109
  222. package/src/entry/mcp/workspace-tools.ts +0 -142
  223. package/src/entry/tui/commands.ts +0 -563
  224. package/src/entry/tui/effects.ts +0 -385
  225. package/src/entry/tui/fixtures.ts +0 -637
  226. package/src/entry/tui/profile-edits.ts +0 -146
  227. package/src/entry/tui/profile-tree.ts +0 -429
  228. package/src/entry/tui/providers/app.tsx +0 -162
  229. package/src/entry/tui/providers/boundary.tsx +0 -57
  230. package/src/entry/tui/providers/data.tsx +0 -160
  231. package/src/entry/tui/providers/keymap.tsx +0 -166
  232. package/src/entry/tui/providers/root.tsx +0 -45
  233. package/src/entry/tui/providers/theme.tsx +0 -17
  234. package/src/entry/tui/providers/toast.tsx +0 -77
  235. package/src/entry/tui/run.tsx +0 -114
  236. package/src/entry/tui/state.ts +0 -400
  237. package/src/entry/tui/stories.ts +0 -114
  238. package/src/entry/tui/text.ts +0 -130
  239. package/src/entry/tui/theme.ts +0 -140
  240. package/src/entry/tui/views/app.tsx +0 -208
  241. package/src/entry/tui/views/dialogs.tsx +0 -41
  242. package/src/entry/tui/views/profile-actions.ts +0 -331
  243. package/src/entry/tui/views/profiles.tsx +0 -520
  244. package/src/entry/tui/views/runs.tsx +0 -619
  245. package/src/entry/tui/views/save-dialog.tsx +0 -344
  246. package/src/entry/tui/views/status.tsx +0 -213
  247. package/src/entry/tui/widgets/chrome.tsx +0 -114
  248. package/src/entry/tui/widgets/dialog-confirm.tsx +0 -58
  249. package/src/entry/tui/widgets/dialog-prompt.tsx +0 -52
  250. package/src/entry/tui/widgets/dialog-select.tsx +0 -212
  251. package/src/entry/tui/widgets/dialog.tsx +0 -68
  252. package/src/entry/tui/widgets/line.tsx +0 -92
  253. package/src/entry/tui/widgets/list.tsx +0 -197
  254. package/src/entry/tui/widgets/spinner.tsx +0 -49
  255. package/src/entry/tui/widgets/toast.tsx +0 -62
  256. package/src/infra/sources/arena.ts +0 -95
  257. package/src/infra/sources/artificial-analysis.ts +0 -119
  258. package/src/infra/sources/cache.ts +0 -126
  259. package/src/infra/sources/csv.ts +0 -38
  260. package/src/infra/sources/epoch.ts +0 -82
  261. package/src/infra/sources/http.ts +0 -75
  262. package/src/infra/sources/litellm.ts +0 -58
  263. package/src/infra/sources/models-dev.ts +0 -59
  264. package/src/infra/sources/openrouter-endpoints.ts +0 -52
  265. package/src/infra/sources/openrouter-models.ts +0 -60
  266. package/src/infra/sources/rows.ts +0 -38
  267. package/src/infra/sources/vectara.ts +0 -69
  268. package/src/infra/sources/zip.ts +0 -48
  269. package/src/services/catalog-refresh.ts +0 -201
  270. package/src/services/doctor-sources.ts +0 -76
  271. package/src/services/runs-page.ts +0 -290
  272. package/src/services/source-derive.ts +0 -397
  273. package/src/services/source-sync.ts +0 -312
  274. package/src/services/standins.ts +0 -238
  275. package/src/services/treat-likes.ts +0 -113
@@ -1,385 +0,0 @@
1
- import type { HostContext } from "../../domain/host.ts";
2
- import { sessionKey } from "../../domain/host.ts";
3
- import { realpathSync, statSync, watch } from "node:fs";
4
- import { adapterFor } from "../../adapters/registry.ts";
5
- import "../../adapters/all.ts";
6
- import { agentFiles } from "../../domain/agents.ts";
7
- import { type Catalog, rungInfo } from "../../domain/catalog.ts";
8
- import { HARNESS_KEYS, type Profile, type ProfileDoc, type ProfilePatch } from "../../domain/profile.ts";
9
- import type { Validation } from "../../domain/profile-rules.ts";
10
- import type { Access } from "../../domain/record.ts";
11
- import { VERSION } from "../../infra/version.ts";
12
- import {
13
- type CatalogModel,
14
- catalogQuery,
15
- loadCatalog,
16
- type Refreshed,
17
- refreshDiscovery,
18
- saveTreatLike,
19
- } from "../../services/catalog-service.ts";
20
- import { cancel } from "../../services/dispatch-service.ts";
21
- import { type DoctorReport, doctor } from "../../services/doctor.ts";
22
- import { ageText } from "../../services/doctor-sources.ts";
23
- import { valuesUsed, type ValueUsed } from "../../services/provenance.ts";
24
- import { evidenceLine, evidenceOf, runEvidence } from "../../services/run-evidence.ts";
25
- import { type SyncReport, sourcesStatus, syncSources } from "../../services/source-sync.ts";
26
- import { type Suggestion, suggestStandIns } from "../../services/standins.ts";
27
- import type { Synced } from "../../services/agent-links.ts";
28
- import {
29
- activate,
30
- createProfile,
31
- deleteProfile,
32
- patchProfile,
33
- type Saved,
34
- } from "../../services/profile-service.ts";
35
- import {
36
- activeName,
37
- configFile,
38
- enforcementOf,
39
- listProfiles,
40
- profilesDir,
41
- projectsFile,
42
- readProfileDoc,
43
- readProjects,
44
- validateHere,
45
- } from "../../services/profile-store.ts";
46
- import { listRuns, type Run, runPaths } from "../../services/run-store.ts";
47
- import {
48
- type MilestoneDetail,
49
- milestoneDetail,
50
- type RoleDetail,
51
- roleDetail,
52
- type SessionDetail,
53
- sessionDetail,
54
- type SessionRow,
55
- sessionRows,
56
- } from "../../services/runs-page.ts";
57
- import { type RunSummary, summarizeRun } from "../../services/summary.ts";
58
- import { defaultDeps } from "../deps.ts";
59
- import { mcpHandshake } from "../mcp/handshake.ts";
60
-
61
- /** A save refused because the profile changed on disk since its preview read it carries this path. */
62
- export { CHANGED_ON_DISK } from "../../services/profile-service.ts";
63
- export type {
64
- Milestone,
65
- MilestoneDetail,
66
- RoleDetail,
67
- RoleRow,
68
- SessionDetail,
69
- SessionRow,
70
- SessionRun,
71
- } from "../../services/runs-page.ts";
72
-
73
- /** One line of the run list. */
74
- export interface RunRow {
75
- waiting?: RunSummary["waiting"];
76
- id: string;
77
- title: string;
78
- repo: string;
79
- createdAt: string;
80
- live: number;
81
- roleRuns: number;
82
- landed: number;
83
- /** the budget's spent fraction, null without a cap */
84
- budget: number | null;
85
- /** the session that started it (the Runs tab opens it); null for a run from before 1.1 */
86
- session: string | null;
87
- }
88
-
89
- /**
90
- * Everything the TUI reads or changes, behind one seam (spec §9.4 `effects.ts`: services injected).
91
- * The live set calls the services; tests and the storybook pass fakes.
92
- */
93
- export interface Effects {
94
- host: HostContext;
95
- version: string;
96
- doctor(): Promise<DoctorReport>;
97
- runs(): { rows: RunRow[]; warnings: string[] };
98
- /** spec §4: the Runs tab's top level, the sessions */
99
- sessions(): { rows: SessionRow[]; warnings: string[] };
100
- /** a session's screen; `key` null is "earlier runs" */
101
- session(key: string | null): SessionDetail;
102
- /** a role's screen */
103
- role(run: string, dispatchId: string): RoleDetail;
104
- /** spec 1.1 §10: a milestone's screen, its digest */
105
- milestone(run: string, name: string): MilestoneDetail;
106
- /**
107
- * calls `onChange` when a file under one of `dirs` changes (spec §4: the open screen redraws when a run file
108
- * changes); null when they cannot be watched here, and the screen polls every second instead
109
- */
110
- watch(dirs: string[], onChange: () => void): (() => void) | null;
111
- /** stops a live role; resolves to the line the toast shows */
112
- cancel(run: string, name: string): Promise<string>;
113
- profiles(): {
114
- names: string[];
115
- /** global */
116
- active: string;
117
- /** what this directory runs on */
118
- here: string;
119
- /** the bound repo, null outside one or unbound */
120
- repo: string | null;
121
- };
122
- readProfile(name: string): ProfileDoc;
123
- catalog(billing: Profile["billing"]): { catalog: Catalog; models: CatalogModel[] };
124
- validate(p: Profile, c: Catalog): Validation;
125
- enforcement(rung: string, access: Access): "enforced" | "advisory";
126
- /** the harnesses a profile can isolate */
127
- harnesses: readonly string[];
128
- /** spec 1.3 §8: what a harness's native mode loads, and the API key its isolation needs, when it says */
129
- isolation(harness: string): { note?: string; key?: string };
130
- /** the native agents a profile links, by name */
131
- agents(p: Profile): string[];
132
- /**
133
- * writes the staged treat-likes, then the profile patch, through the ProfileService (validation reads
134
- * the treat-likes, so they go first); a refused patch leaves the treat-likes written. With `shown` (the
135
- * profile as the save dialog's last preview read it), the patch is written only over that profile: when
136
- * another process changed it meanwhile (while a treat-like waited for the catalog lock, say), nothing
137
- * is written and the result's one error has the path CHANGED_ON_DISK
138
- */
139
- save(
140
- name: string,
141
- patch: ProfilePatch,
142
- treatLikes: Record<string, string>,
143
- shown?: ProfileDoc,
144
- ): Promise<Saved>;
145
- /**
146
- * applies the scope the user confirmed: binds `repo` (this TUI's repo) to it, or with null makes it the
147
- * global active profile; never a scope recomputed as it writes
148
- */
149
- activate(name: string, repo: string | null): Synced;
150
- create(name: string, from?: string): Saved;
151
- remove(name: string): Synced;
152
- refreshCatalog(): Promise<Refreshed[]>;
153
- /** spec 1.2 §9 (`r`): syncs the public sources (each at most every 12 hours) */
154
- syncSources(): Promise<SyncReport>;
155
- /** spec 1.2 §9: each source's name, age and last error, as the `r` dialog shows them */
156
- sources(): { source: string; name: string; age: string; error: string | null }[];
157
- /** spec 1.2 §6.3: the three nearest stand-ins for a rung, for the treat-like picker */
158
- suggest(c: Catalog, rung: string): Suggestion[];
159
- /** spec 1.2 §5.3, §8: a rung's values with confidence and source, and catherd's own runs on it */
160
- rungDetail(c: Catalog, rung: string): { values: ValueUsed[]; evidence: string | null };
161
- }
162
-
163
- /** Spec 1.2 §9: each source's age and last error, from the sync's state. */
164
- export function sourceRows(now = Date.now()): ReturnType<Effects["sources"]> {
165
- return sourcesStatus().sources.map((s) => ({
166
- source: s.source,
167
- name: s.name,
168
- age: s.fetchedAt ? `${ageText(now - Date.parse(s.fetchedAt))} ago` : "never fetched",
169
- error: s.error,
170
- }));
171
- }
172
-
173
- /** Spec 1.2 §5.3, §8: what the rung detail shows; an unparseable rung has nothing to show. */
174
- export function rungDetailOf(c: Catalog, rung: string): ReturnType<Effects["rungDetail"]> {
175
- try {
176
- const canonical = rungInfo(c, rung).canonical;
177
- return {
178
- values: valuesUsed(c, canonical),
179
- evidence: evidenceLine(evidenceOf(runEvidence(c), canonical)),
180
- };
181
- } catch {
182
- return { values: [], evidence: null };
183
- }
184
- }
185
-
186
- /** Spec 1.2 §6.3: a rung's nearest stand-ins; none for a rung that does not parse. */
187
- export function suggestFor(c: Catalog, rung: string): Suggestion[] {
188
- try {
189
- return suggestStandIns(c, rungInfo(c, rung).canonical);
190
- } catch {
191
- return [];
192
- }
193
- }
194
-
195
- /** The word for `here`: the bound repo's profile, or the global active one. */
196
- export const hereWord = (p: { repo: string | null }): string => (p.repo ? "this repo" : "active");
197
-
198
- /**
199
- * What changes when a run changes: the mtimes of its record files and of its roles folder, plus the
200
- * profiles folder, config.json and projects.json (a saved budget cap or another active profile moves the
201
- * run's budget fraction).
202
- */
203
- export function stampOf(dir: string): string {
204
- const p = runPaths(dir);
205
- return [
206
- p.runs,
207
- p.routes,
208
- p.jev,
209
- p.agents,
210
- p.state,
211
- p.stateJson,
212
- p.ledger,
213
- p.roles,
214
- profilesDir(),
215
- configFile(),
216
- projectsFile(),
217
- ]
218
- .map((f) => {
219
- try {
220
- return String(statSync(f).mtimeMs);
221
- } catch {
222
- return "-";
223
- }
224
- })
225
- .join(" ");
226
- }
227
-
228
- /**
229
- * The run list, each row computed again only when its run's files changed or a role is live (spec §9.4:
230
- * runs data memoised by mtime). A live run's elapsed times move without any file changing.
231
- */
232
- export function memoRuns(compute: (run: Run) => RunRow, stamp: (dir: string) => string = stampOf) {
233
- const cache = new Map<string, { stamp: string; row: RunRow }>();
234
- return (runs: Run[]): RunRow[] =>
235
- runs.map((r) => {
236
- const s = stamp(r.dir);
237
- const hit = cache.get(r.dir);
238
- // the wait is computed with the row, once per stamp change; the views age it (waitingLine)
239
- if (hit && hit.stamp === s && hit.row.live === 0) return hit.row;
240
- const row = compute(r);
241
- cache.set(r.dir, { stamp: s, row });
242
- return row;
243
- });
244
- }
245
-
246
- export function rowOf(s: RunSummary): RunRow {
247
- return {
248
- waiting: s.waiting,
249
- id: s.id,
250
- title: s.title,
251
- repo: s.repo,
252
- createdAt: s.createdAt,
253
- live: s.live.length,
254
- roleRuns: s.totals.runs,
255
- landed: s.milestones.length,
256
- budget: s.budget?.fraction ?? null,
257
- session: s.session ? sessionKey(s.session) : null,
258
- };
259
- }
260
-
261
- /** How long a burst of file events is gathered into one redraw. */
262
- const WATCH_SETTLE_MS = 100;
263
-
264
- /** What `watchDirs` watches and times with; tests pass fakes. */
265
- export interface WatchPorts {
266
- watch(dir: string, fire: () => void): { on(event: "error", f: () => void): unknown; close(): void };
267
- setTimeout(f: () => void, ms: number): unknown;
268
- clearTimeout(h: unknown): void;
269
- /** the folder's real path: macOS reports FSEvents under it (/var is /private/var) */
270
- realpath(dir: string): string;
271
- }
272
-
273
- const LIVE_WATCH: WatchPorts = {
274
- watch: (dir, fire) => watch(dir, { recursive: true }, fire),
275
- setTimeout: (f, ms) => setTimeout(f, ms),
276
- clearTimeout: (h) => clearTimeout(h as ReturnType<typeof setTimeout>),
277
- realpath: (dir) => realpathSync(dir),
278
- };
279
-
280
- /**
281
- * `fs.watch` on each run folder's real path, recursive; null when any of them cannot be watched. After the
282
- * returned stop, `onChange` is never called, even for an event already queued.
283
- */
284
- export function watchDirs(
285
- dirs: string[],
286
- onChange: () => void,
287
- ports: Partial<WatchPorts> = {},
288
- ): (() => void) | null {
289
- const io = { ...LIVE_WATCH, ...ports };
290
- const watchers: { close(): void }[] = [];
291
- let timer: unknown = null;
292
- let stopped = false;
293
- const fire = () => {
294
- if (stopped || timer !== null) return;
295
- timer = io.setTimeout(() => {
296
- timer = null;
297
- if (!stopped) onChange();
298
- }, WATCH_SETTLE_MS);
299
- };
300
- const stop = () => {
301
- stopped = true;
302
- if (timer !== null) io.clearTimeout(timer);
303
- timer = null;
304
- for (const w of watchers) w.close();
305
- };
306
- try {
307
- for (const d of dirs) {
308
- const w = io.watch(io.realpath(d), fire);
309
- // a watcher that breaks later (the folder removed) just stops; the screen's slow poll still reads
310
- w.on("error", () => w.close());
311
- watchers.push(w);
312
- }
313
- } catch {
314
- stop();
315
- return null;
316
- }
317
- return stop;
318
- }
319
-
320
- /**
321
- * The live effects. `repo` is the git toplevel the TUI was opened in (null outside one): inside a repo
322
- * bound to a profile, `here` is that profile and activating binds the repo instead of the global profile.
323
- */
324
- export function liveEffects(
325
- repo: string | null = null,
326
- host: HostContext = { host: "unknown", session: null, conflict: null },
327
- ): Effects {
328
- const deps = defaultDeps(host);
329
- const rows = memoRuns((r) => rowOf(summarizeRun(deps, r)));
330
- const harnesses = HARNESS_KEYS.filter((h) => adapterFor(h));
331
- const bound = () => (repo !== null && readProjects().bindings[repo] !== undefined ? repo : null);
332
- return {
333
- host,
334
- version: VERSION,
335
- doctor: () =>
336
- doctor({ host, repo, bunVersion: Bun.version, version: VERSION, handshake: () => mcpHandshake() }),
337
- runs() {
338
- const { runs, corrupt } = listRuns();
339
- return { rows: rows(runs), warnings: corrupt.map((c) => `skipped run ${c.id}: ${c.reason}`) };
340
- },
341
- sessions: () => sessionRows(deps),
342
- session: (key) => sessionDetail(deps, key),
343
- role: (run, dispatchId) => roleDetail(deps, run, dispatchId),
344
- milestone: (run, name) => milestoneDetail(run, name),
345
- watch: watchDirs,
346
- async cancel(run, name) {
347
- const r = await cancel(deps, run, name);
348
- return `${r.record.name} ${r.record.status}`;
349
- },
350
- profiles: () => ({ names: listProfiles(), active: activeName(), here: activeName(repo), repo: bound() }),
351
- readProfile: readProfileDoc,
352
- catalog(billing) {
353
- return {
354
- catalog: loadCatalog({ timings: false }),
355
- models: catalogQuery({ scoredOnly: false, limit: Number.MAX_SAFE_INTEGER }, billing).models,
356
- };
357
- },
358
- validate: (p, c) => validateHere(p, c, undefined, host.host),
359
- enforcement: enforcementOf,
360
- harnesses,
361
- isolation(h) {
362
- const a = adapterFor(h);
363
- return {
364
- ...(a?.isolationNote ? { note: a.isolationNote } : {}),
365
- ...(a?.isolationKey ? { key: a.isolationKey } : {}),
366
- };
367
- },
368
- agents: (p) => agentFiles(p, VERSION).map((f) => f.name),
369
- async save(name, patch, treatLikes, shown) {
370
- for (const [rung, like] of Object.entries(treatLikes)) await saveTreatLike(rung, like);
371
- return patchProfile(name, patch, {
372
- host: host.host,
373
- ...(shown === undefined ? {} : { expect: shown }),
374
- });
375
- },
376
- activate: (name, scope) => activate(name, scope, host.host),
377
- create: (name, from) => createProfile(name, from, host.host),
378
- remove: deleteProfile,
379
- refreshCatalog: () => refreshDiscovery(),
380
- syncSources: () => syncSources(),
381
- sources: () => sourceRows(),
382
- suggest: suggestFor,
383
- rungDetail: rungDetailOf,
384
- };
385
- }