tumwater 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (446) hide show
  1. package/README.md +27 -13
  2. package/dist/build-info.json +2 -2
  3. package/dist/src/agent/agent-args.js +8 -9
  4. package/dist/src/agent/agent-backend.js +8 -6
  5. package/dist/src/agent/backend-traits.js +42 -25
  6. package/dist/src/agent/claude/claude-account.js +71 -204
  7. package/dist/src/agent/claude/claude-args.js +9 -6
  8. package/dist/src/agent/claude/claude-backend.js +10 -10
  9. package/dist/src/agent/claude/claude-env.js +1 -1
  10. package/dist/src/agent/claude/claude-stream.js +4 -3
  11. package/dist/src/agent/claude/claude-write-guard.js +49 -0
  12. package/dist/src/agent/claude/write-guard-hook.js +13 -0
  13. package/dist/src/agent/codex/codex-account.js +119 -0
  14. package/dist/src/agent/codex/codex-args.js +181 -0
  15. package/dist/src/agent/codex/codex-backend.js +279 -0
  16. package/dist/src/agent/codex/codex-catalog.js +97 -0
  17. package/dist/src/agent/codex/codex-env.js +18 -0
  18. package/dist/src/agent/codex/codex-hook-trust.js +246 -0
  19. package/dist/src/agent/codex/codex-patch-paths.js +36 -0
  20. package/dist/src/agent/codex/codex-rollout.js +179 -0
  21. package/dist/src/agent/codex/codex-stream.js +402 -0
  22. package/dist/src/agent/codex/codex-write-guard.js +86 -0
  23. package/dist/src/agent/codex/write-guard-hook.js +12 -0
  24. package/dist/src/agent/image-input.js +29 -24
  25. package/dist/src/agent/notes-shim.js +11 -12
  26. package/dist/src/agent/opencode/opencode-args.js +0 -1
  27. package/dist/src/agent/opencode/opencode-backend.js +12 -9
  28. package/dist/src/agent/opencode/opencode-catalog.js +55 -29
  29. package/dist/src/agent/opencode/opencode-config.js +19 -14
  30. package/dist/src/agent/opencode/opencode-env.js +0 -1
  31. package/dist/src/agent/opencode/opencode-session-id.js +1 -2
  32. package/dist/src/agent/opencode/opencode-stream.js +1 -1
  33. package/dist/src/agent/opencode/write-guard-plugin/index.js +48 -0
  34. package/dist/src/agent/role-notes-write.js +5 -6
  35. package/dist/src/agent/run-agent.js +465 -448
  36. package/dist/src/agent/run-settle.js +214 -0
  37. package/dist/src/agent/session-ledger.js +86 -16
  38. package/dist/src/agent/subscription-account.js +220 -0
  39. package/dist/src/agent/write-guard.js +148 -0
  40. package/dist/src/backlog/backlog-archive.js +246 -55
  41. package/dist/src/backlog/backlog-cli-view.js +280 -0
  42. package/dist/src/backlog/backlog-cli.js +85 -274
  43. package/dist/src/backlog/backlog-commit.js +4 -5
  44. package/dist/src/backlog/backlog-eligibility.js +175 -242
  45. package/dist/src/backlog/backlog-index.js +149 -0
  46. package/dist/src/backlog/backlog-md.js +54 -6
  47. package/dist/src/backlog/backlog-parts.js +254 -0
  48. package/dist/src/backlog/backlog-release.js +117 -0
  49. package/dist/src/backlog/backlog-render.js +27 -0
  50. package/dist/src/backlog/backlog-structure.js +99 -156
  51. package/dist/src/backlog/backlog-write.js +40 -127
  52. package/dist/src/backlog/backlog.js +21 -6
  53. package/dist/src/backlog/question-answer.js +21 -15
  54. package/dist/src/baseline/main-baseline.js +89 -20
  55. package/dist/src/baseline/main-culprit.js +94 -0
  56. package/dist/src/baseline/main-guardrails.js +209 -0
  57. package/dist/src/baseline/main-red.js +139 -32
  58. package/dist/src/baseline/main-revert.js +133 -0
  59. package/dist/src/baseline/main-watch.js +184 -0
  60. package/dist/src/brief/brief-edit.js +9 -11
  61. package/dist/src/brief/brief.js +31 -51
  62. package/dist/src/budget/budget.js +30 -20
  63. package/dist/src/budget/fallback-breaker.js +26 -27
  64. package/dist/src/build/build-check-detect.js +23 -15
  65. package/dist/src/build/build-check-events.js +25 -6
  66. package/dist/src/build/build-check-report.js +49 -9
  67. package/dist/src/build/build-check-scoped.js +18 -11
  68. package/dist/src/build/build-check.js +2 -1
  69. package/dist/src/build/build-info.js +2 -3
  70. package/dist/src/build/build-stage.js +9 -14
  71. package/dist/src/build/dep-install.js +1 -2
  72. package/dist/src/change/change-data.js +123 -31
  73. package/dist/src/change/change-render.js +46 -5
  74. package/dist/src/cli/brief-command.js +3 -4
  75. package/dist/src/cli/cli-args.js +118 -33
  76. package/dist/src/cli/cli-command-args.js +46 -18
  77. package/dist/src/cli/cli-editor.js +2 -1
  78. package/dist/src/cli/cli-flag-specs.js +150 -45
  79. package/dist/src/cli/cli-init.js +51 -0
  80. package/dist/src/cli/cli-marker-commands.js +4 -4
  81. package/dist/src/cli/cli-query-commands.js +308 -590
  82. package/dist/src/cli/cli-repo-gate.js +11 -1
  83. package/dist/src/cli/cli-role-command.js +420 -0
  84. package/dist/src/cli/cli-start-summary.js +160 -0
  85. package/dist/src/cli/{cli-run.js → cli-start.js} +165 -237
  86. package/dist/src/cli/completion.js +149 -22
  87. package/dist/src/cli/config-commands.js +41 -18
  88. package/dist/src/cli/follow-view.js +75 -0
  89. package/dist/src/cli/help.js +189 -73
  90. package/dist/src/cli/log-commands.js +148 -82
  91. package/dist/src/cli/question-commands.js +205 -10
  92. package/dist/src/cli.js +106 -43
  93. package/dist/src/collections.js +26 -10
  94. package/dist/src/concurrency/check-permit.js +20 -3
  95. package/dist/src/concurrency/lock.js +17 -13
  96. package/dist/src/concurrency/provider-lease.js +44 -8
  97. package/dist/src/concurrency/semaphore.js +102 -26
  98. package/dist/src/config/config-aliases.js +0 -1
  99. package/dist/src/config/config-editable-keys.js +13 -1
  100. package/dist/src/config/config-fallbacks.js +93 -56
  101. package/dist/src/config/config-field-checks.js +40 -10
  102. package/dist/src/config/config-keys.js +30 -8
  103. package/dist/src/config/config-live.js +63 -24
  104. package/dist/src/config/config-request.js +137 -0
  105. package/dist/src/config/config-schema.js +31 -24
  106. package/dist/src/config/config-selector-checks.js +123 -0
  107. package/dist/src/config/config-validation-agents.js +80 -0
  108. package/dist/src/config/config-validation-model.js +116 -0
  109. package/dist/src/config/config-validation.js +141 -199
  110. package/dist/src/config/config-views.js +127 -53
  111. package/dist/src/config/config-write-core.js +135 -0
  112. package/dist/src/config/config-write.js +131 -264
  113. package/dist/src/config/config.js +48 -38
  114. package/dist/src/config/model-selector.js +34 -34
  115. package/dist/src/doctor/doctor-backlog-bugs.js +287 -0
  116. package/dist/src/doctor/doctor-backlog-plans.js +263 -0
  117. package/dist/src/doctor/doctor-backlog.js +100 -496
  118. package/dist/src/doctor/doctor-check-names.js +81 -0
  119. package/dist/src/doctor/doctor-checks.js +52 -579
  120. package/dist/src/doctor/doctor-claude-checks.js +145 -0
  121. package/dist/src/doctor/doctor-codex-checks.js +279 -0
  122. package/dist/src/doctor/doctor-contention-checks.js +380 -0
  123. package/dist/src/doctor/doctor-holds.js +82 -66
  124. package/dist/src/doctor/doctor-install.js +2 -1
  125. package/dist/src/doctor/doctor-model-checks.js +236 -402
  126. package/dist/src/doctor/doctor-notify.js +3 -4
  127. package/dist/src/doctor/doctor-opencode-checks.js +162 -0
  128. package/dist/src/doctor/doctor-orphans.js +182 -63
  129. package/dist/src/doctor/doctor-project-checks.js +303 -0
  130. package/dist/src/doctor/doctor.js +121 -77
  131. package/dist/src/doctor/image-probe.js +21 -8
  132. package/dist/src/doctor/outcome.js +3 -2
  133. package/dist/src/doctor/probe.js +7 -11
  134. package/dist/src/events/event-format.js +181 -86
  135. package/dist/src/events/event-labels.js +12 -5
  136. package/dist/src/events/event-legacy.js +39 -15
  137. package/dist/src/events/event-read.js +39 -39
  138. package/dist/src/events/event-tally.js +16 -0
  139. package/dist/src/events/event-window.js +60 -24
  140. package/dist/src/events/events.js +30 -13
  141. package/dist/src/events/notify.js +7 -7
  142. package/dist/src/failure/failure-cluster.js +12 -12
  143. package/dist/src/failure/failure-data.js +56 -56
  144. package/dist/src/failure/failure-render.js +31 -23
  145. package/dist/src/failure/failure-state-change.js +17 -12
  146. package/dist/src/failure/time-spend.js +99 -35
  147. package/dist/src/files/files.js +111 -35
  148. package/dist/src/files/json-files.js +3 -3
  149. package/dist/src/files/json-object.js +28 -7
  150. package/dist/src/files/stat-cache.js +2 -2
  151. package/dist/src/files/tail.js +1 -1
  152. package/dist/src/gates/bootstrap-gates.js +4 -5
  153. package/dist/src/gates/budget-gates.js +177 -84
  154. package/dist/src/gates/disk-gate.js +1 -1
  155. package/dist/src/gates/gate-polls.js +108 -58
  156. package/dist/src/gates/maintenance-quota.js +10 -13
  157. package/dist/src/gates/pause-gates.js +28 -10
  158. package/dist/src/gates/readiness.js +7 -3
  159. package/dist/src/gates/role-cap-gates.js +8 -8
  160. package/dist/src/gates/startup-gate.js +94 -32
  161. package/dist/src/gates/streak-gate.js +54 -30
  162. package/dist/src/git/commit-message.js +77 -46
  163. package/dist/src/git/git-diff.js +16 -10
  164. package/dist/src/git/git-run.js +79 -38
  165. package/dist/src/git/git.js +36 -34
  166. package/dist/src/git/slot-salvage.js +253 -0
  167. package/dist/src/git/slots-state.js +6 -6
  168. package/dist/src/git/worktree-pool.js +132 -38
  169. package/dist/src/git/worktree-use.js +4 -11
  170. package/dist/src/git/worktree.js +94 -37
  171. package/dist/src/gui/gui-admin-endpoints.js +96 -0
  172. package/dist/src/gui/gui-args.js +79 -8
  173. package/dist/src/gui/gui-backlog-commands.js +42 -18
  174. package/dist/src/gui/gui-backlog-endpoints.js +155 -0
  175. package/dist/src/gui/gui-command.js +8 -13
  176. package/dist/src/gui/gui-config-commands.js +94 -72
  177. package/dist/src/gui/gui-endpoint-commands.js +4 -3
  178. package/dist/src/gui/gui-endpoint-common.js +4 -4
  179. package/dist/src/gui/gui-endpoints.js +11 -380
  180. package/dist/src/gui/gui-history-endpoints.js +301 -0
  181. package/dist/src/gui/gui-operator-commands.js +108 -13
  182. package/dist/src/gui/gui-prompt-commands.js +11 -14
  183. package/dist/src/gui/gui-report-endpoints.js +90 -0
  184. package/dist/src/gui/gui-server.js +11 -4
  185. package/dist/src/gui/gui-team-commands.js +7 -7
  186. package/dist/src/gui/http-body.js +1 -1
  187. package/dist/src/history/history-data.js +122 -234
  188. package/dist/src/history/history-queued.js +147 -0
  189. package/dist/src/history/history-render.js +42 -22
  190. package/dist/src/history/history.js +193 -49
  191. package/dist/src/inbox/inbox-attachments.js +29 -30
  192. package/dist/src/inbox/inbox-cancel.js +10 -11
  193. package/dist/src/inbox/inbox-edit.js +9 -9
  194. package/dist/src/inbox/inbox-submit.js +18 -18
  195. package/dist/src/inbox/inbox-top.js +25 -9
  196. package/dist/src/inbox/inbox.js +42 -43
  197. package/dist/src/inbox/pending-prompt.js +26 -26
  198. package/dist/src/inbox/prompt-commands.js +62 -49
  199. package/dist/src/inbox/prompt-not-before.js +2 -2
  200. package/dist/src/init/init-templates.js +1 -2
  201. package/dist/src/init/init.js +28 -23
  202. package/dist/src/merge/backlog-conflicts.js +215 -35
  203. package/dist/src/merge/merge-batch.js +14 -8
  204. package/dist/src/merge/merge-cancel.js +53 -0
  205. package/dist/src/merge/merge-check-failures.js +44 -22
  206. package/dist/src/merge/merge-core.js +81 -32
  207. package/dist/src/merge/merge-diff.js +5 -4
  208. package/dist/src/merge/merge-drain.js +150 -11
  209. package/dist/src/merge/merge-git.js +177 -38
  210. package/dist/src/merge/merge-pipeline.js +15 -3
  211. package/dist/src/merge/merge-post-review.js +90 -0
  212. package/dist/src/merge/merge-queue-render.js +49 -11
  213. package/dist/src/merge/merge-queue.js +31 -15
  214. package/dist/src/merge/merge-requeue.js +102 -0
  215. package/dist/src/merge/merge-slot.js +61 -16
  216. package/dist/src/merge/merge-stack.js +33 -11
  217. package/dist/src/merge/merge-to-main.js +86 -27
  218. package/dist/src/merge/merge-top.js +69 -0
  219. package/dist/src/merge/merge-verify.js +12 -10
  220. package/dist/src/notes-cli.js +30 -15
  221. package/dist/src/operator/operator-commands.js +192 -81
  222. package/dist/src/operator/operator-intent.js +69 -42
  223. package/dist/src/operator/operator-requests.js +35 -40
  224. package/dist/src/operator/retire.js +5 -6
  225. package/dist/src/operator/revert.js +142 -34
  226. package/dist/src/orchestrator/orchestrator-launch.js +60 -52
  227. package/dist/src/orchestrator/orchestrator-poll.js +320 -0
  228. package/dist/src/orchestrator/orchestrator-scheduling.js +68 -369
  229. package/dist/src/orchestrator/orchestrator.js +128 -369
  230. package/dist/src/orchestrator/retention.js +42 -8
  231. package/dist/src/orchestrator/scheduling-admit.js +116 -0
  232. package/dist/src/orchestrator/scheduling-scope.js +201 -0
  233. package/dist/src/orchestrator/scheduling-skip.js +185 -0
  234. package/dist/src/orchestrator/startup-migration-steps.js +8 -7
  235. package/dist/src/orchestrator/startup-migrations.js +11 -7
  236. package/dist/src/paths.js +116 -49
  237. package/dist/src/pi/command-shape.js +12 -1
  238. package/dist/src/pi/pi-args.js +6 -4
  239. package/dist/src/pi/pi-backend.js +6 -6
  240. package/dist/src/pi/pi-bin.js +54 -63
  241. package/dist/src/pi/pi-listed-models.js +53 -0
  242. package/dist/src/pi/pi-models.js +63 -30
  243. package/dist/src/pi/pi-run-result.js +1 -1
  244. package/dist/src/pi/pi-settings.js +1 -1
  245. package/dist/src/pi/pi-stream.js +6 -6
  246. package/dist/src/pi/pi-watchdogs.js +18 -19
  247. package/dist/src/pi-extension/bounded-output.js +5 -3
  248. package/dist/src/pi-extension/context-budget.js +2 -2
  249. package/dist/src/pi-extension/context-shake.js +1 -1
  250. package/dist/src/pi-extension/context-window.js +0 -1
  251. package/dist/src/pi-extension/full-output.js +3 -10
  252. package/dist/src/pi-extension/strip-thinking.js +0 -1
  253. package/dist/src/pi-extension/trim-system-prompt.js +3 -4
  254. package/dist/src/pi-extension/write-guard.js +37 -0
  255. package/dist/src/process/launch-services.js +1 -2
  256. package/dist/src/process/node-bin.js +16 -0
  257. package/dist/src/process/process-group.js +8 -18
  258. package/dist/src/process/process.js +41 -2
  259. package/dist/src/process/run-marker.js +18 -6
  260. package/dist/src/process/supervisor.js +22 -21
  261. package/dist/src/prompt/dialect.js +17 -2
  262. package/dist/src/prompt/gate-notes.js +203 -0
  263. package/dist/src/prompt/gate-prompts.js +121 -211
  264. package/dist/src/prompt/notes-backlog.js +181 -0
  265. package/dist/src/prompt/principles.js +1 -2
  266. package/dist/src/prompt/prompt-followup.js +19 -14
  267. package/dist/src/prompt/prompt.js +129 -204
  268. package/dist/src/redeploy/redeploy-policy.js +24 -19
  269. package/dist/src/redeploy/redeploy-probes.js +1 -1
  270. package/dist/src/redeploy/redeploy.js +2 -3
  271. package/dist/src/redeploy/redeployer.js +20 -20
  272. package/dist/src/redeploy/self-reload.js +2 -1
  273. package/dist/src/report/report-data.js +161 -77
  274. package/dist/src/report/report-fold.js +129 -52
  275. package/dist/src/report/report-main-red.js +55 -0
  276. package/dist/src/report/report-render.js +133 -64
  277. package/dist/src/report/report-types.js +1 -0
  278. package/dist/src/report/report.js +26 -9
  279. package/dist/src/request-timeouts.js +2 -2
  280. package/dist/src/review/known-flakes.js +100 -1
  281. package/dist/src/review/post-merge-review.js +106 -0
  282. package/dist/src/review/protected-edits.js +4 -5
  283. package/dist/src/review/review-followup.js +8 -8
  284. package/dist/src/review/review-precheck.js +66 -23
  285. package/dist/src/review/review-run.js +395 -0
  286. package/dist/src/review/review-verdict.js +2 -2
  287. package/dist/src/review/review.js +155 -360
  288. package/dist/src/review/secret-leaks.js +36 -16
  289. package/dist/src/review/suite-rerun.js +0 -1
  290. package/dist/src/roles/legacy-role-migration.js +80 -7
  291. package/dist/src/roles/role-catalog.js +69 -12
  292. package/dist/src/roles/role-guidance.js +55 -20
  293. package/dist/src/roles/role-labels.js +1 -2
  294. package/dist/src/roles/role-render.js +67 -50
  295. package/dist/src/roles/role-view.js +59 -47
  296. package/dist/src/roles/roles.js +31 -31
  297. package/dist/src/roles/runner-ids.js +97 -0
  298. package/dist/src/run/acceptance-greps.js +119 -0
  299. package/dist/src/run/feedback-filings.js +51 -0
  300. package/dist/src/{tick → run}/qa-coverage.js +9 -27
  301. package/dist/src/{tick/tick-apply.js → run/run-apply.js} +95 -94
  302. package/dist/src/run/run-detail-data.js +179 -0
  303. package/dist/src/run/run-detail-render.js +98 -0
  304. package/dist/src/run/run-detail.js +168 -0
  305. package/dist/src/{tick/tick-finalize.js → run/run-finalize.js} +72 -60
  306. package/dist/src/{tick/tick-outcome.js → run/run-outcome.js} +6 -7
  307. package/dist/src/{tick/tick-prompt.js → run/run-prompt.js} +108 -73
  308. package/dist/src/{tick/tick-resume.js → run/run-resume.js} +55 -26
  309. package/dist/src/{tick/tick-stage.js → run/run-stage.js} +108 -50
  310. package/dist/src/{tick/tick-timing.js → run/run-timing.js} +43 -41
  311. package/dist/src/{tick/tick-usage.js → run/run-usage.js} +45 -37
  312. package/dist/src/{tick/tick-verdict.js → run/run-verdict.js} +91 -56
  313. package/dist/src/run/selfcheck-digest.js +40 -0
  314. package/dist/src/run/stage-backlog-checks.js +249 -0
  315. package/dist/src/run/stage-check.js +172 -0
  316. package/dist/src/run/stage-path-checks.js +730 -0
  317. package/dist/src/{loop → runner}/leftover.js +80 -25
  318. package/dist/src/{loop → runner}/model-fallback.js +12 -12
  319. package/dist/src/runner/revision.js +202 -0
  320. package/dist/src/{loop/loop-fallback.js → runner/runner-fallback.js} +8 -8
  321. package/dist/src/runner/runner-outcome.js +107 -0
  322. package/dist/src/{loop/loop-pi.js → runner/runner-pi.js} +140 -131
  323. package/dist/src/{loop/loop-recovery.js → runner/runner-recovery.js} +21 -15
  324. package/dist/src/{loop/loop-tick.js → runner/runner-run.js} +218 -240
  325. package/dist/src/{loop/loop-state.js → runner/runner-state-load.js} +61 -114
  326. package/dist/src/runner/runner-state.js +102 -0
  327. package/dist/src/{loop/loop.js → runner/runner.js} +123 -120
  328. package/dist/src/scheduling/backoff.js +38 -38
  329. package/dist/src/scheduling/claims.js +35 -22
  330. package/dist/src/scheduling/once-round.js +22 -22
  331. package/dist/src/scheduling/quiet-hours.js +5 -6
  332. package/dist/src/scheduling/scheduling.js +74 -64
  333. package/dist/src/scheduling/work-merged-cache.js +2 -2
  334. package/dist/src/status/status-budget.js +55 -47
  335. package/dist/src/status/status-data.js +43 -16
  336. package/dist/src/status/status-inbox.js +2 -2
  337. package/dist/src/status/status-install.js +0 -1
  338. package/dist/src/status/status-polls.js +28 -28
  339. package/dist/src/status/status-roles.js +39 -13
  340. package/dist/src/team/error-storm.js +8 -8
  341. package/dist/src/team/failure-spread.js +2 -2
  342. package/dist/src/team/orchestrator-info.js +15 -9
  343. package/dist/src/team/reclaim.js +5 -6
  344. package/dist/src/team/team-hold.js +7 -7
  345. package/dist/src/team/team-polls.js +91 -50
  346. package/dist/src/team/team-state.js +8 -2
  347. package/dist/src/text/datetime.js +63 -17
  348. package/dist/src/text/format.js +48 -11
  349. package/dist/src/text/markdown.js +8 -5
  350. package/dist/src/text/phrases.js +57 -23
  351. package/dist/src/text/suggest.js +1 -1
  352. package/dist/src/text/text.js +67 -13
  353. package/dist/src/ui/badges.js +65 -37
  354. package/dist/src/ui/gui/gui-client-backlog.js +292 -0
  355. package/dist/src/ui/gui/gui-client-boot.js +32 -26
  356. package/dist/src/ui/gui/gui-client-composer.js +8 -9
  357. package/dist/src/ui/gui/gui-client-drawer-config.js +212 -0
  358. package/dist/src/ui/gui/gui-client-drawer.js +126 -285
  359. package/dist/src/ui/gui/gui-client-feed.js +271 -0
  360. package/dist/src/ui/gui/gui-client-history.js +434 -67
  361. package/dist/src/ui/gui/gui-client-labels.js +1 -2
  362. package/dist/src/ui/gui/gui-client-markdown.js +1 -2
  363. package/dist/src/ui/gui/gui-client-model.js +27 -36
  364. package/dist/src/ui/gui/gui-client-operator.js +55 -51
  365. package/dist/src/ui/gui/gui-client-pending.js +227 -23
  366. package/dist/src/ui/gui/gui-client-report-chart.js +464 -0
  367. package/dist/src/ui/gui/gui-client-report.js +87 -427
  368. package/dist/src/ui/gui/{gui-client-loops.js → gui-client-roles.js} +43 -44
  369. package/dist/src/ui/gui/gui-client-settings.js +56 -4
  370. package/dist/src/ui/gui/gui-client-team.js +25 -465
  371. package/dist/src/ui/gui/gui-client.js +27 -25
  372. package/dist/src/ui/gui/gui-icons.js +0 -2
  373. package/dist/src/ui/gui/gui-page.js +9 -7
  374. package/dist/src/ui/gui/gui-styles.js +34 -34
  375. package/dist/src/ui/progress-data.js +9 -11
  376. package/dist/src/ui/{tick-progress-model.js → run-progress-model.js} +31 -31
  377. package/dist/src/ui/status-model.js +133 -125
  378. package/dist/src/ui/status-payload.js +136 -49
  379. package/dist/src/ui/status-render.js +101 -82
  380. package/dist/src/ui/team-alerts.js +248 -57
  381. package/dist/src/ui/tone.js +5 -6
  382. package/dist/src/ui/transcript-run.js +58 -0
  383. package/dist/src/ui/transcript-tail.js +28 -20
  384. package/dist/src/ui/transcript.js +63 -23
  385. package/dist/src/ui/tui/tui-app.js +0 -1
  386. package/dist/src/ui/tui/tui-backlog.js +51 -14
  387. package/dist/src/ui/tui/tui-editor-answer.js +27 -31
  388. package/dist/src/ui/tui/tui-editor-attach.js +3 -5
  389. package/dist/src/ui/tui/tui-editor-backends.js +132 -0
  390. package/dist/src/ui/tui/tui-editor-brief.js +10 -12
  391. package/dist/src/ui/tui/tui-editor-budget.js +35 -31
  392. package/dist/src/ui/tui/tui-editor-claude-reserve.js +24 -0
  393. package/dist/src/ui/tui/tui-editor-concurrency.js +24 -0
  394. package/dist/src/ui/tui/tui-editor-config.js +86 -0
  395. package/dist/src/ui/tui/tui-editor-entry.js +36 -47
  396. package/dist/src/ui/tui/tui-editor-fallback.js +31 -0
  397. package/dist/src/ui/tui/tui-editor-instances.js +37 -0
  398. package/dist/src/ui/tui/tui-editor-instructions.js +136 -0
  399. package/dist/src/ui/tui/tui-editor-interval.js +11 -9
  400. package/dist/src/ui/tui/tui-editor-model.js +29 -0
  401. package/dist/src/ui/tui/tui-editor-notes.js +3 -5
  402. package/dist/src/ui/tui/tui-editor-notify.js +25 -0
  403. package/dist/src/ui/tui/tui-editor-pause.js +48 -59
  404. package/dist/src/ui/tui/tui-editor-prompt.js +11 -16
  405. package/dist/src/ui/tui/tui-editor-provider-caps.js +27 -0
  406. package/dist/src/ui/tui/tui-editor-queued.js +5 -9
  407. package/dist/src/ui/tui/tui-editor-quiet.js +99 -35
  408. package/dist/src/ui/tui/tui-editor-retire.js +4 -6
  409. package/dist/src/ui/tui/tui-editor-role-key.js +76 -0
  410. package/dist/src/ui/tui/tui-editor-role-model.js +28 -0
  411. package/dist/src/ui/tui/tui-editor-search.js +45 -33
  412. package/dist/src/ui/tui/tui-editor-session.js +60 -34
  413. package/dist/src/ui/tui/tui-editor-slots.js +23 -0
  414. package/dist/src/ui/tui/tui-editors.js +35 -2
  415. package/dist/src/ui/tui/tui-frame.js +56 -11
  416. package/dist/src/ui/tui/tui-history.js +8 -9
  417. package/dist/src/ui/tui/tui-input.js +13 -11
  418. package/dist/src/ui/tui/tui-keymap.js +0 -1
  419. package/dist/src/ui/tui/tui-keys.js +24 -9
  420. package/dist/src/ui/tui/tui-pane-activity.js +14 -11
  421. package/dist/src/ui/tui/tui-pane-backlog.js +171 -126
  422. package/dist/src/ui/tui/tui-pane-brief.js +0 -1
  423. package/dist/src/ui/tui/tui-pane-ctx.js +2 -2
  424. package/dist/src/ui/tui/tui-pane-health.js +58 -8
  425. package/dist/src/ui/tui/tui-pane-history.js +402 -82
  426. package/dist/src/ui/tui/tui-pane-pending.js +308 -13
  427. package/dist/src/ui/tui/tui-pane-queued.js +71 -55
  428. package/dist/src/ui/tui/tui-pane-report.js +46 -14
  429. package/dist/src/ui/tui/tui-pane-session.js +25 -6
  430. package/dist/src/ui/tui/tui-pane-transcript.js +111 -37
  431. package/dist/src/ui/tui/tui-pane.js +80 -23
  432. package/dist/src/ui/tui/tui-panes.js +100 -30
  433. package/dist/src/ui/tui/tui.js +14 -14
  434. package/dist/src/verdict/fix-claim.js +82 -32
  435. package/dist/src/verdict/no-change.js +4 -4
  436. package/dist/src/verdict/refusal.js +40 -16
  437. package/dist/src/verdict/reply-contract.js +42 -31
  438. package/dist/src/version.js +0 -1
  439. package/package.json +2 -1
  440. package/dist/src/loop/revision.js +0 -70
  441. package/dist/src/roles/loop-ids.js +0 -77
  442. package/dist/src/tick/stage-check.js +0 -419
  443. package/dist/src/tick/telemetry-digest.js +0 -25
  444. package/dist/src/tick/tick-detail-data.js +0 -108
  445. package/dist/src/tick/tick-detail.js +0 -163
  446. package/dist/src/ui/gui/gui-client-sound.js +0 -53
