mergestorm 0.3.18 → 0.3.20
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 +4 -3
- package/dist/api.d.ts +2 -2
- package/dist/api.js +42 -2
- package/dist/automation-catalog.d.ts +44 -0
- package/dist/automation-catalog.js +10 -0
- package/dist/client/index.d.ts +4 -1
- package/dist/client/index.js +4 -1
- package/dist/commands/browse.d.ts +1 -1
- package/dist/commands/browse.js +3 -3
- package/dist/commands/help.js +1 -1
- package/dist/commands/queue.d.ts +1 -1
- package/dist/commands/queue.js +21 -6
- package/dist/commands/registry.js +13 -6
- package/dist/commands/review-client.js +2 -10
- package/dist/commands/settings.d.ts +12 -4
- package/dist/commands/settings.js +78 -16
- package/dist/commands/skill.d.ts +4 -3
- package/dist/commands/skill.js +16 -6
- package/dist/commands/stack.d.ts +18 -4
- package/dist/commands/stack.js +145 -40
- package/dist/errors.d.ts +1 -1
- package/dist/gh.d.ts +69 -7
- package/dist/gh.js +236 -35
- package/dist/stack-blockers.d.ts +25 -0
- package/dist/stack-blockers.js +120 -0
- package/dist/stack-dto.d.ts +62 -1
- package/dist/stack-dto.js +55 -1
- package/dist/stack-watch.d.ts +51 -0
- package/dist/stack-watch.js +163 -0
- package/dist/ui/banner.js +6 -0
- package/package.json +1 -1
- package/skill/SKILL.md +6 -4
- package/skill/mergestorm-bounce-watch/SKILL.md +51 -0
- package/skill/mergestorm-pr-loop/SKILL.md +9 -4
package/README.md
CHANGED
|
@@ -45,12 +45,12 @@ On a TTY, bare `mergestorm` (or `mergestorm shell`) opens a branded REPL: a comp
|
|
|
45
45
|
|
|
46
46
|
```
|
|
47
47
|
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
48
|
-
│ ▟██▛ mergestorm v0.3.
|
|
48
|
+
│ ▟██▛ mergestorm v0.3.20 │
|
|
49
49
|
│ ▜▙ ● msk_live_… · maelstrom │
|
|
50
50
|
│ [████████░░░░░░░░░░░░░░░░░░░░░░] 25% used · Resets Sep 5, 10:53am (UTC)│
|
|
51
51
|
│ │
|
|
52
52
|
│ review a diff · usage for tabs · /help for all commands │
|
|
53
|
-
│ New in v0.3.
|
|
53
|
+
│ New in v0.3.20: stack watch pair-gate attention and upstack issues │
|
|
54
54
|
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
55
55
|
mergestorm
|
|
56
56
|
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
@@ -86,7 +86,7 @@ mergestorm jobs [n] [--json] Recent review jobs (default 10, max 50)
|
|
|
86
86
|
mergestorm branches [n] Recently reviewed branches (arrow-pick on TTY)
|
|
87
87
|
mergestorm chain [slug] Branch review-chain timeline (default: current branch)
|
|
88
88
|
mergestorm whoami [--json] Key prefix, plan, API base, config path
|
|
89
|
-
mergestorm skill install --claude|--cursor Copy the
|
|
89
|
+
mergestorm skill install --claude|--cursor|--agents Copy the Mergestorm skills into this repo
|
|
90
90
|
mergestorm thread <slug> Jobs in a review thread
|
|
91
91
|
mergestorm stack create [name] New local stack layer (optional `--onto` / `--trunk` / `--extend`)
|
|
92
92
|
mergestorm stack submit Push layers, open PRs (`gh`), register via adopt (optional `--extend`)
|
|
@@ -94,6 +94,7 @@ mergestorm stack list [--json] List registered stacks
|
|
|
94
94
|
mergestorm stack adopt <owner/repo>#<pr> Import an existing open PR chain
|
|
95
95
|
mergestorm stack restack <stack-id> Restack descendants
|
|
96
96
|
mergestorm stack land <stack-id> Land / promote (into review unit when present)
|
|
97
|
+
mergestorm stack wait <stack-id> [--timeout <s>] Wait for stack attention (default 45s, max 300s; 0 = one snapshot, no wait)
|
|
97
98
|
mergestorm stack reset --force Clear local authoring state (not branches/PRs)
|
|
98
99
|
```
|
|
99
100
|
|
package/dist/api.d.ts
CHANGED
|
@@ -159,9 +159,9 @@ export declare function applyPrReviewQuery(query: URLSearchParams, opts: PrRevie
|
|
|
159
159
|
*/
|
|
160
160
|
export { SETTINGS_WRITABLE_KEYS };
|
|
161
161
|
export type { SettingsWritableKey };
|
|
162
|
-
export type SettingsPatch = Partial<
|
|
162
|
+
export type SettingsPatch = Partial<import("./automation-catalog.js").BearerSettingsValues>;
|
|
163
163
|
/** GET and PATCH `/api/v1/settings` both answer with this shape. */
|
|
164
|
-
export type SettingsResponse =
|
|
164
|
+
export type SettingsResponse = import("./automation-catalog.js").BearerSettingsValues & {
|
|
165
165
|
cyclone_connected: boolean;
|
|
166
166
|
github_connected: boolean;
|
|
167
167
|
};
|
package/dist/api.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { apiBase, loadConfig, resolveApiKey } from "./config.js";
|
|
2
2
|
import { BEARER_SETTINGS_KEYS as SETTINGS_WRITABLE_KEYS, } from "./automation-catalog.js";
|
|
3
|
-
import { CommandError, REVIEW_EXIT, rateLimitedMessage } from "./errors.js";
|
|
3
|
+
import { CommandError, REVIEW_EXIT, rateLimitedMessage, } from "./errors.js";
|
|
4
4
|
/** Default per-request timeout so a hung API cannot stall the shell/CI forever. */
|
|
5
5
|
export const DEFAULT_API_TIMEOUT_MS = 30_000;
|
|
6
6
|
/** Message prefix of the CommandError thrown when a request exceeds its timeout. */
|
|
@@ -382,7 +382,43 @@ export async function adoptStack(owner, repo, prNumber, cfg, policy) {
|
|
|
382
382
|
throw new CommandError(rateLimitedMessage(retryAfterSeconds), REVIEW_EXIT.rate_limited, "rate_limited", { retryAfterSeconds });
|
|
383
383
|
}
|
|
384
384
|
if (status !== 200) {
|
|
385
|
-
|
|
385
|
+
const error = body && typeof body === "object" && "error" in body && typeof body.error === "string"
|
|
386
|
+
? body.error
|
|
387
|
+
: undefined;
|
|
388
|
+
const message = body && typeof body === "object" && "message" in body && typeof body.message === "string"
|
|
389
|
+
? body.message
|
|
390
|
+
: undefined;
|
|
391
|
+
const code = error === "auth_invalid" ||
|
|
392
|
+
error === "api_timeout" ||
|
|
393
|
+
error === "not_a_repo" ||
|
|
394
|
+
error === "not_found" ||
|
|
395
|
+
error === "missing_api_key" ||
|
|
396
|
+
error === "registered_parent" ||
|
|
397
|
+
error === "usage" ||
|
|
398
|
+
error === "review_quota" ||
|
|
399
|
+
error === "review_failed" ||
|
|
400
|
+
error === "review_timeout" ||
|
|
401
|
+
error === "rate_limited" ||
|
|
402
|
+
error === "cyclone_not_installed" ||
|
|
403
|
+
error === "pr_merged" ||
|
|
404
|
+
error === "pr_closed" ||
|
|
405
|
+
error === "not_stacked" ||
|
|
406
|
+
error === "draft" ||
|
|
407
|
+
error === "reserved_branch" ||
|
|
408
|
+
error === "supabase_unconfigured" ||
|
|
409
|
+
error === "cyclone_not_connected" ||
|
|
410
|
+
error === "busy" ||
|
|
411
|
+
error === "adopt_failed"
|
|
412
|
+
? error
|
|
413
|
+
: undefined;
|
|
414
|
+
const fallbackMessage = code === "not_stacked"
|
|
415
|
+
? "This pull request is not part of a stack."
|
|
416
|
+
: code === "supabase_unconfigured"
|
|
417
|
+
? "Stacks are not configured on this server."
|
|
418
|
+
: undefined;
|
|
419
|
+
throw new CommandError(message?.trim()
|
|
420
|
+
? message
|
|
421
|
+
: fallbackMessage ?? `Failed to import stack (HTTP ${status}): ${JSON.stringify(body)}`, 1, code);
|
|
386
422
|
}
|
|
387
423
|
return body;
|
|
388
424
|
}
|
|
@@ -414,6 +450,10 @@ async function stackMutation(stackId, pathSuffix, method, json, failLabel, cfg)
|
|
|
414
450
|
throw new CommandError("Stacks API is not available on this server yet. Deploy the API update or use the dashboard.");
|
|
415
451
|
}
|
|
416
452
|
if (status !== 200) {
|
|
453
|
+
const rejection = body;
|
|
454
|
+
if (method === "PATCH" && rejection?.error === "cyclone_not_connected") {
|
|
455
|
+
throw new CommandError(typeof rejection.message === "string" ? rejection.message : rejection.error, 1, "cyclone_not_connected");
|
|
456
|
+
}
|
|
417
457
|
throw new CommandError(`${failLabel} (HTTP ${status}): ${JSON.stringify(body)}`);
|
|
418
458
|
}
|
|
419
459
|
return body;
|
|
@@ -26,6 +26,18 @@ export declare const BEARER_SETTINGS: readonly [{
|
|
|
26
26
|
readonly key: "cyclone_skip_ci_enabled";
|
|
27
27
|
readonly flag: "--cyclone-skip-ci";
|
|
28
28
|
readonly label: "Skip CI on Cyclone commits";
|
|
29
|
+
}, {
|
|
30
|
+
readonly key: "cyclone_patch_unverified_languages";
|
|
31
|
+
readonly flag: "--cyclone-patch-unverified";
|
|
32
|
+
readonly label: "Patch languages we cannot typecheck";
|
|
33
|
+
}, {
|
|
34
|
+
readonly key: "vortex_auto_overflow_enabled";
|
|
35
|
+
readonly flag: "--vortex-auto-overflow";
|
|
36
|
+
readonly label: "Vortex auto overflow";
|
|
37
|
+
}, {
|
|
38
|
+
readonly key: "vortex_skip_all_clear_comments";
|
|
39
|
+
readonly flag: "--vortex-skip-all-clear";
|
|
40
|
+
readonly label: "Vortex skip All-clear comments";
|
|
29
41
|
}, {
|
|
30
42
|
readonly key: "vortex_seam_specialist_enabled";
|
|
31
43
|
readonly flag: "--vortex-seam";
|
|
@@ -34,8 +46,40 @@ export declare const BEARER_SETTINGS: readonly [{
|
|
|
34
46
|
readonly key: "auto_land_default";
|
|
35
47
|
readonly flag: "--auto-land";
|
|
36
48
|
readonly label: "Auto land (new stacks)";
|
|
49
|
+
}, {
|
|
50
|
+
readonly key: "ignored_bot_logins";
|
|
51
|
+
readonly flag: "--ignore-bot";
|
|
52
|
+
readonly label: "Ignored bot logins";
|
|
53
|
+
readonly kind: "logins";
|
|
54
|
+
}, {
|
|
55
|
+
readonly key: "vortex_bot_skip_check";
|
|
56
|
+
readonly flag: "--vortex-skip-check";
|
|
57
|
+
readonly label: "Vortex bot skip check";
|
|
58
|
+
readonly kind: "enum";
|
|
59
|
+
readonly values: readonly ["none", "neutral"];
|
|
60
|
+
}, {
|
|
61
|
+
readonly key: "vortex_findings_check";
|
|
62
|
+
readonly flag: "--vortex-findings";
|
|
63
|
+
readonly label: "Vortex findings check";
|
|
64
|
+
readonly kind: "enum";
|
|
65
|
+
readonly values: readonly ["failure", "neutral", "success"];
|
|
66
|
+
}, {
|
|
67
|
+
readonly key: "cyclone_patch_failure_check";
|
|
68
|
+
readonly flag: "--cyclone-fail-check";
|
|
69
|
+
readonly label: "Cyclone patch failure check";
|
|
70
|
+
readonly kind: "enum";
|
|
71
|
+
readonly values: readonly ["failure", "neutral"];
|
|
37
72
|
}];
|
|
38
73
|
export type BearerSettingsKey = (typeof BEARER_SETTINGS)[number]["key"];
|
|
39
74
|
export declare const BEARER_SETTINGS_KEYS: readonly BearerSettingsKey[];
|
|
40
75
|
export declare const BEARER_SETTINGS_FLAGS: Record<string, BearerSettingsKey>;
|
|
41
76
|
export declare const BEARER_SETTINGS_LABELS: Record<BearerSettingsKey, string>;
|
|
77
|
+
/** Omitted kind means boolean for the original toggle rows. */
|
|
78
|
+
export type BearerSettingsValues = {
|
|
79
|
+
[Row in (typeof BEARER_SETTINGS)[number] as Row["key"]]: Row extends {
|
|
80
|
+
kind: "logins";
|
|
81
|
+
} ? string[] : Row extends {
|
|
82
|
+
values: readonly (infer Value)[];
|
|
83
|
+
} ? Value : boolean;
|
|
84
|
+
};
|
|
85
|
+
export declare const BEARER_BOOLEAN_SETTINGS_KEYS: ("auto_review_enabled" | "auto_patch_enabled" | "vortex_show_thinking_traces" | "repo_overview_enabled" | "review_unit_land_prs_enabled" | "cyclone_review_unit_land_prs_enabled" | "cyclone_skip_ci_enabled" | "cyclone_patch_unverified_languages" | "vortex_auto_overflow_enabled" | "vortex_skip_all_clear_comments" | "vortex_seam_specialist_enabled" | "auto_land_default" | "ignored_bot_logins" | "vortex_bot_skip_check" | "vortex_findings_check" | "cyclone_patch_failure_check")[];
|
|
@@ -6,9 +6,19 @@ export const BEARER_SETTINGS = [
|
|
|
6
6
|
{ key: "review_unit_land_prs_enabled", flag: "--review-unit-land", label: "Review Unit land PRs" },
|
|
7
7
|
{ key: "cyclone_review_unit_land_prs_enabled", flag: "--cyclone-review-unit-land", label: "Cyclone Review Unit land PRs" },
|
|
8
8
|
{ key: "cyclone_skip_ci_enabled", flag: "--cyclone-skip-ci", label: "Skip CI on Cyclone commits" },
|
|
9
|
+
{ key: "cyclone_patch_unverified_languages", flag: "--cyclone-patch-unverified", label: "Patch languages we cannot typecheck" },
|
|
10
|
+
{ key: "vortex_auto_overflow_enabled", flag: "--vortex-auto-overflow", label: "Vortex auto overflow" },
|
|
11
|
+
{ key: "vortex_skip_all_clear_comments", flag: "--vortex-skip-all-clear", label: "Vortex skip All-clear comments" },
|
|
9
12
|
{ key: "vortex_seam_specialist_enabled", flag: "--vortex-seam", label: "Vortex seam specialist" },
|
|
10
13
|
{ key: "auto_land_default", flag: "--auto-land", label: "Auto land (new stacks)" },
|
|
14
|
+
{ key: "ignored_bot_logins", flag: "--ignore-bot", label: "Ignored bot logins", kind: "logins" },
|
|
15
|
+
{ key: "vortex_bot_skip_check", flag: "--vortex-skip-check", label: "Vortex bot skip check", kind: "enum", values: ["none", "neutral"] },
|
|
16
|
+
{ key: "vortex_findings_check", flag: "--vortex-findings", label: "Vortex findings check", kind: "enum", values: ["failure", "neutral", "success"] },
|
|
17
|
+
{ key: "cyclone_patch_failure_check", flag: "--cyclone-fail-check", label: "Cyclone patch failure check", kind: "enum", values: ["failure", "neutral"] },
|
|
11
18
|
];
|
|
12
19
|
export const BEARER_SETTINGS_KEYS = BEARER_SETTINGS.map((r) => r.key);
|
|
13
20
|
export const BEARER_SETTINGS_FLAGS = Object.fromEntries(BEARER_SETTINGS.map((r) => [r.flag, r.key]));
|
|
14
21
|
export const BEARER_SETTINGS_LABELS = Object.fromEntries(BEARER_SETTINGS.map((r) => [r.key, r.label]));
|
|
22
|
+
export const BEARER_BOOLEAN_SETTINGS_KEYS = BEARER_SETTINGS
|
|
23
|
+
.filter((row) => !("kind" in row))
|
|
24
|
+
.map((row) => row.key);
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
export { DEFAULT_API, apiBase, clearConfig, configPath, keyDisplay, loadConfig, mergestormHome, resolveApiKey, saveConfig, type Config, } from "../config.js";
|
|
2
|
-
export { BEARER_SETTINGS, BEARER_SETTINGS_FLAGS, BEARER_SETTINGS_KEYS, BEARER_SETTINGS_LABELS, type BearerSettingsKey, } from "../automation-catalog.js";
|
|
2
|
+
export { BEARER_SETTINGS, BEARER_BOOLEAN_SETTINGS_KEYS, BEARER_SETTINGS_FLAGS, BEARER_SETTINGS_KEYS, BEARER_SETTINGS_LABELS, type BearerSettingsKey, } from "../automation-catalog.js";
|
|
3
3
|
export { API_TIMEOUT_PREFIX, DEFAULT_API_TIMEOUT_MS, SETTINGS_WRITABLE_KEYS, adoptStack, apiFetch, cancelMergeQueueEntry, devicePost, enqueueStack, ensureUpperPark, getEnrichedStack, getMe, getPrVortexReview, getReview, getSettings, landNextStack, listJobs, listMergeQueueEntries, listStacks, listThreads, parseRetryAfterSeconds, patchSettings, restackStack, setStackAutoLand, setStackPolicy, stackPolicyBody, type ApiFetchInit, type ApiFetchResult, type JobListItem, type MeResponse, type MergeQueueBounceDetail, type MergeQueueBounceKind, type MergeQueueEnqueuedBy, type MergeQueueEnqueuedVia, type MergeQueueEntryDto, type MergeQueueEntryState, type PrReviewFinding, type PrReviewFindings, type PrVortexReview, type SettingsPatch, type SettingsResponse, type SettingsWritableKey, type StackDto, type StackPolicyPatch, type ThreadDetail, type ThreadListItem, } from "../api.js";
|
|
4
4
|
export { CommandError, DetachedError, RATE_LIMITED_HINT, REVIEW_EXIT, isCommandErrorCode, rateLimitedMessage, type CommandErrorCode, type CommandErrorExtras, } from "../errors.js";
|
|
5
5
|
export { REVIEW_POLL_BACKOFF_MS, REVIEW_POLL_DEFAULT_TIMEOUT_MS, REVIEW_POLL_INTERVAL_MS, REVIEW_POLL_MAX_INTERVAL_MS, REVIEW_POLL_MAX_TRANSIENT_RETRIES, REVIEW_POLL_STRETCH_AFTER_MS, REVIEW_POLL_STRETCH_SPAN_MS, REVIEW_THREAD_SLUG_RE, PrReviewPollTimeoutError, ReviewPollTimeoutError, collectReviewInput, formatReviewSubmitError, isTransientReviewPollError, isTransientReviewPollStatus, loadReviewContext, pollPrVortexReview, pollReview, stretchedPollIntervalMs, submitReview, transientRetryWaitMs, type PollReviewOptions, type PollPrVortexReviewOptions, type ReviewInput, type SubmitReviewOptions, } from "../commands/review-client.js";
|
|
6
6
|
export { REVIEW_JOB_ENVELOPE_SCHEMA, toReviewJobEnvelope, type ReviewEnvelopeFallbacks, type ReviewJobEnvelope, type ReviewJobRow, } from "../ui/job-envelope.js";
|
|
7
7
|
export { discoverTrunk } from "../git-stack.js";
|
|
8
|
+
export { BOUNCE_KIND_LABELS, mergeQueueBounceLabel } from "../stack-dto.js";
|
|
9
|
+
export { STACK_WATCH_SCHEMA, STACK_WATCH_DEFAULT_TIMEOUT_MS, StackWatchError, StackWatchTimeoutError, pollStackWatch, type PollStackWatchOptions, type StackWatchCursor, type StackWatchEnvelope, } from "../stack-watch.js";
|
|
10
|
+
export { stackBlockers, stackBlockersSummary, sameHead, type StackBlocker } from "../stack-blockers.js";
|
package/dist/client/index.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
export { DEFAULT_API, apiBase, clearConfig, configPath, keyDisplay, loadConfig, mergestormHome, resolveApiKey, saveConfig, } from "../config.js";
|
|
2
|
-
export { BEARER_SETTINGS, BEARER_SETTINGS_FLAGS, BEARER_SETTINGS_KEYS, BEARER_SETTINGS_LABELS, } from "../automation-catalog.js";
|
|
2
|
+
export { BEARER_SETTINGS, BEARER_BOOLEAN_SETTINGS_KEYS, BEARER_SETTINGS_FLAGS, BEARER_SETTINGS_KEYS, BEARER_SETTINGS_LABELS, } from "../automation-catalog.js";
|
|
3
3
|
export { API_TIMEOUT_PREFIX, DEFAULT_API_TIMEOUT_MS, SETTINGS_WRITABLE_KEYS, adoptStack, apiFetch, cancelMergeQueueEntry, devicePost, enqueueStack, ensureUpperPark, getEnrichedStack, getMe, getPrVortexReview, getReview, getSettings, landNextStack, listJobs, listMergeQueueEntries, listStacks, listThreads, parseRetryAfterSeconds, patchSettings, restackStack, setStackAutoLand, setStackPolicy, stackPolicyBody, } from "../api.js";
|
|
4
4
|
export { CommandError, DetachedError, RATE_LIMITED_HINT, REVIEW_EXIT, isCommandErrorCode, rateLimitedMessage, } from "../errors.js";
|
|
5
5
|
export { REVIEW_POLL_BACKOFF_MS, REVIEW_POLL_DEFAULT_TIMEOUT_MS, REVIEW_POLL_INTERVAL_MS, REVIEW_POLL_MAX_INTERVAL_MS, REVIEW_POLL_MAX_TRANSIENT_RETRIES, REVIEW_POLL_STRETCH_AFTER_MS, REVIEW_POLL_STRETCH_SPAN_MS, REVIEW_THREAD_SLUG_RE, PrReviewPollTimeoutError, ReviewPollTimeoutError, collectReviewInput, formatReviewSubmitError, isTransientReviewPollError, isTransientReviewPollStatus, loadReviewContext, pollPrVortexReview, pollReview, stretchedPollIntervalMs, submitReview, transientRetryWaitMs, } from "../commands/review-client.js";
|
|
6
6
|
export { REVIEW_JOB_ENVELOPE_SCHEMA, toReviewJobEnvelope, } from "../ui/job-envelope.js";
|
|
7
7
|
export { discoverTrunk } from "../git-stack.js";
|
|
8
|
+
export { BOUNCE_KIND_LABELS, mergeQueueBounceLabel } from "../stack-dto.js";
|
|
9
|
+
export { STACK_WATCH_SCHEMA, STACK_WATCH_DEFAULT_TIMEOUT_MS, StackWatchError, StackWatchTimeoutError, pollStackWatch, } from "../stack-watch.js";
|
|
10
|
+
export { stackBlockers, stackBlockersSummary, sameHead } from "../stack-blockers.js";
|
|
@@ -27,7 +27,7 @@ export declare function buildStatusTabLines(input: StatusTabInput): string[];
|
|
|
27
27
|
/** In-place job detail for the Jobs tab (same envelope as `status <id>`). */
|
|
28
28
|
export declare function formatJobDetailLines(row: ReviewJobRow): string[];
|
|
29
29
|
/** Human labels for the Config tab and the static `settings` printout. */
|
|
30
|
-
export declare const SETTINGS_LABELS: Record<"auto_review_enabled" | "auto_patch_enabled" | "vortex_show_thinking_traces" | "repo_overview_enabled" | "review_unit_land_prs_enabled" | "cyclone_review_unit_land_prs_enabled" | "cyclone_skip_ci_enabled" | "vortex_seam_specialist_enabled" | "auto_land_default", string>;
|
|
30
|
+
export declare const SETTINGS_LABELS: Record<"auto_review_enabled" | "auto_patch_enabled" | "vortex_show_thinking_traces" | "repo_overview_enabled" | "review_unit_land_prs_enabled" | "cyclone_review_unit_land_prs_enabled" | "cyclone_skip_ci_enabled" | "cyclone_patch_unverified_languages" | "vortex_auto_overflow_enabled" | "vortex_skip_all_clear_comments" | "vortex_seam_specialist_enabled" | "auto_land_default" | "ignored_bot_logins" | "vortex_bot_skip_check" | "vortex_findings_check" | "cyclone_patch_failure_check", string>;
|
|
31
31
|
/** Config rows in a stable order: writable toggles, then connected flags. */
|
|
32
32
|
export declare function buildConfigRows(settings: SettingsResponse): TabsConfigRow[];
|
|
33
33
|
/**
|
package/dist/commands/browse.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { getMe, getReview, getSettings, listJobs, patchSettings,
|
|
2
|
-
import { BEARER_SETTINGS_LABELS } from "../automation-catalog.js";
|
|
1
|
+
import { getMe, getReview, getSettings, listJobs, patchSettings, } from "../api.js";
|
|
2
|
+
import { BEARER_BOOLEAN_SETTINGS_KEYS, BEARER_SETTINGS_LABELS } from "../automation-catalog.js";
|
|
3
3
|
import { apiBase, keyDisplay, loadConfig, resolveApiKey } from "../config.js";
|
|
4
4
|
import { CommandError } from "../errors.js";
|
|
5
5
|
import { ansi } from "../ui/ansi.js";
|
|
@@ -124,7 +124,7 @@ export const SETTINGS_LABELS = BEARER_SETTINGS_LABELS;
|
|
|
124
124
|
/** Config rows in a stable order: writable toggles, then connected flags. */
|
|
125
125
|
export function buildConfigRows(settings) {
|
|
126
126
|
return [
|
|
127
|
-
...
|
|
127
|
+
...BEARER_BOOLEAN_SETTINGS_KEYS.map((key) => ({
|
|
128
128
|
key,
|
|
129
129
|
label: SETTINGS_LABELS[key],
|
|
130
130
|
value: settings[key] === true,
|
package/dist/commands/help.js
CHANGED
|
@@ -58,7 +58,7 @@ export function buildHelpTabs() {
|
|
|
58
58
|
"",
|
|
59
59
|
rule("Single stack (from trunk)"),
|
|
60
60
|
cmd("stack create --onto main", "fresh stack, not on an existing one"),
|
|
61
|
-
cmd("stack list", "
|
|
61
|
+
cmd("stack list · wait <id>", "registered stacks · wait for attention"),
|
|
62
62
|
cmd("queue [add <stack-id>]", "list entries · queue verified landing"),
|
|
63
63
|
cmd("stack land <id>", "land now · skips queue verification"),
|
|
64
64
|
"",
|
package/dist/commands/queue.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export type QueueCmdDeps = {
|
|
|
5
5
|
canBrowse?: () => boolean;
|
|
6
6
|
runLineTabsBrowser?: typeof runLineTabsBrowser;
|
|
7
7
|
};
|
|
8
|
-
export declare const QUEUE_USAGE = "usage:\n mergestorm queue [list] [--json]\n mergestorm queue add <stack-id> [--json]\n mergestorm queue rm <entry-id|stack-id> [--json]\n\n queue lists
|
|
8
|
+
export declare const QUEUE_USAGE = "usage:\n mergestorm queue [list] [--json]\n mergestorm queue add <stack-id> [--json]\n mergestorm queue rm <entry-id|stack-id> [--json]\n\n queue lists live and recent bounced merge-queue entries. rm prefers an entry id; a live\n stack id is resolved to its queue entry before cancellation.";
|
|
9
9
|
/** Entry ids win; otherwise a live stack id resolves to its entry id. */
|
|
10
10
|
export declare function resolveQueueEntryId(idOrStackId: string, entries: readonly MergeQueueEntryDto[]): string;
|
|
11
11
|
export declare function buildQueueListLines(entries: readonly MergeQueueEntryDto[], fullStackId?: boolean): string[];
|
package/dist/commands/queue.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { cancelMergeQueueEntry, enqueueStack, listMergeQueueEntries, } from "../api.js";
|
|
2
2
|
import { loadConfig } from "../config.js";
|
|
3
3
|
import { CommandError } from "../errors.js";
|
|
4
|
+
import { mergeQueueBounceLabel } from "../stack-dto.js";
|
|
4
5
|
import { ansi } from "../ui/ansi.js";
|
|
5
6
|
import { runLineTabsBrowser } from "../ui/line-tabs.js";
|
|
6
7
|
import { present } from "../ui/present.js";
|
|
@@ -31,7 +32,7 @@ export const QUEUE_USAGE = `usage:
|
|
|
31
32
|
mergestorm queue add <stack-id> [--json]
|
|
32
33
|
mergestorm queue rm <entry-id|stack-id> [--json]
|
|
33
34
|
|
|
34
|
-
queue lists
|
|
35
|
+
queue lists live and recent bounced merge-queue entries. rm prefers an entry id; a live
|
|
35
36
|
stack id is resolved to its queue entry before cancellation.`;
|
|
36
37
|
function positionalArgs(args) {
|
|
37
38
|
const unknown = args.filter((arg) => arg.startsWith("-") &&
|
|
@@ -66,17 +67,31 @@ export function buildQueueListLines(entries, fullStackId = false) {
|
|
|
66
67
|
];
|
|
67
68
|
}
|
|
68
69
|
return entries.map((entry) => {
|
|
69
|
-
const reason = entry.
|
|
70
|
+
const reason = entry.bounceDetail
|
|
71
|
+
? mergeQueueBounceLabel(entry)
|
|
72
|
+
: entry.waitReason ?? (entry.bounceReason ? mergeQueueBounceLabel(entry) : null);
|
|
73
|
+
const prNumber = entry.bounceDetail?.prNumber != null && entry.bounceDetail.prNumber > 0
|
|
74
|
+
? entry.bounceDetail.prNumber
|
|
75
|
+
: entry.waitReason?.match(/#([1-9]\d*)\b/)?.[1];
|
|
76
|
+
const headSha = entry.bounceDetail?.headSha ?? entry.verifyHeadSha;
|
|
77
|
+
const pr = prNumber != null
|
|
78
|
+
? ` #${prNumber}${headSha ? `@${headSha.slice(0, 7)}` : ""}`
|
|
79
|
+
: "";
|
|
70
80
|
const glyph = colorQueueGlyph(entry.state, queueStateGlyph(entry.state));
|
|
71
81
|
const id = fullStackId ? entry.stackId : entry.stackId.slice(0, 8);
|
|
72
|
-
const
|
|
73
|
-
`${glyph} ${entry.state.padEnd(8)} ${entry.owner}/${entry.repo} ${id}
|
|
74
|
-
|
|
82
|
+
const coreWithoutReason = ` ${String(entry.position).padStart(2)} ` +
|
|
83
|
+
`${glyph} ${entry.state.padEnd(8)} ${entry.owner}/${entry.repo} ${id}`;
|
|
84
|
+
const coreWithoutPr = coreWithoutReason + (reason ? ` ${reason}` : "");
|
|
85
|
+
const core = `${coreWithoutPr}${pr}`;
|
|
75
86
|
const who = entry.enqueuedBy === "human" || entry.enqueuedBy === "agent"
|
|
76
87
|
? ` by ${entry.enqueuedBy}`
|
|
77
88
|
: "";
|
|
78
89
|
const withWho = `${core}${who}`;
|
|
79
|
-
|
|
90
|
+
if (visibleWidth(withWho) <= 80)
|
|
91
|
+
return withWho;
|
|
92
|
+
if (visibleWidth(core) <= 80)
|
|
93
|
+
return core;
|
|
94
|
+
return pr ? core : coreWithoutPr;
|
|
80
95
|
});
|
|
81
96
|
}
|
|
82
97
|
async function listQueue(asJson, deps = {}) {
|
|
@@ -166,7 +166,12 @@ export const COMMAND_REGISTRY = [
|
|
|
166
166
|
" [--auto-review on|off] [--auto-patch on|off] [--vortex-thinking on|off]",
|
|
167
167
|
" [--repo-overview on|off] [--review-unit-land on|off]",
|
|
168
168
|
" [--cyclone-review-unit-land on|off] [--cyclone-skip-ci on|off]",
|
|
169
|
-
" [--
|
|
169
|
+
" [--cyclone-patch-unverified on|off] Patch languages we cannot typecheck",
|
|
170
|
+
" [--vortex-seam on|off] [--vortex-skip-all-clear on|off]",
|
|
171
|
+
" [--auto-land on|off] Auto land default for new stacks",
|
|
172
|
+
" [--ignore-bot add <login>|remove <login>|clear]",
|
|
173
|
+
" [--vortex-skip-check none|neutral] [--vortex-findings failure|neutral|success]",
|
|
174
|
+
" [--cyclone-fail-check failure|neutral]",
|
|
170
175
|
" With flags: PATCH those settings and print the stored result",
|
|
171
176
|
],
|
|
172
177
|
async run(args, ctx) {
|
|
@@ -175,9 +180,9 @@ export const COMMAND_REGISTRY = [
|
|
|
175
180
|
},
|
|
176
181
|
{
|
|
177
182
|
name: "skill",
|
|
178
|
-
summary: "Install
|
|
183
|
+
summary: "Install Mergestorm skills for Claude, Cursor, or agents",
|
|
179
184
|
usage: [
|
|
180
|
-
"mergestorm skill install --claude|--cursor Copy the
|
|
185
|
+
"mergestorm skill install --claude|--cursor|--agents Copy the Mergestorm skills into this repo",
|
|
181
186
|
" [--json]",
|
|
182
187
|
],
|
|
183
188
|
async run(args) {
|
|
@@ -201,7 +206,7 @@ export const COMMAND_REGISTRY = [
|
|
|
201
206
|
name: "queue",
|
|
202
207
|
summary: "Merge queue: list, add a stack, or remove an entry",
|
|
203
208
|
usage: [
|
|
204
|
-
"mergestorm queue [list] [--json] List live merge-queue entries",
|
|
209
|
+
"mergestorm queue [list] [--json] List live and recent bounced merge-queue entries",
|
|
205
210
|
"mergestorm queue add <stack-id> [--json] Queue a stack for verified landing",
|
|
206
211
|
"mergestorm queue rm <entry-id|stack-id> [--json] Remove a live queue entry",
|
|
207
212
|
],
|
|
@@ -217,13 +222,15 @@ export const COMMAND_REGISTRY = [
|
|
|
217
222
|
"mergestorm stack submit Push/open active stack ([--extend] [--auto-land on|off] [--auto-review on|off] [--auto-patch on|off])",
|
|
218
223
|
"mergestorm stack reset --force Clear this repo's pre-submit authoring state",
|
|
219
224
|
"mergestorm stack list [--json] List registered stacks",
|
|
225
|
+
"mergestorm stack status <stack-id> [--json] Enriched stack status",
|
|
226
|
+
"mergestorm stack wait <stack-id> [--json] [--timeout <s>] Wait for stack attention (default 45s, max 300s; 0 = one snapshot, no wait)",
|
|
220
227
|
"mergestorm stack set <stack-id> [--auto-land on|off] [--auto-review on|off|default] [--auto-patch on|off|default] [--json] Per-stack policy",
|
|
221
228
|
"mergestorm stack adopt <owner/repo>#<pr> [--auto-land on|off] [--auto-review on|off] [--auto-patch on|off] Import an open PR chain",
|
|
222
229
|
"mergestorm stack restack <stack-id> Restack stack descendants",
|
|
223
230
|
"mergestorm stack land <stack-id> Land bottom PR (or promote into unit)",
|
|
224
231
|
],
|
|
225
|
-
async run(args) {
|
|
226
|
-
await cmdStack(args);
|
|
232
|
+
async run(args, ctx) {
|
|
233
|
+
await cmdStack(args, { signal: ctx.signal });
|
|
227
234
|
},
|
|
228
235
|
},
|
|
229
236
|
{
|
|
@@ -271,7 +271,6 @@ function shaMatches(actual, expected) {
|
|
|
271
271
|
/** Poll the DB-only PR review endpoint until the requested pass is resting. */
|
|
272
272
|
export async function pollPrVortexReview(cfg, owner, repo, prNumber, opts = {}) {
|
|
273
273
|
const timeoutMs = opts.timeoutMs ?? REVIEW_POLL_DEFAULT_TIMEOUT_MS;
|
|
274
|
-
const intervalMs = opts.intervalMs ?? REVIEW_POLL_INTERVAL_MS;
|
|
275
274
|
const now = opts.now ?? Date.now;
|
|
276
275
|
const wait = opts.sleep ?? sleep;
|
|
277
276
|
const startedAt = now();
|
|
@@ -296,17 +295,13 @@ export async function pollPrVortexReview(cfg, owner, repo, prNumber, opts = {})
|
|
|
296
295
|
if (waitMs > 0)
|
|
297
296
|
await wait(waitMs, opts.signal);
|
|
298
297
|
};
|
|
299
|
-
const sleepBeforePoll = async () => {
|
|
300
|
-
const waitMs = Math.min(stretchedPollIntervalMs(now() - startedAt, intervalMs), Math.max(0, deadline - now()));
|
|
301
|
-
if (waitMs > 0)
|
|
302
|
-
await wait(waitMs, opts.signal);
|
|
303
|
-
};
|
|
304
298
|
while (now() < deadline) {
|
|
299
|
+
query.set("wait", String(Math.min(45, Math.max(1, Math.ceil((deadline - now()) / 1000)))));
|
|
305
300
|
let poll;
|
|
306
301
|
try {
|
|
307
302
|
poll = await apiFetch(cfg, `/api/v1/stacks/pr-review?${query.toString()}`, {
|
|
308
303
|
signal: opts.signal,
|
|
309
|
-
timeoutMs:
|
|
304
|
+
timeoutMs: Number(query.get("wait")) * 1000 + 5_000,
|
|
310
305
|
});
|
|
311
306
|
}
|
|
312
307
|
catch (err) {
|
|
@@ -331,7 +326,6 @@ export async function pollPrVortexReview(cfg, owner, repo, prNumber, opts = {})
|
|
|
331
326
|
if (poll.status === 404) {
|
|
332
327
|
transientFailures = 0;
|
|
333
328
|
opts.onTick?.("progress");
|
|
334
|
-
await sleepBeforePoll();
|
|
335
329
|
continue;
|
|
336
330
|
}
|
|
337
331
|
if (poll.status !== 200) {
|
|
@@ -358,7 +352,6 @@ export async function pollPrVortexReview(cfg, owner, repo, prNumber, opts = {})
|
|
|
358
352
|
(!isPositiveInteger(envelope.pass) || (opts.pass !== undefined && envelope.pass !== opts.pass) ||
|
|
359
353
|
(opts.afterPass !== undefined && envelope.pass <= opts.afterPass))) {
|
|
360
354
|
opts.onTick?.("progress");
|
|
361
|
-
await sleepBeforePoll();
|
|
362
355
|
continue;
|
|
363
356
|
}
|
|
364
357
|
lastEnvelope = envelope;
|
|
@@ -370,7 +363,6 @@ export async function pollPrVortexReview(cfg, owner, repo, prNumber, opts = {})
|
|
|
370
363
|
if (resting && matches && !stale)
|
|
371
364
|
return lastEnvelope;
|
|
372
365
|
opts.onTick?.("progress");
|
|
373
|
-
await sleepBeforePoll();
|
|
374
366
|
}
|
|
375
367
|
throw new PrReviewPollTimeoutError(owner, repo, prNumber, lastEnvelope);
|
|
376
368
|
}
|
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import { type SettingsPatch, type SettingsResponse } from "../api.js";
|
|
2
2
|
/**
|
|
3
|
-
* CLI flag → Bearer settings key. on|off
|
|
3
|
+
* CLI flag → Bearer settings key. Boolean flags take on|off. The connected flags are
|
|
4
4
|
* read-only on the API and deliberately have no flag here.
|
|
5
5
|
*/
|
|
6
|
-
export declare const SETTINGS_FLAGS: Record<string, "auto_review_enabled" | "auto_patch_enabled" | "vortex_show_thinking_traces" | "repo_overview_enabled" | "review_unit_land_prs_enabled" | "cyclone_review_unit_land_prs_enabled" | "cyclone_skip_ci_enabled" | "vortex_seam_specialist_enabled" | "auto_land_default">;
|
|
6
|
+
export declare const SETTINGS_FLAGS: Record<string, "auto_review_enabled" | "auto_patch_enabled" | "vortex_show_thinking_traces" | "repo_overview_enabled" | "review_unit_land_prs_enabled" | "cyclone_review_unit_land_prs_enabled" | "cyclone_skip_ci_enabled" | "cyclone_patch_unverified_languages" | "vortex_auto_overflow_enabled" | "vortex_skip_all_clear_comments" | "vortex_seam_specialist_enabled" | "auto_land_default" | "ignored_bot_logins" | "vortex_bot_skip_check" | "vortex_findings_check" | "cyclone_patch_failure_check">;
|
|
7
|
+
type IgnoreBotOperation = {
|
|
8
|
+
action: "add" | "remove";
|
|
9
|
+
login: string;
|
|
10
|
+
} | {
|
|
11
|
+
action: "clear";
|
|
12
|
+
};
|
|
7
13
|
export type SettingsArgs = {
|
|
8
14
|
json: boolean;
|
|
9
15
|
patch: SettingsPatch;
|
|
16
|
+
ignoreBots?: IgnoreBotOperation[];
|
|
10
17
|
};
|
|
11
|
-
/** Parse
|
|
18
|
+
/** Parse boolean and enum flags plus ordered ignore-list operations. */
|
|
12
19
|
export declare function parseSettingsArgs(args: string[]): SettingsArgs;
|
|
13
|
-
/** Static printout
|
|
20
|
+
/** Static printout includes the Config toggles plus enum and login settings. */
|
|
14
21
|
export declare function formatSettingsLines(settings: SettingsResponse): string[];
|
|
15
22
|
export declare function cmdSettings(args: string[], opts?: {
|
|
16
23
|
mode?: "oneshot" | "shell";
|
|
17
24
|
}): Promise<void>;
|
|
25
|
+
export {};
|
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import { getSettings, patchSettings, } from "../api.js";
|
|
2
|
-
import { BEARER_SETTINGS_FLAGS } from "../automation-catalog.js";
|
|
2
|
+
import { BEARER_SETTINGS, BEARER_SETTINGS_FLAGS } from "../automation-catalog.js";
|
|
3
3
|
import { loadConfig } from "../config.js";
|
|
4
4
|
import { CommandError } from "../errors.js";
|
|
5
5
|
import { present } from "../ui/present.js";
|
|
6
6
|
import { buildConfigRows, canBrowse, openTabsBrowser } from "./browse.js";
|
|
7
7
|
/**
|
|
8
|
-
* CLI flag → Bearer settings key. on|off
|
|
8
|
+
* CLI flag → Bearer settings key. Boolean flags take on|off. The connected flags are
|
|
9
9
|
* read-only on the API and deliberately have no flag here.
|
|
10
10
|
*/
|
|
11
11
|
export const SETTINGS_FLAGS = BEARER_SETTINGS_FLAGS;
|
|
12
|
-
|
|
12
|
+
function canonicalLogin(raw) {
|
|
13
|
+
const login = raw.trim().toLowerCase().replace(/\[bot\]$/, "");
|
|
14
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(login)) {
|
|
15
|
+
throw new CommandError(`Invalid bot login: ${raw}.`, 2, "usage");
|
|
16
|
+
}
|
|
17
|
+
return login;
|
|
18
|
+
}
|
|
19
|
+
/** Parse boolean and enum flags plus ordered ignore-list operations. */
|
|
13
20
|
export function parseSettingsArgs(args) {
|
|
14
21
|
let json = false;
|
|
22
|
+
const ignoreBots = [];
|
|
15
23
|
const patch = {};
|
|
16
24
|
for (let i = 0; i < args.length; i++) {
|
|
17
25
|
const arg = args[i];
|
|
@@ -26,37 +34,91 @@ export function parseSettingsArgs(args) {
|
|
|
26
34
|
throw new CommandError(`Unknown settings flag: ${flag}. See \`mergestorm --help\`.`, 2, "usage");
|
|
27
35
|
}
|
|
28
36
|
const value = eq >= 0 ? arg.slice(eq + 1) : args[++i];
|
|
37
|
+
const row = BEARER_SETTINGS.find((row) => row.key === key);
|
|
38
|
+
if ("kind" in row && row.kind === "logins") {
|
|
39
|
+
if (value === "clear")
|
|
40
|
+
ignoreBots.push({ action: "clear" });
|
|
41
|
+
else if ((value === "add" || value === "remove") && args[i + 1] && !args[i + 1].startsWith("--")) {
|
|
42
|
+
ignoreBots.push({ action: value, login: canonicalLogin(args[++i]) });
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
throw new CommandError(`${flag} takes add <login>, remove <login>, or clear.`, 2, "usage");
|
|
46
|
+
}
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if ("kind" in row && row.kind === "enum") {
|
|
50
|
+
if (value === undefined || !row.values.includes(value)) {
|
|
51
|
+
throw new CommandError(`${flag} takes ${row.values.join(" or ")}.`, 2, "usage");
|
|
52
|
+
}
|
|
53
|
+
Object.assign(patch, { [key]: value });
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
29
56
|
if (value !== "on" && value !== "off") {
|
|
30
57
|
throw new CommandError(`${flag} takes on or off.`, 2, "usage");
|
|
31
58
|
}
|
|
32
|
-
patch[key]
|
|
59
|
+
Object.assign(patch, { [key]: value === "on" });
|
|
33
60
|
}
|
|
34
|
-
return { json, patch };
|
|
61
|
+
return { json, patch, ...(ignoreBots.length ? { ignoreBots } : {}) };
|
|
35
62
|
}
|
|
36
|
-
/** Static printout
|
|
63
|
+
/** Static printout includes the Config toggles plus enum and login settings. */
|
|
37
64
|
export function formatSettingsLines(settings) {
|
|
38
|
-
const rows =
|
|
65
|
+
const rows = [
|
|
66
|
+
...buildConfigRows(settings),
|
|
67
|
+
...BEARER_SETTINGS.filter((row) => "kind" in row).map((row) => ({
|
|
68
|
+
...row, writable: true, value: settings[row.key],
|
|
69
|
+
})),
|
|
70
|
+
];
|
|
39
71
|
const labelWidth = Math.max(...rows.map((r) => r.label.length));
|
|
40
72
|
return rows.map((row) => {
|
|
41
|
-
const value = row.
|
|
42
|
-
?
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
73
|
+
const value = "kind" in row && row.value === undefined
|
|
74
|
+
? "(unavailable)"
|
|
75
|
+
: Array.isArray(row.value)
|
|
76
|
+
? row.value.join(", ") || "(empty)"
|
|
77
|
+
: typeof row.value === "string"
|
|
78
|
+
? row.value
|
|
79
|
+
: row.writable
|
|
80
|
+
? row.value
|
|
81
|
+
? "on"
|
|
82
|
+
: "off"
|
|
83
|
+
: row.value
|
|
84
|
+
? "connected"
|
|
85
|
+
: "not connected";
|
|
48
86
|
return ` ${row.label.padEnd(labelWidth + 2)}${value}`;
|
|
49
87
|
});
|
|
50
88
|
}
|
|
51
89
|
export async function cmdSettings(args, opts = {}) {
|
|
52
|
-
const { json, patch } = parseSettingsArgs(args);
|
|
53
|
-
const hasPatch = Object.keys(patch).length > 0;
|
|
90
|
+
const { json, patch, ignoreBots } = parseSettingsArgs(args);
|
|
91
|
+
const hasPatch = Object.keys(patch).length > 0 || Boolean(ignoreBots?.length);
|
|
54
92
|
// Bare `settings` on a TTY: the tabbed browser, opened on Config.
|
|
55
93
|
if (!hasPatch && !json && canBrowse()) {
|
|
56
94
|
await openTabsBrowser("config");
|
|
57
95
|
return;
|
|
58
96
|
}
|
|
59
97
|
const cfg = await loadConfig();
|
|
98
|
+
if (ignoreBots?.length) {
|
|
99
|
+
// A leading clear needs no read. Otherwise fetch before writing the full list.
|
|
100
|
+
const current = ignoreBots[0].action === "clear" ? null : await getSettings(cfg);
|
|
101
|
+
if (ignoreBots[0].action !== "clear" && !current) {
|
|
102
|
+
throw new CommandError("Settings are not available; cannot update ignored bot logins.");
|
|
103
|
+
}
|
|
104
|
+
let logins = [...new Set((current?.ignored_bot_logins ?? []).flatMap((raw) => {
|
|
105
|
+
try {
|
|
106
|
+
return [canonicalLogin(raw)];
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
}))];
|
|
112
|
+
for (const op of ignoreBots) {
|
|
113
|
+
if (op.action === "clear")
|
|
114
|
+
logins = [];
|
|
115
|
+
else if (op.action === "remove")
|
|
116
|
+
logins = logins.filter((login) => login !== op.login);
|
|
117
|
+
else if (!logins.includes(op.login))
|
|
118
|
+
logins.push(op.login);
|
|
119
|
+
}
|
|
120
|
+
patch.ignored_bot_logins = logins;
|
|
121
|
+
}
|
|
60
122
|
let settings;
|
|
61
123
|
if (hasPatch) {
|
|
62
124
|
settings = await patchSettings(patch, cfg);
|
package/dist/commands/skill.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export declare const SKILL_NAME = "mergestorm-review";
|
|
2
2
|
export declare const PR_LOOP_SKILL_NAME = "mergestorm-pr-loop";
|
|
3
|
-
export declare const
|
|
3
|
+
export declare const BOUNCE_WATCH_SKILL_NAME = "mergestorm-bounce-watch";
|
|
4
|
+
export declare const SKILL_NAMES: readonly ["mergestorm-review", "mergestorm-pr-loop", "mergestorm-bounce-watch"];
|
|
4
5
|
export type SkillName = (typeof SKILL_NAMES)[number];
|
|
5
|
-
export declare const SKILL_USAGE
|
|
6
|
-
export type SkillTarget = "claude" | "cursor";
|
|
6
|
+
export declare const SKILL_USAGE: string;
|
|
7
|
+
export type SkillTarget = "claude" | "cursor" | "agents";
|
|
7
8
|
export type SkillInstallRequest = {
|
|
8
9
|
help: boolean;
|
|
9
10
|
json: boolean;
|