mergestorm-mcp 0.2.25 → 0.2.27
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 +17 -4
- package/dist/index.js +456 -101
- 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 +13 -0
- package/dist/lib/pr-loop-instructions.js +62 -0
- package/dist/lib/server.d.ts +22 -0
- package/dist/lib/server.js +558 -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-wake.d.ts +21 -0
- package/dist/lib/tools/chat-wake.js +26 -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 +29 -0
- package/dist/lib/tools/cyclone-job.js +70 -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/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 +54 -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 +21 -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 +74 -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 +24 -0
- package/dist/lib/tools/stack-wait.js +84 -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 +11 -3
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { publishWorkingBranch, workingBranchPublication, type Config } from "mergestorm/client";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import type { ToolPayload } from "./types.js";
|
|
4
|
+
export declare const stackPublishSchema: {
|
|
5
|
+
base: z.ZodOptional<z.ZodString>;
|
|
6
|
+
extend: z.ZodOptional<z.ZodBoolean>;
|
|
7
|
+
title: z.ZodOptional<z.ZodString>;
|
|
8
|
+
body: z.ZodOptional<z.ZodString>;
|
|
9
|
+
replace_head: z.ZodOptional<z.ZodString>;
|
|
10
|
+
auto_land: z.ZodOptional<z.ZodBoolean>;
|
|
11
|
+
cwd: z.ZodOptional<z.ZodString>;
|
|
12
|
+
owner: z.ZodOptional<z.ZodString>;
|
|
13
|
+
repo: z.ZodOptional<z.ZodString>;
|
|
14
|
+
branch: z.ZodOptional<z.ZodString>;
|
|
15
|
+
};
|
|
16
|
+
export declare const stackPublishStatusSchema: {
|
|
17
|
+
cwd: z.ZodOptional<z.ZodString>;
|
|
18
|
+
owner: z.ZodOptional<z.ZodString>;
|
|
19
|
+
repo: z.ZodOptional<z.ZodString>;
|
|
20
|
+
branch: z.ZodOptional<z.ZodString>;
|
|
21
|
+
};
|
|
22
|
+
declare const publishInput: z.ZodObject<{
|
|
23
|
+
base: z.ZodOptional<z.ZodString>;
|
|
24
|
+
extend: z.ZodOptional<z.ZodBoolean>;
|
|
25
|
+
title: z.ZodOptional<z.ZodString>;
|
|
26
|
+
body: z.ZodOptional<z.ZodString>;
|
|
27
|
+
replace_head: z.ZodOptional<z.ZodString>;
|
|
28
|
+
auto_land: z.ZodOptional<z.ZodBoolean>;
|
|
29
|
+
cwd: z.ZodOptional<z.ZodString>;
|
|
30
|
+
owner: z.ZodOptional<z.ZodString>;
|
|
31
|
+
repo: z.ZodOptional<z.ZodString>;
|
|
32
|
+
branch: z.ZodOptional<z.ZodString>;
|
|
33
|
+
}, "strip", z.ZodTypeAny, {
|
|
34
|
+
owner?: string | undefined;
|
|
35
|
+
title?: string | undefined;
|
|
36
|
+
body?: string | undefined;
|
|
37
|
+
repo?: string | undefined;
|
|
38
|
+
cwd?: string | undefined;
|
|
39
|
+
branch?: string | undefined;
|
|
40
|
+
auto_land?: boolean | undefined;
|
|
41
|
+
base?: string | undefined;
|
|
42
|
+
extend?: boolean | undefined;
|
|
43
|
+
replace_head?: string | undefined;
|
|
44
|
+
}, {
|
|
45
|
+
owner?: string | undefined;
|
|
46
|
+
title?: string | undefined;
|
|
47
|
+
body?: string | undefined;
|
|
48
|
+
repo?: string | undefined;
|
|
49
|
+
cwd?: string | undefined;
|
|
50
|
+
branch?: string | undefined;
|
|
51
|
+
auto_land?: boolean | undefined;
|
|
52
|
+
base?: string | undefined;
|
|
53
|
+
extend?: boolean | undefined;
|
|
54
|
+
replace_head?: string | undefined;
|
|
55
|
+
}>;
|
|
56
|
+
declare const statusInput: z.ZodObject<{
|
|
57
|
+
cwd: z.ZodOptional<z.ZodString>;
|
|
58
|
+
owner: z.ZodOptional<z.ZodString>;
|
|
59
|
+
repo: z.ZodOptional<z.ZodString>;
|
|
60
|
+
branch: z.ZodOptional<z.ZodString>;
|
|
61
|
+
}, "strip", z.ZodTypeAny, {
|
|
62
|
+
owner?: string | undefined;
|
|
63
|
+
repo?: string | undefined;
|
|
64
|
+
cwd?: string | undefined;
|
|
65
|
+
branch?: string | undefined;
|
|
66
|
+
}, {
|
|
67
|
+
owner?: string | undefined;
|
|
68
|
+
repo?: string | undefined;
|
|
69
|
+
cwd?: string | undefined;
|
|
70
|
+
branch?: string | undefined;
|
|
71
|
+
}>;
|
|
72
|
+
export type StackPublishInput = z.infer<typeof publishInput>;
|
|
73
|
+
export type StackPublishStatusInput = z.infer<typeof statusInput>;
|
|
74
|
+
type PublishDeps = Parameters<typeof publishWorkingBranch>[1];
|
|
75
|
+
type StatusDeps = Parameters<typeof workingBranchPublication>[1];
|
|
76
|
+
export declare function stackPublish(input: StackPublishInput, cfg?: Config, deps?: PublishDeps): Promise<ToolPayload>;
|
|
77
|
+
export declare function stackPublishStatus(input: StackPublishStatusInput, cfg?: Config, deps?: StatusDeps): Promise<ToolPayload>;
|
|
78
|
+
export {};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { CommandError, publishWorkingBranch, stackWatchObligation, workingBranchPublication, } from "mergestorm/client";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { withWatchText } from "./stack-wait.js";
|
|
4
|
+
const target = {
|
|
5
|
+
cwd: z.string().trim().min(1).optional(),
|
|
6
|
+
owner: z.string().trim().min(1).optional(),
|
|
7
|
+
repo: z.string().trim().min(1).optional(),
|
|
8
|
+
branch: z.string().trim().min(1).optional(),
|
|
9
|
+
};
|
|
10
|
+
export const stackPublishSchema = {
|
|
11
|
+
...target,
|
|
12
|
+
base: z.string().trim().min(1).optional(),
|
|
13
|
+
extend: z.boolean().optional(),
|
|
14
|
+
title: z.string().trim().min(1).max(256).optional(),
|
|
15
|
+
body: z.string().max(60_000).optional(),
|
|
16
|
+
replace_head: z.string().regex(/^[0-9a-f]{40}$/).optional(),
|
|
17
|
+
auto_land: z.boolean().optional(),
|
|
18
|
+
};
|
|
19
|
+
export const stackPublishStatusSchema = target;
|
|
20
|
+
const publishInput = z.object(stackPublishSchema);
|
|
21
|
+
const statusInput = z.object(stackPublishStatusSchema);
|
|
22
|
+
function failure(err, fallback) {
|
|
23
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
24
|
+
return {
|
|
25
|
+
summary: message,
|
|
26
|
+
data: {
|
|
27
|
+
error: {
|
|
28
|
+
code: err instanceof CommandError ? err.code ?? fallback : fallback,
|
|
29
|
+
message,
|
|
30
|
+
...(err instanceof CommandError && err.retryAfterSeconds !== undefined
|
|
31
|
+
? { retry_after_seconds: err.retryAfterSeconds } : {}),
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
isError: true,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function targetOf(input, cfg) {
|
|
38
|
+
return {
|
|
39
|
+
...(input.cwd ? { cwd: input.cwd } : {}),
|
|
40
|
+
...(input.owner ? { owner: input.owner } : {}),
|
|
41
|
+
...(input.repo ? { repo: input.repo } : {}),
|
|
42
|
+
...(input.branch ? { branch: input.branch } : {}),
|
|
43
|
+
...(cfg ? { cfg } : {}),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export async function stackPublish(input, cfg, deps) {
|
|
47
|
+
const parsed = publishInput.safeParse(input);
|
|
48
|
+
if (!parsed.success || Boolean(parsed.data.owner) !== Boolean(parsed.data.repo)) {
|
|
49
|
+
const message = "stack_publish takes owner and repo together, and replace_head as a full 40-character commit SHA";
|
|
50
|
+
return { summary: message, data: { error: { code: "invalid_input", message } }, isError: true };
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const result = await publishWorkingBranch({
|
|
54
|
+
...targetOf(parsed.data, cfg),
|
|
55
|
+
...(parsed.data.base ? { base: parsed.data.base } : {}),
|
|
56
|
+
...(parsed.data.extend ? { extend: true } : {}),
|
|
57
|
+
...(parsed.data.title ? { title: parsed.data.title } : {}),
|
|
58
|
+
...(parsed.data.body !== undefined ? { body: parsed.data.body } : {}),
|
|
59
|
+
...(parsed.data.replace_head ? { replaceHead: parsed.data.replace_head } : {}),
|
|
60
|
+
...(typeof parsed.data.auto_land === "boolean" ? { autoLand: parsed.data.auto_land } : {}),
|
|
61
|
+
}, deps);
|
|
62
|
+
const watch = stackWatchObligation({ stackId: result.stack.id, terminal: null, unread: true, freshCursor: true });
|
|
63
|
+
const moved = result.branchCreated ? "created" : result.branchUpdated ? "updated" : "already current";
|
|
64
|
+
return {
|
|
65
|
+
summary: withWatchText(watch, `Published ${result.owner}/${result.repo} ${result.branch} (${moved}) at ${result.head.sha}, PR #${result.pr.number} (${result.stacked ? `a layer stacked on ${result.baseBranch}` : `a single PR onto ${result.baseBranch}`}), stack ${result.stack.id}`),
|
|
66
|
+
data: { result, watch },
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
return failure(err, "stack_publish_failed");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export async function stackPublishStatus(input, cfg, deps) {
|
|
74
|
+
const parsed = statusInput.safeParse(input);
|
|
75
|
+
if (!parsed.success || Boolean(parsed.data.owner) !== Boolean(parsed.data.repo)) {
|
|
76
|
+
const message = "stack_publish_status takes owner and repo together";
|
|
77
|
+
return { summary: message, data: { error: { code: "invalid_input", message } }, isError: true };
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const status = await workingBranchPublication(targetOf(parsed.data, cfg), deps);
|
|
81
|
+
return {
|
|
82
|
+
summary: `${status.owner}/${status.repo} ${status.branch}: ${status.state}${status.head ? ` at ${status.head.sha}` : ""}${status.pr ? `, PR #${status.pr.number}` : ""}${status.stack ? `, stack ${status.stack.id}` : ""}`,
|
|
83
|
+
data: { status },
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
return failure(err, "stack_publish_status_failed");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type Config, type StackPolicyPatch } from "mergestorm/client";
|
|
2
|
+
import type { ToolPayload } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* `stack_set` input. Every key is optional but at least one must be present.
|
|
5
|
+
* `auto_land` is boolean; `auto_review` / `auto_patch` are tri-state where
|
|
6
|
+
* `null` clears the per-stack override back to the account setting.
|
|
7
|
+
*/
|
|
8
|
+
export type StackSetInput = {
|
|
9
|
+
auto_land?: boolean;
|
|
10
|
+
auto_review?: boolean | null;
|
|
11
|
+
auto_patch?: boolean | null;
|
|
12
|
+
auto_resolve_conflicts?: boolean | null;
|
|
13
|
+
auto_fix_ci?: boolean | null;
|
|
14
|
+
};
|
|
15
|
+
export declare function stackSetPatch(input: StackSetInput): StackPolicyPatch;
|
|
16
|
+
export declare function stackSet(stackId: string, input: StackSetInput, cfg?: Config): Promise<ToolPayload>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { setStackPolicy, stackWatchObligation } from "mergestorm/client";
|
|
2
|
+
import { withWatchText } from "./stack-wait.js";
|
|
3
|
+
function triWord(value) {
|
|
4
|
+
return value === null ? "default" : value ? "on" : "off";
|
|
5
|
+
}
|
|
6
|
+
export function stackSetPatch(input) {
|
|
7
|
+
const patch = {};
|
|
8
|
+
if (typeof input.auto_land === "boolean")
|
|
9
|
+
patch.autoEnqueueWhenReady = input.auto_land;
|
|
10
|
+
if (input.auto_review !== undefined)
|
|
11
|
+
patch.autoReviewOverride = input.auto_review;
|
|
12
|
+
if (input.auto_patch !== undefined)
|
|
13
|
+
patch.autoPatchOverride = input.auto_patch;
|
|
14
|
+
if (input.auto_resolve_conflicts !== undefined) {
|
|
15
|
+
patch.autoResolveConflictsOverride = input.auto_resolve_conflicts;
|
|
16
|
+
}
|
|
17
|
+
if (input.auto_fix_ci !== undefined)
|
|
18
|
+
patch.autoFixCiOverride = input.auto_fix_ci;
|
|
19
|
+
return patch;
|
|
20
|
+
}
|
|
21
|
+
export async function stackSet(stackId, input, cfg) {
|
|
22
|
+
const patch = stackSetPatch(input);
|
|
23
|
+
if (Object.keys(patch).length === 0) {
|
|
24
|
+
return {
|
|
25
|
+
summary: "stack_set needs at least one of auto_land, auto_review, auto_patch, auto_resolve_conflicts, auto_fix_ci",
|
|
26
|
+
isError: true,
|
|
27
|
+
data: {
|
|
28
|
+
error: {
|
|
29
|
+
code: "invalid_input",
|
|
30
|
+
message: "Pass at least one of auto_land, auto_review, auto_patch, auto_resolve_conflicts, auto_fix_ci.",
|
|
31
|
+
stack_id: stackId,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const result = await setStackPolicy(stackId, patch, cfg);
|
|
37
|
+
const parts = [];
|
|
38
|
+
if (typeof input.auto_land === "boolean") {
|
|
39
|
+
parts.push(`Auto land ${input.auto_land ? "on" : "off"}`);
|
|
40
|
+
}
|
|
41
|
+
if (input.auto_review !== undefined) {
|
|
42
|
+
parts.push(`auto-review ${triWord(input.auto_review)}`);
|
|
43
|
+
}
|
|
44
|
+
if (input.auto_patch !== undefined) {
|
|
45
|
+
parts.push(`auto-patch ${triWord(input.auto_patch)}`);
|
|
46
|
+
}
|
|
47
|
+
if (input.auto_resolve_conflicts !== undefined) {
|
|
48
|
+
parts.push(`auto-resolve-conflicts ${triWord(input.auto_resolve_conflicts)}`);
|
|
49
|
+
}
|
|
50
|
+
if (input.auto_fix_ci !== undefined) {
|
|
51
|
+
parts.push(`auto-fix-ci ${triWord(input.auto_fix_ci)}`);
|
|
52
|
+
}
|
|
53
|
+
const watch = stackWatchObligation({ stackId, terminal: null, unread: true, freshCursor: true });
|
|
54
|
+
return {
|
|
55
|
+
summary: withWatchText(watch, `${parts.join(", ")} for stack ${stackId}`),
|
|
56
|
+
data: {
|
|
57
|
+
stack_id: stackId,
|
|
58
|
+
...(typeof input.auto_land === "boolean" ? { auto_land: input.auto_land } : {}),
|
|
59
|
+
...(input.auto_review !== undefined ? { auto_review: input.auto_review } : {}),
|
|
60
|
+
...(input.auto_patch !== undefined ? { auto_patch: input.auto_patch } : {}),
|
|
61
|
+
...(input.auto_resolve_conflicts !== undefined
|
|
62
|
+
? { auto_resolve_conflicts: input.auto_resolve_conflicts }
|
|
63
|
+
: {}),
|
|
64
|
+
...(input.auto_fix_ci !== undefined ? { auto_fix_ci: input.auto_fix_ci } : {}),
|
|
65
|
+
result,
|
|
66
|
+
watch,
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { getEnrichedStack, listMergeQueueEntries, isCommandErrorCode, stackBlockers, stackTerminalReason, stackWatchObligation, } from "mergestorm/client";
|
|
2
|
+
import { repairSummary, withWatchText } from "./stack-wait.js";
|
|
3
|
+
import { stackSummary } from "./stack-summary.js";
|
|
4
|
+
export async function stackStatus(stackId, cfg) {
|
|
5
|
+
try {
|
|
6
|
+
const stack = await getEnrichedStack(stackId, cfg);
|
|
7
|
+
if (!stack) {
|
|
8
|
+
const message = `Stack not found or not owned by the current user: ${stackId}`;
|
|
9
|
+
const watch = stackWatchObligation({ stackId, terminal: "not_found" });
|
|
10
|
+
return {
|
|
11
|
+
summary: withWatchText(watch, message),
|
|
12
|
+
data: {
|
|
13
|
+
error: {
|
|
14
|
+
code: "stack_not_found",
|
|
15
|
+
message,
|
|
16
|
+
stack_id: stackId,
|
|
17
|
+
},
|
|
18
|
+
watch,
|
|
19
|
+
},
|
|
20
|
+
isError: true,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const entries = await listMergeQueueEntries(cfg, { stackId: stack.id });
|
|
24
|
+
const { attention, held, issues, currentCandidate, busy, agents, repair, landGatePending, queueWait } = stackBlockers(stack, entries);
|
|
25
|
+
const watch = stackWatchObligation({
|
|
26
|
+
stackId: stack.id,
|
|
27
|
+
terminal: stackTerminalReason(stack),
|
|
28
|
+
cursor: { enrolledHeadSha: currentCandidate?.headSha ?? null },
|
|
29
|
+
attention: attention ? { prNumber: attention.prNumber, blocker: attention.blocker } : null,
|
|
30
|
+
freshCursor: true,
|
|
31
|
+
});
|
|
32
|
+
return {
|
|
33
|
+
summary: withWatchText(watch, `${stackSummary(stack, entries)}${repairSummary(repair)}`),
|
|
34
|
+
data: { stack, attention, held, issues, currentCandidate, busy, agents, repair, landGatePending, queueWait, watch },
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
if (isCommandErrorCode(err, "rate_limited")) {
|
|
39
|
+
const watch = stackWatchObligation({
|
|
40
|
+
stackId, terminal: null, status: "rate_limited", retryAfterSeconds: err.retryAfterSeconds, freshCursor: true,
|
|
41
|
+
});
|
|
42
|
+
return {
|
|
43
|
+
summary: withWatchText(watch, err.message),
|
|
44
|
+
data: {
|
|
45
|
+
error: {
|
|
46
|
+
code: "rate_limited",
|
|
47
|
+
message: err.message,
|
|
48
|
+
retry_after_seconds: err.retryAfterSeconds,
|
|
49
|
+
},
|
|
50
|
+
watch,
|
|
51
|
+
},
|
|
52
|
+
isError: true,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { stackBlockers, stackBlockersSummary } from "mergestorm/client";
|
|
2
|
+
export function stackSummary(stack, entries = []) {
|
|
3
|
+
const count = stack.layers.length;
|
|
4
|
+
const layerLabel = `${count} layer${count === 1 ? "" : "s"}`;
|
|
5
|
+
const states = [...new Set(stack.layers.map((layer) => layer.state))];
|
|
6
|
+
const currentState = states.length > 0
|
|
7
|
+
? `restack: ${states.join(", ")}`
|
|
8
|
+
: stack.unit
|
|
9
|
+
? `unit: ${stack.unit.state}`
|
|
10
|
+
: "no open layers";
|
|
11
|
+
const overrides = [];
|
|
12
|
+
if (typeof stack.autoReviewOverride === "boolean") {
|
|
13
|
+
overrides.push(`auto-review ${stack.autoReviewOverride ? "on" : "off"}`);
|
|
14
|
+
}
|
|
15
|
+
if (typeof stack.autoPatchOverride === "boolean") {
|
|
16
|
+
overrides.push(`auto-patch ${stack.autoPatchOverride ? "on" : "off"}`);
|
|
17
|
+
}
|
|
18
|
+
if (typeof stack.autoResolveConflictsOverride === "boolean") {
|
|
19
|
+
overrides.push(`auto-resolve-conflicts ${stack.autoResolveConflictsOverride ? "on" : "off"}`);
|
|
20
|
+
}
|
|
21
|
+
if (typeof stack.autoFixCiOverride === "boolean") {
|
|
22
|
+
overrides.push(`auto-fix-ci ${stack.autoFixCiOverride ? "on" : "off"}`);
|
|
23
|
+
}
|
|
24
|
+
if (stack.cycloneOwnerMatch) {
|
|
25
|
+
overrides.push(`cyclone-owner ${stack.cycloneOwnerMatch}`);
|
|
26
|
+
}
|
|
27
|
+
const { attention, held, issues, busy, landGatePending, queueWait } = stackBlockers(stack, entries);
|
|
28
|
+
return `${stack.owner}/${stack.repo} · ${layerLabel} · ${currentState} · auto-land ${stack.autoEnqueueWhenReady ? "on" : "off"}${overrides.map((label) => ` · ${label}`).join("")}${stackBlockersSummary(attention ?? held, issues, busy, landGatePending, attention ? null : held?.actAfter ?? null, queueWait)}`;
|
|
29
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { pollStackWatch, type Config, type StackRepairHint, type StackWatchObligation } 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 declare function stackWait(input: StackWaitInput, cfg?: Config, deps?: {
|
|
21
|
+
pollStackWatch?: typeof pollStackWatch;
|
|
22
|
+
signal?: AbortSignal;
|
|
23
|
+
chatId?: string;
|
|
24
|
+
}): Promise<ToolPayload>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { loadConfig, stackBlockersSummary, pollStackWatch, StackWatchError, StackWatchTimeoutError, stackWatchBackgroundCommand, stackWatchObligation, } from "mergestorm/client";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { CHAT_WAKE_STACK_NOTE, registerChatWake } from "./chat-wake.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
|
+
function envelopeWatch(envelope) {
|
|
22
|
+
return envelope.watch ?? stackWatchObligation({
|
|
23
|
+
stackId: envelope.stackId, terminal: null, cursor: envelope.cursor, status: envelope.status,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function stackWaitSummary(envelope) {
|
|
27
|
+
return withWatchText(envelopeWatch(envelope), `${stackWaitStatusLine(envelope)}${repairSummary(envelope.repair)}`);
|
|
28
|
+
}
|
|
29
|
+
function stackWaitStatusLine(envelope) {
|
|
30
|
+
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" : ""}`;
|
|
31
|
+
if (envelopeWatch(envelope).done || (envelope.status !== "waiting" && envelope.status !== "in_progress"))
|
|
32
|
+
return base;
|
|
33
|
+
const selectors = [
|
|
34
|
+
`stack_id ${JSON.stringify(envelope.cursor.stackId)}`,
|
|
35
|
+
`enrolled_head_sha ${JSON.stringify(envelope.cursor.enrolledHeadSha)}`,
|
|
36
|
+
...(envelope.cursor.afterFinishedAt !== undefined ? [`after_finished_at ${JSON.stringify(envelope.cursor.afterFinishedAt)}`] : []),
|
|
37
|
+
...(envelope.cursor.bounceId !== undefined ? [`bounce_id ${JSON.stringify(envelope.cursor.bounceId)}`] : []),
|
|
38
|
+
];
|
|
39
|
+
return `${base} · watch not finished: 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 call stack_wait again with timeout_s 45 and the same cursor: ${selectors.join(", ")}`;
|
|
40
|
+
}
|
|
41
|
+
export async function stackWait(input, cfg, deps = {}) {
|
|
42
|
+
const args = z.object(stackWaitSchema).parse(input);
|
|
43
|
+
const config = cfg ?? await loadConfig();
|
|
44
|
+
const cursor = args.enrolled_head_sha !== undefined || args.after_finished_at !== undefined || args.bounce_id !== undefined
|
|
45
|
+
? {
|
|
46
|
+
stackId: args.stack_id,
|
|
47
|
+
enrolledHeadSha: args.enrolled_head_sha ?? null,
|
|
48
|
+
...(args.after_finished_at !== undefined ? { afterFinishedAt: args.after_finished_at } : {}),
|
|
49
|
+
...(args.bounce_id !== undefined ? { bounceId: args.bounce_id } : {}),
|
|
50
|
+
}
|
|
51
|
+
: undefined;
|
|
52
|
+
let envelope;
|
|
53
|
+
try {
|
|
54
|
+
envelope = await (deps.pollStackWatch ?? pollStackWatch)(config, args.stack_id, {
|
|
55
|
+
timeoutMs: args.timeout_s * 1000,
|
|
56
|
+
cursor,
|
|
57
|
+
signal: deps.signal,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
if (err instanceof StackWatchError) {
|
|
62
|
+
const envelope = { ...err.lastEnvelope, watch: envelopeWatch(err.lastEnvelope) };
|
|
63
|
+
return {
|
|
64
|
+
summary: stackWaitSummary(envelope),
|
|
65
|
+
data: {
|
|
66
|
+
...envelope,
|
|
67
|
+
...(err.retryAfterSeconds !== undefined ? { retry_after_seconds: err.retryAfterSeconds } : {}),
|
|
68
|
+
},
|
|
69
|
+
isError: !envelope.watch.done,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
if (!(err instanceof StackWatchTimeoutError))
|
|
73
|
+
throw err;
|
|
74
|
+
envelope = err.lastEnvelope;
|
|
75
|
+
}
|
|
76
|
+
const sealed = { ...envelope, watch: envelopeWatch(envelope) };
|
|
77
|
+
const wake = deps.chatId && !sealed.watch.done
|
|
78
|
+
? await registerChatWake(config, deps.chatId, { kind: "stack", stackId: sealed.stackId }, deps.signal)
|
|
79
|
+
: null;
|
|
80
|
+
return {
|
|
81
|
+
summary: wake ? `${stackWaitSummary(sealed)}\n\n${CHAT_WAKE_STACK_NOTE}` : stackWaitSummary(sealed),
|
|
82
|
+
data: wake ? { ...sealed, chat_wake: wake } : sealed,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
@@ -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.27",
|
|
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,21 @@
|
|
|
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",
|
|
18
26
|
"README.md",
|
|
19
27
|
"LICENSE"
|
|
20
28
|
],
|
|
21
29
|
"scripts": {
|
|
22
|
-
"build": "esbuild src/index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --packages=external --banner:js=\"#!/usr/bin/env node\"",
|
|
30
|
+
"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
31
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
24
32
|
"test": "tsx --test src/*.test.ts src/**/*.test.ts",
|
|
25
33
|
"start": "node dist/index.js"
|
|
@@ -29,7 +37,7 @@
|
|
|
29
37
|
},
|
|
30
38
|
"dependencies": {
|
|
31
39
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
32
|
-
"mergestorm": "0.3.
|
|
40
|
+
"mergestorm": "0.3.63",
|
|
33
41
|
"zod": "^3.24.2"
|
|
34
42
|
},
|
|
35
43
|
"devDependencies": {
|