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
@@ -1,30 +1,37 @@
1
- // vocabulary: plain
2
1
  import { setConfigKey, unsetConfigKey } from "../../config/config-write.js";
3
2
  import { loadConfigSafe } from "../../config/config.js";
4
3
  import { DIRECTOR_ROLE } from "../../roles/roles.js";
5
- import { baseRoleOf } from "../../roles/loop-ids.js";
6
- /** The Ctrl+O quiet-hours editor on the shared prompt line, the terminal twin of the dashboard
7
- * role drawer's `Quiet hours` row and `tumwater role <id> --quiet-hours`. It owns its mode
8
- * flag, its saved-draft pair, and the role pinned when it opened, so a roster shift cannot
9
- * redirect the write. Enter writes through the shared setConfigKey/unsetConfigKey family the
10
- * CLI and the drawer use — so checkQuietHours (naming `quietHoursPerRole.<id>`) and
11
- * validateConfig own the window's truth — and Esc, Ctrl+O again, and Ctrl+T cancel by
12
- * restoring the prompt draft. Mutually exclusive with the other editors on the one prompt
13
- * line via the session's held registry. */
4
+ import { baseRoleOf } from "../../roles/runner-ids.js";
5
+ /** The Ctrl+O per-role / Ctrl+S team-wide quiet-hours editor on the shared prompt line, the
6
+ * terminal twin of the dashboard's `Quiet hours` rows and `tumwater role <id> --quiet-hours`
7
+ * / `tumwater config set quietHours`. It owns its mode flags, its saved-draft pair, and the
8
+ * role pinned when it opened (per-role scope only), so a roster shift cannot redirect the
9
+ * write. Enter writes through the shared setConfigKey/unsetConfigKey family the CLI and the
10
+ * drawer use — so checkQuietHours (naming `quietHoursPerRole.<id>`) and validateConfig (naming
11
+ * `quietHours` for the team key) own the window's truth — and Esc, the matching Ctrl again,
12
+ * and Ctrl+T cancel by restoring the prompt draft. Mutually exclusive with the other editors
13
+ * on the one prompt line via the session's held registry. The two scopes never overlap: Ctrl+O
14
+ * opens only on a role's Transcript and toggles only the per-role scope, Ctrl+S only on the
15
+ * Activity view and toggles only the team scope, so a view change cannot leave a half-open
16
+ * editor pointing at the wrong window. */
14
17
  export function createQuietEditor(session) {
15
18
  let quietMode = false;
16
- let savedInput = "";
17
- let savedCursor = 0;
18
- // The role scope pinned when the editor opened, so Enter writes the same role's window even
19
- // if syncRoster shrinks or reorders the roster while the editor is open.
19
+ let quietTeam = false;
20
+ let restoreDraft = () => { };
21
+ // The role scope pinned when the editor opened (per-role scope only), so Enter writes the
22
+ // same role's window even if syncRoster shrinks or reorders the roster while the editor is
23
+ // open. The team scope pins no role: it writes the top-level `quietHours` key.
20
24
  let lockedRole = null;
21
- // Leave quiet-edit mode (Esc, Ctrl+O again, or Ctrl+T): restore the saved prompt text.
25
+ // Leave quiet-edit mode (Esc, the matching Ctrl again, or Ctrl+T): restore the saved prompt
26
+ // text. Clears both scope flags; the caller's arm decides which scope to wake next.
22
27
  const exitQuietMode = () => {
23
28
  if (!quietMode)
24
29
  return;
25
30
  quietMode = false;
31
+ quietTeam = false;
26
32
  session.held.quiet = false;
27
- session.restorePromptFrom(savedInput, savedCursor);
33
+ session.held.quietTeam = false;
34
+ restoreDraft();
28
35
  lockedRole = null;
29
36
  };
30
37
  const handleKey = (_str, key, ctx) => {
@@ -33,11 +40,19 @@ export function createQuietEditor(session) {
33
40
  // hours) on its Transcript view. Every non-transcript view keeps Ctrl+O — the guard
34
41
  // lets the key fall through to the pane arms (Activity's event-type cycle, History's
35
42
  // outcome cycle, Health's problems-only toggle) unless the quiet editor already holds
36
- // the line, in which case Ctrl+O is the toggle-out arm below. Entering pre-fills the
37
- // window from `quietHoursPerRole.<base>` (empty when unset — empty means "off" on
38
- // save) and saves the prompt text; leaving restores it. The director is exempt — its
43
+ // the line. While a scope is open, Ctrl+O toggles only the per-role scope out; in the
44
+ // team scope it is consumed and does nothing. Entering pre-fills the window from
45
+ // `quietHoursPerRole.<base>` (empty when unset — empty means "off" on save) and saves
46
+ // the prompt text; leaving restores it. The director is exempt — its
39
47
  // quietHoursPerRole.director entry is inert (the gate and doctor both exempt it).
40
- if (!quietMode && !(ctx.view >= 1 && ctx.view <= ctx.roleIds.length))
48
+ if (quietMode) {
49
+ // Consumed either way: in the per-role scope it toggles the editor out, in the team
50
+ // scope Ctrl+O leaves the team editor open.
51
+ if (!quietTeam)
52
+ exitQuietMode();
53
+ return true;
54
+ }
55
+ if (!(ctx.view >= 1 && ctx.view <= ctx.roleIds.length))
41
56
  return false;
42
57
  const role = ctx.roleIds[ctx.view - 1];
43
58
  const held = session.heldEditorFlash("quiet");
@@ -51,23 +66,62 @@ export function createQuietEditor(session) {
51
66
  session.flashMessage("the director has no quiet hours");
52
67
  return true;
53
68
  }
54
- if (quietMode) {
55
- exitQuietMode();
56
- return true;
57
- }
58
69
  // Pin the role first so the write below is safe against a mid-edit roster shift, then
59
70
  // pre-fill from the file's own entry — one fresh read per open is the smallest honest
60
71
  // source; a broken config degrades to no pre-fill and the write reports the error.
61
72
  lockedRole = role;
62
73
  const existing = loadConfigSafe(session.root).config?.quietHoursPerRole?.[baseRoleOf(role)] ?? "";
63
- session.settleDraft((t) => (savedInput = t), (n) => (savedCursor = n));
74
+ restoreDraft = session.takeDraft();
64
75
  quietMode = true;
76
+ quietTeam = false;
65
77
  session.held.quiet = true;
78
+ session.held.quietTeam = false;
66
79
  session.input = existing;
67
80
  session.cursor = session.input.length;
68
81
  session.flashMessage(`edit ${role}'s quiet hours (e.g. 23:00-07:00, empty clears): Enter to save, Esc to cancel`);
69
82
  return true;
70
83
  }
84
+ if (key.ctrl && key.name === "s") {
85
+ // Ctrl+S edits the team-wide quiet-hours window on the Activity view (view 0) — the
86
+ // terminal twin of the dashboard Settings `quietHours` row and `tumwater config set
87
+ // quietHours`, so an operator can put the whole team to sleep for a nightly window. On
88
+ // every non-Activity view Ctrl+S keeps its other meaning — the guard lets the key fall
89
+ // through to the interval editor (a role's Transcript) and the pane arms (History's
90
+ // trailing-window cycle). While a scope is open, Ctrl+S toggles only the team scope out;
91
+ // in the per-role scope it is consumed and does nothing (so it never opens the interval
92
+ // editor over the quiet editor). Entering pre-fills the window from the FILE's top-level
93
+ // `quietHours` (empty when unset — empty means "off" on save) and saves the prompt text;
94
+ // leaving restores it. The team window owns no role, so lockedRole stays null.
95
+ if (quietMode) {
96
+ // In the team scope Ctrl+S toggles the editor out. In the per-role scope it must not
97
+ // touch the per-role editor, but it also must not silently vanish: fall through so the
98
+ // interval editor (the other Ctrl+S arm, which runs after this one) flashes the way out,
99
+ // naming the per-role quiet editor as the holder.
100
+ if (!quietTeam)
101
+ return false;
102
+ exitQuietMode();
103
+ return true;
104
+ }
105
+ if (ctx.view !== 0)
106
+ return false;
107
+ const held = session.heldEditorFlash("quiet");
108
+ if (held) {
109
+ // Mutually exclusive with the other editors: all share one prompt line, so a mode may
110
+ // not enter while another holds it. Flash the way out first.
111
+ session.flashMessage(held);
112
+ return true;
113
+ }
114
+ const existing = loadConfigSafe(session.root).config?.quietHours ?? "";
115
+ restoreDraft = session.takeDraft();
116
+ quietMode = true;
117
+ quietTeam = true;
118
+ session.held.quiet = true;
119
+ session.held.quietTeam = true;
120
+ session.input = existing;
121
+ session.cursor = session.input.length;
122
+ session.flashMessage("edit the team's quiet hours (e.g. 23:00-07:00, empty clears): Enter to save, Esc to cancel");
123
+ return true;
124
+ }
71
125
  if (key.name === "escape" && quietMode) {
72
126
  // Esc cancels quiet-edit mode, restoring the previous prompt text.
73
127
  exitQuietMode();
@@ -75,22 +129,32 @@ export function createQuietEditor(session) {
75
129
  }
76
130
  if (key.name === "return" && quietMode) {
77
131
  // Enter commits the window through the shared setter family the CLI's `--quiet-hours`
78
- // and the dashboard drawer use: an empty or whitespace-only line clears the key
79
- // (`unsetConfigKey`), otherwise `setConfigKey` writes the dotted `quietHoursPerRole
80
- // .<base>` entry as a JSON string. checkQuietHours (naming the dotted key) rejects a
81
- // malformed window and validateConfig guards the write; both write tumwater.json
82
- // atomically and the running orchestrator picks the change up on its next poll. An
83
- // invalid window flashes and STAYS in edit mode so the operator can fix it; success
84
- // restores the saved prompt text.
85
- const role = lockedRole;
86
- const cfgKey = `quietHoursPerRole.${baseRoleOf(role)}`;
132
+ // and `tumwater config set quietHours` / the dashboard rows use: an empty or
133
+ // whitespace-only line clears the key (`unsetConfigKey`), otherwise `setConfigKey` writes
134
+ // the dotted `quietHoursPerRole.<base>` entry (per-role scope) or the top-level
135
+ // `quietHours` (team scope) as a JSON string. checkQuietHours (naming the dotted key)
136
+ // rejects a malformed per-role window and validateConfig guards the team write; both
137
+ // write tumwater.json atomically and the running orchestrator picks the change up on its
138
+ // next poll. An invalid window flashes and STAYS in edit mode so the operator can fix
139
+ // it; success restores the saved prompt text.
140
+ const cfgKey = quietTeam ? "quietHours" : `quietHoursPerRole.${baseRoleOf(lockedRole)}`;
87
141
  const text = session.input.trim();
88
142
  const result = text === ""
89
143
  ? unsetConfigKey(session.root, cfgKey)
90
144
  : setConfigKey(session.root, cfgKey, JSON.stringify(text));
91
145
  if (result.ok) {
146
+ const team = quietTeam;
147
+ const role = lockedRole;
92
148
  exitQuietMode();
93
- session.flashMessage(text === "" ? `${role}: quiet hours cleared` : `${role}: quiet hours set to ${text}`);
149
+ const scope = team ? "team" : `${role}`;
150
+ const noop = text === "" ? result.oldValue === undefined : "unchanged" in result;
151
+ session.flashMessage(noop
152
+ ? text === ""
153
+ ? `${scope}: quiet hours is not set in tumwater.json, so nothing was cleared`
154
+ : `${scope}: quiet hours is already ${text}, so tumwater.json was not written`
155
+ : text === ""
156
+ ? `${scope}: quiet hours cleared`
157
+ : `${scope}: quiet hours set to ${text}`);
94
158
  }
95
159
  else {
96
160
  session.flashMessage(result.error); // broken config or write failure — stay in edit mode
@@ -1,4 +1,3 @@
1
- // vocabulary: plain
2
1
  import { artifactPhrase, retireRole } from "../../operator/retire.js";
3
2
  /** The Health view's Ctrl+X retire editor on the shared prompt line, the terminal twin of
4
3
  * `tumwater retire --role <id>` and the dashboard Settings card's Retire button. It owns its
@@ -12,20 +11,19 @@ import { artifactPhrase, retireRole } from "../../operator/retire.js";
12
11
  * so the operator can fix the id, Esc cancels and restores the draft. */
13
12
  export function createRetireEditor(session) {
14
13
  let retireMode = false;
15
- let retireSavedInput = "";
16
- let retireSavedCursor = 0;
14
+ let restoreDraft = () => { };
17
15
  // Leave retire-edit mode (Esc, Enter's success path, or Ctrl+T): restore the saved prompt text.
18
16
  const exitRetireMode = () => {
19
17
  if (!retireMode)
20
18
  return;
21
19
  retireMode = false;
22
20
  session.held.retire = false;
23
- session.restorePromptFrom(retireSavedInput, retireSavedCursor);
21
+ restoreDraft();
24
22
  };
25
23
  const handleKey = (_str, key, ctx) => {
26
24
  if (key.ctrl && key.name === "x" && ctx.view === ctx.roleIds.length + 7 && !retireMode) {
27
25
  // Ctrl+X toggles retire-edit mode on the Health view's prompt line (mnemonic: **x**
28
- // remove — the same key the Transcript notebook-clear, Backlog cancel, and Inbox cancel
26
+ // remove — the same key the Transcript notes-clear, Backlog cancel, and Inbox cancel
29
27
  // arms use on their own views, guarded away here by the Health index). The line edits one
30
28
  // role id; Enter retires that role's leftovers through the same retireRole core the CLI
31
29
  // and the dashboard button use. Entering saves the prompt draft and blanks the line;
@@ -41,7 +39,7 @@ export function createRetireEditor(session) {
41
39
  // Mid-recall, hand the arrows' saved draft back before the retire editor takes the line —
42
40
  // the same settle the role editors do — so leaving restores the draft, not a
43
41
  // history entry, with no stale recall state riding along.
44
- session.settleDraft((t) => (retireSavedInput = t), (n) => (retireSavedCursor = n));
42
+ restoreDraft = session.takeDraft();
45
43
  retireMode = true;
46
44
  session.held.retire = true;
47
45
  session.input = "";
@@ -0,0 +1,76 @@
1
+ import { baseRoleOf } from "../../roles/runner-ids.js";
2
+ /** Build a role-pinned editor. It may take the line only on a role's Transcript view
3
+ * (view 1..roleIds.length) or when it already holds it (the toggle-out arm), and only while no
4
+ * other editor holds the line. An invalid value flashes and STAYS in edit mode so the operator
5
+ * can fix it; success restores the saved prompt text. */
6
+ export function createRoleKeyEditor(session, spec) {
7
+ let mode = false;
8
+ let restoreDraft = () => { };
9
+ // The role pinned when the editor opened, so Enter writes the same role's key even if
10
+ // syncRoster shrinks or reorders the roster while the editor is open.
11
+ let lockedRole = null;
12
+ // Leave the editor (Esc, the key again, or Ctrl+T): restore the saved prompt text.
13
+ const exit = () => {
14
+ if (!mode)
15
+ return;
16
+ mode = false;
17
+ session.held[spec.mode] = false;
18
+ restoreDraft();
19
+ lockedRole = null;
20
+ };
21
+ const handleKey = (_str, key, ctx) => {
22
+ if (key.ctrl && key.name === spec.openCtrl) {
23
+ // While open the key toggles the editor out. Otherwise it opens only on a role's
24
+ // Transcript view; elsewhere it keeps its own meaning and falls through to the pane arms.
25
+ if (mode) {
26
+ exit();
27
+ return true;
28
+ }
29
+ if (!(ctx.view >= 1 && ctx.view <= ctx.roleIds.length) || spec.yieldsOn?.(ctx))
30
+ return false;
31
+ const role = ctx.roleIds[ctx.view - 1];
32
+ const held = session.heldEditorFlash(spec.mode);
33
+ if (held) {
34
+ // Mutually exclusive with the other editors: all share one prompt line, so a mode may
35
+ // not enter while another holds it. Flash the way out first.
36
+ session.flashMessage(held);
37
+ return true;
38
+ }
39
+ // Pin the role first so the write below is safe against a mid-edit roster shift, then
40
+ // pre-fill from the file's own entry — one fresh read per open is the smallest honest
41
+ // source; a broken config degrades to no pre-fill and the write reports the error.
42
+ lockedRole = role;
43
+ const existing = spec.prefill(session, baseRoleOf(role));
44
+ restoreDraft = session.takeDraft();
45
+ mode = true;
46
+ session.held[spec.mode] = true;
47
+ session.input = existing;
48
+ session.cursor = session.input.length;
49
+ session.flashMessage(spec.openFlash(role));
50
+ return true;
51
+ }
52
+ if (key.name === "escape" && mode) {
53
+ // Esc cancels the editor, restoring the previous prompt text.
54
+ exit();
55
+ return true;
56
+ }
57
+ if (key.name === "return" && mode) {
58
+ // Enter commits the whitespace-trimmed line through the spec's shared setter. A rejected
59
+ // value flashes and STAYS in edit mode so the operator can fix it; success restores the
60
+ // saved prompt text.
61
+ const role = lockedRole;
62
+ const text = session.input.trim();
63
+ const result = spec.commit(session, baseRoleOf(role), text);
64
+ if (result.ok) {
65
+ exit();
66
+ session.flashMessage(result.noop && spec.noopFlash ? spec.noopFlash(role, text) : spec.savedFlash(role, text));
67
+ }
68
+ else {
69
+ session.flashMessage(result.error); // broken config or write failure — stay in edit mode
70
+ }
71
+ return true;
72
+ }
73
+ return false;
74
+ };
75
+ return { handleKey, exit };
76
+ }
@@ -0,0 +1,28 @@
1
+ import { setRoleModelFields } from "../../config/config-write.js";
2
+ import { rawRoleField } from "../../roles/role-view.js";
3
+ import { createRoleKeyEditor } from "./tui-editor-role-key.js";
4
+ /** The Ctrl+X role-model editor on the Transcript view's shared prompt line, the terminal twin
5
+ * of `tumwater role <id> --model` / `--clear-model` and the dashboard role drawer's Model
6
+ * overrides editor. Enter writes the viewed base role's `roles.<base>.model` through the shared
7
+ * setRoleModelFields the CLI and the drawer use — so checkModelTripleField (a non-empty model id
8
+ * or a bare tier name like `strong`) owns the value's truth, and an empty line clears the
9
+ * override. Pre-fill reads the FILE's raw `roles.<base>.model`: overlayDefaults seeds every
10
+ * built-in role's model from the catalog, so the merged value cannot tell an operator override
11
+ * from a seeded default. On the notes view Ctrl+X keeps its clear-notes meaning, and on every
12
+ * non-Transcript view it falls through to its own pane arms (the backlog cancel, the queued
13
+ * cancel, the Health retire). The shape it shares with the instances editor lives in
14
+ * createRoleKeyEditor. */
15
+ export function createRoleModelEditor(session) {
16
+ return createRoleKeyEditor(session, {
17
+ mode: "roleModel",
18
+ openCtrl: "x",
19
+ yieldsOn: (ctx) => ctx.noteMode,
20
+ prefill: (s, base) => {
21
+ const existing = rawRoleField(s.root, base, "model");
22
+ return typeof existing === "string" ? existing : "";
23
+ },
24
+ openFlash: (role) => `edit ${role}'s model override (a model id or tier name like strong; empty clears): Enter to save, Esc to cancel`,
25
+ commit: (s, base, text) => setRoleModelFields(s.root, base, { model: text }),
26
+ savedFlash: (role, text) => text === "" ? `${role}: model override cleared` : `${role}: model override set to ${text}`,
27
+ });
28
+ }
@@ -1,14 +1,14 @@
1
- /** The shared `/` search editor on the prompt line: over the History pane's tick rows or the
1
+ /** The shared `/` search editor on the prompt line: over the History pane's run rows or the
2
2
  * Activity feed's events. It owns its mode flag (`searchMode`), its target pane (`searchFor`),
3
3
  * its saved-draft pair, and the applyHistorySearch / applyActivitySearch leaves. Mutually
4
4
  * exclusive with the other editors via the session's held registry. */
5
5
  export function createSearchEditor(session) {
6
6
  let searchMode = false;
7
- // Which pane the shared `/` editor feeds: History's tick search or Activity's event search.
8
- // The target is captured when `/` opens so a roster change mid-edit cannot retarget Enter.
7
+ // Which pane the shared `/` editor feeds: History's run search, Activity's event search, the
8
+ // Backlog pane's entry search, or a role's live-transcript search. The target is captured when
9
+ // `/` opens so a roster change mid-edit cannot retarget Enter.
9
10
  let searchFor = "history";
10
- let searchSavedInput = "";
11
- let searchSavedCursor = 0;
11
+ let restoreDraft = () => { };
12
12
  // Leave the shared search editor (Enter applies, Esc cancels, Ctrl+T cycles): restore the
13
13
  // saved prompt text, and reset the recall state with it — the same settle contract the budget
14
14
  // and role-prompt editors honor. The applied activityGrep/historyGrep itself persists across
@@ -18,27 +18,37 @@ export function createSearchEditor(session) {
18
18
  return;
19
19
  searchMode = false;
20
20
  session.held.search = false;
21
- session.restorePromptFrom(searchSavedInput, searchSavedCursor);
21
+ restoreDraft();
22
22
  };
23
23
  const handleKey = (str, key, ctx) => {
24
- if (!key.ctrl && str === "/" && !searchMode && !session.held.budget &&
25
- !session.held.rolePromptFor && !session.held.pause &&
26
- !session.held.queued && !session.held.answer && !session.held.entry &&
27
- !session.held.attach && !session.held.retire &&
28
- (ctx.view === 0 || ctx.view === ctx.roleIds.length + 4) && session.input === "") {
24
+ if (!key.ctrl && str === "/" && !searchMode && !session.anyEditorHeld() &&
25
+ (ctx.view === 0 || ctx.view === ctx.roleIds.length + 1 || ctx.view === ctx.roleIds.length + 4 || ctx.view === ctx.roleIds.length + 6 ||
26
+ (ctx.view >= 1 && ctx.view <= ctx.roleIds.length && !ctx.noteMode && !ctx.changeMode)) && session.input === "") {
29
27
  // `/` opens a free-text search on the prompt line (the pager's search key; this project
30
- // already reserves `/` for a meta action): over the History pane's tick rows on History,
31
- // over the Activity feed's events on Activity. Guarded to those two views and to an empty
32
- // line, so elsewhere it types into the prompt as today and a director draft is never
33
- // disturbed. Pre-fills the applied pattern so Enter re-submits it and the operator can
34
- // edit it; Esc cancels without applying.
35
- session.settleDraft((t) => (searchSavedInput = t), (n) => (searchSavedCursor = n));
36
- searchFor = ctx.view === 0 ? "activity" : "history";
28
+ // already reserves `/` for a meta action): over the History pane's run rows on History,
29
+ // over the Activity feed's events on Activity, and over the Backlog pane's active entries
30
+ // on Backlog, over the Inbox pane's queued prompts, and over a role's live transcript.
31
+ // Guarded to those views and to an empty line, so elsewhere it types into the prompt as
32
+ // today and a director draft is never disturbed. Pre-fills the applied pattern so Enter
33
+ // re-submits it and the operator can edit it; Esc cancels without applying.
34
+ restoreDraft = session.takeDraft();
35
+ if (ctx.view === 0)
36
+ searchFor = "activity";
37
+ else if (ctx.view === ctx.roleIds.length + 1)
38
+ searchFor = "backlog";
39
+ else if (ctx.view === ctx.roleIds.length + 4)
40
+ searchFor = "history";
41
+ else if (ctx.view === ctx.roleIds.length + 6)
42
+ searchFor = "queued";
43
+ else
44
+ searchFor = "transcript";
37
45
  searchMode = true;
38
46
  session.held.search = true;
39
- session.input = (searchFor === "activity" ? ctx.activityGrep : ctx.historyGrep) ?? "";
47
+ const applied = { activity: ctx.activityGrep, history: ctx.trailSearch ? ctx.trailGrep : ctx.historyGrep, backlog: ctx.backlogGrep, queued: ctx.queuedGrep, transcript: ctx.transcriptGrep }[searchFor];
48
+ const noun = { activity: "events", history: ctx.trailSearch ? "transcript" : "runs", backlog: "backlog", queued: "inbox", transcript: "transcript" }[searchFor];
49
+ session.input = applied ?? "";
40
50
  session.cursor = session.input.length;
41
- session.flashMessage(searchFor === "activity" ? "search events: Enter to filter, Esc to cancel" : "search runs: Enter to filter, Esc to cancel");
51
+ session.flashMessage(`search ${noun}: Enter to filter, Esc to cancel`);
42
52
  return true;
43
53
  }
44
54
  if (key.name === "escape" && searchMode) {
@@ -50,21 +60,23 @@ export function createSearchEditor(session) {
50
60
  // Enter applies the trimmed line to the pane the editor opened over: Activity sets
51
61
  // activityGrep (an empty line clears it) and resets the scroll to the live tail — the
52
62
  // offset indexed the old filtered list; History recomputes its row cache now (the pane
53
- // is showing) and re-enters at its head. A successful apply restores the saved prompt
54
- // draft through exitHistorySearch, the shared editor's exit.
55
- const pattern = session.input.trim();
56
- if (searchFor === "activity") {
57
- const applied = pattern === "" ? null : pattern;
63
+ // is showing) and re-enters at its head; Backlog sets backlogGrep and clears its entry
64
+ // selection so the next frame starts in list mode; Inbox sets queuedGrep, clears its row
65
+ // selection, and re-reads the filtered rows. A successful apply restores the saved
66
+ // prompt draft through exitHistorySearch, the shared editor's exit.
67
+ const applied = session.input.trim() === "" ? null : session.input.trim();
68
+ if (searchFor === "activity")
58
69
  session.applyActivitySearch(applied);
59
- exitHistorySearch();
60
- session.flashMessage(applied === null ? "search cleared" : `search: "${applied}"`);
61
- }
62
- else {
63
- const applied = pattern === "" ? null : pattern;
70
+ else if (searchFor === "history")
64
71
  session.applyHistorySearch(applied);
65
- exitHistorySearch();
66
- session.flashMessage(applied === null ? "search cleared" : `search: "${applied}"`);
67
- }
72
+ else if (searchFor === "backlog")
73
+ session.applyBacklogSearch(applied);
74
+ else if (searchFor === "queued")
75
+ session.applyQueuedSearch(applied);
76
+ else
77
+ session.applyTranscriptSearch(applied);
78
+ exitHistorySearch();
79
+ session.flashMessage(applied === null ? "search cleared" : `search: "${applied}"`);
68
80
  return true;
69
81
  }
70
82
  return false;
@@ -1,5 +1,26 @@
1
- // vocabulary: plain
2
1
  import { newPromptHistory, resetPromptRecall, settlePromptRecall, } from "./tui-prompt-history.js";
2
+ /** The prompt-line editors a keypress can be refused for, each with its busy-guard subject, in
3
+ * the order `heldEditorFlash` reports them when more than one flag reads held. */
4
+ const HOLDER_SUBJECTS = [
5
+ ["attach", "image attach"],
6
+ ["notes", "notes edit"],
7
+ ["brief", "brief edit"],
8
+ ["retire", "retire role"],
9
+ ["quiet", "quiet hours"],
10
+ ["model", "model edit"],
11
+ ["interval", "min run interval"],
12
+ ["instructions", "instructions edit"],
13
+ ["providerCaps", "provider caps"],
14
+ ["concurrency", "run cap edit"],
15
+ ["slots", "worktree slots"],
16
+ ["notify", "notify hook"],
17
+ ["backends", "agent backends"],
18
+ ["fallback", "fallback edit"],
19
+ ["roleModel", "role model edit"],
20
+ ["instances", "role instances"],
21
+ ["claudeReserve", "claude reserve"],
22
+ ["budget", "budget edit"],
23
+ ];
3
24
  /** Build the shared prompt-line session from the disk-side deps the aggregator received. The
4
25
  * editor modules then take this session plus each keypress's `TuiEditorCtx`. */
5
26
  export function createTuiEditorSession(deps) {
@@ -24,7 +45,19 @@ export function createTuiEditorSession(deps) {
24
45
  brief: false,
25
46
  retire: false,
26
47
  quiet: false,
48
+ quietTeam: false,
27
49
  interval: false,
50
+ instructions: null,
51
+ providerCaps: false,
52
+ concurrency: false,
53
+ slots: false,
54
+ notify: false,
55
+ backends: false,
56
+ fallback: false,
57
+ roleModel: false,
58
+ model: false,
59
+ instances: false,
60
+ claudeReserve: false,
28
61
  },
29
62
  root: deps.root,
30
63
  now: deps.now,
@@ -35,28 +68,41 @@ export function createTuiEditorSession(deps) {
35
68
  clearBacklogSelection: deps.clearBacklogSelection,
36
69
  applyHistorySearch: deps.applyHistorySearch,
37
70
  applyActivitySearch: deps.applyActivitySearch,
71
+ applyBacklogSearch: deps.applyBacklogSearch,
72
+ applyQueuedSearch: deps.applyQueuedSearch,
73
+ applyTranscriptSearch: deps.applyTranscriptSearch,
38
74
  flashMessage: deps.flashMessage,
39
75
  flashError: deps.flashError,
40
76
  commitBacklogEdit: deps.commitBacklogEdit,
41
- restorePromptFrom(text, pos) {
42
- session.input = text;
43
- session.cursor = pos;
44
- session.promptHistory = resetPromptRecall(session.promptHistory);
45
- },
46
- settleDraft(saveInput, saveCursor) {
77
+ takeDraft() {
47
78
  const settled = settlePromptRecall(session.promptHistory, session.input, session.cursor);
48
79
  session.promptHistory = settled.history;
49
- saveInput(settled.text);
50
- saveCursor(settled.cursor);
80
+ return () => {
81
+ session.input = settled.text;
82
+ session.cursor = settled.cursor;
83
+ session.promptHistory = resetPromptRecall(session.promptHistory);
84
+ };
51
85
  },
52
86
  busyFlash(subject) {
53
87
  return `finish or cancel the ${subject} first (Esc cancels)`;
54
88
  },
89
+ anyEditorHeld(...except) {
90
+ const h = session.held;
91
+ if (h.search && !except.includes("search"))
92
+ return true;
93
+ if (h.pause && !except.includes("pause"))
94
+ return true;
95
+ if (h.rolePromptFor && !except.includes("rolePromptFor"))
96
+ return true;
97
+ if (h.queued || h.answer || h.entry)
98
+ return true;
99
+ return HOLDER_SUBJECTS.some(([mode]) => h[mode]);
100
+ },
55
101
  heldEditorFlash(entering) {
56
102
  const h = session.held;
57
103
  if (h.search)
58
104
  return session.busyFlash("search");
59
- if (h.pause)
105
+ if (h.pause && entering !== "pause")
60
106
  return session.busyFlash("pause");
61
107
  if (h.queued)
62
108
  return session.busyFlash("inbox edit");
@@ -66,30 +112,10 @@ export function createTuiEditorSession(deps) {
66
112
  return session.busyFlash("entry edit");
67
113
  if (h.rolePromptFor)
68
114
  return session.busyFlash(`prompt for ${h.rolePromptFor}`);
69
- if (h.attach && entering !== "attach")
70
- return session.busyFlash("image attach");
71
- if (h.notes && entering !== "notes")
72
- return session.busyFlash("notes edit");
73
- if (h.brief && entering !== "brief")
74
- return session.busyFlash("brief edit");
75
- if (h.retire && entering !== "retire")
76
- return session.busyFlash("retire role");
77
- if (h.quiet && entering !== "quiet")
78
- return session.busyFlash("quiet hours");
79
- if (h.interval && entering !== "interval")
80
- return session.busyFlash("min run interval");
81
- if (entering === "attach" && h.budget)
82
- return session.busyFlash("budget edit");
83
- if (entering === "notes" && (h.attach || h.budget))
84
- return session.busyFlash(h.attach ? "image attach" : "budget edit");
85
- if (entering === "brief" && h.budget)
86
- return session.busyFlash("budget edit");
87
- if (entering === "retire" && (h.attach || h.notes || h.budget))
88
- return session.busyFlash(h.attach ? "image attach" : h.notes ? "notes edit" : "budget edit");
89
- if (entering === "quiet" && (h.attach || h.notes || h.budget || h.brief || h.retire))
90
- return session.busyFlash(h.attach ? "image attach" : h.notes ? "notes edit" : h.brief ? "brief edit" : h.retire ? "retire role" : "budget edit");
91
- if (entering === "interval" && (h.attach || h.notes || h.budget || h.brief || h.retire || h.quiet))
92
- return session.busyFlash(h.attach ? "image attach" : h.notes ? "notes edit" : h.brief ? "brief edit" : h.retire ? "retire role" : h.quiet ? "quiet hours" : "budget edit");
115
+ for (const [mode, subject] of HOLDER_SUBJECTS) {
116
+ if (h[mode] && entering !== mode)
117
+ return session.busyFlash(subject);
118
+ }
93
119
  return null;
94
120
  },
95
121
  };
@@ -0,0 +1,23 @@
1
+ import { loadConfigSafe } from "../../config/config.js";
2
+ import { createTeamConfigKeyEditor } from "./tui-editor-config.js";
3
+ /** The Ctrl+W worktree-pool editor on the shared prompt line, the terminal twin of the
4
+ * dashboard Settings `Worktree slots` field and `tumwater config set worktreeSlots`. It owns
5
+ * its mode flag and its saved-draft pair. Enter writes through the shared
6
+ * setConfigKey/unsetConfigKey family the CLI and the dashboard use — so validateConfig owns
7
+ * the value's truth (`worktreeSlots must be an integer of at least 1`) — and Esc, Ctrl+W
8
+ * again, and Ctrl+T cancel by restoring the prompt draft. On the Activity view (view 0) it
9
+ * opens pre-filled from the file's `worktreeSlots` (empty when unset — the default is
10
+ * `maxConcurrent + 1`); on a role's Transcript view (where the shared pause editor's wake arm
11
+ * pins Ctrl+W) it falls through so the wake editor keeps the key. Mutually exclusive with the
12
+ * other editors on the one prompt line via the session's held registry. */
13
+ export function createSlotsEditor(session) {
14
+ return createTeamConfigKeyEditor(session, {
15
+ mode: "slots",
16
+ openCtrl: "w",
17
+ configKey: "worktreeSlots",
18
+ prefill: (s) => String(loadConfigSafe(s.root).config?.worktreeSlots ?? ""),
19
+ openFlash: "edit the team's worktree pool size (worktreeSlots, an integer ≥ 1; empty restores the default of maxConcurrent + 1): Enter to save, Esc to cancel",
20
+ clearedFlash: "worktree slots reset to the default",
21
+ setFlash: (text) => `worktree slots set to ${text}`,
22
+ });
23
+ }