@arii/boomtick-mcp 0.4.2 → 0.5.0
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/.mcp/schemas/github.get_pr_commits.json +32 -0
- package/.mcp/schemas/github.get_pr_reviews.json +32 -0
- package/.mcp/schemas/github_get_pr_commits.json +32 -0
- package/.mcp/schemas/github_get_pr_reviews.json +32 -0
- package/.mcp/schemas/repo.get_workflow_runs.json +50 -0
- package/.mcp/schemas/repo.logs.json +14 -0
- package/.mcp/schemas/repo.read_ci_logs.json +14 -0
- package/.mcp/schemas/repo_get_workflow_runs.json +50 -0
- package/.mcp/schemas/repo_logs.json +14 -0
- package/.mcp/schemas/repo_read_ci_logs.json +14 -0
- package/CHANGELOG.md +7 -0
- package/README.md +0 -1
- package/actions/impact-analysis/action.yml +4 -16
- package/actions/run-project-gate/action.yml +2 -9
- package/actions/test-build/action.yml +1 -19
- package/dist/lib/git.js +44 -0
- package/dist/lib/git.test.js +82 -0
- package/dist/mcp/definitions.js +16 -39
- package/dist/mcp/server.js +0 -46
- package/dist/mcp/server.test.js +0 -33
- package/dist/tools/contract.js +135 -2
- package/dist/tools/github.get_merge_conflict_files.js +10 -18
- package/dist/tools/github.get_pr_commits.js +21 -0
- package/dist/tools/github.get_pr_reviews.js +21 -0
- package/dist/tools/github.get_pr_reviews.test.js +99 -0
- package/dist/tools/jules/create-session.js +1 -2
- package/dist/tools/jules/get-pr.js +1 -2
- package/dist/tools/jules/get-session.js +1 -2
- package/dist/tools/jules/shared.js +24 -0
- package/dist/tools/repo.get_changed_files.js +2 -13
- package/dist/tools/repo.get_workflow_runs.js +29 -0
- package/dist/tools/repo.logs.js +5 -1
- package/dist/tools/repo.read_agent_context.js +27 -1
- package/dist/tools/repo.read_agent_context.test.js +1 -1
- package/dist/tools/repo.read_ci_logs.js +5 -1
- package/dist/tools/repo.run_tests.js +1 -1
- package/package.json +3 -6
- package/src/agents/verifier-agent.prompt.md +0 -4
- package/src/lib/git.test.ts +110 -0
- package/src/lib/git.ts +75 -0
- package/src/mcp/definitions.ts +18 -42
- package/src/mcp/server.test.ts +0 -42
- package/src/mcp/server.ts +0 -45
- package/src/tools/contract.ts +147 -2
- package/src/tools/github.get_merge_conflict_files.ts +10 -20
- package/src/tools/github.get_pr_commits.ts +27 -0
- package/src/tools/github.get_pr_reviews.test.ts +110 -0
- package/src/tools/github.get_pr_reviews.ts +27 -0
- package/src/tools/jules/create-session.ts +1 -2
- package/src/tools/jules/get-pr.ts +1 -2
- package/src/tools/jules/get-session.ts +1 -2
- package/src/tools/jules/shared.ts +35 -0
- package/src/tools/repo.get_changed_files.ts +2 -16
- package/src/tools/repo.get_workflow_runs.ts +36 -0
- package/src/tools/repo.logs.ts +5 -1
- package/src/tools/repo.read_agent_context.test.ts +1 -1
- package/src/tools/repo.read_agent_context.ts +35 -1
- package/src/tools/repo.read_ci_logs.ts +5 -1
- package/src/tools/repo.run_tests.ts +1 -1
- package/.mcp/schemas/agent.search_ddgs.json +0 -31
- package/.mcp/schemas/agent_search_ddgs.json +0 -31
- package/.mcp/schemas/repo.get_route_map.json +0 -23
- package/.mcp/schemas/repo.run_lighthouse.json +0 -29
- package/.mcp/schemas/repo.run_playwright.json +0 -36
- package/.mcp/schemas/repo_get_route_map.json +0 -23
- package/.mcp/schemas/repo_run_lighthouse.json +0 -29
- package/.mcp/schemas/repo_run_playwright.json +0 -36
- package/dist/lib/td-cli.js +0 -54
- package/dist/tools/ddgs.search.js +0 -50
- package/dist/tools/ddgs.search.test.js +0 -67
- package/dist/tools/ddgs_search.py +0 -23
- package/dist/tools/repo.get_route_map.js +0 -37
- package/dist/tools/repo.run_lighthouse.js +0 -37
- package/dist/tools/repo.run_playwright.js +0 -34
- package/scripts/export-mcp-schema.ts +0 -7
- package/src/lib/td-cli.ts +0 -73
- package/src/tools/ddgs.search.test.ts +0 -92
- package/src/tools/ddgs.search.ts +0 -58
- package/src/tools/ddgs_search.py +0 -23
- package/src/tools/repo.get_route_map.ts +0 -41
- package/src/tools/repo.run_lighthouse.ts +0 -40
- package/src/tools/repo.run_playwright.ts +0 -42
package/dist/mcp/server.js
CHANGED
|
@@ -14,14 +14,11 @@ import { getChangedFilesHandler, GetChangedFilesInputSchema } from "../tools/rep
|
|
|
14
14
|
import { readAgentContextHandler, ReadAgentContextInputSchema } from "../tools/repo.read_agent_context.js";
|
|
15
15
|
import { getCommandSchemaHandler, GetCommandSchemaInputSchema } from "../tools/repo.get_command_schema.js";
|
|
16
16
|
import { getPackageScriptsHandler, GetPackageScriptsInputSchema } from "../tools/repo.get_package_scripts.js";
|
|
17
|
-
import { getRouteMapHandler, GetRouteMapInputSchema } from "../tools/repo.get_route_map.js";
|
|
18
17
|
import { readCiLogsHandler, ReadCiLogsInputSchema } from "../tools/repo.read_ci_logs.js";
|
|
19
18
|
import { repoLogsHandler, RepoLogsInputSchema } from "../tools/repo.logs.js";
|
|
20
19
|
import { createRepairBranchHandler, CreateRepairBranchInputSchema } from "../tools/repo.create_repair_branch.js";
|
|
21
20
|
import { createBranchHandler, CreateBranchInputSchema } from "../tools/repo.create_branch.js";
|
|
22
21
|
import { runTestsHandler, RunTestsInputSchema } from "../tools/repo.run_tests.js";
|
|
23
|
-
import { runLighthouseHandler, RunLighthouseInputSchema } from "../tools/repo.run_lighthouse.js";
|
|
24
|
-
import { runPlaywrightHandler, RunPlaywrightInputSchema } from "../tools/repo.run_playwright.js";
|
|
25
22
|
import { commitPatchHandler, CommitPatchInputSchema } from "../tools/repo.commit_patch.js";
|
|
26
23
|
import { openReplacementPrHandler, OpenReplacementPrInputSchema } from "../tools/github.open_replacement_pr.js";
|
|
27
24
|
import { commentTriageSummaryHandler, CommentTriageSummaryInputSchema } from "../tools/github.comment_triage_summary.js";
|
|
@@ -39,12 +36,10 @@ import { listJulesSessionsHandler, ListJulesSessionsInputSchema } from "../tools
|
|
|
39
36
|
import { cancelJulesSessionHandler, CancelJulesSessionInputSchema } from "../tools/jules/cancel-session.js";
|
|
40
37
|
import { getJulesPullRequestHandler, GetJulesPullRequestInputSchema } from "../tools/jules/get-pr.js";
|
|
41
38
|
import { triggerJulesFeedbackHandler, TriggerJulesFeedbackInputSchema } from "../tools/jules/trigger-feedback.js";
|
|
42
|
-
import { ddgsSearchHandler, DdgsSearchInputSchema } from "../tools/ddgs.search.js";
|
|
43
39
|
import fs from "fs/promises";
|
|
44
40
|
import { existsSync, readFileSync } from "fs";
|
|
45
41
|
import path from "path";
|
|
46
42
|
import { spawnSync } from "child_process";
|
|
47
|
-
import { fileURLToPath } from "url";
|
|
48
43
|
export class BoomtickMCPServer {
|
|
49
44
|
server;
|
|
50
45
|
constructor() {
|
|
@@ -106,12 +101,6 @@ export class BoomtickMCPServer {
|
|
|
106
101
|
contents: [{ uri, mimeType: "application/json", text: content }],
|
|
107
102
|
};
|
|
108
103
|
}
|
|
109
|
-
if (uri === "repo://routes") {
|
|
110
|
-
const routeMap = await getRouteMapHandler({});
|
|
111
|
-
return {
|
|
112
|
-
contents: [{ uri, mimeType: "application/json", text: JSON.stringify(routeMap, null, 2) }],
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
104
|
if (uri === "repo://design-tokens") {
|
|
116
105
|
const tokensPath = path.join(config.repoPath, "src/styles/design-tokens.ts");
|
|
117
106
|
const content = await fs.readFile(tokensPath, "utf-8");
|
|
@@ -133,20 +122,6 @@ export class BoomtickMCPServer {
|
|
|
133
122
|
contents: [{ uri, mimeType: "application/json", text: JSON.stringify(logs, null, 2) }],
|
|
134
123
|
};
|
|
135
124
|
}
|
|
136
|
-
if (uri.startsWith("repo://lighthouse/")) {
|
|
137
|
-
const branch = uri.split("/").pop() || "";
|
|
138
|
-
const report = await runLighthouseHandler({ route: "/", worktreePath: `../boomtick-mcp-rescue-${branch}` });
|
|
139
|
-
return {
|
|
140
|
-
contents: [{ uri, mimeType: "application/json", text: JSON.stringify(report, null, 2) }],
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
if (uri.startsWith("repo://playwright/")) {
|
|
144
|
-
const branch = uri.split("/").pop() || "";
|
|
145
|
-
const report = await runPlaywrightHandler({ worktreePath: `../boomtick-mcp-rescue-${branch}` });
|
|
146
|
-
return {
|
|
147
|
-
contents: [{ uri, mimeType: "application/json", text: JSON.stringify(report, null, 2) }],
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
125
|
throw new Error(`Resource not found: ${uri}`);
|
|
151
126
|
});
|
|
152
127
|
}
|
|
@@ -187,9 +162,6 @@ export class BoomtickMCPServer {
|
|
|
187
162
|
case "repo.get.package.scripts":
|
|
188
163
|
case "repo.get_package_scripts":
|
|
189
164
|
return createSuccessResult(await getPackageScriptsHandler(GetPackageScriptsInputSchema.parse(normArgs)));
|
|
190
|
-
case "repo.get.route.map":
|
|
191
|
-
case "repo.get_route_map":
|
|
192
|
-
return createSuccessResult(await getRouteMapHandler(GetRouteMapInputSchema.parse(normArgs)));
|
|
193
165
|
case "repo.read.ci.logs":
|
|
194
166
|
case "repo.read_ci_logs":
|
|
195
167
|
return createSuccessResult(await readCiLogsHandler(ReadCiLogsInputSchema.parse(normArgs)));
|
|
@@ -204,12 +176,6 @@ export class BoomtickMCPServer {
|
|
|
204
176
|
case "repo.run.tests":
|
|
205
177
|
case "repo.run_tests":
|
|
206
178
|
return createSuccessResult(await runTestsHandler(RunTestsInputSchema.parse(normArgs)));
|
|
207
|
-
case "repo.run.lighthouse":
|
|
208
|
-
case "repo.run_lighthouse":
|
|
209
|
-
return createSuccessResult(await runLighthouseHandler(RunLighthouseInputSchema.parse(normArgs)));
|
|
210
|
-
case "repo.run.playwright":
|
|
211
|
-
case "repo.run_playwright":
|
|
212
|
-
return createSuccessResult(await runPlaywrightHandler(RunPlaywrightInputSchema.parse(normArgs)));
|
|
213
179
|
case "repo.commit.patch":
|
|
214
180
|
case "repo.commit_patch":
|
|
215
181
|
return createSuccessResult(await commitPatchHandler(CommitPatchInputSchema.parse(normArgs)));
|
|
@@ -261,9 +227,6 @@ export class BoomtickMCPServer {
|
|
|
261
227
|
case "jules.trigger.feedback":
|
|
262
228
|
case "jules.trigger_feedback":
|
|
263
229
|
return createSuccessResult(await triggerJulesFeedbackHandler(TriggerJulesFeedbackInputSchema.parse(normArgs)));
|
|
264
|
-
case "agent.search.ddgs":
|
|
265
|
-
case "agent.search_ddgs":
|
|
266
|
-
return createSuccessResult(await ddgsSearchHandler(DdgsSearchInputSchema.parse(normArgs)));
|
|
267
230
|
default:
|
|
268
231
|
return createErrorResult(`Tool not found: ${request.params.name}`);
|
|
269
232
|
}
|
|
@@ -337,15 +300,6 @@ export class BoomtickMCPServer {
|
|
|
337
300
|
console.error("❌ Fatal: td-cli pre-flight check failed:", error instanceof Error ? error.message : String(error));
|
|
338
301
|
process.exit(1);
|
|
339
302
|
}
|
|
340
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
341
|
-
const ddgsScriptPath = path.join(__dirname, "..", "tools", "ddgs_search.py");
|
|
342
|
-
try {
|
|
343
|
-
await fs.access(ddgsScriptPath);
|
|
344
|
-
}
|
|
345
|
-
catch (err) {
|
|
346
|
-
console.error(`❌ Fatal: ddgs_search.py not found at ${ddgsScriptPath}`);
|
|
347
|
-
process.exit(1);
|
|
348
|
-
}
|
|
349
303
|
const transport = new StdioServerTransport();
|
|
350
304
|
await this.server.connect(transport);
|
|
351
305
|
console.error("Boomtick MCP Server running on stdio");
|
package/dist/mcp/server.test.js
CHANGED
|
@@ -2,11 +2,8 @@ import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
|
2
2
|
import { BoomtickMCPServer } from "./server.js";
|
|
3
3
|
import fs from "fs/promises";
|
|
4
4
|
import * as healthModule from "./tools.js";
|
|
5
|
-
import * as routeMapModule from "../tools/repo.get_route_map.js";
|
|
6
5
|
import * as prDiffModule from "../tools/github.get_pr_diff.js";
|
|
7
6
|
import * as ciLogsModule from "../tools/repo.read_ci_logs.js";
|
|
8
|
-
import * as lighthouseModule from "../tools/repo.run_lighthouse.js";
|
|
9
|
-
import * as playwrightModule from "../tools/repo.run_playwright.js";
|
|
10
7
|
vi.mock("fs/promises");
|
|
11
8
|
vi.mock("../config.js", () => ({
|
|
12
9
|
config: {
|
|
@@ -99,16 +96,6 @@ describe("BoomtickMCPServer", () => {
|
|
|
99
96
|
contents: [{ uri: "repo://package-json", mimeType: "application/json", text: '{"name": "test"}' }],
|
|
100
97
|
});
|
|
101
98
|
});
|
|
102
|
-
it("should handle repo://routes", async () => {
|
|
103
|
-
const handler = requestHandlers.get("resources/read");
|
|
104
|
-
vi.spyOn(routeMapModule, "getRouteMapHandler").mockResolvedValue({ routes: ["/"] });
|
|
105
|
-
const result = await handler({
|
|
106
|
-
method: "resources/read",
|
|
107
|
-
params: { uri: "repo://routes" },
|
|
108
|
-
});
|
|
109
|
-
expect(result.contents[0].uri).toBe("repo://routes");
|
|
110
|
-
expect(JSON.parse(result.contents[0].text)).toEqual({ routes: ["/"] });
|
|
111
|
-
});
|
|
112
99
|
it("should handle repo://design-tokens", async () => {
|
|
113
100
|
const handler = requestHandlers.get("resources/read");
|
|
114
101
|
vi.mocked(fs.readFile).mockResolvedValue("export const tokens = {};");
|
|
@@ -141,26 +128,6 @@ describe("BoomtickMCPServer", () => {
|
|
|
141
128
|
expect(result.contents[0].uri).toBe("repo://ci/42");
|
|
142
129
|
expect(JSON.parse(result.contents[0].text)).toEqual({ status: "success" });
|
|
143
130
|
});
|
|
144
|
-
it("should handle repo://lighthouse/:branch", async () => {
|
|
145
|
-
const handler = requestHandlers.get("resources/read");
|
|
146
|
-
vi.spyOn(lighthouseModule, "runLighthouseHandler").mockResolvedValue({ score: 100 });
|
|
147
|
-
const result = await handler({
|
|
148
|
-
method: "resources/read",
|
|
149
|
-
params: { uri: "repo://lighthouse/feature-x" },
|
|
150
|
-
});
|
|
151
|
-
expect(result.contents[0].uri).toBe("repo://lighthouse/feature-x");
|
|
152
|
-
expect(JSON.parse(result.contents[0].text)).toEqual({ score: 100 });
|
|
153
|
-
});
|
|
154
|
-
it("should handle repo://playwright/:branch", async () => {
|
|
155
|
-
const handler = requestHandlers.get("resources/read");
|
|
156
|
-
vi.spyOn(playwrightModule, "runPlaywrightHandler").mockResolvedValue({ passed: true });
|
|
157
|
-
const result = await handler({
|
|
158
|
-
method: "resources/read",
|
|
159
|
-
params: { uri: "repo://playwright/feature-x" },
|
|
160
|
-
});
|
|
161
|
-
expect(result.contents[0].uri).toBe("repo://playwright/feature-x");
|
|
162
|
-
expect(JSON.parse(result.contents[0].text)).toEqual({ passed: true });
|
|
163
|
-
});
|
|
164
131
|
it("should throw when resource is unknown", async () => {
|
|
165
132
|
const handler = requestHandlers.get("resources/read");
|
|
166
133
|
await expect(handler({
|
package/dist/tools/contract.js
CHANGED
|
@@ -1512,6 +1512,35 @@ export const GetPackageScriptsInputJsonSchema = {
|
|
|
1512
1512
|
"title": "GetPackageScriptsInput",
|
|
1513
1513
|
"type": "object"
|
|
1514
1514
|
};
|
|
1515
|
+
export const GetPrCommitsInputSchema = z.object({ "prNumber": z.number().int().describe("The number of the PR to list commits for."), "repo": z.union([z.string().regex(new RegExp("^[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+$")), z.null()]).describe("The target repository override (e.g. org/repo).").default(null) });
|
|
1516
|
+
export const GetPrCommitsInputJsonSchema = {
|
|
1517
|
+
"properties": {
|
|
1518
|
+
"prNumber": {
|
|
1519
|
+
"description": "The number of the PR to list commits for.",
|
|
1520
|
+
"title": "Prnumber",
|
|
1521
|
+
"type": "integer"
|
|
1522
|
+
},
|
|
1523
|
+
"repo": {
|
|
1524
|
+
"anyOf": [
|
|
1525
|
+
{
|
|
1526
|
+
"pattern": "^[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+$",
|
|
1527
|
+
"type": "string"
|
|
1528
|
+
},
|
|
1529
|
+
{
|
|
1530
|
+
"type": "null"
|
|
1531
|
+
}
|
|
1532
|
+
],
|
|
1533
|
+
"default": null,
|
|
1534
|
+
"description": "The target repository override (e.g. org/repo).",
|
|
1535
|
+
"title": "Repo"
|
|
1536
|
+
}
|
|
1537
|
+
},
|
|
1538
|
+
"required": [
|
|
1539
|
+
"prNumber"
|
|
1540
|
+
],
|
|
1541
|
+
"title": "GetPrCommitsInput",
|
|
1542
|
+
"type": "object"
|
|
1543
|
+
};
|
|
1515
1544
|
export const GetPrDiffInputSchema = z.object({ "prNumber": z.number().int().describe("The number of the pull request to get the diff for."), "repo": z.union([z.string().regex(new RegExp("^[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+$")), z.null()]).describe("The target repository override (e.g. org/repo).").default(null) });
|
|
1516
1545
|
export const GetPrDiffInputJsonSchema = {
|
|
1517
1546
|
"properties": {
|
|
@@ -1570,6 +1599,35 @@ export const GetPrInputJsonSchema = {
|
|
|
1570
1599
|
"title": "GetPrInput",
|
|
1571
1600
|
"type": "object"
|
|
1572
1601
|
};
|
|
1602
|
+
export const GetPrReviewsInputSchema = z.object({ "prNumber": z.number().int().describe("The number of the PR to view reviews for."), "repo": z.union([z.string().regex(new RegExp("^[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+$")), z.null()]).describe("The target repository override (e.g. org/repo).").default(null) });
|
|
1603
|
+
export const GetPrReviewsInputJsonSchema = {
|
|
1604
|
+
"properties": {
|
|
1605
|
+
"prNumber": {
|
|
1606
|
+
"description": "The number of the PR to view reviews for.",
|
|
1607
|
+
"title": "Prnumber",
|
|
1608
|
+
"type": "integer"
|
|
1609
|
+
},
|
|
1610
|
+
"repo": {
|
|
1611
|
+
"anyOf": [
|
|
1612
|
+
{
|
|
1613
|
+
"pattern": "^[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+$",
|
|
1614
|
+
"type": "string"
|
|
1615
|
+
},
|
|
1616
|
+
{
|
|
1617
|
+
"type": "null"
|
|
1618
|
+
}
|
|
1619
|
+
],
|
|
1620
|
+
"default": null,
|
|
1621
|
+
"description": "The target repository override (e.g. org/repo).",
|
|
1622
|
+
"title": "Repo"
|
|
1623
|
+
}
|
|
1624
|
+
},
|
|
1625
|
+
"required": [
|
|
1626
|
+
"prNumber"
|
|
1627
|
+
],
|
|
1628
|
+
"title": "GetPrReviewsInput",
|
|
1629
|
+
"type": "object"
|
|
1630
|
+
};
|
|
1573
1631
|
export const GetRouteMapInputSchema = z.object({ "includeStatic": z.union([z.boolean(), z.null()]).describe("Whether to include static assets in the route map.").default(null) });
|
|
1574
1632
|
export const GetRouteMapInputJsonSchema = {
|
|
1575
1633
|
"properties": {
|
|
@@ -1590,6 +1648,53 @@ export const GetRouteMapInputJsonSchema = {
|
|
|
1590
1648
|
"title": "GetRouteMapInput",
|
|
1591
1649
|
"type": "object"
|
|
1592
1650
|
};
|
|
1651
|
+
export const GetWorkflowRunsInputSchema = z.object({ "branch": z.union([z.string(), z.null()]).describe("Filter workflow runs by branch name.").default(null), "event": z.union([z.string(), z.null()]).describe("Filter workflow runs by event (e.g. push, pull_request).").default(null), "repo": z.union([z.string().regex(new RegExp("^[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+$")), z.null()]).describe("The target repository override (e.g. org/repo).").default(null) });
|
|
1652
|
+
export const GetWorkflowRunsInputJsonSchema = {
|
|
1653
|
+
"properties": {
|
|
1654
|
+
"branch": {
|
|
1655
|
+
"anyOf": [
|
|
1656
|
+
{
|
|
1657
|
+
"type": "string"
|
|
1658
|
+
},
|
|
1659
|
+
{
|
|
1660
|
+
"type": "null"
|
|
1661
|
+
}
|
|
1662
|
+
],
|
|
1663
|
+
"default": null,
|
|
1664
|
+
"description": "Filter workflow runs by branch name.",
|
|
1665
|
+
"title": "Branch"
|
|
1666
|
+
},
|
|
1667
|
+
"event": {
|
|
1668
|
+
"anyOf": [
|
|
1669
|
+
{
|
|
1670
|
+
"type": "string"
|
|
1671
|
+
},
|
|
1672
|
+
{
|
|
1673
|
+
"type": "null"
|
|
1674
|
+
}
|
|
1675
|
+
],
|
|
1676
|
+
"default": null,
|
|
1677
|
+
"description": "Filter workflow runs by event (e.g. push, pull_request).",
|
|
1678
|
+
"title": "Event"
|
|
1679
|
+
},
|
|
1680
|
+
"repo": {
|
|
1681
|
+
"anyOf": [
|
|
1682
|
+
{
|
|
1683
|
+
"pattern": "^[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+$",
|
|
1684
|
+
"type": "string"
|
|
1685
|
+
},
|
|
1686
|
+
{
|
|
1687
|
+
"type": "null"
|
|
1688
|
+
}
|
|
1689
|
+
],
|
|
1690
|
+
"default": null,
|
|
1691
|
+
"description": "The target repository override (e.g. org/repo).",
|
|
1692
|
+
"title": "Repo"
|
|
1693
|
+
}
|
|
1694
|
+
},
|
|
1695
|
+
"title": "GetWorkflowRunsInput",
|
|
1696
|
+
"type": "object"
|
|
1697
|
+
};
|
|
1593
1698
|
export const HealthCheckInputSchema = z.object({ "checkDeep": z.boolean().describe("Whether to perform a deep health check including external dependencies.").default(false) });
|
|
1594
1699
|
export const HealthCheckInputJsonSchema = {
|
|
1595
1700
|
"properties": {
|
|
@@ -2189,7 +2294,7 @@ export const ReadAgentContextInputJsonSchema = {
|
|
|
2189
2294
|
"title": "ReadAgentContextInput",
|
|
2190
2295
|
"type": "object"
|
|
2191
2296
|
};
|
|
2192
|
-
export const ReadCiLogsInputSchema = z.object({ "prNumber": z.number().int().describe("The number of the pull request to read logs for."), "all": z.boolean().describe("Include logs for successful runs (default: false).").default(false) });
|
|
2297
|
+
export const ReadCiLogsInputSchema = z.object({ "prNumber": z.number().int().describe("The number of the pull request to read logs for."), "all": z.boolean().describe("Include logs for successful runs (default: false).").default(false), "repo": z.union([z.string().regex(new RegExp("^[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+$")), z.null()]).describe("The target repository override (e.g. org/repo).").default(null) });
|
|
2193
2298
|
export const ReadCiLogsInputJsonSchema = {
|
|
2194
2299
|
"properties": {
|
|
2195
2300
|
"prNumber": {
|
|
@@ -2202,6 +2307,20 @@ export const ReadCiLogsInputJsonSchema = {
|
|
|
2202
2307
|
"description": "Include logs for successful runs (default: false).",
|
|
2203
2308
|
"title": "All",
|
|
2204
2309
|
"type": "boolean"
|
|
2310
|
+
},
|
|
2311
|
+
"repo": {
|
|
2312
|
+
"anyOf": [
|
|
2313
|
+
{
|
|
2314
|
+
"pattern": "^[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+$",
|
|
2315
|
+
"type": "string"
|
|
2316
|
+
},
|
|
2317
|
+
{
|
|
2318
|
+
"type": "null"
|
|
2319
|
+
}
|
|
2320
|
+
],
|
|
2321
|
+
"default": null,
|
|
2322
|
+
"description": "The target repository override (e.g. org/repo).",
|
|
2323
|
+
"title": "Repo"
|
|
2205
2324
|
}
|
|
2206
2325
|
},
|
|
2207
2326
|
"required": [
|
|
@@ -2373,7 +2492,7 @@ export const ReadPRCommentsResponseJsonSchema = {
|
|
|
2373
2492
|
"title": "ReadPRCommentsResponse",
|
|
2374
2493
|
"type": "object"
|
|
2375
2494
|
};
|
|
2376
|
-
export const RepoLogsInputSchema = z.object({ "prNumber": z.number().int().describe("The number of the pull request to stream logs for."), "grep": z.union([z.string(), z.null()]).describe("Optional pattern to filter log lines.").default(null) });
|
|
2495
|
+
export const RepoLogsInputSchema = z.object({ "prNumber": z.number().int().describe("The number of the pull request to stream logs for."), "grep": z.union([z.string(), z.null()]).describe("Optional pattern to filter log lines.").default(null), "repo": z.union([z.string().regex(new RegExp("^[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+$")), z.null()]).describe("The target repository override (e.g. org/repo).").default(null) });
|
|
2377
2496
|
export const RepoLogsInputJsonSchema = {
|
|
2378
2497
|
"properties": {
|
|
2379
2498
|
"prNumber": {
|
|
@@ -2393,6 +2512,20 @@ export const RepoLogsInputJsonSchema = {
|
|
|
2393
2512
|
"default": null,
|
|
2394
2513
|
"description": "Optional pattern to filter log lines.",
|
|
2395
2514
|
"title": "Grep"
|
|
2515
|
+
},
|
|
2516
|
+
"repo": {
|
|
2517
|
+
"anyOf": [
|
|
2518
|
+
{
|
|
2519
|
+
"pattern": "^[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+$",
|
|
2520
|
+
"type": "string"
|
|
2521
|
+
},
|
|
2522
|
+
{
|
|
2523
|
+
"type": "null"
|
|
2524
|
+
}
|
|
2525
|
+
],
|
|
2526
|
+
"default": null,
|
|
2527
|
+
"description": "The target repository override (e.g. org/repo).",
|
|
2528
|
+
"title": "Repo"
|
|
2396
2529
|
}
|
|
2397
2530
|
},
|
|
2398
2531
|
"required": [
|
|
@@ -1,26 +1,18 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { getMergeConflictFiles } from "../lib/git.js";
|
|
3
3
|
export const GetMergeConflictFilesInputSchema = z.object({
|
|
4
|
-
prNumber: z.number(),
|
|
4
|
+
prNumber: z.number().optional(),
|
|
5
5
|
baseBranch: z.string().optional().default("main"),
|
|
6
|
+
headRef: z.string().optional().default("HEAD"),
|
|
6
7
|
});
|
|
7
8
|
export async function getMergeConflictFilesHandler(args) {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
]);
|
|
12
|
-
if (result.exitCode !== 0) {
|
|
13
|
-
throw new Error(`Failed to get merge conflicts: ${result.stderr}`);
|
|
14
|
-
}
|
|
15
|
-
const output = JSON.parse(result.stdout);
|
|
16
|
-
if (output.status === "error") {
|
|
17
|
-
throw new Error(`Failed to get merge conflicts: ${output.message}`);
|
|
18
|
-
}
|
|
9
|
+
const baseBranch = args.baseBranch || "main";
|
|
10
|
+
const headRef = args.headRef || "HEAD";
|
|
11
|
+
const conflictFiles = await getMergeConflictFiles(baseBranch, headRef);
|
|
19
12
|
return {
|
|
20
|
-
prNumber:
|
|
21
|
-
baseBranch
|
|
22
|
-
headRef
|
|
23
|
-
conflictFiles
|
|
24
|
-
commandLog: output.commandLog
|
|
13
|
+
prNumber: args.prNumber,
|
|
14
|
+
baseBranch,
|
|
15
|
+
headRef,
|
|
16
|
+
conflictFiles,
|
|
25
17
|
};
|
|
26
18
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCommand } from "../lib/shell.js";
|
|
3
|
+
import { resolveRepo } from "../lib/git.js";
|
|
4
|
+
import { sanitizeError } from "../lib/error_utils.js";
|
|
5
|
+
const GetPrCommitsInputSchema = z.object({
|
|
6
|
+
prNumber: z.number().describe("The number of the PR to list commits for."),
|
|
7
|
+
repo: z.string().optional().describe("The target repository override (e.g. org/repo)."),
|
|
8
|
+
});
|
|
9
|
+
export async function getPrCommitsHandler(args) {
|
|
10
|
+
const params = GetPrCommitsInputSchema.parse(args);
|
|
11
|
+
const targetRepo = resolveRepo(params.repo);
|
|
12
|
+
const result = await runCommand("td-cli", ["gh", "list-pr-commits", "--pr-number", params.prNumber.toString(), "--repo", targetRepo]);
|
|
13
|
+
if (result.exitCode !== 0) {
|
|
14
|
+
throw new Error(`Failed to list PR commits: ${sanitizeError(result.stderr)}`);
|
|
15
|
+
}
|
|
16
|
+
const output = JSON.parse(result.stdout);
|
|
17
|
+
if (output.status === "error") {
|
|
18
|
+
throw new Error(`Failed to list PR commits: ${output.message}`);
|
|
19
|
+
}
|
|
20
|
+
return { commits: output.commits };
|
|
21
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCommand } from "../lib/shell.js";
|
|
3
|
+
import { resolveRepo } from "../lib/git.js";
|
|
4
|
+
import { sanitizeError } from "../lib/error_utils.js";
|
|
5
|
+
const GetPrReviewsInputSchema = z.object({
|
|
6
|
+
prNumber: z.number().describe("The number of the PR to view reviews for."),
|
|
7
|
+
repo: z.string().optional().describe("The target repository override (e.g. org/repo)."),
|
|
8
|
+
});
|
|
9
|
+
export async function getPrReviewsHandler(args) {
|
|
10
|
+
const params = GetPrReviewsInputSchema.parse(args);
|
|
11
|
+
const targetRepo = resolveRepo(params.repo);
|
|
12
|
+
const result = await runCommand("td-cli", ["gh", "read-pr-reviews", "--pr-number", params.prNumber.toString(), "--repo", targetRepo]);
|
|
13
|
+
if (result.exitCode !== 0) {
|
|
14
|
+
throw new Error(`Failed to read PR reviews: ${sanitizeError(result.stderr)}`);
|
|
15
|
+
}
|
|
16
|
+
const output = JSON.parse(result.stdout);
|
|
17
|
+
if (output.status === "error") {
|
|
18
|
+
throw new Error(`Failed to read PR reviews: ${output.message}`);
|
|
19
|
+
}
|
|
20
|
+
return { reviews: output.reviews, reviewCount: output.review_count };
|
|
21
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import { getPrReviewsHandler } from "./github.get_pr_reviews.js";
|
|
3
|
+
import { getPrCommitsHandler } from "./github.get_pr_commits.js";
|
|
4
|
+
import { getWorkflowRunsHandler } from "./repo.get_workflow_runs.js";
|
|
5
|
+
import * as shell from "../lib/shell.js";
|
|
6
|
+
vi.mock("../lib/shell.js", () => ({
|
|
7
|
+
runCommand: vi.fn(),
|
|
8
|
+
}));
|
|
9
|
+
describe("MCP GitHub tools", () => {
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
vi.clearAllMocks();
|
|
12
|
+
});
|
|
13
|
+
describe("getPrReviewsHandler", () => {
|
|
14
|
+
it("should fetch PR reviews via td-cli gh read-pr-reviews", async () => {
|
|
15
|
+
vi.mocked(shell.runCommand).mockResolvedValueOnce({
|
|
16
|
+
stdout: JSON.stringify({
|
|
17
|
+
status: "success",
|
|
18
|
+
reviews: [{ id: 1, state: "APPROVED", body: "LGTM" }],
|
|
19
|
+
review_count: 1,
|
|
20
|
+
}),
|
|
21
|
+
stderr: "",
|
|
22
|
+
exitCode: 0,
|
|
23
|
+
durationMs: 10,
|
|
24
|
+
command: "td-cli gh read-pr-reviews",
|
|
25
|
+
});
|
|
26
|
+
const res = await getPrReviewsHandler({ prNumber: 819, repo: "arii/boomtick" });
|
|
27
|
+
expect(shell.runCommand).toHaveBeenCalledWith("td-cli", [
|
|
28
|
+
"gh",
|
|
29
|
+
"read-pr-reviews",
|
|
30
|
+
"--pr-number",
|
|
31
|
+
"819",
|
|
32
|
+
"--repo",
|
|
33
|
+
"arii/boomtick",
|
|
34
|
+
]);
|
|
35
|
+
expect(res).toEqual({
|
|
36
|
+
reviews: [{ id: 1, state: "APPROVED", body: "LGTM" }],
|
|
37
|
+
reviewCount: 1,
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
describe("getPrCommitsHandler", () => {
|
|
42
|
+
it("should list PR commits via td-cli gh list-pr-commits", async () => {
|
|
43
|
+
vi.mocked(shell.runCommand).mockResolvedValueOnce({
|
|
44
|
+
stdout: JSON.stringify({
|
|
45
|
+
status: "success",
|
|
46
|
+
commits: [{ sha: "abc1234", author: "Dev", message: "feat: test" }],
|
|
47
|
+
}),
|
|
48
|
+
stderr: "",
|
|
49
|
+
exitCode: 0,
|
|
50
|
+
durationMs: 10,
|
|
51
|
+
command: "td-cli gh list-pr-commits",
|
|
52
|
+
});
|
|
53
|
+
const res = await getPrCommitsHandler({ prNumber: 819, repo: "arii/boomtick" });
|
|
54
|
+
expect(shell.runCommand).toHaveBeenCalledWith("td-cli", [
|
|
55
|
+
"gh",
|
|
56
|
+
"list-pr-commits",
|
|
57
|
+
"--pr-number",
|
|
58
|
+
"819",
|
|
59
|
+
"--repo",
|
|
60
|
+
"arii/boomtick",
|
|
61
|
+
]);
|
|
62
|
+
expect(res).toEqual({
|
|
63
|
+
commits: [{ sha: "abc1234", author: "Dev", message: "feat: test" }],
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
describe("getWorkflowRunsHandler", () => {
|
|
68
|
+
it("should fetch workflow runs via td-cli repo workflow-runs", async () => {
|
|
69
|
+
vi.mocked(shell.runCommand).mockResolvedValueOnce({
|
|
70
|
+
stdout: JSON.stringify({
|
|
71
|
+
status: "success",
|
|
72
|
+
workflow_runs: [{ id: 10, name: "CI" }],
|
|
73
|
+
}),
|
|
74
|
+
stderr: "",
|
|
75
|
+
exitCode: 0,
|
|
76
|
+
durationMs: 10,
|
|
77
|
+
command: "td-cli repo workflow-runs",
|
|
78
|
+
});
|
|
79
|
+
const res = await getWorkflowRunsHandler({
|
|
80
|
+
branch: "main",
|
|
81
|
+
event: "push",
|
|
82
|
+
repo: "arii/boomtick",
|
|
83
|
+
});
|
|
84
|
+
expect(shell.runCommand).toHaveBeenCalledWith("td-cli", [
|
|
85
|
+
"repo",
|
|
86
|
+
"workflow-runs",
|
|
87
|
+
"--repo",
|
|
88
|
+
"arii/boomtick",
|
|
89
|
+
"--branch",
|
|
90
|
+
"main",
|
|
91
|
+
"--event",
|
|
92
|
+
"push",
|
|
93
|
+
]);
|
|
94
|
+
expect(res).toEqual({
|
|
95
|
+
workflowRuns: [{ id: 10, name: "CI" }],
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { runTDCli } from "
|
|
3
|
-
import { parseJulesSession } from "./shared.js";
|
|
2
|
+
import { runTDCli, parseJulesSession } from "./shared.js";
|
|
4
3
|
export const CreateJulesSessionInputSchema = z.object({
|
|
5
4
|
task: z.string(),
|
|
6
5
|
branch: z.string().optional(),
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { runTDCli } from "
|
|
3
|
-
import { parseJulesSession } from "./shared.js";
|
|
2
|
+
import { runTDCli, parseJulesSession } from "./shared.js";
|
|
4
3
|
export const GetJulesPullRequestInputSchema = z.object({
|
|
5
4
|
sessionId: z.string(),
|
|
6
5
|
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { runTDCli } from "
|
|
3
|
-
import { parseJulesSession } from "./shared.js";
|
|
2
|
+
import { runTDCli, parseJulesSession } from "./shared.js";
|
|
4
3
|
export const GetJulesSessionInputSchema = z.object({
|
|
5
4
|
sessionId: z.string(),
|
|
6
5
|
});
|
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
import { runCommand } from "../../lib/shell.js";
|
|
2
|
+
function tryParseJson(text) {
|
|
3
|
+
try {
|
|
4
|
+
return JSON.parse(text);
|
|
5
|
+
}
|
|
6
|
+
catch {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export async function runTDCli(args) {
|
|
11
|
+
const result = await runCommand("td-cli", args);
|
|
12
|
+
const stdout = result.stdout.trim();
|
|
13
|
+
const output = stdout.startsWith("{") ? tryParseJson(stdout) : null;
|
|
14
|
+
if (output?.status === "error") {
|
|
15
|
+
throw new Error(`td-cli returned error: ${output.message ?? "Unknown error"}`);
|
|
16
|
+
}
|
|
17
|
+
if (result.exitCode !== 0) {
|
|
18
|
+
throw new Error(`td-cli command failed (${args.join(" ")}): ${result.stderr || stdout}`);
|
|
19
|
+
}
|
|
20
|
+
if (!output) {
|
|
21
|
+
throw new Error(`td-cli returned non-JSON output with exit code 0: ${stdout}`);
|
|
22
|
+
}
|
|
23
|
+
return output;
|
|
24
|
+
}
|
|
1
25
|
export function parseJulesSession(data) {
|
|
2
26
|
const name = data.name || "";
|
|
3
27
|
const id = name.startsWith("sessions/") ? name.substring(9) : name;
|
|
@@ -1,21 +1,10 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { getChangedFiles } from "../lib/git.js";
|
|
3
3
|
export const GetChangedFilesInputSchema = z.object({
|
|
4
4
|
base: z.string().optional().default("main"),
|
|
5
5
|
head: z.string().optional().default("HEAD"),
|
|
6
6
|
});
|
|
7
7
|
export async function getChangedFilesHandler(args) {
|
|
8
|
-
const
|
|
9
|
-
"diff",
|
|
10
|
-
"--name-status",
|
|
11
|
-
`${args.base}...${args.head}`
|
|
12
|
-
]);
|
|
13
|
-
if (result.exitCode !== 0) {
|
|
14
|
-
throw new Error(`Failed to get changed files: ${result.stderr}`);
|
|
15
|
-
}
|
|
16
|
-
const files = result.stdout.trim().split("\n").filter(line => line.length > 0).map(line => {
|
|
17
|
-
const [status, path] = line.split(/\s+/);
|
|
18
|
-
return { status, path };
|
|
19
|
-
});
|
|
8
|
+
const files = await getChangedFiles(args.base || "main", args.head || "HEAD");
|
|
20
9
|
return { files };
|
|
21
10
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCommand } from "../lib/shell.js";
|
|
3
|
+
import { resolveRepo } from "../lib/git.js";
|
|
4
|
+
import { sanitizeError } from "../lib/error_utils.js";
|
|
5
|
+
const GetWorkflowRunsInputSchema = z.object({
|
|
6
|
+
branch: z.string().optional().describe("Filter workflow runs by branch name."),
|
|
7
|
+
event: z.string().optional().describe("Filter workflow runs by event (e.g. push, pull_request)."),
|
|
8
|
+
repo: z.string().optional().describe("The target repository override (e.g. org/repo)."),
|
|
9
|
+
});
|
|
10
|
+
export async function getWorkflowRunsHandler(args) {
|
|
11
|
+
const params = GetWorkflowRunsInputSchema.parse(args);
|
|
12
|
+
const targetRepo = resolveRepo(params.repo);
|
|
13
|
+
const cliArgs = ["repo", "workflow-runs", "--repo", targetRepo];
|
|
14
|
+
if (params.branch) {
|
|
15
|
+
cliArgs.push("--branch", params.branch);
|
|
16
|
+
}
|
|
17
|
+
if (params.event) {
|
|
18
|
+
cliArgs.push("--event", params.event);
|
|
19
|
+
}
|
|
20
|
+
const result = await runCommand("td-cli", cliArgs);
|
|
21
|
+
if (result.exitCode !== 0) {
|
|
22
|
+
throw new Error(`Failed to fetch workflow runs: ${sanitizeError(result.stderr)}`);
|
|
23
|
+
}
|
|
24
|
+
const output = JSON.parse(result.stdout);
|
|
25
|
+
if (output.status === "error") {
|
|
26
|
+
throw new Error(`Failed to fetch workflow runs: ${output.message}`);
|
|
27
|
+
}
|
|
28
|
+
return { workflowRuns: output.workflow_runs };
|
|
29
|
+
}
|