tumwater 0.1.2 → 0.2.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 +74 -108
- package/dist/build-info.json +2 -2
- package/dist/src/agent/agent-args.js +24 -0
- package/dist/src/agent/agent-backend.js +2 -2
- package/dist/src/agent/backend-traits.js +60 -0
- package/dist/src/agent/claude/claude-account.js +61 -8
- package/dist/src/agent/claude/claude-args.js +12 -15
- package/dist/src/agent/claude/claude-backend.js +51 -41
- package/dist/src/agent/claude/claude-env.js +7 -3
- package/dist/src/agent/claude/claude-stream.js +7 -16
- package/dist/src/agent/image-input.js +107 -0
- package/dist/src/agent/notes-shim.js +41 -0
- package/dist/src/agent/opencode/opencode-args.js +61 -0
- package/dist/src/agent/opencode/opencode-backend.js +255 -0
- package/dist/src/agent/opencode/opencode-catalog.js +184 -0
- package/dist/src/agent/opencode/opencode-config.js +188 -0
- package/dist/src/agent/opencode/opencode-env.js +36 -0
- package/dist/src/agent/opencode/opencode-session-id.js +37 -0
- package/dist/src/agent/opencode/opencode-stream.js +278 -0
- package/dist/src/agent/role-notes-write.js +16 -32
- package/dist/src/agent/run-agent.js +95 -18
- package/dist/src/agent/session-ledger.js +57 -1
- package/dist/src/backlog/backlog-archive.js +296 -0
- package/dist/src/backlog/backlog-cli.js +573 -0
- package/dist/src/backlog/backlog-commit.js +31 -0
- package/dist/src/backlog/backlog-eligibility.js +357 -33
- package/dist/src/backlog/backlog-md.js +98 -18
- package/dist/src/backlog/backlog-render.js +62 -11
- package/dist/src/backlog/backlog-structure.js +261 -58
- package/dist/src/backlog/backlog-write.js +197 -319
- package/dist/src/backlog/backlog.js +113 -16
- package/dist/src/backlog/question-answer.js +339 -0
- package/dist/src/baseline/main-baseline.js +75 -88
- package/dist/src/baseline/main-red.js +38 -30
- package/dist/src/brief/brief-edit.js +43 -0
- package/dist/src/{brief.js → brief/brief.js} +66 -10
- package/dist/src/budget/budget.js +32 -15
- package/dist/src/budget/fallback-breaker.js +8 -7
- package/dist/src/build/build-check-detect.js +8 -7
- package/dist/src/build/build-check-events.js +68 -22
- package/dist/src/build/build-check-report.js +22 -23
- package/dist/src/build/build-check-scoped.js +51 -62
- package/dist/src/build/build-check.js +15 -14
- package/dist/src/build/build-info.js +2 -1
- package/dist/src/build/build-stage.js +23 -22
- package/dist/src/build/dep-install.js +22 -21
- package/dist/src/build/host-sleep.js +1 -1
- package/dist/src/change/change-data.js +25 -14
- package/dist/src/change/change-render.js +11 -10
- package/dist/src/cli/brief-command.js +58 -0
- package/dist/src/cli/cli-args.js +25 -5
- package/dist/src/cli/cli-command-args.js +46 -14
- package/dist/src/cli/cli-editor.js +64 -0
- package/dist/src/cli/cli-flag-specs.js +54 -16
- package/dist/src/cli/cli-marker-commands.js +13 -10
- package/dist/src/cli/cli-output.js +9 -0
- package/dist/src/cli/cli-query-commands.js +458 -49
- package/dist/src/cli/cli-repo-gate.js +1 -1
- package/dist/src/cli/cli-run.js +208 -59
- package/dist/src/cli/completion.js +207 -0
- package/dist/src/cli/config-commands.js +14 -11
- package/dist/src/cli/help.js +305 -110
- package/dist/src/cli/log-commands.js +127 -21
- package/dist/src/cli/notify-command.js +33 -18
- package/dist/src/cli/question-commands.js +29 -224
- package/dist/src/cli.js +121 -40
- package/dist/src/collections.js +66 -16
- package/dist/src/concurrency/check-permit.js +16 -17
- package/dist/src/concurrency/lock.js +6 -5
- package/dist/src/concurrency/provider-lease.js +69 -0
- package/dist/src/concurrency/semaphore.js +258 -36
- package/dist/src/config/config-aliases.js +106 -0
- package/dist/src/config/config-editable-keys.js +3 -3
- package/dist/src/config/config-fallbacks.js +379 -0
- package/dist/src/config/config-field-checks.js +19 -10
- package/dist/src/config/config-keys.js +160 -0
- package/dist/src/config/config-live.js +64 -12
- package/dist/src/config/config-schema.js +34 -22
- package/dist/src/config/config-validation.js +132 -48
- package/dist/src/config/config-views.js +119 -337
- package/dist/src/config/config-write.js +138 -177
- package/dist/src/config/config.js +60 -49
- package/dist/src/config/model-selector.js +28 -2
- package/dist/src/doctor/doctor-backlog.js +546 -49
- package/dist/src/doctor/doctor-checks.js +534 -56
- package/dist/src/doctor/doctor-holds.js +193 -53
- package/dist/src/doctor/doctor-install.js +30 -0
- package/dist/src/doctor/doctor-launch-services.js +2 -2
- package/dist/src/doctor/doctor-model-checks.js +549 -157
- package/dist/src/doctor/doctor-notify.js +27 -0
- package/dist/src/doctor/doctor-orphans.js +10 -9
- package/dist/src/doctor/doctor-render.js +36 -9
- package/dist/src/doctor/doctor.js +80 -22
- package/dist/src/doctor/image-probe.js +244 -0
- package/dist/src/doctor/probe.js +64 -0
- package/dist/src/events/event-format.js +226 -102
- package/dist/src/events/event-labels.js +116 -0
- package/dist/src/events/event-legacy.js +140 -0
- package/dist/src/events/event-read.js +58 -22
- package/dist/src/events/event-window.js +27 -7
- package/dist/src/events/events.js +18 -14
- package/dist/src/events/notify.js +137 -38
- package/dist/src/failure/failure-cluster.js +12 -9
- package/dist/src/failure/failure-data.js +96 -48
- package/dist/src/failure/failure-render.js +69 -58
- package/dist/src/failure/failure-state-change.js +33 -26
- package/dist/src/failure/time-spend.js +79 -59
- package/dist/src/files/file-queue.js +16 -4
- package/dist/src/files/files.js +18 -6
- package/dist/src/files/json-object.js +18 -3
- package/dist/src/files/tail.js +1 -1
- package/dist/src/gates/bootstrap-gates.js +5 -4
- package/dist/src/gates/budget-gates.js +250 -34
- package/dist/src/gates/disk-gate.js +18 -18
- package/dist/src/gates/gate-polls.js +117 -69
- package/dist/src/gates/maintenance-quota.js +29 -29
- package/dist/src/gates/pause-gates.js +11 -11
- package/dist/src/gates/readiness.js +2 -1
- package/dist/src/gates/role-cap-gates.js +16 -21
- package/dist/src/gates/startup-gate.js +75 -31
- package/dist/src/gates/streak-gate.js +18 -16
- package/dist/src/git/commit-message.js +12 -5
- package/dist/src/git/git-diff.js +16 -16
- package/dist/src/git/git-run.js +8 -8
- package/dist/src/git/git.js +86 -55
- package/dist/src/git/slots-state.js +14 -5
- package/dist/src/git/worktree-pool.js +25 -18
- package/dist/src/git/worktree-use.js +15 -14
- package/dist/src/git/worktree.js +18 -18
- package/dist/src/gui/gui-args.js +10 -14
- package/dist/src/gui/gui-backlog-commands.js +273 -0
- package/dist/src/gui/gui-command.js +1 -1
- package/dist/src/gui/gui-config-commands.js +440 -0
- package/dist/src/gui/gui-endpoint-commands.js +16 -669
- package/dist/src/gui/gui-endpoint-common.js +49 -0
- package/dist/src/gui/gui-endpoints.js +116 -56
- package/dist/src/gui/gui-operator-commands.js +145 -0
- package/dist/src/gui/gui-prompt-commands.js +234 -0
- package/dist/src/gui/gui-server.js +28 -8
- package/dist/src/gui/gui-team-commands.js +164 -0
- package/dist/src/gui/http-body.js +17 -1
- package/dist/src/history/history-data.js +194 -98
- package/dist/src/history/history-render.js +76 -0
- package/dist/src/history/history.js +37 -58
- package/dist/src/inbox/inbox-attachments.js +75 -14
- package/dist/src/inbox/inbox-cancel.js +7 -5
- package/dist/src/inbox/inbox-edit.js +1 -1
- package/dist/src/inbox/inbox-submit.js +44 -4
- package/dist/src/inbox/inbox-top.js +83 -0
- package/dist/src/inbox/inbox.js +8 -2
- package/dist/src/inbox/pending-prompt.js +1 -1
- package/dist/src/inbox/prompt-commands.js +170 -80
- package/dist/src/init/init-templates.js +16 -14
- package/dist/src/init/init.js +12 -11
- package/dist/src/loop/leftover.js +28 -27
- package/dist/src/loop/loop-fallback.js +2 -2
- package/dist/src/loop/loop-pi.js +106 -52
- package/dist/src/loop/loop-recovery.js +15 -15
- package/dist/src/loop/loop-state.js +139 -13
- package/dist/src/loop/loop-tick.js +194 -86
- package/dist/src/loop/loop.js +72 -36
- package/dist/src/loop/model-fallback.js +3 -2
- package/dist/src/loop/revision.js +16 -7
- package/dist/src/merge/backlog-conflicts.js +155 -0
- package/dist/src/{landing/landing-batch.js → merge/merge-batch.js} +90 -89
- package/dist/src/{landing/landing-check-failures.js → merge/merge-check-failures.js} +87 -41
- package/dist/src/{landing/landing-core.js → merge/merge-core.js} +119 -95
- package/dist/src/{landing/landing-diff.js → merge/merge-diff.js} +1 -1
- package/dist/src/merge/merge-drain.js +212 -0
- package/dist/src/{landing/landing-git.js → merge/merge-git.js} +94 -21
- package/dist/src/merge/merge-pipeline.js +99 -0
- package/dist/src/{landing/landing-questions.js → merge/merge-questions.js} +9 -6
- package/dist/src/merge/merge-queue-render.js +75 -0
- package/dist/src/{landing/landing-queue.js → merge/merge-queue.js} +95 -36
- package/dist/src/merge/merge-slot.js +340 -0
- package/dist/src/{landing/landing-stack.js → merge/merge-stack.js} +66 -62
- package/dist/src/{landing/landing-merge.js → merge/merge-to-main.js} +72 -62
- package/dist/src/merge/merge-verify.js +212 -0
- package/dist/src/notes-cli.js +97 -0
- package/dist/src/operator/operator-commands.js +154 -47
- package/dist/src/operator/operator-intent.js +87 -49
- package/dist/src/operator/operator-requests.js +52 -52
- package/dist/src/operator/retire.js +62 -27
- package/dist/src/operator/revert.js +71 -0
- package/dist/src/orchestrator/orchestrator-launch.js +74 -14
- package/dist/src/orchestrator/orchestrator-scheduling.js +129 -44
- package/dist/src/orchestrator/orchestrator.js +158 -100
- package/dist/src/orchestrator/retention.js +29 -12
- package/dist/src/orchestrator/startup-migration-steps.js +51 -0
- package/dist/src/orchestrator/startup-migrations.js +40 -0
- package/dist/src/paths.js +128 -37
- package/dist/src/pi/pi-args.js +26 -3
- package/dist/src/pi/pi-backend.js +15 -23
- package/dist/src/pi/pi-bin.js +18 -12
- package/dist/src/pi/pi-event-line.js +17 -1
- package/dist/src/pi/pi-failure.js +87 -0
- package/dist/src/pi/pi-models.js +126 -38
- package/dist/src/pi/pi-run-result.js +1 -1
- package/dist/src/pi/pi-settings.js +86 -0
- package/dist/src/pi/pi-stream.js +131 -107
- package/dist/src/pi/pi-watchdogs.js +47 -43
- package/dist/src/pi-extension/bounded-output.js +20 -5
- package/dist/src/pi-extension/context-budget.js +3 -1
- package/dist/src/pi-extension/context-shake.js +54 -21
- package/dist/src/pi-extension/context-window.js +37 -0
- package/dist/src/pi-extension/full-output.js +2 -2
- package/dist/src/pi-extension/strip-thinking.js +80 -0
- package/dist/src/pi-extension/trim-system-prompt.js +38 -0
- package/dist/src/process/launch-services.js +11 -10
- package/dist/src/process/process-table.js +1 -1
- package/dist/src/process/process.js +7 -7
- package/dist/src/process/run-marker.js +29 -6
- package/dist/src/process/supervisor.js +47 -25
- package/dist/src/prompt/dialect.js +30 -10
- package/dist/src/prompt/gate-prompts.js +170 -67
- package/dist/src/prompt/principles.js +1 -0
- package/dist/src/prompt/prompt-followup.js +35 -20
- package/dist/src/prompt/prompt.js +249 -91
- package/dist/src/redeploy/redeploy-policy.js +10 -10
- package/dist/src/redeploy/redeploy.js +6 -4
- package/dist/src/redeploy/redeployer.js +63 -55
- package/dist/src/redeploy/self-reload.js +1 -1
- package/dist/src/report/report-data.js +264 -255
- package/dist/src/report/report-fold.js +353 -0
- package/dist/src/report/report-render.js +270 -33
- package/dist/src/request-timeouts.js +1 -1
- package/dist/src/review/known-flakes.js +34 -14
- package/dist/src/review/protected-edits.js +33 -0
- package/dist/src/review/review-followup.js +16 -4
- package/dist/src/review/review-precheck.js +14 -13
- package/dist/src/review/review-verdict.js +14 -0
- package/dist/src/review/review.js +81 -39
- package/dist/src/review/secret-leaks.js +127 -0
- package/dist/src/review/suite-rerun.js +4 -3
- package/dist/src/roles/legacy-role-ids.js +21 -0
- package/dist/src/roles/legacy-role-migration.js +62 -0
- package/dist/src/roles/loop-ids.js +19 -2
- package/dist/src/roles/role-catalog.js +125 -135
- package/dist/src/roles/role-guidance.js +64 -31
- package/dist/src/roles/role-labels.js +10 -0
- package/dist/src/roles/role-render.js +194 -18
- package/dist/src/roles/role-view.js +216 -17
- package/dist/src/roles/roles.js +18 -15
- package/dist/src/scheduling/backoff.js +29 -19
- package/dist/src/scheduling/claims.js +25 -9
- package/dist/src/scheduling/once-round.js +11 -11
- package/dist/src/scheduling/quiet-hours.js +25 -32
- package/dist/src/scheduling/scheduling.js +43 -24
- package/dist/src/scheduling/{work-landed-cache.js → work-merged-cache.js} +14 -14
- package/dist/src/status/status-budget.js +115 -0
- package/dist/src/status/status-data.js +77 -184
- package/dist/src/status/status-inbox.js +47 -0
- package/dist/src/status/status-install.js +27 -0
- package/dist/src/status/status-polls.js +68 -33
- package/dist/src/status/status-roles.js +105 -0
- package/dist/src/{fleet → team}/error-storm.js +10 -10
- package/dist/src/{fleet → team}/failure-spread.js +8 -8
- package/dist/src/{fleet → team}/orchestrator-info.js +86 -18
- package/dist/src/{fleet → team}/reclaim.js +5 -4
- package/dist/src/{fleet/fleet-hold.js → team/team-hold.js} +55 -21
- package/dist/src/{fleet/fleet-polls.js → team/team-polls.js} +116 -65
- package/dist/src/{fleet/fleet-state.js → team/team-state.js} +62 -37
- package/dist/src/text/datetime.js +42 -2
- package/dist/src/text/format.js +10 -1
- package/dist/src/text/markdown.js +1 -1
- package/dist/src/text/phrases.js +35 -34
- package/dist/src/text/suggest.js +3 -2
- package/dist/src/text/text.js +2 -2
- package/dist/src/tick/qa-coverage.js +2 -1
- package/dist/src/tick/stage-check.js +147 -26
- package/dist/src/tick/telemetry-digest.js +10 -9
- package/dist/src/tick/tick-apply.js +101 -72
- package/dist/src/tick/tick-detail-data.js +32 -18
- package/dist/src/tick/tick-detail.js +62 -21
- package/dist/src/tick/tick-finalize.js +29 -22
- package/dist/src/tick/tick-outcome.js +2 -1
- package/dist/src/tick/tick-prompt.js +94 -26
- package/dist/src/tick/tick-resume.js +29 -13
- package/dist/src/tick/tick-stage.js +91 -47
- package/dist/src/tick/tick-timing.js +60 -59
- package/dist/src/tick/tick-usage.js +53 -23
- package/dist/src/tick/tick-verdict.js +76 -10
- package/dist/src/ui/badges.js +118 -67
- package/dist/src/ui/gui/gui-client-boot.js +209 -30
- package/dist/src/ui/gui/gui-client-composer.js +102 -23
- package/dist/src/ui/gui/gui-client-drawer.js +330 -75
- package/dist/src/ui/gui/gui-client-history.js +96 -38
- package/dist/src/ui/gui/gui-client-labels.js +24 -0
- package/dist/src/ui/gui/gui-client-loops.js +94 -52
- package/dist/src/ui/gui/gui-client-markdown.js +1 -0
- package/dist/src/ui/gui/gui-client-model.js +63 -43
- package/dist/src/ui/gui/gui-client-operator.js +129 -39
- package/dist/src/ui/gui/gui-client-pending.js +36 -32
- package/dist/src/ui/gui/gui-client-report.js +339 -46
- package/dist/src/ui/gui/gui-client-settings.js +148 -14
- package/dist/src/ui/gui/gui-client-sound.js +2 -1
- package/dist/src/ui/gui/{gui-client-fleet.js → gui-client-team.js} +196 -72
- package/dist/src/ui/gui/gui-client.js +26 -20
- package/dist/src/ui/gui/gui-page.js +19 -19
- package/dist/src/ui/gui/gui-styles.js +12 -4
- package/dist/src/ui/progress-data.js +5 -3
- package/dist/src/ui/status-model.js +184 -122
- package/dist/src/ui/status-payload.js +119 -32
- package/dist/src/ui/status-render.js +122 -52
- package/dist/src/ui/team-alerts.js +341 -0
- package/dist/src/ui/tick-progress-model.js +14 -14
- package/dist/src/ui/tone.js +11 -8
- package/dist/src/ui/transcript-tail.js +1 -0
- package/dist/src/ui/transcript.js +23 -16
- package/dist/src/ui/tui/tui-app.js +1 -0
- package/dist/src/ui/tui/tui-backlog.js +85 -11
- package/dist/src/ui/tui/tui-editor-answer.js +97 -0
- package/dist/src/ui/tui/tui-editor-attach.js +107 -0
- package/dist/src/ui/tui/tui-editor-brief.js +107 -0
- package/dist/src/ui/tui/tui-editor-budget.js +155 -0
- package/dist/src/ui/tui/tui-editor-ctx.js +1 -0
- package/dist/src/ui/tui/tui-editor-entry.js +230 -0
- package/dist/src/ui/tui/tui-editor-interval.js +122 -0
- package/dist/src/ui/tui/tui-editor-notes.js +114 -0
- package/dist/src/ui/tui/tui-editor-pause.js +266 -0
- package/dist/src/ui/tui/tui-editor-prompt.js +95 -0
- package/dist/src/ui/tui/tui-editor-queued.js +98 -0
- package/dist/src/ui/tui/tui-editor-quiet.js +106 -0
- package/dist/src/ui/tui/tui-editor-retire.js +83 -0
- package/dist/src/ui/tui/tui-editor-search.js +73 -0
- package/dist/src/ui/tui/tui-editor-session.js +97 -0
- package/dist/src/ui/tui/tui-editors.js +152 -0
- package/dist/src/ui/tui/tui-frame.js +67 -55
- package/dist/src/ui/tui/tui-history.js +12 -7
- package/dist/src/ui/tui/tui-input.js +49 -44
- package/dist/src/ui/tui/tui-keymap.js +1 -0
- package/dist/src/ui/tui/tui-keys.js +91 -1704
- package/dist/src/ui/tui/tui-pane-activity.js +99 -0
- package/dist/src/ui/tui/tui-pane-backlog.js +224 -0
- package/dist/src/ui/tui/tui-pane-brief.js +30 -0
- package/dist/src/ui/tui/tui-pane-ctx.js +11 -0
- package/dist/src/ui/tui/tui-pane-health.js +124 -0
- package/dist/src/ui/tui/tui-pane-history.js +316 -0
- package/dist/src/ui/tui/tui-pane-pending.js +74 -0
- package/dist/src/ui/tui/tui-pane-queued.js +143 -0
- package/dist/src/ui/tui/tui-pane-report.js +122 -0
- package/dist/src/ui/tui/tui-pane-session.js +101 -0
- package/dist/src/ui/tui/tui-pane-transcript.js +150 -0
- package/dist/src/ui/tui/tui-pane.js +66 -28
- package/dist/src/ui/tui/tui-panes.js +294 -0
- package/dist/src/ui/tui/tui.js +45 -25
- package/dist/src/verdict/fix-claim.js +141 -38
- package/dist/src/verdict/no-change.js +19 -0
- package/dist/src/verdict/refusal.js +92 -8
- package/dist/src/verdict/reply-contract.js +17 -5
- package/dist/src/version.js +3 -2
- package/package.json +4 -2
- package/dist/src/agent/mcp/role-notes-server.js +0 -160
- package/dist/src/landing/backlog-conflicts.js +0 -116
- package/dist/src/landing/landing-drain.js +0 -204
- package/dist/src/landing/landing-pipeline.js +0 -96
- package/dist/src/landing/landing-queue-render.js +0 -31
- package/dist/src/landing/landing-slot.js +0 -246
- package/dist/src/landing/landing-vetting.js +0 -196
- package/dist/src/pi-extension/role-notes.js +0 -40
- package/dist/src/ui/fleet-alerts.js +0 -174
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// vocabulary: plain
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { readBrief, renderBrief } from "../brief/brief.js";
|
|
4
|
+
import { editInitialPrompt } from "../brief/brief-edit.js";
|
|
5
|
+
import { editInEditor } from "./cli-editor.js";
|
|
6
|
+
import { readPromptFileArg } from "./cli-command-args.js";
|
|
7
|
+
import { JSON_FLAG, rejectUnknownArgs } from "./cli-flag-specs.js";
|
|
8
|
+
import { fail, say, sayJson } from "./cli-output.js";
|
|
9
|
+
import { requireReadyRepo } from "./cli-repo-gate.js";
|
|
10
|
+
/** `brief`'s `--file <path>` flag: the replacement prompt (`-` for stdin). */
|
|
11
|
+
const FILE_SPEC = { names: ["--file"], value: true, valueName: "<path>" };
|
|
12
|
+
/** `brief`'s `--edit` flag: open the prompt in `$VISUAL`/`$EDITOR`. */
|
|
13
|
+
const EDIT_SPEC = { names: ["--edit"] };
|
|
14
|
+
/** `tumwater brief`: show the initial prompt and the file holding it, or with `--file <path>` to
|
|
15
|
+
* replace it (or with `--edit`, open it in the editor) under the checks init applies and commit
|
|
16
|
+
* that one file on main. */
|
|
17
|
+
export async function cmdBrief(root, args) {
|
|
18
|
+
rejectUnknownArgs("brief", args, [JSON_FLAG, FILE_SPEC, EDIT_SPEC]);
|
|
19
|
+
await requireReadyRepo(root);
|
|
20
|
+
const json = args.includes("--json");
|
|
21
|
+
const fileIdx = args.indexOf("--file");
|
|
22
|
+
const edit = args.includes("--edit");
|
|
23
|
+
if (edit && fileIdx >= 0)
|
|
24
|
+
fail("pass --edit or --file, not both");
|
|
25
|
+
const brief = readBrief(root);
|
|
26
|
+
if (brief === null)
|
|
27
|
+
fail("no project brief with a prompt block found — run `tumwater init` first");
|
|
28
|
+
if (fileIdx < 0 && !edit) {
|
|
29
|
+
if (json)
|
|
30
|
+
sayJson({ file: brief.file, prompt: brief.prompt });
|
|
31
|
+
else
|
|
32
|
+
say(renderBrief(brief));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
let text;
|
|
36
|
+
if (edit) {
|
|
37
|
+
text = editInEditor(brief.prompt, "brief.md", "pass --file <path>");
|
|
38
|
+
if (text.trim() === brief.prompt.trim()) {
|
|
39
|
+
if (json)
|
|
40
|
+
sayJson({ file: path.basename(brief.file), changed: false, committed: false });
|
|
41
|
+
else
|
|
42
|
+
say("brief unchanged");
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else
|
|
47
|
+
text = readPromptFileArg(args[fileIdx + 1]);
|
|
48
|
+
const outcome = await editInitialPrompt(root, text);
|
|
49
|
+
if (!outcome.ok)
|
|
50
|
+
fail(outcome.error);
|
|
51
|
+
const { file: name, changed, committed } = outcome;
|
|
52
|
+
if (json)
|
|
53
|
+
sayJson({ file: name, changed, committed });
|
|
54
|
+
else if (!changed)
|
|
55
|
+
say("brief unchanged");
|
|
56
|
+
else
|
|
57
|
+
say(`brief updated in ${name}${committed ? " (committed to main)" : ""}`);
|
|
58
|
+
}
|
package/dist/src/cli/cli-args.js
CHANGED
|
@@ -3,6 +3,7 @@ import { BRANCH_VALUE_ERROR, ROLE_VALUE_ERROR } from "./cli-flag-specs.js";
|
|
|
3
3
|
import { knownRoleIdsCached } from "../config/config.js";
|
|
4
4
|
import { allRoleIds, unknownRoleMessage } from "../roles/roles.js";
|
|
5
5
|
import { gotSuffix, parsePositiveInt } from "../text/text.js";
|
|
6
|
+
import { canonicalRoleId } from "../roles/legacy-role-ids.js";
|
|
6
7
|
/** CLI argument parsing and validation, shared by every command in cli.ts. The execution
|
|
7
8
|
* layer calls these before running a command, so a bad flag fails fast with an actionable
|
|
8
9
|
* message instead of the command silently running with default behavior. The plain-decimal
|
|
@@ -32,7 +33,7 @@ export function flagValue(args, flag) {
|
|
|
32
33
|
* the shared wording (`<flag> may only be given once`), `seen` being the caller's accumulator
|
|
33
34
|
* for that flag. One home for the wording the spec-driven gate (cli-flag-specs.ts), the
|
|
34
35
|
* free-form parsers' pre-scan (cli-command-args.ts), and the bespoke command shells
|
|
35
|
-
* (backlog-
|
|
36
|
+
* (backlog-cli.ts, cli-query-commands.ts) all apply, so a repeat cannot be reported as a
|
|
36
37
|
* different error on one surface. Called per occurrence rather than pre-scanned at every site,
|
|
37
38
|
* so a malformed invocation still reports whichever error its token order reaches first. */
|
|
38
39
|
export function rejectRepeatFlag(seen, flag) {
|
|
@@ -59,6 +60,24 @@ export function parseGrepFlag(args, missingValueError) {
|
|
|
59
60
|
fail(missingValueError);
|
|
60
61
|
return { rest, pattern: raw };
|
|
61
62
|
}
|
|
63
|
+
/** Parse an optional `--type <eventType>` exact-type filter flag for the event feed view
|
|
64
|
+
* (logs): the exact type id, or null when absent, returned alongside `rest` — the args with
|
|
65
|
+
* the flag's value token removed, exactly like parseGrepFlag. It mirrors that parser and not
|
|
66
|
+
* a bare flagValue read because the value token must leave the list: `logs --type --since`
|
|
67
|
+
* treats "--since" as the type text, not the flag, so the rival-flag and --since scans run
|
|
68
|
+
* over `rest` and cannot misfire on the pattern. A valueless or empty type fails with the
|
|
69
|
+
* caller's wording (each view names its own command) — the same error the `--type` gate spec
|
|
70
|
+
* (typeFlagSpec) reports earlier for the trailing-no-value case. */
|
|
71
|
+
export function parseTypeFlag(args, missingValueError) {
|
|
72
|
+
const i = args.indexOf("--type");
|
|
73
|
+
const rest = i >= 0 ? args.filter((_, j) => j !== i + 1) : args;
|
|
74
|
+
const raw = flagValue(args, "--type");
|
|
75
|
+
if (raw === null)
|
|
76
|
+
return { rest, type: null };
|
|
77
|
+
if (raw === undefined || raw === "")
|
|
78
|
+
fail(missingValueError);
|
|
79
|
+
return { rest, type: raw };
|
|
80
|
+
}
|
|
62
81
|
/** Parse a `-n`-style count flag value: a positive integer, or fail with a clear message.
|
|
63
82
|
* Unvalidated, NaN/0/negative limits make readEvents' `slice(-limit)` dump the whole log
|
|
64
83
|
* (or drop leading lines) instead of showing the requested tail. With `max`, the count is
|
|
@@ -81,7 +100,7 @@ export function parseCountFlag(flag, raw, max) {
|
|
|
81
100
|
* `(e.g. …)` example because a bare count flag's `needs a value` reads thin next to the `<n>`
|
|
82
101
|
* the operator typed; `example` is the spelling shown back to them. One home for the two
|
|
83
102
|
* failure messages and the plain-decimal rule, shared by the prompt queue's parser
|
|
84
|
-
* (cli-command-args.ts) and the bug/plan backlog shells (backlog-
|
|
103
|
+
* (cli-command-args.ts) and the bug/plan backlog shells (backlog-cli.ts), so the rule cannot
|
|
85
104
|
* drift to Number() at one site. */
|
|
86
105
|
export function parsePositionFlag(flag, raw, example) {
|
|
87
106
|
if (raw === undefined)
|
|
@@ -238,11 +257,12 @@ export function parseSinceFlag(args, label, maxMs) {
|
|
|
238
257
|
* catalog alone, which keeps in-process callers (and tests) that have no config working.
|
|
239
258
|
*/
|
|
240
259
|
export function parseRoleFlag(args, validIds) {
|
|
241
|
-
const
|
|
242
|
-
if (
|
|
260
|
+
const typed = flagValue(args, "--role");
|
|
261
|
+
if (typed === null)
|
|
243
262
|
return null;
|
|
244
|
-
if (!
|
|
263
|
+
if (!typed)
|
|
245
264
|
fail(ROLE_VALUE_ERROR);
|
|
265
|
+
const role = canonicalRoleId(typed);
|
|
246
266
|
const ids = validIds ?? allRoleIds();
|
|
247
267
|
if (!ids.includes(role))
|
|
248
268
|
fail(unknownRoleMessage(role, ids));
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// vocabulary: plain
|
|
1
2
|
/** The free-form-prompt commands' bespoke argument parsers: `tumwater init` and
|
|
2
3
|
* `tumwater prompt`, whose positionals are prompt TEXT rather than a token list. Every
|
|
3
4
|
* shared helper in cli/cli-args.ts assumes flags claim their tokens and rejects whatever is
|
|
@@ -20,7 +21,7 @@ import { errorMessage } from "../text/text.js";
|
|
|
20
21
|
* prefix test — a negative tick number (`tick bugfix -3`) arrives flag-shaped and fails the
|
|
21
22
|
* unknown-arg gate, which is the right answer for a non-positive n anyway. Lives here with
|
|
22
23
|
* the CLI's other argument helpers, shared by cli.ts's tick case and
|
|
23
|
-
* backlog-
|
|
24
|
+
* backlog-cli.ts's fileAndAnnounce. */
|
|
24
25
|
export function peelPositionals(args) {
|
|
25
26
|
const positionals = [];
|
|
26
27
|
const rest = [];
|
|
@@ -28,6 +29,21 @@ export function peelPositionals(args) {
|
|
|
28
29
|
(arg.startsWith("-") ? rest : positionals).push(arg);
|
|
29
30
|
return { positionals, rest };
|
|
30
31
|
}
|
|
32
|
+
/** The `history` listing flags that take a separate value token. */
|
|
33
|
+
const HISTORY_LISTING_VALUED_FLAGS = ["-n", "--since", "--grep", "--result", "--role"];
|
|
34
|
+
/** True when `history`'s args are the one-run trail form (`history <role> <n>`, `history
|
|
35
|
+
* <role> --last`): a token that is neither a flag nor the value of a listing-form valued flag.
|
|
36
|
+
* A valued flag's value is skipped by position, so `--grep -x` stays a listing. */
|
|
37
|
+
export function historyTrailForm(args) {
|
|
38
|
+
for (let i = 0; i < args.length; i++) {
|
|
39
|
+
const arg = args[i];
|
|
40
|
+
if (HISTORY_LISTING_VALUED_FLAGS.includes(arg))
|
|
41
|
+
i++;
|
|
42
|
+
else if (!arg.startsWith("-"))
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
31
47
|
/** Fail when any token is not at one of the `claimed` positions — the shared "no extra tokens"
|
|
32
48
|
* check for commands whose positionals are free-form prompt text (init, prompt): with their
|
|
33
49
|
* flags present, every other token would be silently baked into the prompt. Names the first
|
|
@@ -206,17 +222,19 @@ const PROMPT_FLAG_SPECS = [
|
|
|
206
222
|
JSON_FLAG,
|
|
207
223
|
{ names: ["--cancel"], value: true, valueName: "<n>" },
|
|
208
224
|
{ names: ["--edit"], value: true, valueName: "<n>" },
|
|
225
|
+
{ names: ["--top"], value: true, valueName: "<n>" },
|
|
209
226
|
{ names: ["--file"], value: true, valueName: "<path>" },
|
|
210
227
|
{ names: ["--at"], value: true, valueName: "<duration>" },
|
|
211
228
|
{ names: ["--attach"], value: true, valueName: "<path>" },
|
|
212
229
|
];
|
|
213
230
|
/** `tumwater prompt` argument handling, following parseInitArgs' pattern. Like init's,
|
|
214
231
|
* positionals are free-form prompt content — but a double-dash token must be a real flag
|
|
215
|
-
* (`--role <id>`, `--list`, `--json`, `--cancel <n>`, `--
|
|
232
|
+
* (`--role <id>`, `--list`, `--json`, `--cancel <n>`, `--edit <n>`, `--top <n>`, `--file <path>`,
|
|
233
|
+
* `--at <duration>`), or
|
|
216
234
|
* it would be baked into the queued prompt (the same class of bug parseInitArgs fixed: today
|
|
217
235
|
* `tumwater prompt --foo text` enqueues "--foo text"). Single-dash positionals remain prompt
|
|
218
|
-
* content. `--list` and `--
|
|
219
|
-
* text; `--role` is accepted in every mode and is never
|
|
236
|
+
* content. `--list`, `--cancel`, `--edit` and `--top` are mutually exclusive and may
|
|
237
|
+
* not combine with positional text; `--role` is accepted in every mode and is never
|
|
220
238
|
* prompt content. `--at <duration>` (PLANS.md "tumwater prompt --at <duration>") only queues
|
|
221
239
|
* a prompt: it defers delivery — parsed here with parseDurationFlag, riding the enqueue as
|
|
222
240
|
* the queue file's not-before marker — so the read-only and destructive modes refuse it
|
|
@@ -231,6 +249,7 @@ export function parsePromptArgs(args) {
|
|
|
231
249
|
const listFlag = args.indexOf("--list");
|
|
232
250
|
const cancelFlag = args.indexOf("--cancel");
|
|
233
251
|
const editFlag = args.indexOf("--edit");
|
|
252
|
+
const topFlag = args.indexOf("--top");
|
|
234
253
|
const fileFlag = args.indexOf("--file");
|
|
235
254
|
const atFlag = args.indexOf("--at");
|
|
236
255
|
// --attach is repeatable: every occurrence claims itself and its value token, like --role's
|
|
@@ -244,6 +263,12 @@ export function parsePromptArgs(args) {
|
|
|
244
263
|
fail("--list and --edit are mutually exclusive");
|
|
245
264
|
if (cancelFlag >= 0 && editFlag >= 0)
|
|
246
265
|
fail("--cancel and --edit are mutually exclusive");
|
|
266
|
+
if (listFlag >= 0 && topFlag >= 0)
|
|
267
|
+
fail("--list and --top are mutually exclusive");
|
|
268
|
+
if (cancelFlag >= 0 && topFlag >= 0)
|
|
269
|
+
fail("--cancel and --top are mutually exclusive");
|
|
270
|
+
if (editFlag >= 0 && topFlag >= 0)
|
|
271
|
+
fail("--edit and --top are mutually exclusive");
|
|
247
272
|
const roleFlag = args.indexOf("--role");
|
|
248
273
|
const roleRaw = roleFlag >= 0 ? args[roleFlag + 1] : undefined;
|
|
249
274
|
if (roleFlag >= 0 && (!roleRaw || roleRaw.startsWith("--")))
|
|
@@ -259,12 +284,13 @@ export function parsePromptArgs(args) {
|
|
|
259
284
|
// The queue-only flags --file and --at have nothing to mean in the read-only and destructive
|
|
260
285
|
// modes (--list and --cancel): --file is the file-shaped twin of free-form text (--edit
|
|
261
286
|
// accepts it as its replacement text, so the editFlag test below exempts that one mode), and
|
|
262
|
-
// a delivery deferral has nothing to mean to a listing or a cancel. --attach is queue/edit-only:
|
|
263
|
-
// by name, while --edit accepts it to append images to the
|
|
264
|
-
// test below exempts that one mode). One home for the mode
|
|
265
|
-
// `<flag> only queues a prompt` wording, so a new mode or queue-only flag
|
|
287
|
+
// a delivery deferral has nothing to mean to a listing or a cancel. --attach is queue/edit-only:
|
|
288
|
+
// --list and --cancel refuse it by name, while --edit accepts it to append images to the
|
|
289
|
+
// prompt it rewrites (the editFlag test below exempts that one mode). One home for the mode
|
|
290
|
+
// predicate and the `<flag> only queues a prompt` wording, so a new mode or queue-only flag
|
|
291
|
+
// is added here.
|
|
266
292
|
const refuseQueueOnlyFlag = (flag, present) => {
|
|
267
|
-
if (present && (listFlag >= 0 || cancelFlag >= 0 || editFlag >= 0))
|
|
293
|
+
if (present && (listFlag >= 0 || cancelFlag >= 0 || editFlag >= 0 || topFlag >= 0))
|
|
268
294
|
fail(`${flag} only queues a prompt`);
|
|
269
295
|
};
|
|
270
296
|
refuseQueueOnlyFlag("--file", fileFlag >= 0 && editFlag < 0);
|
|
@@ -307,7 +333,7 @@ export function parsePromptArgs(args) {
|
|
|
307
333
|
// claims. Tokens preceding --edit (other than a claimed flag) would be silently dropped —
|
|
308
334
|
// a mistyped command would edit prompt N with only the trailing fragment — so they are
|
|
309
335
|
// refused here by the same stray-argument wording the sibling modes use; tokens after the
|
|
310
|
-
// position are the replacement text, minus any claimed attach pair.
|
|
336
|
+
// position are the replacement text, minus any claimed attach pair (none: edit in $EDITOR).
|
|
311
337
|
const fileClaim = fileFlag >= 0 ? [fileFlag, fileFlag + 1] : [];
|
|
312
338
|
for (let i = 0; i < editFlag; i++) {
|
|
313
339
|
if (roleClaim.includes(i) || attachClaims.includes(i) || fileClaim.includes(i))
|
|
@@ -328,9 +354,15 @@ export function parsePromptArgs(args) {
|
|
|
328
354
|
textTokens.push(args[i]);
|
|
329
355
|
}
|
|
330
356
|
const text = textTokens.join(" ").trim();
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
357
|
+
// No text and no --file: the caller opens the prompt's current body in the editor.
|
|
358
|
+
return { mode: "edit", role, position: n, text: text === "" ? null : text, attachPaths };
|
|
359
|
+
}
|
|
360
|
+
if (topFlag >= 0) {
|
|
361
|
+
const n = parsePositionFlag("--top", args[topFlag + 1], "--top 2");
|
|
362
|
+
// The position is the only token --top may carry; anything else alongside it would be
|
|
363
|
+
// prompt text, and this mode has none.
|
|
364
|
+
failStrayArg(args, "with --top there is no prompt text", topFlag, topFlag + 1, ...roleClaim);
|
|
365
|
+
return { mode: "top", role, position: n };
|
|
334
366
|
}
|
|
335
367
|
if (fileFlag >= 0) {
|
|
336
368
|
// The file is the prompt: read it whole (a path of `-` reads stdin, so a pipe or heredoc
|
|
@@ -353,6 +385,6 @@ export function parsePromptArgs(args) {
|
|
|
353
385
|
// Name the fix, like the sibling operator commands' usage errors (bug/plan carry their
|
|
354
386
|
// BUG_USAGE/PLAN_USAGE lines): "prompt text required" alone never said what to type.
|
|
355
387
|
if (!text)
|
|
356
|
-
fail('prompt text required — usage: tumwater prompt "<text>" (add --role <id> to aim it at one
|
|
388
|
+
fail('prompt text required — usage: tumwater prompt "<text>" (add --role <id> to aim it at one role)');
|
|
357
389
|
return { mode: "enqueue", role, text, atDelayMs, attachPaths: attachPaths };
|
|
358
390
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// Open text in the operator's editor, the way `git commit` and `crontab -e` do.
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { spawnSync } from "node:child_process";
|
|
6
|
+
import { fail } from "./cli-output.js";
|
|
7
|
+
/** Signal number → name, for the shell's 128+n convention: a shell that does not re-raise a
|
|
8
|
+
* signal-killed child (dash on Linux) exits with 128+signo, so node sees an exit status where
|
|
9
|
+
* bash on macOS lets it see the signal. Keyed by number, so a later alias (SIGABRT over SIGIOT)
|
|
10
|
+
* wins; any name is fine for the message. */
|
|
11
|
+
const SIGNAL_NAMES = Object.fromEntries(Object.entries(os.constants.signals).map(([name, n]) => [n, name]));
|
|
12
|
+
/** How an editor run ended, naming a signal even when the shell folded it into 128+n. A null
|
|
13
|
+
* status (spawn error, signal) and a plain non-zero status fall through unchanged. */
|
|
14
|
+
function exitReason(r) {
|
|
15
|
+
if (r.error)
|
|
16
|
+
return r.error.message;
|
|
17
|
+
if (r.signal)
|
|
18
|
+
return `signal ${r.signal}`;
|
|
19
|
+
// The editor value runs through `sh -c`, and sh reports a command it could not launch as
|
|
20
|
+
// 127 (not found) or 126 (found but not executable) — most often the $EDITOR/$VISUAL value
|
|
21
|
+
// itself is a typo or a path to a binary that is not installed. A bare "status 127" tells
|
|
22
|
+
// the user only that sh refused, so name the likely cause and the env var to fix, the way
|
|
23
|
+
// the no-editor arm does. Occasionally the 127/126 came from inside the editor's own shell
|
|
24
|
+
// script rather than the editor command; an occasionally-missed guess still beats a bare
|
|
25
|
+
// status code that never suggests anything.
|
|
26
|
+
if (r.status === 127)
|
|
27
|
+
return "the editor command was not found (exit 127) — check $EDITOR/$VISUAL";
|
|
28
|
+
if (r.status === 126)
|
|
29
|
+
return "the editor command was found but not executable (exit 126) — check $EDITOR/$VISUAL";
|
|
30
|
+
const signalled = r.status !== null && r.status > 128 ? SIGNAL_NAMES[r.status - 128] : undefined;
|
|
31
|
+
return signalled ? `signal ${signalled}` : `status ${r.status}`;
|
|
32
|
+
}
|
|
33
|
+
/** Write `initial` to a temp file named `fileName`, run `$VISUAL` (else `$EDITOR`) on it, and
|
|
34
|
+
* return the saved text. The editor value runs through `sh -c` (git's convention) so one with
|
|
35
|
+
* arguments, like `code -w`, works. There is no `vi` fallback: a script without a TTY must fail
|
|
36
|
+
* rather than hang. A missing editor, a spawn error, a non-zero exit, or a signal fails the
|
|
37
|
+
* command (`fallback` ends the no-editor message); the temp dir is removed before the failure
|
|
38
|
+
* exits. */
|
|
39
|
+
export function editInEditor(initial, fileName, fallback = "pass the new text or --file <path>") {
|
|
40
|
+
const editor = (process.env.VISUAL || process.env.EDITOR || "").trim();
|
|
41
|
+
if (editor === "")
|
|
42
|
+
fail(`no editor: set $VISUAL or $EDITOR, or ${fallback}`);
|
|
43
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "tumwater-edit-"));
|
|
44
|
+
let saved;
|
|
45
|
+
let failure;
|
|
46
|
+
try {
|
|
47
|
+
const file = path.join(dir, fileName);
|
|
48
|
+
fs.writeFileSync(file, `${initial}\n`);
|
|
49
|
+
const r = spawnSync("sh", ["-c", `${editor} "$1"`, "sh", file], { stdio: "inherit" });
|
|
50
|
+
if (r.error || r.status !== 0) {
|
|
51
|
+
failure = `editor exited with ${exitReason(r)}; nothing changed`;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
saved = fs.readFileSync(file, "utf8");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
59
|
+
}
|
|
60
|
+
// fail() exits the process, which would skip the finally above, so it runs after cleanup.
|
|
61
|
+
if (failure !== undefined)
|
|
62
|
+
fail(failure);
|
|
63
|
+
return saved;
|
|
64
|
+
}
|
|
@@ -127,15 +127,22 @@ export const PAUSE_FOR_OVERFLOW_HINT = "for a longer or standing pause run bare
|
|
|
127
127
|
* `pause --for` honors, the same not-initialized-masking rationale durationFlagSpec records).
|
|
128
128
|
* missingValue names the command for a trailing `--in` (`wake --in needs a value`), the same
|
|
129
129
|
* command-named pattern durationFlagSpec and sinceFlagSpec follow. */
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
130
|
+
function inFlagSpec(command) {
|
|
131
|
+
return {
|
|
132
|
+
names: ["--in"],
|
|
133
|
+
value: true,
|
|
134
|
+
valueName: "<duration>",
|
|
135
|
+
missingValue: `${command} --in needs a value`,
|
|
136
|
+
validate: (value) => {
|
|
137
|
+
failOverDurationCap(`${command} --in`, parseDurationFlag(`${command} --in`, value), PAUSE_FOR_MAX_MS);
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
export const WAKE_IN_FLAG = inFlagSpec("wake");
|
|
142
|
+
/** `tumwater stop --in <duration>`: the scheduled graceful stop, same shape and cap as wake's. */
|
|
143
|
+
export const STOP_IN_FLAG = inFlagSpec("stop");
|
|
144
|
+
/** `tumwater stop --cancel`: drop the scheduled stop. */
|
|
145
|
+
export const STOP_CANCEL_FLAG = { names: ["--cancel"] };
|
|
139
146
|
/** The `--cancel` flag spec for `tumwater wake --cancel`: drop pending scheduled wakes. */
|
|
140
147
|
export const WAKE_CANCEL_FLAG = { names: ["--cancel"] };
|
|
141
148
|
/** `wake --list`: print the pending scheduled wakes instead of writing one. Takes no value. */
|
|
@@ -144,7 +151,7 @@ export const WAKE_LIST_FLAG = { names: ["--list"] };
|
|
|
144
151
|
* one definition of the flag's spelling and value shape, beside ROLE_FLAG and durationFlagSpec,
|
|
145
152
|
* so the gate's accepted vocabulary cannot drift from cmdPause's parse. The missing-value
|
|
146
153
|
* wording names its only command (the GREP_VALUE_ERROR idiom — each command's parser names
|
|
147
|
-
* itself); the 200-char cap stays in
|
|
154
|
+
* itself); the 200-char cap stays in pauseTeam beside the marker it bounds, and per-role
|
|
148
155
|
* pauses carry no reason, so cmdPause rejects the `--role` + `--reason` combination. Exported
|
|
149
156
|
* so the gate and the command body share one definition of what `pause` accepts. Its
|
|
150
157
|
* missing-value wording is REASON_VALUE_ERROR, exported so cmdPause's own guard reads it too. */
|
|
@@ -195,11 +202,29 @@ export const N_FLAG = {
|
|
|
195
202
|
* siblings it needs neither a factory nor a missing-value error — so the constant is the
|
|
196
203
|
* whole spec, and the render-vs-JSON switch stays in each command body beside its render. */
|
|
197
204
|
export const JSON_FLAG = { names: ["--json"] };
|
|
205
|
+
/** The `--ready` flag spec, accepted by `backlog`: keep only the entries no hold keeps from
|
|
206
|
+
* being picked. Value-less, like STAT_FLAG. */
|
|
207
|
+
export const READY_FLAG = { names: ["--ready"] };
|
|
208
|
+
/** Force re-probing of every claimed image-input model in `tumwater doctor` (part 2/5),
|
|
209
|
+
* bypassing the 7-day cache: `--reprobe` (a value-less flag like JSON_FLAG). */
|
|
210
|
+
export const REPROBE_FLAG = { names: ["--reprobe"] };
|
|
211
|
+
/** The `--problems` flag spec, accepted by `doctor`: print only the warn and fail rows. Value-less,
|
|
212
|
+
* like REPROBE_FLAG. */
|
|
213
|
+
export const PROBLEMS_FLAG = { names: ["--problems"] };
|
|
214
|
+
/** The `--stat` flag spec, accepted by `diff --role`: print each patch as its `git diff --stat`
|
|
215
|
+
* summary. Value-less, like JSON_FLAG. */
|
|
216
|
+
export const STAT_FLAG = { names: ["--stat"] };
|
|
217
|
+
/** The `--summary` flag spec, accepted by `history`: tally each loop's tick outcomes instead of
|
|
218
|
+
* printing the rows. Value-less, like STAT_FLAG. */
|
|
219
|
+
export const SUMMARY_FLAG = { names: ["--summary"] };
|
|
220
|
+
/** The `--count` flag spec, accepted by `logs`: tally the selected window's event types
|
|
221
|
+
* instead of printing the rows. Value-less, like SUMMARY_FLAG. */
|
|
222
|
+
export const COUNT_FLAG = { names: ["--count"] };
|
|
198
223
|
/** The `--force` flag spec, accepted by `retire` (the only override-the-rails command): one
|
|
199
224
|
* definition of the flag's spelling beside ROLE_FLAG and JSON_FLAG, so the gate's accepted
|
|
200
225
|
* vocabulary cannot drift from the command body's `args.includes("--force")`. */
|
|
201
226
|
export const FORCE_FLAG = { names: ["--force"] };
|
|
202
|
-
/** The `--dry-run` flag spec: preview a reclaim pass without
|
|
227
|
+
/** The `--dry-run` flag spec: preview a reclaim pass or a retire without changing anything. Beside
|
|
203
228
|
* FORCE_FLAG so its spelling lives with the other boolean flags the gate admits. */
|
|
204
229
|
export const DRY_RUN_FLAG = { names: ["--dry-run"] };
|
|
205
230
|
/** The `--last` flag spec for `tumwater tick <role> --last`: one definition of the flag's
|
|
@@ -216,6 +241,13 @@ export const LAST_FLAG = { names: ["--last"] };
|
|
|
216
241
|
export function grepFlagSpec(missingValue) {
|
|
217
242
|
return { names: ["--grep"], value: true, valueName: "<text>", missingValue };
|
|
218
243
|
}
|
|
244
|
+
/** The `--type <event type>` flag spec of `logs`' exact-type filter, beside grepFlagSpec: one
|
|
245
|
+
* definition of the flag's spelling and value shape, so the gate's accepted vocabulary cannot
|
|
246
|
+
* drift. The missing-value wording names the command (each names its own command), so the
|
|
247
|
+
* spec is a factory taking it — the same string parseTypeFlag fails with in the command body. */
|
|
248
|
+
export function typeFlagSpec(missingValue) {
|
|
249
|
+
return { names: ["--type"], value: true, valueName: "<event type>", missingValue };
|
|
250
|
+
}
|
|
219
251
|
/** The `--result <class>` flag spec of `history`'s outcome filter, beside grepFlagSpec: one
|
|
220
252
|
* definition of the flag's spelling and value shape, so the gate's accepted vocabulary cannot
|
|
221
253
|
* drift. The missing-value wording names the command, so the spec is a factory taking it — the
|
|
@@ -228,23 +260,29 @@ export function resultFlagSpec(missingValue) {
|
|
|
228
260
|
* parseBranchFlag), `--once` (one full round of ticks, then exit), `--role <id>`
|
|
229
261
|
* (scope a --once round to one loop, parsed by parseRoleFlag), and `--gui` (also serve the
|
|
230
262
|
* browser dashboard on its defaults; port/token selection stays on `tumwater gui`). Exported
|
|
231
|
-
* so the caller's rejectUnknownArgs check and its tests share one definition of what `
|
|
232
|
-
* accepts — before this vocabulary existed, run parsed --branch
|
|
233
|
-
* every other flag, so a typo'd option ran the daemon with default
|
|
263
|
+
* so the caller's rejectUnknownArgs check and its tests share one definition of what `start`
|
|
264
|
+
* (and its hidden alias `run`) accepts — before this vocabulary existed, run parsed --branch
|
|
265
|
+
* and silently ignored every other flag, so a typo'd option ran the daemon with default
|
|
266
|
+
* behavior. */
|
|
234
267
|
export const RUN_FLAG_SPECS = [
|
|
235
268
|
{ names: ["--branch"], value: true, valueName: "<name>", missingValue: BRANCH_VALUE_ERROR },
|
|
236
269
|
{ names: ["--once"] },
|
|
237
|
-
// `run --gui` is the one-command form of the dashboard: it boots the
|
|
270
|
+
// `run --gui` is the one-command form of the dashboard: it boots the team and serves the
|
|
238
271
|
// browser dashboard at its defaults, loopback on port 7180. The dashboard's own --port,
|
|
239
272
|
// --all-interfaces, and --token stay on `tumwater gui`; widening them here would mean two
|
|
240
273
|
// flag vocabularies to keep in step. `run --gui --once` is refused in cmdRun beside the
|
|
241
274
|
// timer it arms.
|
|
242
275
|
{ names: ["--gui"] },
|
|
276
|
+
// `run --tui` is the one-command form of the observer TUI: it boots the team and hosts the
|
|
277
|
+
// same in-terminal TUI `tumwater tui` attaches from another terminal. Its flag vocabulary
|
|
278
|
+
// stays at run's (`tumwater tui` takes no flags); `run --tui --once` is refused in cmdRun
|
|
279
|
+
// beside the `--gui` rival.
|
|
280
|
+
{ names: ["--tui"] },
|
|
243
281
|
// `run --for <duration>` (the windowed run): the same spec `pause --for` passes, so the
|
|
244
282
|
// gate's accepted vocabulary and parseDurationFlag's error wording cannot drift; the
|
|
245
283
|
// 90-day cap is the spec's own validate (shared with pause), and the --once rival rule
|
|
246
284
|
// stays in cmdRun beside the timer it arms.
|
|
247
|
-
durationFlagSpec("
|
|
285
|
+
durationFlagSpec("start --for"),
|
|
248
286
|
ROLE_FLAG,
|
|
249
287
|
];
|
|
250
288
|
/** The one refusal for a trailing `gui --token` with no value, phrased for what the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DRY_RUN_FLAG, JSON_FLAG, durationFlagSpec, PAUSE_FOR_OVERFLOW_HINT, REASON_FLAG, WAKE_CANCEL_FLAG, WAKE_LIST_FLAG, WAKE_IN_FLAG, rejectUnknownArgs, ROLE_FLAG, } from "./cli-flag-specs.js";
|
|
2
2
|
import { requireReadyRepo } from "./cli-repo-gate.js";
|
|
3
|
-
import { cmdAbort, cmdPause, cmdReclaim, cmdResetCounters, cmdResume, cmdWake } from "../operator/operator-commands.js";
|
|
3
|
+
import { cmdAbort, cmdPause, cmdReclaim, cmdResetCounters, cmdRestart, cmdResume, cmdWake } from "../operator/operator-commands.js";
|
|
4
4
|
/** The CLI command layer of each marker command, keyed by its CLI name (the command bodies
|
|
5
5
|
* live in operator/operator-commands.ts, the shared marker-writing cores in
|
|
6
6
|
* operator/operator-intent.ts). One map so a new marker command registers its core beside its
|
|
@@ -12,20 +12,22 @@ const markerCommandCores = {
|
|
|
12
12
|
pause: cmdPause,
|
|
13
13
|
resume: cmdResume,
|
|
14
14
|
reclaim: cmdReclaim,
|
|
15
|
+
restart: cmdRestart,
|
|
15
16
|
};
|
|
16
|
-
/** The shared shape of the
|
|
17
|
-
* reclaim): reject unknown args against each command's flag set, gate on a
|
|
18
|
-
* dispatch to its operator-commands core. One copy of the guard sequence so
|
|
19
|
-
* drift on validation order or gating. Lives beside
|
|
17
|
+
/** The shared shape of the seven marker commands (reset-counters, wake, abort, pause,
|
|
18
|
+
* resume, reclaim, restart): reject unknown args against each command's flag set, gate on a
|
|
19
|
+
* ready repo, then dispatch to its operator-commands core. One copy of the guard sequence so
|
|
20
|
+
* the seven cannot drift on validation order or gating. Lives beside
|
|
21
|
+
* operator/operator-commands.ts's importers
|
|
20
22
|
* as the marker commands' CLI layer, with cli.ts's main() dispatch casting to MarkerCommand
|
|
21
23
|
* here. */
|
|
22
24
|
export async function runMarkerCommand(root, command, args) {
|
|
23
25
|
// `pause` alone accepts `--for <duration>` (the timed pause) and `--reason <text>` (the
|
|
24
|
-
// operator pause's why); `wake` alone accepts `--in <duration>` (the scheduled wake) and
|
|
25
|
-
// scheduled wakes), `--list` (show them), and `--json`
|
|
26
|
-
// `reclaim` alone accepts `--dry-run`; the other marker commands keep the
|
|
27
|
-
// vocabulary, so a stray --for, --reason, --in, --cancel, or --dry-run fails
|
|
28
|
-
// silently ignored.
|
|
26
|
+
// operator pause's why); `wake` alone accepts `--in <duration>` (the scheduled wake) and
|
|
27
|
+
// `--cancel` (drop the pending scheduled wakes), `--list` (show them), and `--json`
|
|
28
|
+
// (with --list); `reclaim` alone accepts `--dry-run`; the other marker commands keep the
|
|
29
|
+
// plain --role vocabulary, so a stray --for, --reason, --in, --cancel, or --dry-run fails
|
|
30
|
+
// fast instead of being silently ignored.
|
|
29
31
|
const perCommandFlags = {
|
|
30
32
|
"reset-counters": [ROLE_FLAG],
|
|
31
33
|
wake: [ROLE_FLAG, WAKE_IN_FLAG, WAKE_CANCEL_FLAG, WAKE_LIST_FLAG, JSON_FLAG],
|
|
@@ -37,6 +39,7 @@ export async function runMarkerCommand(root, command, args) {
|
|
|
37
39
|
],
|
|
38
40
|
resume: [ROLE_FLAG],
|
|
39
41
|
reclaim: [DRY_RUN_FLAG],
|
|
42
|
+
restart: [],
|
|
40
43
|
};
|
|
41
44
|
rejectUnknownArgs(command, args, perCommandFlags[command] ?? [ROLE_FLAG]);
|
|
42
45
|
await requireReadyRepo(root);
|
|
@@ -20,6 +20,15 @@ function writeLine(text) {
|
|
|
20
20
|
export function say(text) {
|
|
21
21
|
writeLine(stripTerminalControls(text));
|
|
22
22
|
}
|
|
23
|
+
/** Tell the operator, on stderr, that an operator backlog write landed in the working tree but
|
|
24
|
+
* was not committed to main (commitOperatorBacklogEdit returned false: the primary is off the
|
|
25
|
+
* base branch, or git failed). The write itself succeeded, so this warns instead of failing;
|
|
26
|
+
* a no-op when the commit landed. */
|
|
27
|
+
export function warnIfUncommitted(committed, file) {
|
|
28
|
+
if (committed)
|
|
29
|
+
return;
|
|
30
|
+
process.stderr.write(`warning: ${file} was written but not committed to main — commit it before the next fast-forward\n`);
|
|
31
|
+
}
|
|
23
32
|
/** Print a value as pretty-printed (2-space) JSON — the `--json` output every CLI query
|
|
24
33
|
* command (status, history, report, config) renders through: the pretty-print spelling
|
|
25
34
|
* (indent 2) decided once so the machine-readable surface cannot drift per command. Writes
|