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,12 +1,11 @@
1
- // vocabulary: plain
2
1
  /** The CLI layer of `tumwater prompt` — the steering-prompt command: submit a prompt to the
3
- * director (default) or one role's queue, `--list` what is queued with per-loop position
2
+ * director (default) or one role's queue, `--list` what is queued with per-role position
4
3
  * numbering, or `--cancel <n>` a queued prompt by position. Split out of
5
4
  * operator/operator-commands.ts because this command is not a team-control marker: its
6
5
  * sibling commands there ride the operator-intent marker protocol, while this command drives
7
- * the durable per-loop queues (src/inbox/inbox.ts) through the inbox-cancel, inbox-edit, and
6
+ * the durable per-role queues (src/inbox/inbox.ts) through the inbox-cancel, inbox-edit, and
8
7
  * operator-intent helpers, each with its own broken-config policy — so the command lives
9
- * beside the queue module it drives. The team-side half — the dequeue and requeue a loop
8
+ * beside the queue module it drives. The team-side half — the dequeue and requeue a role
10
9
  * performs — is inbox.ts and pending-prompt.ts. */
11
10
  import { fail, say, sayJson } from "../cli/cli-output.js";
12
11
  import path from "node:path";
@@ -15,8 +14,8 @@ import { cancelListedPrompt, cancelRolePrompt, resolveListedQueue, } from "./inb
15
14
  import { editListedPrompt, editQueuedFile, editRolePrompt, readQueuedPromptBody, } from "./inbox-edit.js";
16
15
  import { movePromptToTop } from "./inbox-top.js";
17
16
  import { editInEditor } from "../cli/cli-editor.js";
18
- import { listWideScope, queuedFileAtPosition, promptPreview, queuedListRows, renderQueuedList } from "./inbox.js";
19
- import { durationLabel } from "../cli/cli-args.js";
17
+ import { listWideScope, queuedFileAtPosition, promptPreview, queuedListRows, queuedRowsMatching, renderQueuedList } from "./inbox.js";
18
+ import { durationLabel, parseRoleListFlag } from "../cli/cli-args.js";
20
19
  import { knownRoleIds, knownRoleIdsCached, loadConfig } from "../config/config.js";
21
20
  import { errorMessage } from "../text/text.js";
22
21
  import { readPromptImageFiles } from "./inbox-attachments.js";
@@ -33,8 +32,8 @@ function failQueueMiss(listed, position) {
33
32
  if (listed.status === "ambiguous") {
34
33
  fail(`position ${position} is in more than one role's inbox (${listed.roles.join(", ")}) — name one with --role <id>`);
35
34
  }
36
- // `queued` is the longest queue's length — the highest position any loop's numbering can
37
- // reach — so a miss names it as the valid range when at least one loop holds a prompt.
35
+ // `queued` is the longest queue's length — the highest position any role's numbering can
36
+ // reach — so a miss names it as the valid range when at least one role holds a prompt.
38
37
  const range = listed.queued > 0 ? ` — valid: 1-${listed.queued}` : "";
39
38
  fail(`no prompt at position ${position} (longest inbox holds ${listed.queued})${range}`);
40
39
  }
@@ -46,15 +45,15 @@ function isQueueMiss(listed) {
46
45
  return listed.status === "ambiguous" || listed.status === "missing";
47
46
  }
48
47
  /** The shared tail of the list-wide cancel/edit branches: report an ambiguity (naming the
49
- * rival loops, with the `--role` escape hatch) or a miss (carrying the length of the longest
50
- * queue, the highest position numbering any loop can hold), else render the resolved outcome
48
+ * rival roles, with the `--role` escape hatch) or a miss (carrying the length of the longest
49
+ * queue, the highest position numbering any role can hold), else render the resolved outcome
51
50
  * through sayPromptOutcome. One helper so the
52
51
  * two verbs' handling of the miss shapes cannot drift. */
53
52
  function sayListedOutcome(listed, position) {
54
53
  const found = failQueueMiss(listed, position);
55
54
  sayPromptOutcome(position, found.role, found.outcome, true);
56
55
  }
