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