mergestorm-mcp 0.1.8 → 0.1.10

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 (2) hide show
  1. package/dist/index.js +40 -9
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -592,6 +592,17 @@ function settingsPatchFromArgs(args) {
592
592
  const value = args[key];
593
593
  if (value === void 0) continue;
594
594
  const row = BEARER_SETTINGS.find((row2) => row2.key === key);
595
+ if ("kind" in row && row.kind === "seconds") {
596
+ if (typeof value !== "number" || !Number.isInteger(value) || value < row.min || value > row.max) {
597
+ throw new CommandError8(
598
+ `${key} takes a whole number of seconds from ${row.min} through ${row.max}.`,
599
+ 2,
600
+ "usage"
601
+ );
602
+ }
603
+ Object.assign(patch, { [key]: value });
604
+ continue;
605
+ }
595
606
  if ("kind" in row) {
596
607
  const valid = row.kind === "logins" ? Array.isArray(value) && value.every((login) => typeof login === "string") : typeof value === "string" && row.values.includes(value);
597
608
  if (!valid) throw new CommandError8(`Invalid value for ${key}.`, 2, "usage");
@@ -791,6 +802,17 @@ var stackWaitSchema = {
791
802
  after_finished_at: z2.string().nullable().optional(),
792
803
  bounce_id: z2.string().nullable().optional()
793
804
  };
805
+ function stackWaitSummary(envelope) {
806
+ const base = `Stack ${envelope.stackId} \xB7 ${envelope.status}${stackBlockersSummary2(envelope.blocker && envelope.prNumber != null ? { prNumber: envelope.prNumber, blocker: envelope.blocker } : null, envelope.issues)}${envelope.assessment === "unavailable" ? " \xB7 assessment unavailable" : ""}`;
807
+ if (envelope.status !== "waiting" && envelope.status !== "in_progress") return base;
808
+ const selectors = [
809
+ `stack_id ${JSON.stringify(envelope.cursor.stackId)}`,
810
+ `enrolled_head_sha ${JSON.stringify(envelope.cursor.enrolledHeadSha)}`,
811
+ ...envelope.cursor.afterFinishedAt !== void 0 ? [`after_finished_at ${JSON.stringify(envelope.cursor.afterFinishedAt)}`] : [],
812
+ ...envelope.cursor.bounceId !== void 0 ? [`bounce_id ${JSON.stringify(envelope.cursor.bounceId)}`] : []
813
+ ];
814
+ return `${base} \xB7 watch not finished: call stack_wait again with timeout_s 45 and the same cursor: ${selectors.join(", ")}`;
815
+ }
794
816
  async function stackWait(input, cfg, deps = {}) {
795
817
  const args = z2.object(stackWaitSchema).parse(input);
796
818
  const cursor = args.enrolled_head_sha !== void 0 || args.after_finished_at !== void 0 || args.bounce_id !== void 0 ? {
@@ -809,7 +831,7 @@ async function stackWait(input, cfg, deps = {}) {
809
831
  } catch (err) {
810
832
  if (err instanceof StackWatchError) {
811
833
  return {
812
- summary: `Stack ${err.lastEnvelope.stackId} \xB7 ${err.lastEnvelope.status}${stackBlockersSummary2(err.lastEnvelope.blocker && err.lastEnvelope.prNumber != null ? { prNumber: err.lastEnvelope.prNumber, blocker: err.lastEnvelope.blocker } : null, err.lastEnvelope.issues)}${err.lastEnvelope.assessment === "unavailable" ? " \xB7 assessment unavailable" : ""}`,
834
+ summary: stackWaitSummary(err.lastEnvelope),
813
835
  data: {
814
836
  ...err.lastEnvelope,
815
837
  ...err.retryAfterSeconds !== void 0 ? { retry_after_seconds: err.retryAfterSeconds } : {}
@@ -821,7 +843,7 @@ async function stackWait(input, cfg, deps = {}) {
821
843
  envelope = err.lastEnvelope;
822
844
  }
823
845
  return {
824
- summary: `Stack ${envelope.stackId} \xB7 ${envelope.status}${stackBlockersSummary2(envelope.blocker && envelope.prNumber != null ? { prNumber: envelope.prNumber, blocker: envelope.blocker } : null, envelope.issues)}${envelope.assessment === "unavailable" ? " \xB7 assessment unavailable" : ""}`,
846
+ summary: stackWaitSummary(envelope),
825
847
  data: envelope
826
848
  };
827
849
  }
@@ -923,6 +945,13 @@ var MCP_PR_LOOP_INSTRUCTIONS = [
923
945
  `- If you skip a finding (not reproducible, policy fork, or needs a human), post a public GitHub PR comment before you stop. First line must be: ${MCP_FINDING_DISMISS_MARKER}. Then one line per skipped finding (path, severity, why).`,
924
946
  "- Do not prefix that comment with cyclone-outcome: (reserved)."
925
947
  ].join("\n");
948
+ var MCP_STACK_BASE_INSTRUCTIONS = [
949
+ "Stack PR bases (Mergestorm sets these; leave them):",
950
+ "- Stack of 2+ PRs: after adopt (stack_adopt, mg stack adopt, or mg stack submit) the bottom PR's GitHub base is the owned trunk mg-stack-<n>, not main. That is correct. Layer 2 is based on layer 1; layer 3+ on the mg-park-* freeze; the git parent of layer 2+ is still the layer below. A 1-PR stack stays on main and gets no mg-stack-<n>.",
951
+ "- Never retarget a stack PR's GitHub base (gh pr edit --base, a REST base change, or the UI). Moving the bottom to main makes Mergestorm abandon the stack's review unit; setting mg-stack-<n> again does not restore it. If a base was already moved, stop and tell the human; do not run stack land or re-adopt to repair it.",
952
+ "- mg-stack-<n> is the bottom PR's live parent: merging it into the bottom branch is that PR's conflict repair (bounce-watch rules). Never push to mg-stack-<n>, and never rebase or merge layer 2+ onto it or onto main. mg-park-* is never a merge or rebase target.",
953
+ "- mg stack submit prints the base each PR opened with; the bottom moves to mg-stack-<n> right after. stack_status trunkBranch is the current trunk."
954
+ ].join("\n");
926
955
  var MCP_STACK_WATCH_INSTRUCTIONS = [
927
956
  "Stack-watch facts (mergestorm.stack_watch/v1):",
928
957
  "- waiting with nonempty issues[] is not idle; in_progress can also carry issues[].",
@@ -936,7 +965,7 @@ var MCP_STACK_WATCH_INSTRUCTIONS = [
936
965
 
937
966
  // src/server.ts
938
967
  var MCP_SERVER_NAME = "mergestorm";
939
- var MCP_SERVER_VERSION = "0.1.8";
968
+ var MCP_SERVER_VERSION = "0.1.10";
940
969
  function stripNulls(value) {
941
970
  if (Array.isArray(value)) {
942
971
  return value.map((item) => stripNulls(item));
@@ -972,6 +1001,8 @@ function createMergestormMcpServer() {
972
1001
  },
973
1002
  { instructions: `${MCP_PR_LOOP_INSTRUCTIONS}
974
1003
 
1004
+ ${MCP_STACK_BASE_INSTRUCTIONS}
1005
+
975
1006
  ${MCP_STACK_WATCH_INSTRUCTIONS}` }
976
1007
  );
977
1008
  const addTool = server2.registerTool.bind(server2);
@@ -1148,7 +1179,7 @@ ${MCP_STACK_WATCH_INSTRUCTIONS}` }
1148
1179
  "stack_adopt",
1149
1180
  {
1150
1181
  title: "Adopt PR stack",
1151
- 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 requires a Cyclone GitHub App install; without it the API returns cyclone_not_connected \u2014 do not retry, tell the human. Read stack_status with result.stack.id afterward to verify policy and Cyclone ownership.",
1182
+ 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 requires a Cyclone GitHub App install; without it the API returns cyclone_not_connected \u2014 do not retry, tell the human. 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.",
1152
1183
  inputSchema: stackAdoptSchema
1153
1184
  },
1154
1185
  async (args) => {
@@ -1203,7 +1234,7 @@ ${MCP_STACK_WATCH_INSTRUCTIONS}` }
1203
1234
  "stack_status",
1204
1235
  {
1205
1236
  title: "Get stack status",
1206
- description: "Fetch one owned Mergestorm 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. This tool is read-only.",
1237
+ description: "Fetch one owned Mergestorm 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. This tool is read-only. trunkBranch is mg-stack-<n> for an adopted 2+ PR stack and matches the bottom open PR's GitHub base; an mg-park-* parentBranch is a freeze. Neither is drift to fix.",
1207
1238
  inputSchema: {
1208
1239
  stack_id: z3.string().min(1)
1209
1240
  }
@@ -1220,7 +1251,7 @@ ${MCP_STACK_WATCH_INSTRUCTIONS}` }
1220
1251
  "stack_wait",
1221
1252
  {
1222
1253
  title: "Wait for stack attention",
1223
- description: "Wait for a stack to need attention. Returns a mergestorm.stack_watch/v1 envelope; timeout_s: 0 returns one snapshot and timeout_s: 1-45 waits up to that many seconds, returning the current status (waiting or in_progress) if a snapshot was read and no attention is found. A timeout with either of those statuses is not a failure. A timeout of failed means no assessment was produced. This tool is read-only.",
1254
+ description: "Wait for a stack to need attention. Returns a mergestorm.stack_watch/v1 envelope; timeout_s: 0 returns one snapshot and timeout_s: 1-45 waits up to that many seconds, returning the current status (waiting or in_progress) if a snapshot was read and no attention is found. A timeout with either of those statuses is not a failure; waiting and in_progress end this slice, and the next call is stack_wait again with the same cursor. A timeout of failed means no assessment was produced. This tool is read-only. Attention never calls for changing a PR's GitHub base.",
1224
1255
  inputSchema: stackWaitSchema
1225
1256
  },
1226
1257
  async (args, extra) => {
@@ -1253,7 +1284,7 @@ ${MCP_STACK_WATCH_INSTRUCTIONS}` }
1253
1284
  "settings_get",
1254
1285
  {
1255
1286
  title: "Get settings",
1256
- description: "Read the Bearer /api/v1/settings toggles, including auto_patch_enabled and cyclone_connected. This tool is read-only."
1287
+ description: "Read the Bearer /api/v1/settings toggles, including auto_patch_enabled, auto_land_settle_seconds (how long Auto land waits before queueing), and cyclone_connected. This tool is read-only."
1257
1288
  },
1258
1289
  async () => {
1259
1290
  try {
@@ -1267,12 +1298,12 @@ ${MCP_STACK_WATCH_INSTRUCTIONS}` }
1267
1298
  "settings_set",
1268
1299
  {
1269
1300
  title: "Update settings",
1270
- description: "Update writable Bearer /api/v1/settings values and return the stored result. At least one key is required. cyclone_connected and github_connected are read-only and cannot be set.",
1301
+ description: "Update writable Bearer /api/v1/settings values and return the stored result. At least one key is required. auto_land_settle_seconds is a whole number of seconds from 15 through 300; it applies to settle clocks that start after the write. cyclone_connected and github_connected are read-only and cannot be set.",
1271
1302
  inputSchema: {
1272
1303
  ...Object.fromEntries(
1273
1304
  BEARER_SETTINGS2.map((row) => [
1274
1305
  row.key,
1275
- ("kind" in row ? row.kind === "logins" ? z3.array(z3.string()) : z3.enum(row.values) : z3.boolean()).optional()
1306
+ ("kind" in row ? row.kind === "logins" ? z3.array(z3.string()) : row.kind === "seconds" ? z3.number().int().min(row.min).max(row.max) : z3.enum(row.values) : z3.boolean()).optional()
1276
1307
  ])
1277
1308
  ),
1278
1309
  // Preserve these args so the handler rejects them even alongside a writable key.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mergestorm-mcp",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
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.22",
32
+ "mergestorm": "0.3.24",
33
33
  "zod": "^3.24.2"
34
34
  },
35
35
  "devDependencies": {