57
- /** Run one loop-targeted queue verb (cancelRolePrompt/editRolePrompt) for a `--role`-scoped
56
+ /** Run one role-targeted queue verb (cancelRolePrompt/editRolePrompt) for a `--role`-scoped
58
57
  * command: a thrown failure (a broken config, an unwritable queue) exits through fail with
59
58
  * its message, a success returns the verb's outcome for the caller to render. One helper so
60
59
  * the cancel and edit branches' broken-config policy cannot drift. */
@@ -70,7 +69,7 @@ function runRolePromptCommand(root, role, position, verb) {
70
69
  * cancel and edit modes (list-wide and `--role`-scoped each): a concurrent dequeue is a normal
71
70
  * race, not an error, so it is reported and exited clean; a resolved one previews the text it
72
71
  * removed (a cancel's `text`) or wrote (an edit's `newText`). When `labelRole` the line names
73
- * the loop — the no-`--role` command resolves across every loop, so its output must say where
72
+ * the role — the no-`--role` command resolves across every role, so its output must say where
74
73
  * the prompt went — while a `--role`-scoped command already names it in the user's own
75
74
  * command. One helper so the cancel and edit wordings cannot drift. */
76
75
  function sayPromptOutcome(position, role, outcome, labelRole) {
@@ -83,7 +82,7 @@ function sayPromptOutcome(position, role, outcome, labelRole) {
83
82
  say(labelRole ? `${outcome.status} (${role}): ${promptPreview(text)}` : `${outcome.status}: ${promptPreview(text)}`);
84
83
  }
85
84
  /** The gone-case tail shared by every resolved queue-verb reply: a concurrent dequeue is a
86
- * normal race, not an error, so report it and exit clean, naming the loop that took the
85
+ * normal race, not an error, so report it and exit clean, naming the role that took the
87
86
  * prompt. One helper so the cancel, edit and top verbs' gone wording cannot drift. */
88
87
  function sayPromptGone(position, role) {
89
88
  say(`prompt ${position} is no longer in the inbox — ${roleLabel(role)} already took it`);
@@ -91,7 +90,7 @@ function sayPromptGone(position, role) {
91
90
  /** The user-facing reply for one resolved top — the two branches of cmdPrompt's top mode
92
91
  * (list-wide and `--role`-scoped): a concurrent dequeue is a normal race, not an error, so it
93
92
  * is reported and exited clean; a resolved one previews the moved prompt. When `labelRole` the
94
- * line names the loop — the no-`--role` command resolves across every loop, so its output must
93
+ * line names the role — the no-`--role` command resolves across every role, so its output must
95
94
  * say where the prompt moved — while a `--role`-scoped command already names it in the user's
96
95
  * own command. The `sayPromptOutcome` contract, so the verbs cannot drift. */
97
96
  function sayTopOutcome(position, role, outcome, labelRole) {
@@ -115,53 +114,67 @@ function readPromptImages(paths) {
115
114
  }
116
115
  }
117
116
  /** `tumwater prompt [--role <id>] <text|list|cancel <n>|top <n>>`: submit a steering prompt to the
118
- * director (default) or one role's queue, list what is queued with per-loop position
119
- * numbering, cancel a queued prompt by position, or move one to the head of its
120
- * queue. The dispatcher in cli.ts gates on a ready
121
- * repo and delegates here; list output is grouped by loop — the director first (its queue is
117
+ * director (default) or one role's queue (`--list` takes `--role <id>[,<id>...]`), list what
118
+ * is queued with per-role position numbering, cancel a queued prompt by position, or move
119
+ * one to the head of its queue. The dispatcher in cli.ts gates on a ready repo and
120
+ * delegates here; list output is grouped by role — the director first (its queue is
122
121
  * the shared one every pre-1/2 prompt landed in), then each role with queued prompts — so a
123
122
  * per-role queue's position numbering stays unambiguous. */
124
123
  export async function cmdPrompt(root, args) {
125
124
  const parsed = parsePromptArgs(args);
126
- if (parsed.role !== null)
125
+ // --list takes a comma list (parseRoleListFlag below); every other mode keeps one id.
126
+ if (parsed.role !== null && parsed.mode !== "list")
127
127
  parsed.role = canonicalRoleId(parsed.role);
128
- // `--role <id>` scopes every mode to one loop's queue; the value is validated here, with
128
+ // `--role <id>` scopes every mode to one role's queue; the value is validated here, with
129
129
  // the same message every other --role consumer uses. The director is always valid: its
130
130
  // queue is the historical inbox (inbox.ts).
131
131
  //
132
132
  // Where the id set comes from differs by mode, following parseRoleScope's split: --list is
133
133
  // a read-only view, so a transiently broken tumwater.json must not take it down — it falls
134
134
  // back to the built-in catalog (knownRoleIdsCached), exactly like cmdLogs/cmdHistory's
135
- // --role scope, at the cost of a broken config hiding a custom loop's queued section. The
135
+ // --role scope, at the cost of a broken config hiding a custom role's queued section. The
136
136
  // state-changing modes (enqueue, cancel) instead read through loadConfig and fail loudly
137
- // when an id was given: before writing to a named loop's queue the operator is owed the
138
- // config error, not a built-ins-only guess about whether the loop exists. With no --role,
137
+ // when an id was given: before writing to a named role's queue the operator is owed the
138
+ // config error, not a built-ins-only guess about whether the role exists. With no --role,
139
139
  // enqueue touches only the director queue (inbox.ts) and never reads the config — a broken
140
140
  // tumwater.json must not block steering the director — while cancel resolves its position
141
- // across the per-loop sections --list prints, through the same cached, never-throwing id set
141
+ // across the per-role sections --list prints, through the same cached, never-throwing id set
142
142
  // --list uses, so it cancels exactly what the list showed even under a broken config.
143
143
  const validIds = parsed.mode === "list"
144
144
  ? knownRoleIdsCached(root)
145
145
  : parsed.role !== null
146
146
  ? knownRoleIds(loadConfig(root))
147
147
  : null;
148
- if (validIds !== null && parsed.role !== null && !validIds.includes(parsed.role)) {
148
+ if (parsed.mode !== "list" && validIds !== null && parsed.role !== null && !validIds.includes(parsed.role)) {
149
149
  fail(unknownRoleMessage(parsed.role, validIds));
150
150
  }
151
151
  const role = parsed.role;
152
152
  if (parsed.mode === "list") {
153
153
  // One payload, two shapes: the prose render and the --json output both come from the
154
- // same array, so the per-loop positions --cancel consumes can never disagree with the
154
+ // same array, so the per-role positions --cancel consumes can never disagree with the
155
155
  // data a script reads. Full text, verbatim: this is the inspection command that tells
156
156
  // you what a queued prompt actually says before you cancel it.
157
- const prompts = queuedListRows(root, role, validIds);
157
+ // `--role a,b` filters the list-wide rows, so section order and each section's own
158
+ // numbering stay what `--cancel <n> --role <id>` takes.
159
+ const listRoles = parseRoleListFlag(args, validIds);
160
+ const allRows = queuedListRows(root, null, validIds);
161
+ const scoped = listRoles === null ? allRows : allRows.filter((row) => listRoles.includes(row.role));
162
+ const prompts = queuedRowsMatching(scoped, parsed.grep ?? null);
158
163
  if (parsed.json) {
159
164
  // An empty queue still prints the document, the history --json empty-rows precedent.
160
165
  sayJson({ prompts });
161
166
  return;
162
167
  }
168
+ if (prompts.length === 0 && scoped.length > 0) {
169
+ say(`no queued prompt matches "${parsed.grep}"`);
170
+ return;
171
+ }
163
172
  if (prompts.length === 0) {
164
- say(role !== null ? `${roleLabel(role)}'s inbox is empty` : "every inbox is empty");
173
+ say(listRoles === null
174
+ ? "every inbox is empty"
175
+ : listRoles.length === 1
176
+ ? `${roleLabel(listRoles[0])}'s inbox is empty`
177
+ : `every listed inbox is empty (${listRoles.join(", ")})`);
165
178
  return;
166
179
  }
167
180
  say(renderQueuedList(prompts, Date.now()));
@@ -169,10 +182,10 @@ export async function cmdPrompt(root, args) {
169
182
  }
170
183
  if (parsed.mode === "cancel") {
171
184
  if (role === null) {
172
- // No --role: the position addresses what --list shows — its per-loop sections, each
185
+ // No --role: the position addresses what --list shows — its per-role sections, each
173
186
  // numbered from 1 (the director first, then the roles in catalog order). Resolve across
174
- // that scope: one loop holding the position cancels there, several are ambiguous (the
175
- // list itself shows two "N." lines), none is a miss. The output names the loop, since
187
+ // that scope: one role holding the position cancels there, several are ambiguous (the
188
+ // list itself shows two "N." lines), none is a miss. The output names the role, since
176
189
  // the caller scoped nothing.
177
190
  sayListedOutcome(cancelListedPrompt(root, listWideScope(root), parsed.position), parsed.position);
178
191
  return;
@@ -190,14 +203,14 @@ export async function cmdPrompt(root, args) {
190
203
  const images = parsed.attachPaths.length > 0 ? readPromptImages(parsed.attachPaths) : undefined;
191
204
  // Image-input gate, `--edit`'s twin of the submit branch below (plans/image-input.md
192
205
  // "Attachments", part 5/5): editing an attachment into a queued prompt is the same
193
- // tick-time discovery the submit gate prevents, so the refusal promise holds across every
206
+ // run-time discovery the submit gate prevents, so the refusal promise holds across every
194
207
  // CLI attach surface — an image no configured model can read is refused before a byte is
195
- // written, and a loop that reroutes a blind read is told where the prompt will run. The
196
- // target loop is resolved first (the same resolution the verb below makes) so the gate can
208
+ // written, and a role that reroutes a blind read is told where the prompt will run. The
209
+ // target role is resolved first (the same resolution the verb below makes) so the gate can
197
210
  // name it.
198
211
  if (images !== undefined && images.length > 0) {
199
- const loop = role ?? failQueueMiss(resolveListedQueue(root, listWideScope(root), parsed.position), parsed.position).role;
200
- const guidance = imagePromptGuidance(root, loop);
212
+ const targetRole = role ?? failQueueMiss(resolveListedQueue(root, listWideScope(root), parsed.position), parsed.position).role;
213
+ const guidance = imagePromptGuidance(root, targetRole);
201
214
  if (guidance.refusal)
202
215
  return fail(guidance.refusal);
203
216
  if (guidance.notice)
@@ -205,7 +218,7 @@ export async function cmdPrompt(root, args) {
205
218
  }
206
219
  const typed = parsed.text;
207
220
  if (typed === null) {
208
- // Bare `--edit <n>`: open the prompt's body in the editor. The loop is resolved first
221
+ // Bare `--edit <n>`: open the prompt's body in the editor. The role is resolved first
209
222
  // and the write goes by file name, so a dequeue while the editor is open reports gone
210
223
  // instead of editing the neighbour that shifted into the position.
211
224
  let target = role;
@@ -213,18 +226,18 @@ export async function cmdPrompt(root, args) {
213
226
  const resolved = resolveListedQueue(root, listWideScope(root), parsed.position);
214
227
  target = failQueueMiss(resolved, parsed.position).role;
215
228
  }
216
- const loop = target;
229
+ const targetRole = target;
217
230
  let name;
218
231
  let body;
219
232
  try {
220
- name = path.basename(queuedFileAtPosition(root, loop, parsed.position));
221
- body = readQueuedPromptBody(root, loop, name);
233
+ name = path.basename(queuedFileAtPosition(root, targetRole, parsed.position));
234
+ body = readQueuedPromptBody(root, targetRole, name);
222
235
  }
223
236
  catch (err) {
224
237
  return fail(errorMessage(err));
225
238
  }
226
239
  if (body === null) {
227
- sayPromptOutcome(parsed.position, loop, { status: "gone" }, role === null);
240
+ sayPromptOutcome(parsed.position, targetRole, { status: "gone" }, role === null);
228
241
  return;
229
242
  }
230
243
  const after = editInEditor(body, "prompt.md");
@@ -237,7 +250,7 @@ export async function cmdPrompt(root, args) {
237
250
  return;
238
251
  }
239
252
  try {
240
- sayPromptOutcome(parsed.position, loop, editQueuedFile(root, loop, name, after, images), role === null);
253
+ sayPromptOutcome(parsed.position, targetRole, editQueuedFile(root, targetRole, name, after, images), role === null);
241
254
  }
242
255
  catch (err) {
243
256
  fail(errorMessage(err));
@@ -254,19 +267,19 @@ export async function cmdPrompt(root, args) {
254
267
  }
255
268
  if (parsed.mode === "top") {
256
269
  // The same list-wide resolution the cancel branch uses, one verb on: no --role resolves the
257
- // position across `--list`'s per-loop sections and moves it in the resolved loop's queue;
270
+ // position across `--list`'s per-role sections and moves it in the resolved role's queue;
258
271
  // with --role it is the role-scoped call. A concurrent dequeue reports gone and exits
259
- // clean; position 1 of the resolved loop is "already first".
272
+ // clean; position 1 of the resolved role is "already first".
260
273
  if (role === null) {
261
- const loop = failQueueMiss(resolveListedQueue(root, listWideScope(root), parsed.position), parsed.position).role;
274
+ const targetRole = failQueueMiss(resolveListedQueue(root, listWideScope(root), parsed.position), parsed.position).role;
262
275
  let outcome;
263
276
  try {
264
- outcome = movePromptToTop(root, loop, parsed.position);
277
+ outcome = movePromptToTop(root, targetRole, parsed.position);
265
278
  }
266
279
  catch (err) {
267
280
  return fail(errorMessage(err));
268
281
  }
269
- sayTopOutcome(parsed.position, loop, outcome, true);
282
+ sayTopOutcome(parsed.position, targetRole, outcome, true);
270
283
  return;
271
284
  }
272
285
  const target = role ?? DIRECTOR_ROLE;
@@ -285,9 +298,9 @@ export async function cmdPrompt(root, args) {
285
298
  // parsed duration, computed at submit so the deferral starts when the operator queued it.
286
299
  const dueMs = parsed.atDelayMs !== null ? Date.now() + parsed.atDelayMs : undefined;
287
300
  // `--attach <path>`: read and validate through the shared helper (a bad path fails by name
288
- // before anything is queued). An attachment the target loop cannot read is refused here by
301
+ // before anything is queued). An attachment the target role cannot read is refused here by
289
302
  // name — the same gate submitRolePrompt enforces, surfaced early so the CLI exits non-zero
290
- // with the fix before anything is written or queued; a loop that reroutes a blind read is
303
+ // with the fix before anything is written or queued; a role that reroutes a blind read is
291
304
  // not refused, but the operator is told where the prompt will actually run.
292
305
  const images = parsed.attachPaths.length > 0 ? readPromptImages(parsed.attachPaths) : undefined;
293
306
  if (images && images.length > 0) {
@@ -5,7 +5,7 @@
5
5
  * format migrates. Composed by notBeforeMarker, parsed by notBeforeMs — the two live together
6
6
  * so format and parser cannot drift. inbox.ts (the prompt-queue store) writes the marker into
7
7
  * enqueueRolePrompt's single atomic write and filters on deliverableNow; the display surfaces
8
- * (prompt-commands.ts, tick-prompt.ts) strip it with stripNotBeforeMarker. */
8
+ * (prompt-commands.ts, run-prompt.ts) strip it with stripNotBeforeMarker. */
9
9
  /** The marker line a deferred prompt's queue file starts with. */
10
10
  const NOT_BEFORE_PREFIX = "tumwater:not-before ";
11
11
  /** The marker line (plus the blank separator) that defers a queue file to `at` epoch ms.
@@ -17,7 +17,7 @@ export function notBeforeMarker(at) {
17
17
  * `YYYY-MM-DDTHH:MM:SS.sssZ`. Only a marker carrying the writer's full shape — the stamp line
18
18
  * plus the blank separator notBeforeMarker appends — counts as plumbing; anything else
19
19
  * after the prefix is the prompt's own content (e.g. a queued prompt that begins with a
20
- * marker-like line asking a loop to fix the marker itself, or a hand-edited file that lost
20
+ * marker-like line asking a role to fix the marker itself, or a hand-edited file that lost
21
21
  * the blank line), not a deferral. The stamp and separator are checked together so this
22
22
  * parser and stripNotBeforeMarker — which strips exactly this shape — can never disagree
23
23
  * about what is plumbing. */
@@ -1,9 +1,8 @@
1
- // vocabulary: plain
2
1
  /** The bundled project templates for `tumwater init --template`
3
2
  * (docs/feature-project-templates.md). Static string data, no file reads and no runtime
4
3
  * dependencies: a template is a brief preamble (the operator's own words stay appended after
5
4
  * it) plus a seeded PLANS.md backlog and a starter directory layout — never code scaffolds,
6
- * since the team's own first ticks write any code.
5
+ * since the team's own first runs write any code.
7
6
  * `blank` is the default and reproduces today's init output byte-identically. */
8
7
  import { typoSuffix } from "../text/suggest.js";
9
8
  const PYTHON_CLI_PREAMBLE = `This project is a Python command-line tool.
@@ -1,13 +1,13 @@
1
- // vocabulary: plain
2
1
  import fs from "node:fs";
3
2
  import path from "node:path";
4
3
  import { saveConfig } from "../config/config.js";
5
4
  import { seedConfig } from "../config/config-example.js";
6
5
  import { findOnPath, writeTextAtomic } from "../files/files.js";
7
- import { COMMIT_IDENT, GIT_MISSING_MESSAGE, git, gitTry } from "../git/git-run.js";
6
+ import { GIT_MISSING_MESSAGE, commitAuthorArgs, committerIdentArgs, git, gitTry } from "../git/git-run.js";
8
7
  import { hasCommits, isGitRepo } from "../git/git.js";
9
- import { INITIAL_PROMPT_MAX_CHARS, PROMPT_END, PROMPT_START, briefFile, briefTemplate, readInitialPrompt, readmeTemplate, } from "../brief/brief.js";
10
- import { CONFIG_BASENAME, STATE_DIR, configPath } from "../paths.js";
8
+ import { BRIEF_TEXT_MAX_CHARS, PROMPT_END, PROMPT_START, briefFile, briefTemplate, readBriefText, readmeTemplate, } from "../brief/brief.js";
9
+ import { teamWorkspaceMessage } from "../gates/readiness.js";
10
+ import { CONFIG_BASENAME, STATE_DIR, configPath, owningTeamRoot } from "../paths.js";
11
11
  import { projectName } from "../project-name.js";
12
12
  import { tooLongMessage } from "../text/text.js";
13
13
  import { PREFILL_REASON } from "../text/phrases.js";
@@ -58,7 +58,7 @@ function isScaffoldingPath(rel) {
58
58
  /** Whether `root` has a project of its own yet: not a git repo, a repo with no commits, or a
59
59
  * repo whose tracked and untracked-non-ignored files are all init's scaffolding. Drives the
60
60
  * bootstrap seed — a project created from nothing runs plan and feature before the maintenance
61
- * loops (plans/work-ratio.md, part 2/2). `withCommits` is whether `root` is a repo with at least
61
+ * roles (plans/work-ratio.md, part 2/2). `withCommits` is whether `root` is a repo with at least
62
62
  * one commit, which initProject has already established. One `ls-files -co` lists the tracked
63
63
  * files and the untracked non-ignored ones together (the exclude rules filter only the
64
64
  * untracked side). */
@@ -80,7 +80,7 @@ async function isFreshProject(root, withCommits) {
80
80
  new repository only and is refused on an existing one, where its value would otherwise be silently
81
81
  ignored. `dryRun` runs
82
82
  * the same validation and computes the same lists, but writes, inits and commits nothing. */
83
- export async function initProject(root, initialPrompt, branch, opts = {}) {
83
+ export async function initProject(root, briefText, branch, opts = {}) {
84
84
  const dryRun = opts.dryRun === true;
85
85
  // Resolve the template before any side effect: an unknown id (possible for direct callers of
86
86
  // initProject — the CLI parser already validated its own path) is refused up front, and the
@@ -92,24 +92,29 @@ export async function initProject(root, initialPrompt, branch, opts = {}) {
92
92
  const tpl = template ?? getTemplate("blank");
93
93
  // The brief records the template's preamble ahead of the operator's words, so their words
94
94
  // remain the tail ("latest instruction wins" reads naturally). The combined text rides into
95
- // every tick's prefill, so it obeys the same cap as a bare prompt — reject before any side
95
+ // every run's prefill, so it obeys the same cap as a bare prompt — reject before any side
96
96
  // effect so an overflowing preamble never lands in README.md.
97
97
  const combinedPrompt = tpl.briefPreamble
98
- ? `${tpl.briefPreamble}\n\n${initialPrompt.trim()}`
99
- : initialPrompt.trim();
100
- if (combinedPrompt.length > INITIAL_PROMPT_MAX_CHARS) {
101
- throw new Error(tooLongMessage(`the brief with the ${tpl.id} template's preamble`, combinedPrompt.length, INITIAL_PROMPT_MAX_CHARS, PREFILL_REASON));
98
+ ? `${tpl.briefPreamble}\n\n${briefText.trim()}`
99
+ : briefText.trim();
100
+ if (combinedPrompt.length > BRIEF_TEXT_MAX_CHARS) {
101
+ throw new Error(tooLongMessage(`the brief with the ${tpl.id} template's preamble`, combinedPrompt.length, BRIEF_TEXT_MAX_CHARS, PREFILL_REASON));
102
102
  }
103
103
  if (!findOnPath("git"))
104
104
  throw new Error(GIT_MISSING_MESSAGE);
105
+ // A team workspace is not a project: seeding it would commit tumwater.json and a phantom
106
+ // .tumwater on the role's branch. Refused before any side effect, dry run included.
107
+ const owner = owningTeamRoot(root);
108
+ if (owner !== undefined)
109
+ throw new Error(teamWorkspaceMessage(owner));
105
110
  // Validate everything that is pure validation before any side effect, so a bad prompt or
106
111
  // README never leaves a half-seeded repo behind.
107
- const prompt = initialPrompt.trim();
112
+ const prompt = briefText.trim();
108
113
  // A project brief that already carries the prompt makes it optional: a fresh clone of an
109
114
  // initialized project, or a checkout that lost its now-untracked tumwater.json
110
115
  // (plans/portability.md §4a/7), re-seeds with a bare `tumwater init` — the existing brief
111
116
  // file is never rewritten, so a prompt given here must match it (refused below otherwise).
112
- if (!prompt && combinedPrompt.trim() === "" && readInitialPrompt(root) === "") {
117
+ if (!prompt && combinedPrompt.trim() === "" && readBriefText(root) === "") {
113
118
  // A bare init with no prompt is only ever refused for one of two reasons, and they have
114
119
  // different fixes: no README (nothing to read — the message below is the whole story) or a
115
120
  // README that lacks the managed markers (bare init reads them — name the file and the
@@ -130,8 +135,8 @@ export async function initProject(root, initialPrompt, branch, opts = {}) {
130
135
  if (branch !== undefined && wasRepo) {
131
136
  throw new Error(`--branch only seeds a new repository: this directory is already a git repository and init leaves its checked-out branch alone — drop --branch, or point the team at a branch with \`tumwater start --branch ${branch}\` (or baseBranch in tumwater.json)`);
132
137
  }
133
- // The loops read the project's reason to exist back out of the project brief on every
134
- // tick (readInitialPrompt). A README.md without the managed section is the project's own
138
+ // The roles read the project's reason to exist back out of the project brief on every
139
+ // run (readBriefText). A README.md without the managed section is the project's own
135
140
  // documentation, so it is never rewritten: the repo is adopted instead, with the brief in
136
141
  // TUMWATER.md (plans/portability.md §7/7) — the same path `--adopt` asks for explicitly.
137
142
  // A brief that is already marked (in either file) wins over both: nothing brief-shaped is
@@ -140,18 +145,18 @@ export async function initProject(root, initialPrompt, branch, opts = {}) {
140
145
  const readmeExists = fs.existsSync(path.join(root, "README.md"));
141
146
  const adopted = brief === null && (opts.adopt === true || readmeExists);
142
147
  // A TUMWATER.md without the markers is create-if-absent's blind spot: it would be left
143
- // untouched and the prompt silently dropped — every loop would then run blind. Fail before
148
+ // untouched and the prompt silently dropped — every role would then run blind. Fail before
144
149
  // creating anything so the user fixes it and re-runs.
145
150
  if (adopted && fs.existsSync(path.join(root, "TUMWATER.md"))) {
146
151
  throw new Error(`TUMWATER.md already exists without a brief between the tumwater:prompt markers, so your prompt would be lost — add it to TUMWATER.md between ${PROMPT_START} and ${PROMPT_END} (or delete TUMWATER.md so init creates one), then re-run \`tumwater init\``);
147
152
  }
148
153
  // The same loss one step later: a brief that already owns the managed sections is never
149
154
  // rewritten (init only creates what is absent), so a DIFFERENT prompt given here would be
150
- // silently dropped while every tick keeps running the old one. Refuse it and name the file
155
+ // silently dropped while every run keeps running the old one. Refuse it and name the file
151
156
  // that owns the brief — TUMWATER.md resolves ahead of README.md (plans/portability.md §7a/7),
152
157
  // so pointing at README.md would send the user to edit a file nobody reads. A re-run with the
153
158
  // same prompt, or a bare init, is the idempotent re-seed and passes.
154
- if (prompt && brief !== null && combinedPrompt !== readInitialPrompt(root)) {
159
+ if (prompt && brief !== null && combinedPrompt !== readBriefText(root)) {
155
160
  throw new Error(`${brief} already carries a different brief between the tumwater:prompt markers, and init never rewrites an existing project brief, so your prompt would be lost — to change the prompt, edit it in ${brief} between ${PROMPT_START} and ${PROMPT_END}; to re-seed with the current one, re-run a bare \`tumwater init\``);
156
161
  }
157
162
  let repoInitialized = false;
@@ -207,7 +212,7 @@ export async function initProject(root, initialPrompt, branch, opts = {}) {
207
212
  const write = (name, content) => claim(name, path.join(root, name), () => writeTextAtomic(path.join(root, name), content));
208
213
  // README.md is the brief only when none exists yet and there is no README to adopt (a fresh
209
214
  // repo): with TUMWATER.md owning the managed sections, a created README.md would carry a
210
- // duplicate prompt + status block that readInitialPrompt never reaches and the readme role
215
+ // duplicate prompt + status block that readBriefText never reaches and the readme role
211
216
  // never maintains.
212
217
  const name = projectName(root);
213
218
  if (brief !== null)
@@ -220,7 +225,7 @@ export async function initProject(root, initialPrompt, branch, opts = {}) {
220
225
  else
221
226
  write("README.md", readmeTemplate(name, combinedPrompt));
222
227
  // A template with starter plans seeds the backlog with them, rendered as `### ` entries —
223
- // the primary entry format every backlog reader parses — so the team's first ticks land on
228
+ // the primary entry format every backlog reader parses — so the team's first runs land on
224
229
  // real work. blank keeps the `_None yet._` placeholder byte-identical to today.
225
230
  const plansContent = tpl.starterPlans.length > 0
226
231
  ? PLANS_TEMPLATE.replace("_None yet._", tpl.starterPlans.map((p) => `### ${p}`).join("\n\n"))
@@ -246,8 +251,8 @@ export async function initProject(root, initialPrompt, branch, opts = {}) {
246
251
  created.push(".gitignore");
247
252
  else if (fs.existsSync(path.join(root, ".gitignore")))
248
253
  leftAlone.push(".gitignore");
249
- // The template's starter directories: empty ones only, before the first tick — the team's
250
- // own ticks write any code. Existing directories are left alone, like existing files above.
254
+ // The template's starter directories: empty ones only, before the first run — the team's
255
+ // own runs write any code. Existing directories are left alone, like existing files above.
251
256
  for (const dir of tpl.starterDirs) {
252
257
  claim(dir, path.join(root, dir), () => fs.mkdirSync(path.join(root, dir), { recursive: true }));
253
258
  }
@@ -265,7 +270,7 @@ export async function initProject(root, initialPrompt, branch, opts = {}) {
265
270
  if (staged === null) {
266
271
  // Non-zero exit = something is staged.
267
272
  const message = withCommits ? "tumwater: init files" : "tumwater: init";
268
- await git(root, ...COMMIT_IDENT, "commit", "-m", message, "--", ...committable);
273
+ await git(root, ...(await committerIdentArgs(root)), "commit", ...commitAuthorArgs(), "-m", message, "--", ...committable);
269
274
  committed = true;
270
275
  }
271
276
  }