mergestorm 0.3.66 → 0.3.67
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 +3 -3
- package/dist/api.d.ts +16 -2
- package/dist/api.js +41 -8
- package/dist/automation-catalog.d.ts +5 -1
- package/dist/automation-catalog.js +1 -0
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +1 -1
- package/dist/commands/issue.js +5 -1
- package/dist/commands/login.d.ts +1 -2
- package/dist/commands/login.js +0 -4
- package/dist/commands/settings.d.ts +1 -1
- package/dist/commands/settings.js +2 -1
- package/dist/commands/stack.d.ts +3 -3
- package/dist/commands/stack.js +21 -11
- package/dist/stack-blockers.js +2 -0
- package/dist/ui/banner.js +3 -0
- package/package.json +1 -1
- package/skill/SKILL.md +1 -1
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.67 │
|
|
49
49
|
│ ▀▀▀▀▀▀▀▀▀ ● msk_live_… · maelstrom │
|
|
50
50
|
│ ▀▀▀▀▀▀ [████████░░░░░░░░░░░░░░░░░░░░░░] 25% used │
|
|
51
51
|
│ ▝▀▀▀▀▀ local reviews + stacked PRs · mergestorm.ai │
|
|
52
52
|
│ ▝▀▀▘ review a diff · usage for tabs · /help for all commands │
|
|
53
|
-
│ ▀ New in v0.3.
|
|
53
|
+
│ ▀ New in v0.3.67: Issue review: reply once, then one re-check │
|
|
54
54
|
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
55
55
|
mergestorm
|
|
56
56
|
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
@@ -171,7 +171,7 @@ mg stack publish --status # absent | pushed | opened | registered
|
|
|
171
171
|
```
|
|
172
172
|
|
|
173
173
|
- **Issues too.** `mg issue create` opens the issue a PR needs through the same Surge App, with existing labels only. Running it again with the same title returns the issue it already opened. Put `Fixes #<n>` in the commit message; `stack submit` carries it into the PR body.
|
|
174
|
-
- **Issue reviews first.** On a repository with Vortex issue reviews on, Vortex reviews each new issue before any PR. `mg issue wait <n>` (or `mg issue create --wait`) waits for that review: it exits 0 once the issue is approved, or exits 4 with the numbered points to answer.
|
|
174
|
+
- **Issue reviews first.** On a repository with Vortex issue reviews on, Vortex reviews each new issue before any PR. `mg issue wait <n>` (or `mg issue create --wait`) waits for that review: it exits 0 once the issue is approved, or exits 4 with the numbered points to answer. Reply once in a comment on the issue, accepting each blocking point's suggested fix or saying why not, and wait again; Vortex re-checks only the blocking points, once, and approves the issue. Notes never block; the PR review checks them against the code. A person can approve early with an issue comment whose first line is `mergestorm-issue: approve`. `stack submit` refuses to open a PR whose tip commit links an issue that is not approved yet, or links no issue at all while issue reviews are on.
|
|
175
175
|
- **Exact contents.** Every commit travels with its Git tree SHA and the whole content of each changed file, so binaries, deletions, renames, executable bits and symlinks arrive unchanged. Mergestorm moves the branch only when GitHub builds the same tree, and `mg` checks the returned tree against your local branch.
|
|
176
176
|
- **Never overwrites surprises.** `mg` looks at the branch on GitHub first. If its files match one of your local commits, only the commits after that one are appended. If they match none (someone else pushed, or you rebased), `mg` stops with `head_changed`; `--replace-head <sha>` replaces exactly that head and nothing else. Mergestorm refuses `main`, `master`, the default branch, protected branches and its own `mg-stack-*`, `mg-park-*` and `mq-batch-*` branches.
|
|
177
177
|
- **Safe to run again.** After a timeout or an error, run the same command again. It finds the branch, pull request and stack it already made and finishes the missing step; it does not create duplicates.
|
package/dist/api.d.ts
CHANGED
|
@@ -188,8 +188,8 @@ export type SettingsResponse = import("./automation-catalog.js").BearerSettingsV
|
|
|
188
188
|
github_connected: boolean;
|
|
189
189
|
stacks_ready?: boolean;
|
|
190
190
|
};
|
|
191
|
-
export declare const REPO_SETTINGS_SWITCH_KEYS: readonly ["auto_patch_enabled", "auto_review_enabled", "auto_land_default", "merge_queue_batch_enabled", "merge_queue_overdrive", "issue_review_enabled"];
|
|
192
|
-
export declare const REPO_SETTINGS_KEYS: readonly ["auto_patch_enabled", "auto_review_enabled", "auto_land_default", "merge_queue_batch_enabled", "merge_queue_overdrive", "issue_review_enabled", "merge_queue_batch_size", "merge_queue_lands_alone_paths"];
|
|
191
|
+
export declare const REPO_SETTINGS_SWITCH_KEYS: readonly ["auto_patch_enabled", "auto_review_enabled", "auto_land_default", "merge_queue_batch_enabled", "merge_queue_overdrive", "issue_review_enabled", "issue_auto_review_enabled"];
|
|
192
|
+
export declare const REPO_SETTINGS_KEYS: readonly ["auto_patch_enabled", "auto_review_enabled", "auto_land_default", "merge_queue_batch_enabled", "merge_queue_overdrive", "issue_review_enabled", "issue_auto_review_enabled", "merge_queue_batch_size", "merge_queue_lands_alone_paths"];
|
|
193
193
|
export type RepoSettingsKey = (typeof REPO_SETTINGS_KEYS)[number];
|
|
194
194
|
type RepoSettingsValues = Record<(typeof REPO_SETTINGS_SWITCH_KEYS)[number], boolean | null> & {
|
|
195
195
|
merge_queue_batch_size: number | null;
|
|
@@ -409,12 +409,17 @@ export type IssueReviewState = (typeof ISSUE_REVIEW_STATES)[number];
|
|
|
409
409
|
export type IssueReviewPoint = {
|
|
410
410
|
kind: string;
|
|
411
411
|
text: string;
|
|
412
|
+
blocking?: boolean;
|
|
412
413
|
};
|
|
414
|
+
export declare const ISSUE_REVIEW_MODES: readonly ["off", "mention", "auto"];
|
|
415
|
+
export type IssueReviewMode = (typeof ISSUE_REVIEW_MODES)[number];
|
|
416
|
+
export declare const ISSUE_REVIEW_TAG = "@mergestorm-vortex";
|
|
413
417
|
export type IssueReviewStatus = {
|
|
414
418
|
owner: string;
|
|
415
419
|
repo: string;
|
|
416
420
|
number: number;
|
|
417
421
|
enabled: boolean;
|
|
422
|
+
mode: IssueReviewMode;
|
|
418
423
|
state: IssueReviewState;
|
|
419
424
|
approvedBy: "vortex" | "human" | null;
|
|
420
425
|
round: number | null;
|
|
@@ -437,6 +442,14 @@ export declare function getIssueReview(cfg: Config, target: IssueReviewTarget, o
|
|
|
437
442
|
timeoutMs?: number;
|
|
438
443
|
signal?: AbortSignal;
|
|
439
444
|
}): Promise<IssueReviewStatus | null>;
|
|
445
|
+
export declare function getIssueReviewMode(cfg: Config, target: {
|
|
446
|
+
owner: string;
|
|
447
|
+
repo: string;
|
|
448
|
+
}, opts?: {
|
|
449
|
+
timeoutMs?: number;
|
|
450
|
+
}): Promise<IssueReviewMode | null>;
|
|
451
|
+
export declare function issueReviewSettled(status: IssueReviewStatus): boolean;
|
|
452
|
+
export declare function issueReviewBlocksPr(status: IssueReviewStatus): boolean;
|
|
440
453
|
export declare class IssueReviewTimeoutError extends CommandError {
|
|
441
454
|
readonly lastStatus: IssueReviewStatus;
|
|
442
455
|
constructor(lastStatus: IssueReviewStatus);
|
|
@@ -452,6 +465,7 @@ export type PollIssueReviewOptions = {
|
|
|
452
465
|
export declare function pollIssueReview(cfg: Config, target: IssueReviewTarget, opts?: PollIssueReviewOptions): Promise<IssueReviewStatus | null>;
|
|
453
466
|
export declare function issueReviewLines(status: IssueReviewStatus): string[];
|
|
454
467
|
export declare function issueNotApprovedError(issues: IssueReviewStatus[], message?: string | null): CommandError;
|
|
468
|
+
export declare const NO_LINKED_ISSUE = "no_linked_issue";
|
|
455
469
|
export declare function issueNotApprovedRefusal(status: number, body: unknown, owner: string, repo: string): CommandError | null;
|
|
456
470
|
/**
|
|
457
471
|
* Patch any subset of one owned stack's automation policy
|
package/dist/api.js
CHANGED
|
@@ -188,6 +188,7 @@ export const REPO_SETTINGS_SWITCH_KEYS = [
|
|
|
188
188
|
"merge_queue_batch_enabled",
|
|
189
189
|
"merge_queue_overdrive",
|
|
190
190
|
"issue_review_enabled",
|
|
191
|
+
"issue_auto_review_enabled",
|
|
191
192
|
];
|
|
192
193
|
export const REPO_SETTINGS_KEYS = [...REPO_SETTINGS_SWITCH_KEYS, "merge_queue_batch_size", "merge_queue_lands_alone_paths"];
|
|
193
194
|
function repoSettingsFailure(status, body, retryAfterSeconds, action) {
|
|
@@ -691,10 +692,12 @@ export async function openStackIssue(input, cfg) {
|
|
|
691
692
|
return { number: row.number, url: typeof row.url === "string" ? row.url : "", created: row.created === true };
|
|
692
693
|
}
|
|
693
694
|
export const ISSUE_REVIEW_STATES = ["approved", "needs_answers", "pending", "unreviewed", "capped", "off"];
|
|
695
|
+
export const ISSUE_REVIEW_MODES = ["off", "mention", "auto"];
|
|
696
|
+
export const ISSUE_REVIEW_TAG = "@mergestorm-vortex";
|
|
694
697
|
export const ISSUE_REVIEW_POLL_INTERVAL_MS = 10_000;
|
|
695
698
|
export const ISSUE_REVIEW_DEFAULT_TIMEOUT_MS = 900_000;
|
|
696
699
|
const ISSUE_REVIEW_MAX_POINTS = 8;
|
|
697
|
-
const ISSUE_REVIEW_DEFAULT_MAX_ROUNDS =
|
|
700
|
+
const ISSUE_REVIEW_DEFAULT_MAX_ROUNDS = 2;
|
|
698
701
|
function issueReviewText(value) {
|
|
699
702
|
if (typeof value !== "string")
|
|
700
703
|
return null;
|
|
@@ -714,7 +717,7 @@ export function parseIssueReviewStatus(body, target) {
|
|
|
714
717
|
const text = issueReviewText(point?.text);
|
|
715
718
|
if (!text)
|
|
716
719
|
return [];
|
|
717
|
-
return [{ kind: issueReviewText(point.kind) ?? "note", text }];
|
|
720
|
+
return [{ kind: issueReviewText(point.kind) ?? "note", text, blocking: point.blocking !== false }];
|
|
718
721
|
})
|
|
719
722
|
.slice(0, ISSUE_REVIEW_MAX_POINTS)
|
|
720
723
|
: [];
|
|
@@ -724,6 +727,7 @@ export function parseIssueReviewStatus(body, target) {
|
|
|
724
727
|
repo: typeof row.repo === "string" && row.repo ? row.repo : target.repo,
|
|
725
728
|
number: isPositiveInteger(row.number) ? row.number : target.number,
|
|
726
729
|
enabled: row.enabled === true,
|
|
730
|
+
mode: ISSUE_REVIEW_MODES.find((known) => known === row.mode) ?? (row.enabled === true ? "auto" : "off"),
|
|
727
731
|
state,
|
|
728
732
|
approvedBy: row.approvedBy === "vortex" || row.approvedBy === "human" ? row.approvedBy : null,
|
|
729
733
|
round: isPositiveInteger(row.round) ? row.round : null,
|
|
@@ -738,8 +742,12 @@ export function parseIssueReviewStatus(body, target) {
|
|
|
738
742
|
export async function getIssueReview(cfg, target, opts = {}) {
|
|
739
743
|
const query = new URLSearchParams({ owner: target.owner, repo: target.repo }).toString();
|
|
740
744
|
const { status, body, retryAfterSeconds } = await apiFetch(cfg, `/api/v1/stacks/issues/${encodeURIComponent(String(target.number))}/review?${query}`, opts);
|
|
741
|
-
if (status === 404)
|
|
742
|
-
|
|
745
|
+
if (status === 404) {
|
|
746
|
+
const message = serverMessage(body?.message);
|
|
747
|
+
if (!message)
|
|
748
|
+
return null;
|
|
749
|
+
throw new CommandError(`${message} Connect it in Mergestorm, then try again.`, 1, "not_found");
|
|
750
|
+
}
|
|
743
751
|
if (status === 429) {
|
|
744
752
|
throw new CommandError(rateLimitedMessage(retryAfterSeconds), REVIEW_EXIT.rate_limited, "rate_limited", retryAfterSeconds !== undefined ? { retryAfterSeconds } : undefined);
|
|
745
753
|
}
|
|
@@ -750,6 +758,29 @@ export async function getIssueReview(cfg, target, opts = {}) {
|
|
|
750
758
|
}
|
|
751
759
|
return parsed;
|
|
752
760
|
}
|
|
761
|
+
export async function getIssueReviewMode(cfg, target, opts = {}) {
|
|
762
|
+
const query = new URLSearchParams({ owner: target.owner, repo: target.repo }).toString();
|
|
763
|
+
const { status, body, retryAfterSeconds } = await apiFetch(cfg, `/api/v1/stacks/issues/review-mode?${query}`, opts);
|
|
764
|
+
if (status === 404)
|
|
765
|
+
return null;
|
|
766
|
+
if (status === 429) {
|
|
767
|
+
throw new CommandError(rateLimitedMessage(retryAfterSeconds), REVIEW_EXIT.rate_limited, "rate_limited", retryAfterSeconds !== undefined ? { retryAfterSeconds } : undefined);
|
|
768
|
+
}
|
|
769
|
+
const mode = status === 200 ? ISSUE_REVIEW_MODES.find((known) => known === body?.mode) : undefined;
|
|
770
|
+
if (!mode) {
|
|
771
|
+
const message = serverMessage(body?.message);
|
|
772
|
+
throw new CommandError(message ?? `Failed to read the issue review mode of ${target.owner}/${target.repo} (HTTP ${status})`);
|
|
773
|
+
}
|
|
774
|
+
return mode;
|
|
775
|
+
}
|
|
776
|
+
export function issueReviewSettled(status) {
|
|
777
|
+
if (status.state === "pending")
|
|
778
|
+
return false;
|
|
779
|
+
return !(status.state === "unreviewed" && status.mode === "auto");
|
|
780
|
+
}
|
|
781
|
+
export function issueReviewBlocksPr(status) {
|
|
782
|
+
return status.mode === "auto" && status.state !== "approved" && status.state !== "off";
|
|
783
|
+
}
|
|
753
784
|
export class IssueReviewTimeoutError extends CommandError {
|
|
754
785
|
lastStatus;
|
|
755
786
|
constructor(lastStatus) {
|
|
@@ -795,7 +826,7 @@ export async function pollIssueReview(cfg, target, opts = {}) {
|
|
|
795
826
|
for (;;) {
|
|
796
827
|
try {
|
|
797
828
|
const status = await getIssueReview(cfg, target, { signal: opts.signal });
|
|
798
|
-
if (!status || status
|
|
829
|
+
if (!status || issueReviewSettled(status))
|
|
799
830
|
return status;
|
|
800
831
|
last = status;
|
|
801
832
|
opts.onTick?.(status);
|
|
@@ -820,7 +851,7 @@ export function issueReviewLines(status) {
|
|
|
820
851
|
const lines = [`${status.owner}/${status.repo}#${status.number}: ${status.state}${round}`];
|
|
821
852
|
if (status.summary)
|
|
822
853
|
lines.push(` ${status.summary}`);
|
|
823
|
-
status.points.forEach((point, i) => lines.push(` ${i + 1}. [${point.kind}] ${point.text}`));
|
|
854
|
+
status.points.forEach((point, i) => lines.push(` ${i + 1}. [${point.blocking === false ? "note: " : ""}${point.kind}] ${point.text}`));
|
|
824
855
|
if (status.commentUrl)
|
|
825
856
|
lines.push(` Review: ${status.commentUrl}`);
|
|
826
857
|
if (status.next)
|
|
@@ -831,10 +862,12 @@ export function issueNotApprovedError(issues, message) {
|
|
|
831
862
|
const lines = [message?.trim() || "Vortex has not approved the linked issue yet; refusing to open the pull request."];
|
|
832
863
|
for (const issue of issues) {
|
|
833
864
|
lines.push(...issueReviewLines(issue));
|
|
834
|
-
|
|
865
|
+
const reply = issue.state === "needs_answers" ? `Reply once in a comment on #${issue.number}, accepting each blocking point's suggested fix or saying why not; then run` : "Run";
|
|
866
|
+
lines.push(` ${reply} \`mergestorm issue wait ${issue.number}\`.`);
|
|
835
867
|
}
|
|
836
|
-
return new CommandError(lines.join("\n"), REVIEW_EXIT.failed, "issue_not_approved");
|
|
868
|
+
return new CommandError(lines.join("\n"), REVIEW_EXIT.failed, "issue_not_approved", issues.length === 0 ? { reason: NO_LINKED_ISSUE } : undefined);
|
|
837
869
|
}
|
|
870
|
+
export const NO_LINKED_ISSUE = "no_linked_issue";
|
|
838
871
|
export function issueNotApprovedRefusal(status, body, owner, repo) {
|
|
839
872
|
if (status !== 409 || !body || typeof body !== "object")
|
|
840
873
|
return null;
|
|
@@ -46,6 +46,10 @@ export declare const BEARER_SETTINGS: readonly [{
|
|
|
46
46
|
readonly key: "issue_review_enabled";
|
|
47
47
|
readonly flag: "--issue-review";
|
|
48
48
|
readonly label: "Vortex issue reviews";
|
|
49
|
+
}, {
|
|
50
|
+
readonly key: "issue_auto_review_enabled";
|
|
51
|
+
readonly flag: "--issue-auto-review";
|
|
52
|
+
readonly label: "Vortex issue auto review";
|
|
49
53
|
}, {
|
|
50
54
|
readonly key: "auto_land_default";
|
|
51
55
|
readonly flag: "--auto-land";
|
|
@@ -116,4 +120,4 @@ export type BearerSettingsValues = {
|
|
|
116
120
|
values: readonly (infer Value)[];
|
|
117
121
|
} ? Value : boolean;
|
|
118
122
|
};
|
|
119
|
-
export declare const BEARER_BOOLEAN_SETTINGS_KEYS: ("auto_review_enabled" | "auto_patch_enabled" | "auto_resolve_conflicts_enabled" | "auto_fix_ci_enabled" | "vortex_show_thinking_traces" | "repo_overview_enabled" | "cyclone_skip_ci_enabled" | "cyclone_patch_unverified_languages" | "vortex_auto_overflow_enabled" | "vortex_skip_all_clear_comments" | "vortex_seam_specialist_enabled" | "issue_review_enabled" | "auto_land_default" | "auto_land_settle_seconds" | "merge_queue_batch_enabled" | "merge_queue_batch_size" | "merge_queue_lands_alone_paths" | "merge_queue_overdrive" | "ignored_bot_logins" | "vortex_bot_skip_check" | "vortex_findings_check" | "cyclone_patch_failure_check")[];
|
|
123
|
+
export declare const BEARER_BOOLEAN_SETTINGS_KEYS: ("auto_review_enabled" | "auto_patch_enabled" | "auto_resolve_conflicts_enabled" | "auto_fix_ci_enabled" | "vortex_show_thinking_traces" | "repo_overview_enabled" | "cyclone_skip_ci_enabled" | "cyclone_patch_unverified_languages" | "vortex_auto_overflow_enabled" | "vortex_skip_all_clear_comments" | "vortex_seam_specialist_enabled" | "issue_review_enabled" | "issue_auto_review_enabled" | "auto_land_default" | "auto_land_settle_seconds" | "merge_queue_batch_enabled" | "merge_queue_batch_size" | "merge_queue_lands_alone_paths" | "merge_queue_overdrive" | "ignored_bot_logins" | "vortex_bot_skip_check" | "vortex_findings_check" | "cyclone_patch_failure_check")[];
|
|
@@ -11,6 +11,7 @@ export const BEARER_SETTINGS = [
|
|
|
11
11
|
{ key: "vortex_skip_all_clear_comments", flag: "--vortex-skip-all-clear", label: "Vortex skip All-clear comments" },
|
|
12
12
|
{ key: "vortex_seam_specialist_enabled", flag: "--vortex-seam", label: "Vortex seam specialist" },
|
|
13
13
|
{ key: "issue_review_enabled", flag: "--issue-review", label: "Vortex issue reviews" },
|
|
14
|
+
{ key: "issue_auto_review_enabled", flag: "--issue-auto-review", label: "Vortex issue auto review" },
|
|
14
15
|
{ key: "auto_land_default", flag: "--auto-land", label: "Auto land (new stacks)" },
|
|
15
16
|
{ key: "auto_land_settle_seconds", flag: "--auto-land-settle", label: "Auto land settle (seconds)", kind: "seconds", min: 15, max: 300 },
|
|
16
17
|
{ key: "merge_queue_batch_enabled", flag: "--merge-queue-batch", label: "Merge queue batching" },
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { ACCOUNT_LIMIT, DEFAULT_API, accountNameFromMe, apiBase, configPath, keyDisplay, listAccounts, loadConfig, mergestormHome, removeAccount, resolveApiKey, saveAccount, settleAccountName, splitAccountFlag, useAccount, withAccount, type AccountSummary, type Config, type StoredAccount, } from "../config.js";
|
|
2
2
|
export { BEARER_SETTINGS, BEARER_BOOLEAN_SETTINGS_KEYS, BEARER_SETTINGS_FLAGS, BEARER_SETTINGS_KEYS, BEARER_SETTINGS_LABELS, type BearerSettingsKey, } from "../automation-catalog.js";
|
|
3
|
-
export { API_TIMEOUT_PREFIX, DEFAULT_API_TIMEOUT_MS, SETTINGS_WRITABLE_KEYS, adoptStack, accountLabel, apiFetch, cancelMergeQueueEntry, devicePost, dismissPrFindings, dismissThreadsLine, enqueueStack, ensureUpperPark, getEnrichedStack, getIssueReview, ISSUE_REVIEW_DEFAULT_TIMEOUT_MS, ISSUE_REVIEW_POLL_INTERVAL_MS, ISSUE_REVIEW_STATES, IssueReviewTimeoutError, issueNotApprovedError, issueNotApprovedRefusal, issueReviewLines, parseIssueReviewStatus, pollIssueReview, type IssueReviewPoint, type IssueReviewState, type IssueReviewStatus, type IssueReviewTarget, type PollIssueReviewOptions, getMe, getPrVortexReview, revokeOwnKey, getReview, getSettings, listRepoSettings, patchRepoSettings, REPO_SETTINGS_KEYS, getStackPublication, landNextStack, listJobs, listMergeQueueEntries, listStacks, listThreads, openStackIssue, parseRetryAfterSeconds, patchSettings, restackStack, setStackPolicy, stackPolicyBody, submitStackPublication, type ApiFetchInit, type ApiFetchResult, type JobListItem, type AccountIdentity, type MeResponse, type MergeQueueBounceDetail, type MergeQueueBounceKind, type MergeQueueEnqueuedBy, type MergeQueueEnqueuedVia, type MergeQueueEntryDto, type MergeQueueEntryState, type PrDismissedFinding, type PrFindingDismissInput, type PrFindingDismissOutcome, type PrFindingDismissResult, type PrReviewFinding, type PrReviewFindings, type PrVortexReview, type SettingsPatch, type RepoSettings, type RepoSettingsKey, type RepoSettingsPatch, type SettingsResponse, type StackDto, type StackPolicyPatch, type StackPublication, type StackPublicationRequest, type StackPublicationStatus, type ThreadDetail, type ThreadListItem, } from "../api.js";
|
|
3
|
+
export { API_TIMEOUT_PREFIX, DEFAULT_API_TIMEOUT_MS, SETTINGS_WRITABLE_KEYS, adoptStack, accountLabel, apiFetch, cancelMergeQueueEntry, devicePost, dismissPrFindings, dismissThreadsLine, enqueueStack, ensureUpperPark, getEnrichedStack, getIssueReview, ISSUE_REVIEW_DEFAULT_TIMEOUT_MS, ISSUE_REVIEW_POLL_INTERVAL_MS, ISSUE_REVIEW_STATES, IssueReviewTimeoutError, NO_LINKED_ISSUE, issueNotApprovedError, issueNotApprovedRefusal, issueReviewLines, parseIssueReviewStatus, pollIssueReview, type IssueReviewPoint, type IssueReviewState, type IssueReviewStatus, type IssueReviewTarget, type PollIssueReviewOptions, getMe, getPrVortexReview, revokeOwnKey, getReview, getSettings, listRepoSettings, patchRepoSettings, REPO_SETTINGS_KEYS, getStackPublication, landNextStack, listJobs, listMergeQueueEntries, listStacks, listThreads, openStackIssue, parseRetryAfterSeconds, patchSettings, restackStack, setStackPolicy, stackPolicyBody, submitStackPublication, type ApiFetchInit, type ApiFetchResult, type JobListItem, type AccountIdentity, type MeResponse, type MergeQueueBounceDetail, type MergeQueueBounceKind, type MergeQueueEnqueuedBy, type MergeQueueEnqueuedVia, type MergeQueueEntryDto, type MergeQueueEntryState, type PrDismissedFinding, type PrFindingDismissInput, type PrFindingDismissOutcome, type PrFindingDismissResult, type PrReviewFinding, type PrReviewFindings, type PrVortexReview, type SettingsPatch, type RepoSettings, type RepoSettingsKey, type RepoSettingsPatch, type SettingsResponse, type StackDto, type StackPolicyPatch, type StackPublication, type StackPublicationRequest, type StackPublicationStatus, 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, noPrReviewYetMessage, 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";
|
package/dist/client/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { ACCOUNT_LIMIT, DEFAULT_API, accountNameFromMe, apiBase, configPath, keyDisplay, listAccounts, loadConfig, mergestormHome, removeAccount, resolveApiKey, saveAccount, settleAccountName, splitAccountFlag, useAccount, withAccount, } from "../config.js";
|
|
2
2
|
export { BEARER_SETTINGS, BEARER_BOOLEAN_SETTINGS_KEYS, BEARER_SETTINGS_FLAGS, BEARER_SETTINGS_KEYS, BEARER_SETTINGS_LABELS, } from "../automation-catalog.js";
|
|
3
|
-
export { API_TIMEOUT_PREFIX, DEFAULT_API_TIMEOUT_MS, SETTINGS_WRITABLE_KEYS, adoptStack, accountLabel, apiFetch, cancelMergeQueueEntry, devicePost, dismissPrFindings, dismissThreadsLine, enqueueStack, ensureUpperPark, getEnrichedStack, getIssueReview, ISSUE_REVIEW_DEFAULT_TIMEOUT_MS, ISSUE_REVIEW_POLL_INTERVAL_MS, ISSUE_REVIEW_STATES, IssueReviewTimeoutError, issueNotApprovedError, issueNotApprovedRefusal, issueReviewLines, parseIssueReviewStatus, pollIssueReview, getMe, getPrVortexReview, revokeOwnKey, getReview, getSettings, listRepoSettings, patchRepoSettings, REPO_SETTINGS_KEYS, getStackPublication, landNextStack, listJobs, listMergeQueueEntries, listStacks, listThreads, openStackIssue, parseRetryAfterSeconds, patchSettings, restackStack, setStackPolicy, stackPolicyBody, submitStackPublication, } from "../api.js";
|
|
3
|
+
export { API_TIMEOUT_PREFIX, DEFAULT_API_TIMEOUT_MS, SETTINGS_WRITABLE_KEYS, adoptStack, accountLabel, apiFetch, cancelMergeQueueEntry, devicePost, dismissPrFindings, dismissThreadsLine, enqueueStack, ensureUpperPark, getEnrichedStack, getIssueReview, ISSUE_REVIEW_DEFAULT_TIMEOUT_MS, ISSUE_REVIEW_POLL_INTERVAL_MS, ISSUE_REVIEW_STATES, IssueReviewTimeoutError, NO_LINKED_ISSUE, issueNotApprovedError, issueNotApprovedRefusal, issueReviewLines, parseIssueReviewStatus, pollIssueReview, getMe, getPrVortexReview, revokeOwnKey, getReview, getSettings, listRepoSettings, patchRepoSettings, REPO_SETTINGS_KEYS, getStackPublication, landNextStack, listJobs, listMergeQueueEntries, listStacks, listThreads, openStackIssue, parseRetryAfterSeconds, patchSettings, restackStack, setStackPolicy, stackPolicyBody, submitStackPublication, } 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, noPrReviewYetMessage, 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";
|
package/dist/commands/issue.js
CHANGED
|
@@ -135,6 +135,10 @@ function asAuthExit(err) {
|
|
|
135
135
|
function approvedLine(status) {
|
|
136
136
|
if (status.state === "off")
|
|
137
137
|
return `Issue reviews are off for ${status.owner}/${status.repo}; #${status.number} needs no approval.`;
|
|
138
|
+
if (status.state === "unreviewed")
|
|
139
|
+
return `Issue reviews for ${status.owner}/${status.repo} run on mention; #${status.number} needs no approval.`;
|
|
140
|
+
if (status.approvedBy === null)
|
|
141
|
+
return `The Vortex review of ${status.owner}/${status.repo}#${status.number} failed, so it does not hold the pull request back.`;
|
|
138
142
|
const by = status.approvedBy === "human" ? "a person" : "Vortex";
|
|
139
143
|
return `${status.owner}/${status.repo}#${status.number} is approved by ${by}.`;
|
|
140
144
|
}
|
|
@@ -161,7 +165,7 @@ export async function waitForIssueReview(target, opts, deps = {}) {
|
|
|
161
165
|
}
|
|
162
166
|
if (opts.json)
|
|
163
167
|
printJson(status);
|
|
164
|
-
if (status.state === "approved" || status.state === "off") {
|
|
168
|
+
if (status.state === "approved" || status.state === "off" || (status.mode === "mention" && status.state === "unreviewed")) {
|
|
165
169
|
if (!opts.json) {
|
|
166
170
|
console.log(approvedLine(status));
|
|
167
171
|
if (status.next)
|
package/dist/commands/login.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { type Config } from "../config.js";
|
|
2
|
-
import { type
|
|
2
|
+
import { type MeResponse } from "../api.js";
|
|
3
3
|
/**
|
|
4
4
|
* Probe /api/v1/me with an in-memory config. Does not write disk.
|
|
5
5
|
* Throws CommandError when the key must not be saved.
|
|
6
6
|
*/
|
|
7
7
|
export declare function validateApiKey(cfg: Config): Promise<MeResponse>;
|
|
8
|
-
export declare function loggedInLine(account: AccountIdentity | null | undefined): string;
|
|
9
8
|
export declare function pendingLoginPath(): string;
|
|
10
9
|
export interface LoginDeps {
|
|
11
10
|
openBrowser: (url: string) => void;
|
package/dist/commands/login.js
CHANGED
|
@@ -44,10 +44,6 @@ async function loginConfig() {
|
|
|
44
44
|
const store = await readStore();
|
|
45
45
|
return configFor(store, store.activeAccount);
|
|
46
46
|
}
|
|
47
|
-
export function loggedInLine(account) {
|
|
48
|
-
const label = accountLabel(account);
|
|
49
|
-
return label ? `Logged in as ${label}.` : "Logged in.";
|
|
50
|
-
}
|
|
51
47
|
async function storeLogin(cfg, me, opts) {
|
|
52
48
|
const apiKey = cfg.apiKey ?? "";
|
|
53
49
|
const name = accountNameFromMe(me) ?? provisionalAccountName(apiKey);
|
|
@@ -3,7 +3,7 @@ import { type RepoSettingsPatch, type SettingsPatch, type SettingsResponse } fro
|
|
|
3
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" | "auto_resolve_conflicts_enabled" | "auto_fix_ci_enabled" | "vortex_show_thinking_traces" | "repo_overview_enabled" | "cyclone_skip_ci_enabled" | "cyclone_patch_unverified_languages" | "vortex_auto_overflow_enabled" | "vortex_skip_all_clear_comments" | "vortex_seam_specialist_enabled" | "issue_review_enabled" | "auto_land_default" | "auto_land_settle_seconds" | "merge_queue_batch_enabled" | "merge_queue_batch_size" | "merge_queue_lands_alone_paths" | "merge_queue_overdrive" | "ignored_bot_logins" | "vortex_bot_skip_check" | "vortex_findings_check" | "cyclone_patch_failure_check">;
|
|
6
|
+
export declare const SETTINGS_FLAGS: Record<string, "auto_review_enabled" | "auto_patch_enabled" | "auto_resolve_conflicts_enabled" | "auto_fix_ci_enabled" | "vortex_show_thinking_traces" | "repo_overview_enabled" | "cyclone_skip_ci_enabled" | "cyclone_patch_unverified_languages" | "vortex_auto_overflow_enabled" | "vortex_skip_all_clear_comments" | "vortex_seam_specialist_enabled" | "issue_review_enabled" | "issue_auto_review_enabled" | "auto_land_default" | "auto_land_settle_seconds" | "merge_queue_batch_enabled" | "merge_queue_batch_size" | "merge_queue_lands_alone_paths" | "merge_queue_overdrive" | "ignored_bot_logins" | "vortex_bot_skip_check" | "vortex_findings_check" | "cyclone_patch_failure_check">;
|
|
7
7
|
type IgnoreBotOperation = {
|
|
8
8
|
action: "add" | "remove";
|
|
9
9
|
login: string;
|
|
@@ -19,6 +19,7 @@ const REPO_FLAG_LABELS = {
|
|
|
19
19
|
merge_queue_lands_alone_paths: "Merge queue lands-alone paths",
|
|
20
20
|
merge_queue_overdrive: "Merge queue overdrive",
|
|
21
21
|
issue_review_enabled: "Vortex issue reviews",
|
|
22
|
+
issue_auto_review_enabled: "Vortex issue auto review",
|
|
22
23
|
};
|
|
23
24
|
function parsePathsFlagValue(flag, value, defaults) {
|
|
24
25
|
if (value === undefined || value === "" || value.startsWith("--")) {
|
|
@@ -80,7 +81,7 @@ function parseRepoSettingsArgs(slug, args) {
|
|
|
80
81
|
throw new CommandError(`Unknown settings flag: ${flag}. See \`mergestorm --help\`.`, 2, "usage");
|
|
81
82
|
}
|
|
82
83
|
if (!REPO_SETTINGS_KEYS.includes(key)) {
|
|
83
|
-
throw new CommandError(`${flag} is an account setting. With --repo only --auto-patch, --auto-review, --auto-land, --merge-queue-batch, --merge-queue-batch-size, --merge-queue-lands-alone, --merge-queue-overdrive and --issue-review can be set.`, 2, "usage");
|
|
84
|
+
throw new CommandError(`${flag} is an account setting. With --repo only --auto-patch, --auto-review, --auto-land, --merge-queue-batch, --merge-queue-batch-size, --merge-queue-lands-alone, --merge-queue-overdrive, --issue-review and --issue-auto-review can be set.`, 2, "usage");
|
|
84
85
|
}
|
|
85
86
|
const value = eq >= 0 ? arg.slice(eq + 1) : args[++i];
|
|
86
87
|
const row = BEARER_SETTINGS.find((row) => row.key === key);
|
package/dist/commands/stack.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type StackRepairHint } from "../stack-blockers.js";
|
|
2
2
|
import { type StackWatchObligation } from "../stack-watch-obligation.js";
|
|
3
|
-
import { adoptStack, ensureUpperPark, findStackPull, getEnrichedStack, getIssueReview, getStackPublication, listMergeQueueEntries, listStacks, openStackPull, claimStackPulls, setStackPolicy, type StackDto, type StackPolicyPatch } from "../api.js";
|
|
3
|
+
import { adoptStack, ensureUpperPark, findStackPull, getEnrichedStack, getIssueReview, type IssueReviewMode, getStackPublication, listMergeQueueEntries, listStacks, openStackPull, claimStackPulls, setStackPolicy, type StackDto, type StackPolicyPatch } from "../api.js";
|
|
4
4
|
import { loadConfig } from "../config.js";
|
|
5
5
|
import { createPrs, findOpenPrNumber } from "../gh.js";
|
|
6
6
|
import { parseGithubOriginRepo } from "../git.js";
|
|
@@ -126,14 +126,14 @@ export type StackCreateDeps = {
|
|
|
126
126
|
};
|
|
127
127
|
export declare function cmdStackCreate(args: string[], deps?: StackCreateDeps): Promise<void>;
|
|
128
128
|
/** Injectable seams for unit tests (STRUCT-04). Production callers omit deps. */
|
|
129
|
-
export declare function
|
|
129
|
+
export declare function issueReviewModeFor(cfg: Awaited<ReturnType<typeof loadConfig>>, owner: string, repo: string): Promise<IssueReviewMode>;
|
|
130
130
|
export type IssueGateLayer = {
|
|
131
131
|
branch: string;
|
|
132
132
|
texts: string[];
|
|
133
133
|
};
|
|
134
134
|
export type IssueGateDeps = {
|
|
135
135
|
getIssueReview?: typeof getIssueReview;
|
|
136
|
-
|
|
136
|
+
issueReviewMode?: typeof issueReviewModeFor;
|
|
137
137
|
};
|
|
138
138
|
export declare function assertLinkedIssuesApproved(input: {
|
|
139
139
|
cfg: Awaited<ReturnType<typeof loadConfig>>;
|
package/dist/commands/stack.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cycloneStatusWords, stackBlockers, stackBlockersSummary } from "../stack-blockers.js";
|
|
2
2
|
import { STACK_WATCH_NOT_DONE_SENTENCE, STACK_WATCH_NOTIFY_PATTERN, stackTerminalReason, stackWatchObligation, } from "../stack-watch-obligation.js";
|
|
3
|
-
import { adoptStack, ensureUpperPark, findStackPull, getEnrichedStack, getIssueReview, getSettings, getStackPublication, issueNotApprovedError, landNextStack, listRepoSettings, listMergeQueueEntries, listStacks, openStackPull, claimStackPulls, restackStack, setStackPolicy, } from "../api.js";
|
|
3
|
+
import { adoptStack, ensureUpperPark, findStackPull, getEnrichedStack, getIssueReview, getIssueReviewMode, issueReviewBlocksPr, getSettings, getStackPublication, issueNotApprovedError, NO_LINKED_ISSUE, landNextStack, listRepoSettings, listMergeQueueEntries, listStacks, openStackPull, claimStackPulls, restackStack, setStackPolicy, } from "../api.js";
|
|
4
4
|
import { loadConfig } from "../config.js";
|
|
5
5
|
import { rateLimitedMessage, REVIEW_EXIT, CommandError } from "../errors.js";
|
|
6
6
|
import { createPrs, findOpenPrNumber, ghReady, githubApiFromEnv, } from "../gh.js";
|
|
@@ -681,15 +681,21 @@ export async function cmdStackCreate(args, deps = {}) {
|
|
|
681
681
|
await present("Stack", created);
|
|
682
682
|
}
|
|
683
683
|
/** Injectable seams for unit tests (STRUCT-04). Production callers omit deps. */
|
|
684
|
-
export async function
|
|
684
|
+
export async function issueReviewModeFor(cfg, owner, repo) {
|
|
685
|
+
const served = await getIssueReviewMode(cfg, { owner, repo }, { timeoutMs: STACK_GUARD_TIMEOUT_MS });
|
|
686
|
+
if (served)
|
|
687
|
+
return served;
|
|
685
688
|
const repos = await listRepoSettings(cfg, { timeoutMs: STACK_GUARD_TIMEOUT_MS });
|
|
686
689
|
const row = repos.find((r) => r.owner.toLowerCase() === owner.toLowerCase() && r.repo.toLowerCase() === repo.toLowerCase());
|
|
687
|
-
if (typeof row?.issue_review_enabled === "boolean")
|
|
688
|
-
return row.issue_review_enabled;
|
|
689
690
|
const settings = await getSettings(cfg, { timeoutMs: STACK_GUARD_TIMEOUT_MS });
|
|
690
691
|
if (!settings)
|
|
691
692
|
throw new CommandError("Could not read the account settings");
|
|
692
|
-
|
|
693
|
+
const account = settings;
|
|
694
|
+
const on = typeof row?.issue_review_enabled === "boolean" ? row.issue_review_enabled : account.issue_review_enabled === true;
|
|
695
|
+
if (!on)
|
|
696
|
+
return "off";
|
|
697
|
+
const auto = typeof row?.issue_auto_review_enabled === "boolean" ? row.issue_auto_review_enabled : account.issue_auto_review_enabled !== false;
|
|
698
|
+
return auto ? "auto" : "mention";
|
|
693
699
|
}
|
|
694
700
|
export async function assertLinkedIssuesApproved(input, deps = {}) {
|
|
695
701
|
const { cfg, owner, repo, layers, warn } = input;
|
|
@@ -720,22 +726,22 @@ export async function assertLinkedIssuesApproved(input, deps = {}) {
|
|
|
720
726
|
}
|
|
721
727
|
const unlinked = linked.filter((layer) => layer.numbers.length === 0);
|
|
722
728
|
if (unlinked.length > 0) {
|
|
723
|
-
let
|
|
729
|
+
let mode;
|
|
724
730
|
try {
|
|
725
|
-
|
|
731
|
+
mode = await (deps.issueReviewMode ?? issueReviewModeFor)(cfg, owner, repo);
|
|
726
732
|
}
|
|
727
733
|
catch (err) {
|
|
728
734
|
throw unverified(err);
|
|
729
735
|
}
|
|
730
|
-
if (
|
|
736
|
+
if (mode === "auto") {
|
|
731
737
|
throw new CommandError([
|
|
732
738
|
`Issue reviews are on for ${owner}/${repo}, and ${unlinked.map((layer) => layer.branch).join(", ")} links no issue.`,
|
|
733
|
-
"Open one with `mergestorm issue create --title <problem> --wait`,
|
|
739
|
+
"Open one with `mergestorm issue create --title <problem> --wait`, reply once to any blocking points in the Vortex review,",
|
|
734
740
|
"then add `Fixes #N` to the tip commit message and submit again.",
|
|
735
|
-
].join("\n"), REVIEW_EXIT.failed, "issue_not_approved");
|
|
741
|
+
].join("\n"), REVIEW_EXIT.failed, "issue_not_approved", { reason: NO_LINKED_ISSUE });
|
|
736
742
|
}
|
|
737
743
|
}
|
|
738
|
-
const blocking = statuses.filter(
|
|
744
|
+
const blocking = statuses.filter(issueReviewBlocksPr);
|
|
739
745
|
if (blocking.length > 0)
|
|
740
746
|
throw issueNotApprovedError(blocking);
|
|
741
747
|
}
|
|
@@ -1561,11 +1567,15 @@ async function cmdStackLand(args) {
|
|
|
1561
1567
|
]);
|
|
1562
1568
|
return;
|
|
1563
1569
|
}
|
|
1570
|
+
const cascadeFailed = body.cascadeFailed;
|
|
1564
1571
|
await present("Stack", [
|
|
1565
1572
|
ansi.brightGreen(` Landed` +
|
|
1566
1573
|
(pr != null ? ` #${pr}` : "") +
|
|
1567
1574
|
(branch ? ` (${branch})` : "") +
|
|
1568
1575
|
` on ${id}`),
|
|
1576
|
+
...(cascadeFailed
|
|
1577
|
+
? [ansi.yellow(` The layers above it were not updated: ${cascadeFailed}`)]
|
|
1578
|
+
: []),
|
|
1569
1579
|
]);
|
|
1570
1580
|
}
|
|
1571
1581
|
export async function cmdStack(args, options = {}) {
|
package/dist/stack-blockers.js
CHANGED
|
@@ -487,6 +487,8 @@ const AUTO_LAND_WAIT_WORDS = {
|
|
|
487
487
|
missing_head: "GitHub returned no head commit for this PR",
|
|
488
488
|
pr_draft: "this PR is a draft",
|
|
489
489
|
trunk_in_land_target_without_land_pr: "the stack's commits are already in the land target, but no merged land PR was found",
|
|
490
|
+
land_pr_push_unreviewed: "someone other than Stacks pushed to the stack trunk, and the land PR has no clean Vortex review at that push or at its head",
|
|
491
|
+
land_pr_head_unexplained: "GitHub's activity list for the stack trunk does not show the push that made the land PR head",
|
|
490
492
|
conflict_unchanged: "the merge queue bounced this PR for a conflict, and neither the PR nor its base has changed since",
|
|
491
493
|
land_pr_unavailable: "the land PR cannot be opened or found",
|
|
492
494
|
github_unreadable: "Mergestorm cannot read this PR from GitHub",
|
package/dist/ui/banner.js
CHANGED
|
@@ -236,6 +236,9 @@ const WHATS_NEW = {
|
|
|
236
236
|
"0.3.66": [
|
|
237
237
|
"`issue wait`; submit needs an approved issue",
|
|
238
238
|
],
|
|
239
|
+
"0.3.67": [
|
|
240
|
+
"Issue review: reply once, then one re-check",
|
|
241
|
+
],
|
|
239
242
|
};
|
|
240
243
|
function densityFromVariant(variant) {
|
|
241
244
|
if (variant === "mini" || variant === "nano")
|
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -15,7 +15,7 @@ Not for merging, landing, or pushing. For a PR that is already open on GitHub, u
|
|
|
15
15
|
|
|
16
16
|
## Before the PR: the issue review
|
|
17
17
|
|
|
18
|
-
Vortex reviews the issue against the repository before any code. Wait for its verdict with `mg issue wait
|
|
18
|
+
Vortex reviews the issue against the repository before any code. Wait for its verdict with `mg issue wait N`, which reads the verdict from Vortex itself; never judge it from comment text. Ready or Ready with notes approves the issue; notes do not block, and the PR review checks them against the code. If the verdict is Needs answers, reply once in a comment on the issue: for each blocking point, accept the suggested fix or say why not, and update the issue to match. Vortex then re-checks only those points, once, and approves the issue; a point still open goes to the PR review. Wait again after your reply. Start the PR only once the issue is approved: `mg stack submit` refuses each PR whose linked issue (`Fixes #N`) is not, so every layer of a stack needs its own approved issue. On a repository with issue reviews off the wait returns at once. If the wait times out, tell the person. A person can approve early with a comment whose first line is `mergestorm-issue: approve`; never post that command yourself.
|
|
19
19
|
|
|
20
20
|
## Install (Claude Code / Cursor / Agents)
|
|
21
21
|
|