mergestorm 0.3.30 → 0.3.31
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 +2 -2
- package/dist/api.d.ts +8 -0
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +1 -1
- package/dist/commands/browse.js +1 -0
- package/dist/commands/credits.js +1 -0
- package/dist/commands/dismiss.js +1 -1
- package/dist/commands/stack.js +5 -2
- package/dist/stack-watch-obligation.d.ts +3 -0
- package/dist/stack-watch-obligation.js +8 -2
- package/dist/ui/banner.js +3 -0
- package/dist/ui/usage.d.ts +7 -0
- package/dist/ui/usage.js +11 -0
- package/package.json +1 -1
- package/skill/mergestorm-bounce-watch/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -45,7 +45,7 @@ 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.31 │
|
|
49
49
|
│ ▀▀▀▀▀▀▀▀▀ ● msk_live_… · maelstrom │
|
|
50
50
|
│ ▀▀▀▀▀▀ [████████░░░░░░░░░░░░░░░░░░░░░░] 25% used │
|
|
51
51
|
│ ▝▀▀▀▀▀ local reviews + stacked PRs · mergestorm.ai │
|
|
@@ -129,7 +129,7 @@ Use `mergestorm dismiss` when a Vortex finding is wrong and you have checked it
|
|
|
129
129
|
- Retrying the same dismissal is safe; it reports the earlier record.
|
|
130
130
|
- The seam gate clears only when every finding of that integration review is dismissed and the review is still the current seam verdict. CI, other reviews and Auto land policy still apply.
|
|
131
131
|
- Vortex does not raise a dismissed finding again on the same diff (the same head, or a restack with an unchanged diff).
|
|
132
|
-
- With `--json`, the result carries `gate` and, for a stack PR, `watch.next` (the next `stack_wait` call).
|
|
132
|
+
- With `--json`, the result carries `gate` and, for a stack PR, `watch.next` (the next `stack_wait` call, plus `watch.next.background`, the `mg stack watch` command to run in the background instead).
|
|
133
133
|
|
|
134
134
|
`mg` is a short alias for `mergestorm` (same binary), e.g. `mg stack create`. Happy path: **create → commit → submit → restack → land**. On review-unit stacks, `stack land` **promotes** the tip into the unit (same gates as the dashboard); otherwise it lands the bottom open PR. `stack adopt` is for importing a chain that already exists on GitHub (legacy / Graphite). Restack/land use the same login key (`/api/v1/stacks`). Unattended landing is `mg queue add`.
|
|
135
135
|
|
package/dist/api.d.ts
CHANGED
|
@@ -29,6 +29,13 @@ export declare function devicePost(base: string, route: string, json: unknown, o
|
|
|
29
29
|
status: number;
|
|
30
30
|
body: any;
|
|
31
31
|
}>;
|
|
32
|
+
export type SurgeUsage = {
|
|
33
|
+
includedMinutes: number;
|
|
34
|
+
usedMinutes: number;
|
|
35
|
+
walletMinutes: number;
|
|
36
|
+
windowStart: string;
|
|
37
|
+
pricePerMinuteUsd: number;
|
|
38
|
+
};
|
|
32
39
|
export type MeResponse = {
|
|
33
40
|
key: {
|
|
34
41
|
prefix: string;
|
|
@@ -50,6 +57,7 @@ export type MeResponse = {
|
|
|
50
57
|
bonus?: {
|
|
51
58
|
remaining: number;
|
|
52
59
|
};
|
|
60
|
+
surge?: SurgeUsage;
|
|
53
61
|
};
|
|
54
62
|
};
|
|
55
63
|
export type JobListItem = {
|
package/dist/client/index.d.ts
CHANGED
|
@@ -8,4 +8,4 @@ export { discoverTrunk } from "../git-stack.js";
|
|
|
8
8
|
export { BOUNCE_KIND_LABELS, deriveStackAgentsBusy, mergeQueueBounceLabel, type StackAgentsBusy } from "../stack-dto.js";
|
|
9
9
|
export { STACK_WATCH_SCHEMA, STACK_WATCH_DEFAULT_TIMEOUT_MS, STACK_WATCH_BUSY_RECHECK_S, StackWatchError, StackWatchTimeoutError, pollStackWatch, type PollStackWatchOptions, type StackWatchCursor, type StackWatchEnvelope, } from "../stack-watch.js";
|
|
10
10
|
export { stackBlockers, stackBlockersSummary, sameHead, layerAgents, layerAgentsBusy, conflictLiveParent, stackRepair, agentsCannotClear, type StackAgentBusy, type StackBlocker, type StackBusyAgent, type StackHeldBlocker, type StackLandGatePending, type StackLayerAgents, type StackRepairHint, } from "../stack-blockers.js";
|
|
11
|
-
export { STACK_WATCH_NOT_DONE_SENTENCE, stackTerminalReason, stackWatchNextArgs, stackWatchObligation, type StackWatchDoneReason, type StackWatchNext, type StackWatchNextArgs, type StackWatchObligation, type StackWatchObligationInput, type StackWatchReason, } from "../stack-watch-obligation.js";
|
|
11
|
+
export { STACK_WATCH_NOT_DONE_SENTENCE, STACK_WATCH_NOTIFY_PATTERN, stackWatchBackgroundCommand, stackTerminalReason, stackWatchNextArgs, stackWatchObligation, type StackWatchDoneReason, type StackWatchNext, type StackWatchNextArgs, type StackWatchObligation, type StackWatchObligationInput, type StackWatchReason, } from "../stack-watch-obligation.js";
|
package/dist/client/index.js
CHANGED
|
@@ -8,4 +8,4 @@ export { discoverTrunk } from "../git-stack.js";
|
|
|
8
8
|
export { BOUNCE_KIND_LABELS, deriveStackAgentsBusy, mergeQueueBounceLabel } from "../stack-dto.js";
|
|
9
9
|
export { STACK_WATCH_SCHEMA, STACK_WATCH_DEFAULT_TIMEOUT_MS, STACK_WATCH_BUSY_RECHECK_S, StackWatchError, StackWatchTimeoutError, pollStackWatch, } from "../stack-watch.js";
|
|
10
10
|
export { stackBlockers, stackBlockersSummary, sameHead, layerAgents, layerAgentsBusy, conflictLiveParent, stackRepair, agentsCannotClear, } from "../stack-blockers.js";
|
|
11
|
-
export { STACK_WATCH_NOT_DONE_SENTENCE, stackTerminalReason, stackWatchNextArgs, stackWatchObligation, } from "../stack-watch-obligation.js";
|
|
11
|
+
export { STACK_WATCH_NOT_DONE_SENTENCE, STACK_WATCH_NOTIFY_PATTERN, stackWatchBackgroundCommand, stackTerminalReason, stackWatchNextArgs, stackWatchObligation, } from "../stack-watch-obligation.js";
|
package/dist/commands/browse.js
CHANGED
|
@@ -221,6 +221,7 @@ export async function openTabsBrowser(initial) {
|
|
|
221
221
|
used: me.usage.standard.used,
|
|
222
222
|
limit: me.usage.standard.limit,
|
|
223
223
|
bonusRemaining: me.usage.bonus?.remaining,
|
|
224
|
+
surge: me.usage.surge,
|
|
224
225
|
resetsAt: me.resets_at,
|
|
225
226
|
jobs: jobs.slice(0, 5).map(toPanelJob),
|
|
226
227
|
jobsError: jobsError ?? undefined,
|
package/dist/commands/credits.js
CHANGED
package/dist/commands/dismiss.js
CHANGED
|
@@ -120,7 +120,7 @@ export function formatDismissResult(result, watch) {
|
|
|
120
120
|
: `Seam gate: not blocking (seam_state=${seam.state}).`);
|
|
121
121
|
}
|
|
122
122
|
if (watch?.next)
|
|
123
|
-
lines.push(`Next: ${watch.next.command}`);
|
|
123
|
+
lines.push(`Next: ${watch.next.background} as a background command, or ${watch.next.command}`);
|
|
124
124
|
return lines.join("\n");
|
|
125
125
|
}
|
|
126
126
|
export async function cmdDismiss(args, deps = {}) {
|
package/dist/commands/stack.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { stackBlockers, stackBlockersSummary } from "../stack-blockers.js";
|
|
2
|
-
import { STACK_WATCH_NOT_DONE_SENTENCE, stackTerminalReason, stackWatchObligation, } from "../stack-watch-obligation.js";
|
|
2
|
+
import { STACK_WATCH_NOT_DONE_SENTENCE, STACK_WATCH_NOTIFY_PATTERN, stackTerminalReason, stackWatchObligation, } from "../stack-watch-obligation.js";
|
|
3
3
|
import { adoptStack, ensureUpperPark, findStackPull, getEnrichedStack, landNextStack, listStacks, openStackPull, restackStack, setStackPolicy, } from "../api.js";
|
|
4
4
|
import { loadConfig } from "../config.js";
|
|
5
5
|
import { rateLimitedMessage, REVIEW_EXIT, CommandError } from "../errors.js";
|
|
@@ -36,7 +36,10 @@ export function stackWatchTextLines(watch) {
|
|
|
36
36
|
return [watch.message];
|
|
37
37
|
const next = watch.next;
|
|
38
38
|
return [
|
|
39
|
-
...(next ? [
|
|
39
|
+
...(next ? [
|
|
40
|
+
`Next: ${next.background} as a background command (notify on ${STACK_WATCH_NOTIFY_PATTERN}, then end your turn)`,
|
|
41
|
+
`Or poll: ${next.command} (MCP: stack_wait ${JSON.stringify(next.args)})`,
|
|
42
|
+
] : []),
|
|
40
43
|
STACK_WATCH_NOT_DONE_SENTENCE,
|
|
41
44
|
];
|
|
42
45
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { StackDto } from "./stack-dto.js";
|
|
2
2
|
export declare const STACK_WATCH_NOT_DONE_SENTENCE = "This stack is not landed. Your task is not done. Call stack_wait again with this cursor.";
|
|
3
|
+
export declare const STACK_WATCH_NOTIFY_PATTERN = "MS-WATCH (ATTENTION|LANDED)";
|
|
4
|
+
export declare function stackWatchBackgroundCommand(stackId: string): string;
|
|
3
5
|
export type StackWatchDoneReason = "landed" | "closed" | "archived" | "not_found";
|
|
4
6
|
export type StackWatchReason = StackWatchDoneReason | "open" | "attention" | "unread" | "rate_limited" | "failed";
|
|
5
7
|
export type StackWatchNextArgs = {
|
|
@@ -13,6 +15,7 @@ export type StackWatchNext = {
|
|
|
13
15
|
tool: "stack_wait";
|
|
14
16
|
args: StackWatchNextArgs;
|
|
15
17
|
command: string;
|
|
18
|
+
background: string;
|
|
16
19
|
};
|
|
17
20
|
export type StackWatchObligation = {
|
|
18
21
|
done: boolean;
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
export const STACK_WATCH_NOT_DONE_SENTENCE = "This stack is not landed. Your task is not done. Call stack_wait again with this cursor.";
|
|
2
|
+
export const STACK_WATCH_NOTIFY_PATTERN = "MS-WATCH (ATTENTION|LANDED)";
|
|
3
|
+
export function stackWatchBackgroundCommand(stackId) {
|
|
4
|
+
return `mg stack watch ${stackId}`;
|
|
5
|
+
}
|
|
2
6
|
const DONE_MESSAGES = {
|
|
3
7
|
landed: "This stack is landed. The watch is done; stop calling stack_wait for it.",
|
|
4
8
|
closed: "Every PR in this stack is merged or closed and nothing is left to land, but not every PR landed. The watch is done; tell the human which PRs were closed without landing.",
|
|
@@ -49,8 +53,10 @@ export function stackWatchObligation(input) {
|
|
|
49
53
|
: input.status === "failed"
|
|
50
54
|
? "failed"
|
|
51
55
|
: input.attention ? "attention" : input.unread ? "unread" : "open";
|
|
56
|
+
const background = stackWatchBackgroundCommand(input.stackId);
|
|
52
57
|
const parts = [
|
|
53
58
|
STACK_WATCH_NOT_DONE_SENTENCE,
|
|
59
|
+
`If you cannot hold a long turn open, run \`${background}\` as a background command instead, notify on output matching ${STACK_WATCH_NOTIFY_PATTERN}, and end your turn: it keeps calling stack_wait for you, stays silent while the stack waits, and wakes you on attention or when the watch is done.`,
|
|
54
60
|
`Watch stack ${input.stackId} as a whole until it lands or the human takes it back. A clean push, a submit, or one merged layer does not finish it: merging a lower layer can break a layer above it.`,
|
|
55
61
|
];
|
|
56
62
|
if (reason === "rate_limited") {
|
|
@@ -60,7 +66,7 @@ export function stackWatchObligation(input) {
|
|
|
60
66
|
parts.push(`This read failed, which does not end the task. Call stack_wait once more with the same cursor; if that fails too, tell the human that stack ${input.stackId} is not landed and is no longer being watched.`);
|
|
61
67
|
}
|
|
62
68
|
else if (reason === "attention" && input.attention) {
|
|
63
|
-
parts.push(`First fix #${input.attention.prNumber} (${input.attention.blocker}); repair, when present, names the fix. Calling stack_wait again before fixing it returns the same attention. After your push, call stack_wait with this cursor and enrolled_head_sha set to the SHA you pushed.`);
|
|
69
|
+
parts.push(`First fix #${input.attention.prNumber} (${input.attention.blocker}); repair, when present, names the fix. Calling stack_wait again before fixing it returns the same attention. After your push, call stack_wait with this cursor and enrolled_head_sha set to the SHA you pushed, or start \`${background} --head <pushed-sha>\` in the background.`);
|
|
64
70
|
}
|
|
65
71
|
else if (reason === "unread") {
|
|
66
72
|
parts.push("This tool did not read the stack's state; stack_wait will.");
|
|
@@ -73,7 +79,7 @@ export function stackWatchObligation(input) {
|
|
|
73
79
|
done: false,
|
|
74
80
|
until: "landed",
|
|
75
81
|
reason,
|
|
76
|
-
next: { tool: "stack_wait", args, command: `mg stack wait ${input.stackId} --json
|
|
82
|
+
next: { tool: "stack_wait", args, command: `mg stack wait ${input.stackId} --json`, background },
|
|
77
83
|
message: parts.join(" "),
|
|
78
84
|
};
|
|
79
85
|
}
|
package/dist/ui/banner.js
CHANGED
|
@@ -123,6 +123,9 @@ const WHATS_NEW = {
|
|
|
123
123
|
"0.3.30": [
|
|
124
124
|
"stack watch --json prints one JSON document",
|
|
125
125
|
],
|
|
126
|
+
"0.3.31": [
|
|
127
|
+
"every stack Next line offers mg stack watch",
|
|
128
|
+
],
|
|
126
129
|
};
|
|
127
130
|
function densityFromVariant(variant) {
|
|
128
131
|
if (variant === "mini" || variant === "nano")
|
package/dist/ui/usage.d.ts
CHANGED
|
@@ -24,6 +24,12 @@ export declare function formatResetLabel(iso: string | null | undefined, now?: D
|
|
|
24
24
|
export declare function formatDaysLeft(iso: string | null | undefined, now?: Date): string;
|
|
25
25
|
/** Bar cells so ` [bar] 100% used` stays inside the terminal. */
|
|
26
26
|
export declare function usageBarWidth(columns: number): number;
|
|
27
|
+
export type SurgeUsageSummary = {
|
|
28
|
+
includedMinutes: number;
|
|
29
|
+
usedMinutes: number;
|
|
30
|
+
walletMinutes: number;
|
|
31
|
+
};
|
|
32
|
+
export declare function formatSurgeSummary(surge: SurgeUsageSummary): string;
|
|
27
33
|
export type UsagePanelJob = {
|
|
28
34
|
job: string;
|
|
29
35
|
verdict: string;
|
|
@@ -37,6 +43,7 @@ export type UsagePanelInput = {
|
|
|
37
43
|
used: number;
|
|
38
44
|
limit: number | null;
|
|
39
45
|
bonusRemaining?: number;
|
|
46
|
+
surge?: SurgeUsageSummary;
|
|
40
47
|
resetsAt: string | null | undefined;
|
|
41
48
|
jobs: UsagePanelJob[];
|
|
42
49
|
/** When the jobs fetch failed, show this instead of "No review jobs yet." */
|
package/dist/ui/usage.js
CHANGED
|
@@ -87,6 +87,16 @@ export function formatDaysLeft(iso, now = new Date()) {
|
|
|
87
87
|
export function usageBarWidth(columns) {
|
|
88
88
|
return Math.max(8, frameWidth(80, columns) - 13);
|
|
89
89
|
}
|
|
90
|
+
function formatMinutes(value) {
|
|
91
|
+
return value.toLocaleString("en-US", { maximumFractionDigits: 1 });
|
|
92
|
+
}
|
|
93
|
+
export function formatSurgeSummary(surge) {
|
|
94
|
+
const wallet = `wallet ${formatMinutes(surge.walletMinutes)} min`;
|
|
95
|
+
if (surge.includedMinutes <= 0)
|
|
96
|
+
return `Surge: ${wallet}`;
|
|
97
|
+
return (`Surge: ${formatMinutes(surge.usedMinutes)} of ${formatMinutes(surge.includedMinutes)} included min this period` +
|
|
98
|
+
` · ${wallet}`);
|
|
99
|
+
}
|
|
90
100
|
function clipLine(line, columns) {
|
|
91
101
|
if (visibleWidth(line) <= columns)
|
|
92
102
|
return line;
|
|
@@ -111,6 +121,7 @@ export function formatUsagePanel(input) {
|
|
|
111
121
|
...(input.bonusRemaining == null
|
|
112
122
|
? []
|
|
113
123
|
: [` Bonus credits: ${input.bonusRemaining} remaining`]),
|
|
124
|
+
...(input.surge ? [` ${formatSurgeSummary(input.surge)}`] : []),
|
|
114
125
|
"",
|
|
115
126
|
];
|
|
116
127
|
if (input.jobs.length === 0) {
|
package/package.json
CHANGED
|
@@ -15,7 +15,7 @@ A stack watch is done only when the stack is landed, or when the human takes it
|
|
|
15
15
|
|
|
16
16
|
If your host cannot hold a long turn open (Cursor, Claude Code, Codex, and other turn-based agents), do not poll `stack_wait` inside your turn and do not subscribe to GitHub events. Hand the waiting to the CLI watcher:
|
|
17
17
|
|
|
18
|
-
1. Run `mg stack watch <stack-id>` as a background command (add `--head <sha>` right after you pushed that SHA) with a notification on output matching `MS-WATCH (ATTENTION|LANDED)`, then end your turn. The watcher runs the same stack wait against the Mergestorm API in slices of at most 300s, carries the cursor (`enrolled_head_sha`, `after_finished_at`, `bounce_id`) from one slice to the next, and prints nothing while the stack is `waiting` or `in_progress`, including `busy[]` holds and `actAfter: "agents_idle"`. It spends no model tokens while it waits. Ending your turn while it runs is not ending the task.
|
|
18
|
+
1. Run `mg stack watch <stack-id>` as a background command (add `--head <sha>` right after you pushed that SHA) with a notification on output matching `MS-WATCH (ATTENTION|LANDED)`, then end your turn. The watcher runs the same stack wait against the Mergestorm API in slices of at most 300s, carries the cursor (`enrolled_head_sha`, `after_finished_at`, `bounce_id`) from one slice to the next, and prints nothing while the stack is `waiting` or `in_progress`, including `busy[]` holds and `actAfter: "agents_idle"`. It spends no model tokens while it waits. Ending your turn while it runs is not ending the task. Every `watch.next` carries this command as `watch.next.background`.
|
|
19
19
|
2. It prints one line and exits for one of three reasons:
|
|
20
20
|
- `MS-WATCH ATTENTION pr=<n> head=<sha> blocker="<blocker>" repair=<kind>` (exit 3): the watch returned `attention`.
|
|
21
21
|
- `MS-WATCH ATTENTION failed stack=<id> error="<message>"` (exit 3): a read failed and its one retry failed too.
|