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,10 +1,11 @@
|
|
|
1
|
-
// vocabulary: plain
|
|
2
1
|
import { bodyStructureProblem, fileEntryWithBody, OPERATOR_FILE_KINDS, splitEntryFile, tryEditEntry } from "../../backlog/backlog-write.js";
|
|
2
|
+
import { backlogHolds, stillHeldNote } from "../../backlog/backlog-eligibility.js";
|
|
3
3
|
import { stripEntryStamp } from "../../backlog/backlog-structure.js";
|
|
4
|
-
import { readTextIfExists } from "../../files/files.js";
|
|
5
4
|
import { submitRolePromptAndWake } from "../../operator/operator-intent.js";
|
|
6
5
|
import { promptLengthProblem } from "../../inbox/inbox-submit.js";
|
|
7
6
|
import { collapseWhitespace } from "../../text/text.js";
|
|
7
|
+
import { selectedEditableEntry } from "./tui-pane-backlog.js";
|
|
8
|
+
import { readEditorPath } from "./tui-editor-session.js";
|
|
8
9
|
/** The backlog pane's title editor (Ctrl+N new entry, Ctrl+E on a selected plan or bug) on the
|
|
9
10
|
* shared prompt line. It owns its target (`entryEditFor`), its plan↔bug flip, its saved-draft
|
|
10
11
|
* pair, its exit helper, and the fileEntryWithBody / tryEditEntry / commit arms, plus the
|
|
@@ -12,8 +13,7 @@ import { collapseWhitespace } from "../../text/text.js";
|
|
|
12
13
|
* the session's held registry. */
|
|
13
14
|
export function createEntryEditor(session) {
|
|
14
15
|
let entryEditFor = null;
|
|
15
|
-
let
|
|
16
|
-
let entryEditSavedCursor = 0;
|
|
16
|
+
let restoreDraft = () => { };
|
|
17
17
|
// The triple state of the entry editor's prompt line: false (the title field), true (the
|
|
18
18
|
// file-path field whose contents replace the whole entry), and the draft the toggle parks
|
|
19
19
|
// when it hands the line to a different field.
|
|
@@ -29,7 +29,7 @@ export function createEntryEditor(session) {
|
|
|
29
29
|
entryEditFor = null;
|
|
30
30
|
entryBodyFile = false;
|
|
31
31
|
session.held.entry = null;
|
|
32
|
-
|
|
32
|
+
restoreDraft();
|
|
33
33
|
};
|
|
34
34
|
const handleKey = (_str, key, ctx) => {
|
|
35
35
|
if (key.ctrl && key.name === "n") {
|
|
@@ -43,13 +43,11 @@ export function createEntryEditor(session) {
|
|
|
43
43
|
return true;
|
|
44
44
|
}
|
|
45
45
|
// A Ctrl+N on any other state (not the Backlog view's new-entry slot, or an editor
|
|
46
|
-
// already holding the line) is the
|
|
46
|
+
// already holding the line) is the notes/change/trail cycle, a pane arm the outer
|
|
47
47
|
// dispatch handles — this factory returns false so it runs.
|
|
48
48
|
if (ctx.view === ctx.roleIds.length + 1 &&
|
|
49
|
-
!session.
|
|
50
|
-
|
|
51
|
-
!session.held.answer && !entryEditFor && !session.held.retire) {
|
|
52
|
-
session.settleDraft((t) => (entryEditSavedInput = t), (n) => (entryEditSavedCursor = n));
|
|
49
|
+
!session.anyEditorHeld()) {
|
|
50
|
+
restoreDraft = session.takeDraft();
|
|
53
51
|
entryEditFor = { kind: "plan", position: null, title: "" };
|
|
54
52
|
session.held.entry = entryEditFor;
|
|
55
53
|
session.input = "";
|
|
@@ -80,33 +78,20 @@ export function createEntryEditor(session) {
|
|
|
80
78
|
return true;
|
|
81
79
|
}
|
|
82
80
|
if (key.ctrl && key.name === "e" &&
|
|
83
|
-
!session.
|
|
84
|
-
!session.held.pause && !session.held.queued &&
|
|
85
|
-
!session.held.answer && !entryEditFor && !session.held.retire &&
|
|
81
|
+
!session.anyEditorHeld() &&
|
|
86
82
|
ctx.view === ctx.roleIds.length + 1 && ctx.selectedEntry !== null) {
|
|
87
83
|
// Ctrl+E opens the title editor on the selected plan or bug (mnemonic: **E**dit); Enter
|
|
88
84
|
// saves through tryEditEntry, the non-exiting core the dashboard drawer shares, keeping
|
|
89
85
|
// the body and stamp. The prompt line is single-line, so only the title is editable here.
|
|
90
86
|
try {
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
else if (picked.label === "done" || picked.label === "fixed" || picked.label === "answered") {
|
|
98
|
-
session.flashMessage("a done plan, fixed bug or answered question is read-only");
|
|
99
|
-
}
|
|
100
|
-
else if (picked.label === "question") {
|
|
101
|
-
session.flashMessage("only a plan or open bug can be edited here");
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
const kind = picked.label === "plan" ? "plan" : "bug";
|
|
105
|
-
const position = kind === "plan" ? sel + 1 : sel - entries.filter((e) => e.label === "plan").length + 1;
|
|
106
|
-
session.settleDraft((t) => (entryEditSavedInput = t), (n) => (entryEditSavedCursor = n));
|
|
107
|
-
entryEditFor = { kind, position, title: picked.title };
|
|
87
|
+
const selected = selectedEditableEntry(session, ctx.selectedEntry, "edited");
|
|
88
|
+
if (selected !== null) {
|
|
89
|
+
const kind = selected.kind;
|
|
90
|
+
const position = selected.position;
|
|
91
|
+
restoreDraft = session.takeDraft();
|
|
92
|
+
entryEditFor = { kind, position, title: selected.picked.title };
|
|
108
93
|
session.held.entry = entryEditFor;
|
|
109
|
-
session.input = stripEntryStamp(picked.title);
|
|
94
|
+
session.input = stripEntryStamp(selected.picked.title);
|
|
110
95
|
session.cursor = session.input.length;
|
|
111
96
|
session.flashMessage(`editing ${kind} #${position} — Enter to save, Esc to cancel`);
|
|
112
97
|
}
|
|
@@ -148,7 +133,12 @@ export function createEntryEditor(session) {
|
|
|
148
133
|
exitEntryEdit();
|
|
149
134
|
if (outcome.status === "edited") {
|
|
150
135
|
session.commitBacklogEdit(outcome.file, `Edit ${target.kind}: ${outcome.title}`);
|
|
151
|
-
|
|
136
|
+
const holds = backlogHolds(session.root);
|
|
137
|
+
const hold = (target.kind === "bug" ? holds.bugs : holds.plans)[position - 1] ?? null;
|
|
138
|
+
session.flashMessage(`edited ${target.kind} #${target.position}: ${outcome.title}${hold === null ? "" : ` — ${stillHeldNote(hold)}`}`);
|
|
139
|
+
}
|
|
140
|
+
else if (outcome.status === "unchanged") {
|
|
141
|
+
session.flashMessage(`unchanged ${target.kind} #${target.position}: ${outcome.title}`);
|
|
152
142
|
}
|
|
153
143
|
else {
|
|
154
144
|
session.clearBacklogSelection();
|
|
@@ -159,28 +149,14 @@ export function createEntryEditor(session) {
|
|
|
159
149
|
session.flashError(err);
|
|
160
150
|
}
|
|
161
151
|
};
|
|
152
|
+
// File mode: the line holds a path whose contents replace title and body through the
|
|
153
|
+
// same title-checked tryEditEntry core the title edit uses, keeping the entry's
|
|
154
|
+
// position and its (planned …)/(found by …) stamp. A missing file, an empty path, a
|
|
155
|
+
// file with no title line, and a structurally unsound body all flash and stay here.
|
|
162
156
|
if (entryBodyFile && target.position !== null) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
// position and its (planned …)/(found by …) stamp. A missing file, an empty path, a
|
|
166
|
-
// file with no title line, and a structurally unsound body all flash and stay here.
|
|
167
|
-
const p = session.input.trim();
|
|
168
|
-
if (!p) {
|
|
169
|
-
session.flashMessage("a file path is required");
|
|
170
|
-
return true;
|
|
171
|
-
}
|
|
172
|
-
let text;
|
|
173
|
-
try {
|
|
174
|
-
text = readTextIfExists(p);
|
|
175
|
-
}
|
|
176
|
-
catch (err) {
|
|
177
|
-
session.flashError(err);
|
|
157
|
+
const text = readEditorPath(session);
|
|
158
|
+
if (text === null)
|
|
178
159
|
return true;
|
|
179
|
-
}
|
|
180
|
-
if (text === null) {
|
|
181
|
-
session.flashMessage(`no such file: ${p}`);
|
|
182
|
-
return true;
|
|
183
|
-
}
|
|
184
160
|
const { title: rawTitle, body } = splitEntryFile(text);
|
|
185
161
|
const newTitle = stripEntryStamp(rawTitle);
|
|
186
162
|
if (!newTitle) {
|
|
@@ -197,31 +173,30 @@ export function createEntryEditor(session) {
|
|
|
197
173
|
}
|
|
198
174
|
if (!session.input.trim()) {
|
|
199
175
|
session.flashMessage("an entry needs a title");
|
|
176
|
+
return true;
|
|
200
177
|
}
|
|
201
|
-
|
|
202
|
-
// New entry (Ctrl+N): file by title with an empty body and wake its
|
|
178
|
+
if (target.position === null) {
|
|
179
|
+
// New entry (Ctrl+N): file by title with an empty body and wake its role, the same
|
|
203
180
|
// steps handleBacklogFile takes; a length problem or a throw stays in the editor.
|
|
204
181
|
try {
|
|
205
182
|
const { role, wakeText } = OPERATOR_FILE_KINDS[target.kind];
|
|
206
183
|
const problem = promptLengthProblem(wakeText(collapseWhitespace(session.input)), role);
|
|
207
184
|
if (problem) {
|
|
208
185
|
session.flashMessage(problem);
|
|
186
|
+
return true;
|
|
209
187
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
session.flashMessage(`filed ${target.kind}: ${filed.title} — woke ${role}`);
|
|
216
|
-
}
|
|
188
|
+
const filed = fileEntryWithBody(session.root, target.kind, session.input, "");
|
|
189
|
+
session.commitBacklogEdit(filed.file, `File ${target.kind}: ${filed.title}`);
|
|
190
|
+
submitRolePromptAndWake(session.root, role, wakeText(filed.title));
|
|
191
|
+
exitEntryEdit();
|
|
192
|
+
session.flashMessage(`filed ${target.kind}: ${filed.title} — woke ${role}`);
|
|
217
193
|
}
|
|
218
194
|
catch (err) {
|
|
219
195
|
session.flashError(err);
|
|
220
196
|
}
|
|
197
|
+
return true;
|
|
221
198
|
}
|
|
222
|
-
|
|
223
|
-
applyEntryEdit(session.input);
|
|
224
|
-
}
|
|
199
|
+
applyEntryEdit(session.input);
|
|
225
200
|
return true;
|
|
226
201
|
}
|
|
227
202
|
return false;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { loadConfigSafe } from "../../config/config.js";
|
|
2
|
+
import { createTeamConfigKeyEditor } from "./tui-editor-config.js";
|
|
3
|
+
/** The Ctrl+L fallback editor on the Activity view's shared prompt line — the terminal twin of
|
|
4
|
+
* the dashboard Settings `Fallback (free at the spend cap)` field and `tumwater config set
|
|
5
|
+
* fallback`, completing the team-config knob set the model field and the budget gate point at.
|
|
6
|
+
* On the Activity view (view 0, the team scope) Ctrl+L opens it pre-filled from the file's
|
|
7
|
+
* top-level `fallback` (a bare selector string as-is, an array or map as compact JSON, empty
|
|
8
|
+
* when unset — empty means "no fallback" on save); a role's Transcript keeps Ctrl+L for its
|
|
9
|
+
* instructions editor, which runs after this arm. Enter writes through the shared
|
|
10
|
+
* setConfigKey/unsetConfigKey family — so setConfigKey JSON-parses the typed text (a
|
|
11
|
+
* `["omlx/..."]` becomes the step list, `omlx/...` the literal selector) and validateConfig
|
|
12
|
+
* owns the value's truth. The skeleton (mode flag, held registry, Esc/Ctrl+L/Ctrl+T exit) is
|
|
13
|
+
* createTeamConfigKeyEditor's. */
|
|
14
|
+
export function createFallbackEditor(session) {
|
|
15
|
+
return createTeamConfigKeyEditor(session, {
|
|
16
|
+
mode: "fallback",
|
|
17
|
+
openCtrl: "l",
|
|
18
|
+
configKey: "fallback",
|
|
19
|
+
prefill: (s) => {
|
|
20
|
+
const existing = loadConfigSafe(s.root).config?.fallback;
|
|
21
|
+
return typeof existing === "string"
|
|
22
|
+
? existing
|
|
23
|
+
: existing && typeof existing === "object"
|
|
24
|
+
? JSON.stringify(existing)
|
|
25
|
+
: "";
|
|
26
|
+
},
|
|
27
|
+
openFlash: "edit the team's fallback model (fallback, a model id or a JSON map by tier; empty clears): Enter to save, Esc to cancel",
|
|
28
|
+
clearedFlash: "team fallback cleared",
|
|
29
|
+
setFlash: (text) => `team fallback set to ${text}`,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { setConfigKey, unsetConfigKey } from "../../config/config-write.js";
|
|
2
|
+
import { rawRoleField } from "../../roles/role-view.js";
|
|
3
|
+
import { createRoleKeyEditor } from "./tui-editor-role-key.js";
|
|
4
|
+
/** The Ctrl+G parallel-runner-count editor on the shared prompt line, the terminal twin of
|
|
5
|
+
* `tumwater role <id> --instances` and the dashboard role drawer's instance-count editor. Enter
|
|
6
|
+
* writes `roles.<base>.instances` through the shared setConfigKey/unsetConfigKey family the CLI
|
|
7
|
+
* and the drawer use — an empty line clears the key (a no-op success when the file never set
|
|
8
|
+
* it), otherwise the raw text is written — so validateConfig's INSTANCE_COUNT (an integer 1–8)
|
|
9
|
+
* and INSTANCE_ROLES (feature and bugfix) own the value's truth, exactly as the CLI documents.
|
|
10
|
+
* Pre-fill reads the FILE's raw `roles.<base>.instances` (empty when unset), since the merged
|
|
11
|
+
* count may default. Ctrl+G keeps its scope-cycle meaning on every other view: the arm consumes
|
|
12
|
+
* the key only on a role's Transcript. The shape it shares with the role-model editor lives in
|
|
13
|
+
* createRoleKeyEditor. */
|
|
14
|
+
export function createInstancesEditor(session) {
|
|
15
|
+
return createRoleKeyEditor(session, {
|
|
16
|
+
mode: "instances",
|
|
17
|
+
openCtrl: "g",
|
|
18
|
+
prefill: (s, base) => {
|
|
19
|
+
const existing = rawRoleField(s.root, base, "instances");
|
|
20
|
+
return typeof existing === "number" ? String(existing) : "";
|
|
21
|
+
},
|
|
22
|
+
openFlash: (role) => `edit ${role}'s parallel-runner count (an integer 1-8; feature and bugfix only; empty clears): Enter to save, Esc to cancel`,
|
|
23
|
+
commit: (s, base, text) => {
|
|
24
|
+
const cfgKey = `roles.${base}.instances`;
|
|
25
|
+
if (text === "") {
|
|
26
|
+
const r = unsetConfigKey(s.root, cfgKey);
|
|
27
|
+
return r.ok && r.oldValue === undefined ? { ok: true, noop: true } : r;
|
|
28
|
+
}
|
|
29
|
+
// Shape, before the write: an exponent spelling like "2e0" is itself a JSON number (2),
|
|
30
|
+
// so setConfigKey's JSON.parse would read it past validateConfig's INSTANCE_COUNT integer
|
|
31
|
+
// check and silently write 2 — the same silent misread the CLI's --instances guard
|
|
32
|
+
// refuses (and the dashboard drawer's instance editor), so this refuses it too, naming
|
|
33
|
+
// the key and staying in edit mode. A non-exponent spelling falls through to
|
|
34
|
+
// validateConfig, whose message names the key.
|
|
35
|
+
if (!/^\d*\.?\d+$/.test(text) && /^\d+(\.\d+)?[eE][+-]?\d+$/.test(text)) {
|
|
36
|
+
return { ok: false, error: `${cfgKey} must be a plain integer from 1 to 8 — no exponent notation` };
|
|
37
|
+
}
|
|
38
|
+
const r = setConfigKey(s.root, cfgKey, text);
|
|
39
|
+
return r.ok && r.unchanged ? { ok: true, noop: true } : r;
|
|
40
|
+
},
|
|
41
|
+
savedFlash: (role, text) => text === "" ? `${role}: instances cleared` : `${role}: instances set to ${text}`,
|
|
42
|
+
noopFlash: (role, text) => text === ""
|
|
43
|
+
? `${role}: instances is not set in tumwater.json, so nothing was cleared`
|
|
44
|
+
: `${role}: instances is already ${text}, so tumwater.json was not written`,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { setConfigKey, unsetConfigKey } from "../../config/config-write.js";
|
|
2
|
+
import { baseRoleOf } from "../../roles/runner-ids.js";
|
|
3
|
+
import { readEditorPath } from "./tui-editor-session.js";
|
|
4
|
+
/** The Transcript view's Ctrl+L replace-standing-instructions editor on the shared prompt line,
|
|
5
|
+
* the terminal twin of `tumwater role <id> --instructions-file` and the dashboard role drawer's
|
|
6
|
+
* `Instructions override` row. It owns its `instructionsFor` flag, its saved-draft pair, and the
|
|
7
|
+
* role pinned when it opened, so a roster shift cannot redirect the write. Ctrl+L opens it on a
|
|
8
|
+
* role transcript (the same in-range guard every transcript editor arm carries — the
|
|
9
|
+
* instructions row is reachable from the plain transcript, not only the notes mode), flashes the
|
|
10
|
+
* held-editor busy message when another prompt-line editor owns the line, settles the recall
|
|
11
|
+
* draft, blanks the line, and flashes the enter/Esc framing. Enter trims the line as a file
|
|
12
|
+
* path, reads it verbatim with the same readTextIfExists the CLI's non-interactive replacement
|
|
13
|
+
* uses, and writes the BASE role's `roles.<base>.instructions` through the same
|
|
14
|
+
* setConfigKey/unsetConfigKey family the CLI and dashboard drawer use — so validateConfig
|
|
15
|
+
* (naming the dotted key and its ROLE_INSTRUCTIONS_MAX_CHARS cap) owns the value's truth. An
|
|
16
|
+
* empty or whitespace-only file clears the key (unsetConfigKey, the dashboard's empty-clears
|
|
17
|
+
* convention, giving the terminal a way to clear); an over-cap or unknown-role write flashes
|
|
18
|
+
* the returned error and STAYS open. Esc, Ctrl+L again, and Ctrl+T cancel by restoring the
|
|
19
|
+
* prompt draft. On success the notes repaints through the session's refreshNotes seam (the
|
|
20
|
+
* notes view's cached Markdown recomputes the same role-inspection body the `Instructions
|
|
21
|
+
* override` row renders). Unlike quiet hours' director refusal there is none here: the
|
|
22
|
+
* dashboard editor and `--instructions-file` both accept the director, so it edits too. */
|
|
23
|
+
export function createInstructionsEditor(session) {
|
|
24
|
+
let instructionsFor = null;
|
|
25
|
+
let restoreDraft = () => { };
|
|
26
|
+
// Leave the instructions editor (Esc, Ctrl+L again, or Ctrl+T): restore the saved prompt
|
|
27
|
+
// text with no override written.
|
|
28
|
+
const exitInstructionsMode = () => {
|
|
29
|
+
if (instructionsFor === null)
|
|
30
|
+
return;
|
|
31
|
+
instructionsFor = null;
|
|
32
|
+
session.held.instructions = null;
|
|
33
|
+
restoreDraft();
|
|
34
|
+
};
|
|
35
|
+
const handleKey = (_str, key, ctx) => {
|
|
36
|
+
if (key.ctrl && key.name === "l" &&
|
|
37
|
+
ctx.roleIds.length > 0 && ctx.view >= 1 && ctx.view <= ctx.roleIds.length) {
|
|
38
|
+
// Ctrl+L toggles the replace-instructions editor on a role's Transcript view (mnemonic:
|
|
39
|
+
// the instructions **l**ine, bound on no other TUI view except the Activity view's
|
|
40
|
+
// fallback editor and the Health view's retire-leftovers arm — the in-range role check
|
|
41
|
+
// keeps the arm from firing off a role transcript, and those other arms (which run before
|
|
42
|
+
// this one) keep Ctrl+L on Activity and Health).
|
|
43
|
+
// The line edits one file path that becomes the viewed role's whole
|
|
44
|
+
// `roles.<base>.instructions` override through the same setConfigKey write the CLI and
|
|
45
|
+
// the dashboard drawer use. Entering saves the prompt draft and blanks the line; leaving
|
|
46
|
+
// restores it.
|
|
47
|
+
if (session.editorEntryDenied("instructions"))
|
|
48
|
+
return true;
|
|
49
|
+
if (instructionsFor !== null) {
|
|
50
|
+
exitInstructionsMode();
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
// Mid-recall, hand the arrows' saved draft back before the instructions editor takes
|
|
54
|
+
// the line — the same settle the role editors do — so leaving restores the draft, not
|
|
55
|
+
// a history entry, with no stale recall state riding along.
|
|
56
|
+
const role = ctx.roleIds[ctx.view - 1]; // defined: the open-arm range guard above pins the view into roleIds
|
|
57
|
+
restoreDraft = session.takeDraft();
|
|
58
|
+
instructionsFor = role;
|
|
59
|
+
session.held.instructions = role;
|
|
60
|
+
session.input = "";
|
|
61
|
+
session.cursor = 0;
|
|
62
|
+
session.flashMessage(`replace ${role}'s instructions from a file — Enter to save, Esc to cancel`);
|
|
63
|
+
}
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
if (key.name === "escape" && instructionsFor !== null) {
|
|
67
|
+
// Esc cancels the instructions editor, restoring the previous prompt text and writing
|
|
68
|
+
// nothing.
|
|
69
|
+
exitInstructionsMode();
|
|
70
|
+
session.flashMessage("instructions edit cancelled");
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
if (key.name === "return" && instructionsFor !== null) {
|
|
74
|
+
// Enter reads the typed path whole and writes it through the shared
|
|
75
|
+
// setConfigKey/unsetConfigKey family the CLI's `--instructions-file`/`--clear-instructions`
|
|
76
|
+
// and the dashboard drawer use, so ROLE_INSTRUCTIONS_MAX_CHARS and the unknown-role check
|
|
77
|
+
// (enforced by validateConfig inside writeConfigMutation) and their wording cannot drift.
|
|
78
|
+
// An empty path, a missing file, an unreadable file, an over-cap text, and an unknown id
|
|
79
|
+
// all flash and STAY in the editor so the operator can fix the path; an empty or
|
|
80
|
+
// whitespace-only file clears the override (the dashboard's empty-clears convention). On
|
|
81
|
+
// success the notes repaints the updated `Instructions override` row and the editor
|
|
82
|
+
// exits with the shared success wording.
|
|
83
|
+
const role = instructionsFor;
|
|
84
|
+
const text = readEditorPath(session);
|
|
85
|
+
if (text === null)
|
|
86
|
+
return true;
|
|
87
|
+
const base = baseRoleOf(role);
|
|
88
|
+
const cfgKey = `roles.${base}.instructions`;
|
|
89
|
+
if (text.trim() === "") {
|
|
90
|
+
const result = unsetConfigKey(session.root, cfgKey);
|
|
91
|
+
if (!result.ok) {
|
|
92
|
+
session.flashMessage(result.error); // broken config or write failure — stay in edit mode
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
session.refreshNotes(role);
|
|
96
|
+
exitInstructionsMode();
|
|
97
|
+
session.flashMessage(result.oldValue === undefined
|
|
98
|
+
? `${role}: instructions is not set in tumwater.json, so nothing was cleared`
|
|
99
|
+
: `${role}: instructions cleared`);
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
const result = setConfigKey(session.root, cfgKey, JSON.stringify(text));
|
|
103
|
+
if (!result.ok) {
|
|
104
|
+
session.flashMessage(result.error); // over the cap, unknown id, or write failure — stay in edit mode
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
session.refreshNotes(role);
|
|
108
|
+
exitInstructionsMode();
|
|
109
|
+
session.flashMessage(result.unchanged
|
|
110
|
+
? `${role}: instructions unchanged, so tumwater.json was not written`
|
|
111
|
+
: `${role}: instructions saved (${text.length} chars)`);
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
return false;
|
|
115
|
+
};
|
|
116
|
+
return { handleKey, exit: exitInstructionsMode };
|
|
117
|
+
}
|
|
@@ -1,22 +1,23 @@
|
|
|
1
|
-
// vocabulary: plain
|
|
2
1
|
import { setConfigKey, unsetConfigKey } from "../../config/config-write.js";
|
|
3
2
|
import { checkMinRunInterval } from "../../config/config-keys.js";
|
|
4
|
-
import { baseRoleOf } from "../../roles/
|
|
3
|
+
import { baseRoleOf } from "../../roles/runner-ids.js";
|
|
5
4
|
import { rawRoleField } from "../../roles/role-view.js";
|
|
5
|
+
import { gotSuffix } from "../../text/text.js";
|
|
6
6
|
/** The Ctrl+S min-run-interval editor on the shared prompt line, the terminal twin of
|
|
7
7
|
* `tumwater role <id> --min-run-interval` and the dashboard role drawer's `Min run interval`
|
|
8
8
|
* row. It owns its mode flag, its saved-draft pair, and the role pinned when it opened, so a
|
|
9
9
|
* roster shift cannot redirect the write. Enter writes through the shared
|
|
10
10
|
* setConfigKey/unsetConfigKey family the CLI and the drawer use — so checkMinRunInterval
|
|
11
|
-
* (naming `roles.<base>.minRunIntervalSeconds`) and validateConfig own the value's truth —
|
|
11
|
+
* (naming `roles.<base>.minRunIntervalSeconds`) and validateConfig own the value's truth — a
|
|
12
|
+
* non-plain-decimal spelling (hex/exponent, negative, or non-numeric) flashes the editor's own
|
|
13
|
+
* shape message first, mirroring the budget editor's plain-decimal rule, and stays open — and
|
|
12
14
|
* Esc, Ctrl+S again, and Ctrl+T cancel by restoring the prompt draft. Unlike the quiet editor
|
|
13
15
|
* there is no director refusal: `tumwater role director --min-run-interval` is accepted, so the
|
|
14
16
|
* director edits too. Mutually exclusive with the other editors on the one prompt line via the
|
|
15
17
|
* session's held registry. */
|
|
16
18
|
export function createIntervalEditor(session) {
|
|
17
19
|
let intervalMode = false;
|
|
18
|
-
let
|
|
19
|
-
let savedCursor = 0;
|
|
20
|
+
let restoreDraft = () => { };
|
|
20
21
|
// The role pinned when the editor opened, so Enter writes the same role's override even if
|
|
21
22
|
// syncRoster shrinks or reorders the roster while the editor is open.
|
|
22
23
|
let lockedRole = null;
|
|
@@ -26,7 +27,7 @@ export function createIntervalEditor(session) {
|
|
|
26
27
|
return;
|
|
27
28
|
intervalMode = false;
|
|
28
29
|
session.held.interval = false;
|
|
29
|
-
|
|
30
|
+
restoreDraft();
|
|
30
31
|
lockedRole = null;
|
|
31
32
|
};
|
|
32
33
|
const handleKey = (_str, key, ctx) => {
|
|
@@ -37,18 +38,13 @@ export function createIntervalEditor(session) {
|
|
|
37
38
|
// holds the line, in which case Ctrl+S is the toggle-out arm. Entering pre-fills the
|
|
38
39
|
// override from the FILE's raw `roles.<base>.minRunIntervalSeconds` (empty when unset —
|
|
39
40
|
// empty means "clear it, fall back to the team-wide interval" on save) and saves the
|
|
40
|
-
// prompt text; leaving restores it. There is no director exemption: the director
|
|
41
|
+
// prompt text; leaving restores it. There is no director exemption: the director runs
|
|
41
42
|
// on the same clock, so `--min-run-interval` is accepted for it too.
|
|
42
43
|
if (!intervalMode && !(ctx.view >= 1 && ctx.view <= ctx.roleIds.length))
|
|
43
44
|
return false;
|
|
44
45
|
const role = ctx.roleIds[ctx.view - 1];
|
|
45
|
-
|
|
46
|
-
if (held) {
|
|
47
|
-
// Mutually exclusive with the other editors: all share one prompt line, so a mode may
|
|
48
|
-
// not enter while another holds it. Flash the way out first.
|
|
49
|
-
session.flashMessage(held);
|
|
46
|
+
if (session.editorEntryDenied("interval"))
|
|
50
47
|
return true;
|
|
51
|
-
}
|
|
52
48
|
if (intervalMode) {
|
|
53
49
|
exitIntervalMode();
|
|
54
50
|
return true;
|
|
@@ -63,7 +59,7 @@ export function createIntervalEditor(session) {
|
|
|
63
59
|
lockedRole = role;
|
|
64
60
|
const base = baseRoleOf(role);
|
|
65
61
|
const existing = rawRoleField(session.root, base, "minRunIntervalSeconds");
|
|
66
|
-
|
|
62
|
+
restoreDraft = session.takeDraft();
|
|
67
63
|
intervalMode = true;
|
|
68
64
|
session.held.interval = true;
|
|
69
65
|
session.input = typeof existing === "number" ? String(existing) : "";
|
|
@@ -90,13 +86,28 @@ export function createIntervalEditor(session) {
|
|
|
90
86
|
const result = unsetConfigKey(session.root, cfgKey);
|
|
91
87
|
if (result.ok) {
|
|
92
88
|
exitIntervalMode();
|
|
93
|
-
session.flashMessage(
|
|
89
|
+
session.flashMessage(result.oldValue === undefined
|
|
90
|
+
? `${role}: min run interval is not set in tumwater.json, so nothing was cleared`
|
|
91
|
+
: `${role}: min run interval cleared`);
|
|
94
92
|
}
|
|
95
93
|
else {
|
|
96
94
|
session.flashMessage(result.error); // broken config or write failure — stay in edit mode
|
|
97
95
|
}
|
|
98
96
|
return true;
|
|
99
97
|
}
|
|
98
|
+
// Plain decimal seconds only — bare Number() would also read hex ("0x10" → 16) and
|
|
99
|
+
// exponent ("1e2" → 100) as an interval, neither of which an operator means when
|
|
100
|
+
// typing a second count; the budget editor's parseBudgetInput applies the same
|
|
101
|
+
// plain-decimal rule in the TUI, the CLI's --min-run-interval uses plainDecimalToJson,
|
|
102
|
+
// and the GUI drawer's interval-save arm applies the same shape guard.
|
|
103
|
+
// Non-plain spellings flash their own shape message — which, like checkMinRunInterval's
|
|
104
|
+
// bounds wording below, names cfgKey so a rejected entry tells which per-role key the
|
|
105
|
+
// editor was about to write — and STAY in edit mode (negative and non-numeric text hit
|
|
106
|
+
// this shape arm too, so the operator sees one consistent message).
|
|
107
|
+
if (!/^\d*\.?\d+$/.test(text)) {
|
|
108
|
+
session.flashMessage(`${cfgKey} must be a plain second count like 300, 0.5, or .5 — no +/-, exponent, or hex notation${gotSuffix(text)}`);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
100
111
|
const value = Number(text);
|
|
101
112
|
const invalid = checkMinRunInterval(value, cfgKey);
|
|
102
113
|
if (invalid) {
|
|
@@ -106,7 +117,9 @@ export function createIntervalEditor(session) {
|
|
|
106
117
|
const result = setConfigKey(session.root, cfgKey, JSON.stringify(value));
|
|
107
118
|
if (result.ok) {
|
|
108
119
|
exitIntervalMode();
|
|
109
|
-
session.flashMessage(
|
|
120
|
+
session.flashMessage(result.unchanged
|
|
121
|
+
? `${role}: min run interval is already ${text}s, so tumwater.json was not written`
|
|
122
|
+
: `${role}: min run interval set to ${text}s`);
|
|
110
123
|
}
|
|
111
124
|
else {
|
|
112
125
|
session.flashMessage(result.error); // broken config or write failure — stay in edit mode
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { loadConfigSafe } from "../../config/config.js";
|
|
2
|
+
import { createTeamConfigKeyEditor } from "./tui-editor-config.js";
|
|
3
|
+
/** The Ctrl+A team-model editor on the Activity view's shared prompt line, the terminal twin
|
|
4
|
+
* of the dashboard Settings `Model` field and `tumwater config set model`. Enter writes the
|
|
5
|
+
* top-level `model` through the shared setConfigKey/unsetConfigKey family — setConfigKey
|
|
6
|
+
* JSON-parses the text, so a bare model id stays a literal string while a `{"strong":"..."}`
|
|
7
|
+
* map becomes the map, and validateConfig owns the value's truth (a number like `42` flashes
|
|
8
|
+
* the model validator's message and stays open). The editor opens only on view 0: on a role's
|
|
9
|
+
* Transcript Ctrl+A keeps its per-role abort meaning, and on Backlog it keeps the answer
|
|
10
|
+
* editor's key. */
|
|
11
|
+
export function createModelEditor(session) {
|
|
12
|
+
return createTeamConfigKeyEditor(session, {
|
|
13
|
+
mode: "model",
|
|
14
|
+
openCtrl: "a",
|
|
15
|
+
configKey: "model",
|
|
16
|
+
// A selector string as-is, a tier map as compact JSON, empty when unset.
|
|
17
|
+
prefill: (s) => {
|
|
18
|
+
const existing = loadConfigSafe(s.root).config?.model;
|
|
19
|
+
return existing === undefined
|
|
20
|
+
? ""
|
|
21
|
+
: typeof existing === "string"
|
|
22
|
+
? existing
|
|
23
|
+
: JSON.stringify(existing);
|
|
24
|
+
},
|
|
25
|
+
openFlash: "edit the team's model (model, a model id or a JSON map by tier like {\"strong\":\"...\"}; empty clears): Enter to save, Esc to cancel",
|
|
26
|
+
clearedFlash: "team model cleared",
|
|
27
|
+
setFlash: (text) => `team model set to ${text}`,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
// vocabulary: plain
|
|
2
1
|
import { saveRoleNote } from "../../agent/role-notes-write.js";
|
|
3
|
-
import { readTextIfExists } from "../../files/files.js";
|
|
4
2
|
import { roleNotesPath } from "../../paths.js";
|
|
3
|
+
import { readEditorPath } from "./tui-editor-session.js";
|
|
5
4
|
/** The notes view's Ctrl+E replace-notes-from-a-file editor on the shared prompt line, the
|
|
6
5
|
* terminal twin of `tumwater role <id> --notes-file <path>` and the dashboard drawer's
|
|
7
6
|
* textarea. It owns its `notesEditFor` flag, its saved-draft pair, and the handleKey arms for
|
|
@@ -16,8 +15,7 @@ import { roleNotesPath } from "../../paths.js";
|
|
|
16
15
|
* session's refreshNotes seam. */
|
|
17
16
|
export function createNotesEditor(session) {
|
|
18
17
|
let notesEditFor = null;
|
|
19
|
-
let
|
|
20
|
-
let notesSavedCursor = 0;
|
|
18
|
+
let restoreDraft = () => { };
|
|
21
19
|
// Leave the notes editor (Esc, Ctrl+E again, or Ctrl+T): restore the saved prompt text with
|
|
22
20
|
// no note written.
|
|
23
21
|
const exitNotesMode = () => {
|
|
@@ -25,7 +23,7 @@ export function createNotesEditor(session) {
|
|
|
25
23
|
return;
|
|
26
24
|
notesEditFor = null;
|
|
27
25
|
session.held.notes = null;
|
|
28
|
-
|
|
26
|
+
restoreDraft();
|
|
29
27
|
};
|
|
30
28
|
const handleKey = (_str, key, ctx) => {
|
|
31
29
|
if (key.ctrl && key.name === "e" && ctx.noteMode &&
|
|
@@ -39,14 +37,8 @@ export function createNotesEditor(session) {
|
|
|
39
37
|
// line edits one file path that becomes the role's whole note through the same
|
|
40
38
|
// saveRoleNote write the CLI and the dashboard drawer use. Entering saves the prompt
|
|
41
39
|
// draft and blanks the line; leaving restores it.
|
|
42
|
-
|
|
43
|
-
if (held) {
|
|
44
|
-
// Mutually exclusive with the other editors: all share one prompt line, so a mode may
|
|
45
|
-
// not enter while another holds it. Flash the way out instead of silently dropping
|
|
46
|
-
// the held mode's saved draft.
|
|
47
|
-
session.flashMessage(held);
|
|
40
|
+
if (session.editorEntryDenied("notes"))
|
|
48
41
|
return true;
|
|
49
|
-
}
|
|
50
42
|
if (notesEditFor !== null) {
|
|
51
43
|
exitNotesMode();
|
|
52
44
|
}
|
|
@@ -55,7 +47,7 @@ export function createNotesEditor(session) {
|
|
|
55
47
|
// line — the same settle the role editors do — so leaving restores the
|
|
56
48
|
// draft, not a history entry, with no stale recall state riding along.
|
|
57
49
|
const role = ctx.roleIds[ctx.view - 1]; // defined: the open-arm range guard above pins the view into roleIds
|
|
58
|
-
|
|
50
|
+
restoreDraft = session.takeDraft();
|
|
59
51
|
notesEditFor = role;
|
|
60
52
|
session.held.notes = role;
|
|
61
53
|
session.input = "";
|
|
@@ -78,23 +70,11 @@ export function createNotesEditor(session) {
|
|
|
78
70
|
// clears the note. On success the notes view repaints the new note without leaving and
|
|
79
71
|
// re-entering, and the editor exits with the shared success wording.
|
|
80
72
|
const role = notesEditFor;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
let text;
|
|
87
|
-
try {
|
|
88
|
-
text = readTextIfExists(p);
|
|
89
|
-
}
|
|
90
|
-
catch (err) {
|
|
91
|
-
session.flashError(err);
|
|
73
|
+
// The trim/read/refuse prologue is shared with the brief, instructions and backlog-entry
|
|
74
|
+
// editors (readEditorPath), which read one typed path the same way.
|
|
75
|
+
const text = readEditorPath(session);
|
|
76
|
+
if (text === null)
|
|
92
77
|
return true;
|
|
93
|
-
}
|
|
94
|
-
if (text === null) {
|
|
95
|
-
session.flashMessage(`no such file: ${p}`);
|
|
96
|
-
return true;
|
|
97
|
-
}
|
|
98
78
|
try {
|
|
99
79
|
const message = saveRoleNote(roleNotesPath(session.root, role), text);
|
|
100
80
|
session.refreshNotes(role);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { loadConfigSafe } from "../../config/config.js";
|
|
2
|
+
import { createTeamConfigKeyEditor } from "./tui-editor-config.js";
|
|
3
|
+
/** The Ctrl+E notify-hook editor on the Activity view's shared prompt line — the terminal
|
|
4
|
+
* twin of the dashboard Settings `Notify hook` field and `tumwater config set notify`, so an
|
|
5
|
+
* operator can point the orchestrator's notable-event command at a new shell command without
|
|
6
|
+
* leaving the terminal (or test it without guessing). Enter writes the top-level `notify`
|
|
7
|
+
* through the shared setConfigKey/unsetConfigKey family: setConfigKey JSON-parses the typed
|
|
8
|
+
* text (a bare command stays the literal string, while a typed number or object becomes a
|
|
9
|
+
* non-string and validateConfig's notify block rejects it with nothing written). The Team
|
|
10
|
+
* scope is the only scope, so the editor takes the line on the Activity view (view 0) alone;
|
|
11
|
+
* on every other view Ctrl+E keeps its own meaning (the Report window cycle, the Health
|
|
12
|
+
* reclaim, and the notes/Brief file replace). Esc, Ctrl+E again, and Ctrl+T cancel by
|
|
13
|
+
* restoring the prompt draft. Mutually exclusive with the other editors on the one prompt
|
|
14
|
+
* line via the session's held registry. */
|
|
15
|
+
export function createNotifyEditor(session) {
|
|
16
|
+
return createTeamConfigKeyEditor(session, {
|
|
17
|
+
mode: "notify",
|
|
18
|
+
openCtrl: "e",
|
|
19
|
+
configKey: "notify",
|
|
20
|
+
prefill: (s) => String(loadConfigSafe(s.root).config?.notify ?? ""),
|
|
21
|
+
openFlash: "edit the team's notify hook (notify, the shell command run on notable events; empty clears): Enter to save, Esc to cancel",
|
|
22
|
+
clearedFlash: "notify hook cleared",
|
|
23
|
+
setFlash: (text) => `notify hook set to ${text}`,
|
|
24
|
+
});
|
|
25
|
+
}
|