tumwater 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/build-info.json +2 -2
- package/dist/src/agent/backend-traits.js +13 -0
- package/dist/src/agent/claude/claude-backend.js +2 -2
- package/dist/src/agent/claude/claude-stream.js +3 -2
- package/dist/src/agent/codex/codex-backend.js +3 -3
- package/dist/src/agent/codex/codex-stream.js +3 -1
- package/dist/src/agent/opencode/opencode-backend.js +2 -2
- package/dist/src/backlog/backlog-archive.js +60 -25
- package/dist/src/backlog/backlog-cli-view.js +3 -28
- package/dist/src/backlog/backlog-cli.js +9 -64
- package/dist/src/backlog/backlog-commit.js +17 -9
- package/dist/src/backlog/backlog-eligibility.js +44 -26
- package/dist/src/backlog/backlog-parts.js +23 -13
- package/dist/src/backlog/backlog-release.js +33 -16
- package/dist/src/baseline/main-guardrails.js +0 -73
- package/dist/src/baseline/main-red.js +31 -21
- package/dist/src/baseline/main-watch.js +82 -34
- package/dist/src/brief/brief-edit.js +3 -1
- package/dist/src/budget/budget-fallback-view.js +288 -0
- package/dist/src/budget/budget-wiring.js +158 -0
- package/dist/src/budget/budget.js +2 -1
- package/dist/src/build/dep-install.js +64 -1
- package/dist/src/cli/cli-args.js +63 -0
- package/dist/src/cli/cli-flag-specs.js +33 -0
- package/dist/src/cli/cli-query-commands.js +16 -41
- package/dist/src/cli/cli-review-commands.js +113 -0
- package/dist/src/cli/cli-role-command.js +118 -13
- package/dist/src/cli/cli-start-summary.js +10 -3
- package/dist/src/cli/cli-start.js +33 -5
- package/dist/src/cli/completion.js +21 -1
- package/dist/src/cli/config-commands.js +17 -8
- package/dist/src/cli/help.js +74 -14
- package/dist/src/cli/log-commands.js +26 -13
- package/dist/src/cli/question-commands.js +7 -1
- package/dist/src/cli.js +71 -6
- package/dist/src/collections.js +31 -4
- package/dist/src/config/config-editable-keys.js +3 -0
- package/dist/src/config/config-fallbacks.js +3 -2
- package/dist/src/config/config-validation.js +47 -42
- package/dist/src/config/config-views.js +10 -2
- package/dist/src/config/config-write.js +33 -1
- package/dist/src/config/config.js +7 -2
- package/dist/src/doctor/doctor-backlog-bugs.js +85 -42
- package/dist/src/doctor/doctor-backlog-plans.js +214 -79
- package/dist/src/doctor/doctor-backlog.js +155 -22
- package/dist/src/doctor/doctor-check-names.js +5 -1
- package/dist/src/doctor/doctor-checks.js +10 -15
- package/dist/src/doctor/doctor-claude-checks.js +6 -9
- package/dist/src/doctor/doctor-codex-checks.js +6 -9
- package/dist/src/doctor/doctor-contention-checks.js +244 -118
- package/dist/src/doctor/doctor-fallback-checks.js +130 -0
- package/dist/src/doctor/doctor-holds.js +4 -6
- package/dist/src/doctor/doctor-model-checks.js +45 -200
- package/dist/src/doctor/doctor-notify.js +7 -2
- package/dist/src/doctor/doctor-opencode-checks.js +7 -5
- package/dist/src/doctor/doctor-pi-checks.js +62 -0
- package/dist/src/doctor/doctor-project-checks.js +106 -12
- package/dist/src/doctor/doctor.js +10 -5
- package/dist/src/doctor/probe.js +14 -1
- package/dist/src/events/event-format.js +7 -6
- package/dist/src/events/event-labels.js +0 -1
- package/dist/src/events/event-read.js +13 -0
- package/dist/src/events/event-window.js +20 -2
- package/dist/src/failure/failure-data.js +9 -4
- package/dist/src/failure/failure-render.js +8 -8
- package/dist/src/failure/time-spend.js +0 -64
- package/dist/src/files/file-queue.js +17 -0
- package/dist/src/gates/budget-gates.js +27 -257
- package/dist/src/gates/gate-polls.js +49 -141
- package/dist/src/gates/maintenance-quota.js +27 -17
- package/dist/src/git/commit-message.js +12 -0
- package/dist/src/git/git.js +18 -0
- package/dist/src/git/slot-salvage.js +3 -3
- package/dist/src/gui/gui-backlog-commands.js +3 -3
- package/dist/src/gui/gui-backlog-endpoints.js +5 -4
- package/dist/src/gui/gui-command.js +19 -1
- package/dist/src/gui/gui-config-commands.js +75 -9
- package/dist/src/gui/gui-endpoint-commands.js +2 -2
- package/dist/src/gui/gui-operator-commands.js +134 -7
- package/dist/src/gui/gui-prompt-commands.js +15 -6
- package/dist/src/gui/gui-report-endpoints.js +10 -3
- package/dist/src/gui/gui-server.js +16 -2
- package/dist/src/history/history-queued.js +8 -0
- package/dist/src/history/history.js +2 -5
- package/dist/src/inbox/inbox-top.js +4 -9
- package/dist/src/merge/merge-core.js +2 -25
- package/dist/src/merge/merge-drain.js +22 -135
- package/dist/src/merge/merge-pipeline.js +1 -1
- package/dist/src/merge/merge-post-review.js +154 -30
- package/dist/src/merge/merge-queue-render.js +11 -12
- package/dist/src/merge/merge-queue.js +22 -1
- package/dist/src/merge/merge-requeue.js +1 -0
- package/dist/src/merge/merge-stack.js +1 -7
- package/dist/src/merge/merge-to-main.js +1 -9
- package/dist/src/merge/merge-top.js +33 -19
- package/dist/src/merge/merge-verify.js +64 -9
- package/dist/src/notes-cli.js +8 -3
- package/dist/src/orchestrator/orchestrator-poll.js +47 -15
- package/dist/src/orchestrator/orchestrator-scheduling.js +4 -2
- package/dist/src/orchestrator/orchestrator.js +5 -4
- package/dist/src/orchestrator/scheduling-scope.js +51 -7
- package/dist/src/orchestrator/scheduling-skip.js +15 -5
- package/dist/src/paths.js +16 -6
- package/dist/src/pi/pi-backend.js +2 -2
- package/dist/src/process/run-marker.js +8 -0
- package/dist/src/prompt/gate-notes.js +1 -1
- package/dist/src/prompt/gate-prompts.js +30 -7
- package/dist/src/prompt/notes-backlog.js +13 -10
- package/dist/src/prompt/prompt.js +3 -1
- package/dist/src/report/report-data.js +49 -69
- package/dist/src/report/report-fold.js +11 -12
- package/dist/src/report/report-render.js +6 -4
- package/dist/src/report/report.js +2 -5
- package/dist/src/review/diff-rules.js +53 -0
- package/dist/src/review/known-flakes.js +12 -0
- package/dist/src/review/post-merge-review-accept.js +51 -0
- package/dist/src/review/post-merge-review-render.js +112 -0
- package/dist/src/review/post-merge-review.js +157 -24
- package/dist/src/review/protected-edits.js +22 -7
- package/dist/src/review/quarantine-render.js +65 -0
- package/dist/src/review/review.js +33 -49
- package/dist/src/roles/role-catalog.js +60 -2
- package/dist/src/roles/role-guidance.js +10 -0
- package/dist/src/roles/role-labels.js +6 -3
- package/dist/src/roles/role-render.js +43 -8
- package/dist/src/roles/role-view.js +36 -9
- package/dist/src/roles/roles.js +3 -3
- package/dist/src/run/ci-parity.js +121 -0
- package/dist/src/run/qa-coverage.js +50 -15
- package/dist/src/run/run-detail-data.js +9 -13
- package/dist/src/run/run-detail-render.js +1 -8
- package/dist/src/run/run-finalize.js +1 -0
- package/dist/src/run/run-prompt.js +14 -0
- package/dist/src/run/run-stage.js +66 -37
- package/dist/src/run/run-verdict.js +14 -1
- package/dist/src/run/stage-check.js +17 -11
- package/dist/src/run/stage-checks-shared.js +65 -0
- package/dist/src/run/stage-name-checks.js +204 -0
- package/dist/src/run/stage-path-checks.js +16 -515
- package/dist/src/run/stage-prose-checks.js +261 -0
- package/dist/src/runner/leftover.js +50 -19
- package/dist/src/runner/revision.js +12 -23
- package/dist/src/runner/runner-outcome.js +4 -2
- package/dist/src/runner/runner-recovery.js +18 -3
- package/dist/src/runner/runner-run.js +21 -8
- package/dist/src/runner/runner-state.js +4 -3
- package/dist/src/scheduling/claim-entries.js +49 -0
- package/dist/src/status/status-data.js +6 -1
- package/dist/src/status/status-polls.js +15 -0
- package/dist/src/status/status-roles.js +11 -0
- package/dist/src/team/reclaim.js +2 -2
- package/dist/src/text/datetime.js +3 -2
- package/dist/src/text/phrases.js +6 -3
- package/dist/src/text/text.js +11 -0
- package/dist/src/ui/badges.js +34 -1
- package/dist/src/ui/gui/gui-client-backlog.js +25 -3
- package/dist/src/ui/gui/gui-client-boot.js +63 -41
- package/dist/src/ui/gui/gui-client-drawer-config.js +56 -14
- package/dist/src/ui/gui/gui-client-drawer.js +29 -4
- package/dist/src/ui/gui/gui-client-feed.js +1 -1
- package/dist/src/ui/gui/gui-client-history.js +74 -72
- package/dist/src/ui/gui/gui-client-labels.js +5 -1
- package/dist/src/ui/gui/gui-client-operator.js +6 -2
- package/dist/src/ui/gui/gui-client-pending.js +188 -11
- package/dist/src/ui/gui/gui-client-report-chart.js +36 -8
- package/dist/src/ui/gui/gui-client-report.js +20 -5
- package/dist/src/ui/gui/gui-client-settings.js +109 -3
- package/dist/src/ui/gui/gui-client-team.js +11 -0
- package/dist/src/ui/gui/gui-client.js +11 -1
- package/dist/src/ui/gui/gui-page.js +1 -0
- package/dist/src/ui/gui/gui-styles.js +4 -0
- package/dist/src/ui/run-progress-model.js +2 -1
- package/dist/src/ui/status-payload.js +237 -9
- package/dist/src/ui/status-render.js +12 -4
- package/dist/src/ui/team-alerts.js +512 -37
- package/dist/src/ui/tone.js +7 -0
- package/dist/src/ui/tui/tui-backlog.js +7 -5
- package/dist/src/ui/tui/tui-editor-attach.js +1 -7
- package/dist/src/ui/tui/tui-editor-backends.js +1 -6
- package/dist/src/ui/tui/tui-editor-brief.js +4 -24
- package/dist/src/ui/tui/tui-editor-budget.js +1 -8
- package/dist/src/ui/tui/tui-editor-check.js +29 -0
- package/dist/src/ui/tui/tui-editor-checks.js +28 -0
- package/dist/src/ui/tui/tui-editor-concurrency.js +1 -0
- package/dist/src/ui/tui/tui-editor-config.js +19 -6
- package/dist/src/ui/tui/tui-editor-entry.js +3 -17
- package/dist/src/ui/tui/tui-editor-instances.js +9 -0
- package/dist/src/ui/tui/tui-editor-instructions.js +7 -26
- package/dist/src/ui/tui/tui-editor-interval.js +18 -7
- package/dist/src/ui/tui/tui-editor-notes.js +6 -24
- package/dist/src/ui/tui/tui-editor-quiet.js +2 -12
- package/dist/src/ui/tui/tui-editor-retire.js +1 -7
- package/dist/src/ui/tui/tui-editor-role-enabled.js +126 -0
- package/dist/src/ui/tui/tui-editor-role-key.js +1 -6
- package/dist/src/ui/tui/tui-editor-session.js +41 -0
- package/dist/src/ui/tui/tui-editor-slots.js +1 -0
- package/dist/src/ui/tui/tui-editors.js +10 -1
- package/dist/src/ui/tui/tui-frame.js +18 -6
- package/dist/src/ui/tui/tui-keys.js +9 -0
- package/dist/src/ui/tui/tui-pane-activity.js +21 -8
- package/dist/src/ui/tui/tui-pane-backlog.js +36 -9
- package/dist/src/ui/tui/tui-pane-ctx.js +1 -1
- package/dist/src/ui/tui/tui-pane-health.js +113 -3
- package/dist/src/ui/tui/tui-pane-history.js +44 -0
- package/dist/src/ui/tui/tui-pane-pending.js +285 -56
- package/dist/src/ui/tui/tui-pane-queued.js +7 -2
- package/dist/src/ui/tui/tui-pane-report.js +9 -3
- package/dist/src/ui/tui/tui-pane-session.js +9 -0
- package/dist/src/ui/tui/tui-pane.js +16 -10
- package/dist/src/ui/tui/tui-panes.js +56 -7
- package/dist/src/ui/tui/tui.js +8 -5
- package/dist/src/verdict/fix-claim.js +47 -1
- package/dist/src/verdict/reply-contract.js +35 -0
- package/package.json +1 -1
- package/dist/src/baseline/main-culprit.js +0 -94
- package/dist/src/baseline/main-revert.js +0 -133
|
@@ -10,6 +10,7 @@ import { templateIds } from "../init/init-templates.js";
|
|
|
10
10
|
import { DOCTOR_CHECK_NAMES } from "../doctor/doctor-check-names.js";
|
|
11
11
|
import { allRoleIds } from "../roles/roles.js";
|
|
12
12
|
import { EVENT_TYPE_LABELS } from "../events/event-labels.js";
|
|
13
|
+
import { POST_REVIEW_STATES } from "../review/post-merge-review-render.js";
|
|
13
14
|
import { HELP, helpStanzas } from "./help.js";
|
|
14
15
|
export const SUPPORTED_SHELLS = ["bash", "zsh", "fish"];
|
|
15
16
|
/** The completable words of a stanza's usage part: the text after `tumwater <command>` up to the
|
|
@@ -162,6 +163,11 @@ const EVENT_TYPE_CODES = Object.keys(EVENT_TYPE_LABELS).sort();
|
|
|
162
163
|
* vocabulary the way `--type` and `--result` complete theirs. One source, so a new level
|
|
163
164
|
* completes the moment THINKING_LEVELS grows. */
|
|
164
165
|
const THINKING_LEVEL_WORDS = [...THINKING_LEVELS];
|
|
166
|
+
/** The review states `reviews --status` filters by: the canonical POST_REVIEW_STATES set,
|
|
167
|
+
* spread preserving its insertion order, so `--status` completes the same vocabulary
|
|
168
|
+
* `parseReviewStatusList` accepts. One source, so a new state completes the moment
|
|
169
|
+
* POST_REVIEW_STATES grows. */
|
|
170
|
+
const REVIEW_STATE_WORDS = [...POST_REVIEW_STATES];
|
|
165
171
|
/** The doctor check names, each in plain double quotes, for the scripts to loop over. The names
|
|
166
172
|
* hold no `"`, `$`, backtick or `\`, so plain double quotes are exact; a test pins this. */
|
|
167
173
|
const DOCTOR_CHECK_WORDS = DOCTOR_CHECK_NAMES.map((n) => `"${n}"`).join(" ");
|
|
@@ -231,10 +237,18 @@ function bashScript(help) {
|
|
|
231
237
|
` _tumwater_list "${EVENT_TYPE_CODES.join(" ")}" "$cur"`,
|
|
232
238
|
` return 0`,
|
|
233
239
|
` fi`,
|
|
240
|
+
` if [ "$prev" = "--status" ]; then`,
|
|
241
|
+
` _tumwater_list "${REVIEW_STATE_WORDS.join(" ")}" "$cur"`,
|
|
242
|
+
` return 0`,
|
|
243
|
+
` fi`,
|
|
234
244
|
` if [ "$prev" = "--thinking" ]; then`,
|
|
235
245
|
` COMPREPLY=( $(compgen -W "${THINKING_LEVEL_WORDS.join(" ")}" -- "$cur") )`,
|
|
236
246
|
` return 0`,
|
|
237
247
|
` fi`,
|
|
248
|
+
` if [ "$prev" = "--backend" ]; then`,
|
|
249
|
+
` COMPREPLY=( $(compgen -W "${BACKEND_IDS.join(" ")}" -- "$cur") )`,
|
|
250
|
+
` return 0`,
|
|
251
|
+
` fi`,
|
|
238
252
|
` if [ "$prev" = "--by" ]; then`,
|
|
239
253
|
` COMPREPLY=( $(compgen -W "${HISTORY_SUMMARY_GROUPS.join(" ")}" -- "$cur") )`,
|
|
240
254
|
` return 0`,
|
|
@@ -276,7 +290,8 @@ function bashScript(help) {
|
|
|
276
290
|
].join("\n");
|
|
277
291
|
}
|
|
278
292
|
/** The value suffix a fish flag line carries: what completes after the flag. `--role`, `--result`
|
|
279
|
-
* and `--
|
|
293
|
+
* `--type` and `--status` take comma lists, so they complete per item through
|
|
294
|
+
* `__fish_complete_list`,
|
|
280
295
|
* skipping the items already listed. */
|
|
281
296
|
function fishFlagValue(command, flag) {
|
|
282
297
|
if (flag === "--role")
|
|
@@ -291,8 +306,12 @@ function fishFlagValue(command, flag) {
|
|
|
291
306
|
return ` -x -a "(__fish_complete_list , __tumwater_results)"`;
|
|
292
307
|
if (flag === "--type")
|
|
293
308
|
return ` -x -a "(__fish_complete_list , __tumwater_types)"`;
|
|
309
|
+
if (flag === "--status")
|
|
310
|
+
return ` -x -a "(__fish_complete_list , __tumwater_review_states)"`;
|
|
294
311
|
if (flag === "--thinking")
|
|
295
312
|
return ` -x -a "${THINKING_LEVEL_WORDS.join(" ")}"`;
|
|
313
|
+
if (flag === "--backend")
|
|
314
|
+
return ` -x -a "${BACKEND_IDS.join(" ")}"`;
|
|
296
315
|
if (flag === "--by")
|
|
297
316
|
return ` -x -a "${HISTORY_SUMMARY_GROUPS.join(" ")}"`;
|
|
298
317
|
// `role --model` completes the closed tier set; `history --model` completes the log's selectors.
|
|
@@ -314,6 +333,7 @@ function fishScript(help) {
|
|
|
314
333
|
`function __tumwater_results; printf '%s\\n' ${Object.values(HISTORY_RESULT_FLAG_NAMES).join(" ")} | __tumwater_unlisted; end`,
|
|
315
334
|
`function __tumwater_models; tumwater completion --models 2>/dev/null | __tumwater_unlisted; end`,
|
|
316
335
|
`function __tumwater_types; printf '%s\\n' ${EVENT_TYPE_CODES.join(" ")} | __tumwater_unlisted; end`,
|
|
336
|
+
`function __tumwater_review_states; printf '%s\\n' ${REVIEW_STATE_WORDS.join(" ")} | __tumwater_unlisted; end`,
|
|
317
337
|
`complete -c tumwater -n __fish_use_subcommand -a "${spec.commands.join(" ")}"`,
|
|
318
338
|
];
|
|
319
339
|
for (const command of spec.commands) {
|
|
@@ -50,15 +50,24 @@ function screenOperableKey(root, key) {
|
|
|
50
50
|
const { config, error } = loadConfigSafe(root);
|
|
51
51
|
if (config === undefined)
|
|
52
52
|
fail(error); // validateConfig's message, via the standard fail()
|
|
53
|
-
if (parsed.kind === "
|
|
54
|
-
const id = parsed.kind === "map" ? parsed.role : parsed.id;
|
|
53
|
+
if (parsed.kind === "role") {
|
|
55
54
|
const ids = knownRoleIds(config);
|
|
56
|
-
if (!ids.includes(id))
|
|
57
|
-
fail(unknownRoleMessage(id, ids));
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
55
|
+
if (!ids.includes(parsed.id))
|
|
56
|
+
fail(unknownRoleMessage(parsed.id, ids));
|
|
57
|
+
const fieldError = unknownRoleFieldError(parsed.id, parsed.field);
|
|
58
|
+
if (fieldError)
|
|
59
|
+
fail(fieldError);
|
|
60
|
+
}
|
|
61
|
+
else if (parsed.kind === "map") {
|
|
62
|
+
// Only role-keyed maps check the role catalog. `maxConcurrentPerProvider.<provider>` is
|
|
63
|
+
// keyed by provider, not a role id, and `config set` writes it without the role check —
|
|
64
|
+
// so get/unset must agree, or set would succeed while get/unset refused the provider as
|
|
65
|
+
// an "unknown role". A typo'd role id in the other two maps is refused the same way set's
|
|
66
|
+
// validator treats it.
|
|
67
|
+
if (parsed.map !== "maxConcurrentPerProvider") {
|
|
68
|
+
const ids = knownRoleIds(config);
|
|
69
|
+
if (!ids.includes(parsed.role))
|
|
70
|
+
fail(unknownRoleMessage(parsed.role, ids));
|
|
62
71
|
}
|
|
63
72
|
}
|
|
64
73
|
return { parsed, config };
|
package/dist/src/cli/help.js
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* <command>` from it by splitting the usage block on its ` tumwater <command>` lines, so a
|
|
5
5
|
* topic can never drift out of sync with the full listing. */
|
|
6
6
|
import { suggestClosest } from "../text/suggest.js";
|
|
7
|
-
/**
|
|
8
|
-
|
|
7
|
+
/** The role-grouped summary's JSON keys, quoted verbatim in `history --summary --json` help
|
|
8
|
+
* (its `--by model` twin is the hardcoded `{ models, total }` beside it). */
|
|
9
|
+
const SUMMARY_JSON_KEYS = "{ roles, total }";
|
|
9
10
|
export const HELP = `tumwater — an autonomous development team, built on pi
|
|
10
11
|
|
|
11
12
|
Usage:
|
|
@@ -84,12 +85,22 @@ Usage:
|
|
|
84
85
|
(merge runs and backlog tallies omitted); --until <duration>
|
|
85
86
|
ends that window the given duration ago (requires --since);
|
|
86
87
|
not combinable with --days
|
|
87
|
-
tumwater doctor [<check>...] [--json] [--reprobe] [--problems] [--kill-orphans]
|
|
88
|
+
tumwater doctor [<check>...] [--json] [--reprobe] [--problems] [--kill-orphans] [--commit-backlog] [--resync-install] [--drop-broken-pins] [--retire-leftovers]
|
|
88
89
|
Pre-flight check: node, git, repo, config, fallback
|
|
89
90
|
model, pi, locks, update, orphans, macOS port leak
|
|
90
|
-
(read-only unless --kill-orphans,
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
(read-only unless --kill-orphans, --commit-backlog
|
|
92
|
+
or --resync-install or --drop-broken-pins or --retire-leftovers:
|
|
93
|
+
--kill-orphans first kills the orphaned processes the orphans
|
|
94
|
+
check lists, each with its descendants; --commit-backlog first commits each uncommitted tracked
|
|
95
|
+
backlog or brief file (PLANS.md, BUGS.md, QUESTIONS.md, their archives, the
|
|
96
|
+
brief) on the base branch under the primary-worktree lock, so a merge the
|
|
97
|
+
dirty file blocks can proceed; --resync-install re-syncs the root checkout's
|
|
98
|
+
install on demand under the merge lock, so the always-on install alert's
|
|
99
|
+
advice is one command; --drop-broken-pins first drops each refs/tumwater/
|
|
100
|
+
pin whose commit is gone — the manual git update-ref -d the tumwater pins
|
|
101
|
+
row names; --retire-leftovers first retires each disabled role's leftover
|
|
102
|
+
worktree, branch and merge ref, like tumwater retire --role <id> per role,
|
|
103
|
+
never --force; exit 0/1; --json prints the report object —
|
|
93
104
|
header, the checks array with level, name, and detail,
|
|
94
105
|
and verdict; probes image-input models — --reprobe
|
|
95
106
|
re-probes every one, bypassing the 7-day cache; --problems prints only the warn
|
|
@@ -112,7 +123,7 @@ Usage:
|
|
|
112
123
|
roles still merges the built-in catalog per entry, so
|
|
113
124
|
disable a role with roles.<id>.enabled false, not by
|
|
114
125
|
omitting it
|
|
115
|
-
tumwater logs [-f/--follow] [-n N] [--since <duration>] [--until <duration>] [--role <id>[,<id>...]] [--grep <text>] [--type <event type>[,<event type>...]] [--count] [--json]
|
|
126
|
+
tumwater logs [-f/--follow] [-n N] [--since <duration>] [--until <duration>] [--role <id>[,<id>...]] [--grep <text>] [--type <event type>[,<event type>...]] [--count] [--notable] [--json]
|
|
116
127
|
Show (and follow) tumwater events; --since shows the
|
|
117
128
|
events of the past window (capped at 7d); --until ends that
|
|
118
129
|
window the given duration ago (requires --since); --grep shows
|
|
@@ -123,7 +134,10 @@ Usage:
|
|
|
123
134
|
composes with --since); --count
|
|
124
135
|
prints one \`N \u00d7 <type>\` line per present event type
|
|
125
136
|
(most-frequent first) over the selected window instead
|
|
126
|
-
of the rows; --
|
|
137
|
+
of the rows; --notable
|
|
138
|
+
keeps only non-routine events, the dashboard feed's Notable
|
|
139
|
+
choice (it composes with --since, --grep, --type, --role,
|
|
140
|
+
-n, -f and --json); --json
|
|
127
141
|
prints each event as one
|
|
128
142
|
JSON object per line (NDJSON) — the raw event objects
|
|
129
143
|
as stored in the log, instead of the rendered text
|
|
@@ -238,6 +252,26 @@ Usage:
|
|
|
238
252
|
execution order (so it merges next when ready), preserving any
|
|
239
253
|
notBefore deferral, refusing an entry a live merge is merging and
|
|
240
254
|
--role/-f, and answering already-first for position 1
|
|
255
|
+
tumwater reviews [--accept <n>] [--role <id>[,<id>...]] [--status <state>[,<state>...]] [--json]
|
|
256
|
+
List the post-merge review queue (\.tumwater/state
|
|
257
|
+
/post-review.json) in FIFO order: one line per merged commit
|
|
258
|
+
waiting for its review — position, role, short sha, summary,
|
|
259
|
+
and either its rejected reasons
|
|
260
|
+
(\`[rejected — <reasons>]\`) or its
|
|
261
|
+
failed-review-run count (\`[N failed review runs]\`); an empty
|
|
262
|
+
queue prints \`post-merge review queue empty\`; --json prints
|
|
263
|
+
the normalized { pending: [...] } list for scripts; --role
|
|
264
|
+
keeps only the listed roles' entries (a base role id taking
|
|
265
|
+
its instances too), each under its position in the whole
|
|
266
|
+
queue; --status keeps only the entries in the listed states
|
|
267
|
+
(waiting, failing, rejected; an entry turned back is rejected
|
|
268
|
+
first), each under its position in the whole queue, and
|
|
269
|
+
composes with --role; --accept <n>
|
|
270
|
+
removes the Nth queued entry (the position \`tumwater reviews\`
|
|
271
|
+
prints) as deliberately accepted unreviewed, logging a warning;
|
|
272
|
+
it refuses an entry whose repair already merged (\`fixed\`);
|
|
273
|
+
no ready-repo gate for the read, a missing or torn file degrades
|
|
274
|
+
to the empty or well-formed list
|
|
241
275
|
tumwater backlog [--ready] [--grep <text>] [-f/--follow] [--json]
|
|
242
276
|
Show planned features, open bugs, and open questions (the
|
|
243
277
|
dashboards' backlog view); --json prints machine-readable
|
|
@@ -253,6 +287,14 @@ Usage:
|
|
|
253
287
|
-f/--follow repaints the backlog every couple of seconds until
|
|
254
288
|
Ctrl+C or the process that started it exits, so a backgrounded
|
|
255
289
|
follow does not outlive its parent; it refuses --json
|
|
290
|
+
tumwater quarantine [--json] List the flake quarantine — the tests the review gate recorded
|
|
291
|
+
as failing then passing on the same tree
|
|
292
|
+
(.tumwater/state/quarantine.json), oldest first, each naming
|
|
293
|
+
its first-seen and last-seen age, flake count, and the BUGS.md
|
|
294
|
+
entry filed for it (or "not filed"); empty prints
|
|
295
|
+
"flake quarantine is empty"; --json prints the same rows as
|
|
296
|
+
{ tests: [...] }; it is read-only — a test leaves the list only
|
|
297
|
+
when its filed bug leaves BUGS.md ## Open
|
|
256
298
|
tumwater archive [--json] Move the finished entries still under PLANS.md ## Done and
|
|
257
299
|
BUGS.md ## Fixed to the top of PLANS-DONE.md and BUGS-FIXED.md
|
|
258
300
|
and commit them on the base branch, waiting for a merge in
|
|
@@ -268,10 +310,11 @@ Usage:
|
|
|
268
310
|
non-blank line is the symptom, the rest is the body kept
|
|
269
311
|
line for line; wakes the bugfix role
|
|
270
312
|
tumwater bug --release <n> [--json]
|
|
271
|
-
Lift the Refused
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
313
|
+
Lift the Refused, Waiting for the next failure, or a
|
|
314
|
+
Needs-review/Needs-replan note held past its threshold
|
|
315
|
+
(one per run) from the Nth entry in BUGS.md ## Open (as
|
|
316
|
+
\`tumwater bug --list\` lists it), commit it on main, and wake
|
|
317
|
+
the bugfix role; --json prints {file, title, lifted}
|
|
275
318
|
tumwater bug --top <n> [--json]
|
|
276
319
|
Move the Nth entry (as \`tumwater bug --list\` lists it) to
|
|
277
320
|
the head of BUGS.md ## Open, so bugfix claims it next; commit
|
|
@@ -324,9 +367,11 @@ Usage:
|
|
|
324
367
|
the first non-blank line is the title, the rest is the body
|
|
325
368
|
kept line for line; wakes the feature role
|
|
326
369
|
tumwater plan --release <n> [--json]
|
|
327
|
-
Lift the Refused
|
|
370
|
+
Lift the Refused, Waiting for the next failure, or a
|
|
371
|
+
Needs-review/Needs-replan note held past its threshold
|
|
372
|
+
(one per run) from the Nth entry in PLANS.md ## Planned
|
|
328
373
|
(as \`tumwater plan --list\` lists it), commit it on main,
|
|
329
|
-
and wake the feature role; --json prints {file, title}
|
|
374
|
+
and wake the feature role; --json prints {file, title, lifted}
|
|
330
375
|
tumwater plan --top <n> [--json]
|
|
331
376
|
Move the Nth entry (as \`tumwater plan --list\` lists it) to
|
|
332
377
|
the head of PLANS.md ## Planned, so feature claims it next;
|
|
@@ -461,6 +506,15 @@ Usage:
|
|
|
461
506
|
for the base id
|
|
462
507
|
tumwater role <id> --clear-thinking
|
|
463
508
|
Clear that role's thinking level
|
|
509
|
+
tumwater role <id> --backend <id>
|
|
510
|
+
Set that role's backend (pi|claude|opencode|codex — the
|
|
511
|
+
agent that runs the model); a model that cannot pair with
|
|
512
|
+
the backend is refused (e.g. a claude backend with a
|
|
513
|
+
provider/model id, or a pi backend with a bare model id),
|
|
514
|
+
written to roles.<id>.backend for the base id
|
|
515
|
+
tumwater role <id> --clear-backend
|
|
516
|
+
Clear that role's backend (back to the team/tier
|
|
517
|
+
default backend)
|
|
464
518
|
tumwater role <id> --enable Enable the role, written to roles.<id>.enabled
|
|
465
519
|
(the base id for an instance)
|
|
466
520
|
tumwater role <id> --disable Disable it (refused for the director); clean up
|
|
@@ -471,6 +525,12 @@ Usage:
|
|
|
471
525
|
written to roles.<id>.instances for the base id
|
|
472
526
|
tumwater role <id> --clear-instances
|
|
473
527
|
Clear that role's instance count (back to 1)
|
|
528
|
+
tumwater role <id> --review-after-merge
|
|
529
|
+
Review that role's commits after they merge, written
|
|
530
|
+
to review.afterMerge (the base id for an instance)
|
|
531
|
+
tumwater role <id> --clear-review-after-merge
|
|
532
|
+
Review it before merge again, removing it from
|
|
533
|
+
review.afterMerge
|
|
474
534
|
tumwater prompt <text...> Send a message to the director's inbox
|
|
475
535
|
tumwater prompt --file <path> Send a message read from a file ("-" reads stdin)
|
|
476
536
|
tumwater prompt --role <id> <text...>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { fail, say, sayJsonLine } from "./cli-output.js";
|
|
2
2
|
import { hasFollowFlag, LOGS_TRANSCRIPT_VALUE_ERROR } from "./cli-flag-specs.js";
|
|
3
|
-
import { durationLabel, failRivalShapes, flagValue, parseCountFlag, parseGrepFlag, parseRoleListScope, parseRoleScope, parseSinceFlag, parseTypeFlag } from "./cli-args.js";
|
|
3
|
+
import { durationLabel, failRivalShapes, failUntilShorterThanSince, flagValue, parseCountFlag, parseGrepFlag, parseRoleListScope, parseRoleScope, parseSinceFlag, parseTypeFlag } from "./cli-args.js";
|
|
4
4
|
import { LOGS_SINCE_MAX_MS, readEventsSince, SPARSE_WINDOW_NOTE, } from "../events/event-window.js";
|
|
5
5
|
import { compareCodeUnits, increment } from "../collections.js";
|
|
6
6
|
import { eventTypeTally } from "../events/event-tally.js";
|
|
7
7
|
import { eventRole, parseEventLine, readEventsTailWithEnd } from "../events/event-read.js";
|
|
8
|
-
import { eventMatchesGrep, formatEvent } from "../events/event-format.js";
|
|
8
|
+
import { eventMatchesGrep, eventResult, formatEvent } from "../events/event-format.js";
|
|
9
9
|
import { canonicalEventType } from "../events/event-legacy.js";
|
|
10
10
|
import { EVENT_TYPE_LABELS, eventTypeLabel } from "../events/event-labels.js";
|
|
11
11
|
import { followFile } from "../files/tail.js";
|
|
@@ -15,6 +15,7 @@ import { typoSuffix } from "../text/suggest.js";
|
|
|
15
15
|
import { splitCommaList } from "../text/text.js";
|
|
16
16
|
import { createTranscriptFilter, createTranscriptRenderer } from "../ui/transcript.js";
|
|
17
17
|
import { readTranscriptTail } from "../ui/transcript-tail.js";
|
|
18
|
+
import { isNotableEvent } from "../ui/tone.js";
|
|
18
19
|
import { eventsLogPath, piLogPath } from "../paths.js";
|
|
19
20
|
/** The read-only observing half of the CLI's non-dispatch commands: `tumwater logs` and its
|
|
20
21
|
* `--transcript` view, split out of cli.ts so the entry point stays a dispatch table. Unlike
|
|
@@ -147,9 +148,18 @@ export async function cmdLogs(root, args, seams = {}) {
|
|
|
147
148
|
: (e) => roleInScope(eventRole(e), roles);
|
|
148
149
|
if (roles !== null)
|
|
149
150
|
rejectTranscriptViewRival(rest, "--role", true);
|
|
151
|
+
// `--notable` culls routine bookkeeping events the way the dashboard Activity feed's Notable
|
|
152
|
+
// choice does (src/ui/tone.ts eventKind): only non-routine rows pass the feed. One harness-side
|
|
153
|
+
// rule (isNotableEvent) shared with the TUI Activity pane, so the CLI feed and the pane cannot
|
|
154
|
+
// disagree about what a notable filter hides. It composes with --since, --grep, --type, --role,
|
|
155
|
+
// -n, -f, --count and --json, and — like --json/--count/--type/--since — is a rival only of the
|
|
156
|
+
// --transcript transcript view.
|
|
157
|
+
const notable = rest.includes("--notable");
|
|
158
|
+
rejectTranscriptViewRival(rest, "--notable", notable);
|
|
150
159
|
const feedFilter = (e) => (grepLower === null || eventMatchesGrep(e, grepLower)) &&
|
|
151
160
|
(typeFilter === null || typeFilter(e)) &&
|
|
152
|
-
(roleFilter === null || roleFilter(e))
|
|
161
|
+
(roleFilter === null || roleFilter(e)) &&
|
|
162
|
+
(!notable || isNotableEvent(e.type, eventResult(e)));
|
|
153
163
|
// Empty-match prose: grep/type keep their wording when given; a role-only filter names the roles.
|
|
154
164
|
const roleEmpty = roles === null ? "" : `no events from ${roles.join(", ")}`;
|
|
155
165
|
const filtersGrepOrType = grepPattern !== null || type !== null;
|
|
@@ -180,10 +190,7 @@ export async function cmdLogs(root, args, seams = {}) {
|
|
|
180
190
|
// both flags. (--prompt requires --transcript, so it is excluded with it.)
|
|
181
191
|
const ms = parseSinceFlag(rest, "logs --since", LOGS_SINCE_MAX_MS);
|
|
182
192
|
const untilMs = parseSinceFlag(rest, "logs --until", LOGS_SINCE_MAX_MS, "--until");
|
|
183
|
-
|
|
184
|
-
fail("logs --until requires --since");
|
|
185
|
-
if (untilMs !== null && ms !== null && untilMs >= ms)
|
|
186
|
-
fail(`logs --until (${durationLabel(untilMs)}) must be shorter than --since (${durationLabel(ms)})`);
|
|
193
|
+
failUntilShorterThanSince("logs", untilMs, ms);
|
|
187
194
|
if (ms !== null) {
|
|
188
195
|
if (follow)
|
|
189
196
|
failRivalShapes("logs --since", "-f/--follow", 'follow means "from now on"; --since shows a bounded past window');
|
|
@@ -204,7 +211,9 @@ export async function cmdLogs(root, args, seams = {}) {
|
|
|
204
211
|
// In JSON mode an empty result answers silently — an empty output IS the machine-readable
|
|
205
212
|
// answer, and prose would corrupt a consumer's NDJSON stream.
|
|
206
213
|
if (!json) {
|
|
207
|
-
if (grepPattern
|
|
214
|
+
if (notable && grepPattern === null && type === null && roles === null)
|
|
215
|
+
say(`no notable events ${windowPhrase}`);
|
|
216
|
+
else if (grepPattern !== null && type !== null)
|
|
208
217
|
say(`no events matching "${grepPattern}" or ${typeEmpty(type, typePieces)}`);
|
|
209
218
|
else if (type !== null)
|
|
210
219
|
say(typeEmpty(type, typePieces));
|
|
@@ -251,7 +260,7 @@ export async function cmdLogs(root, args, seams = {}) {
|
|
|
251
260
|
// the tail-with-end shape (not the per-poll cached readEvents) so the follow below can seed
|
|
252
261
|
// from the byte end this very read covered instead of a later stat of the file.
|
|
253
262
|
const tail = readEventsTailWithEnd(root, limit);
|
|
254
|
-
const shown = grepLower !== null || type !== null || roleFilter !== null ? tail.events.filter(feedFilter) : tail.events;
|
|
263
|
+
const shown = grepLower !== null || type !== null || roleFilter !== null || notable ? tail.events.filter(feedFilter) : tail.events;
|
|
255
264
|
// `--count` over the -n view answers with the tally of exactly the filtered rows the plain
|
|
256
265
|
// view would print, and carries over the plain view's empty-match prose below (a `--type`
|
|
257
266
|
// or `--grep` that matches nothing names its value; an empty unfiltered -n view prints
|
|
@@ -259,8 +268,10 @@ export async function cmdLogs(root, args, seams = {}) {
|
|
|
259
268
|
if (count) {
|
|
260
269
|
if (shown.length !== 0)
|
|
261
270
|
sayTally(shown, json);
|
|
262
|
-
else if (!json && (filtersGrepOrType || roles !== null)) {
|
|
263
|
-
if (grepPattern
|
|
271
|
+
else if (!json && (filtersGrepOrType || roles !== null || notable)) {
|
|
272
|
+
if (notable && grepPattern === null && type === null && roles === null)
|
|
273
|
+
say("no notable events");
|
|
274
|
+
else if (grepPattern !== null && type !== null)
|
|
264
275
|
say(`no events matching "${grepPattern}" or ${typeEmpty(type, typePieces)}`);
|
|
265
276
|
// Type-empty gets the hint (only the type branch); a grep-only empty stays the bare
|
|
266
277
|
// no-match line, because a free-text pattern is not an event type to suggest against.
|
|
@@ -277,8 +288,10 @@ export async function cmdLogs(root, args, seams = {}) {
|
|
|
277
288
|
// The empty-match line is prose: in JSON mode the empty output is the answer. It fires
|
|
278
289
|
// for either filter, naming the flag's value(s) whose match came up empty so the operator
|
|
279
290
|
// knows what to loosen.
|
|
280
|
-
if (shown.length === 0 && !json && (filtersGrepOrType || roles !== null)) {
|
|
281
|
-
if (grepPattern
|
|
291
|
+
if (shown.length === 0 && !json && (filtersGrepOrType || roles !== null || notable)) {
|
|
292
|
+
if (notable && grepPattern === null && type === null && roles === null)
|
|
293
|
+
say("no notable events");
|
|
294
|
+
else if (grepPattern !== null && type !== null)
|
|
282
295
|
say(`no events matching "${grepPattern}" or ${typeEmpty(type, typePieces)}`);
|
|
283
296
|
// Type-empty gets the hint (only the type branch); a grep-only empty stays the bare
|
|
284
297
|
// no-match line, because a free-text pattern is not an event type to suggest against.
|
|
@@ -148,7 +148,13 @@ export async function cmdQuestions(root, args) {
|
|
|
148
148
|
}
|
|
149
149
|
if (arg.startsWith("--") && arg !== "--json") {
|
|
150
150
|
rejectEqualsForm(arg, [JSON_FLAG, FILE_SPEC, { names: ["--show"], value: true, valueName: "<n>" }, { names: ["--grep"], value: true, valueName: "<text>" }]);
|
|
151
|
-
|
|
151
|
+
// The complete flag set (this gate only sees `--`-flags, but `-n` is a real flag the
|
|
152
|
+
// list should not omit) plus the same did-you-mean hint the sibling commands'
|
|
153
|
+
// unknown-argument gate (cli-flag-specs's failUnknownArgument) attaches, so a mistyped
|
|
154
|
+
// `--ansered` names `--answered` instead of only printing the valid list — the missing
|
|
155
|
+
// treatment the questions gate was the one unknown-argument caller without.
|
|
156
|
+
const spellings = ["--json", "--file", "--answered", "--show", "--grep", "-n"];
|
|
157
|
+
fail(`unknown argument: ${arg} (valid flags for tumwater questions: ${spellings.join(", ")})${typoSuffix(arg, spellings)}`);
|
|
152
158
|
}
|
|
153
159
|
if (!numbered) {
|
|
154
160
|
// The position is a plain-decimal positive integer (parsePositiveInt's rule, the same
|
package/dist/src/cli.js
CHANGED
|
@@ -13,10 +13,11 @@
|
|
|
13
13
|
* ready-repo gate. An error escaping main() funnels through its catch to fail(errorMessage(err)),
|
|
14
14
|
* the CLI's one error path. */
|
|
15
15
|
import { parseCountFlag, parseGrepFlag, parseModelFlag, parseResultFlag } from "./cli/cli-args.js";
|
|
16
|
-
import { grepFlagSpec, GUI_FLAG_SPECS, JSON_FLAG, LAST_FLAG, N_FLAG, rejectUnknownArgs, resultFlagSpec, modelFlagSpec, byFlagSpec, LOGS_TRANSCRIPT_FLAG, ROLE_FLAG, START_FLAG_SPECS, sinceFlagSpec, untilFlagSpec, STAT_FLAG, PATCH_FLAG, PROMPT_FLAG, TRANSCRIPT_FLAG, SUMMARY_FLAG, COUNT_FLAG, typeFlagSpec, DRY_RUN_FLAG, FORCE_FLAG, FOLLOW_FLAG, STOP_CANCEL_FLAG, STOP_IN_FLAG, REPROBE_FLAG, PROBLEMS_FLAG, KILL_ORPHANS_FLAG, } from "./cli/cli-flag-specs.js";
|
|
16
|
+
import { grepFlagSpec, GUI_FLAG_SPECS, JSON_FLAG, LAST_FLAG, N_FLAG, rejectUnknownArgs, resultFlagSpec, modelFlagSpec, byFlagSpec, LOGS_TRANSCRIPT_FLAG, ROLE_FLAG, START_FLAG_SPECS, sinceFlagSpec, untilFlagSpec, STAT_FLAG, PATCH_FLAG, PROMPT_FLAG, TRANSCRIPT_FLAG, SUMMARY_FLAG, COUNT_FLAG, NOTABLE_FLAG, typeFlagSpec, DRY_RUN_FLAG, FORCE_FLAG, FOLLOW_FLAG, STOP_CANCEL_FLAG, STOP_IN_FLAG, REPROBE_FLAG, PROBLEMS_FLAG, KILL_ORPHANS_FLAG, COMMIT_BACKLOG_FLAG, RESYNC_INSTALL_FLAG, DROP_BROKEN_PINS_FLAG, RETIRE_LEFTOVERS_FLAG, } from "./cli/cli-flag-specs.js";
|
|
17
17
|
import { fail, say, sayJsonOrRender } from "./cli/cli-output.js";
|
|
18
18
|
import { historyTrailForm, parseDoctorChecks, parsePromptArgs, peelPositionals } from "./cli/cli-command-args.js";
|
|
19
19
|
import { refuseTeamWorkspace, requireReadyRepo } from "./cli/cli-repo-gate.js";
|
|
20
|
+
import { loadConfigSafe } from "./config/config.js";
|
|
20
21
|
import { repoToplevel } from "./git/git.js";
|
|
21
22
|
import { didYouMean } from "./text/suggest.js";
|
|
22
23
|
import { errorMessage, gotSuffix, stripTerminalControls } from "./text/text.js";
|
|
@@ -171,7 +172,7 @@ async function main() {
|
|
|
171
172
|
// No requireReadyRepo gate: doctor's job is to report WHY the environment isn't ready,
|
|
172
173
|
// so it must run outside a git repo and print fail lines rather than throwing.
|
|
173
174
|
const { positionals: checkNames, rest: doctorFlags } = peelPositionals(args);
|
|
174
|
-
rejectUnknownArgs("doctor", doctorFlags, [JSON_FLAG, REPROBE_FLAG, PROBLEMS_FLAG, KILL_ORPHANS_FLAG]);
|
|
175
|
+
rejectUnknownArgs("doctor", doctorFlags, [JSON_FLAG, REPROBE_FLAG, PROBLEMS_FLAG, KILL_ORPHANS_FLAG, COMMIT_BACKLOG_FLAG, RESYNC_INSTALL_FLAG, DROP_BROKEN_PINS_FLAG, RETIRE_LEFTOVERS_FLAG]);
|
|
175
176
|
const only = parseDoctorChecks(checkNames);
|
|
176
177
|
const { runDoctor } = await import("./doctor/doctor.js");
|
|
177
178
|
let orphanKill;
|
|
@@ -185,6 +186,59 @@ async function main() {
|
|
|
185
186
|
say("");
|
|
186
187
|
}
|
|
187
188
|
}
|
|
189
|
+
let backlogCommit;
|
|
190
|
+
if (args.includes("--commit-backlog")) {
|
|
191
|
+
const { commitUncommittedBacklog, backlogCommitLines } = await import("./doctor/doctor-backlog.js");
|
|
192
|
+
const { config } = loadConfigSafe(root); // the report's own config, so base resolution and the off-base skip match the `uncommitted backlog` row's
|
|
193
|
+
backlogCommit = await commitUncommittedBacklog(root, config);
|
|
194
|
+
// The text form prints the commits before the report; --json keeps stdout one document.
|
|
195
|
+
if (!args.includes("--json")) {
|
|
196
|
+
for (const line of backlogCommitLines(backlogCommit))
|
|
197
|
+
say(line);
|
|
198
|
+
say("");
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
let resyncInstall;
|
|
202
|
+
if (args.includes("--resync-install")) {
|
|
203
|
+
const { resyncRootInstall, rootInstallResyncLines } = await import("./build/dep-install.js");
|
|
204
|
+
resyncInstall = await resyncRootInstall(root);
|
|
205
|
+
// The text form prints the re-sync outcome before the report; --json keeps stdout one
|
|
206
|
+
// document, carrying the result as `resyncInstall` on the payload.
|
|
207
|
+
if (!args.includes("--json")) {
|
|
208
|
+
for (const line of rootInstallResyncLines(resyncInstall))
|
|
209
|
+
say(line);
|
|
210
|
+
say("");
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
let brokenPinDrop;
|
|
214
|
+
if (args.includes("--drop-broken-pins")) {
|
|
215
|
+
const { dropBrokenPins, brokenPinDropLines } = await import("./doctor/doctor-contention-checks.js");
|
|
216
|
+
brokenPinDrop = await dropBrokenPins(root);
|
|
217
|
+
// The text form prints the drop before the report; --json keeps stdout one document,
|
|
218
|
+
// carrying the result as `brokenPinDrop` on the payload.
|
|
219
|
+
if (!args.includes("--json")) {
|
|
220
|
+
const lines = brokenPinDropLines(brokenPinDrop);
|
|
221
|
+
for (const line of lines)
|
|
222
|
+
say(line);
|
|
223
|
+
if (lines.length > 0)
|
|
224
|
+
say("");
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
let leftoverRetire;
|
|
228
|
+
if (args.includes("--retire-leftovers")) {
|
|
229
|
+
const { retireLeftovers, leftoverRetireLines } = await import("./doctor/doctor-contention-checks.js");
|
|
230
|
+
const { config } = loadConfigSafe(root); // the row's own config, so the reap and the report agree on what is retirable
|
|
231
|
+
leftoverRetire = await retireLeftovers(root, config);
|
|
232
|
+
// The text form prints the retire lines before the report; --json keeps stdout one
|
|
233
|
+
// document, carrying the result as `leftoverRetire` on the payload.
|
|
234
|
+
if (!args.includes("--json")) {
|
|
235
|
+
const lines = leftoverRetireLines(leftoverRetire);
|
|
236
|
+
for (const line of lines)
|
|
237
|
+
say(line);
|
|
238
|
+
if (lines.length > 0)
|
|
239
|
+
say("");
|
|
240
|
+
}
|
|
241
|
+
}
|
|
188
242
|
const { renderDoctor, renderDoctorProblems, problemsOnly } = await import("./doctor/doctor-render.js");
|
|
189
243
|
// The terminal probes unclaimed-verified models by default (7-day cache); --reprobe
|
|
190
244
|
// forces every claimed model. The TUI/GUI call runDoctor with the default "never", so a
|
|
@@ -198,10 +252,14 @@ async function main() {
|
|
|
198
252
|
// hands --json the filtered one; the exit code below reads the full report either way.
|
|
199
253
|
// --kill-orphans adds the kill result to the JSON object as `orphanKill`.
|
|
200
254
|
const withKill = (payload) => (orphanKill === undefined ? payload : { ...payload, orphanKill });
|
|
255
|
+
const withBacklogCommit = (payload) => backlogCommit === undefined ? withKill(payload) : withKill({ ...payload, backlogCommit });
|
|
256
|
+
const withResyncInstall = (payload) => resyncInstall === undefined ? withBacklogCommit(payload) : withBacklogCommit({ ...payload, resyncInstall });
|
|
257
|
+
const withBrokenPinDrop = (payload) => brokenPinDrop === undefined ? withResyncInstall(payload) : withResyncInstall({ ...payload, brokenPinDrop });
|
|
258
|
+
const withLeftoverRetire = (payload) => leftoverRetire === undefined ? withBrokenPinDrop(payload) : withBrokenPinDrop({ ...payload, leftoverRetire });
|
|
201
259
|
if (args.includes("--problems"))
|
|
202
|
-
sayJsonOrRender(args, args.includes("--json") ?
|
|
260
|
+
sayJsonOrRender(args, args.includes("--json") ? withLeftoverRetire(problemsOnly(report)) : report, renderDoctorProblems);
|
|
203
261
|
else
|
|
204
|
-
sayJsonOrRender(args, args.includes("--json") ?
|
|
262
|
+
sayJsonOrRender(args, args.includes("--json") ? withLeftoverRetire(report) : report, renderDoctor);
|
|
205
263
|
if (report.checks.some((c) => c.level === "fail"))
|
|
206
264
|
process.exitCode = 1; // Warnings never fail the exit.
|
|
207
265
|
break;
|
|
@@ -217,6 +275,7 @@ async function main() {
|
|
|
217
275
|
typeFlagSpec(TYPE_VALUE_ERROR),
|
|
218
276
|
JSON_FLAG,
|
|
219
277
|
COUNT_FLAG,
|
|
278
|
+
NOTABLE_FLAG,
|
|
220
279
|
ROLE_FLAG,
|
|
221
280
|
LOGS_TRANSCRIPT_FLAG,
|
|
222
281
|
PROMPT_FLAG,
|
|
@@ -262,9 +321,15 @@ async function main() {
|
|
|
262
321
|
case "queue":
|
|
263
322
|
await (await import("./cli/cli-query-commands.js")).cmdQueue(root, args);
|
|
264
323
|
break;
|
|
324
|
+
case "reviews":
|
|
325
|
+
await (await import("./cli/cli-review-commands.js")).cmdReviews(root, args);
|
|
326
|
+
break;
|
|
265
327
|
case "backlog":
|
|
266
328
|
await (await import("./cli/cli-query-commands.js")).cmdBacklog(root, args);
|
|
267
329
|
break;
|
|
330
|
+
case "quarantine":
|
|
331
|
+
await (await import("./cli/cli-query-commands.js")).cmdQuarantine(root, args);
|
|
332
|
+
break;
|
|
268
333
|
case "archive":
|
|
269
334
|
rejectUnknownArgs("archive", args, [JSON_FLAG]);
|
|
270
335
|
await requireReadyRepo(root);
|
|
@@ -463,7 +528,7 @@ async function main() {
|
|
|
463
528
|
const topic = helpTopic(name);
|
|
464
529
|
if (topic === null) {
|
|
465
530
|
const suggestion = suggestCommand(name);
|
|
466
|
-
fail(`no help topic: ${name}${didYouMean(suggestion)} (try \`tumwater help\` for the full command list)`);
|
|
531
|
+
fail(`no help topic: ${JSON.stringify(name)}${didYouMean(suggestion)} (try \`tumwater help\` for the full command list)`);
|
|
467
532
|
}
|
|
468
533
|
say(topic);
|
|
469
534
|
}
|
|
@@ -474,7 +539,7 @@ async function main() {
|
|
|
474
539
|
}
|
|
475
540
|
default: {
|
|
476
541
|
const suggestion = suggestCommand(command);
|
|
477
|
-
fail(`unknown command: ${command}${didYouMean(suggestion)} (try \`tumwater help\`)`);
|
|
542
|
+
fail(`unknown command: ${JSON.stringify(command)}${didYouMean(suggestion)} (try \`tumwater help\`)`);
|
|
478
543
|
}
|
|
479
544
|
}
|
|
480
545
|
}
|
package/dist/src/collections.js
CHANGED
|
@@ -31,14 +31,32 @@ export function getOrCreate(map, key, make) {
|
|
|
31
31
|
map.set(key, created);
|
|
32
32
|
return created;
|
|
33
33
|
}
|
|
34
|
+
/** Filter a list by a keep predicate, returning the kept items and each kept item's 1-based
|
|
35
|
+
* position in the original list — the single home of the "keep some, but number by the whole
|
|
36
|
+
* list" step, shared by the two queue-scope twins `scopeMergeQueue` (src/merge/
|
|
37
|
+
* merge-queue-render.ts) and `scopePostReviews` (src/review/post-merge-review-render.ts), so
|
|
38
|
+
* `tumwater queue --role` and `tumwater reviews --role` keep the same whole-queue positions
|
|
39
|
+
* a later position-addressed verb (`--cancel`, `--accept`) still addresses. The predicate runs
|
|
40
|
+
* once per item, in input order; positions run 1..length, not a kept-only renumbering. */
|
|
41
|
+
export function filterKeepPositions(items, keep) {
|
|
42
|
+
const positions = [];
|
|
43
|
+
const kept = items.filter((item, i) => {
|
|
44
|
+
const isKept = keep(item);
|
|
45
|
+
if (isKept)
|
|
46
|
+
positions.push(i + 1);
|
|
47
|
+
return isKept;
|
|
48
|
+
});
|
|
49
|
+
return { kept, positions };
|
|
50
|
+
}
|
|
34
51
|
/** Increment a Map counter, seeding a missing key at 1 — the single home of the
|
|
35
52
|
* `map.set(k, (map.get(k) ?? 0) + 1)` step, shared by the failure-spread kind tally
|
|
36
53
|
* (src/team/failure-spread.ts), the diff multiset's line tally (merge/merge-diff.ts), the
|
|
37
|
-
* section-title tally (backlog/backlog-structure.ts), the
|
|
38
|
-
* (cli/
|
|
54
|
+
* section-title tally (backlog/backlog-structure.ts), the logs' per-type tally
|
|
55
|
+
* (cli/log-commands.ts), the flaky-check tally
|
|
39
56
|
* (doctor/doctor-project-checks.ts), and the multi-runner base tally
|
|
40
|
-
* (orchestrator/orchestrator-scheduling.ts)
|
|
41
|
-
*
|
|
57
|
+
* (orchestrator/orchestrator-scheduling.ts) — the exit-summary round's outcome tally
|
|
58
|
+
* (cli/cli-start-summary.ts) seeds its own copy of this step inline — so those sites cannot
|
|
59
|
+
* drift on how a missing key is seeded. */
|
|
42
60
|
export function increment(map, key) {
|
|
43
61
|
map.set(key, (map.get(key) ?? 0) + 1);
|
|
44
62
|
}
|
|
@@ -118,6 +136,15 @@ export function lowerMedian(values) {
|
|
|
118
136
|
const sorted = [...values].sort((a, b) => a - b);
|
|
119
137
|
return sorted[Math.floor((sorted.length - 1) / 2)];
|
|
120
138
|
}
|
|
139
|
+
/** Most-days-waited-first comparator, ties in ascending index (file order) — `b.days - a.days ||
|
|
140
|
+
* a.index - b.index`, the single home of that one-line ordering. Shared by the stale-bug
|
|
141
|
+
* alert (src/ui/status-payload.ts's staleOpenBugs) and the stale-waiting-bug doctor row
|
|
142
|
+
* (src/doctor/doctor-backlog-bugs.ts's staleWaitingBugRows), which both build `{index, days}`
|
|
143
|
+
* rows off the same backlog scan, so the two cannot drift on how ties order. The generic holds
|
|
144
|
+
* any row type carrying both fields; a row without a natural index passes its first-seen order. */
|
|
145
|
+
export function byDaysThenIndex(a, b) {
|
|
146
|
+
return b.days - a.days || a.index - b.index;
|
|
147
|
+
}
|
|
121
148
|
/** Ascending string comparator by UTF-16 code unit — the locale-independent order a bare `.sort()`
|
|
122
149
|
* uses, unlike `localeCompare` (which `rankByCount` uses for its tiebreak). Used directly by
|
|
123
150
|
* src/cli/cli-start-summary.ts, src/cli/log-commands.ts and src/history/history-data.ts, and by
|
|
@@ -35,4 +35,7 @@ export const EDITABLE_CONFIG_KEYS = [
|
|
|
35
35
|
// The per-backend section (plans/agent-backends.md, part 4a/7): the whole `agents` object is
|
|
36
36
|
// edited here as JSON. The CLI's dotted `backends.<id>.bin`/`.args` merge one field at a time.
|
|
37
37
|
"backends",
|
|
38
|
+
// The declared check (plans/portability.md §6/7, the no-check row): its Settings row is the
|
|
39
|
+
// surfaced remedy for the amber `no-check` alert — set `check.command` and the alert clears.
|
|
40
|
+
"check",
|
|
38
41
|
];
|
|
@@ -19,8 +19,9 @@ import { FALLBACK_REVIEW_TIMEOUT_S, REVIEW_TIMEOUT_S, configForRole, isTierName,
|
|
|
19
19
|
* claude + pi fleets, part 2/4"): the backend is the step's own, else its tier primary's
|
|
20
20
|
* resolved backend (the tier entry's, else the top level's, else pi). A pair routed to a
|
|
21
21
|
* non-pi backend carries that backend as `backend` and its name as `provider` — the key the
|
|
22
|
-
* budget gate's handback (
|
|
23
|
-
* runner-run.ts's setRunPair) matches on — and the id passes to it as
|
|
22
|
+
* budget gate's handback (src/budget/budget-wiring.ts's runOnPair against the run's
|
|
23
|
+
* seamHoldKey capture, runner-run.ts's setRunPair) matches on — and the id passes to it as
|
|
24
|
+
* written (routeModel); a
|
|
24
25
|
* pi pair keeps its own provider (a bare pi selector keeps undefined). */
|
|
25
26
|
function seamKeyedPairFields(config, tier, provider, model, thinking, stepBackend) {
|
|
26
27
|
const backend = stepBackend ?? topTierBackend(config, tier) ?? config.backend ?? "pi";
|