mergestorm-mcp 0.2.14 → 0.2.16

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.
Files changed (3) hide show
  1. package/README.md +2 -2
  2. package/dist/index.js +44 -13
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -18,9 +18,9 @@ Every tool declares MCP `annotations` for read-only access, destructive changes,
18
18
 
19
19
  The stack tools expose reads plus one scoped policy write:
20
20
 
21
- - `stack_adopt({ owner, repo, pr_number, auto_land?, auto_review?, auto_patch? })` adopts an open PR chain with optional per-stack policy. Omitted policy does not force Cyclone off. `auto_review` and `auto_patch` accept `true`, `false`, or `null`; `null` clears the override. Read `stack_status` with the returned `result.stack.id` to verify policy and Cyclone ownership. Adoption needs the Mergestorm Surge GitHub App on the repository (Cyclone is the auto-patch App). A refusal keeps the code `cyclone_not_connected` or `cyclone_not_installed`; its message, and `error.reason` starting with `surge_` when Surge is the one to install, say which App is missing.
21
+ - `stack_adopt({ owner, repo, pr_number, auto_land?, auto_review?, auto_patch?, auto_resolve_conflicts?, auto_fix_ci? })` adopts an open PR chain with optional per-stack policy. Omitted policy does not force Cyclone off. `auto_review`, `auto_patch`, `auto_resolve_conflicts`, and `auto_fix_ci` accept `true`, `false`, or `null`; `null` clears the override. Read `stack_status` with the returned `result.stack.id` to verify policy and Cyclone ownership. Adoption needs the Mergestorm Surge GitHub App on the repository (Cyclone is the auto-patch App). A refusal keeps the code `cyclone_not_connected` or `cyclone_not_installed`; its message, and `error.reason` starting with `surge_` when Surge is the one to install, say which App is missing.
22
22
  - `stack_list` returns the current API key owner's registered stacks.
23
- - `stack_set({ stack_id, auto_land?, auto_review?, auto_patch? })` sets per-stack policy on one owned stack. `auto_land` flips Auto land. `auto_review` and `auto_patch` pin Vortex auto-review or Cyclone auto-patch for that stack in either direction (`true` or `false`); `null` clears the pin so the stack follows the account setting. Pass at least one key. Account settings are never changed here.
23
+ - `stack_set({ stack_id, auto_land?, auto_review?, auto_patch?, auto_resolve_conflicts?, auto_fix_ci? })` sets per-stack policy on one owned stack. `auto_land` flips Auto land. `auto_review`, `auto_patch`, `auto_resolve_conflicts`, and `auto_fix_ci` pin Vortex auto-review, Cyclone auto-patch, Auto-resolve merge conflicts, or Auto-fix failing CI for that stack in either direction (`true` or `false`); `null` clears the pin so the stack follows the account setting. Pass at least one key. Account settings are never changed here.
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, "Seam review stuck in reviewing, no review running", "Seam re-review pending, no review running", "Seam verdict is for an older head, no review running", or a review skipped for review quota) 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. For the three stuck-seam blockers, `repair` has kind `seam_review_stuck`: do not patch or push; tell the human to click Continue, which re-queues the seam review at the live head. `repair` is included when another 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
 
