tumwater 0.2.0 → 0.4.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.
- package/README.md +27 -13
- package/dist/build-info.json +2 -2
- package/dist/src/agent/agent-args.js +8 -9
- package/dist/src/agent/agent-backend.js +8 -6
- package/dist/src/agent/backend-traits.js +55 -25
- package/dist/src/agent/claude/claude-account.js +71 -204
- package/dist/src/agent/claude/claude-args.js +9 -6
- package/dist/src/agent/claude/claude-backend.js +10 -10
- package/dist/src/agent/claude/claude-env.js +1 -1
- package/dist/src/agent/claude/claude-stream.js +6 -4
- package/dist/src/agent/claude/claude-write-guard.js +49 -0
- package/dist/src/agent/claude/write-guard-hook.js +13 -0
- package/dist/src/agent/codex/codex-account.js +119 -0
- package/dist/src/agent/codex/codex-args.js +181 -0
- package/dist/src/agent/codex/codex-backend.js +279 -0
- package/dist/src/agent/codex/codex-catalog.js +97 -0
- package/dist/src/agent/codex/codex-env.js +18 -0
- package/dist/src/agent/codex/codex-hook-trust.js +246 -0
- package/dist/src/agent/codex/codex-patch-paths.js +36 -0
- package/dist/src/agent/codex/codex-rollout.js +179 -0
- package/dist/src/agent/codex/codex-stream.js +404 -0
- package/dist/src/agent/codex/codex-write-guard.js +86 -0
- package/dist/src/agent/codex/write-guard-hook.js +12 -0
- package/dist/src/agent/image-input.js +29 -24
- package/dist/src/agent/notes-shim.js +11 -12
- package/dist/src/agent/opencode/opencode-args.js +0 -1
- package/dist/src/agent/opencode/opencode-backend.js +12 -9
- package/dist/src/agent/opencode/opencode-catalog.js +55 -29
- package/dist/src/agent/opencode/opencode-config.js +19 -14
- package/dist/src/agent/opencode/opencode-env.js +0 -1
- package/dist/src/agent/opencode/opencode-session-id.js +1 -2
- package/dist/src/agent/opencode/opencode-stream.js +1 -1
- package/dist/src/agent/opencode/write-guard-plugin/index.js +48 -0
- package/dist/src/agent/role-notes-write.js +5 -6
- package/dist/src/agent/run-agent.js +465 -448
- package/dist/src/agent/run-settle.js +214 -0
- package/dist/src/agent/session-ledger.js +86 -16
- package/dist/src/agent/subscription-account.js +220 -0
- package/dist/src/agent/write-guard.js +148 -0
- package/dist/src/backlog/backlog-archive.js +281 -55
- package/dist/src/backlog/backlog-cli-view.js +255 -0
- package/dist/src/backlog/backlog-cli.js +91 -335
- package/dist/src/backlog/backlog-commit.js +20 -13
- package/dist/src/backlog/backlog-eligibility.js +193 -242
- package/dist/src/backlog/backlog-index.js +149 -0
- package/dist/src/backlog/backlog-md.js +54 -6
- package/dist/src/backlog/backlog-parts.js +264 -0
- package/dist/src/backlog/backlog-release.js +134 -0
- package/dist/src/backlog/backlog-render.js +27 -0
- package/dist/src/backlog/backlog-structure.js +99 -156
- package/dist/src/backlog/backlog-write.js +40 -127
- package/dist/src/backlog/backlog.js +21 -6
- package/dist/src/backlog/question-answer.js +21 -15
- package/dist/src/baseline/main-baseline.js +89 -20
- package/dist/src/baseline/main-guardrails.js +136 -0
- package/dist/src/baseline/main-red.js +153 -36
- package/dist/src/baseline/main-watch.js +232 -0
- package/dist/src/brief/brief-edit.js +12 -12
- package/dist/src/brief/brief.js +31 -51
- package/dist/src/budget/budget-fallback-view.js +288 -0
- package/dist/src/budget/budget-wiring.js +158 -0
- package/dist/src/budget/budget.js +31 -20
- package/dist/src/budget/fallback-breaker.js +26 -27
- package/dist/src/build/build-check-detect.js +23 -15
- package/dist/src/build/build-check-events.js +25 -6
- package/dist/src/build/build-check-report.js +49 -9
- package/dist/src/build/build-check-scoped.js +18 -11
- package/dist/src/build/build-check.js +2 -1
- package/dist/src/build/build-info.js +2 -3
- package/dist/src/build/build-stage.js +9 -14
- package/dist/src/build/dep-install.js +65 -3
- package/dist/src/change/change-data.js +123 -31
- package/dist/src/change/change-render.js +46 -5
- package/dist/src/cli/brief-command.js +3 -4
- package/dist/src/cli/cli-args.js +181 -33
- package/dist/src/cli/cli-command-args.js +46 -18
- package/dist/src/cli/cli-editor.js +2 -1
- package/dist/src/cli/cli-flag-specs.js +183 -45
- package/dist/src/cli/cli-init.js +51 -0
- package/dist/src/cli/cli-marker-commands.js +4 -4
- package/dist/src/cli/cli-query-commands.js +291 -598
- package/dist/src/cli/cli-repo-gate.js +11 -1
- package/dist/src/cli/cli-review-commands.js +113 -0
- package/dist/src/cli/cli-role-command.js +525 -0
- package/dist/src/cli/cli-start-summary.js +167 -0
- package/dist/src/cli/{cli-run.js → cli-start.js} +193 -237
- package/dist/src/cli/completion.js +169 -22
- package/dist/src/cli/config-commands.js +58 -26
- package/dist/src/cli/follow-view.js +75 -0
- package/dist/src/cli/help.js +258 -82
- package/dist/src/cli/log-commands.js +164 -85
- package/dist/src/cli/question-commands.js +211 -10
- package/dist/src/cli.js +173 -45
- package/dist/src/collections.js +56 -13
- package/dist/src/concurrency/check-permit.js +20 -3
- package/dist/src/concurrency/lock.js +17 -13
- package/dist/src/concurrency/provider-lease.js +44 -8
- package/dist/src/concurrency/semaphore.js +102 -26
- package/dist/src/config/config-aliases.js +0 -1
- package/dist/src/config/config-editable-keys.js +16 -1
- package/dist/src/config/config-fallbacks.js +94 -56
- package/dist/src/config/config-field-checks.js +40 -10
- package/dist/src/config/config-keys.js +30 -8
- package/dist/src/config/config-live.js +63 -24
- package/dist/src/config/config-request.js +137 -0
- package/dist/src/config/config-schema.js +31 -24
- package/dist/src/config/config-selector-checks.js +123 -0
- package/dist/src/config/config-validation-agents.js +80 -0
- package/dist/src/config/config-validation-model.js +116 -0
- package/dist/src/config/config-validation.js +169 -222
- package/dist/src/config/config-views.js +136 -54
- package/dist/src/config/config-write-core.js +135 -0
- package/dist/src/config/config-write.js +164 -265
- package/dist/src/config/config.js +53 -38
- package/dist/src/config/model-selector.js +34 -34
- package/dist/src/doctor/doctor-backlog-bugs.js +330 -0
- package/dist/src/doctor/doctor-backlog-plans.js +398 -0
- package/dist/src/doctor/doctor-backlog.js +249 -512
- package/dist/src/doctor/doctor-check-names.js +85 -0
- package/dist/src/doctor/doctor-checks.js +61 -593
- package/dist/src/doctor/doctor-claude-checks.js +142 -0
- package/dist/src/doctor/doctor-codex-checks.js +276 -0
- package/dist/src/doctor/doctor-contention-checks.js +506 -0
- package/dist/src/doctor/doctor-fallback-checks.js +130 -0
- package/dist/src/doctor/doctor-holds.js +80 -66
- package/dist/src/doctor/doctor-install.js +2 -1
- package/dist/src/doctor/doctor-model-checks.js +216 -537
- package/dist/src/doctor/doctor-notify.js +9 -5
- package/dist/src/doctor/doctor-opencode-checks.js +164 -0
- package/dist/src/doctor/doctor-orphans.js +182 -63
- package/dist/src/doctor/doctor-pi-checks.js +62 -0
- package/dist/src/doctor/doctor-project-checks.js +397 -0
- package/dist/src/doctor/doctor.js +126 -77
- package/dist/src/doctor/image-probe.js +21 -8
- package/dist/src/doctor/outcome.js +3 -2
- package/dist/src/doctor/probe.js +21 -12
- package/dist/src/events/event-format.js +185 -89
- package/dist/src/events/event-labels.js +11 -5
- package/dist/src/events/event-legacy.js +39 -15
- package/dist/src/events/event-read.js +52 -39
- package/dist/src/events/event-tally.js +16 -0
- package/dist/src/events/event-window.js +80 -26
- package/dist/src/events/events.js +30 -13
- package/dist/src/events/notify.js +7 -7
- package/dist/src/failure/failure-cluster.js +12 -12
- package/dist/src/failure/failure-data.js +65 -60
- package/dist/src/failure/failure-render.js +29 -21
- package/dist/src/failure/failure-state-change.js +17 -12
- package/dist/src/failure/time-spend.js +35 -35
- package/dist/src/files/file-queue.js +17 -0
- package/dist/src/files/files.js +111 -35
- package/dist/src/files/json-files.js +3 -3
- package/dist/src/files/json-object.js +28 -7
- package/dist/src/files/stat-cache.js +2 -2
- package/dist/src/files/tail.js +1 -1
- package/dist/src/gates/bootstrap-gates.js +4 -5
- package/dist/src/gates/budget-gates.js +88 -225
- package/dist/src/gates/disk-gate.js +1 -1
- package/dist/src/gates/gate-polls.js +101 -143
- package/dist/src/gates/maintenance-quota.js +37 -30
- package/dist/src/gates/pause-gates.js +28 -10
- package/dist/src/gates/readiness.js +7 -3
- package/dist/src/gates/role-cap-gates.js +8 -8
- package/dist/src/gates/startup-gate.js +94 -32
- package/dist/src/gates/streak-gate.js +54 -30
- package/dist/src/git/commit-message.js +89 -46
- package/dist/src/git/git-diff.js +16 -10
- package/dist/src/git/git-run.js +79 -38
- package/dist/src/git/git.js +54 -34
- package/dist/src/git/slot-salvage.js +253 -0
- package/dist/src/git/slots-state.js +6 -6
- package/dist/src/git/worktree-pool.js +132 -38
- package/dist/src/git/worktree-use.js +4 -11
- package/dist/src/git/worktree.js +94 -37
- package/dist/src/gui/gui-admin-endpoints.js +96 -0
- package/dist/src/gui/gui-args.js +79 -8
- package/dist/src/gui/gui-backlog-commands.js +43 -19
- package/dist/src/gui/gui-backlog-endpoints.js +156 -0
- package/dist/src/gui/gui-command.js +26 -13
- package/dist/src/gui/gui-config-commands.js +168 -80
- package/dist/src/gui/gui-endpoint-commands.js +5 -4
- package/dist/src/gui/gui-endpoint-common.js +4 -4
- package/dist/src/gui/gui-endpoints.js +11 -380
- package/dist/src/gui/gui-history-endpoints.js +301 -0
- package/dist/src/gui/gui-operator-commands.js +235 -13
- package/dist/src/gui/gui-prompt-commands.js +26 -20
- package/dist/src/gui/gui-report-endpoints.js +97 -0
- package/dist/src/gui/gui-server.js +26 -5
- package/dist/src/gui/gui-team-commands.js +7 -7
- package/dist/src/gui/http-body.js +1 -1
- package/dist/src/history/history-data.js +122 -234
- package/dist/src/history/history-queued.js +155 -0
- package/dist/src/history/history-render.js +42 -22
- package/dist/src/history/history.js +190 -49
- package/dist/src/inbox/inbox-attachments.js +29 -30
- package/dist/src/inbox/inbox-cancel.js +10 -11
- package/dist/src/inbox/inbox-edit.js +9 -9
- package/dist/src/inbox/inbox-submit.js +18 -18
- package/dist/src/inbox/inbox-top.js +28 -17
- package/dist/src/inbox/inbox.js +42 -43
- package/dist/src/inbox/pending-prompt.js +26 -26
- package/dist/src/inbox/prompt-commands.js +62 -49
- package/dist/src/inbox/prompt-not-before.js +2 -2
- package/dist/src/init/init-templates.js +1 -2
- package/dist/src/init/init.js +28 -23
- package/dist/src/merge/backlog-conflicts.js +215 -35
- package/dist/src/merge/merge-batch.js +14 -8
- package/dist/src/merge/merge-cancel.js +53 -0
- package/dist/src/merge/merge-check-failures.js +44 -22
- package/dist/src/merge/merge-core.js +58 -32
- package/dist/src/merge/merge-diff.js +5 -4
- package/dist/src/merge/merge-drain.js +45 -19
- package/dist/src/merge/merge-git.js +177 -38
- package/dist/src/merge/merge-pipeline.js +15 -3
- package/dist/src/merge/merge-post-review.js +214 -0
- package/dist/src/merge/merge-queue-render.js +48 -11
- package/dist/src/merge/merge-queue.js +52 -15
- package/dist/src/merge/merge-requeue.js +103 -0
- package/dist/src/merge/merge-slot.js +61 -16
- package/dist/src/merge/merge-stack.js +27 -11
- package/dist/src/merge/merge-to-main.js +77 -26
- package/dist/src/merge/merge-top.js +83 -0
- package/dist/src/merge/merge-verify.js +75 -18
- package/dist/src/notes-cli.js +35 -15
- package/dist/src/operator/operator-commands.js +192 -81
- package/dist/src/operator/operator-intent.js +69 -42
- package/dist/src/operator/operator-requests.js +35 -40
- package/dist/src/operator/retire.js +5 -6
- package/dist/src/operator/revert.js +142 -34
- package/dist/src/orchestrator/orchestrator-launch.js +60 -52
- package/dist/src/orchestrator/orchestrator-poll.js +352 -0
- package/dist/src/orchestrator/orchestrator-scheduling.js +71 -370
- package/dist/src/orchestrator/orchestrator.js +129 -369
- package/dist/src/orchestrator/retention.js +42 -8
- package/dist/src/orchestrator/scheduling-admit.js +116 -0
- package/dist/src/orchestrator/scheduling-scope.js +245 -0
- package/dist/src/orchestrator/scheduling-skip.js +195 -0
- package/dist/src/orchestrator/startup-migration-steps.js +8 -7
- package/dist/src/orchestrator/startup-migrations.js +11 -7
- package/dist/src/paths.js +126 -49
- package/dist/src/pi/command-shape.js +12 -1
- package/dist/src/pi/pi-args.js +6 -4
- package/dist/src/pi/pi-backend.js +6 -6
- package/dist/src/pi/pi-bin.js +54 -63
- package/dist/src/pi/pi-listed-models.js +53 -0
- package/dist/src/pi/pi-models.js +63 -30
- package/dist/src/pi/pi-run-result.js +1 -1
- package/dist/src/pi/pi-settings.js +1 -1
- package/dist/src/pi/pi-stream.js +6 -6
- package/dist/src/pi/pi-watchdogs.js +18 -19
- package/dist/src/pi-extension/bounded-output.js +5 -3
- package/dist/src/pi-extension/context-budget.js +2 -2
- package/dist/src/pi-extension/context-shake.js +1 -1
- package/dist/src/pi-extension/context-window.js +0 -1
- package/dist/src/pi-extension/full-output.js +3 -10
- package/dist/src/pi-extension/strip-thinking.js +0 -1
- package/dist/src/pi-extension/trim-system-prompt.js +3 -4
- package/dist/src/pi-extension/write-guard.js +37 -0
- package/dist/src/process/launch-services.js +1 -2
- package/dist/src/process/node-bin.js +16 -0
- package/dist/src/process/process-group.js +8 -18
- package/dist/src/process/process.js +41 -2
- package/dist/src/process/run-marker.js +26 -6
- package/dist/src/process/supervisor.js +22 -21
- package/dist/src/prompt/dialect.js +17 -2
- package/dist/src/prompt/gate-notes.js +203 -0
- package/dist/src/prompt/gate-prompts.js +150 -217
- package/dist/src/prompt/notes-backlog.js +184 -0
- package/dist/src/prompt/principles.js +1 -2
- package/dist/src/prompt/prompt-followup.js +19 -14
- package/dist/src/prompt/prompt.js +131 -204
- package/dist/src/redeploy/redeploy-policy.js +24 -19
- package/dist/src/redeploy/redeploy-probes.js +1 -1
- package/dist/src/redeploy/redeploy.js +2 -3
- package/dist/src/redeploy/redeployer.js +20 -20
- package/dist/src/redeploy/self-reload.js +2 -1
- package/dist/src/report/report-data.js +188 -124
- package/dist/src/report/report-fold.js +130 -54
- package/dist/src/report/report-main-red.js +55 -0
- package/dist/src/report/report-render.js +136 -65
- package/dist/src/report/report-types.js +1 -0
- package/dist/src/report/report.js +23 -9
- package/dist/src/request-timeouts.js +2 -2
- package/dist/src/review/diff-rules.js +53 -0
- package/dist/src/review/known-flakes.js +112 -1
- package/dist/src/review/post-merge-review-accept.js +51 -0
- package/dist/src/review/post-merge-review-render.js +112 -0
- package/dist/src/review/post-merge-review.js +239 -0
- package/dist/src/review/protected-edits.js +24 -10
- package/dist/src/review/quarantine-render.js +65 -0
- package/dist/src/review/review-followup.js +8 -8
- package/dist/src/review/review-precheck.js +66 -23
- package/dist/src/review/review-run.js +395 -0
- package/dist/src/review/review-verdict.js +2 -2
- package/dist/src/review/review.js +180 -401
- package/dist/src/review/secret-leaks.js +36 -16
- package/dist/src/review/suite-rerun.js +0 -1
- package/dist/src/roles/legacy-role-migration.js +80 -7
- package/dist/src/roles/role-catalog.js +127 -12
- package/dist/src/roles/role-guidance.js +65 -20
- package/dist/src/roles/role-labels.js +7 -5
- package/dist/src/roles/role-render.js +108 -56
- package/dist/src/roles/role-view.js +95 -56
- package/dist/src/roles/roles.js +33 -33
- package/dist/src/roles/runner-ids.js +97 -0
- package/dist/src/run/acceptance-greps.js +119 -0
- package/dist/src/run/ci-parity.js +121 -0
- package/dist/src/run/feedback-filings.js +51 -0
- package/dist/src/run/qa-coverage.js +125 -0
- package/dist/src/{tick/tick-apply.js → run/run-apply.js} +95 -94
- package/dist/src/run/run-detail-data.js +175 -0
- package/dist/src/run/run-detail-render.js +91 -0
- package/dist/src/run/run-detail.js +168 -0
- package/dist/src/{tick/tick-finalize.js → run/run-finalize.js} +73 -60
- package/dist/src/{tick/tick-outcome.js → run/run-outcome.js} +6 -7
- package/dist/src/{tick/tick-prompt.js → run/run-prompt.js} +122 -73
- package/dist/src/{tick/tick-resume.js → run/run-resume.js} +55 -26
- package/dist/src/{tick/tick-stage.js → run/run-stage.js} +170 -83
- package/dist/src/{tick/tick-timing.js → run/run-timing.js} +43 -41
- package/dist/src/{tick/tick-usage.js → run/run-usage.js} +45 -37
- package/dist/src/{tick/tick-verdict.js → run/run-verdict.js} +105 -57
- package/dist/src/run/selfcheck-digest.js +40 -0
- package/dist/src/run/stage-backlog-checks.js +249 -0
- package/dist/src/run/stage-check.js +178 -0
- package/dist/src/run/stage-checks-shared.js +65 -0
- package/dist/src/run/stage-name-checks.js +204 -0
- package/dist/src/run/stage-path-checks.js +231 -0
- package/dist/src/run/stage-prose-checks.js +261 -0
- package/dist/src/runner/leftover.js +216 -0
- package/dist/src/{loop → runner}/model-fallback.js +12 -12
- package/dist/src/runner/revision.js +191 -0
- package/dist/src/{loop/loop-fallback.js → runner/runner-fallback.js} +8 -8
- package/dist/src/runner/runner-outcome.js +109 -0
- package/dist/src/{loop/loop-pi.js → runner/runner-pi.js} +140 -131
- package/dist/src/{loop/loop-recovery.js → runner/runner-recovery.js} +39 -18
- package/dist/src/{loop/loop-tick.js → runner/runner-run.js} +236 -245
- package/dist/src/{loop/loop-state.js → runner/runner-state-load.js} +61 -114
- package/dist/src/runner/runner-state.js +103 -0
- package/dist/src/{loop/loop.js → runner/runner.js} +123 -120
- package/dist/src/scheduling/backoff.js +38 -38
- package/dist/src/scheduling/claim-entries.js +49 -0
- package/dist/src/scheduling/claims.js +35 -22
- package/dist/src/scheduling/once-round.js +22 -22
- package/dist/src/scheduling/quiet-hours.js +5 -6
- package/dist/src/scheduling/scheduling.js +74 -64
- package/dist/src/scheduling/work-merged-cache.js +2 -2
- package/dist/src/status/status-budget.js +55 -47
- package/dist/src/status/status-data.js +49 -17
- package/dist/src/status/status-inbox.js +2 -2
- package/dist/src/status/status-install.js +0 -1
- package/dist/src/status/status-polls.js +43 -28
- package/dist/src/status/status-roles.js +50 -13
- package/dist/src/team/error-storm.js +8 -8
- package/dist/src/team/failure-spread.js +2 -2
- package/dist/src/team/orchestrator-info.js +15 -9
- package/dist/src/team/reclaim.js +7 -8
- package/dist/src/team/team-hold.js +7 -7
- package/dist/src/team/team-polls.js +91 -50
- package/dist/src/team/team-state.js +8 -2
- package/dist/src/text/datetime.js +65 -18
- package/dist/src/text/format.js +48 -11
- package/dist/src/text/markdown.js +8 -5
- package/dist/src/text/phrases.js +60 -23
- package/dist/src/text/suggest.js +1 -1
- package/dist/src/text/text.js +78 -13
- package/dist/src/ui/badges.js +99 -38
- package/dist/src/ui/gui/gui-client-backlog.js +314 -0
- package/dist/src/ui/gui/gui-client-boot.js +89 -61
- package/dist/src/ui/gui/gui-client-composer.js +8 -9
- package/dist/src/ui/gui/gui-client-drawer-config.js +254 -0
- package/dist/src/ui/gui/gui-client-drawer.js +155 -289
- package/dist/src/ui/gui/gui-client-feed.js +271 -0
- package/dist/src/ui/gui/gui-client-history.js +437 -68
- package/dist/src/ui/gui/gui-client-labels.js +6 -3
- package/dist/src/ui/gui/gui-client-markdown.js +1 -2
- package/dist/src/ui/gui/gui-client-model.js +27 -36
- package/dist/src/ui/gui/gui-client-operator.js +60 -52
- package/dist/src/ui/gui/gui-client-pending.js +405 -24
- package/dist/src/ui/gui/gui-client-report-chart.js +492 -0
- package/dist/src/ui/gui/gui-client-report.js +105 -430
- package/dist/src/ui/gui/{gui-client-loops.js → gui-client-roles.js} +43 -44
- package/dist/src/ui/gui/gui-client-settings.js +163 -5
- package/dist/src/ui/gui/gui-client-team.js +36 -465
- package/dist/src/ui/gui/gui-client.js +38 -26
- package/dist/src/ui/gui/gui-icons.js +0 -2
- package/dist/src/ui/gui/gui-page.js +10 -7
- package/dist/src/ui/gui/gui-styles.js +38 -34
- package/dist/src/ui/progress-data.js +9 -11
- package/dist/src/ui/{tick-progress-model.js → run-progress-model.js} +33 -32
- package/dist/src/ui/status-model.js +133 -125
- package/dist/src/ui/status-payload.js +364 -49
- package/dist/src/ui/status-render.js +109 -82
- package/dist/src/ui/team-alerts.js +728 -62
- package/dist/src/ui/tone.js +12 -6
- package/dist/src/ui/transcript-run.js +58 -0
- package/dist/src/ui/transcript-tail.js +28 -20
- package/dist/src/ui/transcript.js +63 -23
- package/dist/src/ui/tui/tui-app.js +0 -1
- package/dist/src/ui/tui/tui-backlog.js +53 -14
- package/dist/src/ui/tui/tui-editor-answer.js +27 -31
- package/dist/src/ui/tui/tui-editor-attach.js +4 -12
- package/dist/src/ui/tui/tui-editor-backends.js +127 -0
- package/dist/src/ui/tui/tui-editor-brief.js +14 -36
- package/dist/src/ui/tui/tui-editor-budget.js +36 -39
- package/dist/src/ui/tui/tui-editor-check.js +29 -0
- package/dist/src/ui/tui/tui-editor-checks.js +28 -0
- package/dist/src/ui/tui/tui-editor-claude-reserve.js +24 -0
- package/dist/src/ui/tui/tui-editor-concurrency.js +25 -0
- package/dist/src/ui/tui/tui-editor-config.js +99 -0
- package/dist/src/ui/tui/tui-editor-entry.js +39 -64
- package/dist/src/ui/tui/tui-editor-fallback.js +31 -0
- package/dist/src/ui/tui/tui-editor-instances.js +46 -0
- package/dist/src/ui/tui/tui-editor-instructions.js +117 -0
- package/dist/src/ui/tui/tui-editor-interval.js +29 -16
- package/dist/src/ui/tui/tui-editor-model.js +29 -0
- package/dist/src/ui/tui/tui-editor-notes.js +9 -29
- package/dist/src/ui/tui/tui-editor-notify.js +25 -0
- package/dist/src/ui/tui/tui-editor-pause.js +48 -59
- package/dist/src/ui/tui/tui-editor-prompt.js +11 -16
- package/dist/src/ui/tui/tui-editor-provider-caps.js +27 -0
- package/dist/src/ui/tui/tui-editor-queued.js +5 -9
- package/dist/src/ui/tui/tui-editor-quiet.js +95 -41
- package/dist/src/ui/tui/tui-editor-retire.js +5 -13
- package/dist/src/ui/tui/tui-editor-role-enabled.js +126 -0
- package/dist/src/ui/tui/tui-editor-role-key.js +71 -0
- package/dist/src/ui/tui/tui-editor-role-model.js +28 -0
- package/dist/src/ui/tui/tui-editor-search.js +45 -33
- package/dist/src/ui/tui/tui-editor-session.js +101 -34
- package/dist/src/ui/tui/tui-editor-slots.js +24 -0
- package/dist/src/ui/tui/tui-editors.js +44 -2
- package/dist/src/ui/tui/tui-frame.js +68 -11
- package/dist/src/ui/tui/tui-history.js +8 -9
- package/dist/src/ui/tui/tui-input.js +13 -11
- package/dist/src/ui/tui/tui-keymap.js +0 -1
- package/dist/src/ui/tui/tui-keys.js +33 -9
- package/dist/src/ui/tui/tui-pane-activity.js +35 -19
- package/dist/src/ui/tui/tui-pane-backlog.js +198 -126
- package/dist/src/ui/tui/tui-pane-brief.js +0 -1
- package/dist/src/ui/tui/tui-pane-ctx.js +2 -2
- package/dist/src/ui/tui/tui-pane-health.js +168 -8
- package/dist/src/ui/tui/tui-pane-history.js +446 -82
- package/dist/src/ui/tui/tui-pane-pending.js +539 -15
- package/dist/src/ui/tui/tui-pane-queued.js +78 -57
- package/dist/src/ui/tui/tui-pane-report.js +54 -16
- package/dist/src/ui/tui/tui-pane-session.js +34 -6
- package/dist/src/ui/tui/tui-pane-transcript.js +111 -37
- package/dist/src/ui/tui/tui-pane.js +91 -28
- package/dist/src/ui/tui/tui-panes.js +153 -34
- package/dist/src/ui/tui/tui.js +18 -15
- package/dist/src/verdict/fix-claim.js +129 -33
- package/dist/src/verdict/no-change.js +4 -4
- package/dist/src/verdict/refusal.js +40 -16
- package/dist/src/verdict/reply-contract.js +77 -31
- package/dist/src/version.js +0 -1
- package/package.json +2 -1
- package/dist/src/loop/leftover.js +0 -130
- package/dist/src/loop/revision.js +0 -70
- package/dist/src/roles/loop-ids.js +0 -77
- package/dist/src/tick/qa-coverage.js +0 -108
- package/dist/src/tick/stage-check.js +0 -419
- package/dist/src/tick/telemetry-digest.js +0 -25
- package/dist/src/tick/tick-detail-data.js +0 -108
- package/dist/src/tick/tick-detail.js +0 -163
- 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-
|
|
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-
|
|
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
|
|
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
|
|
37
|
-
// reach — so a miss names it as the valid range when at least one
|
|
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
|
|
50
|
-
* queue, the highest position numbering any
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
119
|
-
* numbering, cancel a queued prompt by position, or move
|
|
120
|
-
* queue. The dispatcher in cli.ts gates on a ready
|
|
121
|
-
*
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
138
|
-
// config error, not a built-ins-only guess about whether the
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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(
|
|
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-
|
|
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
|
|
175
|
-
// list itself shows two "N." lines), none is a miss. The output names the
|
|
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
|
-
//
|
|
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
|
|
196
|
-
// target
|
|
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
|
|
200
|
-
const guidance = imagePromptGuidance(root,
|
|
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
|
|
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
|
|
229
|
+
const targetRole = target;
|
|
217
230
|
let name;
|
|
218
231
|
let body;
|
|
219
232
|
try {
|
|
220
|
-
name = path.basename(queuedFileAtPosition(root,
|
|
221
|
-
body = readQueuedPromptBody(root,
|
|
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,
|
|
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,
|
|
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-
|
|
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
|
|
272
|
+
// clean; position 1 of the resolved role is "already first".
|
|
260
273
|
if (role === null) {
|
|
261
|
-
const
|
|
274
|
+
const targetRole = failQueueMiss(resolveListedQueue(root, listWideScope(root), parsed.position), parsed.position).role;
|
|
262
275
|
let outcome;
|
|
263
276
|
try {
|
|
264
|
-
outcome = movePromptToTop(root,
|
|
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,
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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.
|
package/dist/src/init/init.js
CHANGED
|
@@ -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 {
|
|
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 {
|
|
10
|
-
import {
|
|
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
|
-
*
|
|
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,
|
|
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
|
|
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${
|
|
99
|
-
:
|
|
100
|
-
if (combinedPrompt.length >
|
|
101
|
-
throw new Error(tooLongMessage(`the brief with the ${tpl.id} template's preamble`, combinedPrompt.length,
|
|
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 =
|
|
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() === "" &&
|
|
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
|
|
134
|
-
//
|
|
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
|
|
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
|
|
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 !==
|
|
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
|
|
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
|
|
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
|
|
250
|
-
// own
|
|
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, ...
|
|
273
|
+
await git(root, ...(await committerIdentArgs(root)), "commit", ...commitAuthorArgs(), "-m", message, "--", ...committable);
|
|
269
274
|
committed = true;
|
|
270
275
|
}
|
|
271
276
|
}
|