mergestorm-mcp 0.2.5 → 0.2.7

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 CHANGED
@@ -24,7 +24,7 @@ The stack tools expose reads plus one scoped policy write:
24
24
  - `stack_status({ stack_id })` returns one owned stack with enriched checks and agent state, plus `attention`, `issues`, and `currentCandidate` from the same blocker rules as `stack_wait`, read against that stack's own merge queue. It returns a structured `stack_not_found` or `rate_limited` error.
25
25
  - `stack_wait({ stack_id, timeout_s?, enrolled_head_sha?, after_finished_at?, bounce_id? })` waits for a stack or queue state change and returns the latest snapshots. `timeout_s: 0` returns one snapshot; a positive timeout after a snapshot with no attention returns the current `waiting` or `in_progress` status with selectors to use for the next call. An unread deadline returns `failed`. While Cyclone or Vortex is still working on the blocked PR, `stack_wait` and `stack_status` return `in_progress` instead of `attention`, with `busy[]` naming the PR, blocker, and agent, and `agents` carrying that PR's `vortexStatus`, `cycloneStatus`, `vortexReview`, and busy flags. A nonempty `busy[]` means wait. A queued Vortex run holds for at most 15 minutes, `seam_pending` alone does not hold, and reviewing or patching holds until the run finishes or its lease or check run is cleared. A blocker no agent run clears (a merge conflict vs the live parent, a restack `Conflict` or `Restack failed`, a Draft PR, a seam review pending with no review running) is still named during that `in_progress`, in `blocker`, `prNumber`, `headSha`, and `issues[]`, with `actAfter: "agents_idle"` and `waitingOn` listing the busy agents; act when a snapshot after the agents finish returns `attention`, re-reading the blocker then. `repair` is included when a concrete repair hint is available. Blockers the agents can change keep `blocker` and `actAfter` null. If a hold looks stuck, refresh `stack_status` and tell the human.
26
26
 
27
- Agents that cannot hold a long turn open should not poll `stack_wait` inside their turn. The server instructions tell them to run `mg stack watch <stack-id>` from the `mergestorm` CLI as a background command, notify on output matching `MS-WATCH (ATTENTION|LANDED)`, and end the turn. The watcher loops the same stack wait against the Mergestorm API with the cursor, stays silent while the stack is waiting or in progress, and exits on attention (exit 3), a failed read that failed again on retry (exit 3), or a finished watch (exit 0). On wake the agent reads `stack_status` once, fixes the blocker, pushes, and restarts the watcher with `--head <pushed-sha>`. The stack is still not done until it lands. Agents are told not to subscribe to GitHub events to wait on a stack.
27
+ Agents that cannot hold a long turn open should not poll `stack_wait` inside their turn. The server instructions tell them to run `mg stack watch <stack-id>` from the `mergestorm` CLI as a background command, notify on output matching `MS-WATCH (ATTENTION|LANDED)`, and end the turn. The watcher loops the same stack wait against the Mergestorm API with the cursor, stays silent while the stack is waiting or in progress, and exits on attention (exit 3), a failed read that failed again on retry (exit 3), or a finished watch (exit 0). Every `watch.next` carries that command as `watch.next.background`, and every not-done `watch.message` offers it before the next `stack_wait` call. On wake the agent reads `stack_status` once, fixes the blocker, pushes, and restarts the watcher with `--head <pushed-sha>`. The stack is still not done until it lands. Agents are told not to subscribe to GitHub events to wait on a stack.
28
28
 
29
29
  Auth is `MERGESTORM_API_KEY`, then `~/.mergestorm/config.json` (same as `mg`).
30
30
 
package/dist/index.js CHANGED
@@ -37,6 +37,17 @@ var MCP_WEBSITE_URL = SITE;
37
37
 
38
38
  // src/tools/credits.ts
39
39
  import { CommandError, getMe, loadConfig } from "mergestorm/client";