package/dist/index.js CHANGED
@@ -201,8 +201,9 @@ var stackWaitSchema = {
201
201
  function repairSummary(repair) {
202
202
  if (!repair) return "";
203
203
  const files = "files" in repair && repair.files.length ? ` Files: ${repair.files.join(", ")}.` : "";
204
+ const note = "note" in repair && repair.note ? ` ${repair.note}` : "";
204
205
  return `
205
- Repair #${repair.prNumber} (${repair.kind}).${files} ${repair.steps}`;
206
+ Repair #${repair.prNumber} (${repair.kind}).${files} ${repair.steps}${note}`;
206
207
  }
207
208
  function withWatchText(watch, body) {
208
209
  return `${watch.message}
@@ -761,10 +762,15 @@ import {
761
762
  } from "mergestorm/client";
762
763
  function settingsSummary(settings) {
763
764
  const autoPatch = settings.auto_patch_enabled ? "on" : "off";
765
+ const automations = [
766
+ settings.auto_resolve_conflicts_enabled ? "auto_resolve_conflicts on" : null,
767
+ settings.auto_fix_ci_enabled ? "auto_fix_ci on" : null
768
+ ].filter((label) => label !== null);
769
+ const autoPatchLabel = [`auto_patch ${autoPatch}`, ...automations].join(" \xB7 ");
764
770
  const cyclone = settings.cyclone_connected ? "Cyclone connected" : "Cyclone not connected";
765
- if (typeof settings.stacks_ready !== "boolean") return `auto_patch ${autoPatch} \xB7 ${cyclone}`;
771
+ if (typeof settings.stacks_ready !== "boolean") return `${autoPatchLabel} \xB7 ${cyclone}`;
766
772
  const stacks = settings.stacks_ready ? "stacks, merge queue and Auto land ready" : "stacks, merge queue and Auto land not ready (install Mergestorm Surge)";
767
- return `auto_patch ${autoPatch} \xB7 ${cyclone} (auto patch) \xB7 ${stacks}`;
773
+ return `${autoPatchLabel} \xB7 ${cyclone} (auto patch) \xB7 ${stacks}`;
768
774
  }
769
775
  async function settingsGet(cfg) {
770
776
  const settings = await getSettings(cfg);
@@ -856,18 +862,22 @@ function stackSetPatch(input) {
856
862
  if (typeof input.auto_land === "boolean") patch.autoEnqueueWhenReady = input.auto_land;
857
863
  if (input.auto_review !== void 0) patch.autoReviewOverride = input.auto_review;
858
864
  if (input.auto_patch !== void 0) patch.autoPatchOverride = input.auto_patch;
865
+ if (input.auto_resolve_conflicts !== void 0) {
866
+ patch.autoResolveConflictsOverride = input.auto_resolve_conflicts;
867
+ }
868
+ if (input.auto_fix_ci !== void 0) patch.autoFixCiOverride = input.auto_fix_ci;
859
869
  return patch;
860
870
  }
861
871
  async function stackSet(stackId, input, cfg) {
862
872
  const patch = stackSetPatch(input);
863
873
  if (Object.keys(patch).length === 0) {
864
874
  return {
865
- summary: "stack_set needs at least one of auto_land, auto_review, auto_patch",
875
+ summary: "stack_set needs at least one of auto_land, auto_review, auto_patch, auto_resolve_conflicts, auto_fix_ci",
866
876
  isError: true,
867
877
  data: {
868
878
  error: {
869
879
  code: "invalid_input",
870
- message: "Pass at least one of auto_land, auto_review, auto_patch.",
880
+ message: "Pass at least one of auto_land, auto_review, auto_patch, auto_resolve_conflicts, auto_fix_ci.",
871
881
  stack_id: stackId
872
882
  }
873
883
  }
@@ -884,6 +894,12 @@ async function stackSet(stackId, input, cfg) {
884
894
  if (input.auto_patch !== void 0) {
885
895
  parts.push(`auto-patch ${triWord(input.auto_patch)}`);
886
896
  }
897
+ if (input.auto_resolve_conflicts !== void 0) {
898
+ parts.push(`auto-resolve-conflicts ${triWord(input.auto_resolve_conflicts)}`);
899
+ }
900
+ if (input.auto_fix_ci !== void 0) {
901
+ parts.push(`auto-fix-ci ${triWord(input.auto_fix_ci)}`);
902
+ }
887
903
  const watch = stackWatchObligation3({ stackId, terminal: null, unread: true, freshCursor: true });
888
904
  return {
889
905
  summary: withWatchText(watch, `${parts.join(", ")} for stack ${stackId}`),
@@ -892,6 +908,8 @@ async function stackSet(stackId, input, cfg) {
892
908
  ...typeof input.auto_land === "boolean" ? { auto_land: input.auto_land } : {},
893
909
  ...input.auto_review !== void 0 ? { auto_review: input.auto_review } : {},
894
910
  ...input.auto_patch !== void 0 ? { auto_patch: input.auto_patch } : {},
911
+ ...input.auto_resolve_conflicts !== void 0 ? { auto_resolve_conflicts: input.auto_resolve_conflicts } : {},
912
+ ...input.auto_fix_ci !== void 0 ? { auto_fix_ci: input.auto_fix_ci } : {},
895
913
  result,
896
914
  watch
897
915
  }
@@ -905,7 +923,9 @@ var stackAdoptSchema = {
905
923
  pr_number: z2.number().int().min(1),
906
924
  auto_land: z2.boolean().optional(),
907
925
  auto_review: z2.boolean().nullable().optional(),
908
- auto_patch: z2.boolean().nullable().optional()
926
+ auto_patch: z2.boolean().nullable().optional(),
927
+ auto_resolve_conflicts: z2.boolean().nullable().optional(),
928
+ auto_fix_ci: z2.boolean().nullable().optional()
909
929
  };
910
930
  var inputSchema = z2.object(stackAdoptSchema);
911
931
  async function stackAdopt(input, cfg) {
@@ -972,6 +992,12 @@ function stackSummary(stack, entries = []) {
972
992
  if (typeof stack.autoPatchOverride === "boolean") {
973
993
  overrides.push(`auto-patch ${stack.autoPatchOverride ? "on" : "off"}`);
974
994
  }
995
+ if (typeof stack.autoResolveConflictsOverride === "boolean") {
996
+ overrides.push(`auto-resolve-conflicts ${stack.autoResolveConflictsOverride ? "on" : "off"}`);
997
+ }
998
+ if (typeof stack.autoFixCiOverride === "boolean") {
999
+ overrides.push(`auto-fix-ci ${stack.autoFixCiOverride ? "on" : "off"}`);
1000
+ }
975
1001
  if (stack.cycloneOwnerMatch) {
976
1002
  overrides.push(`cyclone-owner ${stack.cycloneOwnerMatch}`);
977
1003
  }
@@ -1160,14 +1186,15 @@ var MCP_STACK_WATCH_INSTRUCTIONS = [
1160
1186
  `- While the stack has a live merge queue entry, stack_wait carries queueWait (the entry's wait reason, how long it has been held, and the stack queued ahead of it) and the status line names it. The watch returns attention, blocker "Merge queue stuck: ..." and repair kind queue_stalled, when the entry (or the entry ahead of it) has been held for 45 minutes without landing or bouncing, or has sat queued for 45 minutes with nothing working on it or ahead of it.`,
1161
1187
  '- When no Vortex run works on the head and its review was skipped because auto-review is off (blocker "Vortex auto-review is off, so this head was not reviewed") or because billing could not be confirmed ("Vortex skipped this head: it could not confirm billing"), or it covered only part of the PR ("Vortex reviewed only part of this PR"), the watch returns attention at once with repair kind vortex_skipped. Do not patch for it; the repair names the mention or setting that gets the head reviewed.',
1162
1188
  '- A live-head Vortex request_changes handoff returns attention with blocker "Vortex findings need a person" and repair kind vortex_findings; verify the review and inline findings against the live head before repair.',
1163
- "- Cyclone patches a land PR when auto-patch is on for that stack (its override, else the account setting); otherwise the watching agent fixes the land PR's blocker on mg-stack-<n>.",
1189
+ "- Cyclone, with auto-patch on, patches only Vortex review findings (Core and seam, on any layer, the land PR included). It does not fix merge conflicts, restack conflicts, failing CI, or any other landing blocker by itself; the watching agent fixes those, whatever the auto-patch setting, and does not turn auto-patch off to do it. With auto-resolve conflicts on for the stack, Cyclone resolves a layer conflict itself (never the land PR's conflict with main): while busy[] shows Cyclone, wait, and fix the conflict only after its handoff, when the watch returns attention. With auto-fix CI on for the stack, Cyclone fixes a settled red check on a layer itself (never the land PR), or asks Surge for one re-run of a flaky one: while busy[] shows Cyclone, wait, and fix the failure only after its handoff, when the watch returns attention.",
1190
+ "- Cyclone patches Vortex findings on a land PR when auto-patch is on for that stack (its override, else the account setting); every other land PR blocker, a conflict with main or failing CI included, is fixed by the watching agent on mg-stack-<n>.",
1164
1191
  '- More states the watch names: a Vortex review skipped because the PR author is on the ignored authors list or because reviews are turned off for the repository is attention at once with repair kind vortex_skipped; a Vortex retry that is scheduled ("Vortex hit a temporary error on this head and will retry by itself") is a wait listed in issues[], never attention; "Review failed" carries repair kind vortex_failed (comment @mergestorm-vortex review or push a new commit; if it fails again tell the human); a PR open on mg-stack-N that is not a layer of the stack is attention at once, blocker "The land PR cannot be queued: ...", repair kind members_remaining (do not patch or push; close, retarget or adopt that PR).',
1165
1192
  "- Use the mergestorm-bounce-watch skill for the fix-then-watch policy."
1166
1193
  ].join("\n");
1167
1194
 
1168
1195
  // src/server.ts
1169
1196
  var MCP_SERVER_NAME = "mergestorm";
1170
- var MCP_SERVER_VERSION = "0.2.14";
1197
+ var MCP_SERVER_VERSION = "0.2.16";
1171
1198
  var MCP_SERVER_INSTRUCTIONS = [
1172
1199
  MCP_STACK_DONE_INSTRUCTIONS,
1173
1200
  MCP_PR_LOOP_INSTRUCTIONS,
@@ -1376,7 +1403,7 @@ function createMergestormMcpServer() {
1376
1403
  "stack_adopt",
1377
1404
  {
1378
1405
  title: "Adopt PR stack",
1379
- description: "Adopt an open GitHub PR chain into a Mergestorm stack. auto_land is boolean; auto_review and auto_patch accept true, false, or null, where null clears the override. Omitted auto_land seeds from auto_land_default; omitted auto_review and auto_patch overrides are not seeded. Never changes account settings. Adoption needs the Mergestorm Surge GitHub App on the repository. Stacks, the merge queue and Auto land run through that App; Cyclone is the auto-patch App. Without it the API returns cyclone_not_connected or cyclone_not_installed; the message, and error.reason starting with surge_ when Surge is the one to install, say which App is missing \u2014 do not retry, tell the human what the message says. Read stack_status with result.stack.id afterward to verify policy and Cyclone ownership. With 2+ PRs, adopt moves the bottom PR's GitHub base to mg-stack-<n> and parks PR3+ on an mg-park-* freeze; a 1-PR stack stays on main. Leave those bases. Never retarget the bottom back to main." + STACK_WATCH_RESULT_NOTE,
1406
+ description: "Adopt an open GitHub PR chain into a Mergestorm stack. auto_land is boolean; auto_review, auto_patch, auto_resolve_conflicts, and auto_fix_ci accept true, false, or null, where null clears the override. Omitted auto_land seeds from auto_land_default; omitted overrides are not seeded. Never changes account settings. Adoption needs the Mergestorm Surge GitHub App on the repository. Stacks, the merge queue and Auto land run through that App; Cyclone is the auto-patch App. Without it the API returns cyclone_not_connected or cyclone_not_installed; the message, and error.reason starting with surge_ when Surge is the one to install, say which App is missing \u2014 do not retry, tell the human what the message says. Read stack_status with result.stack.id afterward to verify policy and Cyclone ownership. With 2+ PRs, adopt moves the bottom PR's GitHub base to mg-stack-<n> and parks PR3+ on an mg-park-* freeze; a 1-PR stack stays on main. Leave those bases. Never retarget the bottom back to main." + STACK_WATCH_RESULT_NOTE,
1380
1407
  inputSchema: stackAdoptSchema,
1381
1408
  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true }
1382
1409
  },
@@ -1407,22 +1434,26 @@ function createMergestormMcpServer() {
1407
1434
  "stack_set",
1408
1435
  {
1409
1436
  title: "Set stack policy",
1410
- description: "Set per-stack automation on one owned Mergestorm stack: auto_land arms or disarms Auto land; auto_review and auto_patch pin Vortex auto-review or Cyclone auto-patch for this stack only (true or false), and null clears the pin so the stack follows the account setting again. Pass at least one key. Never changes account settings." + STACK_WATCH_RESULT_NOTE,
1437
+ description: "Set per-stack automation on one owned Mergestorm stack: auto_land arms or disarms Auto land; auto_review, auto_patch, auto_resolve_conflicts, and auto_fix_ci pin Vortex auto-review, Cyclone auto-patch, Auto-resolve merge conflicts, or Auto-fix failing CI for this stack only (true or false), and null clears the pin so the stack follows the account setting again. Pass at least one key. Never changes account settings." + STACK_WATCH_RESULT_NOTE,
1411
1438
  inputSchema: {
1412
1439
  stack_id: z3.string().min(1),
1413
1440
  auto_land: z3.boolean().optional(),
1414
1441
  auto_review: z3.boolean().nullable().optional(),
1415
- auto_patch: z3.boolean().nullable().optional()
1442
+ auto_patch: z3.boolean().nullable().optional(),
1443
+ auto_resolve_conflicts: z3.boolean().nullable().optional(),
1444
+ auto_fix_ci: z3.boolean().nullable().optional()
1416
1445
  },
1417
1446
  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true }
1418
1447
  },
1419
- async ({ stack_id, auto_land, auto_review, auto_patch }) => {
1448
+ async ({ stack_id, auto_land, auto_review, auto_patch, auto_resolve_conflicts, auto_fix_ci }) => {
1420
1449
  try {
1421
1450
  return ok(
1422
1451
  await stackSet(stack_id, {
1423
1452
  ...auto_land !== void 0 ? { auto_land } : {},
1424
1453
  ...auto_review !== void 0 ? { auto_review } : {},
1425
- ...auto_patch !== void 0 ? { auto_patch } : {}
1454
+ ...auto_patch !== void 0 ? { auto_patch } : {},
1455
+ ...auto_resolve_conflicts !== void 0 ? { auto_resolve_conflicts } : {},
1456
+ ...auto_fix_ci !== void 0 ? { auto_fix_ci } : {}
1426
1457
  })
1427
1458
  );
1428
1459
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mergestorm-mcp",
3
- "version": "0.2.14",
3
+ "version": "0.2.16",
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.42",
32
+ "mergestorm": "0.3.45",
33
33
  "zod": "^3.24.2"
34
34
  },
35
35
  "devDependencies": {