tumwater 0.1.2 → 0.2.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 (361) hide show
  1. package/README.md +74 -108
  2. package/dist/build-info.json +2 -2
  3. package/dist/src/agent/agent-args.js +24 -0
  4. package/dist/src/agent/agent-backend.js +2 -2
  5. package/dist/src/agent/backend-traits.js +60 -0
  6. package/dist/src/agent/claude/claude-account.js +61 -8
  7. package/dist/src/agent/claude/claude-args.js +12 -15
  8. package/dist/src/agent/claude/claude-backend.js +51 -41
  9. package/dist/src/agent/claude/claude-env.js +7 -3
  10. package/dist/src/agent/claude/claude-stream.js +7 -16
  11. package/dist/src/agent/image-input.js +107 -0
  12. package/dist/src/agent/notes-shim.js +41 -0
  13. package/dist/src/agent/opencode/opencode-args.js +61 -0
  14. package/dist/src/agent/opencode/opencode-backend.js +255 -0
  15. package/dist/src/agent/opencode/opencode-catalog.js +184 -0
  16. package/dist/src/agent/opencode/opencode-config.js +188 -0
  17. package/dist/src/agent/opencode/opencode-env.js +36 -0
  18. package/dist/src/agent/opencode/opencode-session-id.js +37 -0
  19. package/dist/src/agent/opencode/opencode-stream.js +278 -0
  20. package/dist/src/agent/role-notes-write.js +16 -32
  21. package/dist/src/agent/run-agent.js +95 -18
  22. package/dist/src/agent/session-ledger.js +57 -1
  23. package/dist/src/backlog/backlog-archive.js +296 -0
  24. package/dist/src/backlog/backlog-cli.js +573 -0
  25. package/dist/src/backlog/backlog-commit.js +31 -0
  26. package/dist/src/backlog/backlog-eligibility.js +357 -33
  27. package/dist/src/backlog/backlog-md.js +98 -18
  28. package/dist/src/backlog/backlog-render.js +62 -11
  29. package/dist/src/backlog/backlog-structure.js +261 -58
  30. package/dist/src/backlog/backlog-write.js +197 -319
  31. package/dist/src/backlog/backlog.js +113 -16
  32. package/dist/src/backlog/question-answer.js +339 -0
  33. package/dist/src/baseline/main-baseline.js +75 -88
  34. package/dist/src/baseline/main-red.js +38 -30
  35. package/dist/src/brief/brief-edit.js +43 -0
  36. package/dist/src/{brief.js → brief/brief.js} +66 -10
  37. package/dist/src/budget/budget.js +32 -15
  38. package/dist/src/budget/fallback-breaker.js +8 -7
  39. package/dist/src/build/build-check-detect.js +8 -7
  40. package/dist/src/build/build-check-events.js +68 -22
  41. package/dist/src/build/build-check-report.js +22 -23
  42. package/dist/src/build/build-check-scoped.js +51 -62
  43. package/dist/src/build/build-check.js +15 -14
  44. package/dist/src/build/build-info.js +2 -1
  45. package/dist/src/build/build-stage.js +23 -22
  46. package/dist/src/build/dep-install.js +22 -21
  47. package/dist/src/build/host-sleep.js +1 -1
  48. package/dist/src/change/change-data.js +25 -14
  49. package/dist/src/change/change-render.js +11 -10
  50. package/dist/src/cli/brief-command.js +58 -0
  51. package/dist/src/cli/cli-args.js +25 -5
  52. package/dist/src/cli/cli-command-args.js +46 -14
  53. package/dist/src/cli/cli-editor.js +64 -0
  54. package/dist/src/cli/cli-flag-specs.js +54 -16
  55. package/dist/src/cli/cli-marker-commands.js +13 -10
  56. package/dist/src/cli/cli-output.js +9 -0
  57. package/dist/src/cli/cli-query-commands.js +458 -49
  58. package/dist/src/cli/cli-repo-gate.js +1 -1
  59. package/dist/src/cli/cli-run.js +208 -59
  60. package/dist/src/cli/completion.js +207 -0
  61. package/dist/src/cli/config-commands.js +14 -11
  62. package/dist/src/cli/help.js +305 -110
  63. package/dist/src/cli/log-commands.js +127 -21
  64. package/dist/src/cli/notify-command.js +33 -18
  65. package/dist/src/cli/question-commands.js +29 -224
  66. package/dist/src/cli.js +121 -40
  67. package/dist/src/collections.js +66 -16
  68. package/dist/src/concurrency/check-permit.js +16 -17
  69. package/dist/src/concurrency/lock.js +6 -5
  70. package/dist/src/concurrency/provider-lease.js +69 -0
  71. package/dist/src/concurrency/semaphore.js +258 -36
  72. package/dist/src/config/config-aliases.js +106 -0
  73. package/dist/src/config/config-editable-keys.js +3 -3
  74. package/dist/src/config/config-fallbacks.js +379 -0
  75. package/dist/src/config/config-field-checks.js +19 -10
  76. package/dist/src/config/config-keys.js +160 -0
  77. package/dist/src/config/config-live.js +64 -12
  78. package/dist/src/config/config-schema.js +34 -22
  79. package/dist/src/config/config-validation.js +132 -48
  80. package/dist/src/config/config-views.js +119 -337
  81. package/dist/src/config/config-write.js +138 -177
  82. package/dist/src/config/config.js +60 -49
  83. package/dist/src/config/model-selector.js +28 -2
  84. package/dist/src/doctor/doctor-backlog.js +546 -49
  85. package/dist/src/doctor/doctor-checks.js +534 -56
  86. package/dist/src/doctor/doctor-holds.js +193 -53
  87. package/dist/src/doctor/doctor-install.js +30 -0
  88. package/dist/src/doctor/doctor-launch-services.js +2 -2
  89. package/dist/src/doctor/doctor-model-checks.js +549 -157
  90. package/dist/src/doctor/doctor-notify.js +27 -0
  91. package/dist/src/doctor/doctor-orphans.js +10 -9
  92. package/dist/src/doctor/doctor-render.js +36 -9
  93. package/dist/src/doctor/doctor.js +80 -22
  94. package/dist/src/doctor/image-probe.js +244 -0
  95. package/dist/src/doctor/probe.js +64 -0
  96. package/dist/src/events/event-format.js +226 -102
  97. package/dist/src/events/event-labels.js +116 -0
  98. package/dist/src/events/event-legacy.js +140 -0
  99. package/dist/src/events/event-read.js +58 -22
  100. package/dist/src/events/event-window.js +27 -7
  101. package/dist/src/events/events.js +18 -14
  102. package/dist/src/events/notify.js +137 -38
  103. package/dist/src/failure/failure-cluster.js +12 -9
  104. package/dist/src/failure/failure-data.js +96 -48
  105. package/dist/src/failure/failure-render.js +69 -58
  106. package/dist/src/failure/failure-state-change.js +33 -26
  107. package/dist/src/failure/time-spend.js +79 -59
  108. package/dist/src/files/file-queue.js +16 -4
  109. package/dist/src/files/files.js +18 -6
  110. package/dist/src/files/json-object.js +18 -3
  111. package/dist/src/files/tail.js +1 -1
  112. package/dist/src/gates/bootstrap-gates.js +5 -4
  113. package/dist/src/gates/budget-gates.js +250 -34
  114. package/dist/src/gates/disk-gate.js +18 -18
  115. package/dist/src/gates/gate-polls.js +117 -69
  116. package/dist/src/gates/maintenance-quota.js +29 -29
  117. package/dist/src/gates/pause-gates.js +11 -11
  118. package/dist/src/gates/readiness.js +2 -1
  119. package/dist/src/gates/role-cap-gates.js +16 -21
  120. package/dist/src/gates/startup-gate.js +75 -31
  121. package/dist/src/gates/streak-gate.js +18 -16
  122. package/dist/src/git/commit-message.js +12 -5
  123. package/dist/src/git/git-diff.js +16 -16
  124. package/dist/src/git/git-run.js +8 -8
  125. package/dist/src/git/git.js +86 -55
  126. package/dist/src/git/slots-state.js +14 -5
  127. package/dist/src/git/worktree-pool.js +25 -18
  128. package/dist/src/git/worktree-use.js +15 -14
  129. package/dist/src/git/worktree.js +18 -18
  130. package/dist/src/gui/gui-args.js +10 -14
  131. package/dist/src/gui/gui-backlog-commands.js +273 -0
  132. package/dist/src/gui/gui-command.js +1 -1
  133. package/dist/src/gui/gui-config-commands.js +440 -0
  134. package/dist/src/gui/gui-endpoint-commands.js +16 -669
  135. package/dist/src/gui/gui-endpoint-common.js +49 -0
  136. package/dist/src/gui/gui-endpoints.js +116 -56
  137. package/dist/src/gui/gui-operator-commands.js +145 -0
  138. package/dist/src/gui/gui-prompt-commands.js +234 -0
  139. package/dist/src/gui/gui-server.js +28 -8
  140. package/dist/src/gui/gui-team-commands.js +164 -0
  141. package/dist/src/gui/http-body.js +17 -1
  142. package/dist/src/history/history-data.js +194 -98
  143. package/dist/src/history/history-render.js +76 -0
  144. package/dist/src/history/history.js +37 -58
  145. package/dist/src/inbox/inbox-attachments.js +75 -14
  146. package/dist/src/inbox/inbox-cancel.js +7 -5
  147. package/dist/src/inbox/inbox-edit.js +1 -1
  148. package/dist/src/inbox/inbox-submit.js +44 -4
  149. package/dist/src/inbox/inbox-top.js +83 -0
  150. package/dist/src/inbox/inbox.js +8 -2
  151. package/dist/src/inbox/pending-prompt.js +1 -1
  152. package/dist/src/inbox/prompt-commands.js +170 -80
  153. package/dist/src/init/init-templates.js +16 -14
  154. package/dist/src/init/init.js +12 -11
  155. package/dist/src/loop/leftover.js +28 -27
  156. package/dist/src/loop/loop-fallback.js +2 -2
  157. package/dist/src/loop/loop-pi.js +106 -52
  158. package/dist/src/loop/loop-recovery.js +15 -15
  159. package/dist/src/loop/loop-state.js +139 -13
  160. package/dist/src/loop/loop-tick.js +194 -86
  161. package/dist/src/loop/loop.js +72 -36
  162. package/dist/src/loop/model-fallback.js +3 -2
  163. package/dist/src/loop/revision.js +16 -7
  164. package/dist/src/merge/backlog-conflicts.js +155 -0
  165. package/dist/src/{landing/landing-batch.js → merge/merge-batch.js} +90 -89
  166. package/dist/src/{landing/landing-check-failures.js → merge/merge-check-failures.js} +87 -41
  167. package/dist/src/{landing/landing-core.js → merge/merge-core.js} +119 -95
  168. package/dist/src/{landing/landing-diff.js → merge/merge-diff.js} +1 -1
  169. package/dist/src/merge/merge-drain.js +212 -0
  170. package/dist/src/{landing/landing-git.js → merge/merge-git.js} +94 -21
  171. package/dist/src/merge/merge-pipeline.js +99 -0
  172. package/dist/src/{landing/landing-questions.js → merge/merge-questions.js} +9 -6
  173. package/dist/src/merge/merge-queue-render.js +75 -0
  174. package/dist/src/{landing/landing-queue.js → merge/merge-queue.js} +95 -36
  175. package/dist/src/merge/merge-slot.js +340 -0
  176. package/dist/src/{landing/landing-stack.js → merge/merge-stack.js} +66 -62
  177. package/dist/src/{landing/landing-merge.js → merge/merge-to-main.js} +72 -62
  178. package/dist/src/merge/merge-verify.js +212 -0
  179. package/dist/src/notes-cli.js +97 -0
  180. package/dist/src/operator/operator-commands.js +154 -47
  181. package/dist/src/operator/operator-intent.js +87 -49
  182. package/dist/src/operator/operator-requests.js +52 -52
  183. package/dist/src/operator/retire.js +62 -27
  184. package/dist/src/operator/revert.js +71 -0
  185. package/dist/src/orchestrator/orchestrator-launch.js +74 -14
  186. package/dist/src/orchestrator/orchestrator-scheduling.js +129 -44
  187. package/dist/src/orchestrator/orchestrator.js +158 -100
  188. package/dist/src/orchestrator/retention.js +29 -12
  189. package/dist/src/orchestrator/startup-migration-steps.js +51 -0
  190. package/dist/src/orchestrator/startup-migrations.js +40 -0
  191. package/dist/src/paths.js +128 -37
  192. package/dist/src/pi/pi-args.js +26 -3
  193. package/dist/src/pi/pi-backend.js +15 -23
  194. package/dist/src/pi/pi-bin.js +18 -12
  195. package/dist/src/pi/pi-event-line.js +17 -1
  196. package/dist/src/pi/pi-failure.js +87 -0
  197. package/dist/src/pi/pi-models.js +126 -38
  198. package/dist/src/pi/pi-run-result.js +1 -1
  199. package/dist/src/pi/pi-settings.js +86 -0
  200. package/dist/src/pi/pi-stream.js +131 -107
  201. package/dist/src/pi/pi-watchdogs.js +47 -43
  202. package/dist/src/pi-extension/bounded-output.js +20 -5
  203. package/dist/src/pi-extension/context-budget.js +3 -1
  204. package/dist/src/pi-extension/context-shake.js +54 -21
  205. package/dist/src/pi-extension/context-window.js +37 -0
  206. package/dist/src/pi-extension/full-output.js +2 -2
  207. package/dist/src/pi-extension/strip-thinking.js +80 -0
  208. package/dist/src/pi-extension/trim-system-prompt.js +38 -0
  209. package/dist/src/process/launch-services.js +11 -10
  210. package/dist/src/process/process-table.js +1 -1
  211. package/dist/src/process/process.js +7 -7
  212. package/dist/src/process/run-marker.js +29 -6
  213. package/dist/src/process/supervisor.js +47 -25
  214. package/dist/src/prompt/dialect.js +30 -10
  215. package/dist/src/prompt/gate-prompts.js +170 -67
  216. package/dist/src/prompt/principles.js +1 -0
  217. package/dist/src/prompt/prompt-followup.js +35 -20
  218. package/dist/src/prompt/prompt.js +249 -91
  219. package/dist/src/redeploy/redeploy-policy.js +10 -10
  220. package/dist/src/redeploy/redeploy.js +6 -4
  221. package/dist/src/redeploy/redeployer.js +63 -55
  222. package/dist/src/redeploy/self-reload.js +1 -1
  223. package/dist/src/report/report-data.js +264 -255
  224. package/dist/src/report/report-fold.js +353 -0
  225. package/dist/src/report/report-render.js +270 -33
  226. package/dist/src/request-timeouts.js +1 -1
  227. package/dist/src/review/known-flakes.js +34 -14
  228. package/dist/src/review/protected-edits.js +33 -0
  229. package/dist/src/review/review-followup.js +16 -4
  230. package/dist/src/review/review-precheck.js +14 -13
  231. package/dist/src/review/review-verdict.js +14 -0
  232. package/dist/src/review/review.js +81 -39
  233. package/dist/src/review/secret-leaks.js +127 -0
  234. package/dist/src/review/suite-rerun.js +4 -3
  235. package/dist/src/roles/legacy-role-ids.js +21 -0
  236. package/dist/src/roles/legacy-role-migration.js +62 -0
  237. package/dist/src/roles/loop-ids.js +19 -2
  238. package/dist/src/roles/role-catalog.js +125 -135
  239. package/dist/src/roles/role-guidance.js +64 -31
  240. package/dist/src/roles/role-labels.js +10 -0
  241. package/dist/src/roles/role-render.js +194 -18
  242. package/dist/src/roles/role-view.js +216 -17
  243. package/dist/src/roles/roles.js +18 -15
  244. package/dist/src/scheduling/backoff.js +29 -19
  245. package/dist/src/scheduling/claims.js +25 -9
  246. package/dist/src/scheduling/once-round.js +11 -11
  247. package/dist/src/scheduling/quiet-hours.js +25 -32
  248. package/dist/src/scheduling/scheduling.js +43 -24
  249. package/dist/src/scheduling/{work-landed-cache.js → work-merged-cache.js} +14 -14
  250. package/dist/src/status/status-budget.js +115 -0
  251. package/dist/src/status/status-data.js +77 -184
  252. package/dist/src/status/status-inbox.js +47 -0
  253. package/dist/src/status/status-install.js +27 -0
  254. package/dist/src/status/status-polls.js +68 -33
  255. package/dist/src/status/status-roles.js +105 -0
  256. package/dist/src/{fleet → team}/error-storm.js +10 -10
  257. package/dist/src/{fleet → team}/failure-spread.js +8 -8
  258. package/dist/src/{fleet → team}/orchestrator-info.js +86 -18
  259. package/dist/src/{fleet → team}/reclaim.js +5 -4
  260. package/dist/src/{fleet/fleet-hold.js → team/team-hold.js} +55 -21
  261. package/dist/src/{fleet/fleet-polls.js → team/team-polls.js} +116 -65
  262. package/dist/src/{fleet/fleet-state.js → team/team-state.js} +62 -37
  263. package/dist/src/text/datetime.js +42 -2
  264. package/dist/src/text/format.js +10 -1
  265. package/dist/src/text/markdown.js +1 -1
  266. package/dist/src/text/phrases.js +35 -34
  267. package/dist/src/text/suggest.js +3 -2
  268. package/dist/src/text/text.js +2 -2
  269. package/dist/src/tick/qa-coverage.js +2 -1
  270. package/dist/src/tick/stage-check.js +147 -26
  271. package/dist/src/tick/telemetry-digest.js +10 -9
  272. package/dist/src/tick/tick-apply.js +101 -72
  273. package/dist/src/tick/tick-detail-data.js +32 -18
  274. package/dist/src/tick/tick-detail.js +62 -21
  275. package/dist/src/tick/tick-finalize.js +29 -22
  276. package/dist/src/tick/tick-outcome.js +2 -1
  277. package/dist/src/tick/tick-prompt.js +94 -26
  278. package/dist/src/tick/tick-resume.js +29 -13
  279. package/dist/src/tick/tick-stage.js +91 -47
  280. package/dist/src/tick/tick-timing.js +60 -59
  281. package/dist/src/tick/tick-usage.js +53 -23
  282. package/dist/src/tick/tick-verdict.js +76 -10
  283. package/dist/src/ui/badges.js +118 -67
  284. package/dist/src/ui/gui/gui-client-boot.js +209 -30
  285. package/dist/src/ui/gui/gui-client-composer.js +102 -23
  286. package/dist/src/ui/gui/gui-client-drawer.js +330 -75
  287. package/dist/src/ui/gui/gui-client-history.js +96 -38
  288. package/dist/src/ui/gui/gui-client-labels.js +24 -0
  289. package/dist/src/ui/gui/gui-client-loops.js +94 -52
  290. package/dist/src/ui/gui/gui-client-markdown.js +1 -0
  291. package/dist/src/ui/gui/gui-client-model.js +63 -43
  292. package/dist/src/ui/gui/gui-client-operator.js +129 -39
  293. package/dist/src/ui/gui/gui-client-pending.js +36 -32
  294. package/dist/src/ui/gui/gui-client-report.js +339 -46
  295. package/dist/src/ui/gui/gui-client-settings.js +148 -14
  296. package/dist/src/ui/gui/gui-client-sound.js +2 -1
  297. package/dist/src/ui/gui/{gui-client-fleet.js → gui-client-team.js} +196 -72
  298. package/dist/src/ui/gui/gui-client.js +26 -20
  299. package/dist/src/ui/gui/gui-page.js +19 -19
  300. package/dist/src/ui/gui/gui-styles.js +12 -4
  301. package/dist/src/ui/progress-data.js +5 -3
  302. package/dist/src/ui/status-model.js +184 -122
  303. package/dist/src/ui/status-payload.js +119 -32
  304. package/dist/src/ui/status-render.js +122 -52
  305. package/dist/src/ui/team-alerts.js +341 -0
  306. package/dist/src/ui/tick-progress-model.js +14 -14
  307. package/dist/src/ui/tone.js +11 -8
  308. package/dist/src/ui/transcript-tail.js +1 -0
  309. package/dist/src/ui/transcript.js +23 -16
  310. package/dist/src/ui/tui/tui-app.js +1 -0
  311. package/dist/src/ui/tui/tui-backlog.js +85 -11
  312. package/dist/src/ui/tui/tui-editor-answer.js +97 -0
  313. package/dist/src/ui/tui/tui-editor-attach.js +107 -0
  314. package/dist/src/ui/tui/tui-editor-brief.js +107 -0
  315. package/dist/src/ui/tui/tui-editor-budget.js +155 -0
  316. package/dist/src/ui/tui/tui-editor-ctx.js +1 -0
  317. package/dist/src/ui/tui/tui-editor-entry.js +230 -0
  318. package/dist/src/ui/tui/tui-editor-interval.js +122 -0
  319. package/dist/src/ui/tui/tui-editor-notes.js +114 -0
  320. package/dist/src/ui/tui/tui-editor-pause.js +266 -0
  321. package/dist/src/ui/tui/tui-editor-prompt.js +95 -0
  322. package/dist/src/ui/tui/tui-editor-queued.js +98 -0
  323. package/dist/src/ui/tui/tui-editor-quiet.js +106 -0
  324. package/dist/src/ui/tui/tui-editor-retire.js +83 -0
  325. package/dist/src/ui/tui/tui-editor-search.js +73 -0
  326. package/dist/src/ui/tui/tui-editor-session.js +97 -0
  327. package/dist/src/ui/tui/tui-editors.js +152 -0
  328. package/dist/src/ui/tui/tui-frame.js +67 -55
  329. package/dist/src/ui/tui/tui-history.js +12 -7
  330. package/dist/src/ui/tui/tui-input.js +49 -44
  331. package/dist/src/ui/tui/tui-keymap.js +1 -0
  332. package/dist/src/ui/tui/tui-keys.js +91 -1704
  333. package/dist/src/ui/tui/tui-pane-activity.js +99 -0
  334. package/dist/src/ui/tui/tui-pane-backlog.js +224 -0
  335. package/dist/src/ui/tui/tui-pane-brief.js +30 -0
  336. package/dist/src/ui/tui/tui-pane-ctx.js +11 -0
  337. package/dist/src/ui/tui/tui-pane-health.js +124 -0
  338. package/dist/src/ui/tui/tui-pane-history.js +316 -0
  339. package/dist/src/ui/tui/tui-pane-pending.js +74 -0
  340. package/dist/src/ui/tui/tui-pane-queued.js +143 -0
  341. package/dist/src/ui/tui/tui-pane-report.js +122 -0
  342. package/dist/src/ui/tui/tui-pane-session.js +101 -0
  343. package/dist/src/ui/tui/tui-pane-transcript.js +150 -0
  344. package/dist/src/ui/tui/tui-pane.js +66 -28
  345. package/dist/src/ui/tui/tui-panes.js +294 -0
  346. package/dist/src/ui/tui/tui.js +45 -25
  347. package/dist/src/verdict/fix-claim.js +141 -38
  348. package/dist/src/verdict/no-change.js +19 -0
  349. package/dist/src/verdict/refusal.js +92 -8
  350. package/dist/src/verdict/reply-contract.js +17 -5
  351. package/dist/src/version.js +3 -2
  352. package/package.json +4 -2
  353. package/dist/src/agent/mcp/role-notes-server.js +0 -160
  354. package/dist/src/landing/backlog-conflicts.js +0 -116
  355. package/dist/src/landing/landing-drain.js +0 -204
  356. package/dist/src/landing/landing-pipeline.js +0 -96
  357. package/dist/src/landing/landing-queue-render.js +0 -31
  358. package/dist/src/landing/landing-slot.js +0 -246
  359. package/dist/src/landing/landing-vetting.js +0 -196
  360. package/dist/src/pi-extension/role-notes.js +0 -40
  361. package/dist/src/ui/fleet-alerts.js +0 -174
