mergestorm 0.3.61 → 0.3.63
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 +13 -8
- package/dist/api.d.ts +16 -8
- package/dist/api.js +16 -23
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +1 -1
- package/dist/commands/dismiss.js +4 -1
- package/dist/commands/login.d.ts +4 -1
- package/dist/commands/login.js +5 -3
- package/dist/commands/registry.js +1 -1
- package/dist/commands/review-client.d.ts +1 -2
- package/dist/commands/review-client.js +24 -3
- package/dist/commands/stack.d.ts +9 -1
- package/dist/commands/stack.js +99 -42
- package/dist/config.d.ts +1 -0
- package/dist/config.js +4 -1
- package/dist/errors.d.ts +1 -1
- package/dist/git-stack.d.ts +2 -0
- package/dist/git-stack.js +15 -0
- package/dist/repo-identity.d.ts +0 -3
- package/dist/repo-identity.js +0 -23
- package/dist/server-publish.d.ts +14 -1
- package/dist/server-publish.js +37 -9
- package/dist/stack-blockers.d.ts +4 -17
- package/dist/stack-blockers.js +93 -26
- package/dist/stack-dto.d.ts +2 -0
- package/dist/stack-meta.d.ts +0 -2
- package/dist/stack-meta.js +3 -81
- package/dist/stack-watch-loop.js +4 -1
- package/dist/stack-watch-obligation.d.ts +4 -1
- package/dist/stack-watch-obligation.js +7 -4
- package/dist/stack-watch.js +2 -1
- package/dist/ui/banner.js +6 -0
- package/package.json +9 -2
- package/skill/SKILL.md +1 -1
- package/skill/mergestorm-bounce-watch/SKILL.md +11 -5
- package/skill/mergestorm-pr-loop/SKILL.md +7 -3
package/dist/commands/stack.js
CHANGED
|
@@ -5,9 +5,9 @@ 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";
|
|
7
7
|
import { parseGithubOriginRepo } from "../git.js";
|
|
8
|
-
import { branchExists, commitsAheadOf, createBranchFromHead, currentBranch, defaultLayerBranchName, deleteBranch, discoverTrunk, fetchRemoteBranch, pushBranch, tipCommitMessage, tipCommitSubject, worktreeDirty, } from "../git-stack.js";
|
|
8
|
+
import { branchExists, commitsAheadOf, createBranchFrom, createBranchFromHead, trunkStartRef, currentBranch, defaultLayerBranchName, deleteBranch, discoverTrunk, fetchRemoteBranch, pushBranch, tipCommitMessage, tipCommitSubject, worktreeDirty, } from "../git-stack.js";
|
|
9
9
|
import { buildPrBodyFromCommit } from "../pr-body.js";
|
|
10
|
-
import { publishBranchServerSide, publishWorkingBranch, workingBranchPublication, } from "../server-publish.js";
|
|
10
|
+
import { publishBranchServerSide, assertExplicitStacking, publishWorkingBranch, workingBranchPublication, } from "../server-publish.js";
|
|
11
11
|
import { gitTopLevel } from "../repo-identity.js";
|
|
12
12
|
import { activeLayers, appendLayer, emptyStackMeta, loadStackMeta, removeActiveStack, resetStackMeta, saveStackMeta, selectStackForCreate, setActivePolicy, } from "../stack-meta.js";
|
|
13
13
|
import { pollStackWatch, StackWatchError, StackWatchTimeoutError, } from "../stack-watch.js";
|
|
@@ -52,18 +52,20 @@ export function openStackWatch(stackId, opts = {}) {
|
|
|
52
52
|
const STACK_USAGE = `usage:
|
|
53
53
|
mergestorm stack create [name] [--onto <branch>] [--trunk <branch>] [--extend] [--auto-land on|off] [--auto-review on|off] [--auto-patch on|off] [--auto-resolve-conflicts on|off] [--auto-fix-ci on|off] [--json]
|
|
54
54
|
mergestorm stack submit [--extend] [--auto-land on|off] [--auto-review on|off] [--auto-patch on|off] [--auto-resolve-conflicts on|off] [--auto-fix-ci on|off] [--rest] [--server-side] [--json]
|
|
55
|
-
mergestorm stack publish [--branch <branch>] [--base <branch>] [--title <title>] [--replace-head <sha>] [--auto-land on|off] [--status] [--json]
|
|
55
|
+
mergestorm stack publish [--branch <branch>] [--base <branch>] [--extend] [--title <title>] [--replace-head <sha>] [--auto-land on|off] [--status] [--json]
|
|
56
56
|
mergestorm stack reset --force
|
|
57
57
|
mergestorm stack list [--json]
|
|
58
58
|
mergestorm stack status <stack-id> [--json]
|
|
59
59
|
mergestorm stack wait <stack-id> [--json] [--timeout <s>]
|
|
60
60
|
--json writes exactly one JSON document to stdout. The timeout or error notice goes to stderr
|
|
61
61
|
(exit 5 on timeout), so do not merge the streams (2>&1) before parsing stdout.
|
|
62
|
-
mergestorm stack watch <stack-id> [--until attention|landed] [--ignore <blocker-substring>] [--max <minutes>] [--head <sha>] [--json]
|
|
62
|
+
mergestorm stack watch <stack-id> [--until attention|landed] [--ignore <blocker-substring>] [--max <minutes>] [--head <sha>] [--bounce-id <id>] [--after-finished-at <iso>] [--json]
|
|
63
63
|
Loops stack wait with the cursor and stays silent while the stack is waiting or in progress.
|
|
64
64
|
Prints one MS-WATCH ATTENTION line and exits 3 on attention or on reads that keep failing (a permanent
|
|
65
65
|
error twice, or 15 minutes of timeouts and 5xx, which it retries with backoff); prints MS-WATCH LANDED
|
|
66
66
|
and exits 0 only for a confirmed landing, including a stack Mergestorm deleted after its queue entry landed. Other terminal reasons exit 3 with ATTENTION. Run it as a background command.
|
|
67
|
+
An ATTENTION line for a merge queue bounce ends with restart="mg stack watch <id> --bounce-id <id> --after-finished-at <iso>":
|
|
68
|
+
restart with that command (plus --head <sha> after a push) so a bounce you already handled is not reported again.
|
|
67
69
|
--json writes exactly one JSON document to stdout and sends the MS-WATCH lines to stderr.
|
|
68
70
|
mergestorm stack set <stack-id> [--auto-land on|off] [--auto-review on|off|default] [--auto-patch on|off|default] [--auto-resolve-conflicts on|off|default] [--auto-fix-ci on|off|default] [--json]
|
|
69
71
|
mergestorm stack adopt <owner/repo>#<pr> [--auto-land on|off] [--auto-review on|off] [--auto-patch on|off] [--auto-resolve-conflicts on|off] [--auto-fix-ci on|off] [--json]
|
|
@@ -85,17 +87,21 @@ const STACK_USAGE = `usage:
|
|
|
85
87
|
stack submit --server-side and stack publish need no GitHub credentials: mg
|
|
86
88
|
reads your commits from the local repository and sends them to Mergestorm,
|
|
87
89
|
which writes the commits and the branch on GitHub as the Surge App, opens the
|
|
88
|
-
pull request, and registers the stack
|
|
90
|
+
pull request, and registers the stack. Auto patch follows the stack, the
|
|
91
|
+
repository and your account, like any other stack. They never
|
|
89
92
|
run gh, git push or git fetch. stack publish does this for one branch and is
|
|
90
93
|
how you send later commits to a branch that is already published. Mergestorm
|
|
91
94
|
refuses to move a branch whose head on GitHub matches no local commit unless
|
|
92
95
|
you name that head with --replace-head. stack publish --status reads how far
|
|
93
96
|
a branch got (absent, pushed, opened, registered) without changing anything;
|
|
94
97
|
after a lost response or a failure, run the same command again to resume.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
stack create starts a new single PR from trunk, even on another PR's
|
|
99
|
+
branch. --extend stacks it on the checked-out branch (or --onto <branch>)
|
|
100
|
+
instead; --onto or publish --base a branch other than trunk without
|
|
101
|
+
--extend is refused. A layer on a registered stack also needs --extend on
|
|
102
|
+
submit. create, submit and publish say which one they made. stack land
|
|
103
|
+
promotes into the review unit when one exists; otherwise lands the bottom
|
|
104
|
+
open PR (unit-less stacks).`;
|
|
99
105
|
/** Cap the registered-stack guard's API probe so it cannot stall local authoring. */
|
|
100
106
|
const STACK_GUARD_TIMEOUT_MS = 8_000;
|
|
101
107
|
async function listRegisteredStacksRetryingOnce(list, cfg) {
|
|
@@ -149,6 +155,13 @@ export function assertMayParentOntoRegistered(parentBranch, stacks, extend, verb
|
|
|
149
155
|
`• New independent stack: check out trunk and pass \`--onto <trunk>\` (e.g. \`mg stack create --onto main\`).\n` +
|
|
150
156
|
`• Intentionally grow that stack: pass \`--extend\` on \`stack ${verb}\`.`, 1, "registered_parent");
|
|
151
157
|
}
|
|
158
|
+
export function stackShapeLine(input) {
|
|
159
|
+
if (input.extendedStack)
|
|
160
|
+
return ` Added ${input.layers === 1 ? "1 layer" : `${input.layers} layers`} to an existing stack (--extend).`;
|
|
161
|
+
if (input.layers === 1)
|
|
162
|
+
return ` A new single PR onto ${input.base}.`;
|
|
163
|
+
return ` A new stack of ${input.layers} layers onto ${input.base}.`;
|
|
164
|
+
}
|
|
152
165
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
153
166
|
export function requireStackId(raw, usage) {
|
|
154
167
|
const id = raw?.trim() ?? "";
|
|
@@ -522,6 +535,8 @@ export async function cmdStackCreate(args, deps = {}) {
|
|
|
522
535
|
const currentBranchFn = deps.currentBranch ?? currentBranch;
|
|
523
536
|
const branchExistsFn = deps.branchExists ?? branchExists;
|
|
524
537
|
const createBranchFromHeadFn = deps.createBranchFromHead ?? createBranchFromHead;
|
|
538
|
+
const createBranchFromFn = deps.createBranchFrom ?? createBranchFrom;
|
|
539
|
+
const trunkStartRefFn = deps.trunkStartRef ?? trunkStartRef;
|
|
525
540
|
const deleteBranchFn = deps.deleteBranch ?? deleteBranch;
|
|
526
541
|
const discoverTrunkFn = deps.discoverTrunk ?? discoverTrunk;
|
|
527
542
|
const defaultLayerBranchNameFn = deps.defaultLayerBranchName ?? defaultLayerBranchName;
|
|
@@ -539,17 +554,6 @@ export async function cmdStackCreate(args, deps = {}) {
|
|
|
539
554
|
if (worktreeDirtyFn(cwd)) {
|
|
540
555
|
throw new CommandError("Working tree is dirty. Commit or stash changes before `stack create`.");
|
|
541
556
|
}
|
|
542
|
-
const parentBranch = onto?.trim() || currentBranchFn(cwd);
|
|
543
|
-
if (!parentBranch) {
|
|
544
|
-
throw new CommandError("Detached HEAD. Check out a branch (or pass --onto <branch>) before `stack create`.");
|
|
545
|
-
}
|
|
546
|
-
if (!branchExistsFn(parentBranch, cwd)) {
|
|
547
|
-
throw new CommandError(`Parent branch not found: ${parentBranch}`);
|
|
548
|
-
}
|
|
549
|
-
const branch = defaultLayerBranchNameFn(name);
|
|
550
|
-
if (branchExistsFn(branch, cwd)) {
|
|
551
|
-
throw new CommandError(`Branch already exists: ${branch}`);
|
|
552
|
-
}
|
|
553
557
|
let meta = await loadStackMetaFn(cwd);
|
|
554
558
|
const trunk = trunkFlag?.trim() || meta?.trunk || discoverTrunkFn(cwd);
|
|
555
559
|
if (!meta) {
|
|
@@ -558,8 +562,34 @@ export async function cmdStackCreate(args, deps = {}) {
|
|
|
558
562
|
else if (trunkFlag?.trim() && meta.trunk !== trunk) {
|
|
559
563
|
throw new CommandError(`Stack trunk is ${meta.trunk}; refusing --trunk ${trunk}. Run \`mg stack reset --force\` to discard local pre-submit state.`);
|
|
560
564
|
}
|
|
561
|
-
|
|
562
|
-
|
|
565
|
+
const checkedOut = currentBranchFn(cwd);
|
|
566
|
+
if (extend && !onto?.trim() && !checkedOut) {
|
|
567
|
+
throw new CommandError("Detached HEAD. Check out a branch (or pass --onto <branch>) to stack with --extend.");
|
|
568
|
+
}
|
|
569
|
+
const parentBranch = onto?.trim() || (extend && checkedOut ? checkedOut : trunk);
|
|
570
|
+
assertExplicitStacking({
|
|
571
|
+
parent: parentBranch,
|
|
572
|
+
trunk,
|
|
573
|
+
extend,
|
|
574
|
+
stackCommand: `\`mg stack create ${name ?? "<name>"} --onto ${parentBranch} --extend\``,
|
|
575
|
+
trunkCommand: `\`mg stack create ${name ?? "<name>"}\``,
|
|
576
|
+
});
|
|
577
|
+
let startRef = null;
|
|
578
|
+
if (parentBranch === trunk && checkedOut !== trunk) {
|
|
579
|
+
startRef = trunkStartRefFn(trunk, cwd);
|
|
580
|
+
if (!startRef)
|
|
581
|
+
throw new CommandError(`Trunk branch not found: ${trunk}`);
|
|
582
|
+
}
|
|
583
|
+
else if (!branchExistsFn(parentBranch, cwd)) {
|
|
584
|
+
throw new CommandError(`Parent branch not found: ${parentBranch}`);
|
|
585
|
+
}
|
|
586
|
+
else if (parentBranch !== checkedOut) {
|
|
587
|
+
startRef = parentBranch;
|
|
588
|
+
}
|
|
589
|
+
const branch = defaultLayerBranchNameFn(name);
|
|
590
|
+
if (branchExistsFn(branch, cwd)) {
|
|
591
|
+
throw new CommandError(`Branch already exists: ${branch}`);
|
|
592
|
+
}
|
|
563
593
|
if (trunkFlag?.trim() && !onto?.trim() && parentBranch !== trunk) {
|
|
564
594
|
console.error(ansi.dim(` note: --trunk ${trunk} does not change the parent; new layer parents onto \`${parentBranch}\`. ` +
|
|
565
595
|
`Pass \`--onto ${trunk}\` for a fresh stack from trunk.`));
|
|
@@ -603,7 +633,10 @@ export async function cmdStackCreate(args, deps = {}) {
|
|
|
603
633
|
throw new CommandError(err instanceof Error ? err.message : String(err));
|
|
604
634
|
}
|
|
605
635
|
try {
|
|
606
|
-
|
|
636
|
+
if (startRef)
|
|
637
|
+
createBranchFromFn(branch, startRef, cwd);
|
|
638
|
+
else
|
|
639
|
+
createBranchFromHeadFn(branch, cwd);
|
|
607
640
|
}
|
|
608
641
|
catch (err) {
|
|
609
642
|
throw new CommandError(err instanceof Error ? err.message : String(err));
|
|
@@ -634,7 +667,9 @@ export async function cmdStackCreate(args, deps = {}) {
|
|
|
634
667
|
return;
|
|
635
668
|
}
|
|
636
669
|
const created = [
|
|
637
|
-
ansi.brightGreen(
|
|
670
|
+
ansi.brightGreen(parentBranch === meta.trunk
|
|
671
|
+
? ` Created ${branch}: a new single PR onto ${parentBranch}${startRef ? ` (branched from ${startRef})` : ""}`
|
|
672
|
+
: ` Created ${branch}: a layer stacked on ${parentBranch} (--extend)`),
|
|
638
673
|
];
|
|
639
674
|
if (registeredHit) {
|
|
640
675
|
created.push(ansi.dim(` extending registered stack ${registeredHit.stackId}` +
|
|
@@ -782,9 +817,6 @@ export async function cmdStackSubmit(args, deps = {}) {
|
|
|
782
817
|
}
|
|
783
818
|
}
|
|
784
819
|
if (serverSide) {
|
|
785
|
-
if (autoPatch === true) {
|
|
786
|
-
throw new CommandError("--server-side publishes your own commits and keeps Auto patch off for the stack. Drop --auto-patch on, or submit without --server-side.", REVIEW_EXIT.usage, "usage");
|
|
787
|
-
}
|
|
788
820
|
const parked = plans.find((plan) => plan.githubBase == null);
|
|
789
821
|
if (parked) {
|
|
790
822
|
throw new CommandError(`Layer ${parked.branch} opens onto a park freeze, which --server-side does not mint. ` +
|
|
@@ -796,6 +828,7 @@ export async function cmdStackSubmit(args, deps = {}) {
|
|
|
796
828
|
const remoteHeads = new Map();
|
|
797
829
|
const published = [];
|
|
798
830
|
let publishedStackId = null;
|
|
831
|
+
let publishedAutoPatch = null;
|
|
799
832
|
for (const plan of plans) {
|
|
800
833
|
if (!branchExistsFn(plan.branch, cwd)) {
|
|
801
834
|
throw new CommandError(`Stack layer branch missing locally: ${plan.branch}`);
|
|
@@ -825,6 +858,7 @@ export async function cmdStackSubmit(args, deps = {}) {
|
|
|
825
858
|
});
|
|
826
859
|
remoteHeads.set(plan.branch, result.head.sha);
|
|
827
860
|
publishedStackId = result.stack.id;
|
|
861
|
+
publishedAutoPatch = result.stack.autoPatchOverride;
|
|
828
862
|
published.push({
|
|
829
863
|
branch: plan.branch,
|
|
830
864
|
base: result.baseBranch,
|
|
@@ -834,19 +868,20 @@ export async function cmdStackSubmit(args, deps = {}) {
|
|
|
834
868
|
tree: result.head.tree,
|
|
835
869
|
});
|
|
836
870
|
}
|
|
837
|
-
const { autoEnqueueWhenReady: _autoLand,
|
|
871
|
+
const { autoEnqueueWhenReady: _autoLand, ...otherOverrides } = policy ?? {};
|
|
838
872
|
if (publishedStackId && Object.keys(otherOverrides).length > 0) {
|
|
839
873
|
await (deps.setStackPolicy ?? setStackPolicy)(publishedStackId, otherOverrides, config);
|
|
840
874
|
}
|
|
841
875
|
await saveStackMetaFn(removeActiveStack(meta), cwd);
|
|
842
876
|
const watch = publishedStackId ? openStackWatch(publishedStackId) : null;
|
|
843
877
|
if (asJson) {
|
|
844
|
-
console.log(JSON.stringify({ owner, repo, layers: published, stackId: publishedStackId, trunk: meta.trunk, chain: null, serverSide: true, autoPatchOverride:
|
|
878
|
+
console.log(JSON.stringify({ owner, repo, layers: published, stackId: publishedStackId, trunk: meta.trunk, chain: null, serverSide: true, autoPatchOverride: otherOverrides.autoPatchOverride ?? publishedAutoPatch, watch }, null, 2));
|
|
845
879
|
return;
|
|
846
880
|
}
|
|
847
881
|
const lines = published.map((row) => ansi.brightGreen(` #${row.prNumber} ${row.branch} → ${row.base} (${row.created ? "opened" : "exists"}, head ${row.head.slice(0, 12)})`));
|
|
882
|
+
lines.push(ansi.brightGreen(stackShapeLine({ layers: layers.length, base: plans[0]?.githubBase ?? meta.trunk, extendedStack: registeredHits > 0 })));
|
|
848
883
|
if (publishedStackId)
|
|
849
|
-
lines.push(ansi.brightGreen(` Registered stack ${publishedStackId}
|
|
884
|
+
lines.push(ansi.brightGreen(` Registered stack ${publishedStackId}`));
|
|
850
885
|
lines.push(ansi.dim(" Dashboard: https://mergestorm.ai/dashboard"));
|
|
851
886
|
if (watch)
|
|
852
887
|
lines.push(...stackWatchTextLines(watch).map((line) => ` ${line}`));
|
|
@@ -963,9 +998,6 @@ export async function cmdStackSubmit(args, deps = {}) {
|
|
|
963
998
|
await registerPr(opened[opened.length - 1].prNumber);
|
|
964
999
|
}
|
|
965
1000
|
else {
|
|
966
|
-
if (opened.length === 0) {
|
|
967
|
-
throw new CommandError("No pull requests to register after submit.");
|
|
968
|
-
}
|
|
969
1001
|
await registerPr(opened[0].prNumber);
|
|
970
1002
|
}
|
|
971
1003
|
const data = adoptData;
|
|
@@ -988,6 +1020,7 @@ export async function cmdStackSubmit(args, deps = {}) {
|
|
|
988
1020
|
const verb = row.created ? "opened" : "exists";
|
|
989
1021
|
summary.push(ansi.brightGreen(` #${row.prNumber} ${row.branch} → ${row.base} (${verb})`));
|
|
990
1022
|
}
|
|
1023
|
+
summary.push(ansi.brightGreen(stackShapeLine({ layers: layers.length, base: plans[0]?.githubBase ?? meta.trunk, extendedStack: registeredHits > 0 })));
|
|
991
1024
|
const adoptedTrunk = data.stack?.trunkBranch;
|
|
992
1025
|
if (adoptedTrunk?.startsWith("mg-stack-")) {
|
|
993
1026
|
summary.push(ansi.dim(` Bottom base is now ${adoptedTrunk} (Mergestorm-owned trunk). Leave it.`));
|
|
@@ -1004,8 +1037,8 @@ export async function cmdStackSubmit(args, deps = {}) {
|
|
|
1004
1037
|
await present("Stack", summary);
|
|
1005
1038
|
}
|
|
1006
1039
|
export function parseStackPublishArgs(args) {
|
|
1007
|
-
const usage = "usage: mergestorm stack publish [--branch <branch>] [--base <branch>] [--title <title>] [--replace-head <sha>] [--auto-land on|off] [--status] [--json]";
|
|
1008
|
-
const out = { status: false, asJson: false };
|
|
1040
|
+
const usage = "usage: mergestorm stack publish [--branch <branch>] [--base <branch>] [--extend] [--title <title>] [--replace-head <sha>] [--auto-land on|off] [--status] [--json]";
|
|
1041
|
+
const out = { extend: false, status: false, asJson: false };
|
|
1009
1042
|
const valued = {
|
|
1010
1043
|
"--branch": "branch",
|
|
1011
1044
|
"--base": "base",
|
|
@@ -1022,6 +1055,10 @@ export function parseStackPublishArgs(args) {
|
|
|
1022
1055
|
out.status = true;
|
|
1023
1056
|
continue;
|
|
1024
1057
|
}
|
|
1058
|
+
if (a === "--extend") {
|
|
1059
|
+
out.extend = true;
|
|
1060
|
+
continue;
|
|
1061
|
+
}
|
|
1025
1062
|
if (a === "--auto-land") {
|
|
1026
1063
|
out.autoLand = parseOnOff(args[++i], "--auto-land", "stack publish");
|
|
1027
1064
|
continue;
|
|
@@ -1057,7 +1094,7 @@ export async function cmdStackPublish(args, deps = {}) {
|
|
|
1057
1094
|
...(status.head ? [ansi.dim(` head ${status.head.sha} (tree ${status.head.tree ?? "unknown"})`)] : []),
|
|
1058
1095
|
...(status.pr ? [ansi.dim(` #${status.pr.number} → ${status.pr.base} ${status.pr.url}`)] : []),
|
|
1059
1096
|
...(status.stack
|
|
1060
|
-
? [ansi.dim(` stack ${status.stack.id} auto-patch ${status.stack.autoPatchOverride ===
|
|
1097
|
+
? [ansi.dim(` stack ${status.stack.id} auto-patch ${status.stack.autoPatchOverride === null ? "default" : status.stack.autoPatchOverride ? "on" : "off"} auto-land ${status.stack.autoLand ? "on" : "off"}`)]
|
|
1061
1098
|
: []),
|
|
1062
1099
|
]);
|
|
1063
1100
|
return;
|
|
@@ -1067,6 +1104,7 @@ export async function cmdStackPublish(args, deps = {}) {
|
|
|
1067
1104
|
...(parsed.title ? { title: parsed.title } : {}),
|
|
1068
1105
|
...(typeof parsed.autoLand === "boolean" ? { autoLand: parsed.autoLand } : {}),
|
|
1069
1106
|
...(parsed.replaceHead ? { replaceHead: parsed.replaceHead } : {}),
|
|
1107
|
+
extend: parsed.extend,
|
|
1070
1108
|
}, publishDeps);
|
|
1071
1109
|
const watch = openStackWatch(result.stack.id);
|
|
1072
1110
|
if (parsed.asJson) {
|
|
@@ -1077,7 +1115,8 @@ export async function cmdStackPublish(args, deps = {}) {
|
|
|
1077
1115
|
await present("Stack", [
|
|
1078
1116
|
ansi.brightGreen(` ${result.branch} ${moved} at ${result.head.sha.slice(0, 12)} (tree ${result.head.tree.slice(0, 12)})`),
|
|
1079
1117
|
ansi.brightGreen(` #${result.pr.number} ${result.branch} → ${result.baseBranch} (${result.pr.created ? "opened" : "exists"}) ${result.pr.url}`),
|
|
1080
|
-
ansi.brightGreen(`
|
|
1118
|
+
ansi.brightGreen(result.stacked ? ` A layer stacked on ${result.baseBranch}.` : ` A single PR onto ${result.baseBranch}.`),
|
|
1119
|
+
ansi.brightGreen(` Registered stack ${result.stack.id}`),
|
|
1081
1120
|
...stackWatchTextLines(watch).map((line) => ` ${line}`),
|
|
1082
1121
|
]);
|
|
1083
1122
|
}
|
|
@@ -1175,13 +1214,15 @@ export async function cmdStackWait(args, deps = {}) {
|
|
|
1175
1214
|
}
|
|
1176
1215
|
}
|
|
1177
1216
|
export async function cmdStackWatch(args, deps = {}) {
|
|
1178
|
-
const usage = "usage: mergestorm stack watch <stack-id> [--until attention|landed] [--ignore <blocker-substring>] [--max <minutes>] [--head <sha>] [--json]";
|
|
1217
|
+
const usage = "usage: mergestorm stack watch <stack-id> [--until attention|landed] [--ignore <blocker-substring>] [--max <minutes>] [--head <sha>] [--bounce-id <id>] [--after-finished-at <iso>] [--json]";
|
|
1179
1218
|
const fail = (detail) => new CommandError(detail ? `${usage}\n${detail}` : usage, REVIEW_EXIT.usage, "usage");
|
|
1180
1219
|
const positional = [];
|
|
1181
1220
|
const ignore = [];
|
|
1182
1221
|
let until = "attention";
|
|
1183
1222
|
let maxMinutes;
|
|
1184
1223
|
let head;
|
|
1224
|
+
let bounceId;
|
|
1225
|
+
let afterFinishedAt;
|
|
1185
1226
|
let json = false;
|
|
1186
1227
|
for (let i = 0; i < args.length; i++) {
|
|
1187
1228
|
const arg = args[i];
|
|
@@ -1212,6 +1253,17 @@ export async function cmdStackWatch(args, deps = {}) {
|
|
|
1212
1253
|
if (!/^[0-9a-f]{7,40}$/i.test(head))
|
|
1213
1254
|
throw fail("--head must be a commit SHA");
|
|
1214
1255
|
}
|
|
1256
|
+
else if (flag === "--bounce-id") {
|
|
1257
|
+
bounceId = value();
|
|
1258
|
+
if (!/^[0-9a-z-]{1,64}$/i.test(bounceId))
|
|
1259
|
+
throw fail("--bounce-id must be a merge queue entry id");
|
|
1260
|
+
}
|
|
1261
|
+
else if (flag === "--after-finished-at") {
|
|
1262
|
+
const raw = value();
|
|
1263
|
+
if (Number.isNaN(Date.parse(raw)))
|
|
1264
|
+
throw fail("--after-finished-at must be an ISO timestamp");
|
|
1265
|
+
afterFinishedAt = new Date(raw).toISOString();
|
|
1266
|
+
}
|
|
1215
1267
|
else if (arg.startsWith("-"))
|
|
1216
1268
|
throw fail();
|
|
1217
1269
|
else
|
|
@@ -1227,7 +1279,12 @@ export async function cmdStackWatch(args, deps = {}) {
|
|
|
1227
1279
|
json,
|
|
1228
1280
|
signal: deps.signal,
|
|
1229
1281
|
...(maxMinutes !== undefined ? { maxMs: maxMinutes * 60_000 } : {}),
|
|
1230
|
-
...(head
|
|
1282
|
+
...(head || bounceId || afterFinishedAt ? { cursor: {
|
|
1283
|
+
stackId: stackId.trim().toLowerCase(),
|
|
1284
|
+
enrolledHeadSha: head ? head.toLowerCase() : null,
|
|
1285
|
+
...(bounceId ? { bounceId } : {}),
|
|
1286
|
+
...(afterFinishedAt ? { afterFinishedAt } : {}),
|
|
1287
|
+
} } : {}),
|
|
1231
1288
|
});
|
|
1232
1289
|
if (result.exitCode === STACK_WATCH_EXIT.landed)
|
|
1233
1290
|
return;
|
|
@@ -1273,7 +1330,7 @@ export async function cmdStackStatus(args, deps = {}) {
|
|
|
1273
1330
|
freshCursor: true,
|
|
1274
1331
|
});
|
|
1275
1332
|
if (asJson) {
|
|
1276
|
-
console.log(JSON.stringify({ ...stack, held, repair, queueWait, watch }, null, 2));
|
|
1333
|
+
console.log(JSON.stringify({ ...stack, attention, held, repair, queueWait, watch }, null, 2));
|
|
1277
1334
|
return;
|
|
1278
1335
|
}
|
|
1279
1336
|
const lines = formatStackHuman(stack, false);
|
|
@@ -1287,7 +1344,7 @@ export async function cmdStackStatus(args, deps = {}) {
|
|
|
1287
1344
|
}
|
|
1288
1345
|
if (queueWait && !attention)
|
|
1289
1346
|
lines.push(` Queue: #${queueWait.prNumber} ${queueWait.text}`);
|
|
1290
|
-
lines.push(...[...stackHeldLine(held), ...stackRepairLine(repair), ...stackWatchTextLines(watch)].map((line) => ` ${line}`));
|
|
1347
|
+
lines.push(...[...stackHeldLine(attention ? null : held), ...stackRepairLine(repair), ...stackWatchTextLines(watch)].map((line) => ` ${line}`));
|
|
1291
1348
|
await present("Stack status", lines);
|
|
1292
1349
|
}
|
|
1293
1350
|
/** Human summary lines for a `stack set` write, one per flag. */
|
|
@@ -1446,7 +1503,7 @@ export async function cmdStack(args, options = {}) {
|
|
|
1446
1503
|
return cmdStackAdopt(rest);
|
|
1447
1504
|
if (sub === "restack")
|
|
1448
1505
|
return cmdStackRestack(rest);
|
|
1449
|
-
if (sub === "land"
|
|
1506
|
+
if (sub === "land")
|
|
1450
1507
|
return cmdStackLand(rest);
|
|
1451
1508
|
throw new CommandError(`${STACK_USAGE}\nunknown stack subcommand: ${sub}`);
|
|
1452
1509
|
}
|
package/dist/config.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export declare const DEFAULT_API = "https://api.mergestorm.ai";
|
|
|
2
2
|
export type Config = {
|
|
3
3
|
apiKey?: string;
|
|
4
4
|
apiBase?: string;
|
|
5
|
+
ignoreEnv?: boolean;
|
|
5
6
|
};
|
|
6
7
|
export declare function mergestormHome(env?: NodeJS.ProcessEnv): string;
|
|
7
8
|
export declare function configPath(): string;
|
package/dist/config.js
CHANGED
|
@@ -30,9 +30,12 @@ export async function clearConfig() {
|
|
|
30
30
|
await rm(configPath(), { force: true });
|
|
31
31
|
}
|
|
32
32
|
export function apiBase(cfg) {
|
|
33
|
-
|
|
33
|
+
const fromEnv = cfg.ignoreEnv ? undefined : process.env.MERGESTORM_API_URL?.trim();
|
|
34
|
+
return (fromEnv || cfg.apiBase || DEFAULT_API).replace(/\/$/, "");
|
|
34
35
|
}
|
|
35
36
|
export function resolveApiKey(cfg) {
|
|
37
|
+
if (cfg.ignoreEnv)
|
|
38
|
+
return cfg.apiKey;
|
|
36
39
|
return process.env.MERGESTORM_API_KEY?.trim() || cfg.apiKey;
|
|
37
40
|
}
|
|
38
41
|
/** Short display form of a stored key (prefix only). */
|
package/dist/errors.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Stable machine codes for CommandError control flow.
|
|
3
3
|
* UX copy in `message` may change freely; match on `code`, never on message text.
|
|
4
4
|
*/
|
|
5
|
-
export type CommandErrorCode = "auth_invalid" | "api_timeout" | "not_a_repo" | "not_found" | "missing_api_key" | "registered_parent" | "usage" | "review_quota" | "review_failed" | "review_timeout" | "rate_limited" | "cyclone_not_installed" | "pr_merged" | "pr_closed" | "not_stacked" | "draft" | "reserved_branch" | "supabase_unconfigured" | "cyclone_not_connected" | "cyclone_permission_missing" | "busy" | "adopt_failed" | "github_unreadable" | "stack_attention" | "head_changed" | "base_changed" | "tree_mismatch" | "protected_branch" | "payload_too_large" | "unsupported_publication" | "publication_failed" | "unknown_label";
|
|
5
|
+
export type CommandErrorCode = "auth_invalid" | "api_timeout" | "not_a_repo" | "not_found" | "missing_api_key" | "registered_parent" | "implicit_stack" | "usage" | "review_quota" | "review_failed" | "review_timeout" | "rate_limited" | "cyclone_not_installed" | "pr_merged" | "pr_closed" | "not_stacked" | "draft" | "reserved_branch" | "supabase_unconfigured" | "cyclone_not_connected" | "cyclone_permission_missing" | "busy" | "adopt_failed" | "github_unreadable" | "stack_attention" | "head_changed" | "base_changed" | "tree_mismatch" | "protected_branch" | "payload_too_large" | "unsupported_publication" | "publication_failed" | "unknown_label";
|
|
6
6
|
/** Stable one-shot exit codes for machine callers of `mergestorm review`. */
|
|
7
7
|
export declare const REVIEW_EXIT: {
|
|
8
8
|
readonly success: 0;
|
package/dist/git-stack.d.ts
CHANGED
|
@@ -15,6 +15,8 @@ export declare function discoverTrunk(cwd?: string): string;
|
|
|
15
15
|
export declare function branchExists(name: string, cwd?: string): boolean;
|
|
16
16
|
export declare function deleteBranch(name: string, cwd?: string): void;
|
|
17
17
|
export declare function createBranchFromHead(name: string, cwd?: string): void;
|
|
18
|
+
export declare function createBranchFrom(name: string, start: string, cwd?: string): void;
|
|
19
|
+
export declare function trunkStartRef(trunk: string, cwd?: string): string | null;
|
|
18
20
|
/** Slugify a free-form name into a safe branch segment. */
|
|
19
21
|
export declare function slugifyBranchName(raw: string): string;
|
|
20
22
|
export declare function defaultLayerBranchName(explicit?: string): string;
|
package/dist/git-stack.js
CHANGED
|
@@ -78,6 +78,21 @@ export function deleteBranch(name, cwd = process.cwd()) {
|
|
|
78
78
|
export function createBranchFromHead(name, cwd = process.cwd()) {
|
|
79
79
|
git(["checkout", "-b", name], cwd);
|
|
80
80
|
}
|
|
81
|
+
export function createBranchFrom(name, start, cwd = process.cwd()) {
|
|
82
|
+
git(["checkout", "-b", name, start], cwd);
|
|
83
|
+
}
|
|
84
|
+
export function trunkStartRef(trunk, cwd = process.cwd()) {
|
|
85
|
+
for (const ref of [`refs/remotes/origin/${trunk}`, `refs/heads/${trunk}`]) {
|
|
86
|
+
try {
|
|
87
|
+
git(["rev-parse", "--verify", "--quiet", ref], cwd);
|
|
88
|
+
return ref === `refs/heads/${trunk}` ? trunk : `origin/${trunk}`;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
81
96
|
/** Slugify a free-form name into a safe branch segment. */
|
|
82
97
|
export function slugifyBranchName(raw) {
|
|
83
98
|
const s = raw
|
package/dist/repo-identity.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
export declare const LEGACY_STACK_META_REL: string;
|
|
2
1
|
export declare function gitTopLevel(cwd?: string): string;
|
|
3
|
-
export declare function gitCommonDir(cwd?: string): string;
|
|
4
2
|
/**
|
|
5
3
|
* Each worktree has its own authoring state.
|
|
6
4
|
* Independent clones remain isolated even when they point at the same remote.
|
|
@@ -9,4 +7,3 @@ export declare function stableRepoId(cwd?: string): string;
|
|
|
9
7
|
export declare function cliStacksRoot(): string;
|
|
10
8
|
export declare function repoStateDir(cwd?: string, stacksRoot?: string): string;
|
|
11
9
|
export declare function stackMetaPath(cwd?: string, stacksRoot?: string): string;
|
|
12
|
-
export declare function legacyStackMetaPath(cwd?: string): string;
|
package/dist/repo-identity.js
CHANGED
|
@@ -4,7 +4,6 @@ import path from "node:path";
|
|
|
4
4
|
import { mergestormHome } from "./config.js";
|
|
5
5
|
import { CommandError } from "./errors.js";
|
|
6
6
|
import { git } from "./git.js";
|
|
7
|
-
export const LEGACY_STACK_META_REL = path.join(".mergestorm", "stack.json");
|
|
8
7
|
/** Classify git "not a repo" failures once at the boundary; callers match on code. */
|
|
9
8
|
function rethrowIfNotARepo(err) {
|
|
10
9
|
const detail = err instanceof Error ? err.message : String(err);
|
|
@@ -21,25 +20,6 @@ export function gitTopLevel(cwd = process.cwd()) {
|
|
|
21
20
|
rethrowIfNotARepo(err);
|
|
22
21
|
}
|
|
23
22
|
}
|
|
24
|
-
export function gitCommonDir(cwd = process.cwd()) {
|
|
25
|
-
let raw;
|
|
26
|
-
try {
|
|
27
|
-
raw = git(["rev-parse", "--git-common-dir"], cwd).trim();
|
|
28
|
-
}
|
|
29
|
-
catch (err) {
|
|
30
|
-
rethrowIfNotARepo(err);
|
|
31
|
-
}
|
|
32
|
-
const absolute = path.isAbsolute(raw) ? raw : path.resolve(cwd, raw);
|
|
33
|
-
try {
|
|
34
|
-
return realpathSync(absolute);
|
|
35
|
-
}
|
|
36
|
-
catch (err) {
|
|
37
|
-
if (err?.code === "ENOENT") {
|
|
38
|
-
throw new CommandError(`git common directory is missing (${absolute}); this is often a linked worktree whose main repository was deleted. Run \`mg stack reset --force\` to clear local pre-submit state.`, 1, "not_a_repo");
|
|
39
|
-
}
|
|
40
|
-
throw err;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
23
|
/**
|
|
44
24
|
* Each worktree has its own authoring state.
|
|
45
25
|
* Independent clones remain isolated even when they point at the same remote.
|
|
@@ -67,6 +47,3 @@ export function repoStateDir(cwd = process.cwd(), stacksRoot = cliStacksRoot())
|
|
|
67
47
|
export function stackMetaPath(cwd = process.cwd(), stacksRoot = cliStacksRoot()) {
|
|
68
48
|
return path.join(repoStateDir(cwd, stacksRoot), "stack.json");
|
|
69
49
|
}
|
|
70
|
-
export function legacyStackMetaPath(cwd = process.cwd()) {
|
|
71
|
-
return path.join(gitTopLevel(cwd), LEGACY_STACK_META_REL);
|
|
72
|
-
}
|
package/dist/server-publish.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getStackPublication, submitStackPublication, type StackPublication, type StackPublicationChange, type StackPublicationCommit, type StackPublicationIdentity, type StackPublicationStatus } from "./api.js";
|
|
2
2
|
import { loadConfig, type Config } from "./config.js";
|
|
3
|
+
import { discoverTrunk } from "./git-stack.js";
|
|
3
4
|
export declare const SERVER_PUBLISH_MAX_COMMITS = 20;
|
|
4
5
|
export declare const SERVER_PUBLISH_MAX_CHANGES = 500;
|
|
5
6
|
export declare const SERVER_PUBLISH_MAX_FILE_BYTES: number;
|
|
@@ -39,6 +40,7 @@ export type ServerPublishDeps = {
|
|
|
39
40
|
read?: GitRead;
|
|
40
41
|
getStackPublication?: typeof getStackPublication;
|
|
41
42
|
submitStackPublication?: typeof submitStackPublication;
|
|
43
|
+
assertBase?: (base: string) => void;
|
|
42
44
|
};
|
|
43
45
|
export type ServerPublishResult = StackPublication & {
|
|
44
46
|
baseBranch: string;
|
|
@@ -54,17 +56,28 @@ export type WorkingBranchInput = {
|
|
|
54
56
|
body?: string;
|
|
55
57
|
autoLand?: boolean;
|
|
56
58
|
replaceHead?: string;
|
|
59
|
+
extend?: boolean;
|
|
57
60
|
cfg?: Config;
|
|
58
61
|
};
|
|
59
62
|
export type WorkingBranchDeps = ServerPublishDeps & {
|
|
60
63
|
publishBranchServerSide?: typeof publishBranchServerSide;
|
|
61
64
|
loadConfig?: typeof loadConfig;
|
|
65
|
+
discoverTrunk?: typeof discoverTrunk;
|
|
62
66
|
};
|
|
67
|
+
export declare function assertExplicitStacking(input: {
|
|
68
|
+
parent: string;
|
|
69
|
+
trunk: string;
|
|
70
|
+
extend: boolean;
|
|
71
|
+
stackCommand: string;
|
|
72
|
+
trunkCommand: string;
|
|
73
|
+
}): void;
|
|
63
74
|
export declare function workingBranchTarget(input: WorkingBranchInput): {
|
|
64
75
|
cwd: string;
|
|
65
76
|
owner: string;
|
|
66
77
|
repo: string;
|
|
67
78
|
branch: string;
|
|
68
79
|
};
|
|
69
|
-
export declare function publishWorkingBranch(input: WorkingBranchInput, deps?: WorkingBranchDeps): Promise<ServerPublishResult
|
|
80
|
+
export declare function publishWorkingBranch(input: WorkingBranchInput, deps?: WorkingBranchDeps): Promise<ServerPublishResult & {
|
|
81
|
+
stacked: boolean;
|
|
82
|
+
}>;
|
|
70
83
|
export declare function workingBranchPublication(input: WorkingBranchInput, deps?: Pick<WorkingBranchDeps, "getStackPublication" | "loadConfig">): Promise<StackPublicationStatus>;
|
package/dist/server-publish.js
CHANGED
|
@@ -180,6 +180,7 @@ export async function publishBranchServerSide(input, deps = {}) {
|
|
|
180
180
|
throw new CommandError(`Branch missing locally: ${branch}`);
|
|
181
181
|
const remote = await status(cfg, { owner, repo, branch });
|
|
182
182
|
const baseBranch = remote.pr?.base ?? input.baseBranch;
|
|
183
|
+
deps.assertBase?.(baseBranch);
|
|
183
184
|
const localBase = input.localBaseRef ??
|
|
184
185
|
(resolves(read, `refs/remotes/origin/${input.baseBranch}`, cwd) ? `refs/remotes/origin/${input.baseBranch}` : input.baseBranch);
|
|
185
186
|
if (!resolves(read, localBase, cwd))
|
|
@@ -229,6 +230,13 @@ export async function publishBranchServerSide(input, deps = {}) {
|
|
|
229
230
|
}
|
|
230
231
|
return { ...publication, baseBranch };
|
|
231
232
|
}
|
|
233
|
+
export function assertExplicitStacking(input) {
|
|
234
|
+
if (input.extend || input.parent === input.trunk)
|
|
235
|
+
return;
|
|
236
|
+
throw new CommandError(`\`${input.parent}\` is not trunk (\`${input.trunk}\`), and stacking onto it needs --extend.\n` +
|
|
237
|
+
`• Stack onto \`${input.parent}\`: ${input.stackCommand}\n` +
|
|
238
|
+
`• New stack from \`${input.trunk}\`: ${input.trunkCommand}`, 1, "implicit_stack");
|
|
239
|
+
}
|
|
232
240
|
export function workingBranchTarget(input) {
|
|
233
241
|
const cwd = input.cwd ?? process.cwd();
|
|
234
242
|
const origin = input.owner && input.repo ? { owner: input.owner, repo: input.repo } : parseGithubOriginRepo(cwd);
|
|
@@ -242,17 +250,36 @@ export function workingBranchTarget(input) {
|
|
|
242
250
|
}
|
|
243
251
|
export async function publishWorkingBranch(input, deps = {}) {
|
|
244
252
|
const { cwd, owner, repo, branch } = workingBranchTarget(input);
|
|
245
|
-
|
|
253
|
+
const { publishBranchServerSide: publish = publishBranchServerSide, loadConfig: load = loadConfig, discoverTrunk: findTrunk = discoverTrunk, ...publishDeps } = deps;
|
|
254
|
+
let trunk = null;
|
|
255
|
+
let trunkError = null;
|
|
256
|
+
try {
|
|
257
|
+
trunk = findTrunk(cwd);
|
|
258
|
+
}
|
|
259
|
+
catch (err) {
|
|
260
|
+
trunkError = err;
|
|
261
|
+
}
|
|
262
|
+
const trunkMessage = trunkError instanceof Error ? trunkError.message : String(trunkError);
|
|
263
|
+
const baseBranch = input.base ?? trunk;
|
|
246
264
|
if (!baseBranch) {
|
|
247
|
-
|
|
248
|
-
|
|
265
|
+
throw new CommandError(`${trunkMessage} Name the base branch.`);
|
|
266
|
+
}
|
|
267
|
+
const assertBase = (parent) => {
|
|
268
|
+
if (trunk) {
|
|
269
|
+
assertExplicitStacking({
|
|
270
|
+
parent,
|
|
271
|
+
trunk,
|
|
272
|
+
extend: input.extend === true,
|
|
273
|
+
stackCommand: `\`mg stack publish --base ${parent} --extend\` (MCP stack_publish: extend true)`,
|
|
274
|
+
trunkCommand: "`mg stack publish` with no --base (MCP stack_publish: no base)",
|
|
275
|
+
});
|
|
249
276
|
}
|
|
250
|
-
|
|
251
|
-
throw new CommandError(`${
|
|
277
|
+
else if (input.extend !== true) {
|
|
278
|
+
throw new CommandError(`${trunkMessage} \`${parent}\` cannot be verified as trunk, so stacking onto it needs --extend.`, 1, "implicit_stack");
|
|
252
279
|
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
|
|
280
|
+
};
|
|
281
|
+
assertBase(baseBranch);
|
|
282
|
+
const result = await publish({
|
|
256
283
|
cfg: input.cfg ?? (await load()),
|
|
257
284
|
cwd,
|
|
258
285
|
owner,
|
|
@@ -263,7 +290,8 @@ export async function publishWorkingBranch(input, deps = {}) {
|
|
|
263
290
|
body: input.body ?? buildPrBodyFromCommit(tipCommitMessage(branch, cwd)),
|
|
264
291
|
...(typeof input.autoLand === "boolean" ? { autoLand: input.autoLand } : {}),
|
|
265
292
|
...(input.replaceHead ? { replaceHead: input.replaceHead } : {}),
|
|
266
|
-
}, publishDeps);
|
|
293
|
+
}, { ...publishDeps, assertBase });
|
|
294
|
+
return { ...result, stacked: !trunk || result.baseBranch !== trunk };
|
|
267
295
|
}
|
|
268
296
|
export async function workingBranchPublication(input, deps = {}) {
|
|
269
297
|
const { owner, repo, branch } = workingBranchTarget(input);
|
package/dist/stack-blockers.d.ts
CHANGED
|
@@ -170,6 +170,8 @@ export declare const SEAM_REVIEW_STUCK = "Seam review stuck in reviewing, no rev
|
|
|
170
170
|
export declare const SEAM_REREVIEW_NOT_RUNNING = "Seam re-review pending, no review running";
|
|
171
171
|
export declare const SEAM_VERDICT_STALE = "Seam verdict is for an older head, no review running";
|
|
172
172
|
export declare const VORTEX_FINDINGS_NEED_PERSON = "Vortex findings need a person";
|
|
173
|
+
export declare const CYCLONE_NOT_RUN_ATTENTION_MS: number;
|
|
174
|
+
export declare const CYCLONE_NOT_RUN_REASON = "not_run";
|
|
173
175
|
export declare function cycloneStatusWords(layer: Pick<StackLayerDto, "cycloneStatus" | "cycloneHandoff" | "headSha">): string | null;
|
|
174
176
|
export declare function autoResolveActive(stack: StackDto, layer: StackLayerDto): boolean;
|
|
175
177
|
export declare function autoFixCiActive(stack: StackDto, layer: StackLayerDto): boolean;
|
|
@@ -190,7 +192,6 @@ export declare const AUTO_LAND_CI_FAILED_BLOCKER = "CI failed \u2014 Auto land r
|
|
|
190
192
|
export declare const AUTO_LAND_WAIT_ATTENTION_MS: number;
|
|
191
193
|
export declare const AUTO_LAND_CI_UNREADABLE_ATTENTION_MS: number;
|
|
192
194
|
export declare function autoLandWaitLayer(stack: StackDto): StackLayerDto | undefined;
|
|
193
|
-
export declare function autoLandWaitBlocker(stack: StackDto, layer: StackLayerDto, nowMs: number): string | null;
|
|
194
195
|
export declare const MERGE_QUEUE_STALL_ATTENTION_MS: number;
|
|
195
196
|
export type StackQueueWait = {
|
|
196
197
|
prNumber: number;
|
|
@@ -217,26 +218,12 @@ export declare function restackFailedRepairSteps(branch: string, liveParent: str
|
|
|
217
218
|
detail?: string | null;
|
|
218
219
|
}): string;
|
|
219
220
|
export declare function agentsCannotClear(reason: StackBlockerReason, stack?: StackDto, layer?: StackLayerDto): boolean;
|
|
220
|
-
export declare function stackRepair(stack: StackDto, attention: ClassifiedStackBlocker | null, layer: StackLayerDto | undefined, bounce: MergeQueueEntryDto | undefined, queueWait?: MergeQueueWait | null): StackRepairHint | null;
|
|
221
|
+
export declare function stackRepair(stack: StackDto, attention: ClassifiedStackBlocker | null, layer: StackLayerDto | undefined, bounce: MergeQueueEntryDto | undefined, queueWait?: MergeQueueWait | null, nowMs?: number): StackRepairHint | null;
|
|
221
222
|
export declare const LAND_PR_MISSING_PREFIX = "No open land PR for ";
|
|
222
223
|
/** Pair-gate attention plus live hard blocks on every other open layer. */
|
|
223
224
|
export declare function stackBlockers(stack: StackDto, entries?: MergeQueueEntryDto[], cursor?: Pick<StackWatchCursor, "bounceId" | "afterFinishedAt">, nowMs?: number): {
|
|
224
|
-
attention: null;
|
|
225
|
-
held: StackHeldBlocker;
|
|
226
|
-
issues: StackBlocker[];
|
|
227
|
-
currentCandidate: {
|
|
228
|
-
prNumber: number;
|
|
229
|
-
headSha: string | null;
|
|
230
|
-
} | null;
|
|
231
|
-
bounce: undefined;
|
|
232
|
-
busy: StackAgentBusy[];
|
|
233
|
-
agents: StackLayerAgents | null;
|
|
234
|
-
repair: StackRepairHint | null;
|
|
235
|
-
landGatePending: StackLandGatePending | null;
|
|
236
|
-
queueWait: StackQueueWait | null;
|
|
237
|
-
} | {
|
|
238
225
|
attention: StackBlocker | null;
|
|
239
|
-
held: null;
|
|
226
|
+
held: StackHeldBlocker | null;
|
|
240
227
|
issues: StackBlocker[];
|
|
241
228
|
currentCandidate: {
|
|
242
229
|
prNumber: number;
|