mergestorm-mcp 0.2.0 → 0.2.1
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/dist/index.js +159 -96
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -672,11 +672,100 @@ async function settingsSet(args, cfg) {
|
|
|
672
672
|
}
|
|
673
673
|
|
|
674
674
|
// src/tools/stack-adopt.ts
|
|
675
|
-
import { adoptStack, CommandError as CommandError9 } from "mergestorm/client";
|
|
675
|
+
import { adoptStack, CommandError as CommandError9, stackWatchObligation as stackWatchObligation3 } from "mergestorm/client";
|
|
676
|
+
import { z as z2 } from "zod";
|
|
677
|
+
|
|
678
|
+
// src/tools/stack-set.ts
|
|
679
|
+
import { setStackPolicy, stackWatchObligation as stackWatchObligation2 } from "mergestorm/client";
|
|
680
|
+
|
|
681
|
+
// src/tools/stack-wait.ts
|
|
682
|
+
import {
|
|
683
|
+
loadConfig as loadConfig6,
|
|
684
|
+
stackBlockersSummary,
|
|
685
|
+
pollStackWatch,
|
|
686
|
+
StackWatchError,
|
|
687
|
+
StackWatchTimeoutError,
|
|
688
|
+
stackWatchObligation
|
|
689
|
+
} from "mergestorm/client";
|
|
676
690
|
import { z } from "zod";
|
|
691
|
+
var stackWaitSchema = {
|
|
692
|
+
stack_id: z.string().trim().min(1),
|
|
693
|
+
timeout_s: z.number().finite().min(0).max(45).default(45),
|
|
694
|
+
enrolled_head_sha: z.string().nullable().optional(),
|
|
695
|
+
after_finished_at: z.string().nullable().optional(),
|
|
696
|
+
bounce_id: z.string().nullable().optional()
|
|
697
|
+
};
|
|
698
|
+
function repairSummary(repair) {
|
|
699
|
+
if (!repair) return "";
|
|
700
|
+
const files = "files" in repair && repair.files.length ? ` Files: ${repair.files.join(", ")}.` : "";
|
|
701
|
+
return `
|
|
702
|
+
Repair #${repair.prNumber} (${repair.kind}).${files} ${repair.steps}`;
|
|
703
|
+
}
|
|
704
|
+
function withWatchText(watch, body) {
|
|
705
|
+
return `${watch.message}
|
|
706
|
+
|
|
707
|
+
${body}`;
|
|
708
|
+
}
|
|
709
|
+
function envelopeWatch(envelope) {
|
|
710
|
+
return envelope.watch ?? stackWatchObligation({
|
|
711
|
+
stackId: envelope.stackId,
|
|
712
|
+
terminal: null,
|
|
713
|
+
cursor: envelope.cursor,
|
|
714
|
+
status: envelope.status
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
function stackWaitSummary(envelope) {
|
|
718
|
+
return withWatchText(envelopeWatch(envelope), `${stackWaitStatusLine(envelope)}${repairSummary(envelope.repair)}`);
|
|
719
|
+
}
|
|
720
|
+
function stackWaitStatusLine(envelope) {
|
|
721
|
+
const base = `Stack ${envelope.stackId} \xB7 ${envelope.status}${stackBlockersSummary(envelope.blocker && envelope.prNumber != null ? { prNumber: envelope.prNumber, blocker: envelope.blocker } : null, envelope.issues, envelope.busy, envelope.landGatePending)}${envelope.assessment === "unavailable" ? " \xB7 assessment unavailable" : ""}`;
|
|
722
|
+
if (envelopeWatch(envelope).done || envelope.status !== "waiting" && envelope.status !== "in_progress") return base;
|
|
723
|
+
const selectors = [
|
|
724
|
+
`stack_id ${JSON.stringify(envelope.cursor.stackId)}`,
|
|
725
|
+
`enrolled_head_sha ${JSON.stringify(envelope.cursor.enrolledHeadSha)}`,
|
|
726
|
+
...envelope.cursor.afterFinishedAt !== void 0 ? [`after_finished_at ${JSON.stringify(envelope.cursor.afterFinishedAt)}`] : [],
|
|
727
|
+
...envelope.cursor.bounceId !== void 0 ? [`bounce_id ${JSON.stringify(envelope.cursor.bounceId)}`] : []
|
|
728
|
+
];
|
|
729
|
+
return `${base} \xB7 watch not finished: call stack_wait again with timeout_s 45 and the same cursor: ${selectors.join(", ")}`;
|
|
730
|
+
}
|
|
731
|
+
async function stackWait(input, cfg, deps = {}) {
|
|
732
|
+
const args = z.object(stackWaitSchema).parse(input);
|
|
733
|
+
const cursor = args.enrolled_head_sha !== void 0 || args.after_finished_at !== void 0 || args.bounce_id !== void 0 ? {
|
|
734
|
+
stackId: args.stack_id,
|
|
735
|
+
enrolledHeadSha: args.enrolled_head_sha ?? null,
|
|
736
|
+
...args.after_finished_at !== void 0 ? { afterFinishedAt: args.after_finished_at } : {},
|
|
737
|
+
...args.bounce_id !== void 0 ? { bounceId: args.bounce_id } : {}
|
|
738
|
+
} : void 0;
|
|
739
|
+
let envelope;
|
|
740
|
+
try {
|
|
741
|
+
envelope = await (deps.pollStackWatch ?? pollStackWatch)(cfg ?? await loadConfig6(), args.stack_id, {
|
|
742
|
+
timeoutMs: args.timeout_s * 1e3,
|
|
743
|
+
cursor,
|
|
744
|
+
signal: deps.signal
|
|
745
|
+
});
|
|
746
|
+
} catch (err) {
|
|
747
|
+
if (err instanceof StackWatchError) {
|
|
748
|
+
const envelope2 = { ...err.lastEnvelope, watch: envelopeWatch(err.lastEnvelope) };
|
|
749
|
+
return {
|
|
750
|
+
summary: stackWaitSummary(envelope2),
|
|
751
|
+
data: {
|
|
752
|
+
...envelope2,
|
|
753
|
+
...err.retryAfterSeconds !== void 0 ? { retry_after_seconds: err.retryAfterSeconds } : {}
|
|
754
|
+
},
|
|
755
|
+
isError: !envelope2.watch.done
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
if (!(err instanceof StackWatchTimeoutError)) throw err;
|
|
759
|
+
envelope = err.lastEnvelope;
|
|
760
|
+
}
|
|
761
|
+
const sealed = { ...envelope, watch: envelopeWatch(envelope) };
|
|
762
|
+
return {
|
|
763
|
+
summary: stackWaitSummary(sealed),
|
|
764
|
+
data: sealed
|
|
765
|
+
};
|
|
766
|
+
}
|
|
677
767
|
|
|
678
768
|
// src/tools/stack-set.ts
|
|
679
|
-
import { setStackPolicy } from "mergestorm/client";
|
|
680
769
|
function triWord(value) {
|
|
681
770
|
return value === null ? "default" : value ? "on" : "off";
|
|
682
771
|
}
|
|
@@ -713,28 +802,30 @@ async function stackSet(stackId, input, cfg) {
|
|
|
713
802
|
if (input.auto_patch !== void 0) {
|
|
714
803
|
parts.push(`auto-patch ${triWord(input.auto_patch)}`);
|
|
715
804
|
}
|
|
805
|
+
const watch = stackWatchObligation2({ stackId, terminal: null, unread: true, freshCursor: true });
|
|
716
806
|
return {
|
|
717
|
-
summary: `${parts.join(", ")} for stack ${stackId}
|
|
807
|
+
summary: withWatchText(watch, `${parts.join(", ")} for stack ${stackId}`),
|
|
718
808
|
data: {
|
|
719
809
|
stack_id: stackId,
|
|
720
810
|
...typeof input.auto_land === "boolean" ? { auto_land: input.auto_land } : {},
|
|
721
811
|
...input.auto_review !== void 0 ? { auto_review: input.auto_review } : {},
|
|
722
812
|
...input.auto_patch !== void 0 ? { auto_patch: input.auto_patch } : {},
|
|
723
|
-
result
|
|
813
|
+
result,
|
|
814
|
+
watch
|
|
724
815
|
}
|
|
725
816
|
};
|
|
726
817
|
}
|
|
727
818
|
|
|
728
819
|
// src/tools/stack-adopt.ts
|
|
729
820
|
var stackAdoptSchema = {
|
|
730
|
-
owner:
|
|
731
|
-
repo:
|
|
732
|
-
pr_number:
|
|
733
|
-
auto_land:
|
|
734
|
-
auto_review:
|
|
735
|
-
auto_patch:
|
|
821
|
+
owner: z2.string().trim().min(1),
|
|
822
|
+
repo: z2.string().trim().min(1),
|
|
823
|
+
pr_number: z2.number().int().min(1),
|
|
824
|
+
auto_land: z2.boolean().optional(),
|
|
825
|
+
auto_review: z2.boolean().nullable().optional(),
|
|
826
|
+
auto_patch: z2.boolean().nullable().optional()
|
|
736
827
|
};
|
|
737
|
-
var inputSchema =
|
|
828
|
+
var inputSchema = z2.object(stackAdoptSchema);
|
|
738
829
|
async function stackAdopt(input, cfg) {
|
|
739
830
|
const parsed = inputSchema.safeParse(input);
|
|
740
831
|
if (!parsed.success) {
|
|
@@ -750,9 +841,15 @@ async function stackAdopt(input, cfg) {
|
|
|
750
841
|
cfg,
|
|
751
842
|
stackSetPatch(parsed.data)
|
|
752
843
|
);
|
|
844
|
+
const summary = `Adopted stack for ${owner}/${repo}#${pr_number}`;
|
|
845
|
+
const stackId = typeof result === "object" && result !== null ? result.stack?.id : void 0;
|
|
846
|
+
if (typeof stackId !== "string" || !stackId) {
|
|
847
|
+
return { summary, data: { owner, repo, pr_number, result } };
|
|
848
|
+
}
|
|
849
|
+
const watch = stackWatchObligation3({ stackId, terminal: null, unread: true, freshCursor: true });
|
|
753
850
|
return {
|
|
754
|
-
summary:
|
|
755
|
-
data: { owner, repo, pr_number, result }
|
|
851
|
+
summary: withWatchText(watch, summary),
|
|
852
|
+
data: { owner, repo, pr_number, result, watch }
|
|
756
853
|
};
|
|
757
854
|
} catch (err) {
|
|
758
855
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -778,7 +875,7 @@ import {
|
|
|
778
875
|
} from "mergestorm/client";
|
|
779
876
|
|
|
780
877
|
// src/tools/stack-summary.ts
|
|
781
|
-
import { stackBlockers, stackBlockersSummary } from "mergestorm/client";
|
|
878
|
+
import { stackBlockers, stackBlockersSummary as stackBlockersSummary2 } from "mergestorm/client";
|
|
782
879
|
function stackSummary(stack, entries = []) {
|
|
783
880
|
const count = stack.layers.length;
|
|
784
881
|
const layerLabel = `${count} layer${count === 1 ? "" : "s"}`;
|
|
@@ -794,8 +891,8 @@ function stackSummary(stack, entries = []) {
|
|
|
794
891
|
if (stack.cycloneOwnerMatch) {
|
|
795
892
|
overrides.push(`cyclone-owner ${stack.cycloneOwnerMatch}`);
|
|
796
893
|
}
|
|
797
|
-
const { attention, issues, busy } = stackBlockers(stack, entries);
|
|
798
|
-
return `${stack.owner}/${stack.repo} \xB7 ${layerLabel} \xB7 ${currentState} \xB7 auto-land ${stack.autoEnqueueWhenReady ? "on" : "off"}${overrides.map((label) => ` \xB7 ${label}`).join("")}${
|
|
894
|
+
const { attention, issues, busy, landGatePending } = stackBlockers(stack, entries);
|
|
895
|
+
return `${stack.owner}/${stack.repo} \xB7 ${layerLabel} \xB7 ${currentState} \xB7 auto-land ${stack.autoEnqueueWhenReady ? "on" : "off"}${overrides.map((label) => ` \xB7 ${label}`).join("")}${stackBlockersSummary2(attention, issues, busy, landGatePending)}`;
|
|
799
896
|
}
|
|
800
897
|
|
|
801
898
|
// src/tools/stack-list.ts
|
|
@@ -825,108 +922,65 @@ async function stackList(cfg) {
|
|
|
825
922
|
}
|
|
826
923
|
}
|
|
827
924
|
|
|
828
|
-
// src/tools/stack-wait.ts
|
|
829
|
-
import {
|
|
830
|
-
loadConfig as loadConfig6,
|
|
831
|
-
stackBlockersSummary as stackBlockersSummary2,
|
|
832
|
-
pollStackWatch,
|
|
833
|
-
StackWatchError,
|
|
834
|
-
StackWatchTimeoutError
|
|
835
|
-
} from "mergestorm/client";
|
|
836
|
-
import { z as z2 } from "zod";
|
|
837
|
-
var stackWaitSchema = {
|
|
838
|
-
stack_id: z2.string().trim().min(1),
|
|
839
|
-
timeout_s: z2.number().finite().min(0).max(45).default(45),
|
|
840
|
-
enrolled_head_sha: z2.string().nullable().optional(),
|
|
841
|
-
after_finished_at: z2.string().nullable().optional(),
|
|
842
|
-
bounce_id: z2.string().nullable().optional()
|
|
843
|
-
};
|
|
844
|
-
function stackWaitSummary(envelope) {
|
|
845
|
-
const base = `Stack ${envelope.stackId} \xB7 ${envelope.status}${stackBlockersSummary2(envelope.blocker && envelope.prNumber != null ? { prNumber: envelope.prNumber, blocker: envelope.blocker } : null, envelope.issues, envelope.busy)}${envelope.assessment === "unavailable" ? " \xB7 assessment unavailable" : ""}`;
|
|
846
|
-
if (envelope.status !== "waiting" && envelope.status !== "in_progress") return base;
|
|
847
|
-
const selectors = [
|
|
848
|
-
`stack_id ${JSON.stringify(envelope.cursor.stackId)}`,
|
|
849
|
-
`enrolled_head_sha ${JSON.stringify(envelope.cursor.enrolledHeadSha)}`,
|
|
850
|
-
...envelope.cursor.afterFinishedAt !== void 0 ? [`after_finished_at ${JSON.stringify(envelope.cursor.afterFinishedAt)}`] : [],
|
|
851
|
-
...envelope.cursor.bounceId !== void 0 ? [`bounce_id ${JSON.stringify(envelope.cursor.bounceId)}`] : []
|
|
852
|
-
];
|
|
853
|
-
return `${base} \xB7 watch not finished: call stack_wait again with timeout_s 45 and the same cursor: ${selectors.join(", ")}`;
|
|
854
|
-
}
|
|
855
|
-
async function stackWait(input, cfg, deps = {}) {
|
|
856
|
-
const args = z2.object(stackWaitSchema).parse(input);
|
|
857
|
-
const cursor = args.enrolled_head_sha !== void 0 || args.after_finished_at !== void 0 || args.bounce_id !== void 0 ? {
|
|
858
|
-
stackId: args.stack_id,
|
|
859
|
-
enrolledHeadSha: args.enrolled_head_sha ?? null,
|
|
860
|
-
...args.after_finished_at !== void 0 ? { afterFinishedAt: args.after_finished_at } : {},
|
|
861
|
-
...args.bounce_id !== void 0 ? { bounceId: args.bounce_id } : {}
|
|
862
|
-
} : void 0;
|
|
863
|
-
let envelope;
|
|
864
|
-
try {
|
|
865
|
-
envelope = await (deps.pollStackWatch ?? pollStackWatch)(cfg ?? await loadConfig6(), args.stack_id, {
|
|
866
|
-
timeoutMs: args.timeout_s * 1e3,
|
|
867
|
-
cursor,
|
|
868
|
-
signal: deps.signal
|
|
869
|
-
});
|
|
870
|
-
} catch (err) {
|
|
871
|
-
if (err instanceof StackWatchError) {
|
|
872
|
-
return {
|
|
873
|
-
summary: stackWaitSummary(err.lastEnvelope),
|
|
874
|
-
data: {
|
|
875
|
-
...err.lastEnvelope,
|
|
876
|
-
...err.retryAfterSeconds !== void 0 ? { retry_after_seconds: err.retryAfterSeconds } : {}
|
|
877
|
-
},
|
|
878
|
-
isError: true
|
|
879
|
-
};
|
|
880
|
-
}
|
|
881
|
-
if (!(err instanceof StackWatchTimeoutError)) throw err;
|
|
882
|
-
envelope = err.lastEnvelope;
|
|
883
|
-
}
|
|
884
|
-
return {
|
|
885
|
-
summary: stackWaitSummary(envelope),
|
|
886
|
-
data: envelope
|
|
887
|
-
};
|
|
888
|
-
}
|
|
889
|
-
|
|
890
925
|
// src/tools/stack-status.ts
|
|
891
926
|
import {
|
|
892
927
|
getEnrichedStack,
|
|
893
928
|
listMergeQueueEntries as listMergeQueueEntries2,
|
|
894
929
|
isCommandErrorCode as isCommandErrorCode5,
|
|
895
|
-
stackBlockers as stackBlockers2
|
|
930
|
+
stackBlockers as stackBlockers2,
|
|
931
|
+
stackTerminalReason,
|
|
932
|
+
stackWatchObligation as stackWatchObligation4
|
|
896
933
|
} from "mergestorm/client";
|
|
897
934
|
async function stackStatus(stackId, cfg) {
|
|
898
935
|
try {
|
|
899
936
|
const stack = await getEnrichedStack(stackId, cfg);
|
|
900
937
|
if (!stack) {
|
|
901
938
|
const message = `Stack not found or not owned by the current user: ${stackId}`;
|
|
939
|
+
const watch2 = stackWatchObligation4({ stackId, terminal: "not_found" });
|
|
902
940
|
return {
|
|
903
|
-
summary: message,
|
|
941
|
+
summary: withWatchText(watch2, message),
|
|
904
942
|
data: {
|
|
905
943
|
error: {
|
|
906
944
|
code: "stack_not_found",
|
|
907
945
|
message,
|
|
908
946
|
stack_id: stackId
|
|
909
|
-
}
|
|
947
|
+
},
|
|
948
|
+
watch: watch2
|
|
910
949
|
},
|
|
911
950
|
isError: true
|
|
912
951
|
};
|
|
913
952
|
}
|
|
914
953
|
const entries = await listMergeQueueEntries2(cfg, { stackId: stack.id });
|
|
915
|
-
const { attention, issues, currentCandidate, busy, agents } = stackBlockers2(stack, entries);
|
|
954
|
+
const { attention, issues, currentCandidate, busy, agents, repair, landGatePending } = stackBlockers2(stack, entries);
|
|
955
|
+
const watch = stackWatchObligation4({
|
|
956
|
+
stackId: stack.id,
|
|
957
|
+
terminal: stackTerminalReason(stack),
|
|
958
|
+
cursor: { enrolledHeadSha: currentCandidate?.headSha ?? null },
|
|
959
|
+
attention: attention ? { prNumber: attention.prNumber, blocker: attention.blocker } : null,
|
|
960
|
+
freshCursor: true
|
|
961
|
+
});
|
|
916
962
|
return {
|
|
917
|
-
summary: stackSummary(stack, entries),
|
|
918
|
-
data: { stack, attention, issues, currentCandidate, busy, agents }
|
|
963
|
+
summary: withWatchText(watch, `${stackSummary(stack, entries)}${repairSummary(repair)}`),
|
|
964
|
+
data: { stack, attention, issues, currentCandidate, busy, agents, repair, landGatePending, watch }
|
|
919
965
|
};
|
|
920
966
|
} catch (err) {
|
|
921
967
|
if (isCommandErrorCode5(err, "rate_limited")) {
|
|
968
|
+
const watch = stackWatchObligation4({
|
|
969
|
+
stackId,
|
|
970
|
+
terminal: null,
|
|
971
|
+
status: "rate_limited",
|
|
972
|
+
retryAfterSeconds: err.retryAfterSeconds,
|
|
973
|
+
freshCursor: true
|
|
974
|
+
});
|
|
922
975
|
return {
|
|
923
|
-
summary: err.message,
|
|
976
|
+
summary: withWatchText(watch, err.message),
|
|
924
977
|
data: {
|
|
925
978
|
error: {
|
|
926
979
|
code: "rate_limited",
|
|
927
980
|
message: err.message,
|
|
928
981
|
retry_after_seconds: err.retryAfterSeconds
|
|
929
|
-
}
|
|
982
|
+
},
|
|
983
|
+
watch
|
|
930
984
|
},
|
|
931
985
|
isError: true
|
|
932
986
|
};
|
|
@@ -984,6 +1038,12 @@ var MCP_PR_LOOP_INSTRUCTIONS = [
|
|
|
984
1038
|
`- 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).`,
|
|
985
1039
|
"- Do not prefix that comment with cyclone-outcome: (reserved)."
|
|
986
1040
|
].join("\n");
|
|
1041
|
+
var MCP_STACK_DONE_INSTRUCTIONS = [
|
|
1042
|
+
"When stack work is done:",
|
|
1043
|
+
"- Stack work is done only when the stack is landed, or when the human takes it back (archives it or tells you to stop). A clean push, a submit, an adopt, or one merged layer is not done: merging a lower layer can break a layer above it.",
|
|
1044
|
+
"- Every stack_wait, stack_status, stack_adopt, and stack_set result carries watch: {done, until, reason, next, message}. While watch.done is false, keep calling stack_wait with watch.next.args (the returned cursor), 45s at a time, and do not end your task. Stop only when watch.done is true.",
|
|
1045
|
+
"- attention means something for you to fix; repair, when present, names the concrete fix. A pending land gate (landGatePending: ci_pending, tempest_pending, tempest_running) is a wait, not attention."
|
|
1046
|
+
].join("\n");
|
|
987
1047
|
var MCP_STACK_BASE_INSTRUCTIONS = [
|
|
988
1048
|
"Stack PR bases (Mergestorm sets these; leave them):",
|
|
989
1049
|
"- 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>.",
|
|
@@ -1007,7 +1067,14 @@ var MCP_STACK_WATCH_INSTRUCTIONS = [
|
|
|
1007
1067
|
|
|
1008
1068
|
// src/server.ts
|
|
1009
1069
|
var MCP_SERVER_NAME = "mergestorm";
|
|
1010
|
-
var MCP_SERVER_VERSION = "0.2.
|
|
1070
|
+
var MCP_SERVER_VERSION = "0.2.1";
|
|
1071
|
+
var MCP_SERVER_INSTRUCTIONS = [
|
|
1072
|
+
MCP_STACK_DONE_INSTRUCTIONS,
|
|
1073
|
+
MCP_PR_LOOP_INSTRUCTIONS,
|
|
1074
|
+
MCP_STACK_BASE_INSTRUCTIONS,
|
|
1075
|
+
MCP_STACK_WATCH_INSTRUCTIONS
|
|
1076
|
+
].join("\n\n");
|
|
1077
|
+
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.';
|
|
1011
1078
|
function stripNulls(value) {
|
|
1012
1079
|
if (Array.isArray(value)) {
|
|
1013
1080
|
return value.map((item) => stripNulls(item));
|
|
@@ -1044,11 +1111,7 @@ function createMergestormMcpServer() {
|
|
|
1044
1111
|
websiteUrl: MCP_WEBSITE_URL,
|
|
1045
1112
|
icons: MCP_SERVER_ICONS
|
|
1046
1113
|
},
|
|
1047
|
-
{ instructions:
|
|
1048
|
-
|
|
1049
|
-
${MCP_STACK_BASE_INSTRUCTIONS}
|
|
1050
|
-
|
|
1051
|
-
${MCP_STACK_WATCH_INSTRUCTIONS}` }
|
|
1114
|
+
{ instructions: MCP_SERVER_INSTRUCTIONS }
|
|
1052
1115
|
);
|
|
1053
1116
|
const addTool = server2.registerTool.bind(server2);
|
|
1054
1117
|
addTool(
|
|
@@ -1185,7 +1248,7 @@ ${MCP_STACK_WATCH_INSTRUCTIONS}` }
|
|
|
1185
1248
|
"stack_adopt",
|
|
1186
1249
|
{
|
|
1187
1250
|
title: "Adopt PR stack",
|
|
1188
|
-
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.",
|
|
1251
|
+
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." + STACK_WATCH_RESULT_NOTE,
|
|
1189
1252
|
inputSchema: stackAdoptSchema,
|
|
1190
1253
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false }
|
|
1191
1254
|
},
|
|
@@ -1216,7 +1279,7 @@ ${MCP_STACK_WATCH_INSTRUCTIONS}` }
|
|
|
1216
1279
|
"stack_set",
|
|
1217
1280
|
{
|
|
1218
1281
|
title: "Set stack policy",
|
|
1219
|
-
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.",
|
|
1282
|
+
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,
|
|
1220
1283
|
inputSchema: {
|
|
1221
1284
|
stack_id: z3.string().min(1),
|
|
1222
1285
|
auto_land: z3.boolean().optional(),
|
|
@@ -1243,7 +1306,7 @@ ${MCP_STACK_WATCH_INSTRUCTIONS}` }
|
|
|
1243
1306
|
"stack_status",
|
|
1244
1307
|
{
|
|
1245
1308
|
title: "Get stack status",
|
|
1246
|
-
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. 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.",
|
|
1309
|
+
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. 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. repair names the concrete fix for attention; landGatePending is a wait, not attention." + STACK_WATCH_RESULT_NOTE,
|
|
1247
1310
|
inputSchema: {
|
|
1248
1311
|
stack_id: z3.string().min(1)
|
|
1249
1312
|
},
|
|
@@ -1261,7 +1324,7 @@ ${MCP_STACK_WATCH_INSTRUCTIONS}` }
|
|
|
1261
1324
|
"stack_wait",
|
|
1262
1325
|
{
|
|
1263
1326
|
title: "Wait for stack attention",
|
|
1264
|
-
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. While Cyclone or Vortex is still working on the blocked PR it returns in_progress, not attention, with busy[] naming the PR, blocker, and agent; agents carries that PR's vortexStatus, cycloneStatus, vortexReview, and busy flags. Attention never calls for changing a PR's GitHub base.",
|
|
1327
|
+
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. A stack that no longer exists returns status failed with watch.done true (reason not_found) and is not an error. While Cyclone or Vortex is still working on the blocked PR it returns in_progress, not attention, with busy[] naming the PR, blocker, and agent; agents carries that PR's vortexStatus, cycloneStatus, vortexReview, and busy flags. Attention never calls for changing a PR's GitHub base. repair names the concrete fix for attention (restack_conflict and merge_conflict carry liveParent, never mg-park-*). A pending land gate (landGatePending) returns in_progress, not attention." + STACK_WATCH_RESULT_NOTE,
|
|
1265
1328
|
inputSchema: stackWaitSchema,
|
|
1266
1329
|
annotations: { readOnlyHint: true }
|
|
1267
1330
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mergestorm-mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
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.
|
|
32
|
+
"mergestorm": "0.3.26",
|
|
33
33
|
"zod": "^3.24.2"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|