mergestorm 0.3.62 → 0.3.64
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 +12 -6
- package/dist/api.d.ts +21 -3
- package/dist/api.js +24 -2
- package/dist/automation-catalog.d.ts +13 -3
- package/dist/automation-catalog.js +3 -1
- package/dist/client/index.d.ts +4 -3
- package/dist/client/index.js +4 -3
- package/dist/commands/browse.d.ts +1 -1
- package/dist/commands/dismiss.js +4 -1
- package/dist/commands/memory.d.ts +30 -0
- package/dist/commands/memory.js +152 -0
- package/dist/commands/registry.js +18 -1
- package/dist/commands/settings.d.ts +2 -1
- package/dist/commands/settings.js +49 -4
- package/dist/commands/stack.d.ts +10 -1
- package/dist/commands/stack.js +92 -27
- package/dist/config.d.ts +2 -0
- package/dist/config.js +11 -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/memory-api.d.ts +73 -0
- package/dist/memory-api.js +69 -0
- package/dist/server-publish.d.ts +21 -6
- package/dist/server-publish.js +77 -42
- package/dist/stack-blockers.d.ts +9 -2
- package/dist/stack-blockers.js +55 -16
- package/dist/stack-dto.d.ts +3 -0
- package/dist/stack-watch-loop.js +23 -2
- package/dist/stack-watch.d.ts +2 -1
- package/dist/stack-watch.js +6 -2
- package/dist/ui/banner.js +6 -0
- package/package.json +11 -2
- package/skill/SKILL.md +1 -1
- package/skill/mergestorm-bounce-watch/SKILL.md +5 -3
- package/skill/mergestorm-pr-loop/SKILL.md +7 -3
package/dist/commands/stack.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { adoptStack, ensureUpperPark, findStackPull, getEnrichedStack, getStackP
|
|
|
4
4
|
import { loadConfig } from "../config.js";
|
|
5
5
|
import { createPrs, findOpenPrNumber } from "../gh.js";
|
|
6
6
|
import { parseGithubOriginRepo } from "../git.js";
|
|
7
|
-
import { branchExists, commitsAheadOf, createBranchFromHead, currentBranch, defaultLayerBranchName, deleteBranch, discoverTrunk, fetchRemoteBranch, pushBranch, tipCommitMessage, tipCommitSubject, worktreeDirty } from "../git-stack.js";
|
|
7
|
+
import { branchExists, commitsAheadOf, createBranchFrom, createBranchFromHead, trunkStartRef, currentBranch, defaultLayerBranchName, deleteBranch, discoverTrunk, fetchRemoteBranch, pushBranch, tipCommitMessage, tipCommitSubject, worktreeDirty } from "../git-stack.js";
|
|
8
8
|
import { buildPrBodyFromCommit } from "../pr-body.js";
|
|
9
9
|
import { publishBranchServerSide, type WorkingBranchDeps } from "../server-publish.js";
|
|
10
10
|
import { gitTopLevel } from "../repo-identity.js";
|
|
@@ -33,6 +33,11 @@ export declare function findRegisteredParent(stacks: StackDto[], parentBranch: s
|
|
|
33
33
|
* Local unsubmitted parents are fine; only API-listed layers trip this.
|
|
34
34
|
*/
|
|
35
35
|
export declare function assertMayParentOntoRegistered(parentBranch: string, stacks: StackDto[], extend: boolean, verb: "create" | "submit", owner?: string, repo?: string): RegisteredParentHit | null;
|
|
36
|
+
export declare function stackShapeLine(input: {
|
|
37
|
+
layers: number;
|
|
38
|
+
base: string;
|
|
39
|
+
extendedStack: boolean;
|
|
40
|
+
}): string;
|
|
36
41
|
export declare function requireStackId(raw: string | undefined, usage: string): string;
|
|
37
42
|
/** Parse `owner/repo#12` or `owner/repo 12`. */
|
|
38
43
|
export declare function parseAdoptTarget(args: string[]): {
|
|
@@ -110,6 +115,8 @@ export type StackCreateDeps = {
|
|
|
110
115
|
currentBranch?: typeof currentBranch;
|
|
111
116
|
branchExists?: typeof branchExists;
|
|
112
117
|
createBranchFromHead?: typeof createBranchFromHead;
|
|
118
|
+
createBranchFrom?: typeof createBranchFrom;
|
|
119
|
+
trunkStartRef?: typeof trunkStartRef;
|
|
113
120
|
deleteBranch?: typeof deleteBranch;
|
|
114
121
|
discoverTrunk?: typeof discoverTrunk;
|
|
115
122
|
defaultLayerBranchName?: typeof defaultLayerBranchName;
|
|
@@ -156,6 +163,7 @@ export declare function parseStackPublishArgs(args: string[]): {
|
|
|
156
163
|
title?: string;
|
|
157
164
|
replaceHead?: string;
|
|
158
165
|
autoLand?: boolean;
|
|
166
|
+
extend: boolean;
|
|
159
167
|
status: boolean;
|
|
160
168
|
asJson: boolean;
|
|
161
169
|
};
|
|
@@ -172,6 +180,7 @@ export declare function cmdStackWatch(args: string[], deps?: {
|
|
|
172
180
|
loadConfig?: typeof loadConfig;
|
|
173
181
|
runStackWatch?: typeof runStackWatch;
|
|
174
182
|
signal?: AbortSignal;
|
|
183
|
+
writeMarker?: (line: string) => void;
|
|
175
184
|
}): Promise<void>;
|
|
176
185
|
export declare function cmdStackStatus(args: string[], deps?: {
|
|
177
186
|
loadConfig?: typeof loadConfig;
|
package/dist/commands/stack.js
CHANGED
|
@@ -5,13 +5,13 @@ 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";
|
|
14
|
-
import { runStackWatch, STACK_WATCH_EXIT } from "../stack-watch-loop.js";
|
|
14
|
+
import { runStackWatch, STACK_WATCH_EXIT, stackWatchFailedLine } from "../stack-watch-loop.js";
|
|
15
15
|
import { isMgParkBranch, planSubmitLayerBases } from "../submit-pr-base.js";
|
|
16
16
|
import { ansi } from "../ui/ansi.js";
|
|
17
17
|
import { runLineTabsBrowser } from "../ui/line-tabs.js";
|
|
@@ -52,7 +52,7 @@ 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]
|
|
@@ -62,7 +62,8 @@ const STACK_USAGE = `usage:
|
|
|
62
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
|
-
error twice, or 15 minutes
|
|
65
|
+
error twice, or 15 minutes with no stack read: timeouts, 5xx and rate limits, which it retries with backoff).
|
|
66
|
+
It also prints MS-WATCH ATTENTION failed when it cannot start (bad flags, no config), so it never exits without an MS-WATCH line unless you stop it; prints MS-WATCH LANDED
|
|
66
67
|
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
68
|
An ATTENTION line for a merge queue bounce ends with restart="mg stack watch <id> --bounce-id <id> --after-finished-at <iso>":
|
|
68
69
|
restart with that command (plus --head <sha> after a push) so a bounce you already handled is not reported again.
|
|
@@ -95,10 +96,13 @@ const STACK_USAGE = `usage:
|
|
|
95
96
|
you name that head with --replace-head. stack publish --status reads how far
|
|
96
97
|
a branch got (absent, pushed, opened, registered) without changing anything;
|
|
97
98
|
after a lost response or a failure, run the same command again to resume.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
stack create starts a new single PR from trunk, even on another PR's
|
|
100
|
+
branch. --extend stacks it on the checked-out branch (or --onto <branch>)
|
|
101
|
+
instead; --onto or publish --base a branch other than trunk without
|
|
102
|
+
--extend is refused. A layer on a registered stack also needs --extend on
|
|
103
|
+
submit. create, submit and publish say which one they made. stack land
|
|
104
|
+
promotes into the review unit when one exists; otherwise lands the bottom
|
|
105
|
+
open PR (unit-less stacks).`;
|
|
102
106
|
/** Cap the registered-stack guard's API probe so it cannot stall local authoring. */
|
|
103
107
|
const STACK_GUARD_TIMEOUT_MS = 8_000;
|
|
104
108
|
async function listRegisteredStacksRetryingOnce(list, cfg) {
|
|
@@ -152,6 +156,13 @@ export function assertMayParentOntoRegistered(parentBranch, stacks, extend, verb
|
|
|
152
156
|
`• New independent stack: check out trunk and pass \`--onto <trunk>\` (e.g. \`mg stack create --onto main\`).\n` +
|
|
153
157
|
`• Intentionally grow that stack: pass \`--extend\` on \`stack ${verb}\`.`, 1, "registered_parent");
|
|
154
158
|
}
|
|
159
|
+
export function stackShapeLine(input) {
|
|
160
|
+
if (input.extendedStack)
|
|
161
|
+
return ` Added ${input.layers === 1 ? "1 layer" : `${input.layers} layers`} to an existing stack (--extend).`;
|
|
162
|
+
if (input.layers === 1)
|
|
163
|
+
return ` A new single PR onto ${input.base}.`;
|
|
164
|
+
return ` A new stack of ${input.layers} layers onto ${input.base}.`;
|
|
165
|
+
}
|
|
155
166
|
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;
|
|
156
167
|
export function requireStackId(raw, usage) {
|
|
157
168
|
const id = raw?.trim() ?? "";
|
|
@@ -525,6 +536,8 @@ export async function cmdStackCreate(args, deps = {}) {
|
|
|
525
536
|
const currentBranchFn = deps.currentBranch ?? currentBranch;
|
|
526
537
|
const branchExistsFn = deps.branchExists ?? branchExists;
|
|
527
538
|
const createBranchFromHeadFn = deps.createBranchFromHead ?? createBranchFromHead;
|
|
539
|
+
const createBranchFromFn = deps.createBranchFrom ?? createBranchFrom;
|
|
540
|
+
const trunkStartRefFn = deps.trunkStartRef ?? trunkStartRef;
|
|
528
541
|
const deleteBranchFn = deps.deleteBranch ?? deleteBranch;
|
|
529
542
|
const discoverTrunkFn = deps.discoverTrunk ?? discoverTrunk;
|
|
530
543
|
const defaultLayerBranchNameFn = deps.defaultLayerBranchName ?? defaultLayerBranchName;
|
|
@@ -542,17 +555,6 @@ export async function cmdStackCreate(args, deps = {}) {
|
|
|
542
555
|
if (worktreeDirtyFn(cwd)) {
|
|
543
556
|
throw new CommandError("Working tree is dirty. Commit or stash changes before `stack create`.");
|
|
544
557
|
}
|
|
545
|
-
const parentBranch = onto?.trim() || currentBranchFn(cwd);
|
|
546
|
-
if (!parentBranch) {
|
|
547
|
-
throw new CommandError("Detached HEAD. Check out a branch (or pass --onto <branch>) before `stack create`.");
|
|
548
|
-
}
|
|
549
|
-
if (!branchExistsFn(parentBranch, cwd)) {
|
|
550
|
-
throw new CommandError(`Parent branch not found: ${parentBranch}`);
|
|
551
|
-
}
|
|
552
|
-
const branch = defaultLayerBranchNameFn(name);
|
|
553
|
-
if (branchExistsFn(branch, cwd)) {
|
|
554
|
-
throw new CommandError(`Branch already exists: ${branch}`);
|
|
555
|
-
}
|
|
556
558
|
let meta = await loadStackMetaFn(cwd);
|
|
557
559
|
const trunk = trunkFlag?.trim() || meta?.trunk || discoverTrunkFn(cwd);
|
|
558
560
|
if (!meta) {
|
|
@@ -561,8 +563,34 @@ export async function cmdStackCreate(args, deps = {}) {
|
|
|
561
563
|
else if (trunkFlag?.trim() && meta.trunk !== trunk) {
|
|
562
564
|
throw new CommandError(`Stack trunk is ${meta.trunk}; refusing --trunk ${trunk}. Run \`mg stack reset --force\` to discard local pre-submit state.`);
|
|
563
565
|
}
|
|
564
|
-
|
|
565
|
-
|
|
566
|
+
const checkedOut = currentBranchFn(cwd);
|
|
567
|
+
if (extend && !onto?.trim() && !checkedOut) {
|
|
568
|
+
throw new CommandError("Detached HEAD. Check out a branch (or pass --onto <branch>) to stack with --extend.");
|
|
569
|
+
}
|
|
570
|
+
const parentBranch = onto?.trim() || (extend && checkedOut ? checkedOut : trunk);
|
|
571
|
+
assertExplicitStacking({
|
|
572
|
+
parent: parentBranch,
|
|
573
|
+
trunk,
|
|
574
|
+
extend,
|
|
575
|
+
stackCommand: `\`mg stack create ${name ?? "<name>"} --onto ${parentBranch} --extend\``,
|
|
576
|
+
trunkCommand: `\`mg stack create ${name ?? "<name>"}\``,
|
|
577
|
+
});
|
|
578
|
+
let startRef = null;
|
|
579
|
+
if (parentBranch === trunk && checkedOut !== trunk) {
|
|
580
|
+
startRef = trunkStartRefFn(trunk, cwd);
|
|
581
|
+
if (!startRef)
|
|
582
|
+
throw new CommandError(`Trunk branch not found: ${trunk}`);
|
|
583
|
+
}
|
|
584
|
+
else if (!branchExistsFn(parentBranch, cwd)) {
|
|
585
|
+
throw new CommandError(`Parent branch not found: ${parentBranch}`);
|
|
586
|
+
}
|
|
587
|
+
else if (parentBranch !== checkedOut) {
|
|
588
|
+
startRef = parentBranch;
|
|
589
|
+
}
|
|
590
|
+
const branch = defaultLayerBranchNameFn(name);
|
|
591
|
+
if (branchExistsFn(branch, cwd)) {
|
|
592
|
+
throw new CommandError(`Branch already exists: ${branch}`);
|
|
593
|
+
}
|
|
566
594
|
if (trunkFlag?.trim() && !onto?.trim() && parentBranch !== trunk) {
|
|
567
595
|
console.error(ansi.dim(` note: --trunk ${trunk} does not change the parent; new layer parents onto \`${parentBranch}\`. ` +
|
|
568
596
|
`Pass \`--onto ${trunk}\` for a fresh stack from trunk.`));
|
|
@@ -606,7 +634,10 @@ export async function cmdStackCreate(args, deps = {}) {
|
|
|
606
634
|
throw new CommandError(err instanceof Error ? err.message : String(err));
|
|
607
635
|
}
|
|
608
636
|
try {
|
|
609
|
-
|
|
637
|
+
if (startRef)
|
|
638
|
+
createBranchFromFn(branch, startRef, cwd);
|
|
639
|
+
else
|
|
640
|
+
createBranchFromHeadFn(branch, cwd);
|
|
610
641
|
}
|
|
611
642
|
catch (err) {
|
|
612
643
|
throw new CommandError(err instanceof Error ? err.message : String(err));
|
|
@@ -637,7 +668,9 @@ export async function cmdStackCreate(args, deps = {}) {
|
|
|
637
668
|
return;
|
|
638
669
|
}
|
|
639
670
|
const created = [
|
|
640
|
-
ansi.brightGreen(
|
|
671
|
+
ansi.brightGreen(parentBranch === meta.trunk
|
|
672
|
+
? ` Created ${branch}: a new single PR onto ${parentBranch}${startRef ? ` (branched from ${startRef})` : ""}`
|
|
673
|
+
: ` Created ${branch}: a layer stacked on ${parentBranch} (--extend)`),
|
|
641
674
|
];
|
|
642
675
|
if (registeredHit) {
|
|
643
676
|
created.push(ansi.dim(` extending registered stack ${registeredHit.stackId}` +
|
|
@@ -847,6 +880,7 @@ export async function cmdStackSubmit(args, deps = {}) {
|
|
|
847
880
|
return;
|
|
848
881
|
}
|
|
849
882
|
const lines = published.map((row) => ansi.brightGreen(` #${row.prNumber} ${row.branch} → ${row.base} (${row.created ? "opened" : "exists"}, head ${row.head.slice(0, 12)})`));
|
|
883
|
+
lines.push(ansi.brightGreen(stackShapeLine({ layers: layers.length, base: plans[0]?.githubBase ?? meta.trunk, extendedStack: registeredHits > 0 })));
|
|
850
884
|
if (publishedStackId)
|
|
851
885
|
lines.push(ansi.brightGreen(` Registered stack ${publishedStackId}`));
|
|
852
886
|
lines.push(ansi.dim(" Dashboard: https://mergestorm.ai/dashboard"));
|
|
@@ -987,6 +1021,7 @@ export async function cmdStackSubmit(args, deps = {}) {
|
|
|
987
1021
|
const verb = row.created ? "opened" : "exists";
|
|
988
1022
|
summary.push(ansi.brightGreen(` #${row.prNumber} ${row.branch} → ${row.base} (${verb})`));
|
|
989
1023
|
}
|
|
1024
|
+
summary.push(ansi.brightGreen(stackShapeLine({ layers: layers.length, base: plans[0]?.githubBase ?? meta.trunk, extendedStack: registeredHits > 0 })));
|
|
990
1025
|
const adoptedTrunk = data.stack?.trunkBranch;
|
|
991
1026
|
if (adoptedTrunk?.startsWith("mg-stack-")) {
|
|
992
1027
|
summary.push(ansi.dim(` Bottom base is now ${adoptedTrunk} (Mergestorm-owned trunk). Leave it.`));
|
|
@@ -1003,8 +1038,8 @@ export async function cmdStackSubmit(args, deps = {}) {
|
|
|
1003
1038
|
await present("Stack", summary);
|
|
1004
1039
|
}
|
|
1005
1040
|
export function parseStackPublishArgs(args) {
|
|
1006
|
-
const usage = "usage: mergestorm stack publish [--branch <branch>] [--base <branch>] [--title <title>] [--replace-head <sha>] [--auto-land on|off] [--status] [--json]";
|
|
1007
|
-
const out = { status: false, asJson: false };
|
|
1041
|
+
const usage = "usage: mergestorm stack publish [--branch <branch>] [--base <branch>] [--extend] [--title <title>] [--replace-head <sha>] [--auto-land on|off] [--status] [--json]";
|
|
1042
|
+
const out = { extend: false, status: false, asJson: false };
|
|
1008
1043
|
const valued = {
|
|
1009
1044
|
"--branch": "branch",
|
|
1010
1045
|
"--base": "base",
|
|
@@ -1021,6 +1056,10 @@ export function parseStackPublishArgs(args) {
|
|
|
1021
1056
|
out.status = true;
|
|
1022
1057
|
continue;
|
|
1023
1058
|
}
|
|
1059
|
+
if (a === "--extend") {
|
|
1060
|
+
out.extend = true;
|
|
1061
|
+
continue;
|
|
1062
|
+
}
|
|
1024
1063
|
if (a === "--auto-land") {
|
|
1025
1064
|
out.autoLand = parseOnOff(args[++i], "--auto-land", "stack publish");
|
|
1026
1065
|
continue;
|
|
@@ -1066,6 +1105,7 @@ export async function cmdStackPublish(args, deps = {}) {
|
|
|
1066
1105
|
...(parsed.title ? { title: parsed.title } : {}),
|
|
1067
1106
|
...(typeof parsed.autoLand === "boolean" ? { autoLand: parsed.autoLand } : {}),
|
|
1068
1107
|
...(parsed.replaceHead ? { replaceHead: parsed.replaceHead } : {}),
|
|
1108
|
+
extend: parsed.extend,
|
|
1069
1109
|
}, publishDeps);
|
|
1070
1110
|
const watch = openStackWatch(result.stack.id);
|
|
1071
1111
|
if (parsed.asJson) {
|
|
@@ -1076,6 +1116,7 @@ export async function cmdStackPublish(args, deps = {}) {
|
|
|
1076
1116
|
await present("Stack", [
|
|
1077
1117
|
ansi.brightGreen(` ${result.branch} ${moved} at ${result.head.sha.slice(0, 12)} (tree ${result.head.tree.slice(0, 12)})`),
|
|
1078
1118
|
ansi.brightGreen(` #${result.pr.number} ${result.branch} → ${result.baseBranch} (${result.pr.created ? "opened" : "exists"}) ${result.pr.url}`),
|
|
1119
|
+
ansi.brightGreen(result.stacked ? ` A layer stacked on ${result.baseBranch}.` : ` A single PR onto ${result.baseBranch}.`),
|
|
1079
1120
|
ansi.brightGreen(` Registered stack ${result.stack.id}`),
|
|
1080
1121
|
...stackWatchTextLines(watch).map((line) => ` ${line}`),
|
|
1081
1122
|
]);
|
|
@@ -1173,7 +1214,7 @@ export async function cmdStackWait(args, deps = {}) {
|
|
|
1173
1214
|
throw new CommandError(err.message, REVIEW_EXIT.timeout, "review_timeout");
|
|
1174
1215
|
}
|
|
1175
1216
|
}
|
|
1176
|
-
|
|
1217
|
+
async function startStackWatch(args, deps) {
|
|
1177
1218
|
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]";
|
|
1178
1219
|
const fail = (detail) => new CommandError(detail ? `${usage}\n${detail}` : usage, REVIEW_EXIT.usage, "usage");
|
|
1179
1220
|
const positional = [];
|
|
@@ -1246,6 +1287,30 @@ export async function cmdStackWatch(args, deps = {}) {
|
|
|
1246
1287
|
...(afterFinishedAt ? { afterFinishedAt } : {}),
|
|
1247
1288
|
} } : {}),
|
|
1248
1289
|
});
|
|
1290
|
+
return { stackId, result };
|
|
1291
|
+
}
|
|
1292
|
+
export async function cmdStackWatch(args, deps = {}) {
|
|
1293
|
+
let started;
|
|
1294
|
+
try {
|
|
1295
|
+
started = await startStackWatch(args, deps);
|
|
1296
|
+
}
|
|
1297
|
+
catch (err) {
|
|
1298
|
+
const valueFlags = new Set(["--until", "--ignore", "--max", "--head", "--bounce-id", "--after-finished-at"]);
|
|
1299
|
+
let json = false;
|
|
1300
|
+
for (let i = 0; i < args.length; i++) {
|
|
1301
|
+
if (args[i] === "--json")
|
|
1302
|
+
json = true;
|
|
1303
|
+
else if (valueFlags.has(args[i]))
|
|
1304
|
+
i++;
|
|
1305
|
+
}
|
|
1306
|
+
const marker = deps.writeMarker ?? (json ? console.error : console.log);
|
|
1307
|
+
const stackId = args.find((arg, i) => UUID_RE.test(arg.trim()) && !valueFlags.has(args[i - 1]))?.trim() ?? "none";
|
|
1308
|
+
marker(stackWatchFailedLine(stackId, err instanceof Error ? err.message : String(err)));
|
|
1309
|
+
if (json)
|
|
1310
|
+
console.log(JSON.stringify({ stackId, outcome: "failed" }));
|
|
1311
|
+
throw err;
|
|
1312
|
+
}
|
|
1313
|
+
const { stackId, result } = started;
|
|
1249
1314
|
if (result.exitCode === STACK_WATCH_EXIT.landed)
|
|
1250
1315
|
return;
|
|
1251
1316
|
const reason = result.outcome === "timeout"
|
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;
|
|
@@ -9,6 +10,7 @@ export declare function loadConfig(): Promise<Config>;
|
|
|
9
10
|
export declare function saveConfig(cfg: Config): Promise<void>;
|
|
10
11
|
export declare function clearConfig(): Promise<void>;
|
|
11
12
|
export declare function apiBase(cfg: Config): string;
|
|
13
|
+
export declare function resolveChatId(cfg: Config): string | undefined;
|
|
12
14
|
export declare function resolveApiKey(cfg: Config): string | undefined;
|
|
13
15
|
/** Short display form of a stored key (prefix only). */
|
|
14
16
|
export declare function keyDisplay(key: string | undefined): string | null;
|
package/dist/config.js
CHANGED
|
@@ -30,9 +30,19 @@ 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(/\/$/, "");
|
|
35
|
+
}
|
|
36
|
+
const CHAT_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
37
|
+
export function resolveChatId(cfg) {
|
|
38
|
+
if (cfg.ignoreEnv)
|
|
39
|
+
return undefined;
|
|
40
|
+
const chatId = process.env.MERGESTORM_CHAT_ID?.trim() ?? "";
|
|
41
|
+
return CHAT_ID.test(chatId) ? chatId.toLowerCase() : undefined;
|
|
34
42
|
}
|
|
35
43
|
export function resolveApiKey(cfg) {
|
|
44
|
+
if (cfg.ignoreEnv)
|
|
45
|
+
return cfg.apiKey;
|
|
36
46
|
return process.env.MERGESTORM_API_KEY?.trim() || cfg.apiKey;
|
|
37
47
|
}
|
|
38
48
|
/** 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" | "git_failed" | "unknown_label" | "invalid_input" | "memory_off" | "memory_full" | "memory_unavailable";
|
|
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
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { type Config } from "./config.js";
|
|
2
|
+
export declare const MEMORY_MODES: readonly ["off", "on_request", "all"];
|
|
3
|
+
export declare const MEMORY_SCOPES: readonly ["user", "repo"];
|
|
4
|
+
export declare const MEMORY_KINDS: readonly ["rule", "style", "preference", "fact", "project"];
|
|
5
|
+
export declare const MEMORY_SEARCH_LIMIT_MAX = 20;
|
|
6
|
+
export type MemoryMode = (typeof MEMORY_MODES)[number];
|
|
7
|
+
export type MemoryScope = (typeof MEMORY_SCOPES)[number];
|
|
8
|
+
export type MemoryKind = (typeof MEMORY_KINDS)[number];
|
|
9
|
+
export type MemoryItem = {
|
|
10
|
+
id: string;
|
|
11
|
+
scope: MemoryScope;
|
|
12
|
+
repo: string | null;
|
|
13
|
+
kind: MemoryKind;
|
|
14
|
+
text: string;
|
|
15
|
+
source: string | null;
|
|
16
|
+
source_url: string | null;
|
|
17
|
+
created_at: string;
|
|
18
|
+
updated_at: string;
|
|
19
|
+
};
|
|
20
|
+
export type MemoryList = {
|
|
21
|
+
memory_mode: MemoryMode;
|
|
22
|
+
items: MemoryItem[];
|
|
23
|
+
};
|
|
24
|
+
export type MemorySearch = {
|
|
25
|
+
memory_mode: MemoryMode;
|
|
26
|
+
rules: MemoryItem[];
|
|
27
|
+
items: MemoryItem[];
|
|
28
|
+
total: number;
|
|
29
|
+
};
|
|
30
|
+
export type MemorySaved = {
|
|
31
|
+
id: string;
|
|
32
|
+
merged: boolean;
|
|
33
|
+
};
|
|
34
|
+
export type MemoryUpdated = {
|
|
35
|
+
id: string;
|
|
36
|
+
text: string;
|
|
37
|
+
kind?: MemoryKind;
|
|
38
|
+
};
|
|
39
|
+
export declare function memoryChatId(value?: string | null | undefined): string | null;
|
|
40
|
+
export declare function isMemoryMode(value: unknown): value is MemoryMode;
|
|
41
|
+
export declare function isMemoryKind(value: unknown): value is MemoryKind;
|
|
42
|
+
export declare function isMemoryScope(value: unknown): value is MemoryScope;
|
|
43
|
+
export declare function listMemories(filter?: {
|
|
44
|
+
scope?: MemoryScope;
|
|
45
|
+
repo?: string;
|
|
46
|
+
kind?: MemoryKind;
|
|
47
|
+
}, cfg?: Config): Promise<MemoryList>;
|
|
48
|
+
export declare function searchMemories(input: {
|
|
49
|
+
query: string;
|
|
50
|
+
repo?: string;
|
|
51
|
+
limit?: number;
|
|
52
|
+
chatId?: string | null;
|
|
53
|
+
}, cfg?: Config): Promise<MemorySearch>;
|
|
54
|
+
export declare function saveMemory(input: {
|
|
55
|
+
text: string;
|
|
56
|
+
kind: MemoryKind;
|
|
57
|
+
repo?: string | null;
|
|
58
|
+
chatId?: string | null;
|
|
59
|
+
}, cfg?: Config): Promise<MemorySaved>;
|
|
60
|
+
export declare function updateMemory(id: string, change: {
|
|
61
|
+
text: string;
|
|
62
|
+
kind?: MemoryKind;
|
|
63
|
+
}, cfg?: Config): Promise<MemoryUpdated>;
|
|
64
|
+
export declare function deleteMemory(id: string, cfg?: Config): Promise<{
|
|
65
|
+
id: string;
|
|
66
|
+
deleted: true;
|
|
67
|
+
}>;
|
|
68
|
+
export declare function getMemoryMode(cfg?: Config): Promise<{
|
|
69
|
+
memory_mode: MemoryMode;
|
|
70
|
+
}>;
|
|
71
|
+
export declare function setMemoryMode(mode: MemoryMode, cfg?: Config): Promise<{
|
|
72
|
+
memory_mode: MemoryMode;
|
|
73
|
+
}>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { apiFetch } from "./api.js";
|
|
2
|
+
import { loadConfig } from "./config.js";
|
|
3
|
+
import { CommandError, REVIEW_EXIT, rateLimitedMessage } from "./errors.js";
|
|
4
|
+
export const MEMORY_MODES = ["off", "on_request", "all"];
|
|
5
|
+
export const MEMORY_SCOPES = ["user", "repo"];
|
|
6
|
+
export const MEMORY_KINDS = ["rule", "style", "preference", "fact", "project"];
|
|
7
|
+
export const MEMORY_SEARCH_LIMIT_MAX = 20;
|
|
8
|
+
const CHAT_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
9
|
+
export function memoryChatId(value = process.env.MERGESTORM_CHAT_ID) {
|
|
10
|
+
const trimmed = value?.trim() ?? "";
|
|
11
|
+
return CHAT_ID.test(trimmed) ? trimmed.toLowerCase() : null;
|
|
12
|
+
}
|
|
13
|
+
export function isMemoryMode(value) {
|
|
14
|
+
return typeof value === "string" && MEMORY_MODES.includes(value);
|
|
15
|
+
}
|
|
16
|
+
export function isMemoryKind(value) {
|
|
17
|
+
return typeof value === "string" && MEMORY_KINDS.includes(value);
|
|
18
|
+
}
|
|
19
|
+
export function isMemoryScope(value) {
|
|
20
|
+
return typeof value === "string" && MEMORY_SCOPES.includes(value);
|
|
21
|
+
}
|
|
22
|
+
const KNOWN_CODES = new Set(["invalid_input", "not_found", "memory_off", "memory_full", "memory_unavailable", "payload_too_large"]);
|
|
23
|
+
function memoryFailure(status, body, retryAfterSeconds, action) {
|
|
24
|
+
const row = (body && typeof body === "object" ? body : {});
|
|
25
|
+
if (status === 429 || row.error === "rate_limited") {
|
|
26
|
+
return new CommandError(rateLimitedMessage(retryAfterSeconds), REVIEW_EXIT.rate_limited, "rate_limited", retryAfterSeconds !== undefined ? { retryAfterSeconds } : undefined);
|
|
27
|
+
}
|
|
28
|
+
const message = typeof row.message === "string" && row.message ? row.message : null;
|
|
29
|
+
const code = typeof row.error === "string" && KNOWN_CODES.has(row.error) ? row.error : undefined;
|
|
30
|
+
if (status === 404 && !code)
|
|
31
|
+
return new CommandError("This Mergestorm server does not manage memories yet.", 1, "not_found");
|
|
32
|
+
return new CommandError(message ?? `${action} (HTTP ${status})`, 1, code);
|
|
33
|
+
}
|
|
34
|
+
async function call(cfg, route, init, ok, action) {
|
|
35
|
+
const { status, body, retryAfterSeconds } = await apiFetch(cfg ?? (await loadConfig()), route, init);
|
|
36
|
+
if (!ok.includes(status))
|
|
37
|
+
throw memoryFailure(status, body, retryAfterSeconds, action);
|
|
38
|
+
return body;
|
|
39
|
+
}
|
|
40
|
+
function query(params) {
|
|
41
|
+
const search = new URLSearchParams();
|
|
42
|
+
for (const [key, value] of Object.entries(params))
|
|
43
|
+
if (value !== undefined && value !== null && value !== "")
|
|
44
|
+
search.set(key, String(value));
|
|
45
|
+
const text = search.toString();
|
|
46
|
+
return text ? `?${text}` : "";
|
|
47
|
+
}
|
|
48
|
+
export function listMemories(filter = {}, cfg) {
|
|
49
|
+
return call(cfg, `/api/v1/memories${query(filter)}`, {}, [200], "Could not list memories");
|
|
50
|
+
}
|
|
51
|
+
export function searchMemories(input, cfg) {
|
|
52
|
+
return call(cfg, `/api/v1/memories/search${query({ query: input.query, repo: input.repo, limit: input.limit, chat_id: input.chatId })}`, {}, [200], "Could not search memories");
|
|
53
|
+
}
|
|
54
|
+
export function saveMemory(input, cfg) {
|
|
55
|
+
const json = { text: input.text, kind: input.kind, ...(input.repo ? { repo: input.repo } : {}), ...(input.chatId ? { chat_id: input.chatId } : {}) };
|
|
56
|
+
return call(cfg, "/api/v1/memories", { method: "POST", json }, [200, 201], "Could not save the memory");
|
|
57
|
+
}
|
|
58
|
+
export function updateMemory(id, change, cfg) {
|
|
59
|
+
return call(cfg, `/api/v1/memories/${encodeURIComponent(id)}`, { method: "PATCH", json: change }, [200], "Could not update the memory");
|
|
60
|
+
}
|
|
61
|
+
export function deleteMemory(id, cfg) {
|
|
62
|
+
return call(cfg, `/api/v1/memories/${encodeURIComponent(id)}`, { method: "DELETE" }, [200], "Could not delete the memory");
|
|
63
|
+
}
|
|
64
|
+
export function getMemoryMode(cfg) {
|
|
65
|
+
return call(cfg, "/api/v1/memories/mode", {}, [200], "Could not read the memory mode");
|
|
66
|
+
}
|
|
67
|
+
export function setMemoryMode(mode, cfg) {
|
|
68
|
+
return call(cfg, "/api/v1/memories/mode", { method: "PUT", json: { memory_mode: mode } }, [200], "Could not set the memory mode");
|
|
69
|
+
}
|
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;
|
|
@@ -7,26 +8,28 @@ export declare const SERVER_PUBLISH_MAX_TOTAL_BYTES: number;
|
|
|
7
8
|
export declare const SERVER_PUBLISH_MAX_COMMIT_BYTES: number;
|
|
8
9
|
export declare const SERVER_PUBLISH_FILE_MODES: readonly ["100644", "100755", "120000"];
|
|
9
10
|
export declare const SERVER_PUBLISH_HISTORY_SCAN = 500;
|
|
10
|
-
export type GitRead = (args: string[], cwd: string) => Buffer
|
|
11
|
+
export type GitRead = (args: string[], cwd: string) => Buffer | Promise<Buffer>;
|
|
12
|
+
export declare const READ_ONLY_GIT: Set<string>;
|
|
11
13
|
export declare function readGit(args: string[], cwd: string): Buffer;
|
|
12
|
-
export declare function readCommitHeader(read: GitRead, sha: string, cwd: string): {
|
|
14
|
+
export declare function readCommitHeader(read: GitRead, sha: string, cwd: string): Promise<{
|
|
13
15
|
tree: string;
|
|
14
16
|
parents: string[];
|
|
15
17
|
author: StackPublicationIdentity;
|
|
16
18
|
committer: StackPublicationIdentity;
|
|
17
19
|
message: string;
|
|
18
|
-
}
|
|
20
|
+
}>;
|
|
19
21
|
export declare function readCommitChanges(read: GitRead, parent: string, sha: string, cwd: string, budget: {
|
|
20
22
|
changes: number;
|
|
21
23
|
bytes: number;
|
|
22
|
-
}): StackPublicationChange[]
|
|
23
|
-
export declare function readPublicationCommits(read: GitRead, from: string, to: string, cwd: string): StackPublicationCommit[]
|
|
24
|
+
}): Promise<StackPublicationChange[]>;
|
|
25
|
+
export declare function readPublicationCommits(read: GitRead, from: string, to: string, cwd: string): Promise<StackPublicationCommit[]>;
|
|
24
26
|
export type ServerPublishInput = {
|
|
25
27
|
cfg: Config;
|
|
26
28
|
cwd: string;
|
|
27
29
|
owner: string;
|
|
28
30
|
repo: string;
|
|
29
31
|
branch: string;
|
|
32
|
+
localRef?: string;
|
|
30
33
|
baseBranch: string;
|
|
31
34
|
localBaseRef?: string;
|
|
32
35
|
remoteBaseSha?: string;
|
|
@@ -39,6 +42,7 @@ export type ServerPublishDeps = {
|
|
|
39
42
|
read?: GitRead;
|
|
40
43
|
getStackPublication?: typeof getStackPublication;
|
|
41
44
|
submitStackPublication?: typeof submitStackPublication;
|
|
45
|
+
assertBase?: (base: string) => void;
|
|
42
46
|
};
|
|
43
47
|
export type ServerPublishResult = StackPublication & {
|
|
44
48
|
baseBranch: string;
|
|
@@ -54,17 +58,28 @@ export type WorkingBranchInput = {
|
|
|
54
58
|
body?: string;
|
|
55
59
|
autoLand?: boolean;
|
|
56
60
|
replaceHead?: string;
|
|
61
|
+
extend?: boolean;
|
|
57
62
|
cfg?: Config;
|
|
58
63
|
};
|
|
59
64
|
export type WorkingBranchDeps = ServerPublishDeps & {
|
|
60
65
|
publishBranchServerSide?: typeof publishBranchServerSide;
|
|
61
66
|
loadConfig?: typeof loadConfig;
|
|
67
|
+
discoverTrunk?: typeof discoverTrunk;
|
|
62
68
|
};
|
|
69
|
+
export declare function assertExplicitStacking(input: {
|
|
70
|
+
parent: string;
|
|
71
|
+
trunk: string;
|
|
72
|
+
extend: boolean;
|
|
73
|
+
stackCommand: string;
|
|
74
|
+
trunkCommand: string;
|
|
75
|
+
}): void;
|
|
63
76
|
export declare function workingBranchTarget(input: WorkingBranchInput): {
|
|
64
77
|
cwd: string;
|
|
65
78
|
owner: string;
|
|
66
79
|
repo: string;
|
|
67
80
|
branch: string;
|
|
68
81
|
};
|
|
69
|
-
export declare function publishWorkingBranch(input: WorkingBranchInput, deps?: WorkingBranchDeps): Promise<ServerPublishResult
|
|
82
|
+
export declare function publishWorkingBranch(input: WorkingBranchInput, deps?: WorkingBranchDeps): Promise<ServerPublishResult & {
|
|
83
|
+
stacked: boolean;
|
|
84
|
+
}>;
|
|
70
85
|
export declare function workingBranchPublication(input: WorkingBranchInput, deps?: Pick<WorkingBranchDeps, "getStackPublication" | "loadConfig">): Promise<StackPublicationStatus>;
|