@@ -3,11 +3,21 @@
3
3
  * `## Open` to `## Answered` with a dated operator answer. The markdown surgery itself — the
4
4
  * fence-aware `### ` block cut and the `## Answered` skeleton — lives in src/backlog/
5
5
  * question-answer.ts (the shared QUESTIONS.md core the TUI and dashboard also call); this module
6
- * is only the command's say/fail/editor jacket over that core, imported by src/cli/
7
- * cli-query-commands.ts.
6
+ * is the command's argument scan, say/fail/editor jacket and commit-and-wake tail over that core,
7
+ * routed to by src/cli.ts.
8
8
  */
9
- import { fail, say, sayJson } from "./cli-output.js";
10
- import { openQuestionList, questionListPayload, UNCLOSED_FENCE_MESSAGE, tryAnswerQuestion } from "../backlog/question-answer.js";
9
+ import { fail, say, sayJson, warnIfUncommitted } from "./cli-output.js";
10
+ import { parseCountFlag, parsePositionFlag, rejectRepeatFlag } from "./cli-args.js";
11
+ import { JSON_FLAG, rejectEqualsForm } from "./cli-flag-specs.js";
12
+ import { refuseTeamWorkspace } from "./cli-repo-gate.js";
13
+ import { promptFileValue, readPromptFileArg } from "./cli-command-args.js";
14
+ import { editInEditor } from "./cli-editor.js";
15
+ import { gotSuffix, parsePositiveInt } from "../text/text.js";
16
+ import { typoSuffix } from "../text/suggest.js";
17
+ import { commitOperatorBacklogEdit } from "../backlog/backlog-commit.js";
18
+ import { stampedSubject } from "../git/commit-message.js";
19
+ import { wakeQuestionAsker } from "../operator/operator-intent.js";
20
+ import { answeredQuestionAt, answeredQuestionCount, answeredQuestionList, answeredQuestionPayload, openQuestionAt, openQuestionList, questionListPayload, renderShownQuestion, UNCLOSED_FENCE_MESSAGE, tryAnswerQuestion } from "../backlog/question-answer.js";
11
21
  import { bodyStructureProblem, normalizeEntryBody } from "../backlog/backlog-write.js";