@@ -1,29 +1,33 @@
1
+ // vocabulary: plain
1
2
  import { knownRoleIds, loadConfig } from "../config/config.js";
2
3
  import { fail, say, sayJson } from "../cli/cli-output.js";
3
4
  import { failOverDurationCap, flagValue, parseDurationFlag, parseRoleFlag } from "../cli/cli-args.js";
4
5
  import { errorMessage } from "../text/text.js";
5
- import { PAUSE_FOR_OVERFLOW_HINT, REASON_VALUE_ERROR } from "../cli/cli-flag-specs.js";
6
- import { artifactPhrase, retireRole } from "./retire.js";
7
- import { agree, fleetResumePhrase, pauseReasonSuffix } from "../text/phrases.js";
6
+ import { PAUSE_FOR_OVERFLOW_HINT, REASON_VALUE_ERROR, ROLE_VALUE_ERROR } from "../cli/cli-flag-specs.js";
7
+ import { artifactPhrase, hasRetirableLeftovers, previewRetire, retireRole } from "./retire.js";
8
+ import { revertMergedCommit } from "./revert.js";
9
+ import { agree, plural, teamResumePhrase, pauseReasonSuffix } from "../text/phrases.js";
8
10
  import { errCode } from "../errno.js";
9
- import { allRoleIds } from "../roles/roles.js";
10
- import { normalizePauseReason, pauseFleet, pauseRole, pausedRoles, resumeFleet, resumeRole, } from "../fleet/fleet-state.js";
11
- import { readOrchestratorInfo } from "../fleet/orchestrator-info.js";
11
+ import { shortSha } from "../text/format.js";
12
+ import { INSTANCE_ROLES } from "../roles/loop-ids.js";
13
+ import { allRoleIds, unknownRoleMessage } from "../roles/roles.js";
14
+ import { normalizePauseReason, pauseTeam, pauseRole, pausedRoles, resumeTeam, resumeRole, } from "../team/team-state.js";
15
+ import { readOrchestratorInfo } from "../team/orchestrator-info.js";
12
16
  import { pidAlive } from "../process/process.js";
