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
|
@@ -1,29 +1,33 @@
|
|
|
1
|
+
// vocabulary: plain
|
|
1
2
|
import { knownRoleIds, loadConfig } from "../config/config.js";
|
|
2
3
|
import { fail, say, sayJson } from "../cli/cli-output.js";
|
|
3
4
|
import { failOverDurationCap, flagValue, parseDurationFlag, parseRoleFlag } from "../cli/cli-args.js";
|
|
4
5
|
import { errorMessage } from "../text/text.js";
|
|
5
|
-
import { PAUSE_FOR_OVERFLOW_HINT, REASON_VALUE_ERROR } from "../cli/cli-flag-specs.js";
|
|
6
|
-
import { artifactPhrase, retireRole } from "./retire.js";
|
|
7
|
-
import {
|
|
6
|
+
import { PAUSE_FOR_OVERFLOW_HINT, REASON_VALUE_ERROR, ROLE_VALUE_ERROR } from "../cli/cli-flag-specs.js";
|
|
7
|
+
import { artifactPhrase, hasRetirableLeftovers, previewRetire, retireRole } from "./retire.js";
|
|
8
|
+
import { revertMergedCommit } from "./revert.js";
|
|
9
|
+
import { agree, plural, teamResumePhrase, pauseReasonSuffix } from "../text/phrases.js";
|
|
8
10
|
import { errCode } from "../errno.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
11
|
+
import { shortSha } from "../text/format.js";
|
|
12
|
+
import { INSTANCE_ROLES } from "../roles/loop-ids.js";
|
|
13
|
+
import { allRoleIds, unknownRoleMessage } from "../roles/roles.js";
|
|
14
|
+
import { normalizePauseReason, pauseTeam, pauseRole, pausedRoles, resumeTeam, resumeRole, } from "../team/team-state.js";
|
|
15
|
+
import { readOrchestratorInfo } from "../team/orchestrator-info.js";
|
|
12
16
|
import { pidAlive } from "../process/process.js";
|
|
13
|
-
import { inspectReclaimCandidates } from "../
|
|
14
|
-
import { markerApplyNote, NO_HARNESS_ERROR, PAUSE_FOR_MAX_MS, cancelWakes, listWakes, renderWakeList, requestAbort, requestResetCounters, requestWake, rolePauseMessage, roleResumeMessage, runManualReclaim, timedPauseBits, } from "./operator-intent.js";
|
|
17
|
+
import { inspectReclaimCandidates } from "../team/reclaim.js";
|
|
18
|
+
import { markerApplyNote, NO_HARNESS_ERROR, PAUSE_FOR_MAX_MS, cancelScheduledStopMessage, cancelWakes, listWakes, renderWakeList, requestAbort, requestResetCounters, requestRestart, requestScheduledStop, requestWake, rolePauseMessage, roleResumeMessage, runManualReclaim, timedPauseBits, } from "./operator-intent.js";
|
|
15
19
|
/** The CLI layer of the operator commands: the `reset-counters`, `wake`, `reclaim`, `abort`,
|
|
16
20
|
* `pause`, `resume`, `stop`, and `retire` commands, split out of cli.ts so the command bodies
|
|
17
21
|
* live beside their shared `--role` resolution. The marker-writing cores and shared
|
|
18
22
|
* confirmations they print live in src/operator/operator-intent.ts (shared with the
|
|
19
|
-
* dashboard and TUI); the
|
|
23
|
+
* dashboard and TUI); the team-side consumer half is src/operator/operator-requests.ts;
|
|
20
24
|
* the `prompt` command, which drives the durable per-loop queues rather than the marker
|
|
21
25
|
* protocol, lives beside them in src/inbox/prompt-commands.ts, and the `config` command,
|
|
22
|
-
* which reads and writes tumwater.json directly rather than touching
|
|
26
|
+
* which reads and writes tumwater.json directly rather than touching team state, in
|
|
23
27
|
* src/cli/config-commands.ts. Every command here is deliberately usable without a live
|
|
24
|
-
* harness except `abort` and `stop`, which have nothing to reach when no
|
|
28
|
+
* harness except `abort` and `stop`, which have nothing to reach when no team is up. */
|
|
25
29
|
/** The `--role <id>` value when given, resolved WITHOUT tumwater.json when it names a
|
|
26
|
-
* built-in catalog role: the
|
|
30
|
+
* built-in catalog role: the team itself tolerates a broken config (the live reload keeps
|
|
27
31
|
* the last-known-good one), so an operator marker aimed at a built-in loop must not depend
|
|
28
32
|
* on the file parsing — the same resilience `logs --role` already has. A custom-loop id or
|
|
29
33
|
* an unknown id falls through to the config-backed check, which fails with the honest error
|
|
@@ -42,6 +46,15 @@ function namedRole(root, args) {
|
|
|
42
46
|
fail(errorMessage(err)); // same message main().catch would print, but via the standard fail()
|
|
43
47
|
}
|
|
44
48
|
}
|
|
49
|
+
/** namedRole for the commands that write or lift the per-role pause marker: `--role feature-1`
|
|
50
|
+
* (`bugfix-1`) is the pause key of instance 1 of a multi-instance role (src/roles/loop-ids.ts's
|
|
51
|
+
* instancePauseKey), returned verbatim; every other value resolves through namedRole. */
|
|
52
|
+
function pauseTargetRole(root, args) {
|
|
53
|
+
const role = flagValue(args, "--role");
|
|
54
|
+
if (role && [...INSTANCE_ROLES].some((base) => role === `${base}-1`))
|
|
55
|
+
return role;
|
|
56
|
+
return namedRole(root, args);
|
|
57
|
+
}
|
|
45
58
|
/** The `--role <id>` value a marker command requires: namedRole's resolution plus the
|
|
46
59
|
* command-specific "requires --role" failure. abort and retire both need a target (unlike
|
|
47
60
|
* reset-counters/resume/wake, which default to every role), so the guard lives here once and
|
|
@@ -65,12 +78,14 @@ function targetRoles(root, args) {
|
|
|
65
78
|
export async function cmdResetCounters(root, args) {
|
|
66
79
|
say(requestResetCounters(root, targetRoles(root, args)));
|
|
67
80
|
}
|
|
68
|
-
/** `tumwater wake [--role <id>] [--in <duration> | --cancel | --list [--json]]`: tell the
|
|
69
|
-
* failing on is fixed — try again", so the named roles tick
|
|
81
|
+
/** `tumwater wake [--role <id>] [--in <duration> | --cancel | --list [--json]]`: tell the
|
|
82
|
+
* team "whatever the loops were failing on is fixed — try again", so the named roles tick
|
|
83
|
+
* within one poll instead of
|
|
70
84
|
* sleeping until their backoff expires. With `--in`, the wake is scheduled: the marker is
|
|
71
85
|
* written now but consumed no earlier than now + duration, the same shape `pause --for`'s
|
|
72
86
|
* auto-resume takes — the wake applies at the deadline, not at submit. With `--cancel`, the
|
|
73
|
-
* pending scheduled wakes (or the named role's share) are dropped instead; no wake is
|
|
87
|
+
* pending scheduled wakes (or the named role's share) are dropped instead; no wake is
|
|
88
|
+
* written. With `--list`, every pending scheduled wake (or the named role's)
|
|
74
89
|
* is printed, earliest first — `--json` as an array of {roles, atMs}; read-only. */
|
|
75
90
|
export async function cmdWake(root, args) {
|
|
76
91
|
// `--in <duration>` (the scheduled wake): the command's own gate (runMarkerCommand's
|
|
@@ -109,7 +124,7 @@ export async function cmdWake(root, args) {
|
|
|
109
124
|
/** `tumwater reclaim [--dry-run]`: run one manual reclaim pass over every harness worktree
|
|
110
125
|
* (plans/disk-floor.md, part 3/4). `--dry-run` lists each candidate with its idle age and how
|
|
111
126
|
* many ignored paths `git clean -ndX` would remove, and changes nothing — it neither cleans a
|
|
112
|
-
* worktree nor writes the worktree-use registry. With a live
|
|
127
|
+
* worktree nor writes the worktree-use registry. With a live team the
|
|
113
128
|
* pass is requested through the marker the orchestrator consumes, so in-use worktrees are
|
|
114
129
|
* respected; with none running it runs in-process, since nothing is in use. */
|
|
115
130
|
export async function cmdReclaim(root, args) {
|
|
@@ -124,7 +139,7 @@ export async function cmdReclaim(root, args) {
|
|
|
124
139
|
for (const c of candidates) {
|
|
125
140
|
const notes = [
|
|
126
141
|
`idle ${c.idleHours.toFixed(1)}h`,
|
|
127
|
-
|
|
142
|
+
plural(c.paths, "ignored path"),
|
|
128
143
|
...(c.resumePending ? ["resume pending"] : []),
|
|
129
144
|
...(c.reclaimable ? [] : ["not idle-reclaimable"]),
|
|
130
145
|
];
|
|
@@ -136,9 +151,9 @@ export async function cmdReclaim(root, args) {
|
|
|
136
151
|
}
|
|
137
152
|
/** `tumwater abort --role <id>`: kill one loop's in-flight tick right now. The CLI cannot
|
|
138
153
|
* reach into the orchestrator process, so the request rides on disk like reset-counters':
|
|
139
|
-
* a per-role marker file a running
|
|
154
|
+
* a per-role marker file a running team consumes within one poll cycle (the runner's
|
|
140
155
|
* abortTick kills the pi child and resets the worktree to main). Requires a live harness —
|
|
141
|
-
* with no
|
|
156
|
+
* with no team there is nothing to consume the marker. The loop stays enabled: it backs
|
|
142
157
|
* off normally and later ticks proceed as usual. */
|
|
143
158
|
export async function cmdAbort(root, args) {
|
|
144
159
|
// As in cmdLogs: the config exists only to validate the id against built-ins plus
|
|
@@ -149,17 +164,28 @@ export async function cmdAbort(root, args) {
|
|
|
149
164
|
fail(result.error);
|
|
150
165
|
say(result.message);
|
|
151
166
|
}
|
|
167
|
+
/** `tumwater restart`: apply a pending self-redeploy now, waiving the restart cooldown —
|
|
168
|
+
* the terminal twin of the dashboard update alert's refresh button (POST /api/restart,
|
|
169
|
+
* handleRestart in src/gui/gui-operator-commands.ts). Both call the same marker-writing
|
|
170
|
+
* core requestRestart, so the confirmation (or refusal) is byte-for-byte the GUI's. Takes
|
|
171
|
+
* no flags. */
|
|
172
|
+
export async function cmdRestart(root, _args) {
|
|
173
|
+
const result = requestRestart(root);
|
|
174
|
+
if (!result.ok)
|
|
175
|
+
fail(result.error);
|
|
176
|
+
say(result.message);
|
|
177
|
+
}
|
|
152
178
|
/** `tumwater pause [--role <id>] [--for <duration>]`: with a role, stop THAT loop from
|
|
153
179
|
* starting new ticks — in-flight ones finish, every other role (the director included)
|
|
154
180
|
* keeps running; without one, stop every role loop from starting NEW ticks while in-flight
|
|
155
181
|
* ones finish and the director keeps running (its prompts outrank operator gates, like
|
|
156
182
|
* under the budget cap). The markers
|
|
157
183
|
* are persistent state, not one-shot requests: presence means paused until `resume` removes
|
|
158
|
-
* it — so pausing before startup starts an already-paused
|
|
184
|
+
* it — so pausing before startup starts an already-paused team. Unlike abort, no live
|
|
159
185
|
* harness is required; when none runs, say where the pause takes effect instead of failing.
|
|
160
186
|
* Idempotent: a second pause reports the existing marker as-is. */
|
|
161
187
|
export async function cmdPause(root, args = [], now = Date.now()) {
|
|
162
|
-
// `--for <duration>` (the timed pause): the ms-epoch deadline
|
|
188
|
+
// `--for <duration>` (the timed pause): the ms-epoch deadline pauseTeam/pauseRole write
|
|
163
189
|
// into the marker and every consumer honors — the command's own gate (runMarkerCommand's
|
|
164
190
|
// rejectUnknownArgs) has already restricted the flag to this command and validated its
|
|
165
191
|
// value — shape and the 90-day cap both, via durationFlagSpec("pause --for")'s validate —
|
|
@@ -169,7 +195,7 @@ export async function cmdPause(root, args = [], now = Date.now()) {
|
|
|
169
195
|
// `--reason <text>` (the operator pause's why): the gate in cli.ts has already restricted
|
|
170
196
|
// the flag to this command, and its trailing-no-value slip to the spec's wording, so here
|
|
171
197
|
// a valueless or empty reason fails with that same line beside the writer it feeds. The
|
|
172
|
-
// reason folds to one line (normalizePauseReason, the same home
|
|
198
|
+
// reason folds to one line (normalizePauseReason, the same home pauseTeam writes through)
|
|
173
199
|
// so the confirmation line, the marker, and every single-line surface carry the same text
|
|
174
200
|
// — a raw newline in the flag's value would otherwise break the header's one-line badge.
|
|
175
201
|
const reasonRaw = flagValue(args, "--reason");
|
|
@@ -186,15 +212,15 @@ export async function cmdPause(root, args = [], now = Date.now()) {
|
|
|
186
212
|
// discipline timedPauseBits's own `now` parameter follows.
|
|
187
213
|
const timed = forMs === undefined ? undefined : { ms: forMs, untilMs: now + forMs };
|
|
188
214
|
const untilMs = timed?.untilMs;
|
|
189
|
-
const role =
|
|
215
|
+
const role = pauseTargetRole(root, args);
|
|
190
216
|
if (role) {
|
|
191
217
|
// The plan keeps per-role pauses anonymous (a per-role reason would need a per-role map
|
|
192
218
|
// for no observed need), so a reason aimed at a role pause fails fast here — accepting
|
|
193
219
|
// the flag and silently dropping the note would tell the operator the reason stands
|
|
194
220
|
// when no marker carries it. Before any marker is written, beside the other fail-fasts.
|
|
195
221
|
if (reason !== undefined)
|
|
196
|
-
fail("pause --reason states why the whole
|
|
197
|
-
// Per-role branch: pauseRole in src/
|
|
222
|
+
fail("pause --reason states why the whole team is paused — a per-role pause carries no reason (drop --role, or run bare `tumwater pause --reason <text>`)");
|
|
223
|
+
// Per-role branch: pauseRole in src/team/team-state.ts is the single writer of the role
|
|
198
224
|
// marker (the TUI's Ctrl+P and the dashboard's /api/pause-role toggle call it too), so
|
|
199
225
|
// the CLI, TUI, and GUI cannot drift on format or idempotence; a false return means the
|
|
200
226
|
// role was already in the set, which rolePauseMessage words — unless a `--for` stands,
|
|
@@ -202,20 +228,20 @@ export async function cmdPause(root, args = [], now = Date.now()) {
|
|
|
202
228
|
say(rolePauseMessage(root, role, pauseRole(root, role, untilMs), timed, now));
|
|
203
229
|
return;
|
|
204
230
|
}
|
|
205
|
-
//
|
|
231
|
+
// pauseTeam in src/team/team-state.ts is the single writer of the pause marker — the GUI's
|
|
206
232
|
// /api/pause toggle calls it too, so the CLI and the dashboard cannot drift on format
|
|
207
233
|
// or idempotence; a false return means the marker was already there (a `--for` never
|
|
208
234
|
// no-ops: it overwrites the standing deadline instead).
|
|
209
235
|
// The reason rides the fresh pause write (or a `--for` overwrite): the same last-write-wins
|
|
210
236
|
// rule as the deadline, so an "already paused" no-op keeps the standing note as-is.
|
|
211
|
-
if (!
|
|
237
|
+
if (!pauseTeam(root, untilMs, reason)) {
|
|
212
238
|
say("already paused");
|
|
213
239
|
return;
|
|
214
240
|
}
|
|
215
241
|
const { when, tail } = markerApplyNote(root);
|
|
216
242
|
const { forPhrase, note } = timedPauseBits(timed, now);
|
|
217
243
|
const why = pauseReasonSuffix(reason);
|
|
218
|
-
say(`
|
|
244
|
+
say(`team paused${forPhrase}${why} — roles start no new runs${when} (current runs finish; the director keeps running your messages)${note}${tail}`);
|
|
219
245
|
}
|
|
220
246
|
/** Deliver the stop signal to the recorded orchestrator pid: "signalled" when SIGTERM was
|
|
221
247
|
* delivered, "gone" when the process died between the caller's liveness check and the
|
|
@@ -245,59 +271,119 @@ export function signalOrchestrator(pid) {
|
|
|
245
271
|
* that `orchestratorAlive` already accepts everywhere (status, abort, pause) applies here
|
|
246
272
|
* too: the info file is refreshed by the live orchestrator, so the window is small, and the
|
|
247
273
|
* signal is the same one a human `kill <pid>` would send. */
|
|
248
|
-
export async function cmdStop(root) {
|
|
274
|
+
export async function cmdStop(root, args = []) {
|
|
275
|
+
const inRaw = flagValue(args, "--in");
|
|
276
|
+
if (args.includes("--cancel")) {
|
|
277
|
+
if (inRaw !== null)
|
|
278
|
+
fail("stop --cancel and --in cannot be combined");
|
|
279
|
+
say(cancelScheduledStopMessage(root));
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
if (inRaw !== null) {
|
|
283
|
+
// The gate (STOP_IN_FLAG's validate) checked shape and cap; re-parse beside the writer.
|
|
284
|
+
const ms = parseDurationFlag("stop --in", inRaw);
|
|
285
|
+
failOverDurationCap("stop --in", ms, PAUSE_FOR_MAX_MS);
|
|
286
|
+
const result = requestScheduledStop(root, ms);
|
|
287
|
+
if (!result.ok)
|
|
288
|
+
fail(result.error);
|
|
289
|
+
say(result.message);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
249
292
|
// Deliberately the same liveness gate and wording `requestAbort` uses: an unreadable info
|
|
250
293
|
// file (torn write) and a dead recorded pid both mean nothing is running to stop.
|
|
251
294
|
const info = readOrchestratorInfo(root);
|
|
252
295
|
if (!info || !pidAlive(info.pid)) {
|
|
253
296
|
fail(NO_HARNESS_ERROR);
|
|
254
297
|
}
|
|
255
|
-
// A pid that dies between the liveness check and the signal (ESRCH) means the
|
|
298
|
+
// A pid that dies between the liveness check and the signal (ESRCH) means the team is
|
|
256
299
|
// already stopped — say so and exit clean rather than surfacing Node's raw kill error;
|
|
257
300
|
// anything else (EPERM on a recycled pid, …) comes back as signalOrchestrator's message.
|
|
258
301
|
if (signalOrchestrator(info.pid) === "signalled")
|
|
259
|
-
say("stop requested — the
|
|
302
|
+
say("stop requested — the team finishes current work, then stops (the same path as Ctrl+C)");
|
|
260
303
|
else
|
|
261
|
-
say("the orchestrator exited before the stop signal
|
|
304
|
+
say("the orchestrator exited before the stop signal arrived — nothing is running");
|
|
262
305
|
}
|
|
263
306
|
/** `tumwater resume [--role <id>]`: with a role, lift that one role's pause (removed from the
|
|
264
|
-
* per-role marker set); without one, lift a
|
|
307
|
+
* per-role marker set); without one, lift a team pause by removing its marker. Idempotent
|
|
265
308
|
* like pause: with no marker there is nothing to do. No live harness required — resuming
|
|
266
309
|
* before startup just means the next `tumwater run` starts unpaused. */
|
|
267
310
|
export async function cmdResume(root, args = []) {
|
|
268
|
-
const role =
|
|
311
|
+
const role = pauseTargetRole(root, args);
|
|
269
312
|
if (role) {
|
|
270
|
-
// resumeRole (src/
|
|
313
|
+
// resumeRole (src/team/team-state.ts) is the single remover of the per-role marker; a false
|
|
271
314
|
// return means the role was never paused — the changed-state contract roleResumeMessage
|
|
272
315
|
// words ("is already paused" vs "was not paused").
|
|
273
316
|
say(roleResumeMessage(root, role, resumeRole(root, role)));
|
|
274
317
|
return;
|
|
275
318
|
}
|
|
276
|
-
//
|
|
319
|
+
// resumeTeam (src/team/team-state.ts) is the single remover, shared with the GUI toggle;
|
|
277
320
|
// a false return means there was no marker to lift.
|
|
278
|
-
if (!
|
|
321
|
+
if (!resumeTeam(root)) {
|
|
279
322
|
say("not paused");
|
|
280
323
|
return;
|
|
281
324
|
}
|
|
282
325
|
const { when, tail } = markerApplyNote(root);
|
|
283
|
-
// The per-role pause outlives a
|
|
284
|
-
//
|
|
326
|
+
// The per-role pause outlives a team resume (the markers are independent), so the resumed
|
|
327
|
+
// team's confirmation must name the roles still individually paused instead of claiming
|
|
285
328
|
// "role loops tick again" for loops the scheduler keeps gated.
|
|
286
329
|
const still = pausedRoles(root);
|
|
287
330
|
const roleNote = still.length > 0
|
|
288
331
|
? ` (${still.join(", ")} ${agree(still.length, "is", "are")} still individually paused — \`tumwater resume --role <id>\` lifts each)`
|
|
289
332
|
: "";
|
|
290
|
-
say(`${
|
|
333
|
+
say(`${teamResumePhrase()}${when}${roleNote}${tail}`);
|
|
334
|
+
}
|
|
335
|
+
/** The role id retire targets: a built-in or configured id as for the other marker commands, or
|
|
336
|
+
* else any well-formed id that still has a branch or merge ref (a loop no config names). */
|
|
337
|
+
async function retireTargetRole(root, args) {
|
|
338
|
+
const role = flagValue(args, "--role");
|
|
339
|
+
if (role === null)
|
|
340
|
+
return requiredNamedRole(root, args, "retire");
|
|
341
|
+
if (!role)
|
|
342
|
+
fail(ROLE_VALUE_ERROR);
|
|
343
|
+
if (allRoleIds().includes(role))
|
|
344
|
+
return role;
|
|
345
|
+
let ids;
|
|
346
|
+
try {
|
|
347
|
+
ids = knownRoleIds(loadConfig(root));
|
|
348
|
+
}
|
|
349
|
+
catch (err) {
|
|
350
|
+
fail(errorMessage(err));
|
|
351
|
+
}
|
|
352
|
+
if (ids.includes(role) || (await hasRetirableLeftovers(root, role)))
|
|
353
|
+
return role;
|
|
354
|
+
fail(unknownRoleMessage(role, ids));
|
|
291
355
|
}
|
|
292
356
|
/** `tumwater retire --role <id> [--force] [--json]`: remove a disabled loop's leftover worktree,
|
|
293
|
-
* branch,
|
|
294
|
-
*
|
|
295
|
-
*
|
|
357
|
+
* branch, merge ref, and paused-state marker. The collection, safety rails, and removal live in
|
|
358
|
+
* src/operator/retire.ts; this is the render layer — one line per removed artifact, the skip lines
|
|
359
|
+
* that make a second run idempotent, or the `--json` payload. */
|
|
296
360
|
export async function cmdRetire(root, args) {
|
|
297
|
-
//
|
|
298
|
-
//
|
|
299
|
-
const role =
|
|
300
|
-
const
|
|
361
|
+
// The config exists only to validate the id against built-ins plus user-defined loops, with
|
|
362
|
+
// the leftover-branch gate for ids no config names; a missing --role fails before it is needed.
|
|
363
|
+
const role = await retireTargetRole(root, args);
|
|
364
|
+
const force = args.includes("--force");
|
|
365
|
+
if (args.includes("--dry-run")) {
|
|
366
|
+
const preview = await previewRetire(root, role);
|
|
367
|
+
if (args.includes("--json")) {
|
|
368
|
+
sayJson({
|
|
369
|
+
role,
|
|
370
|
+
dryRun: true,
|
|
371
|
+
wouldRemove: preview.wouldRemove,
|
|
372
|
+
skipped: preview.skipped,
|
|
373
|
+
objections: preview.objections,
|
|
374
|
+
});
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
for (const name of preview.wouldRemove)
|
|
378
|
+
say(`would remove ${artifactPhrase(name, role)}`);
|
|
379
|
+
for (const name of preview.skipped)
|
|
380
|
+
say(`nothing to remove: ${artifactPhrase(name, role)}`);
|
|
381
|
+
for (const reason of preview.objections)
|
|
382
|
+
say(force ? `--force overrides: ${reason}` : `would refuse: ${reason}`);
|
|
383
|
+
say("dry run — nothing removed");
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
const result = await retireRole(root, role, { force });
|
|
301
387
|
if (args.includes("--json")) {
|
|
302
388
|
sayJson({ role, removed: result.removed, skipped: result.skipped });
|
|
303
389
|
return;
|
|
@@ -309,3 +395,24 @@ export async function cmdRetire(root, args) {
|
|
|
309
395
|
if (result.removed.length === 0 && result.skipped.length === 0)
|
|
310
396
|
say(`nothing to remove for role ${role}`);
|
|
311
397
|
}
|
|
398
|
+
/** `tumwater revert <sha>`: undo one merged change on main, committing the inverse as
|
|
399
|
+
* `tumwater(operator): Revert <short sha>` under the primary-worktree lock. The lock, branch
|
|
400
|
+
* guard, and fail-closed ref/conflict handling live in src/operator/revert.ts; this renderer
|
|
401
|
+
* names the new main head and reminds the caller the reverted role may redo the work. */
|
|
402
|
+
export async function cmdRevert(root, args) {
|
|
403
|
+
// No flags; exactly one positional names the commit to undo. A flag or extra positional
|
|
404
|
+
// fails before anything is looked up or written.
|
|
405
|
+
if (args.length !== 1)
|
|
406
|
+
fail("revert needs a commit (e.g. run tumwater revert 1a2b3c4)");
|
|
407
|
+
// A flag-shaped token is a dropped flag, not a commit: refuse it before rev-parse turns a
|
|
408
|
+
// lone `--json` into the misleading "no such commit". Mirrors `config set`'s
|
|
409
|
+
// flag-looking-value gate and gui-command.ts's --token gate, and a real sha never starts
|
|
410
|
+
// with `-`.
|
|
411
|
+
if (args[0].startsWith("-"))
|
|
412
|
+
fail(`revert takes no flags (got ${args[0]}); pass the commit to undo as the only argument`);
|
|
413
|
+
const result = await revertMergedCommit(root, args[0]);
|
|
414
|
+
if (!result.ok)
|
|
415
|
+
fail(result.error);
|
|
416
|
+
say(`main is now ${shortSha(result.headSha)}`);
|
|
417
|
+
say("the reverted role may redo the work, so cancel or edit its PLANS.md/BUGS.md entry if it should not");
|
|
418
|
+
}
|