12
22
  /** The `tumwater questions answer` entry point: `tryAnswerQuestion` without a title check, its
13
23
  * refusals mapped to `fail` (exit 1) with the CLI's wording. Returns the answered question's
@@ -32,7 +42,7 @@ function answeredOrFail(n, outcome) {
32
42
  /** `tumwater questions answer <n> --file <path>`: the decision's lines kept (normalizeEntryBody),
33
43
  * behind the same structural guard `plan --file` uses, so a stray `## ` line cannot grow a
34
44
  * phantom section. Refusals exit 1 through `fail`. */
35
- export function answerQuestionFromText(root, n, text, expectedTitle) {
45
+ function answerQuestionFromText(root, n, text, expectedTitle) {
36
46
  const body = normalizeEntryBody(text);
37
47
  if (body === "")
38
48
  fail("questions answer needs a decision: the file is blank");
@@ -45,12 +55,12 @@ export function answerQuestionFromText(root, n, text, expectedTitle) {
45
55
  export const ANSWER_EDITOR_CUT = "<!-- tumwater: write your decision above this line; this line and everything below it are dropped -->";
46
56
  /** The editor buffer for bare `questions answer <n>`: an empty decision area, the cut line, then
47
57
  * the question's heading and body for reference. */
48
- export function answerEditorBuffer(title, body) {
58
+ function answerEditorBuffer(title, body) {
49
59
  return ["", "", ANSWER_EDITOR_CUT, `### ${title}`, body].join("\n");
50
60
  }
51
61
  /** The decision an edited `answerEditorBuffer` holds: the text before the first cut line (all of
52
62
  * it when the operator deleted the line). */
53
- export function decisionFromEditorBuffer(text) {
63
+ function decisionFromEditorBuffer(text) {
54
64
  const lines = text.split("\n");
55
65
  const cut = lines.findIndex((l) => l.trim() === ANSWER_EDITOR_CUT);
56
66
  return (cut === -1 ? lines : lines.slice(0, cut)).join("\n");
@@ -66,9 +76,194 @@ export function sayAnswered(position, title, json, decision) {
66
76
  /** One entry point for the list modes so cli.ts stays a dispatcher: --json prints the payload
67
77
  * document (an empty outbox still prints it), prose prints the numbered lines or the empty
68
78
  * line. */
69
- export function sayQuestionList(root, json) {
79
+ function sayQuestionList(root, json, pattern) {
70
80
  if (json)
71
- sayJson(questionListPayload(root));
81
+ sayJson(questionListPayload(root, pattern));
72
82
  else
73
- say(openQuestionList(root));
83
+ say(openQuestionList(root, pattern));
84
+ }
85
+ /** `questions answer`'s `--file <path>` flag: the decision comes from the file, lines kept. */
86
+ const FILE_SPEC = { names: ["--file"], value: true, valueName: "<path>" };
87
+ /** `tumwater questions`: list open questions, or answer one with `answer <n> <decision>`. */
88
+ export async function cmdQuestions(root, args) {
89
+ // No requireReadyRepo gate, like backlog: the reader degrades to an empty list on a
90
+ // missing QUESTIONS.md, so the command inspects any directory instead of refusing (a team
91
+ // workspace still refuses).
92
+ // The answer form's decision is free-form prose, so peelPositionals cannot route the
93
+ // tokens: a decision word may begin with a single dash (`questions answer 1 "-50% spend
94
+ // cap"`), and peelPositionals would hand it to the flag gate as an unknown flag — the
95
+ // same masking parsePromptArgs avoids for prompt text by owning only `--`-prefixed
96
+ // tokens. So the questions parse scans args itself: a `--json` token before the
97
+ // question's number positional is the one flag (repeats refused, equals form named by
98
+ // rejectEqualsForm), any other `--`-prefixed token before it is refused as unknown, and
99
+ // every remaining token in order is prose — subcommand, position number, then the
100
+ // decision words. Past the number positional, tokens are decision words even when one
101
+ // is spelled `--json`: unquoted decision prose (`questions answer 1 keep --json output`)
102
+ // reaches the command as separate argv tokens, and a flag scan that matched `--json`
103
+ // anywhere silently ate the token out of the recorded decision and flipped the command
104
+ // into JSON mode. Unknown `--`-prefixed tokens stay refused even there, so a misspelled
105
+ // flag is still an error rather than silent decision text.
106
+ const words = [];
107
+ let jsonFlag = false;
108
+ let numbered = false;
109
+ let file;
110
+ let answeredFlag = false;
111
+ let limit;
112
+ let grep;
113
+ let show;
114
+ for (let i = 0; i < args.length; i++) {
115
+ const arg = args[i];
116
+ if (!numbered && arg === "--show") {
117
+ rejectRepeatFlag(show !== undefined, "--show");
118
+ show = parsePositionFlag("--show", args[++i], "--show 2");
119
+ continue;
120
+ }
121
+ if (!numbered && arg === "--answered") {
122
+ rejectRepeatFlag(answeredFlag, "--answered");
123
+ answeredFlag = true;
124
+ continue;
125
+ }
126
+ if (!numbered && arg === "-n") {
127
+ rejectRepeatFlag(limit !== undefined, "-n");
128
+ limit = parseCountFlag("-n", args[++i]);
129
+ continue;
130
+ }
131
+ if (!numbered && arg === "--grep") {
132
+ rejectRepeatFlag(grep !== undefined, "--grep");
133
+ const text = args[++i];
134
+ if (text === undefined || text === "")
135
+ fail("--grep needs text (usage: tumwater questions [--answered] --grep <text>)");
136
+ grep = text;
137
+ continue;
138
+ }
139
+ if (arg === "--file") {
140
+ rejectRepeatFlag(file !== undefined, "--file");
141
+ file = promptFileValue(args[++i]);
142
+ continue;
143
+ }
144
+ if (!numbered && arg === "--json") {
145
+ rejectRepeatFlag(jsonFlag, "--json");
146
+ jsonFlag = true;
147
+ continue;
148
+ }
149
+ if (arg.startsWith("--") && arg !== "--json") {
150
+ rejectEqualsForm(arg, [JSON_FLAG, FILE_SPEC, { names: ["--show"], value: true, valueName: "<n>" }, { names: ["--grep"], value: true, valueName: "<text>" }]);
151
+ fail(`unknown argument: ${arg} (valid flags for tumwater questions: --json, --file, --answered, --show, --grep)`);
152
+ }
153
+ if (!numbered) {
154
+ // The position is a plain-decimal positive integer (parsePositiveInt's rule, the same
155
+ // one --cancel's position honors): bare Number() admitted hex ("0x2"), exponent
156
+ // ("1e2"), and whitespace-padded spellings as a valid question number, answering a
157
+ // question the operator never named.
158
+ if (parsePositiveInt(arg) !== null)
159
+ numbered = true;
160
+ }
161
+ words.push(arg);
162
+ }
163
+ if (limit !== undefined && !answeredFlag)
164
+ fail("-n needs --answered (it limits the answered questions listed)");
165
+ refuseTeamWorkspace(root);
166
+ if (show !== undefined) {
167
+ if (grep !== undefined)
168
+ fail("--grep does not apply to --show");
169
+ if (words.length > 0)
170
+ fail("--show prints one open question; it takes no subcommand");
171
+ if (file !== undefined)
172
+ fail("--file only applies to questions answer <n>");
173
+ if (answeredFlag) {
174
+ if (limit !== undefined)
175
+ fail("-n does not apply to --show");
176
+ const a = answeredQuestionAt(root, show);
177
+ if (a === null) {
178
+ const count = answeredQuestionCount(root);
179
+ const range = count > 0 ? ` — valid: 1-${count}` : "";
180
+ fail(`no answered question at position ${show} (${count} in QUESTIONS.md)${range}`);
181
+ }
182
+ if (jsonFlag)
183
+ sayJson(a);
184
+ else
185
+ say(renderShownQuestion(a));
186
+ return;
187
+ }
188
+ const q = openQuestionAt(root, show);
189
+ if (q === null) {
190
+ const count = questionListPayload(root).questions.length;
191
+ const range = count > 0 ? ` — valid: 1-${count}` : "";
192
+ fail(`no open question at position ${show} (${count} in QUESTIONS.md)${range}`);
193
+ }
194
+ if (jsonFlag)
195
+ sayJson(q);
196
+ else
197
+ say(renderShownQuestion(q));
198
+ return;
199
+ }
200
+ if (answeredFlag) {
201
+ if (words.length > 0)
202
+ fail("--answered lists answered questions; it takes no subcommand");
203
+ if (file !== undefined)
204
+ fail("--file only applies to questions answer <n>");
205
+ if (jsonFlag)
206
+ sayJson(answeredQuestionPayload(root, limit ?? 10, grep));
207
+ else
208
+ say(answeredQuestionList(root, limit ?? 10, grep));
209
+ return;
210
+ }
211
+ if (words.length === 0 && file !== undefined)
212
+ fail("--file only applies to questions answer <n>");
213
+ if (words.length === 0) {
214
+ sayQuestionList(root, jsonFlag, grep);
215
+ return;
216
+ }
217
+ if (grep !== undefined)
218
+ fail("--grep only applies to listing questions");
219
+ if (words[0] !== "answer")
220
+ fail(`unknown questions subcommand: ${words[0]} (use "answer <n> <decision>", or run \`tumwater questions\` with no subcommand to list them)${typoSuffix(words[0], ["answer"])}`);
221
+ // The offending token carries the same ` (got "...")` suffix every sibling number parser
222
+ // adds (parsePositiveInt's flag callers, --cancel); the bare `answer` form names none.
223
+ const raw = words.length > 1 ? words[1] : undefined;
224
+ const n = raw === undefined ? null : parsePositiveInt(raw);
225
+ if (n === null)
226
+ fail(`questions answer needs a positive question number: questions answer <n> "<decision>"${gotSuffix(raw)}`);
227
+ if (file !== undefined) {
228
+ if (words.length > 2)
229
+ fail(`unexpected argument ${JSON.stringify(words[2])} — with --file the decision comes from the file`);
230
+ const answered = answerQuestionFromText(root, n, readPromptFileArg(file));
231
+ await commitAndReportAnswer(root, n, jsonFlag, answered.title, answered.decision);
232
+ return;
233
+ }
234
+ const decision = words.slice(2).join(" ").trim();
235
+ if (words.length === 2) {
236
+ const open = questionListPayload(root).questions;
237
+ const question = open[n - 1];
238
+ if (question === undefined) {
239
+ const range = open.length > 0 ? ` — valid: 1-${open.length}` : "";
240
+ fail(`no question at position ${n} (${open.length} open)${range}`);
241
+ }
242
+ const edited = decisionFromEditorBuffer(editInEditor(answerEditorBuffer(question.title, question.body), `answer-${n}.md`, "pass a decision or --file <path>"));
243
+ if (edited.trim() === "")
244
+ fail("questions answer needs a decision: the editor left it blank; nothing changed");
245
+ const answered = answerQuestionFromText(root, n, edited, question.title);
246
+ await commitAndReportAnswer(root, n, jsonFlag, answered.title, answered.decision);
247
+ return;
248
+ }
249
+ if (decision === "")
250
+ fail('questions answer needs a decision: questions answer <n> "<decision>"');
251
+ // answerQuestion collapses the free-form decision to one line before writing it; report
252
+ // that written text in the --json payload so it matches the file, not the caller's spacing.
253
+ const { title, decision: writtenDecision } = answerQuestion(root, n, decision);
254
+ await commitAndReportAnswer(root, n, jsonFlag, title, writtenDecision);
255
+ }
256
+ /** Commit the answered question, report it in the requested output mode, and wake the
257
+ * asking role in prose mode — the tail every `questions answer` branch ends with. */
258
+ async function commitAndReportAnswer(root, n, jsonFlag, title, decision) {
259
+ warnIfUncommitted(await commitOperatorBacklogEdit(root, "QUESTIONS.md", stampedSubject("operator", `Answer question: ${title}`)), "QUESTIONS.md"); // vocabulary-ok: commit prefix stays
260
+ sayAnswered(n, title, jsonFlag, decision);
261
+ if (!jsonFlag)
262
+ sayAskerWake(root, title);
263
+ }
264
+ /** Prose-mode follow-up to `questions answer`: wake the asking role and print the confirmation. */
265
+ function sayAskerWake(root, title) {
266
+ const wake = wakeQuestionAsker(root, title);
267
+ if (wake !== null)
268
+ say(wake);
74
269
  }
package/dist/src/cli.js CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- // vocabulary: plain
3
2
  /** The CLI's entry point and command dispatcher — the `bin` target package.json points at
4
3
  * (dist/src/cli.js). main() runs the Node-floor gate from package.json's `engines.node`, answers a
5
4
  * `--help` topic before any command parses arguments or gates on a ready repo, resolves the repo
@@ -13,33 +12,48 @@
13
12
  * modules; this file owns the shared sequence, including each case's flag rejection and its
14
13
  * ready-repo gate. An error escaping main() funnels through its catch to fail(errorMessage(err)),
15
14
  * the CLI's one error path. */
16
- import { parseCountFlag, } from "./cli/cli-args.js";
17
- import { grepFlagSpec, GUI_FLAG_SPECS, JSON_FLAG, LAST_FLAG, N_FLAG, rejectUnknownArgs, resultFlagSpec, ROLE_FLAG, RUN_FLAG_SPECS, sinceFlagSpec, STAT_FLAG, SUMMARY_FLAG, COUNT_FLAG, typeFlagSpec, DRY_RUN_FLAG, FORCE_FLAG, STOP_CANCEL_FLAG, STOP_IN_FLAG, REPROBE_FLAG, PROBLEMS_FLAG, } from "./cli/cli-flag-specs.js";
15
+ import { parseCountFlag, parseGrepFlag, parseModelFlag, parseResultFlag } from "./cli/cli-args.js";
16
+ import { grepFlagSpec, GUI_FLAG_SPECS, JSON_FLAG, LAST_FLAG, N_FLAG, rejectUnknownArgs, resultFlagSpec, modelFlagSpec, byFlagSpec, LOGS_TRANSCRIPT_FLAG, ROLE_FLAG, START_FLAG_SPECS, sinceFlagSpec, untilFlagSpec, STAT_FLAG, PATCH_FLAG, PROMPT_FLAG, TRANSCRIPT_FLAG, SUMMARY_FLAG, COUNT_FLAG, typeFlagSpec, DRY_RUN_FLAG, FORCE_FLAG, FOLLOW_FLAG, STOP_CANCEL_FLAG, STOP_IN_FLAG, REPROBE_FLAG, PROBLEMS_FLAG, KILL_ORPHANS_FLAG, } from "./cli/cli-flag-specs.js";
18
17
  import { fail, say, sayJsonOrRender } from "./cli/cli-output.js";
19
- import { historyTrailForm, parsePromptArgs, peelPositionals } from "./cli/cli-command-args.js";
20
- import { requireReadyRepo } from "./cli/cli-repo-gate.js";
18
+ import { historyTrailForm, parseDoctorChecks, parsePromptArgs, peelPositionals } from "./cli/cli-command-args.js";
19
+ import { refuseTeamWorkspace, requireReadyRepo } from "./cli/cli-repo-gate.js";
21
20
  import { repoToplevel } from "./git/git.js";
22
21
  import { didYouMean } from "./text/suggest.js";
23
22
  import { errorMessage, gotSuffix, stripTerminalControls } from "./text/text.js";
24
23
  import { typoSuffix } from "./text/suggest.js";
25
- import { completionConfigKeys, completionRoleIds, completionScript, SUPPORTED_SHELLS } from "./cli/completion.js";
24
+ import { completionConfigKeys, completionModels, completionRoleIds, completionScript, configValueWords, SUPPORTED_SHELLS } from "./cli/completion.js";
26
25
  import { HELP, helpTopic, helpTopicForArgs, suggestCommand } from "./cli/help.js";
27
26
  import { PACKAGE_JSON, nodeFloorProblem, packageEnginesNode, packageVersion } from "./version.js";
28
27
  // The CLI's help text and its per-command topic parser live in help.ts — importing cli.ts
29
28
  // would run main(), so tests pin the topics against help.ts directly.
30
29
  /** One run's trail, shared by `history <role> <n>` and its hidden alias `tick` so the two cannot
31
30
  * drift. Positional-first: the <role> <n> tokens peel off and the remaining flags go through
32
- * rejectUnknownArgs (only --json, --last, --stat), so a listing flag such as --role is refused.
31
+ * rejectUnknownArgs (only --json, --last, --stat, --patch, --transcript, --prompt, --grep,
32
+ * --result, --model), so a listing flag such as --role is refused.
33
33
  * Arity is gated before the ready-repo check so a malformed call fails with its usage in any
34
- * directory; cmdTick owns the value validation and the --last/numbered rivals. */
34
+ * directory; cmdRunDetail owns the value validation and the --last/numbered rivals. */
35
35
  async function runTrailCommand(root, args) {
36
- const { cmdTick, TICK_USAGE } = await import("./tick/tick-detail.js");
37
- const { positionals, rest } = peelPositionals(args);
38
- rejectUnknownArgs("history", rest, [JSON_FLAG, LAST_FLAG, STAT_FLAG]);
39
- if (positionals.length < 1 || positionals.length > 2)
40
- fail(`usage: ${TICK_USAGE}`);
36
+ const { cmdRunDetail, RUN_DETAIL_USAGE } = await import("./run/run-detail.js");
37
+ const { HISTORY_GREP_VALUE_ERROR, HISTORY_RESULT_VALUE_ERROR, HISTORY_MODEL_VALUE_ERROR } = await import("./history/history.js");
38
+ // parseGrepFlag removes the pattern token, so `--grep two` cannot read "two" as a positional.
39
+ const { rest: ungrepped, pattern: grep } = parseGrepFlag(args, HISTORY_GREP_VALUE_ERROR);
40
+ const { rest: unresulted, raw: result } = parseResultFlag(ungrepped, HISTORY_RESULT_VALUE_ERROR);
41
+ const { rest: unmodeled, raw: model } = parseModelFlag(unresulted, HISTORY_MODEL_VALUE_ERROR);
42
+ const { positionals, rest } = peelPositionals(unmodeled);
43
+ rejectUnknownArgs("history", rest, [JSON_FLAG, LAST_FLAG, STAT_FLAG, PATCH_FLAG, TRANSCRIPT_FLAG, PROMPT_FLAG, { names: ["--grep"] }, { names: ["--result"] }, { names: ["--model"] }]);
44
+ if ((positionals.length < 1 && !rest.includes("--last")) || positionals.length > 2)
45
+ fail(`usage: ${RUN_DETAIL_USAGE}`);
41
46
  await requireReadyRepo(root);
42
- await cmdTick(root, positionals, rest.includes("--json"), rest.includes("--last"), rest.includes("--stat"));
47
+ await cmdRunDetail(root, positionals, rest.includes("--json"), {
48
+ last: rest.includes("--last"),
49
+ stat: rest.includes("--stat"),
50
+ patch: rest.includes("--patch"),
51
+ transcript: rest.includes("--transcript"),
52
+ prompt: rest.includes("--prompt"),
53
+ grep,
54
+ result,
55
+ model,
56
+ });
43
57
  }
44
58
  async function main() {
45
59
  const [, , command, ...args] = process.argv;
@@ -72,16 +86,16 @@ async function main() {
72
86
  const root = (await repoToplevel(process.cwd())) ?? process.cwd();
73
87
  switch (command) {
74
88
  case "init":
75
- // Imported lazily: cli/cli-run.ts pulls the whole team-booting subgraph (orchestrator,
89
+ // Imported lazily: cli/cli-start.ts pulls the whole team-booting subgraph (orchestrator,
76
90
  // supervisor, redeploy, launch-services — the run path's machinery), and every other
77
91
  // command's spawn pays module compilation for it unless the dispatch defers that load
78
- // to the commands that actually boot or serve a team.
79
- await (await import("./cli/cli-run.js")).cmdInit(root, args);
92
+ // to the commands that actually boot or serve a team. cli-init.ts stays light for init.
93
+ await (await import("./cli/cli-init.js")).cmdInit(root, args);
80
94
  break;
81
95
  case "start":
82
96
  case "run":
83
- rejectUnknownArgs("start", args, RUN_FLAG_SPECS);
84
- await (await import("./cli/cli-run.js")).cmdRun(root, args);
97
+ rejectUnknownArgs("start", args, START_FLAG_SPECS);
98
+ await (await import("./cli/cli-start.js")).cmdStart(root, args);
85
99
  break;
86
100
  case "tui":
87
101
  rejectUnknownArgs("tui", args, []);
@@ -108,7 +122,10 @@ async function main() {
108
122
  // args.
109
123
  if (args.length > 0) {
110
124
  if (args[0] !== "get" && args[0] !== "set" && args[0] !== "unset")
111
- fail(CONFIG_USAGE);
125
+ // A near-miss subcommand word gets the same typoSuffix `completion <shell>` and
126
+ // `logs --type` give, so `config gte` reads "did you mean `get`?" while a far-off
127
+ // word (`show`) and a flag (`--anything`) stay hint-free.
128
+ fail(`${CONFIG_USAGE}${typoSuffix(args[0] ?? "", ["get", "set", "unset"])}`);
112
129
  if (args[0] === "get" && args.length !== 2)
113
130
  fail("usage: tumwater config get <key>");
114
131
  if (args[0] === "set" && args.length !== 3)
@@ -124,7 +141,8 @@ async function main() {
124
141
  }
125
142
  case "report": {
126
143
  // No requireReadyRepo gate: the report aggregates files that degrade to zeros when
127
- // missing, so it runs (and prints an all-zero window) in any directory.
144
+ // missing, so it runs (and prints an all-zero window) in any directory; the team-workspace
145
+ // refusal still applies.
128
146
  rejectUnknownArgs("report", args, [
129
147
  {
130
148
  names: ["--days"],
@@ -138,9 +156,12 @@ async function main() {
138
156
  },
139
157
  { names: ["--failures"] },
140
158
  sinceFlagSpec("report --since"),
159
+ untilFlagSpec("report --until"),
141
160
  JSON_FLAG,
142
161
  ROLE_FLAG,
162
+ FOLLOW_FLAG,
143
163
  ]);
164
+ refuseTeamWorkspace(root);
144
165
  // --since is handled before the day-shape reads: it is a rival shape (totals over a
145
166
  // trailing window vs a series over whole days), not a modifier of either.
146
167
  await (await import("./report/report.js")).cmdReport(root, args);
@@ -149,23 +170,38 @@ async function main() {
149
170
  case "doctor": {
150
171
  // No requireReadyRepo gate: doctor's job is to report WHY the environment isn't ready,
151
172
  // so it must run outside a git repo and print fail lines rather than throwing.
152
- rejectUnknownArgs("doctor", args, [JSON_FLAG, REPROBE_FLAG, PROBLEMS_FLAG]);
173
+ const { positionals: checkNames, rest: doctorFlags } = peelPositionals(args);
174
+ rejectUnknownArgs("doctor", doctorFlags, [JSON_FLAG, REPROBE_FLAG, PROBLEMS_FLAG, KILL_ORPHANS_FLAG]);
175
+ const only = parseDoctorChecks(checkNames);
153
176
  const { runDoctor } = await import("./doctor/doctor.js");
177
+ let orphanKill;
178
+ if (args.includes("--kill-orphans")) {
179
+ const { killOrphans, orphanKillLines } = await import("./doctor/doctor-orphans.js");
180
+ orphanKill = await killOrphans(root);
181
+ // The text form prints the kill before the report; --json keeps stdout one document.
182
+ if (!args.includes("--json")) {
183
+ for (const line of orphanKillLines(orphanKill))
184
+ say(line);
185
+ say("");
186
+ }
187
+ }
154
188
  const { renderDoctor, renderDoctorProblems, problemsOnly } = await import("./doctor/doctor-render.js");
155
189
  // The terminal probes unclaimed-verified models by default (7-day cache); --reprobe
156
190
  // forces every claimed model. The TUI/GUI call runDoctor with the default "never", so a
157
191
  // refresh never spends a model call.
158
192
  const probeImages = args.includes("--reprobe") ? "all" : "missing";
159
- const report = await runDoctor(root, undefined, undefined, probeImages);
193
+ const report = await runDoctor(root, undefined, undefined, probeImages, only);
160
194
  // --json prints the collector's own payload (the DoctorReport object), not a re-parse of
161
195
  // the render — the `report --json` precedent. The exit-code contract below holds in both
162
196
  // forms: 1 when any check fails, 0 otherwise; warnings never fail the exit.
163
197
  // --problems keeps the full report for the render (it counts the hidden ok rows) and
164
198
  // hands --json the filtered one; the exit code below reads the full report either way.
199
+ // --kill-orphans adds the kill result to the JSON object as `orphanKill`.
200
+ const withKill = (payload) => (orphanKill === undefined ? payload : { ...payload, orphanKill });
165
201
  if (args.includes("--problems"))
166
- sayJsonOrRender(args, args.includes("--json") ? problemsOnly(report) : report, renderDoctorProblems);
202
+ sayJsonOrRender(args, args.includes("--json") ? withKill(problemsOnly(report)) : report, renderDoctorProblems);
167
203
  else
168
- sayJsonOrRender(args, report, renderDoctor);
204
+ sayJsonOrRender(args, args.includes("--json") ? withKill(report) : report, renderDoctor);
169
205
  if (report.checks.some((c) => c.level === "fail"))
170
206
  process.exitCode = 1; // Warnings never fail the exit.
171
207
  break;
@@ -173,15 +209,17 @@ async function main() {
173
209
  case "logs": {
174
210
  const { cmdLogs, GREP_VALUE_ERROR, TYPE_VALUE_ERROR } = await import("./cli/log-commands.js");
175
211
  rejectUnknownArgs("logs", args, [
176
- { names: ["-f", "--follow"] },
212
+ FOLLOW_FLAG,
177
213
  N_FLAG,
178
214
  sinceFlagSpec("logs --since"),
215
+ untilFlagSpec("logs --until"),
179
216
  grepFlagSpec(GREP_VALUE_ERROR),
180
217
  typeFlagSpec(TYPE_VALUE_ERROR),
181
218
  JSON_FLAG,
182
219
  COUNT_FLAG,
183
220
  ROLE_FLAG,
184
- { names: ["--prompt"] },
221
+ LOGS_TRANSCRIPT_FLAG,
222
+ PROMPT_FLAG,
185
223
  ]);
186
224
  await requireReadyRepo(root);
187
225
  await cmdLogs(root, args);
@@ -193,16 +231,20 @@ async function main() {
193
231
  await (await import("./cli/notify-command.js")).cmdNotify(root, args);
194
232
  break;
195
233
  case "history": {
196
- const { cmdHistory, HISTORY_GREP_VALUE_ERROR, HISTORY_RESULT_VALUE_ERROR } = await import("./history/history.js");
234
+ const { cmdHistory, HISTORY_GREP_VALUE_ERROR, HISTORY_RESULT_VALUE_ERROR, HISTORY_MODEL_VALUE_ERROR, HISTORY_BY_VALUE_ERROR } = await import("./history/history.js");
197
235
  if (historyTrailForm(args)) {
198
236
  await runTrailCommand(root, args);
199
237
  break;
200
238
  }
201
239
  rejectUnknownArgs("history", args, [
202
240
  N_FLAG,
241
+ FOLLOW_FLAG,
203
242
  sinceFlagSpec("history --since"),
243
+ untilFlagSpec("history --until"),
204
244
  grepFlagSpec(HISTORY_GREP_VALUE_ERROR),
205
245
  resultFlagSpec(HISTORY_RESULT_VALUE_ERROR),
246
+ modelFlagSpec(HISTORY_MODEL_VALUE_ERROR),
247
+ byFlagSpec(HISTORY_BY_VALUE_ERROR),
206
248
  JSON_FLAG,
207
249
  ROLE_FLAG,
208
250
  SUMMARY_FLAG,
@@ -224,13 +266,15 @@ async function main() {
224
266
  await (await import("./cli/cli-query-commands.js")).cmdBacklog(root, args);
225
267
  break;
226
268
  case "archive":
227
- rejectUnknownArgs("archive", args, []);
269
+ rejectUnknownArgs("archive", args, [JSON_FLAG]);
228
270
  await requireReadyRepo(root);
229
- await (await import("./backlog/backlog-cli.js")).cmdArchive(root);
271
+ await (await import("./backlog/backlog-cli.js")).cmdArchive(root, args);
230
272
  break;
231
273
  case "bug": {
232
- const { OPERATOR_FILE_KINDS, bugWakeText, cancelBug, moveEntryToTop, releaseEntry, editBug, editBugFromText, fileBug, fileBugFromText } = await import("./backlog/backlog-write.js");
233
- const { bugTitleOf, cancelAndAnnounce, releaseAndAnnounce, topAndAnnounce, editAndAnnounce, fileAndAnnounce, listAndAnnounce, showAndAnnounce } = await import("./backlog/backlog-cli.js");
274
+ const { OPERATOR_FILE_KINDS, bugWakeText, cancelBug, editBug, editBugFromText, fileBug, fileBugFromText } = await import("./backlog/backlog-write.js");
275
+ const { moveEntryToTop, releaseEntry } = await import("./backlog/backlog-release.js");
276
+ const { bugTitleOf, cancelAndAnnounce, releaseAndAnnounce, topAndAnnounce, editAndAnnounce, fileAndAnnounce } = await import("./backlog/backlog-cli.js");
277
+ const { listAndAnnounce, showAndAnnounce } = await import("./backlog/backlog-cli-view.js");
234
278
  const usage = 'tumwater bug "<symptom>"';
235
279
  if (args.includes("--show")) {
236
280
  showAndAnnounce(root, args, "bug");
@@ -260,8 +304,10 @@ async function main() {
260
304
  break;
261
305
  }
262
306
  case "plan": {
263
- const { OPERATOR_FILE_KINDS, cancelPlan, moveEntryToTop, releaseEntry, editPlan, editPlanFromText, filePlan, filePlanFromText, planWakeText } = await import("./backlog/backlog-write.js");
264
- const { cancelAndAnnounce, releaseAndAnnounce, topAndAnnounce, editAndAnnounce, fileAndAnnounce, listAndAnnounce, showAndAnnounce, planTitleOf } = await import("./backlog/backlog-cli.js");
307
+ const { OPERATOR_FILE_KINDS, cancelPlan, editPlan, editPlanFromText, filePlan, filePlanFromText, planWakeText } = await import("./backlog/backlog-write.js");
308
+ const { moveEntryToTop, releaseEntry } = await import("./backlog/backlog-release.js");
309
+ const { cancelAndAnnounce, releaseAndAnnounce, topAndAnnounce, editAndAnnounce, fileAndAnnounce, planTitleOf } = await import("./backlog/backlog-cli.js");
310
+ const { listAndAnnounce, showAndAnnounce } = await import("./backlog/backlog-cli-view.js");
265
311
  const usage = 'tumwater plan "<title>" [body...]';
266
312
  if (args.includes("--show")) {
267
313
  showAndAnnounce(root, args, "plan");
@@ -291,13 +337,13 @@ async function main() {
291
337
  break;
292
338
  }
293
339
  case "questions":
294
- await (await import("./cli/cli-query-commands.js")).cmdQuestions(root, args);
340
+ await (await import("./cli/question-commands.js")).cmdQuestions(root, args);
295
341
  break;
296
342
  case "brief":
297
343
  await (await import("./cli/brief-command.js")).cmdBrief(root, args);
298
344
  break;
299
345
  case "role":
300
- await (await import("./cli/cli-query-commands.js")).cmdRole(root, args);
346
+ await (await import("./cli/cli-role-command.js")).cmdRole(root, args);
301
347
  break;
302
348
  case "prompt":
303
349
  // Parse before the gate: prompt's positionals are free-form, so rejectUnknownArgs
@@ -336,8 +382,8 @@ async function main() {
336
382
  break;
337
383
  }
338
384
  case "revert": {
339
- // Acts immediately on the primary checkout under the primary-worktree lock; the single
340
- // positional (the commit to undo) is validated inside cmdRevert.
385
+ // Acts immediately on the primary checkout under the primary-worktree lock; the
386
+ // positionals (a sha, or a run) are validated inside cmdRevert.
341
387
  await requireReadyRepo(root);
342
388
  await (await import("./operator/operator-commands.js")).cmdRevert(root, args);
343
389
  break;
@@ -362,12 +408,29 @@ async function main() {
362
408
  say(key);
363
409
  break;
364
410
  }
411
+ if (args.length === 2 && args[0] === "--config-value") {
412
+ // The vocabulary `config set <key> <value>` completes for a discrete-valued key
413
+ // (boolean/backend/model/thinking); free-form keys print nothing, so a `set` on one
414
+ // completes nothing instead of offering a wrong vocabulary.
415
+ for (const word of configValueWords(args[1]))
416
+ say(word);
417
+ break;
418
+ }
419
+ if (args.length === 1 && args[0] === "--models") {
420
+ for (const m of completionModels(root))
421
+ say(m);
422
+ break;
423
+ }
365
424
  const shell = args[0];
366
- if (args.length !== 1 || !SUPPORTED_SHELLS.includes(shell))
367
- // A single near-miss shell word gets the same typoSuffix `logs --type`/`history --result`
368
- // give, so `completion zs` reads "did you mean `zsh`?" while a far-off value (`tcsh`) and
369
- // a wrong arity (none or several words, typed shown by gotSuffix) stay hint-free.
370
- fail(`completion takes one shell: bash, zsh, or fish${gotSuffix(args.length > 1 ? args[1] : shell)}${args.length === 1 ? typoSuffix(shell ?? "", SUPPORTED_SHELLS) : ""}`);
425
+ // A wrong word count names the count, not a bad shell. A single near-miss shell word gets
426
+ // the same typoSuffix `logs --type`/`history --result` give, so `completion zs` reads
427
+ // "did you mean `zsh`?" while a far-off value (`tcsh`) stays hint-free.
428
+ if (args.length === 0)
429
+ fail("completion needs a shell: bash, zsh, or fish");
430
+ if (args.length > 1)
431
+ fail(`completion takes one shell: bash, zsh, or fish (got ${args.length} words: ${args.join(" ")})`);
432
+ if (!SUPPORTED_SHELLS.includes(shell))
433
+ fail(`completion takes one shell: bash, zsh, or fish${gotSuffix(shell)}${typoSuffix(shell ?? "", SUPPORTED_SHELLS)}`);
371
434
  say(completionScript(shell));
372
435
  break;
373
436
  }