13
- import { inspectReclaimCandidates } from "../fleet/reclaim.js";
14
- import { markerApplyNote, NO_HARNESS_ERROR, PAUSE_FOR_MAX_MS, cancelWakes, listWakes, renderWakeList, requestAbort, requestResetCounters, requestWake, rolePauseMessage, roleResumeMessage, runManualReclaim, timedPauseBits, } from "./operator-intent.js";
17
+ import { inspectReclaimCandidates } from "../team/reclaim.js";
18
+ import { markerApplyNote, NO_HARNESS_ERROR, PAUSE_FOR_MAX_MS, cancelScheduledStopMessage, cancelWakes, listWakes, renderWakeList, requestAbort, requestResetCounters, requestRestart, requestScheduledStop, requestWake, rolePauseMessage, roleResumeMessage, runManualReclaim, timedPauseBits, } from "./operator-intent.js";
15
19
  /** The CLI layer of the operator commands: the `reset-counters`, `wake`, `reclaim`, `abort`,
16
20
  * `pause`, `resume`, `stop`, and `retire` commands, split out of cli.ts so the command bodies
17
21
  * live beside their shared `--role` resolution. The marker-writing cores and shared
18
22
  * confirmations they print live in src/operator/operator-intent.ts (shared with the
19
- * dashboard and TUI); the fleet-side consumer half is src/operator/operator-requests.ts;
23
+ * dashboard and TUI); the team-side consumer half is src/operator/operator-requests.ts;
20
24
  * the `prompt` command, which drives the durable per-loop queues rather than the marker
21
25
  * protocol, lives beside them in src/inbox/prompt-commands.ts, and the `config` command,
22
- * which reads and writes tumwater.json directly rather than touching fleet state, in
26
+ * which reads and writes tumwater.json directly rather than touching team state, in
23
27
  * src/cli/config-commands.ts. Every command here is deliberately usable without a live
24
- * harness except `abort` and `stop`, which have nothing to reach when no fleet is up. */
28
+ * harness except `abort` and `stop`, which have nothing to reach when no team is up. */
25
29
  /** The `--role <id>` value when given, resolved WITHOUT tumwater.json when it names a
26
- * built-in catalog role: the fleet itself tolerates a broken config (the live reload keeps
30
+ * built-in catalog role: the team itself tolerates a broken config (the live reload keeps
27
31
  * the last-known-good one), so an operator marker aimed at a built-in loop must not depend
28
32
  * on the file parsing — the same resilience `logs --role` already has. A custom-loop id or
29
33
  * an unknown id falls through to the config-backed check, which fails with the honest error
@@ -42,6 +46,15 @@ function namedRole(root, args) {
42
46
  fail(errorMessage(err)); // same message main().catch would print, but via the standard fail()
43
47
  }
44
48
  }
49
+ /** namedRole for the commands that write or lift the per-role pause marker: `--role feature-1`
50
+ * (`bugfix-1`) is the pause key of instance 1 of a multi-instance role (src/roles/loop-ids.ts's
51
+ * instancePauseKey), returned verbatim; every other value resolves through namedRole. */
52
+ function pauseTargetRole(root, args) {
53
+ const role = flagValue(args, "--role");
54
+ if (role && [...INSTANCE_ROLES].some((base) => role === `${base}-1`))
55
+ return role;
56
+ return namedRole(root, args);
57
+ }
45
58
  /** The `--role <id>` value a marker command requires: namedRole's resolution plus the
46
59
  * command-specific "requires --role" failure. abort and retire both need a target (unlike
47
60
  * reset-counters/resume/wake, which default to every role), so the guard lives here once and
@@ -65,12 +78,14 @@ function targetRoles(root, args) {
65
78
  export async function cmdResetCounters(root, args) {
66
79
  say(requestResetCounters(root, targetRoles(root, args)));
67
80
  }
68
- /** `tumwater wake [--role <id>] [--in <duration> | --cancel | --list [--json]]`: tell the fleet "whatever the loops were
69
- * failing on is fixed — try again", so the named roles tick within one poll instead of
81
+ /** `tumwater wake [--role <id>] [--in <duration> | --cancel | --list [--json]]`: tell the
82
+ * team "whatever the loops were failing on is fixed — try again", so the named roles tick
83
+ * within one poll instead of
70
84
  * sleeping until their backoff expires. With `--in`, the wake is scheduled: the marker is
71
85
  * written now but consumed no earlier than now + duration, the same shape `pause --for`'s
72
86
  * auto-resume takes — the wake applies at the deadline, not at submit. With `--cancel`, the
73
- * pending scheduled wakes (or the named role's share) are dropped instead; no wake is written. With `--list`, every pending scheduled wake (or the named role's)
87
+ * pending scheduled wakes (or the named role's share) are dropped instead; no wake is
88
+ * written. With `--list`, every pending scheduled wake (or the named role's)
74
89
  * is printed, earliest first — `--json` as an array of {roles, atMs}; read-only. */
