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.
- 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 +42 -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 +4 -3
- 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 +402 -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 +246 -55
- package/dist/src/backlog/backlog-cli-view.js +280 -0
- package/dist/src/backlog/backlog-cli.js +85 -274
- package/dist/src/backlog/backlog-commit.js +4 -5
- package/dist/src/backlog/backlog-eligibility.js +175 -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 +254 -0
- package/dist/src/backlog/backlog-release.js +117 -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-culprit.js +94 -0
- package/dist/src/baseline/main-guardrails.js +209 -0
- package/dist/src/baseline/main-red.js +139 -32
- package/dist/src/baseline/main-revert.js +133 -0
- package/dist/src/baseline/main-watch.js +184 -0
- package/dist/src/brief/brief-edit.js +9 -11
- package/dist/src/brief/brief.js +31 -51
- package/dist/src/budget/budget.js +30 -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 +1 -2
- 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 +118 -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 +150 -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 +308 -590
- package/dist/src/cli/cli-repo-gate.js +11 -1
- package/dist/src/cli/cli-role-command.js +420 -0
- package/dist/src/cli/cli-start-summary.js +160 -0
- package/dist/src/cli/{cli-run.js → cli-start.js} +165 -237
- package/dist/src/cli/completion.js +149 -22
- package/dist/src/cli/config-commands.js +41 -18
- package/dist/src/cli/follow-view.js +75 -0
- package/dist/src/cli/help.js +189 -73
- package/dist/src/cli/log-commands.js +148 -82
- package/dist/src/cli/question-commands.js +205 -10
- package/dist/src/cli.js +106 -43
- package/dist/src/collections.js +26 -10
- 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 +13 -1
- package/dist/src/config/config-fallbacks.js +93 -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 +141 -199
- package/dist/src/config/config-views.js +127 -53
- package/dist/src/config/config-write-core.js +135 -0
- package/dist/src/config/config-write.js +131 -264
- package/dist/src/config/config.js +48 -38
- package/dist/src/config/model-selector.js +34 -34
- package/dist/src/doctor/doctor-backlog-bugs.js +287 -0
- package/dist/src/doctor/doctor-backlog-plans.js +263 -0
- package/dist/src/doctor/doctor-backlog.js +100 -496
- package/dist/src/doctor/doctor-check-names.js +81 -0
- package/dist/src/doctor/doctor-checks.js +52 -579
- package/dist/src/doctor/doctor-claude-checks.js +145 -0
- package/dist/src/doctor/doctor-codex-checks.js +279 -0
- package/dist/src/doctor/doctor-contention-checks.js +380 -0
- package/dist/src/doctor/doctor-holds.js +82 -66
- package/dist/src/doctor/doctor-install.js +2 -1
- package/dist/src/doctor/doctor-model-checks.js +236 -402
- package/dist/src/doctor/doctor-notify.js +3 -4
- package/dist/src/doctor/doctor-opencode-checks.js +162 -0
- package/dist/src/doctor/doctor-orphans.js +182 -63
- package/dist/src/doctor/doctor-project-checks.js +303 -0
- package/dist/src/doctor/doctor.js +121 -77
- package/dist/src/doctor/image-probe.js +21 -8
- package/dist/src/doctor/outcome.js +3 -2
- package/dist/src/doctor/probe.js +7 -11
- package/dist/src/events/event-format.js +181 -86
- package/dist/src/events/event-labels.js +12 -5
- package/dist/src/events/event-legacy.js +39 -15
- package/dist/src/events/event-read.js +39 -39
- package/dist/src/events/event-tally.js +16 -0
- package/dist/src/events/event-window.js +60 -24
- 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 +56 -56
- package/dist/src/failure/failure-render.js +31 -23
- package/dist/src/failure/failure-state-change.js +17 -12
- package/dist/src/failure/time-spend.js +99 -35
- 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 +177 -84
- package/dist/src/gates/disk-gate.js +1 -1
- package/dist/src/gates/gate-polls.js +108 -58
- package/dist/src/gates/maintenance-quota.js +10 -13
- 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 +77 -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 +36 -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 +42 -18
- package/dist/src/gui/gui-backlog-endpoints.js +155 -0
- package/dist/src/gui/gui-command.js +8 -13
- package/dist/src/gui/gui-config-commands.js +94 -72
- package/dist/src/gui/gui-endpoint-commands.js +4 -3
- 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 +108 -13
- package/dist/src/gui/gui-prompt-commands.js +11 -14
- package/dist/src/gui/gui-report-endpoints.js +90 -0
- package/dist/src/gui/gui-server.js +11 -4
- 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 +147 -0
- package/dist/src/history/history-render.js +42 -22
- package/dist/src/history/history.js +193 -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 +25 -9
- 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 +81 -32
- package/dist/src/merge/merge-diff.js +5 -4
- package/dist/src/merge/merge-drain.js +150 -11
- 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 +90 -0
- package/dist/src/merge/merge-queue-render.js +49 -11
- package/dist/src/merge/merge-queue.js +31 -15
- package/dist/src/merge/merge-requeue.js +102 -0
- package/dist/src/merge/merge-slot.js +61 -16
- package/dist/src/merge/merge-stack.js +33 -11
- package/dist/src/merge/merge-to-main.js +86 -27
- package/dist/src/merge/merge-top.js +69 -0
- package/dist/src/merge/merge-verify.js +12 -10
- package/dist/src/notes-cli.js +30 -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 +320 -0
- package/dist/src/orchestrator/orchestrator-scheduling.js +68 -369
- package/dist/src/orchestrator/orchestrator.js +128 -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 +201 -0
- package/dist/src/orchestrator/scheduling-skip.js +185 -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 +116 -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 +18 -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 +121 -211
- package/dist/src/prompt/notes-backlog.js +181 -0
- package/dist/src/prompt/principles.js +1 -2
- package/dist/src/prompt/prompt-followup.js +19 -14
- package/dist/src/prompt/prompt.js +129 -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 +161 -77
- package/dist/src/report/report-fold.js +129 -52
- package/dist/src/report/report-main-red.js +55 -0
- package/dist/src/report/report-render.js +133 -64
- package/dist/src/report/report-types.js +1 -0
- package/dist/src/report/report.js +26 -9
- package/dist/src/request-timeouts.js +2 -2
- package/dist/src/review/known-flakes.js +100 -1
- package/dist/src/review/post-merge-review.js +106 -0
- package/dist/src/review/protected-edits.js +4 -5
- 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 +155 -360
- 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 +69 -12
- package/dist/src/roles/role-guidance.js +55 -20
- package/dist/src/roles/role-labels.js +1 -2
- package/dist/src/roles/role-render.js +67 -50
- package/dist/src/roles/role-view.js +59 -47
- package/dist/src/roles/roles.js +31 -31
- package/dist/src/roles/runner-ids.js +97 -0
- package/dist/src/run/acceptance-greps.js +119 -0
- package/dist/src/run/feedback-filings.js +51 -0
- package/dist/src/{tick → run}/qa-coverage.js +9 -27
- package/dist/src/{tick/tick-apply.js → run/run-apply.js} +95 -94
- package/dist/src/run/run-detail-data.js +179 -0
- package/dist/src/run/run-detail-render.js +98 -0
- package/dist/src/run/run-detail.js +168 -0
- package/dist/src/{tick/tick-finalize.js → run/run-finalize.js} +72 -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} +108 -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} +108 -50
- 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} +91 -56
- 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 +172 -0
- package/dist/src/run/stage-path-checks.js +730 -0
- package/dist/src/{loop → runner}/leftover.js +80 -25
- package/dist/src/{loop → runner}/model-fallback.js +12 -12
- package/dist/src/runner/revision.js +202 -0
- package/dist/src/{loop/loop-fallback.js → runner/runner-fallback.js} +8 -8
- package/dist/src/runner/runner-outcome.js +107 -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} +21 -15
- package/dist/src/{loop/loop-tick.js → runner/runner-run.js} +218 -240
- package/dist/src/{loop/loop-state.js → runner/runner-state-load.js} +61 -114
- package/dist/src/runner/runner-state.js +102 -0
- package/dist/src/{loop/loop.js → runner/runner.js} +123 -120
- package/dist/src/scheduling/backoff.js +38 -38
- 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 +43 -16
- 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 +28 -28
- package/dist/src/status/status-roles.js +39 -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 +5 -6
- 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 +63 -17
- package/dist/src/text/format.js +48 -11
- package/dist/src/text/markdown.js +8 -5
- package/dist/src/text/phrases.js +57 -23
- package/dist/src/text/suggest.js +1 -1
- package/dist/src/text/text.js +67 -13
- package/dist/src/ui/badges.js +65 -37
- package/dist/src/ui/gui/gui-client-backlog.js +292 -0
- package/dist/src/ui/gui/gui-client-boot.js +32 -26
- package/dist/src/ui/gui/gui-client-composer.js +8 -9
- package/dist/src/ui/gui/gui-client-drawer-config.js +212 -0
- package/dist/src/ui/gui/gui-client-drawer.js +126 -285
- package/dist/src/ui/gui/gui-client-feed.js +271 -0
- package/dist/src/ui/gui/gui-client-history.js +434 -67
- package/dist/src/ui/gui/gui-client-labels.js +1 -2
- 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 +55 -51
- package/dist/src/ui/gui/gui-client-pending.js +227 -23
- package/dist/src/ui/gui/gui-client-report-chart.js +464 -0
- package/dist/src/ui/gui/gui-client-report.js +87 -427
- package/dist/src/ui/gui/{gui-client-loops.js → gui-client-roles.js} +43 -44
- package/dist/src/ui/gui/gui-client-settings.js +56 -4
- package/dist/src/ui/gui/gui-client-team.js +25 -465
- package/dist/src/ui/gui/gui-client.js +27 -25
- package/dist/src/ui/gui/gui-icons.js +0 -2
- package/dist/src/ui/gui/gui-page.js +9 -7
- package/dist/src/ui/gui/gui-styles.js +34 -34
- package/dist/src/ui/progress-data.js +9 -11
- package/dist/src/ui/{tick-progress-model.js → run-progress-model.js} +31 -31
- package/dist/src/ui/status-model.js +133 -125
- package/dist/src/ui/status-payload.js +136 -49
- package/dist/src/ui/status-render.js +101 -82
- package/dist/src/ui/team-alerts.js +248 -57
- package/dist/src/ui/tone.js +5 -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 +51 -14
- package/dist/src/ui/tui/tui-editor-answer.js +27 -31
- package/dist/src/ui/tui/tui-editor-attach.js +3 -5
- package/dist/src/ui/tui/tui-editor-backends.js +132 -0
- package/dist/src/ui/tui/tui-editor-brief.js +10 -12
- package/dist/src/ui/tui/tui-editor-budget.js +35 -31
- package/dist/src/ui/tui/tui-editor-claude-reserve.js +24 -0
- package/dist/src/ui/tui/tui-editor-concurrency.js +24 -0
- package/dist/src/ui/tui/tui-editor-config.js +86 -0
- package/dist/src/ui/tui/tui-editor-entry.js +36 -47
- package/dist/src/ui/tui/tui-editor-fallback.js +31 -0
- package/dist/src/ui/tui/tui-editor-instances.js +37 -0
- package/dist/src/ui/tui/tui-editor-instructions.js +136 -0
- package/dist/src/ui/tui/tui-editor-interval.js +11 -9
- package/dist/src/ui/tui/tui-editor-model.js +29 -0
- package/dist/src/ui/tui/tui-editor-notes.js +3 -5
- 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 +99 -35
- package/dist/src/ui/tui/tui-editor-retire.js +4 -6
- package/dist/src/ui/tui/tui-editor-role-key.js +76 -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 +60 -34
- package/dist/src/ui/tui/tui-editor-slots.js +23 -0
- package/dist/src/ui/tui/tui-editors.js +35 -2
- package/dist/src/ui/tui/tui-frame.js +56 -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 +24 -9
- package/dist/src/ui/tui/tui-pane-activity.js +14 -11
- package/dist/src/ui/tui/tui-pane-backlog.js +171 -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 +58 -8
- package/dist/src/ui/tui/tui-pane-history.js +402 -82
- package/dist/src/ui/tui/tui-pane-pending.js +308 -13
- package/dist/src/ui/tui/tui-pane-queued.js +71 -55
- package/dist/src/ui/tui/tui-pane-report.js +46 -14
- package/dist/src/ui/tui/tui-pane-session.js +25 -6
- package/dist/src/ui/tui/tui-pane-transcript.js +111 -37
- package/dist/src/ui/tui/tui-pane.js +80 -23
- package/dist/src/ui/tui/tui-panes.js +100 -30
- package/dist/src/ui/tui/tui.js +14 -14
- package/dist/src/verdict/fix-claim.js +82 -32
- package/dist/src/verdict/no-change.js +4 -4
- package/dist/src/verdict/refusal.js +40 -16
- package/dist/src/verdict/reply-contract.js +42 -31
- package/dist/src/version.js +0 -1
- package/package.json +2 -1
- package/dist/src/loop/revision.js +0 -70
- package/dist/src/roles/loop-ids.js +0 -77
- 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,382 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The dashboard's GET data endpoint handlers (src/gui/gui-server.ts routes to them)
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* gui/gui-server.ts. The domain work itself lives one layer down (transcript.ts, backlog.ts,
|
|
10
|
-
* report-data.ts, src/failure/failure-data.ts, history-data.ts, tick-detail-data.ts, config.ts)
|
|
11
|
-
* — this module only adapts HTTP onto it.
|
|
2
|
+
* The dashboard's GET data endpoint handlers (src/gui/gui-server.ts routes to them), gathered
|
|
3
|
+
* from the per-family modules gui-history-endpoints (transcript, history, logs, run, role),
|
|
4
|
+
* gui-backlog-endpoints (backlog, backlog-match, diff), gui-report-endpoints (report,
|
|
5
|
+
* failures), and gui-admin-endpoints (prompt-text, doctor, config, retire-list, brief) — the
|
|
6
|
+
* read-only surface, split one family per file the way the POST side keeps a file per command
|
|
7
|
+
* domain. The per-family modules adapt HTTP onto the domain work a layer down; server
|
|
8
|
+
* lifecycle, routing, the static page, and the token gate stay in gui/gui-server.ts.
|
|
12
9
|
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
import { loopEnabled } from "../roles/loop-ids.js";
|
|
18
|
-
import { artifactPhrase, previewRetire } from "../operator/retire.js";
|
|
19
|
-
import { EDITABLE_CONFIG_KEYS } from "../config/config-editable-keys.js";
|
|
20
|
-
import { collectReport, collectReportSince } from "../report/report-data.js";
|
|
21
|
-
import { collectFailureReport, collectFailureReportSince } from "../failure/failure-data.js";
|
|
22
|
-
import { renderFailureMarkdown } from "../failure/failure-render.js";
|
|
23
|
-
import { renderSinceReportMarkdown } from "../report/report-render.js";
|
|
24
|
-
import { REPORT_SINCE_MAX_MS, reportSinceMsg } from "../events/event-window.js";
|
|
25
|
-
import { humanSeconds } from "../text/datetime.js";
|
|
26
|
-
import { collectTeamChanges, collectRoleChange } from "../change/change-data.js";
|
|
27
|
-
import { readTranscript } from "../ui/transcript.js";
|
|
28
|
-
import { HISTORY_DEFAULT_TICKS, HISTORY_MAX_TICKS, readTickRows, readTickRowsSince, summarizeTickRows, tickRowsMatchingGrep } from "../history/history-data.js";
|
|
29
|
-
import { LOGS_SINCE_MAX_MS, SPARSE_WINDOW_NOTE } from "../events/event-window.js";
|
|
30
|
-
import { eventRole, readEvents } from "../events/event-read.js";
|
|
31
|
-
import { eventFeedItem, eventMatchesGrep } from "../events/event-format.js";
|
|
32
|
-
import { readTickDetail } from "../tick/tick-detail-data.js";
|
|
33
|
-
import { renderTickDetail, tickDetailStat, tickNotFoundMessage } from "../tick/tick-detail.js";
|
|
34
|
-
import { rolePayload } from "../roles/role-view.js";
|
|
35
|
-
import { DIRECTOR_ROLE } from "../roles/roles.js";
|
|
36
|
-
import { readQueuedPromptBody } from "../inbox/inbox-edit.js";
|
|
37
|
-
import { intQuery, rejectBadQueueFileName, rejectBadRole, sinceMsQuery, windowDays } from "./gui-args.js";
|
|
38
|
-
import { readBrief } from "../brief/brief.js";
|
|
39
|
-
import { badRequest, okGone, sendJson } from "./http-body.js";
|
|
40
|
-
import { typoSuffix } from "../text/suggest.js";
|
|
41
|
-
/** Handle GET /api/transcript?role=<id>&n=N: rendered transcript lines for one loop's pi
|
|
42
|
-
* log (same rendering as `tumwater logs --role <id>`). Unknown/missing role or a bad n → 400.
|
|
43
|
-
* User-defined loops are valid targets too — the GUI marks them with an asterisk, so clicking
|
|
44
|
-
* one must open its transcript: ids validate through rejectBadRole. The 400 message lists
|
|
45
|
-
* exactly the ids accepted. The query arrives pre-parsed — the server parses the target
|
|
46
|
-
* once (gui/gui-server.ts's parseRequestTarget) and threads it down, so the parse idiom lives
|
|
47
|
-
* in one place and cannot disagree with the routing or the token gate about what the URL said. */
|
|
48
|
-
export function handleTranscript(q, res, root) {
|
|
49
|
-
const role = q.get("role");
|
|
50
|
-
if (rejectBadRole(root, res, role))
|
|
51
|
-
return;
|
|
52
|
-
const n = intQuery(q, res, "n", "positive", 50);
|
|
53
|
-
if (n === null)
|
|
54
|
-
return;
|
|
55
|
-
sendJson(res, 200, { lines: readTranscript(root, role, n) });
|
|
56
|
-
}
|
|
57
|
-
/** The /api/backlog `file` values and the open-section reader each addresses — one table so
|
|
58
|
-
* the on-demand dispatch and the "valid values" list in both 400s cannot drift apart. A Map,
|
|
59
|
-
* not an object literal, so no `file` spelling (`constructor`, `toString`, `__proto__`) can
|
|
60
|
-
* reach an inherited member through the lookup. Insertion order is the list order. */
|
|
61
|
-
const BACKLOG_FILE_READERS = new Map([
|
|
62
|
-
["plans", plannedPlanEntries],
|
|
63
|
-
["bugs", openBugEntries],
|
|
64
|
-
["questions", openQuestionEntries],
|
|
65
|
-
["done", donePlanEntries],
|
|
66
|
-
["fixed", fixedBugEntries],
|
|
67
|
-
["answered", (root) => answeredQuestionEntries(root).reverse()],
|
|
68
|
-
]);
|
|
69
|
-
const BACKLOG_FILE_NAMES = [...BACKLOG_FILE_READERS.keys()];
|
|
70
|
-
const BACKLOG_FILE_VALUES = BACKLOG_FILE_NAMES.join(", ");
|
|
71
|
-
/** Handle GET /api/backlog?file=<plans|bugs|questions|done|fixed|answered>&index=N: one
|
|
72
|
-
* backlog entry's full text ({title, body}), fetched on demand so multi-KB bodies (long repros,
|
|
73
|
-
* whole plans) never ride the 1-second /api/status poll. index addresses the Nth entry of that
|
|
74
|
-
* file's section in the same order statusPayload lists its titles — plans: PLANS.md ## Planned,
|
|
75
|
-
* bugs: BUGS.md ## Open, questions: QUESTIONS.md ## Open, done: PLANS.md ## Done and fixed:
|
|
76
|
-
* BUGS.md ## Fixed (both newest first, the order of the dashboard's recent rows) and answered:
|
|
77
|
-
* QUESTIONS.md ## Answered, newest first (the file is appended to, so the reverse of file
|
|
78
|
-
* order, unlike done/fixed) — zero-based.
|
|
79
|
-
* Unknown/missing file, missing or bad index, and out-of-range index
|
|
80
|
-
* → 400 JSON error via sendJson.
|
|
81
|
-
* The query arrives pre-parsed (gui/gui-server.ts's parseRequestTarget), like every
|
|
82
|
-
* GET-data handler here. */
|
|
83
|
-
export function handleBacklog(q, res, root) {
|
|
84
|
-
const file = q.get("file");
|
|
85
|
-
if (file === null) {
|
|
86
|
-
badRequest(res, `file required (valid values: ${BACKLOG_FILE_VALUES})`);
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
const reader = BACKLOG_FILE_READERS.get(file);
|
|
90
|
-
if (!reader) {
|
|
91
|
-
badRequest(res, `unknown file ${JSON.stringify(file)} (valid values: ${BACKLOG_FILE_VALUES})${typoSuffix(file, BACKLOG_FILE_NAMES)}`);
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
const entries = reader(root);
|
|
95
|
-
const index = intQuery(q, res, "index", "non-negative");
|
|
96
|
-
if (index === null)
|
|
97
|
-
return;
|
|
98
|
-
const entry = entries[index];
|
|
99
|
-
if (!entry) {
|
|
100
|
-
badRequest(res, `index ${index} out of range (${file} has ${entries.length} entries)`);
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
sendJson(res, 200, { title: entry.title, body: entry.body, refused: refusalNote(entry.body) !== null, waiting: waitingNote(entry.body) !== null });
|
|
104
|
-
}
|
|
105
|
-
/** Handle GET /api/diff[?role=<id>]: the unmerged-change view the CLI prints from the same
|
|
106
|
-
* collectors — no `role` → collectTeamChanges (exactly `tumwater diff --json`'s document:
|
|
107
|
-
* `{ mainBranch, roles }`, no per-role patch halves); `?role=<id>` → collectRoleChange (the
|
|
108
|
-
* full per-role view with `diff`/`uncommittedDiff`, `tumwater diff --role <id> --json`). The
|
|
109
|
-
* role is a target, not a filter, so it validates through rejectBadRole exactly like
|
|
110
|
-
* /api/transcript and /api/run (unknown/missing → 400 naming the valid ids) — the same ids
|
|
111
|
-
* `parseRoleFlag` accepts on the CLI, including disabled and user-defined loops. The
|
|
112
|
-
* collectors degrade absent worktrees and missing base branches to their `state` instead of
|
|
113
|
-
* throwing, so a fresh or half-built repo still answers 200. Reads git plumbing directly, so
|
|
114
|
-
* it works whether or not the team is running. */
|
|
115
|
-
export async function handleDiff(q, res, root) {
|
|
116
|
-
const role = q.get("role");
|
|
117
|
-
if (role === null) {
|
|
118
|
-
sendJson(res, 200, await collectTeamChanges(root));
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
if (rejectBadRole(root, res, role))
|
|
122
|
-
return;
|
|
123
|
-
sendJson(res, 200, await collectRoleChange(root, role));
|
|
124
|
-
}
|
|
125
|
-
/** Handle GET /api/report?days=N[&role=<id>] or /api/report?sinceMs=<ms>[&role=<id>]: the
|
|
126
|
-
* usage report — the day series (collectReport's ReportData) as JSON the dashboard's report tab
|
|
127
|
-
* charts, or, when sinceMs is set, the trailing-window totals rendered through
|
|
128
|
-
* renderSinceReportMarkdown as { markdown } (the CLI's `report --since` shape), bounded by the
|
|
129
|
-
* reportSinceMs helper. The day window follows windowDays. An absent role is the team-wide
|
|
130
|
-
* report exactly as before; a present one is a filter, validated through rejectBadRole (unknown,
|
|
131
|
-
* including the empty string → 400 naming the valid ids) and handed to the collector's role
|
|
132
|
-
* scope. Reads files directly, so it works whether or not the team is running. */
|
|
133
|
-
export function handleReport(q, res, root) {
|
|
134
|
-
const role = q.get("role");
|
|
135
|
-
if (role !== null && rejectBadRole(root, res, role))
|
|
136
|
-
return;
|
|
137
|
-
if (q.get("sinceMs") !== null) {
|
|
138
|
-
const sinceMs = reportSinceMs(q, res);
|
|
139
|
-
if (sinceMs === null)
|
|
140
|
-
return;
|
|
141
|
-
sendJson(res, 200, { markdown: renderSinceReportMarkdown(collectReportSince(root, sinceMs, role ?? undefined)) });
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
sendJson(res, 200, collectReport(root, windowDays(q), role ?? undefined));
|
|
145
|
-
}
|
|
146
|
-
/** Parse and bound the report endpoints' `?sinceMs=` trailing-window length: a simultaneous
|
|
147
|
-
* `?days=` is the CLI's rival shape (whole local days vs a trailing window) → 400 naming both;
|
|
148
|
-
* otherwise the length rides sinceMsQuery (gui-args.ts) — positive-integer parse, then a 400
|
|
149
|
-
* naming the cap rather than a silent clamp (an explicit window typo must not shrink to some
|
|
150
|
-
* other window) — with the "must be between 1 and …" wording. Returns the parsed length, or
|
|
151
|
-
* null once the 400 is sent. */
|
|
152
|
-
function reportSinceMs(q, res) {
|
|
153
|
-
if (q.get("days") !== null) {
|
|
154
|
-
badRequest(res, "days and sinceMs are mutually exclusive; days counts whole local days, sinceMs totals a trailing window");
|
|
155
|
-
return null;
|
|
156
|
-
}
|
|
157
|
-
return sinceMsQuery(q, res, REPORT_SINCE_MAX_MS, reportSinceMsg);
|
|
158
|
-
}
|
|
159
|
-
/** Handle GET /api/failures?days=N[&role=<id>] or /api/failures?sinceMs=<ms>[&role=<id>]: the
|
|
160
|
-
* same bounded Markdown failure digest the telemetry loop feeds on and `tumwater report
|
|
161
|
-
* --failures` prints, as JSON ({ markdown }) — the dashboard's failures tab renders it; with
|
|
162
|
-
* sinceMs it is the trailing-window digest (collectFailureReportSince) through the same render,
|
|
163
|
-
* the CLI's `report --failures --since` shape. The days window follows windowDays (so it can
|
|
164
|
-
* never drift from /api/report's). role rides the same absent-team / present-filter rule as
|
|
165
|
-
* handleReport: absent → the team digest, present → validated and scoped. Reads files
|
|
166
|
-
* directly, so it works with no team running. */
|
|
167
|
-
export function handleFailures(q, res, root) {
|
|
168
|
-
const role = q.get("role");
|
|
169
|
-
if (role !== null && rejectBadRole(root, res, role))
|
|
170
|
-
return;
|
|
171
|
-
if (q.get("sinceMs") !== null) {
|
|
172
|
-
const sinceMs = reportSinceMs(q, res);
|
|
173
|
-
if (sinceMs === null)
|
|
174
|
-
return;
|
|
175
|
-
sendJson(res, 200, { markdown: renderFailureMarkdown(collectFailureReportSince(root, sinceMs, role ?? undefined)) });
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
sendJson(res, 200, { markdown: renderFailureMarkdown(collectFailureReport(root, windowDays(q), role ?? undefined)) });
|
|
179
|
-
}
|
|
180
|
-
/** Handle GET /api/history?role=<id>&n=N[&grep=<text>] — or ?role=<id>&sinceMs=<ms> for a
|
|
181
|
-
* trailing window: the per-tick rows the CLI's `tumwater history` derives from the event log,
|
|
182
|
-
* the dashboard's history tab renders them. The count shape ({ rows }) rides readTickRows (the
|
|
183
|
-
* shared scan, window growth included, so a role-filtered ask on a busy team cannot silently
|
|
184
|
-
* come back short); n is optional: absent → HISTORY_DEFAULT_TICKS; present but not a plain
|
|
185
|
-
* non-negative integer → 400 (the handleBacklog index discipline — an explicit count that does
|
|
186
|
-
* not parse is a client error, unlike the report's degrade-to-default typo rule); then clamped
|
|
187
|
-
* to [1, HISTORY_MAX_TICKS] — the GUI clamps where the CLI fails fast, its established
|
|
188
|
-
* convention. grep is optional: absent or empty filters nothing; present → the rows are
|
|
189
|
-
* filtered through history-data.ts's shared tickRowsMatchingGrep rule, the same
|
|
190
|
-
* case-insensitive haystack rule `tumwater history --grep` uses (shared, not
|
|
191
|
-
* re-implemented), after the n/role scan so n bounds the scanned window and not the
|
|
192
|
-
* printed rows — the CLI's rule. The window shape ({ rows, covered, note }) rides
|
|
193
|
-
* readTickRowsSince, the collector behind `history --since`,
|
|
194
|
-
* so the browser and the CLI cannot disagree about the window's rows; a simultaneous n is a
|
|
195
|
-
* rival shape → 400 (the CLI's count-vs-window rule), sinceMs parses and is capped through
|
|
196
|
-
* sinceMsQuery (gui-args.ts, LOGS_SINCE_MAX_MS — the shared `history --since` cap), and note is
|
|
197
|
-
* SPARSE_WINDOW_NOTE when covered is false, else "". role is optional: absent or empty → all
|
|
198
|
-
* loops; present → passed straight through as the role filter with no config validation (a
|
|
199
|
-
* filter is not a target — an id with no ticks legitimately yields zero rows), so the endpoint
|
|
200
|
-
* reads nothing but files, like /api/report and /api/failures. Reads files directly, so it
|
|
201
|
-
* present → passed straight through as the role filter with no config validation (a
|
|
202
|
-
* filter is not a target — an id with no ticks legitimately yields zero rows), so the endpoint
|
|
203
|
-
* reads nothing but files, like /api/report and /api/failures. summary is optional: absent /
|
|
204
|
-
* empty adds nothing (the response is byte-for-byte today's); present (any value — the flag's
|
|
205
|
-
* mere presence, a bad value ignored like a missing one) adds an additive `summary` folding the
|
|
206
|
-
* exact served rows through the shared summarizeTickRows collector, the same tally
|
|
207
|
-
* `tumwater history --summary` prints, so the dashboard Summary and the table cannot disagree
|
|
208
|
-
* about what a tick was. Reads files directly, so it works whether or not the team is running. */
|
|
209
|
-
export function handleHistory(q, res, root) {
|
|
210
|
-
const role = q.get("role") || null; // "" and absent both read all loops
|
|
211
|
-
const grep = q.get("grep") || null; // "" and absent both filter nothing
|
|
212
|
-
const summary = q.get("summary") !== null; // the flag's presence, whatever its value
|
|
213
|
-
if (q.get("sinceMs") !== null) {
|
|
214
|
-
if (q.get("n") !== null) {
|
|
215
|
-
badRequest(res, "sinceMs cannot be combined with n (a count and a window are rival shapes)");
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
const sinceMs = sinceMsQuery(q, res, LOGS_SINCE_MAX_MS, (ms) => `sinceMs is capped at ${humanSeconds(LOGS_SINCE_MAX_MS / 1000)} (got ${ms})`);
|
|
219
|
-
if (sinceMs === null)
|
|
220
|
-
return;
|
|
221
|
-
const { rows, covered } = readTickRowsSince(root, sinceMs, role);
|
|
222
|
-
const matched = grep === null ? rows : tickRowsMatchingGrep(rows, grep);
|
|
223
|
-
sendJson(res, 200, { rows: matched, covered, note: covered ? "" : SPARSE_WINDOW_NOTE, ...(summary ? { summary: summarizeTickRows(matched) } : {}) });
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
const parsed = intQuery(q, res, "n", "non-negative", HISTORY_DEFAULT_TICKS);
|
|
227
|
-
if (parsed === null)
|
|
228
|
-
return;
|
|
229
|
-
const n = Math.min(HISTORY_MAX_TICKS, Math.max(1, parsed));
|
|
230
|
-
let rows = readTickRows(root, n, role);
|
|
231
|
-
if (grep !== null)
|
|
232
|
-
rows = tickRowsMatchingGrep(rows, grep);
|
|
233
|
-
sendJson(res, 200, summary ? { rows, summary: summarizeTickRows(rows) } : { rows });
|
|
234
|
-
}
|
|
235
|
-
/** How many of the log's newest events `GET /api/logs` scans by default, and the most it will
|
|
236
|
-
* scan however large `n` asks: the CLI's `-n` shape (n bounds the scanned tail, not the printed
|
|
237
|
-
* rows), sized so the dashboard's search can reach well past the RECENT_EVENTS window the
|
|
238
|
-
* status poll carries. */
|
|
239
|
-
const LOGS_SEARCH_EVENTS = 500;
|
|
240
|
-
const LOGS_SEARCH_MAX_EVENTS = 5000;
|
|
241
|
-
/** Handle GET /api/logs?n=N[&grep=<text>][&role=<id>]: a slice of the raw event log scanned
|
|
242
|
-
* from its newest end, filtered with the CLI's own rules and shaped as the activity feed's
|
|
243
|
-
* items. `n` is optional (absent → LOGS_SEARCH_EVENTS) and clamps to [1, LOGS_SEARCH_MAX_EVENTS]
|
|
244
|
-
* after intQuery's non-negative parse (an explicit count that does not parse is a 400, the
|
|
245
|
-
* handleHistory discipline); it bounds the scanned tail, so a matching event older than the
|
|
246
|
-
* last n events is absent — the same scan-then-filter rule `tumwater logs -n N --grep` applies.
|
|
247
|
-
* grep (absent or empty filters nothing) matches through eventMatchesGrep, and role is a
|
|
248
|
-
* filter, not a target: an id with no events legitimately yields an empty list, so it does not
|
|
249
|
-
* validate through rejectBadRole (the handleHistory rule). The matched events answer
|
|
250
|
-
* oldest-first as eventFeedItem objects — the same shape the status payload's eventItems carry.
|
|
251
|
-
* Reads the tail through readEvents' stat-keyed cache (no covered byte end, unlike the CLI's
|
|
252
|
-
* follow), so a dashboard search that repeats on an unchanged log — the debounced filter fires
|
|
253
|
-
* one per filter change — costs one stat instead of re-reading and re-parsing the scanned tail.
|
|
254
|
-
* Reads files directly, so it works whether or not the team is running. */
|
|
255
|
-
export function handleLogs(q, res, root) {
|
|
256
|
-
const parsed = intQuery(q, res, "n", "non-negative", LOGS_SEARCH_EVENTS);
|
|
257
|
-
if (parsed === null)
|
|
258
|
-
return;
|
|
259
|
-
const n = Math.min(LOGS_SEARCH_MAX_EVENTS, Math.max(1, parsed));
|
|
260
|
-
const role = q.get("role") || null; // "" and absent both read all loops
|
|
261
|
-
const grep = q.get("grep") || null; // "" and absent both filter nothing
|
|
262
|
-
const needle = grep === null ? null : grep.toLowerCase();
|
|
263
|
-
const events = readEvents(root, n);
|
|
264
|
-
const matched = events.filter((e) => (role === null || eventRole(e) === role) && (needle === null || eventMatchesGrep(e, needle)));
|
|
265
|
-
sendJson(res, 200, { events: matched.map(eventFeedItem) });
|
|
266
|
-
}
|
|
267
|
-
/** Handle GET /api/run?role=<id>&run=N: one tick's full event trail — readTickDetail's
|
|
268
|
-
* TickDetail, the collector `tumwater tick <role> <n>` prints from, plus `text`: the same
|
|
269
|
-
* payload through renderTickDetail, pre-rendered server-side like /api/transcript's lines so
|
|
270
|
-
* the browser shows the CLI's exact rendering instead of re-implementing the event formatting.
|
|
271
|
-
* Serving the shared collector is the point: the CLI and this endpoint cannot drift on what one
|
|
272
|
-
* tick's block contains. The role is a target here, not a filter, so it validates through
|
|
273
|
-
* rejectBadRole (unknown/missing → 400 naming the valid ids); the tick is an explicit count
|
|
274
|
-
* through intQuery (missing or not a positive integer → 400); and a tick the scanned window
|
|
275
|
-
* does not hold — never ran, or rotation ate it — answers 404 with tickNotFoundMessage, the
|
|
276
|
-
* CLI's exact not-found wording,
|
|
277
|
-
* never a crash. Reads files directly, so it works whether or not the team is running. */
|
|
278
|
-
export async function handleTick(q, res, root) {
|
|
279
|
-
const role = q.get("role");
|
|
280
|
-
if (rejectBadRole(root, res, role))
|
|
281
|
-
return;
|
|
282
|
-
const tick = intQuery(q, res, "run", "positive");
|
|
283
|
-
if (tick === null)
|
|
284
|
-
return;
|
|
285
|
-
const detail = readTickDetail(root, role, tick);
|
|
286
|
-
if (detail === null) {
|
|
287
|
-
sendJson(res, 404, { error: tickNotFoundMessage(role, tick) });
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
sendJson(res, 200, { ...detail, text: renderTickDetail(detail), stat: await tickDetailStat(root, detail) });
|
|
291
|
-
}
|
|
292
|
-
/** Handle GET /api/role?role=<id>: one loop's inspection payload — rolePayload's object,
|
|
293
|
-
* the same collection behind `tumwater role <id>` — so the drawer's Notebook and Next tick
|
|
294
|
-
* prompt sections render what the CLI shows without re-implementing the assembly. The role
|
|
295
|
-
* is a target here, so it validates through rejectBadRole exactly like /api/transcript and
|
|
296
|
-
* /api/run (unknown/missing → the shared 400 naming the valid ids and correcting typos);
|
|
297
|
-
* rolePayload is only reached with an id rejectBadRole accepted, so its own unknown-role
|
|
298
|
-
* throw never fires. Reads files directly, so it works whether or not the team is running. */
|
|
299
|
-
export function handleRole(q, res, root) {
|
|
300
|
-
const role = q.get("role");
|
|
301
|
-
if (rejectBadRole(root, res, role))
|
|
302
|
-
return;
|
|
303
|
-
sendJson(res, 200, rolePayload(root, role));
|
|
304
|
-
}
|
|
305
|
-
/** Handle GET /api/prompt-text?file=<name>[&role=<id>]: one queued prompt's editable body —
|
|
306
|
-
* the text an edit replaces, for the dashboard's per-row edit affordance (PLANS.md
|
|
307
|
-
* 2026-10-11). The prompt is addressed by its queue-file basename (the /api/prompt-cancel
|
|
308
|
-
* address), so a stale poll's snapshot can never read the wrong entry; the not-before deferral
|
|
309
|
-
* marker and the `[image attached: …]` reference block are stripped, both plumbing the edit
|
|
310
|
-
* carries over unchanged. The role is optional (absent reads the director's queue) and
|
|
311
|
-
* validates exactly like /api/transcript; a file name failing the shared
|
|
312
|
-
* rejectBadQueueFileName guard is a user-input error answered 400 with nothing read. A
|
|
313
|
-
* vanished file answers 200 { ok: true, status: "gone" } — the loop already dequeued
|
|
314
|
-
* it, which is data for the UI, not a server fault. Reads one file directly, so it works
|
|
315
|
-
* whether or not the team is running. */
|
|
316
|
-
export function handlePromptText(q, res, root) {
|
|
317
|
-
const roleParam = q.get("role");
|
|
318
|
-
if (roleParam !== null && rejectBadRole(root, res, roleParam))
|
|
319
|
-
return;
|
|
320
|
-
const role = roleParam ?? DIRECTOR_ROLE;
|
|
321
|
-
const file = q.get("file");
|
|
322
|
-
if (rejectBadQueueFileName(res, file))
|
|
323
|
-
return;
|
|
324
|
-
const text = readQueuedPromptBody(root, role, file);
|
|
325
|
-
if (text === null) {
|
|
326
|
-
okGone(res);
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
sendJson(res, 200, { ok: true, text });
|
|
330
|
-
}
|
|
331
|
-
/** Handle GET /api/doctor: the same DoctorReport `tumwater doctor --json` prints, so the
|
|
332
|
-
* Settings view's Health check card and the CLI cannot disagree. Read-only — the checks write
|
|
333
|
-
* nothing (runDoctor's own contract) — and it works whether or not the team is running. `run`
|
|
334
|
-
* exists so tests can skip the host probes; a throw reaches the server's outer 500. */
|
|
335
|
-
export async function handleDoctor(res, root, run = (r) => runDoctor(r, undefined, undefined, "never")) {
|
|
336
|
-
sendJson(res, 200, await run(root));
|
|
337
|
-
}
|
|
338
|
-
/** Handle GET /api/config: the Settings view's resolved values for exactly
|
|
339
|
-
* EDITABLE_CONFIG_KEYS — read through the same load path `tumwater config get` uses
|
|
340
|
-
* (loadConfigSafe, defaults merged in), so the page shows what the team would actually
|
|
341
|
-
* load. A broken or invalid tumwater.json answers 500 with validateConfig's message instead
|
|
342
|
-
* of serving a half-empty panel. */
|
|
343
|
-
export function handleConfig(res, root) {
|
|
344
|
-
const { config, error } = loadConfigSafe(root);
|
|
345
|
-
if (config === undefined) {
|
|
346
|
-
sendJson(res, 500, { error });
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
const record = config;
|
|
350
|
-
const out = {};
|
|
351
|
-
for (const key of EDITABLE_CONFIG_KEYS)
|
|
352
|
-
out[key] = record[key] ?? null;
|
|
353
|
-
sendJson(res, 200, out);
|
|
354
|
-
}
|
|
355
|
-
/** Handle GET /api/retire: the Settings view's "Disabled roles" card — every configured,
|
|
356
|
-
* disabled, non-director role, with what `tumwater retire` would remove (previewRetire, so the
|
|
357
|
-
* rails and wording are the CLI's) and the rails' objections. `wouldRemove` is empty when there
|
|
358
|
-
* is nothing to retire (a role disabled with a clean checkout), so the card can also re-enable
|
|
359
|
-
* a role via POST /api/role-enable. Read-only (previewRetire's contract) and works whether or
|
|
360
|
-
* not the team runs; a throw reaches the server's outer 500, an unreadable tumwater.json
|
|
361
|
-
* answers 500 like /api/config. */
|
|
362
|
-
export async function handleRetireList(res, root) {
|
|
363
|
-
const { config, error } = loadConfigSafe(root);
|
|
364
|
-
if (config === undefined) {
|
|
365
|
-
sendJson(res, 500, { error });
|
|
366
|
-
return;
|
|
367
|
-
}
|
|
368
|
-
const roles = [];
|
|
369
|
-
for (const role of knownRoleIds(config).filter((id) => id !== DIRECTOR_ROLE && !loopEnabled(config, id))) {
|
|
370
|
-
const { wouldRemove, objections } = await previewRetire(root, role);
|
|
371
|
-
roles.push({ role, wouldRemove, phrases: wouldRemove.map((a) => artifactPhrase(a, role)), objections });
|
|
372
|
-
}
|
|
373
|
-
sendJson(res, 200, { roles });
|
|
374
|
-
}
|
|
375
|
-
/** Handle GET /api/brief: the brief file's name and the current initial prompt (what
|
|
376
|
-
* `tumwater brief --json` prints), or 404 when no brief carries a prompt block. */
|
|
377
|
-
export function handleBrief(res, root) {
|
|
378
|
-
const brief = readBrief(root);
|
|
379
|
-
if (brief === null)
|
|
380
|
-
return sendJson(res, 404, { error: "no project brief with a prompt block found — run `tumwater init` first" });
|
|
381
|
-
sendJson(res, 200, { file: brief.file, prompt: brief.prompt });
|
|
382
|
-
}
|
|
10
|
+
export { handleTranscript, handleHistory, handleLogs, handleRun, handleRole } from "./gui-history-endpoints.js";
|
|
11
|
+
export { handleBacklog, handleBacklogMatch, handleDiff } from "./gui-backlog-endpoints.js";
|
|
12
|
+
export { handleReport, handleFailures } from "./gui-report-endpoints.js";
|
|
13
|
+
export { handlePromptText, handleDoctor, handleConfig, handleRetireList, handleBrief } from "./gui-admin-endpoints.js";
|