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
|
@@ -3,11 +3,21 @@
|
|
|
3
3
|
* `## Open` to `## Answered` with a dated operator answer. The markdown surgery itself — the
|
|
4
4
|
* fence-aware `### ` block cut and the `## Answered` skeleton — lives in src/backlog/
|
|
5
5
|
* question-answer.ts (the shared QUESTIONS.md core the TUI and dashboard also call); this module
|
|
6
|
-
* is
|
|
7
|
-
* cli
|
|
6
|
+
* is the command's argument scan, say/fail/editor jacket and commit-and-wake tail over that core,
|
|
7
|
+
* routed to by src/cli.ts.
|
|
8
8
|
*/
|
|
9
|
-
import { fail, say, sayJson } from "./cli-output.js";
|
|
10
|
-
import {
|
|
9
|
+
import { fail, say, sayJson, warnIfUncommitted } from "./cli-output.js";
|
|
10
|
+
import { parseCountFlag, parsePositionFlag, rejectRepeatFlag } from "./cli-args.js";
|
|
11
|
+
import { JSON_FLAG, rejectEqualsForm } from "./cli-flag-specs.js";
|
|
12
|
+
import { refuseTeamWorkspace } from "./cli-repo-gate.js";
|
|
13
|
+
import { promptFileValue, readPromptFileArg } from "./cli-command-args.js";
|
|
14
|
+
import { editInEditor } from "./cli-editor.js";
|
|
15
|
+
import { gotSuffix, parsePositiveInt } from "../text/text.js";
|
|
16
|
+
import { typoSuffix } from "../text/suggest.js";
|
|
17
|
+
import { commitOperatorBacklogEdit } from "../backlog/backlog-commit.js";
|
|
18
|
+
import { stampedSubject } from "../git/commit-message.js";
|
|
19
|
+
import { wakeQuestionAsker } from "../operator/operator-intent.js";
|
|
20
|
+
import { answeredQuestionAt, answeredQuestionCount, answeredQuestionList, answeredQuestionPayload, openQuestionAt, openQuestionList, questionListPayload, renderShownQuestion, UNCLOSED_FENCE_MESSAGE, tryAnswerQuestion } from "../backlog/question-answer.js";
|
|
11
21
|
import { bodyStructureProblem, normalizeEntryBody } from "../backlog/backlog-write.js";
|
|
12
22
|
/** The `tumwater questions answer` entry point: `tryAnswerQuestion` without a title check, its
|
|
13
23
|
* refusals mapped to `fail` (exit 1) with the CLI's wording. Returns the answered question's
|
|
@@ -32,7 +42,7 @@ function answeredOrFail(n, outcome) {
|
|
|
32
42
|
/** `tumwater questions answer <n> --file <path>`: the decision's lines kept (normalizeEntryBody),
|
|
33
43
|
* behind the same structural guard `plan --file` uses, so a stray `## ` line cannot grow a
|
|
34
44
|
* phantom section. Refusals exit 1 through `fail`. */
|
|
35
|
-
|
|
45
|
+
function answerQuestionFromText(root, n, text, expectedTitle) {
|
|
36
46
|
const body = normalizeEntryBody(text);
|
|
37
47
|
if (body === "")
|
|
38
48
|
fail("questions answer needs a decision: the file is blank");
|
|
@@ -45,12 +55,12 @@ export function answerQuestionFromText(root, n, text, expectedTitle) {
|
|
|
45
55
|
export const ANSWER_EDITOR_CUT = "<!-- tumwater: write your decision above this line; this line and everything below it are dropped -->";
|
|
46
56
|
/** The editor buffer for bare `questions answer <n>`: an empty decision area, the cut line, then
|
|
47
57
|
* the question's heading and body for reference. */
|
|
48
|
-
|
|
58
|
+
function answerEditorBuffer(title, body) {
|
|
49
59
|
return ["", "", ANSWER_EDITOR_CUT, `### ${title}`, body].join("\n");
|
|
50
60
|
}
|
|
51
61
|
/** The decision an edited `answerEditorBuffer` holds: the text before the first cut line (all of
|
|
52
62
|
* it when the operator deleted the line). */
|
|
53
|
-
|
|
63
|
+
function decisionFromEditorBuffer(text) {
|
|
54
64
|
const lines = text.split("\n");
|
|
55
65
|
const cut = lines.findIndex((l) => l.trim() === ANSWER_EDITOR_CUT);
|
|
56
66
|
return (cut === -1 ? lines : lines.slice(0, cut)).join("\n");
|
|
@@ -66,9 +76,200 @@ export function sayAnswered(position, title, json, decision) {
|
|
|
66
76
|
/** One entry point for the list modes so cli.ts stays a dispatcher: --json prints the payload
|
|
67
77
|
* document (an empty outbox still prints it), prose prints the numbered lines or the empty
|
|
68
78
|
* line. */
|
|
69
|
-
|
|
79
|
+
function sayQuestionList(root, json, pattern) {
|
|
70
80
|
if (json)
|
|
71
|
-
sayJson(questionListPayload(root));
|
|
81
|
+
sayJson(questionListPayload(root, pattern));
|
|
72
82
|
else
|
|
73
|
-
say(openQuestionList(root));
|
|
83
|
+
say(openQuestionList(root, pattern));
|
|
84
|
+
}
|
|
85
|
+
/** `questions answer`'s `--file <path>` flag: the decision comes from the file, lines kept. */
|
|
86
|
+
const FILE_SPEC = { names: ["--file"], value: true, valueName: "<path>" };
|
|
87
|
+
/** `tumwater questions`: list open questions, or answer one with `answer <n> <decision>`. */
|
|
88
|
+
export async function cmdQuestions(root, args) {
|
|
89
|
+
// No requireReadyRepo gate, like backlog: the reader degrades to an empty list on a
|
|
90
|
+
// missing QUESTIONS.md, so the command inspects any directory instead of refusing (a team
|
|
91
|
+
// workspace still refuses).
|
|
92
|
+
// The answer form's decision is free-form prose, so peelPositionals cannot route the
|
|
93
|
+
// tokens: a decision word may begin with a single dash (`questions answer 1 "-50% spend
|
|
94
|
+
// cap"`), and peelPositionals would hand it to the flag gate as an unknown flag — the
|
|
95
|
+
// same masking parsePromptArgs avoids for prompt text by owning only `--`-prefixed
|
|
96
|
+
// tokens. So the questions parse scans args itself: a `--json` token before the
|
|
97
|
+
// question's number positional is the one flag (repeats refused, equals form named by
|
|
98
|
+
// rejectEqualsForm), any other `--`-prefixed token before it is refused as unknown, and
|
|
99
|
+
// every remaining token in order is prose — subcommand, position number, then the
|
|
100
|
+
// decision words. Past the number positional, tokens are decision words even when one
|
|
101
|
+
// is spelled `--json`: unquoted decision prose (`questions answer 1 keep --json output`)
|
|
102
|
+
// reaches the command as separate argv tokens, and a flag scan that matched `--json`
|
|
103
|
+
// anywhere silently ate the token out of the recorded decision and flipped the command
|
|
104
|
+
// into JSON mode. Unknown `--`-prefixed tokens stay refused even there, so a misspelled
|
|
105
|
+
// flag is still an error rather than silent decision text.
|
|
106
|
+
const words = [];
|
|
107
|
+
let jsonFlag = false;
|
|
108
|
+
let numbered = false;
|
|
109
|
+
let file;
|
|
110
|
+
let answeredFlag = false;
|
|
111
|
+
let limit;
|
|
112
|
+
let grep;
|
|
113
|
+
let show;
|
|
114
|
+
for (let i = 0; i < args.length; i++) {
|
|
115
|
+
const arg = args[i];
|
|
116
|
+
if (!numbered && arg === "--show") {
|
|
117
|
+
rejectRepeatFlag(show !== undefined, "--show");
|
|
118
|
+
show = parsePositionFlag("--show", args[++i], "--show 2");
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (!numbered && arg === "--answered") {
|
|
122
|
+
rejectRepeatFlag(answeredFlag, "--answered");
|
|
123
|
+
answeredFlag = true;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (!numbered && arg === "-n") {
|
|
127
|
+
rejectRepeatFlag(limit !== undefined, "-n");
|
|
128
|
+
limit = parseCountFlag("-n", args[++i]);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (!numbered && arg === "--grep") {
|
|
132
|
+
rejectRepeatFlag(grep !== undefined, "--grep");
|
|
133
|
+
const text = args[++i];
|
|
134
|
+
if (text === undefined || text === "")
|
|
135
|
+
fail("--grep needs text (usage: tumwater questions [--answered] --grep <text>)");
|
|
136
|
+
grep = text;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (arg === "--file") {
|
|
140
|
+
rejectRepeatFlag(file !== undefined, "--file");
|
|
141
|
+
file = promptFileValue(args[++i]);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (!numbered && arg === "--json") {
|
|
145
|
+
rejectRepeatFlag(jsonFlag, "--json");
|
|
146
|
+
jsonFlag = true;
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (arg.startsWith("--") && arg !== "--json") {
|
|
150
|
+
rejectEqualsForm(arg, [JSON_FLAG, FILE_SPEC, { names: ["--show"], value: true, valueName: "<n>" }, { names: ["--grep"], value: true, valueName: "<text>" }]);
|
|
151
|
+
// The complete flag set (this gate only sees `--`-flags, but `-n` is a real flag the
|
|
152
|
+
// list should not omit) plus the same did-you-mean hint the sibling commands'
|
|
153
|
+
// unknown-argument gate (cli-flag-specs's failUnknownArgument) attaches, so a mistyped
|
|
154
|
+
// `--ansered` names `--answered` instead of only printing the valid list — the missing
|
|
155
|
+
// treatment the questions gate was the one unknown-argument caller without.
|
|
156
|
+
const spellings = ["--json", "--file", "--answered", "--show", "--grep", "-n"];
|
|
157
|
+
fail(`unknown argument: ${arg} (valid flags for tumwater questions: ${spellings.join(", ")})${typoSuffix(arg, spellings)}`);
|
|
158
|
+
}
|
|
159
|
+
if (!numbered) {
|
|
160
|
+
// The position is a plain-decimal positive integer (parsePositiveInt's rule, the same
|
|
161
|
+
// one --cancel's position honors): bare Number() admitted hex ("0x2"), exponent
|
|
162
|
+
// ("1e2"), and whitespace-padded spellings as a valid question number, answering a
|
|
163
|
+
// question the operator never named.
|
|
164
|
+
if (parsePositiveInt(arg) !== null)
|
|
165
|
+
numbered = true;
|
|
166
|
+
}
|
|
167
|
+
words.push(arg);
|
|
168
|
+
}
|
|
169
|
+
if (limit !== undefined && !answeredFlag)
|
|
170
|
+
fail("-n needs --answered (it limits the answered questions listed)");
|
|
171
|
+
refuseTeamWorkspace(root);
|
|
172
|
+
if (show !== undefined) {
|
|
173
|
+
if (grep !== undefined)
|
|
174
|
+
fail("--grep does not apply to --show");
|
|
175
|
+
if (words.length > 0)
|
|
176
|
+
fail("--show prints one open question; it takes no subcommand");
|
|
177
|
+
if (file !== undefined)
|
|
178
|
+
fail("--file only applies to questions answer <n>");
|
|
179
|
+
if (answeredFlag) {
|
|
180
|
+
if (limit !== undefined)
|
|
181
|
+
fail("-n does not apply to --show");
|
|
182
|
+
const a = answeredQuestionAt(root, show);
|
|
183
|
+
if (a === null) {
|
|
184
|
+
const count = answeredQuestionCount(root);
|
|
185
|
+
const range = count > 0 ? ` — valid: 1-${count}` : "";
|
|
186
|
+
fail(`no answered question at position ${show} (${count} in QUESTIONS.md)${range}`);
|
|
187
|
+
}
|
|
188
|
+
if (jsonFlag)
|
|
189
|
+
sayJson(a);
|
|
190
|
+
else
|
|
191
|
+
say(renderShownQuestion(a));
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const q = openQuestionAt(root, show);
|
|
195
|
+
if (q === null) {
|
|
196
|
+
const count = questionListPayload(root).questions.length;
|
|
197
|
+
const range = count > 0 ? ` — valid: 1-${count}` : "";
|
|
198
|
+
fail(`no open question at position ${show} (${count} in QUESTIONS.md)${range}`);
|
|
199
|
+
}
|
|
200
|
+
if (jsonFlag)
|
|
201
|
+
sayJson(q);
|
|
202
|
+
else
|
|
203
|
+
say(renderShownQuestion(q));
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (answeredFlag) {
|
|
207
|
+
if (words.length > 0)
|
|
208
|
+
fail("--answered lists answered questions; it takes no subcommand");
|
|
209
|
+
if (file !== undefined)
|
|
210
|
+
fail("--file only applies to questions answer <n>");
|
|
211
|
+
if (jsonFlag)
|
|
212
|
+
sayJson(answeredQuestionPayload(root, limit ?? 10, grep));
|
|
213
|
+
else
|
|
214
|
+
say(answeredQuestionList(root, limit ?? 10, grep));
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
if (words.length === 0 && file !== undefined)
|
|
218
|
+
fail("--file only applies to questions answer <n>");
|
|
219
|
+
if (words.length === 0) {
|
|
220
|
+
sayQuestionList(root, jsonFlag, grep);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
if (grep !== undefined)
|
|
224
|
+
fail("--grep only applies to listing questions");
|
|
225
|
+
if (words[0] !== "answer")
|
|
226
|
+
fail(`unknown questions subcommand: ${words[0]} (use "answer <n> <decision>", or run \`tumwater questions\` with no subcommand to list them)${typoSuffix(words[0], ["answer"])}`);
|
|
227
|
+
// The offending token carries the same ` (got "...")` suffix every sibling number parser
|
|
228
|
+
// adds (parsePositiveInt's flag callers, --cancel); the bare `answer` form names none.
|
|
229
|
+
const raw = words.length > 1 ? words[1] : undefined;
|
|
230
|
+
const n = raw === undefined ? null : parsePositiveInt(raw);
|
|
231
|
+
if (n === null)
|
|
232
|
+
fail(`questions answer needs a positive question number: questions answer <n> "<decision>"${gotSuffix(raw)}`);
|
|
233
|
+
if (file !== undefined) {
|
|
234
|
+
if (words.length > 2)
|
|
235
|
+
fail(`unexpected argument ${JSON.stringify(words[2])} — with --file the decision comes from the file`);
|
|
236
|
+
const answered = answerQuestionFromText(root, n, readPromptFileArg(file));
|
|
237
|
+
await commitAndReportAnswer(root, n, jsonFlag, answered.title, answered.decision);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
const decision = words.slice(2).join(" ").trim();
|
|
241
|
+
if (words.length === 2) {
|
|
242
|
+
const open = questionListPayload(root).questions;
|
|
243
|
+
const question = open[n - 1];
|
|
244
|
+
if (question === undefined) {
|
|
245
|
+
const range = open.length > 0 ? ` — valid: 1-${open.length}` : "";
|
|
246
|
+
fail(`no question at position ${n} (${open.length} open)${range}`);
|
|
247
|
+
}
|
|
248
|
+
const edited = decisionFromEditorBuffer(editInEditor(answerEditorBuffer(question.title, question.body), `answer-${n}.md`, "pass a decision or --file <path>"));
|
|
249
|
+
if (edited.trim() === "")
|
|
250
|
+
fail("questions answer needs a decision: the editor left it blank; nothing changed");
|
|
251
|
+
const answered = answerQuestionFromText(root, n, edited, question.title);
|
|
252
|
+
await commitAndReportAnswer(root, n, jsonFlag, answered.title, answered.decision);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (decision === "")
|
|
256
|
+
fail('questions answer needs a decision: questions answer <n> "<decision>"');
|
|
257
|
+
// answerQuestion collapses the free-form decision to one line before writing it; report
|
|
258
|
+
// that written text in the --json payload so it matches the file, not the caller's spacing.
|
|
259
|
+
const { title, decision: writtenDecision } = answerQuestion(root, n, decision);
|
|
260
|
+
await commitAndReportAnswer(root, n, jsonFlag, title, writtenDecision);
|
|
261
|
+
}
|
|
262
|
+
/** Commit the answered question, report it in the requested output mode, and wake the
|
|
263
|
+
* asking role in prose mode — the tail every `questions answer` branch ends with. */
|
|
264
|
+
async function commitAndReportAnswer(root, n, jsonFlag, title, decision) {
|
|
265
|
+
warnIfUncommitted(await commitOperatorBacklogEdit(root, "QUESTIONS.md", stampedSubject("operator", `Answer question: ${title}`)), "QUESTIONS.md"); // vocabulary-ok: commit prefix stays
|
|
266
|
+
sayAnswered(n, title, jsonFlag, decision);
|
|
267
|
+
if (!jsonFlag)
|
|
268
|
+
sayAskerWake(root, title);
|
|
269
|
+
}
|
|
270
|
+
/** Prose-mode follow-up to `questions answer`: wake the asking role and print the confirmation. */
|
|
271
|
+
function sayAskerWake(root, title) {
|
|
272
|
+
const wake = wakeQuestionAsker(root, title);
|
|
273
|
+
if (wake !== null)
|
|
274
|
+
say(wake);
|
|
74
275
|
}
|
package/dist/src/cli.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// vocabulary: plain
|
|
3
2
|
/** The CLI's entry point and command dispatcher — the `bin` target package.json points at
|
|
4
3
|
* (dist/src/cli.js). main() runs the Node-floor gate from package.json's `engines.node`, answers a
|
|
5
4
|
* `--help` topic before any command parses arguments or gates on a ready repo, resolves the repo
|
|
@@ -13,33 +12,49 @@
|
|
|
13
12
|
* modules; this file owns the shared sequence, including each case's flag rejection and its
|
|
14
13
|
* ready-repo gate. An error escaping main() funnels through its catch to fail(errorMessage(err)),
|
|
15
14
|
* the CLI's one error path. */
|
|
16
|
-
import { parseCountFlag, } from "./cli/cli-args.js";
|
|
17
|
-
import { grepFlagSpec, GUI_FLAG_SPECS, JSON_FLAG, LAST_FLAG, N_FLAG, rejectUnknownArgs, resultFlagSpec, ROLE_FLAG,
|
|
15
|
+
import { parseCountFlag, parseGrepFlag, parseModelFlag, parseResultFlag } from "./cli/cli-args.js";
|
|
16
|
+
import { grepFlagSpec, GUI_FLAG_SPECS, JSON_FLAG, LAST_FLAG, N_FLAG, rejectUnknownArgs, resultFlagSpec, modelFlagSpec, byFlagSpec, LOGS_TRANSCRIPT_FLAG, ROLE_FLAG, START_FLAG_SPECS, sinceFlagSpec, untilFlagSpec, STAT_FLAG, PATCH_FLAG, PROMPT_FLAG, TRANSCRIPT_FLAG, SUMMARY_FLAG, COUNT_FLAG, NOTABLE_FLAG, typeFlagSpec, DRY_RUN_FLAG, FORCE_FLAG, FOLLOW_FLAG, STOP_CANCEL_FLAG, STOP_IN_FLAG, REPROBE_FLAG, PROBLEMS_FLAG, KILL_ORPHANS_FLAG, COMMIT_BACKLOG_FLAG, RESYNC_INSTALL_FLAG, DROP_BROKEN_PINS_FLAG, RETIRE_LEFTOVERS_FLAG, } from "./cli/cli-flag-specs.js";
|
|
18
17
|
import { fail, say, sayJsonOrRender } from "./cli/cli-output.js";
|
|
19
|
-
import { historyTrailForm, parsePromptArgs, peelPositionals } from "./cli/cli-command-args.js";
|
|
20
|
-
import { requireReadyRepo } from "./cli/cli-repo-gate.js";
|
|
18
|
+
import { historyTrailForm, parseDoctorChecks, parsePromptArgs, peelPositionals } from "./cli/cli-command-args.js";
|
|
19
|
+
import { refuseTeamWorkspace, requireReadyRepo } from "./cli/cli-repo-gate.js";
|
|
20
|
+
import { loadConfigSafe } from "./config/config.js";
|
|
21
21
|
import { repoToplevel } from "./git/git.js";
|
|
22
22
|
import { didYouMean } from "./text/suggest.js";
|
|
23
23
|
import { errorMessage, gotSuffix, stripTerminalControls } from "./text/text.js";
|
|
24
24
|
import { typoSuffix } from "./text/suggest.js";
|
|
25
|
-
import { completionConfigKeys, completionRoleIds, completionScript, SUPPORTED_SHELLS } from "./cli/completion.js";
|
|
25
|
+
import { completionConfigKeys, completionModels, completionRoleIds, completionScript, configValueWords, SUPPORTED_SHELLS } from "./cli/completion.js";
|
|
26
26
|
import { HELP, helpTopic, helpTopicForArgs, suggestCommand } from "./cli/help.js";
|
|
27
27
|
import { PACKAGE_JSON, nodeFloorProblem, packageEnginesNode, packageVersion } from "./version.js";
|
|
28
28
|
// The CLI's help text and its per-command topic parser live in help.ts — importing cli.ts
|
|
29
29
|
// would run main(), so tests pin the topics against help.ts directly.
|
|
30
30
|
/** One run's trail, shared by `history <role> <n>` and its hidden alias `tick` so the two cannot
|
|
31
31
|
* drift. Positional-first: the <role> <n> tokens peel off and the remaining flags go through
|
|
32
|
-
* rejectUnknownArgs (only --json, --last, --stat
|
|
32
|
+
* rejectUnknownArgs (only --json, --last, --stat, --patch, --transcript, --prompt, --grep,
|
|
33
|
+
* --result, --model), so a listing flag such as --role is refused.
|
|
33
34
|
* Arity is gated before the ready-repo check so a malformed call fails with its usage in any
|
|
34
|
-
* directory;
|
|
35
|
+
* directory; cmdRunDetail owns the value validation and the --last/numbered rivals. */
|
|
35
36
|
async function runTrailCommand(root, args) {
|
|
36
|
-
const {
|
|
37
|
-
const {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
const { cmdRunDetail, RUN_DETAIL_USAGE } = await import("./run/run-detail.js");
|
|
38
|
+
const { HISTORY_GREP_VALUE_ERROR, HISTORY_RESULT_VALUE_ERROR, HISTORY_MODEL_VALUE_ERROR } = await import("./history/history.js");
|
|
39
|
+
// parseGrepFlag removes the pattern token, so `--grep two` cannot read "two" as a positional.
|
|
40
|
+
const { rest: ungrepped, pattern: grep } = parseGrepFlag(args, HISTORY_GREP_VALUE_ERROR);
|
|
41
|
+
const { rest: unresulted, raw: result } = parseResultFlag(ungrepped, HISTORY_RESULT_VALUE_ERROR);
|
|
42
|
+
const { rest: unmodeled, raw: model } = parseModelFlag(unresulted, HISTORY_MODEL_VALUE_ERROR);
|
|
43
|
+
const { positionals, rest } = peelPositionals(unmodeled);
|
|
44
|
+
rejectUnknownArgs("history", rest, [JSON_FLAG, LAST_FLAG, STAT_FLAG, PATCH_FLAG, TRANSCRIPT_FLAG, PROMPT_FLAG, { names: ["--grep"] }, { names: ["--result"] }, { names: ["--model"] }]);
|
|
45
|
+
if ((positionals.length < 1 && !rest.includes("--last")) || positionals.length > 2)
|
|
46
|
+
fail(`usage: ${RUN_DETAIL_USAGE}`);
|
|
41
47
|
await requireReadyRepo(root);
|
|
42
|
-
await
|
|
48
|
+
await cmdRunDetail(root, positionals, rest.includes("--json"), {
|
|
49
|
+
last: rest.includes("--last"),
|
|
50
|
+
stat: rest.includes("--stat"),
|
|
51
|
+
patch: rest.includes("--patch"),
|
|
52
|
+
transcript: rest.includes("--transcript"),
|
|
53
|
+
prompt: rest.includes("--prompt"),
|
|
54
|
+
grep,
|
|
55
|
+
result,
|
|
56
|
+
model,
|
|
57
|
+
});
|
|
43
58
|
}
|
|
44
59
|
async function main() {
|
|
45
60
|
const [, , command, ...args] = process.argv;
|
|
@@ -72,16 +87,16 @@ async function main() {
|
|
|
72
87
|
const root = (await repoToplevel(process.cwd())) ?? process.cwd();
|
|
73
88
|
switch (command) {
|
|
74
89
|
case "init":
|
|
75
|
-
// Imported lazily: cli/cli-
|
|
90
|
+
// Imported lazily: cli/cli-start.ts pulls the whole team-booting subgraph (orchestrator,
|
|
76
91
|
// supervisor, redeploy, launch-services — the run path's machinery), and every other
|
|
77
92
|
// command's spawn pays module compilation for it unless the dispatch defers that load
|
|
78
|
-
// to the commands that actually boot or serve a team.
|
|
79
|
-
await (await import("./cli/cli-
|
|
93
|
+
// to the commands that actually boot or serve a team. cli-init.ts stays light for init.
|
|
94
|
+
await (await import("./cli/cli-init.js")).cmdInit(root, args);
|
|
80
95
|
break;
|
|
81
96
|
case "start":
|
|
82
97
|
case "run":
|
|
83
|
-
rejectUnknownArgs("start", args,
|
|
84
|
-
await (await import("./cli/cli-
|
|
98
|
+
rejectUnknownArgs("start", args, START_FLAG_SPECS);
|
|
99
|
+
await (await import("./cli/cli-start.js")).cmdStart(root, args);
|
|
85
100
|
break;
|
|
86
101
|
case "tui":
|
|
87
102
|
rejectUnknownArgs("tui", args, []);
|
|
@@ -108,7 +123,10 @@ async function main() {
|
|
|
108
123
|
// args.
|
|
109
124
|
if (args.length > 0) {
|
|
110
125
|
if (args[0] !== "get" && args[0] !== "set" && args[0] !== "unset")
|
|
111
|
-
|
|
126
|
+
// A near-miss subcommand word gets the same typoSuffix `completion <shell>` and
|
|
127
|
+
// `logs --type` give, so `config gte` reads "did you mean `get`?" while a far-off
|
|
128
|
+
// word (`show`) and a flag (`--anything`) stay hint-free.
|
|
129
|
+
fail(`${CONFIG_USAGE}${typoSuffix(args[0] ?? "", ["get", "set", "unset"])}`);
|
|
112
130
|
if (args[0] === "get" && args.length !== 2)
|
|
113
131
|
fail("usage: tumwater config get <key>");
|
|
114
132
|
if (args[0] === "set" && args.length !== 3)
|
|
@@ -124,7 +142,8 @@ async function main() {
|
|
|
124
142
|
}
|
|
125
143
|
case "report": {
|
|
126
144
|
// No requireReadyRepo gate: the report aggregates files that degrade to zeros when
|
|
127
|
-
// missing, so it runs (and prints an all-zero window) in any directory
|
|
145
|
+
// missing, so it runs (and prints an all-zero window) in any directory; the team-workspace
|
|
146
|
+
// refusal still applies.
|
|
128
147
|
rejectUnknownArgs("report", args, [
|
|
129
148
|
{
|
|
130
149
|
names: ["--days"],
|
|
@@ -138,9 +157,12 @@ async function main() {
|
|
|
138
157
|
},
|
|
139
158
|
{ names: ["--failures"] },
|
|
140
159
|
sinceFlagSpec("report --since"),
|
|
160
|
+
untilFlagSpec("report --until"),
|
|
141
161
|
JSON_FLAG,
|
|
142
162
|
ROLE_FLAG,
|
|
163
|
+
FOLLOW_FLAG,
|
|
143
164
|
]);
|
|
165
|
+
refuseTeamWorkspace(root);
|
|
144
166
|
// --since is handled before the day-shape reads: it is a rival shape (totals over a
|
|
145
167
|
// trailing window vs a series over whole days), not a modifier of either.
|
|
146
168
|
await (await import("./report/report.js")).cmdReport(root, args);
|
|
@@ -149,23 +171,95 @@ async function main() {
|
|
|
149
171
|
case "doctor": {
|
|
150
172
|
// No requireReadyRepo gate: doctor's job is to report WHY the environment isn't ready,
|
|
151
173
|
// so it must run outside a git repo and print fail lines rather than throwing.
|
|
152
|
-
|
|
174
|
+
const { positionals: checkNames, rest: doctorFlags } = peelPositionals(args);
|
|
175
|
+
rejectUnknownArgs("doctor", doctorFlags, [JSON_FLAG, REPROBE_FLAG, PROBLEMS_FLAG, KILL_ORPHANS_FLAG, COMMIT_BACKLOG_FLAG, RESYNC_INSTALL_FLAG, DROP_BROKEN_PINS_FLAG, RETIRE_LEFTOVERS_FLAG]);
|
|
176
|
+
const only = parseDoctorChecks(checkNames);
|
|
153
177
|
const { runDoctor } = await import("./doctor/doctor.js");
|
|
178
|
+
let orphanKill;
|
|
179
|
+
if (args.includes("--kill-orphans")) {
|
|
180
|
+
const { killOrphans, orphanKillLines } = await import("./doctor/doctor-orphans.js");
|
|
181
|
+
orphanKill = await killOrphans(root);
|
|
182
|
+
// The text form prints the kill before the report; --json keeps stdout one document.
|
|
183
|
+
if (!args.includes("--json")) {
|
|
184
|
+
for (const line of orphanKillLines(orphanKill))
|
|
185
|
+
say(line);
|
|
186
|
+
say("");
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
let backlogCommit;
|
|
190
|
+
if (args.includes("--commit-backlog")) {
|
|
191
|
+
const { commitUncommittedBacklog, backlogCommitLines } = await import("./doctor/doctor-backlog.js");
|
|
192
|
+
const { config } = loadConfigSafe(root); // the report's own config, so base resolution and the off-base skip match the `uncommitted backlog` row's
|
|
193
|
+
backlogCommit = await commitUncommittedBacklog(root, config);
|
|
194
|
+
// The text form prints the commits before the report; --json keeps stdout one document.
|
|
195
|
+
if (!args.includes("--json")) {
|
|
196
|
+
for (const line of backlogCommitLines(backlogCommit))
|
|
197
|
+
say(line);
|
|
198
|
+
say("");
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
let resyncInstall;
|
|
202
|
+
if (args.includes("--resync-install")) {
|
|
203
|
+
const { resyncRootInstall, rootInstallResyncLines } = await import("./build/dep-install.js");
|
|
204
|
+
resyncInstall = await resyncRootInstall(root);
|
|
205
|
+
// The text form prints the re-sync outcome before the report; --json keeps stdout one
|
|
206
|
+
// document, carrying the result as `resyncInstall` on the payload.
|
|
207
|
+
if (!args.includes("--json")) {
|
|
208
|
+
for (const line of rootInstallResyncLines(resyncInstall))
|
|
209
|
+
say(line);
|
|
210
|
+
say("");
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
let brokenPinDrop;
|
|
214
|
+
if (args.includes("--drop-broken-pins")) {
|
|
215
|
+
const { dropBrokenPins, brokenPinDropLines } = await import("./doctor/doctor-contention-checks.js");
|
|
216
|
+
brokenPinDrop = await dropBrokenPins(root);
|
|
217
|
+
// The text form prints the drop before the report; --json keeps stdout one document,
|
|
218
|
+
// carrying the result as `brokenPinDrop` on the payload.
|
|
219
|
+
if (!args.includes("--json")) {
|
|
220
|
+
const lines = brokenPinDropLines(brokenPinDrop);
|
|
221
|
+
for (const line of lines)
|
|
222
|
+
say(line);
|
|
223
|
+
if (lines.length > 0)
|
|
224
|
+
say("");
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
let leftoverRetire;
|
|
228
|
+
if (args.includes("--retire-leftovers")) {
|
|
229
|
+
const { retireLeftovers, leftoverRetireLines } = await import("./doctor/doctor-contention-checks.js");
|
|
230
|
+
const { config } = loadConfigSafe(root); // the row's own config, so the reap and the report agree on what is retirable
|
|
231
|
+
leftoverRetire = await retireLeftovers(root, config);
|
|
232
|
+
// The text form prints the retire lines before the report; --json keeps stdout one
|
|
233
|
+
// document, carrying the result as `leftoverRetire` on the payload.
|
|
234
|
+
if (!args.includes("--json")) {
|
|
235
|
+
const lines = leftoverRetireLines(leftoverRetire);
|
|
236
|
+
for (const line of lines)
|
|
237
|
+
say(line);
|
|
238
|
+
if (lines.length > 0)
|
|
239
|
+
say("");
|
|
240
|
+
}
|
|
241
|
+
}
|
|
154
242
|
const { renderDoctor, renderDoctorProblems, problemsOnly } = await import("./doctor/doctor-render.js");
|
|
155
243
|
// The terminal probes unclaimed-verified models by default (7-day cache); --reprobe
|
|
156
244
|
// forces every claimed model. The TUI/GUI call runDoctor with the default "never", so a
|
|
157
245
|
// refresh never spends a model call.
|
|
158
246
|
const probeImages = args.includes("--reprobe") ? "all" : "missing";
|
|
159
|
-
const report = await runDoctor(root, undefined, undefined, probeImages);
|
|
247
|
+
const report = await runDoctor(root, undefined, undefined, probeImages, only);
|
|
160
248
|
// --json prints the collector's own payload (the DoctorReport object), not a re-parse of
|
|
161
249
|
// the render — the `report --json` precedent. The exit-code contract below holds in both
|
|
162
250
|
// forms: 1 when any check fails, 0 otherwise; warnings never fail the exit.
|
|
163
251
|
// --problems keeps the full report for the render (it counts the hidden ok rows) and
|
|
164
252
|
// hands --json the filtered one; the exit code below reads the full report either way.
|
|
253
|
+
// --kill-orphans adds the kill result to the JSON object as `orphanKill`.
|
|
254
|
+
const withKill = (payload) => (orphanKill === undefined ? payload : { ...payload, orphanKill });
|
|
255
|
+
const withBacklogCommit = (payload) => backlogCommit === undefined ? withKill(payload) : withKill({ ...payload, backlogCommit });
|
|
256
|
+
const withResyncInstall = (payload) => resyncInstall === undefined ? withBacklogCommit(payload) : withBacklogCommit({ ...payload, resyncInstall });
|
|
257
|
+
const withBrokenPinDrop = (payload) => brokenPinDrop === undefined ? withResyncInstall(payload) : withResyncInstall({ ...payload, brokenPinDrop });
|
|
258
|
+
const withLeftoverRetire = (payload) => leftoverRetire === undefined ? withBrokenPinDrop(payload) : withBrokenPinDrop({ ...payload, leftoverRetire });
|
|
165
259
|
if (args.includes("--problems"))
|
|
166
|
-
sayJsonOrRender(args, args.includes("--json") ? problemsOnly(report) : report, renderDoctorProblems);
|
|
260
|
+
sayJsonOrRender(args, args.includes("--json") ? withLeftoverRetire(problemsOnly(report)) : report, renderDoctorProblems);
|
|
167
261
|
else
|
|
168
|
-
sayJsonOrRender(args, report, renderDoctor);
|
|
262
|
+
sayJsonOrRender(args, args.includes("--json") ? withLeftoverRetire(report) : report, renderDoctor);
|
|
169
263
|
if (report.checks.some((c) => c.level === "fail"))
|
|
170
264
|
process.exitCode = 1; // Warnings never fail the exit.
|
|
171
265
|
break;
|
|
@@ -173,15 +267,18 @@ async function main() {
|
|
|
173
267
|
case "logs": {
|
|
174
268
|
const { cmdLogs, GREP_VALUE_ERROR, TYPE_VALUE_ERROR } = await import("./cli/log-commands.js");
|
|
175
269
|
rejectUnknownArgs("logs", args, [
|
|
176
|
-
|
|
270
|
+
FOLLOW_FLAG,
|
|
177
271
|
N_FLAG,
|
|
178
272
|
sinceFlagSpec("logs --since"),
|
|
273
|
+
untilFlagSpec("logs --until"),
|
|
179
274
|
grepFlagSpec(GREP_VALUE_ERROR),
|
|
180
275
|
typeFlagSpec(TYPE_VALUE_ERROR),
|
|
181
276
|
JSON_FLAG,
|
|
182
277
|
COUNT_FLAG,
|
|
278
|
+
NOTABLE_FLAG,
|
|
183
279
|
ROLE_FLAG,
|
|
184
|
-
|
|
280
|
+
LOGS_TRANSCRIPT_FLAG,
|
|
281
|
+
PROMPT_FLAG,
|
|
185
282
|
]);
|
|
186
283
|
await requireReadyRepo(root);
|
|
187
284
|
await cmdLogs(root, args);
|
|
@@ -193,16 +290,20 @@ async function main() {
|
|
|
193
290
|
await (await import("./cli/notify-command.js")).cmdNotify(root, args);
|
|
194
291
|
break;
|
|
195
292
|
case "history": {
|
|
196
|
-
const { cmdHistory, HISTORY_GREP_VALUE_ERROR, HISTORY_RESULT_VALUE_ERROR } = await import("./history/history.js");
|
|
293
|
+
const { cmdHistory, HISTORY_GREP_VALUE_ERROR, HISTORY_RESULT_VALUE_ERROR, HISTORY_MODEL_VALUE_ERROR, HISTORY_BY_VALUE_ERROR } = await import("./history/history.js");
|
|
197
294
|
if (historyTrailForm(args)) {
|
|
198
295
|
await runTrailCommand(root, args);
|
|
199
296
|
break;
|
|
200
297
|
}
|
|
201
298
|
rejectUnknownArgs("history", args, [
|
|
202
299
|
N_FLAG,
|
|
300
|
+
FOLLOW_FLAG,
|
|
203
301
|
sinceFlagSpec("history --since"),
|
|
302
|
+
untilFlagSpec("history --until"),
|
|
204
303
|
grepFlagSpec(HISTORY_GREP_VALUE_ERROR),
|
|
205
304
|
resultFlagSpec(HISTORY_RESULT_VALUE_ERROR),
|
|
305
|
+
modelFlagSpec(HISTORY_MODEL_VALUE_ERROR),
|
|
306
|
+
byFlagSpec(HISTORY_BY_VALUE_ERROR),
|
|
206
307
|
JSON_FLAG,
|
|
207
308
|
ROLE_FLAG,
|
|
208
309
|
SUMMARY_FLAG,
|
|
@@ -220,17 +321,25 @@ async function main() {
|
|
|
220
321
|
case "queue":
|
|
221
322
|
await (await import("./cli/cli-query-commands.js")).cmdQueue(root, args);
|
|
222
323
|
break;
|
|
324
|
+
case "reviews":
|
|
325
|
+
await (await import("./cli/cli-review-commands.js")).cmdReviews(root, args);
|
|
326
|
+
break;
|
|
223
327
|
case "backlog":
|
|
224
328
|
await (await import("./cli/cli-query-commands.js")).cmdBacklog(root, args);
|
|
225
329
|
break;
|
|
330
|
+
case "quarantine":
|
|
331
|
+
await (await import("./cli/cli-query-commands.js")).cmdQuarantine(root, args);
|
|
332
|
+
break;
|
|
226
333
|
case "archive":
|
|
227
|
-
rejectUnknownArgs("archive", args, []);
|
|
334
|
+
rejectUnknownArgs("archive", args, [JSON_FLAG]);
|
|
228
335
|
await requireReadyRepo(root);
|
|
229
|
-
await (await import("./backlog/backlog-cli.js")).cmdArchive(root);
|
|
336
|
+
await (await import("./backlog/backlog-cli.js")).cmdArchive(root, args);
|
|
230
337
|
break;
|
|
231
338
|
case "bug": {
|
|
232
|
-
const { OPERATOR_FILE_KINDS, bugWakeText, cancelBug,
|
|
233
|
-
const {
|
|
339
|
+
const { OPERATOR_FILE_KINDS, bugWakeText, cancelBug, editBug, editBugFromText, fileBug, fileBugFromText } = await import("./backlog/backlog-write.js");
|
|
340
|
+
const { moveEntryToTop, releaseEntry } = await import("./backlog/backlog-release.js");
|
|
341
|
+
const { bugTitleOf, cancelAndAnnounce, releaseAndAnnounce, topAndAnnounce, editAndAnnounce, fileAndAnnounce } = await import("./backlog/backlog-cli.js");
|
|
342
|
+
const { listAndAnnounce, showAndAnnounce } = await import("./backlog/backlog-cli-view.js");
|
|
234
343
|
const usage = 'tumwater bug "<symptom>"';
|
|
235
344
|
if (args.includes("--show")) {
|
|
236
345
|
showAndAnnounce(root, args, "bug");
|
|
@@ -260,8 +369,10 @@ async function main() {
|
|
|
260
369
|
break;
|
|
261
370
|
}
|
|
262
371
|
case "plan": {
|
|
263
|
-
const { OPERATOR_FILE_KINDS, cancelPlan,
|
|
264
|
-
const {
|
|
372
|
+
const { OPERATOR_FILE_KINDS, cancelPlan, editPlan, editPlanFromText, filePlan, filePlanFromText, planWakeText } = await import("./backlog/backlog-write.js");
|
|
373
|
+
const { moveEntryToTop, releaseEntry } = await import("./backlog/backlog-release.js");
|
|
374
|
+
const { cancelAndAnnounce, releaseAndAnnounce, topAndAnnounce, editAndAnnounce, fileAndAnnounce, planTitleOf } = await import("./backlog/backlog-cli.js");
|
|
375
|
+
const { listAndAnnounce, showAndAnnounce } = await import("./backlog/backlog-cli-view.js");
|
|
265
376
|
const usage = 'tumwater plan "<title>" [body...]';
|
|
266
377
|
if (args.includes("--show")) {
|
|
267
378
|
showAndAnnounce(root, args, "plan");
|
|
@@ -291,13 +402,13 @@ async function main() {
|
|
|
291
402
|
break;
|
|
292
403
|
}
|
|
293
404
|
case "questions":
|
|
294
|
-
await (await import("./cli/
|
|
405
|
+
await (await import("./cli/question-commands.js")).cmdQuestions(root, args);
|
|
295
406
|
break;
|
|
296
407
|
case "brief":
|
|
297
408
|
await (await import("./cli/brief-command.js")).cmdBrief(root, args);
|
|
298
409
|
break;
|
|
299
410
|
case "role":
|
|
300
|
-
await (await import("./cli/cli-
|
|
411
|
+
await (await import("./cli/cli-role-command.js")).cmdRole(root, args);
|
|
301
412
|
break;
|
|
302
413
|
case "prompt":
|
|
303
414
|
// Parse before the gate: prompt's positionals are free-form, so rejectUnknownArgs
|
|
@@ -336,8 +447,8 @@ async function main() {
|
|
|
336
447
|
break;
|
|
337
448
|
}
|
|
338
449
|
case "revert": {
|
|
339
|
-
// Acts immediately on the primary checkout under the primary-worktree lock; the
|
|
340
|
-
//
|
|
450
|
+
// Acts immediately on the primary checkout under the primary-worktree lock; the
|
|
451
|
+
// positionals (a sha, or a run) are validated inside cmdRevert.
|
|
341
452
|
await requireReadyRepo(root);
|
|
342
453
|
await (await import("./operator/operator-commands.js")).cmdRevert(root, args);
|
|
343
454
|
break;
|
|
@@ -362,12 +473,29 @@ async function main() {
|
|
|
362
473
|
say(key);
|
|
363
474
|
break;
|
|
364
475
|
}
|
|
476
|
+
if (args.length === 2 && args[0] === "--config-value") {
|
|
477
|
+
// The vocabulary `config set <key> <value>` completes for a discrete-valued key
|
|
478
|
+
// (boolean/backend/model/thinking); free-form keys print nothing, so a `set` on one
|
|
479
|
+
// completes nothing instead of offering a wrong vocabulary.
|
|
480
|
+
for (const word of configValueWords(args[1]))
|
|
481
|
+
say(word);
|
|
482
|
+
break;
|
|
483
|
+
}
|
|
484
|
+
if (args.length === 1 && args[0] === "--models") {
|
|
485
|
+
for (const m of completionModels(root))
|
|
486
|
+
say(m);
|
|
487
|
+
break;
|
|
488
|
+
}
|
|
365
489
|
const shell = args[0];
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
fail(
|
|
490
|
+
// A wrong word count names the count, not a bad shell. A single near-miss shell word gets
|
|
491
|
+
// the same typoSuffix `logs --type`/`history --result` give, so `completion zs` reads
|
|
492
|
+
// "did you mean `zsh`?" while a far-off value (`tcsh`) stays hint-free.
|
|
493
|
+
if (args.length === 0)
|
|
494
|
+
fail("completion needs a shell: bash, zsh, or fish");
|
|
495
|
+
if (args.length > 1)
|
|
496
|
+
fail(`completion takes one shell: bash, zsh, or fish (got ${args.length} words: ${args.join(" ")})`);
|
|
497
|
+
if (!SUPPORTED_SHELLS.includes(shell))
|
|
498
|
+
fail(`completion takes one shell: bash, zsh, or fish${gotSuffix(shell)}${typoSuffix(shell ?? "", SUPPORTED_SHELLS)}`);
|
|
371
499
|
say(completionScript(shell));
|
|
372
500
|
break;
|
|
373
501
|
}
|
|
@@ -400,7 +528,7 @@ async function main() {
|
|
|
400
528
|
const topic = helpTopic(name);
|
|
401
529
|
if (topic === null) {
|
|
402
530
|
const suggestion = suggestCommand(name);
|
|
403
|
-
fail(`no help topic: ${name}${didYouMean(suggestion)} (try \`tumwater help\` for the full command list)`);
|
|
531
|
+
fail(`no help topic: ${JSON.stringify(name)}${didYouMean(suggestion)} (try \`tumwater help\` for the full command list)`);
|
|
404
532
|
}
|
|
405
533
|
say(topic);
|
|
406
534
|
}
|
|
@@ -411,7 +539,7 @@ async function main() {
|
|
|
411
539
|
}
|
|
412
540
|
default: {
|
|
413
541
|
const suggestion = suggestCommand(command);
|
|
414
|
-
fail(`unknown command: ${command}${didYouMean(suggestion)} (try \`tumwater help\`)`);
|
|
542
|
+
fail(`unknown command: ${JSON.stringify(command)}${didYouMean(suggestion)} (try \`tumwater help\`)`);
|
|
415
543
|
}
|
|
416
544
|
}
|
|
417
545
|
}
|