75
90
  export async function cmdWake(root, args) {
76
91
  // `--in <duration>` (the scheduled wake): the command's own gate (runMarkerCommand's
@@ -109,7 +124,7 @@ export async function cmdWake(root, args) {
109
124
  /** `tumwater reclaim [--dry-run]`: run one manual reclaim pass over every harness worktree
110
125
  * (plans/disk-floor.md, part 3/4). `--dry-run` lists each candidate with its idle age and how
111
126
  * many ignored paths `git clean -ndX` would remove, and changes nothing — it neither cleans a
112
- * worktree nor writes the worktree-use registry. With a live fleet the
127
+ * worktree nor writes the worktree-use registry. With a live team the
113
128
  * pass is requested through the marker the orchestrator consumes, so in-use worktrees are
114
129
  * respected; with none running it runs in-process, since nothing is in use. */
115
130
  export async function cmdReclaim(root, args) {
@@ -124,7 +139,7 @@ export async function cmdReclaim(root, args) {
124
139
  for (const c of candidates) {
125
140
  const notes = [
126
141
  `idle ${c.idleHours.toFixed(1)}h`,
127
- `${c.paths} ignored path${c.paths === 1 ? "" : "s"}`,
142
+ plural(c.paths, "ignored path"),
128
143
  ...(c.resumePending ? ["resume pending"] : []),
129
144
  ...(c.reclaimable ? [] : ["not idle-reclaimable"]),
130
145
  ];
@@ -136,9 +151,9 @@ export async function cmdReclaim(root, args) {
136
151
  }
137
152
  /** `tumwater abort --role <id>`: kill one loop's in-flight tick right now. The CLI cannot
138
153
  * reach into the orchestrator process, so the request rides on disk like reset-counters':
139
- * a per-role marker file a running fleet consumes within one poll cycle (the runner's
154
+ * a per-role marker file a running team consumes within one poll cycle (the runner's
140
155
  * abortTick kills the pi child and resets the worktree to main). Requires a live harness —
141
- * with no fleet there is nothing to consume the marker. The loop stays enabled: it backs
156
+ * with no team there is nothing to consume the marker. The loop stays enabled: it backs
142
157
  * off normally and later ticks proceed as usual. */
143
158
  export async function cmdAbort(root, args) {
144
159
  // As in cmdLogs: the config exists only to validate the id against built-ins plus
@@ -149,17 +164,28 @@ export async function cmdAbort(root, args) {
149
164
  fail(result.error);
150
165
  say(result.message);
151
166
  }
167
+ /** `tumwater restart`: apply a pending self-redeploy now, waiving the restart cooldown —
168
+ * the terminal twin of the dashboard update alert's refresh button (POST /api/restart,
169
+ * handleRestart in src/gui/gui-operator-commands.ts). Both call the same marker-writing
170
+ * core requestRestart, so the confirmation (or refusal) is byte-for-byte the GUI's. Takes
171
+ * no flags. */
172
+ export async function cmdRestart(root, _args) {
173
+ const result = requestRestart(root);
174
+ if (!result.ok)
175
+ fail(result.error);
176
+ say(result.message);
177
+ }
152
178
  /** `tumwater pause [--role <id>] [--for <duration>]`: with a role, stop THAT loop from
153
179
  * starting new ticks — in-flight ones finish, every other role (the director included)
154
180
  * keeps running; without one, stop every role loop from starting NEW ticks while in-flight
155
181
  * ones finish and the director keeps running (its prompts outrank operator gates, like
156
182
  * under the budget cap). The markers
157
183
  * are persistent state, not one-shot requests: presence means paused until `resume` removes
158
- * it — so pausing before startup starts an already-paused fleet. Unlike abort, no live
184
+ * it — so pausing before startup starts an already-paused team. Unlike abort, no live
159
185
  * harness is required; when none runs, say where the pause takes effect instead of failing.
160
186
  * Idempotent: a second pause reports the existing marker as-is. */
161
187
  export async function cmdPause(root, args = [], now = Date.now()) {
162
- // `--for <duration>` (the timed pause): the ms-epoch deadline pauseFleet/pauseRole write
188
+ // `--for <duration>` (the timed pause): the ms-epoch deadline pauseTeam/pauseRole write
163
189
  // into the marker and every consumer honors — the command's own gate (runMarkerCommand's
164
190
  // rejectUnknownArgs) has already restricted the flag to this command and validated its
165
191
  // value — shape and the 90-day cap both, via durationFlagSpec("pause --for")'s validate —
@@ -169,7 +195,7 @@ export async function cmdPause(root, args = [], now = Date.now()) {
169
195
  // `--reason <text>` (the operator pause's why): the gate in cli.ts has already restricted
170
196
  // the flag to this command, and its trailing-no-value slip to the spec's wording, so here
171
197
  // a valueless or empty reason fails with that same line beside the writer it feeds. The
172
- // reason folds to one line (normalizePauseReason, the same home pauseFleet writes through)
198
+ // reason folds to one line (normalizePauseReason, the same home pauseTeam writes through)
173
199
  // so the confirmation line, the marker, and every single-line surface carry the same text
174
200
  // — a raw newline in the flag's value would otherwise break the header's one-line badge.
175
201
  const reasonRaw = flagValue(args, "--reason");
@@ -186,15 +212,15 @@ export async function cmdPause(root, args = [], now = Date.now()) {
186
212
  // discipline timedPauseBits's own `now` parameter follows.
187
213
  const timed = forMs === undefined ? undefined : { ms: forMs, untilMs: now + forMs };
188
214
  const untilMs = timed?.untilMs;
189
- const role = namedRole(root, args);
215
+ const role = pauseTargetRole(root, args);
190
216
  if (role) {
191
217
  // The plan keeps per-role pauses anonymous (a per-role reason would need a per-role map
192
218
  // for no observed need), so a reason aimed at a role pause fails fast here — accepting
193
219
  // the flag and silently dropping the note would tell the operator the reason stands
194
220
  // when no marker carries it. Before any marker is written, beside the other fail-fasts.
195
221
  if (reason !== undefined)
196
- fail("pause --reason states why the whole fleet is paused — a per-role pause carries no reason (drop --role, or run bare `tumwater pause --reason <text>`)");
197
- // Per-role branch: pauseRole in src/fleet/fleet-state.ts is the single writer of the role
222
+ fail("pause --reason states why the whole team is paused — a per-role pause carries no reason (drop --role, or run bare `tumwater pause --reason <text>`)");
223
+ // Per-role branch: pauseRole in src/team/team-state.ts is the single writer of the role
198
224
  // marker (the TUI's Ctrl+P and the dashboard's /api/pause-role toggle call it too), so
199
225
  // the CLI, TUI, and GUI cannot drift on format or idempotence; a false return means the
200
226
  // role was already in the set, which rolePauseMessage words — unless a `--for` stands,
@@ -202,20 +228,20 @@ export async function cmdPause(root, args = [], now = Date.now()) {
202
228
  say(rolePauseMessage(root, role, pauseRole(root, role, untilMs), timed, now));
203
229
  return;
204
230
  }
205
- // pauseFleet in src/fleet/fleet-state.ts is the single writer of the pause marker — the GUI's
231
+ // pauseTeam in src/team/team-state.ts is the single writer of the pause marker — the GUI's
206
232
  // /api/pause toggle calls it too, so the CLI and the dashboard cannot drift on format
207
233
  // or idempotence; a false return means the marker was already there (a `--for` never
208
234
  // no-ops: it overwrites the standing deadline instead).
209
235
  // The reason rides the fresh pause write (or a `--for` overwrite): the same last-write-wins
210
236
  // rule as the deadline, so an "already paused" no-op keeps the standing note as-is.
211
- if (!pauseFleet(root, untilMs, reason)) {
237
+ if (!pauseTeam(root, untilMs, reason)) {
212
238
  say("already paused");
213
239
  return;
214
240
  }
215
241
  const { when, tail } = markerApplyNote(root);
216
242
  const { forPhrase, note } = timedPauseBits(timed, now);
217
243
  const why = pauseReasonSuffix(reason);
218
- say(`fleet paused${forPhrase}${why} — role loops stop starting new ticks${when} (in-flight ticks finish; the director keeps running your prompts)${note}${tail}`);
244
+ say(`team paused${forPhrase}${why} — roles start no new runs${when} (current runs finish; the director keeps running your messages)${note}${tail}`);
219
245
  }
220
246
  /** Deliver the stop signal to the recorded orchestrator pid: "signalled" when SIGTERM was
221
247
  * delivered, "gone" when the process died between the caller's liveness check and the
@@ -245,59 +271,119 @@ export function signalOrchestrator(pid) {
245
271
  * that `orchestratorAlive` already accepts everywhere (status, abort, pause) applies here
246
272
  * too: the info file is refreshed by the live orchestrator, so the window is small, and the
247
273
  * signal is the same one a human `kill <pid>` would send. */
248
- export async function cmdStop(root) {
274
+ export async function cmdStop(root, args = []) {
275
+ const inRaw = flagValue(args, "--in");
276
+ if (args.includes("--cancel")) {
277
+ if (inRaw !== null)
278
+ fail("stop --cancel and --in cannot be combined");
279
+ say(cancelScheduledStopMessage(root));
280
+ return;
281
+ }
282
+ if (inRaw !== null) {
283
+ // The gate (STOP_IN_FLAG's validate) checked shape and cap; re-parse beside the writer.
284
+ const ms = parseDurationFlag("stop --in", inRaw);
285
+ failOverDurationCap("stop --in", ms, PAUSE_FOR_MAX_MS);
286
+ const result = requestScheduledStop(root, ms);
287
+ if (!result.ok)
288
+ fail(result.error);
289
+ say(result.message);
290
+ return;
291
+ }
249
292
  // Deliberately the same liveness gate and wording `requestAbort` uses: an unreadable info
250
293
  // file (torn write) and a dead recorded pid both mean nothing is running to stop.
251
294
  const info = readOrchestratorInfo(root);
252
295
  if (!info || !pidAlive(info.pid)) {
253
296
  fail(NO_HARNESS_ERROR);
254
297
  }
255
- // A pid that dies between the liveness check and the signal (ESRCH) means the fleet is
298
+ // A pid that dies between the liveness check and the signal (ESRCH) means the team is
256
299
  // already stopped — say so and exit clean rather than surfacing Node's raw kill error;
257
300
  // anything else (EPERM on a recycled pid, …) comes back as signalOrchestrator's message.
258
301
  if (signalOrchestrator(info.pid) === "signalled")
259
- say("stop requested — the fleet drains its in-flight ticks and exits (the same path as Ctrl+C)");
302
+ say("stop requested — the team finishes current work, then stops (the same path as Ctrl+C)");
260
303
  else
261
- say("the orchestrator exited before the stop signal landed — nothing is running");
304
+ say("the orchestrator exited before the stop signal arrived — nothing is running");
262
305
  }
263
306
  /** `tumwater resume [--role <id>]`: with a role, lift that one role's pause (removed from the
264
- * per-role marker set); without one, lift a fleet pause by removing its marker. Idempotent
307
+ * per-role marker set); without one, lift a team pause by removing its marker. Idempotent
265
308
  * like pause: with no marker there is nothing to do. No live harness required — resuming
266
309
  * before startup just means the next `tumwater run` starts unpaused. */
267
310
  export async function cmdResume(root, args = []) {
268
- const role = namedRole(root, args);
311
+ const role = pauseTargetRole(root, args);
269
312
  if (role) {
270
- // resumeRole (src/fleet/fleet-state.ts) is the single remover of the per-role marker; a false
313
+ // resumeRole (src/team/team-state.ts) is the single remover of the per-role marker; a false
271
314
  // return means the role was never paused — the changed-state contract roleResumeMessage
272
315
  // words ("is already paused" vs "was not paused").
273
316
  say(roleResumeMessage(root, role, resumeRole(root, role)));
274
317
  return;
275
318
  }
276
- // resumeFleet (src/fleet/fleet-state.ts) is the single remover, shared with the GUI toggle;
319
+ // resumeTeam (src/team/team-state.ts) is the single remover, shared with the GUI toggle;
277
320
  // a false return means there was no marker to lift.
278
- if (!resumeFleet(root)) {
321
+ if (!resumeTeam(root)) {
279
322
  say("not paused");
280
323
  return;
281
324
  }
282
325
  const { when, tail } = markerApplyNote(root);
283
- // The per-role pause outlives a fleet resume (the markers are independent), so the resumed
284
- // fleet's confirmation must name the roles still individually paused instead of claiming
326
+ // The per-role pause outlives a team resume (the markers are independent), so the resumed
327
+ // team's confirmation must name the roles still individually paused instead of claiming
285
328
  // "role loops tick again" for loops the scheduler keeps gated.
286
329
  const still = pausedRoles(root);
287
330
  const roleNote = still.length > 0
288
331
  ? ` (${still.join(", ")} ${agree(still.length, "is", "are")} still individually paused — \`tumwater resume --role <id>\` lifts each)`
289
332
  : "";
290
- say(`${fleetResumePhrase()}${when}${roleNote}${tail}`);
333
+ say(`${teamResumePhrase()}${when}${roleNote}${tail}`);
334
+ }
335
+ /** The role id retire targets: a built-in or configured id as for the other marker commands, or
336
+ * else any well-formed id that still has a branch or merge ref (a loop no config names). */
337
+ async function retireTargetRole(root, args) {
338
+ const role = flagValue(args, "--role");
339
+ if (role === null)
340
+ return requiredNamedRole(root, args, "retire");
341
+ if (!role)
342
+ fail(ROLE_VALUE_ERROR);
343
+ if (allRoleIds().includes(role))
344
+ return role;
345
+ let ids;
346
+ try {
347
+ ids = knownRoleIds(loadConfig(root));
348
+ }
349
+ catch (err) {
350
+ fail(errorMessage(err));
351
+ }
352
+ if (ids.includes(role) || (await hasRetirableLeftovers(root, role)))
353
+ return role;
354
+ fail(unknownRoleMessage(role, ids));
291
355
  }
292
356
  /** `tumwater retire --role <id> [--force] [--json]`: remove a disabled loop's leftover worktree,
293
- * branch, landing ref, and paused-state marker. The collection, safety rails, and removal live
294
- * in src/operator/retire.ts; this is the render layer — one line per removed artifact, the skip
295
- * lines that make a second run idempotent, or the `--json` payload. */
357
+ * branch, merge ref, and paused-state marker. The collection, safety rails, and removal live in
358
+ * src/operator/retire.ts; this is the render layer — one line per removed artifact, the skip lines
359
+ * that make a second run idempotent, or the `--json` payload. */
296
360
  export async function cmdRetire(root, args) {
297
- // As in cmdAbort: the config exists only to validate the id against built-ins plus
298
- // user-defined loops; a missing --role fails before it is ever needed.
299
- const role = requiredNamedRole(root, args, "retire");
300
- const result = await retireRole(root, role, { force: args.includes("--force") });
361
+ // The config exists only to validate the id against built-ins plus user-defined loops, with
362
+ // the leftover-branch gate for ids no config names; a missing --role fails before it is needed.
363
+ const role = await retireTargetRole(root, args);
364
+ const force = args.includes("--force");
365
+ if (args.includes("--dry-run")) {
366
+ const preview = await previewRetire(root, role);
367
+ if (args.includes("--json")) {
368
+ sayJson({
369
+ role,
370
+ dryRun: true,
371
+ wouldRemove: preview.wouldRemove,
372
+ skipped: preview.skipped,
373
+ objections: preview.objections,
374
+ });
375
+ return;
376
+ }
377
+ for (const name of preview.wouldRemove)
378
+ say(`would remove ${artifactPhrase(name, role)}`);
379
+ for (const name of preview.skipped)
380
+ say(`nothing to remove: ${artifactPhrase(name, role)}`);
381
+ for (const reason of preview.objections)
382
+ say(force ? `--force overrides: ${reason}` : `would refuse: ${reason}`);
383
+ say("dry run — nothing removed");
384
+ return;
385
+ }
386
+ const result = await retireRole(root, role, { force });
301
387
  if (args.includes("--json")) {
302
388
  sayJson({ role, removed: result.removed, skipped: result.skipped });
303
389
  return;
@@ -309,3 +395,24 @@ export async function cmdRetire(root, args) {
309
395
  if (result.removed.length === 0 && result.skipped.length === 0)
310
396
  say(`nothing to remove for role ${role}`);
311
397
  }
398
+ /** `tumwater revert <sha>`: undo one merged change on main, committing the inverse as
399
+ * `tumwater(operator): Revert <short sha>` under the primary-worktree lock. The lock, branch
400
+ * guard, and fail-closed ref/conflict handling live in src/operator/revert.ts; this renderer
401
+ * names the new main head and reminds the caller the reverted role may redo the work. */
402
+ export async function cmdRevert(root, args) {
403
+ // No flags; exactly one positional names the commit to undo. A flag or extra positional
404
+ // fails before anything is looked up or written.
405
+ if (args.length !== 1)
406
+ fail("revert needs a commit (e.g. run tumwater revert 1a2b3c4)");
407
+ // A flag-shaped token is a dropped flag, not a commit: refuse it before rev-parse turns a
408
+ // lone `--json` into the misleading "no such commit". Mirrors `config set`'s
409
+ // flag-looking-value gate and gui-command.ts's --token gate, and a real sha never starts
410
+ // with `-`.
411
+ if (args[0].startsWith("-"))
412
+ fail(`revert takes no flags (got ${args[0]}); pass the commit to undo as the only argument`);
413
+ const result = await revertMergedCommit(root, args[0]);
414
+ if (!result.ok)
415
+ fail(result.error);
416
+ say(`main is now ${shortSha(result.headSha)}`);
417
+ say("the reverted role may redo the work, so cancel or edit its PLANS.md/BUGS.md entry if it should not");
418
+ }