mergestorm-mcp 0.2.26 → 0.2.28
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 +16 -2
- package/dist/index.js +828 -223
- package/dist/lib/brand-icons.d.ts +16 -0
- package/dist/lib/brand-icons.js +23 -0
- package/dist/lib/hosted.d.ts +7 -0
- package/dist/lib/hosted.js +21 -0
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.js +5 -0
- package/dist/lib/pr-loop-instructions.d.ts +14 -0
- package/dist/lib/pr-loop-instructions.js +73 -0
- package/dist/lib/server.d.ts +24 -0
- package/dist/lib/server.js +644 -0
- package/dist/lib/tools/agent-request.d.ts +6 -0
- package/dist/lib/tools/agent-request.js +25 -0
- package/dist/lib/tools/chat-task.d.ts +18 -0
- package/dist/lib/tools/chat-task.js +28 -0
- package/dist/lib/tools/credits.d.ts +3 -0
- package/dist/lib/tools/credits.js +38 -0
- package/dist/lib/tools/cyclone-job.d.ts +28 -0
- package/dist/lib/tools/cyclone-job.js +71 -0
- package/dist/lib/tools/envelope.d.ts +5 -0
- package/dist/lib/tools/envelope.js +30 -0
- package/dist/lib/tools/issue-create.d.ts +47 -0
- package/dist/lib/tools/issue-create.js +53 -0
- package/dist/lib/tools/memory.d.ts +68 -0
- package/dist/lib/tools/memory.js +74 -0
- package/dist/lib/tools/queue-status.d.ts +3 -0
- package/dist/lib/tools/queue-status.js +79 -0
- package/dist/lib/tools/repo-settings.d.ts +6 -0
- package/dist/lib/tools/repo-settings.js +78 -0
- package/dist/lib/tools/review-dismiss.d.ts +16 -0
- package/dist/lib/tools/review-dismiss.js +68 -0
- package/dist/lib/tools/review-get-pr.d.ts +22 -0
- package/dist/lib/tools/review-get-pr.js +52 -0
- package/dist/lib/tools/review-get.d.ts +3 -0
- package/dist/lib/tools/review-get.js +27 -0
- package/dist/lib/tools/review-list.d.ts +3 -0
- package/dist/lib/tools/review-list.js +10 -0
- package/dist/lib/tools/review-start.d.ts +11 -0
- package/dist/lib/tools/review-start.js +22 -0
- package/dist/lib/tools/review-submit.d.ts +36 -0
- package/dist/lib/tools/review-submit.js +172 -0
- package/dist/lib/tools/review-wait-pr.d.ts +11 -0
- package/dist/lib/tools/review-wait-pr.js +66 -0
- package/dist/lib/tools/review-wait.d.ts +6 -0
- package/dist/lib/tools/review-wait.js +33 -0
- package/dist/lib/tools/settings-get.d.ts +8 -0
- package/dist/lib/tools/settings-get.js +33 -0
- package/dist/lib/tools/settings-set.d.ts +11 -0
- package/dist/lib/tools/settings-set.js +62 -0
- package/dist/lib/tools/stack-actions.d.ts +4 -0
- package/dist/lib/tools/stack-actions.js +22 -0
- package/dist/lib/tools/stack-adopt.d.ts +44 -0
- package/dist/lib/tools/stack-adopt.js +55 -0
- package/dist/lib/tools/stack-list.d.ts +3 -0
- package/dist/lib/tools/stack-list.js +30 -0
- package/dist/lib/tools/stack-publish.d.ts +78 -0
- package/dist/lib/tools/stack-publish.js +89 -0
- package/dist/lib/tools/stack-set.d.ts +16 -0
- package/dist/lib/tools/stack-set.js +69 -0
- package/dist/lib/tools/stack-status.d.ts +3 -0
- package/dist/lib/tools/stack-status.js +57 -0
- package/dist/lib/tools/stack-summary.d.ts +2 -0
- package/dist/lib/tools/stack-summary.js +29 -0
- package/dist/lib/tools/stack-wait.d.ts +44 -0
- package/dist/lib/tools/stack-wait.js +146 -0
- package/dist/lib/tools/surge-setup.d.ts +6 -0
- package/dist/lib/tools/surge-setup.js +4 -0
- package/dist/lib/tools/types.d.ts +5 -0
- package/dist/lib/tools/types.js +1 -0
- package/dist/lib/tools/whoami.d.ts +3 -0
- package/dist/lib/tools/whoami.js +27 -0
- package/package.json +13 -3
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { pollStackWatch, type Config, type StackRepairHint, type StackWatchObligation, type StackWatchNextArgs } from "mergestorm/client";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import type { ToolPayload } from "./types.js";
|
|
4
|
+
export declare const stackWaitSchema: {
|
|
5
|
+
stack_id: z.ZodString;
|
|
6
|
+
timeout_s: z.ZodDefault<z.ZodNumber>;
|
|
7
|
+
enrolled_head_sha: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
8
|
+
after_finished_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
9
|
+
bounce_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
10
|
+
};
|
|
11
|
+
export type StackWaitInput = {
|
|
12
|
+
stack_id: string;
|
|
13
|
+
timeout_s?: number;
|
|
14
|
+
enrolled_head_sha?: string | null;
|
|
15
|
+
after_finished_at?: string | null;
|
|
16
|
+
bounce_id?: string | null;
|
|
17
|
+
};
|
|
18
|
+
export declare function repairSummary(repair: StackRepairHint | null | undefined): string;
|
|
19
|
+
export declare function withWatchText(watch: StackWatchObligation, body: string): string;
|
|
20
|
+
export type HostedStackWatch = Omit<StackWatchObligation, "next"> & {
|
|
21
|
+
next: {
|
|
22
|
+
tool: "stack_wait";
|
|
23
|
+
args: StackWatchNextArgs;
|
|
24
|
+
} | null;
|
|
25
|
+
};
|
|
26
|
+
export declare function hostedStackWatch(watch: StackWatchObligation): HostedStackWatch;
|
|
27
|
+
type McpTextResult = {
|
|
28
|
+
isError?: boolean;
|
|
29
|
+
content: {
|
|
30
|
+
type: "text";
|
|
31
|
+
text: string;
|
|
32
|
+
}[];
|
|
33
|
+
structuredContent?: Record<string, unknown>;
|
|
34
|
+
};
|
|
35
|
+
export type HostedStackRepair = Omit<StackRepairHint, "steps" | "note">;
|
|
36
|
+
export declare function hostedStackRepair(repair: StackRepairHint): HostedStackRepair;
|
|
37
|
+
export declare function hostedToolResult<T extends McpTextResult>(result: T): T;
|
|
38
|
+
export declare function stackWait(input: StackWaitInput, cfg?: Config, deps?: {
|
|
39
|
+
pollStackWatch?: typeof pollStackWatch;
|
|
40
|
+
signal?: AbortSignal;
|
|
41
|
+
chatId?: string;
|
|
42
|
+
hosted?: boolean;
|
|
43
|
+
}): Promise<ToolPayload>;
|
|
44
|
+
export {};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { loadConfig, stackBlockersSummary, pollStackWatch, StackWatchError, StackWatchTimeoutError, stackWatchBackgroundCommand, stackWatchObligation, } from "mergestorm/client";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { chatTaskNote, trackInChat } from "./chat-task.js";
|
|
4
|
+
export const stackWaitSchema = {
|
|
5
|
+
stack_id: z.string().trim().min(1),
|
|
6
|
+
timeout_s: z.number().finite().min(0).max(45).default(45),
|
|
7
|
+
enrolled_head_sha: z.string().nullable().optional(),
|
|
8
|
+
after_finished_at: z.string().nullable().optional(),
|
|
9
|
+
bounce_id: z.string().nullable().optional(),
|
|
10
|
+
};
|
|
11
|
+
export function repairSummary(repair) {
|
|
12
|
+
if (!repair)
|
|
13
|
+
return "";
|
|
14
|
+
const files = "files" in repair && repair.files.length ? ` Files: ${repair.files.join(", ")}.` : "";
|
|
15
|
+
const note = "note" in repair && repair.note ? ` ${repair.note}` : "";
|
|
16
|
+
return `\nRepair #${repair.prNumber} (${repair.kind}).${files} ${repair.steps}${note}`;
|
|
17
|
+
}
|
|
18
|
+
export function withWatchText(watch, body) {
|
|
19
|
+
return `${watch.message}\n\n${body}`;
|
|
20
|
+
}
|
|
21
|
+
const KEEP_EARLIER_CURSOR = "If you already hold a cursor from an earlier stack_wait on this stack, keep passing that cursor instead.";
|
|
22
|
+
export function hostedStackWatch(watch) {
|
|
23
|
+
if (watch.done) {
|
|
24
|
+
return {
|
|
25
|
+
...watch,
|
|
26
|
+
next: null,
|
|
27
|
+
message: watch.reason === "not_found"
|
|
28
|
+
? "No stack with this id exists for this account. Mergestorm deletes a stack once none of its layers is open, which can mean merged or closed, so landing is unconfirmed: check on GitHub that each PR is merged before reporting it landed. The watch is done."
|
|
29
|
+
: watch.message,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const args = watch.next?.args;
|
|
33
|
+
const attention = watch.reason === "attention";
|
|
34
|
+
const retry = watch.reason === "rate_limited" ? " Wait retry_after_seconds first." : "";
|
|
35
|
+
const failed = watch.reason === "failed" ? " This read failed; if the next one fails too, tell the human the stack is no longer watched." : "";
|
|
36
|
+
const keep = watch.message.includes(KEEP_EARLIER_CURSOR) ? ` ${KEEP_EARLIER_CURSOR}` : "";
|
|
37
|
+
const resume = args
|
|
38
|
+
? attention
|
|
39
|
+
? ` After the blocker is handled, call stack_wait ${JSON.stringify(args)}.`
|
|
40
|
+
: ` Next call: stack_wait ${JSON.stringify(args)}.`
|
|
41
|
+
: "";
|
|
42
|
+
return {
|
|
43
|
+
...watch,
|
|
44
|
+
next: args && !attention ? { tool: "stack_wait", args } : null,
|
|
45
|
+
message: `This stack is not landed and the watch is not done.${retry}${failed}${resume}${keep}`,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export function hostedStackRepair(repair) {
|
|
49
|
+
const { steps: _steps, note: _note, findings: _findings, ...facts } = repair;
|
|
50
|
+
return facts;
|
|
51
|
+
}
|
|
52
|
+
function hostedRepairSummary(repair) {
|
|
53
|
+
const files = "files" in repair && repair.files.length ? ` Files: ${repair.files.join(", ")}.` : "";
|
|
54
|
+
const breeze = "publishedBy" in repair && repair.publishedBy === "breeze"
|
|
55
|
+
? ` ${repair.branch} is a Breeze branch${repair.liveParent ? ` on ${repair.liveParent}` : ""}: open_pull_request publishes it with single-parent commits only, so a merge commit cannot be published.`
|
|
56
|
+
: "";
|
|
57
|
+
return `\nRepair #${repair.prNumber} (${repair.kind}).${files}${breeze}`;
|
|
58
|
+
}
|
|
59
|
+
export function hostedToolResult(result) {
|
|
60
|
+
const data = result.structuredContent;
|
|
61
|
+
const watch = data?.watch;
|
|
62
|
+
const repair = data?.repair;
|
|
63
|
+
const hasWatch = !!watch && typeof watch.message === "string";
|
|
64
|
+
const hasRepair = !!repair && typeof repair.steps === "string";
|
|
65
|
+
if (!data || (!hasWatch && !hasRepair))
|
|
66
|
+
return result;
|
|
67
|
+
let text = result.content[0]?.text ?? "";
|
|
68
|
+
const structured = { ...data };
|
|
69
|
+
if (hasWatch) {
|
|
70
|
+
const hosted = hostedStackWatch(watch);
|
|
71
|
+
structured.watch = hosted;
|
|
72
|
+
if (text.startsWith(watch.message))
|
|
73
|
+
text = `${hosted.message}${text.slice(watch.message.length)}`;
|
|
74
|
+
}
|
|
75
|
+
if (hasRepair) {
|
|
76
|
+
const hosted = hostedStackRepair(repair);
|
|
77
|
+
structured.repair = hosted;
|
|
78
|
+
text = text.replace(repairSummary(repair), hostedRepairSummary(repair));
|
|
79
|
+
}
|
|
80
|
+
return { ...result, content: [{ type: "text", text }, { type: "text", text: JSON.stringify(structured) }], structuredContent: structured };
|
|
81
|
+
}
|
|
82
|
+
function envelopeWatch(envelope) {
|
|
83
|
+
return envelope.watch ?? stackWatchObligation({
|
|
84
|
+
stackId: envelope.stackId, terminal: null, cursor: envelope.cursor, status: envelope.status,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function stackWaitSummary(envelope, hosted = false) {
|
|
88
|
+
return withWatchText(envelopeWatch(envelope), `${stackWaitStatusLine(envelope, hosted)}${repairSummary(envelope.repair)}`);
|
|
89
|
+
}
|
|
90
|
+
function stackWaitStatusLine(envelope, hosted) {
|
|
91
|
+
const base = `Stack ${envelope.stackId} · ${envelope.status}${stackBlockersSummary(envelope.blocker && envelope.prNumber != null ? { prNumber: envelope.prNumber, blocker: envelope.blocker } : null, envelope.issues, envelope.busy, envelope.landGatePending, envelope.actAfter ?? null, envelope.queueWait ?? null)}${envelope.assessment === "unavailable" ? " · assessment unavailable" : ""}`;
|
|
92
|
+
if (envelopeWatch(envelope).done || (envelope.status !== "waiting" && envelope.status !== "in_progress"))
|
|
93
|
+
return base;
|
|
94
|
+
const selectors = [
|
|
95
|
+
`stack_id ${JSON.stringify(envelope.cursor.stackId)}`,
|
|
96
|
+
`enrolled_head_sha ${JSON.stringify(envelope.cursor.enrolledHeadSha)}`,
|
|
97
|
+
...(envelope.cursor.afterFinishedAt !== undefined ? [`after_finished_at ${JSON.stringify(envelope.cursor.afterFinishedAt)}`] : []),
|
|
98
|
+
...(envelope.cursor.bounceId !== undefined ? [`bounce_id ${JSON.stringify(envelope.cursor.bounceId)}`] : []),
|
|
99
|
+
];
|
|
100
|
+
const background = hosted ? "" : `only with a confirmed host notification that resumes this task, run ${stackWatchBackgroundCommand(envelope.stackId, envelope.cursor)} as a background command and end your turn; otherwise `;
|
|
101
|
+
return `${base} · watch not finished: ${background}call stack_wait again with timeout_s 45 and the same cursor: ${selectors.join(", ")}`;
|
|
102
|
+
}
|
|
103
|
+
export async function stackWait(input, cfg, deps = {}) {
|
|
104
|
+
const args = z.object(stackWaitSchema).parse(input);
|
|
105
|
+
const config = cfg ?? await loadConfig();
|
|
106
|
+
const cursor = args.enrolled_head_sha !== undefined || args.after_finished_at !== undefined || args.bounce_id !== undefined
|
|
107
|
+
? {
|
|
108
|
+
stackId: args.stack_id,
|
|
109
|
+
enrolledHeadSha: args.enrolled_head_sha ?? null,
|
|
110
|
+
...(args.after_finished_at !== undefined ? { afterFinishedAt: args.after_finished_at } : {}),
|
|
111
|
+
...(args.bounce_id !== undefined ? { bounceId: args.bounce_id } : {}),
|
|
112
|
+
}
|
|
113
|
+
: undefined;
|
|
114
|
+
let envelope;
|
|
115
|
+
try {
|
|
116
|
+
envelope = await (deps.pollStackWatch ?? pollStackWatch)(config, args.stack_id, {
|
|
117
|
+
timeoutMs: args.timeout_s * 1000,
|
|
118
|
+
cursor,
|
|
119
|
+
signal: deps.signal,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
if (err instanceof StackWatchError) {
|
|
124
|
+
const envelope = { ...err.lastEnvelope, watch: envelopeWatch(err.lastEnvelope) };
|
|
125
|
+
return {
|
|
126
|
+
summary: stackWaitSummary(envelope, deps.hosted),
|
|
127
|
+
data: {
|
|
128
|
+
...envelope,
|
|
129
|
+
...(err.retryAfterSeconds !== undefined ? { retry_after_seconds: err.retryAfterSeconds } : {}),
|
|
130
|
+
},
|
|
131
|
+
isError: !envelope.watch.done,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
if (!(err instanceof StackWatchTimeoutError))
|
|
135
|
+
throw err;
|
|
136
|
+
envelope = err.lastEnvelope;
|
|
137
|
+
}
|
|
138
|
+
const sealed = { ...envelope, watch: envelopeWatch(envelope) };
|
|
139
|
+
const task = deps.chatId && !sealed.watch.done
|
|
140
|
+
? await trackInChat(config, deps.chatId, { kind: "stack", stackId: sealed.stackId }, deps.signal)
|
|
141
|
+
: null;
|
|
142
|
+
return {
|
|
143
|
+
summary: task ? `${stackWaitSummary(sealed, deps.hosted)}\n\n${chatTaskNote(task)}` : stackWaitSummary(sealed, deps.hosted),
|
|
144
|
+
data: task ? { ...sealed, background_task: task } : sealed,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { CommandError, apiBase, configPath, getMe, loadConfig, resolveApiKey, } from "mergestorm/client";
|
|
2
|
+
export async function whoami(cfg, serverVersion) {
|
|
3
|
+
const resolved = cfg ?? (await loadConfig());
|
|
4
|
+
const key = resolveApiKey(resolved);
|
|
5
|
+
if (!key) {
|
|
6
|
+
throw new CommandError("No API key. Run `mergestorm login` (agents: `mergestorm login --start`, then `--finish`) or set MERGESTORM_API_KEY.", 1, "missing_api_key");
|
|
7
|
+
}
|
|
8
|
+
const me = await getMe(resolved);
|
|
9
|
+
if (!me) {
|
|
10
|
+
throw new CommandError("Live account details unavailable. Could not verify account with /me.");
|
|
11
|
+
}
|
|
12
|
+
const data = {
|
|
13
|
+
key_prefix: me.key.prefix,
|
|
14
|
+
key_name: me.key.name ?? null,
|
|
15
|
+
plan_key: me.plan_key ?? null,
|
|
16
|
+
plan_label_key: me.plan_label_key ?? null,
|
|
17
|
+
api_base: apiBase(resolved),
|
|
18
|
+
config_path: resolved.ignoreEnv ? null : configPath(),
|
|
19
|
+
usage: me.usage ?? null,
|
|
20
|
+
mcp_server_version: serverVersion ?? null,
|
|
21
|
+
};
|
|
22
|
+
const plan = data.plan_label_key ?? data.plan_key ?? "unknown plan";
|
|
23
|
+
return {
|
|
24
|
+
summary: `${data.key_prefix} · ${plan}${serverVersion ? ` · mergestorm-mcp ${serverVersion}` : ""}`,
|
|
25
|
+
data,
|
|
26
|
+
};
|
|
27
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mergestorm-mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.28",
|
|
4
4
|
"description": "Mergestorm MCP server — identity, review, and read-only stack tools over stdio",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -13,13 +13,23 @@
|
|
|
13
13
|
"bin": {
|
|
14
14
|
"mergestorm-mcp": "./dist/index.js"
|
|
15
15
|
},
|
|
16
|
+
"exports": {
|
|
17
|
+
"./hosted": {
|
|
18
|
+
"mergestorm-source": "./src/hosted.ts",
|
|
19
|
+
"types": "./dist/lib/hosted.d.ts",
|
|
20
|
+
"default": "./dist/lib/hosted.js"
|
|
21
|
+
},
|
|
22
|
+
"./package.json": "./package.json"
|
|
23
|
+
},
|
|
16
24
|
"files": [
|
|
17
25
|
"dist",
|
|
26
|
+
"!dist/.tsbuildinfo",
|
|
27
|
+
"!dist/.build-inputs",
|
|
18
28
|
"README.md",
|
|
19
29
|
"LICENSE"
|
|
20
30
|
],
|
|
21
31
|
"scripts": {
|
|
22
|
-
"build": "esbuild src/index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --packages=external --banner:js=\"#!/usr/bin/env node\"",
|
|
32
|
+
"build": "esbuild src/index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --packages=external --banner:js=\"#!/usr/bin/env node\" && tsc -p tsconfig.build.json",
|
|
23
33
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
24
34
|
"test": "tsx --test src/*.test.ts src/**/*.test.ts",
|
|
25
35
|
"start": "node dist/index.js"
|
|
@@ -29,7 +39,7 @@
|
|
|
29
39
|
},
|
|
30
40
|
"dependencies": {
|
|
31
41
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
32
|
-
"mergestorm": "0.3.
|
|
42
|
+
"mergestorm": "0.3.64",
|
|
33
43
|
"zod": "^3.24.2"
|
|
34
44
|
},
|
|
35
45
|
"devDependencies": {
|