40
+ function formatMinutes(value) {
41
+ return value.toLocaleString("en-US", { maximumFractionDigits: 1 });
42
+ }
43
+ function surgeSummary(surge) {
44
+ if (!surge) return "";
45
+ const wallet = `wallet ${formatMinutes(surge.walletMinutes)} min`;
46
+ if (surge.includedMinutes <= 0) return `
47
+ Surge: ${wallet}`;
48
+ return `
49
+ Surge: ${formatMinutes(surge.usedMinutes)} of ${formatMinutes(surge.includedMinutes)} included min this period \xB7 ${wallet}`;
50
+ }
40
51
  async function credits(cfg) {
41
52
  const resolved = cfg ?? await loadConfig();
42
53
  const me = await getMe(resolved);
@@ -53,9 +64,10 @@ async function credits(cfg) {
53
64
  const standard = me.usage.standard;
54
65
  const remaining = standard.remaining ?? (standard.limit != null ? Math.max(0, standard.limit - standard.used) : null);
55
66
  const bonusRemaining = me.usage.bonus?.remaining;
67
+ const surge = me.usage.surge;
56
68
  const bonusSummary = bonusRemaining == null ? "" : ` \xB7 ${bonusRemaining} bonus remaining`;
57
69
  return {
58
- summary: remaining == null ? `${standard.used} used \xB7 unlimited${bonusSummary}` : `${standard.used} used \xB7 ${remaining} remaining${bonusSummary}`,
70
+ summary: remaining == null ? `${standard.used} used \xB7 unlimited${bonusSummary}${surgeSummary(surge)}` : `${standard.used} used \xB7 ${remaining} remaining${bonusSummary}${surgeSummary(surge)}`,
59
71
  data: {
60
72
  usage: me.usage,
61
73
  resets_at: me.resets_at ?? null
@@ -175,6 +187,7 @@ import {
175
187
  pollStackWatch,
176
188
  StackWatchError,
177
189
  StackWatchTimeoutError,
190
+ stackWatchBackgroundCommand,
178
191
  stackWatchObligation
179
192
  } from "mergestorm/client";
180
193
  import { z } from "zod";
@@ -216,7 +229,7 @@ function stackWaitStatusLine(envelope) {
216
229
  ...envelope.cursor.afterFinishedAt !== void 0 ? [`after_finished_at ${JSON.stringify(envelope.cursor.afterFinishedAt)}`] : [],
217
230
  ...envelope.cursor.bounceId !== void 0 ? [`bounce_id ${JSON.stringify(envelope.cursor.bounceId)}`] : []
218
231
  ];
219
- return `${base} \xB7 watch not finished: call stack_wait again with timeout_s 45 and the same cursor: ${selectors.join(", ")}`;
232
+ return `${base} \xB7 watch not finished: run ${stackWatchBackgroundCommand(envelope.stackId)} as a background command and end your turn, or call stack_wait again with timeout_s 45 and the same cursor: ${selectors.join(", ")}`;
220
233
  }
221
234
  async function stackWait(input, cfg, deps = {}) {
222
235
  const args = z.object(stackWaitSchema).parse(input);
@@ -1127,20 +1140,21 @@ var MCP_STACK_WATCH_INSTRUCTIONS = [
1127
1140
  "- mg-park-* is a frozen GitHub base, not a repair target: never merge or rebase onto mg-park-*.",
1128
1141
  "- merge_failed, gh_error, and ci_timeout bounces turn Auto land off, and stack autoLandOff then reads reason bounced with that queue entry's id; any other autoLandOff reason (user, archive, watcher, cancelled, landed) is not a bounce to undo. The watching agent turns Auto land back on with stack_set auto_land: true once it has handled that bounce, at most once per bounce and 3 times per watch; mergestorm-bounce-watch has the conditions.",
1129
1142
  '- While stack autoLandOff reads reason bounced, the watch returns attention: blocker "Auto land off after <kind> bounce", the bounce in issues[], repair kind auto_land_off.',
1143
+ '- A stack whose review unit was abandoned (a human moved its bottom PR off mg-stack-N) returns attention: blocker "Review unit abandoned: based on <base>", repair kind unit_abandoned. Do not patch or retarget; ask the human whether to re-root it. If at least two PRs remain open, tell them that mg stack adopt owner/repo#<bottom PR> re-roots it on a new review unit. With only one open PR, re-adopting will not create a review unit, so tell them to close and resubmit it to create a fresh stack.',
1130
1144
  "- Tempest never patches. Cyclone patches a land PR when auto-patch is on for that stack (its override, else the account setting); otherwise the watching agent fixes tempest_findings on the land PR, and repair kind tempest_findings names it.",
1131
1145
  "- Use the mergestorm-bounce-watch skill for the fix-then-watch policy."
1132
1146
  ].join("\n");
1133
1147
 
1134
1148
  // src/server.ts
1135
1149
  var MCP_SERVER_NAME = "mergestorm";
1136
- var MCP_SERVER_VERSION = "0.2.5";
1150
+ var MCP_SERVER_VERSION = "0.2.7";
1137
1151
  var MCP_SERVER_INSTRUCTIONS = [
1138
1152
  MCP_STACK_DONE_INSTRUCTIONS,
1139
1153
  MCP_PR_LOOP_INSTRUCTIONS,
1140
1154
  MCP_STACK_BASE_INSTRUCTIONS,
1141
1155
  MCP_STACK_WATCH_INSTRUCTIONS
1142
1156
  ].join("\n\n");
1143
- var STACK_WATCH_RESULT_NOTE = ' The result carries watch {done, until: "landed", reason, next, message}: while watch.done is false the stack is not landed and your task is not done; call stack_wait with watch.next.args.';
1157
+ var STACK_WATCH_RESULT_NOTE = ' The result carries watch {done, until: "landed", reason, next, message}: while watch.done is false the stack is not landed and your task is not done; call stack_wait with watch.next.args, or run watch.next.background as a background command and end your turn.';
1144
1158
  function stripNulls(value) {
1145
1159
  if (Array.isArray(value)) {
1146
1160
  return value.map((item) => stripNulls(item));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mergestorm-mcp",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "Mergestorm MCP server — identity, review, and read-only stack tools over stdio",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -29,7 +29,7 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@modelcontextprotocol/sdk": "^1.30.0",
32
- "mergestorm": "0.3.30",
32
+ "mergestorm": "0.3.34",
33
33
  "zod": "^3.24.2"
34
34
  },
35
35
  "devDependencies": {