@arii/boomtick-mcp 0.4.1 → 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/boomtick.health.json +14 -10
- package/.mcp/schemas/boomtick_health.json +16 -0
- package/.mcp/schemas/github.checkout_branch.json +28 -24
- package/.mcp/schemas/github.comment_triage_summary.json +21 -17
- package/.mcp/schemas/github.create_issue.json +55 -50
- package/.mcp/schemas/github.create_pull_request.json +53 -48
- package/.mcp/schemas/github.get_merge_conflict_files.json +21 -17
- package/.mcp/schemas/github.get_pr.json +30 -12
- package/.mcp/schemas/github.get_pr_commits.json +32 -0
- package/.mcp/schemas/github.get_pr_diff.json +30 -12
- package/.mcp/schemas/github.get_pr_reviews.json +32 -0
- package/.mcp/schemas/github.issue_comment.json +35 -17
- package/.mcp/schemas/github.issue_update.json +114 -96
- package/.mcp/schemas/github.issue_view.json +30 -12
- package/.mcp/schemas/github.open_replacement_pr.json +64 -60
- package/.mcp/schemas/github.search_open_prs.json +61 -43
- package/.mcp/schemas/github_checkout_branch.json +31 -0
- package/.mcp/schemas/github_comment_triage_summary.json +24 -0
- package/.mcp/schemas/github_create_issue.json +58 -0
- package/.mcp/schemas/github_create_pull_request.json +56 -0
- package/.mcp/schemas/github_get_merge_conflict_files.json +24 -0
- package/.mcp/schemas/github_get_pr.json +32 -0
- package/.mcp/schemas/github_get_pr_commits.json +32 -0
- package/.mcp/schemas/github_get_pr_diff.json +32 -0
- package/.mcp/schemas/github_get_pr_reviews.json +32 -0
- package/.mcp/schemas/github_issue_comment.json +38 -0
- package/.mcp/schemas/github_issue_update.json +123 -0
- package/.mcp/schemas/github_issue_view.json +32 -0
- package/.mcp/schemas/github_open_replacement_pr.json +67 -0
- package/.mcp/schemas/github_search_open_prs.json +65 -0
- package/.mcp/schemas/jules.cancel_session.json +16 -12
- package/.mcp/schemas/jules.create_session.json +41 -37
- package/.mcp/schemas/jules.get_messages.json +16 -12
- package/.mcp/schemas/jules.get_pr.json +16 -12
- package/.mcp/schemas/jules.get_session.json +16 -12
- package/.mcp/schemas/jules.list_sessions.json +33 -29
- package/.mcp/schemas/jules.send_message.json +30 -26
- package/.mcp/schemas/jules.trigger_feedback.json +16 -12
- package/.mcp/schemas/jules_cancel_session.json +18 -0
- package/.mcp/schemas/jules_create_session.json +44 -0
- package/.mcp/schemas/jules_get_messages.json +18 -0
- package/.mcp/schemas/jules_get_pr.json +18 -0
- package/.mcp/schemas/jules_get_session.json +18 -0
- package/.mcp/schemas/jules_list_sessions.json +36 -0
- package/.mcp/schemas/jules_send_message.json +35 -0
- package/.mcp/schemas/jules_trigger_feedback.json +18 -0
- package/.mcp/schemas/repo.commit_patch.json +34 -30
- package/.mcp/schemas/repo.create_branch.json +21 -17
- package/.mcp/schemas/repo.create_repair_branch.json +34 -30
- package/.mcp/schemas/repo.get_changed_files.json +19 -15
- package/.mcp/schemas/repo.get_command_schema.json +16 -12
- package/.mcp/schemas/repo.get_package_scripts.json +21 -17
- package/.mcp/schemas/repo.get_workflow_runs.json +50 -0
- package/.mcp/schemas/repo.logs.json +42 -24
- package/.mcp/schemas/repo.read_agent_context.json +7 -3
- package/.mcp/schemas/repo.read_ci_logs.json +35 -17
- package/.mcp/schemas/repo.run_tests.json +40 -36
- package/.mcp/schemas/repo_commit_patch.json +39 -0
- package/.mcp/schemas/repo_create_branch.json +24 -0
- package/.mcp/schemas/repo_create_repair_branch.json +37 -0
- package/.mcp/schemas/repo_get_changed_files.json +22 -0
- package/.mcp/schemas/repo_get_command_schema.json +18 -0
- package/.mcp/schemas/repo_get_package_scripts.json +23 -0
- package/.mcp/schemas/repo_get_workflow_runs.json +50 -0
- package/.mcp/schemas/repo_logs.json +45 -0
- package/.mcp/schemas/repo_read_agent_context.json +9 -0
- package/.mcp/schemas/repo_read_ci_logs.json +38 -0
- package/.mcp/schemas/repo_run_tests.json +45 -0
- package/CHANGELOG.md +16 -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 +74 -0
- package/dist/lib/git.test.js +82 -0
- package/dist/lib/param_utils.js +35 -0
- package/dist/lib/param_utils.test.js +44 -0
- package/dist/mcp/definitions.js +27 -40
- package/dist/mcp/schemas.test.js +28 -0
- package/dist/mcp/server.js +64 -74
- package/dist/mcp/server.test.js +0 -33
- package/dist/tools/contract.js +229 -10
- package/dist/tools/github.create_issue.js +3 -2
- package/dist/tools/github.create_issue.test.js +10 -0
- package/dist/tools/github.create_pull_request.js +4 -4
- package/dist/tools/github.get_merge_conflict_files.js +10 -18
- package/dist/tools/github.get_pr.js +4 -1
- package/dist/tools/github.get_pr_commits.js +21 -0
- package/dist/tools/github.get_pr_diff.js +6 -2
- package/dist/tools/github.get_pr_reviews.js +21 -0
- package/dist/tools/github.get_pr_reviews.test.js +99 -0
- package/dist/tools/github.issue_comment.js +8 -1
- package/dist/tools/github.issue_update.js +3 -0
- package/dist/tools/github.issue_view.js +4 -1
- package/dist/tools/github.search_open_prs.js +4 -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.commit_patch.js +30 -10
- 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 +26 -4
- package/package.json +4 -7
- package/scripts/sync-mcp-schemas.ts +6 -1
- package/src/agents/verifier-agent.prompt.md +0 -4
- package/src/lib/git.test.ts +110 -0
- package/src/lib/git.ts +109 -0
- package/src/lib/param_utils.test.ts +49 -0
- package/src/lib/param_utils.ts +42 -0
- package/src/mcp/definitions.ts +30 -43
- package/src/mcp/schemas.test.ts +34 -0
- package/src/mcp/server.test.ts +0 -42
- package/src/mcp/server.ts +65 -75
- package/src/tools/contract.ts +241 -10
- package/src/tools/github.create_issue.test.ts +10 -0
- package/src/tools/github.create_issue.ts +3 -1
- package/src/tools/github.create_pull_request.ts +4 -5
- package/src/tools/github.get_merge_conflict_files.ts +10 -20
- package/src/tools/github.get_pr.ts +4 -1
- package/src/tools/github.get_pr_commits.ts +27 -0
- package/src/tools/github.get_pr_diff.ts +7 -2
- package/src/tools/github.get_pr_reviews.test.ts +110 -0
- package/src/tools/github.get_pr_reviews.ts +27 -0
- package/src/tools/github.issue_comment.ts +8 -1
- package/src/tools/github.issue_update.ts +4 -1
- package/src/tools/github.issue_view.ts +4 -1
- package/src/tools/github.search_open_prs.ts +4 -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.commit_patch.ts +35 -11
- 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 +28 -5
- package/.mcp/schemas/agent.search_ddgs.json +0 -27
- package/.mcp/schemas/repo.get_route_map.json +0 -19
- package/.mcp/schemas/repo.run_lighthouse.json +0 -25
- package/.mcp/schemas/repo.run_playwright.json +0 -32
- 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 -29
- 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 -35
|
@@ -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,9 +1,11 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { runCommand } from "../lib/shell.js";
|
|
3
|
+
import { resolveRepo } from "../lib/git.js";
|
|
3
4
|
import { sanitizeError } from "../lib/error_utils.js";
|
|
4
5
|
export const IssueCommentInputSchema = z.object({
|
|
5
6
|
issueNumber: z.number().describe("The number of the issue to comment on."),
|
|
6
7
|
body: z.string().min(1, "Comment body cannot be empty").describe("The content of the comment."),
|
|
8
|
+
repo: z.string().optional().describe("The target repository override (e.g. org/repo)."),
|
|
7
9
|
});
|
|
8
10
|
const IssueCommentOutputSchema = z.object({
|
|
9
11
|
status: z.string(),
|
|
@@ -12,7 +14,12 @@ const IssueCommentOutputSchema = z.object({
|
|
|
12
14
|
});
|
|
13
15
|
export async function issueCommentHandler(args) {
|
|
14
16
|
const params = IssueCommentInputSchema.parse(args);
|
|
15
|
-
const
|
|
17
|
+
const targetRepo = resolveRepo(params.repo);
|
|
18
|
+
const result = await runCommand("td-cli", [
|
|
19
|
+
"gh", "issue-comment", params.issueNumber.toString(),
|
|
20
|
+
"--body", params.body,
|
|
21
|
+
"--repo", targetRepo
|
|
22
|
+
]);
|
|
16
23
|
if (result.exitCode !== 0) {
|
|
17
24
|
throw new Error(`Failed to post comment: ${sanitizeError(result.stderr)}`);
|
|
18
25
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { runCommand } from "../lib/shell.js";
|
|
2
|
+
import { resolveRepo } from "../lib/git.js";
|
|
2
3
|
import { sanitizeError } from "../lib/error_utils.js";
|
|
3
4
|
import { IssueUpdateInputSchema, IssueUpdateResponseSchema } from "./contract.js";
|
|
4
5
|
export { IssueUpdateInputSchema };
|
|
5
6
|
export async function issueUpdateHandler(args) {
|
|
6
7
|
const params = IssueUpdateInputSchema.parse(args);
|
|
8
|
+
const targetRepo = resolveRepo(params.repo);
|
|
7
9
|
const cmdArgs = ["gh", "issue-update", params.issueNumber.toString()];
|
|
8
10
|
if (params.body)
|
|
9
11
|
cmdArgs.push("--body", params.body);
|
|
@@ -17,6 +19,7 @@ export async function issueUpdateHandler(args) {
|
|
|
17
19
|
cmdArgs.push("--remove-labels", params.removeLabels.join(","));
|
|
18
20
|
if (params.state)
|
|
19
21
|
cmdArgs.push("--state", params.state);
|
|
22
|
+
cmdArgs.push("--repo", targetRepo);
|
|
20
23
|
const result = await runCommand("td-cli", cmdArgs);
|
|
21
24
|
if (result.exitCode !== 0) {
|
|
22
25
|
throw new Error(`Failed to update issue: ${sanitizeError(result.stderr)}`);
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { runCommand } from "../lib/shell.js";
|
|
3
|
+
import { resolveRepo } from "../lib/git.js";
|
|
3
4
|
import { sanitizeError } from "../lib/error_utils.js";
|
|
4
5
|
export const IssueViewInputSchema = z.object({
|
|
5
6
|
issueNumber: z.number().describe("The number of the issue to view."),
|
|
7
|
+
repo: z.string().optional().describe("The target repository override (e.g. org/repo)."),
|
|
6
8
|
});
|
|
7
9
|
const IssueViewOutputSchema = z.object({
|
|
8
10
|
status: z.string(),
|
|
@@ -16,7 +18,8 @@ const IssueViewOutputSchema = z.object({
|
|
|
16
18
|
});
|
|
17
19
|
export async function issueViewHandler(args) {
|
|
18
20
|
const params = IssueViewInputSchema.parse(args);
|
|
19
|
-
const
|
|
21
|
+
const targetRepo = resolveRepo(params.repo);
|
|
22
|
+
const result = await runCommand("td-cli", ["gh", "issue-view", params.issueNumber.toString(), "--repo", targetRepo]);
|
|
20
23
|
if (result.exitCode !== 0) {
|
|
21
24
|
throw new Error(`Failed to view issue: ${sanitizeError(result.stderr)}`);
|
|
22
25
|
}
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { runCommand } from "../lib/shell.js";
|
|
3
|
+
import { resolveRepo } from "../lib/git.js";
|
|
3
4
|
export const SearchOpenPrsInputSchema = z.object({
|
|
4
5
|
state: z.enum(["open", "closed", "all"]).optional().default("open"),
|
|
5
6
|
includeDrafts: z.boolean().optional().default(true),
|
|
6
7
|
limit: z.number().min(1).max(100).optional().default(100),
|
|
7
8
|
labels: z.array(z.string()).optional(),
|
|
9
|
+
repo: z.string().optional().describe("The target repository override (e.g. org/repo)."),
|
|
8
10
|
});
|
|
9
11
|
export async function searchOpenPrsHandler(args) {
|
|
10
12
|
const params = SearchOpenPrsInputSchema.parse(args);
|
|
13
|
+
const targetRepo = resolveRepo(params.repo);
|
|
11
14
|
const tdArgs = [
|
|
12
15
|
"gh", "search-prs",
|
|
13
16
|
"--state", params.state,
|
|
14
17
|
"--limit", params.limit.toString(),
|
|
18
|
+
"--repo", targetRepo,
|
|
15
19
|
];
|
|
16
20
|
if (!params.includeDrafts) {
|
|
17
21
|
tdArgs.push("--no-include-drafts");
|
|
@@ -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,33 +1,53 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { runCommand } from "../lib/shell.js";
|
|
3
|
+
import { config } from "../config.js";
|
|
3
4
|
export const CommitPatchInputSchema = z.object({
|
|
4
|
-
worktreePath: z.string(),
|
|
5
|
+
worktreePath: z.string().optional(),
|
|
5
6
|
message: z.string(),
|
|
6
7
|
allowedFiles: z.array(z.string()),
|
|
7
|
-
writeMode: z.boolean().optional().default(
|
|
8
|
+
writeMode: z.boolean().optional().default(true),
|
|
8
9
|
});
|
|
9
10
|
export async function commitPatchHandler(args) {
|
|
10
|
-
|
|
11
|
+
const parsed = CommitPatchInputSchema.parse(args || {});
|
|
12
|
+
const worktreePath = parsed.worktreePath || config.repoPath;
|
|
13
|
+
if (parsed.writeMode === false) {
|
|
11
14
|
throw new Error("writeMode must be true to commit changes.");
|
|
12
15
|
}
|
|
13
|
-
const statusResult = await runCommand("git", ["status", "--porcelain"], { cwd:
|
|
14
|
-
const changedFiles = statusResult.stdout
|
|
15
|
-
|
|
16
|
+
const statusResult = await runCommand("git", ["status", "--porcelain"], { cwd: worktreePath });
|
|
17
|
+
const changedFiles = statusResult.stdout
|
|
18
|
+
.split("\n")
|
|
19
|
+
.map((l) => (l.endsWith("\r") ? l.slice(0, -1) : l))
|
|
20
|
+
.filter((l) => l.trim().length > 0)
|
|
21
|
+
.map((l) => {
|
|
22
|
+
const match = l.match(/^..\s+(.*)$/);
|
|
23
|
+
if (!match)
|
|
24
|
+
return l.slice(3).trim();
|
|
25
|
+
let file = match[1].trim();
|
|
26
|
+
if (file.includes(" -> ")) {
|
|
27
|
+
file = file.split(" -> ").pop().trim();
|
|
28
|
+
}
|
|
29
|
+
if (file.startsWith('"') && file.endsWith('"')) {
|
|
30
|
+
file = file.slice(1, -1);
|
|
31
|
+
}
|
|
32
|
+
return file;
|
|
33
|
+
})
|
|
34
|
+
.filter(Boolean);
|
|
35
|
+
const forbiddenFiles = changedFiles.filter((f) => !parsed.allowedFiles.includes(f));
|
|
16
36
|
if (forbiddenFiles.length > 0) {
|
|
17
37
|
throw new Error(`The following files are not in the allowed list: ${forbiddenFiles.join(", ")}`);
|
|
18
38
|
}
|
|
19
39
|
if (changedFiles.length === 0) {
|
|
20
40
|
throw new Error("No changes to commit.");
|
|
21
41
|
}
|
|
22
|
-
await runCommand("git", ["add", "
|
|
23
|
-
const commitResult = await runCommand("git", ["commit", "-m",
|
|
42
|
+
await runCommand("git", ["add", "--", ...changedFiles], { cwd: worktreePath });
|
|
43
|
+
const commitResult = await runCommand("git", ["commit", "-m", parsed.message], { cwd: worktreePath });
|
|
24
44
|
if (commitResult.exitCode !== 0) {
|
|
25
45
|
throw new Error(`Failed to commit: ${commitResult.stderr}`);
|
|
26
46
|
}
|
|
27
|
-
const shaResult = await runCommand("git", ["rev-parse", "HEAD"], { cwd:
|
|
47
|
+
const shaResult = await runCommand("git", ["rev-parse", "HEAD"], { cwd: worktreePath });
|
|
28
48
|
return {
|
|
29
49
|
success: true,
|
|
30
50
|
commitSha: shaResult.stdout.trim(),
|
|
31
|
-
changedFiles
|
|
51
|
+
changedFiles,
|
|
32
52
|
};
|
|
33
53
|
}
|
|
@@ -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
|
+
}
|
package/dist/tools/repo.logs.js
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { runTDCli } from "
|
|
2
|
+
import { runTDCli } from "./jules/shared.js";
|
|
3
3
|
export const RepoLogsInputSchema = z.object({
|
|
4
4
|
prNumber: z.number(),
|
|
5
5
|
grep: z.string().optional(),
|
|
6
|
+
repo: z.string().optional(),
|
|
6
7
|
});
|
|
7
8
|
export async function repoLogsHandler(args) {
|
|
8
9
|
const params = ["repo", "logs", args.prNumber.toString()];
|
|
9
10
|
if (args.grep) {
|
|
10
11
|
params.push("--grep", args.grep);
|
|
11
12
|
}
|
|
13
|
+
if (args.repo) {
|
|
14
|
+
params.push("--repo", args.repo);
|
|
15
|
+
}
|
|
12
16
|
const output = await runTDCli(params);
|
|
13
17
|
return {
|
|
14
18
|
logs: output.logs || (output.data ? output.data.logs : undefined)
|
|
@@ -1,6 +1,32 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { config } from "../config.js";
|
|
3
|
-
import {
|
|
3
|
+
import { execSync } from "child_process";
|
|
4
|
+
import * as fs from "fs";
|
|
5
|
+
import * as path from "path";
|
|
6
|
+
export const loadStandaloneAgentContext = (repoRoot = process.cwd()) => {
|
|
7
|
+
const configPath = path.join(repoRoot, "project_config.json");
|
|
8
|
+
let projectConfig = {};
|
|
9
|
+
if (fs.existsSync(configPath)) {
|
|
10
|
+
try {
|
|
11
|
+
projectConfig = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
12
|
+
}
|
|
13
|
+
catch (e) {
|
|
14
|
+
console.warn(`[MCP Warning] Failed to parse ${configPath}:`, e);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
let gitCommit = "unknown";
|
|
18
|
+
try {
|
|
19
|
+
gitCommit = execSync("git rev-parse HEAD", { cwd: repoRoot, encoding: "utf-8" }).trim();
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// Fallback if git is unavailable
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
packageName: projectConfig.packageName ?? "boomtick",
|
|
26
|
+
gitCommit,
|
|
27
|
+
config: projectConfig,
|
|
28
|
+
};
|
|
29
|
+
};
|
|
4
30
|
export const ReadAgentContextInputSchema = z.object({});
|
|
5
31
|
export async function readAgentContextHandler(_args) {
|
|
6
32
|
const context = loadStandaloneAgentContext(config.repoPath);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { loadStandaloneAgentContext } from "
|
|
2
|
+
import { loadStandaloneAgentContext } from "./repo.read_agent_context.js";
|
|
3
3
|
describe("Standalone Agent Context Loader", () => {
|
|
4
4
|
it("should load agent context without submodule references", () => {
|
|
5
5
|
const context = loadStandaloneAgentContext(process.cwd());
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { runTDCli } from "
|
|
2
|
+
import { runTDCli } from "./jules/shared.js";
|
|
3
3
|
export const ReadCiLogsInputSchema = z.object({
|
|
4
4
|
prNumber: z.number(),
|
|
5
5
|
all: z.boolean().optional(),
|
|
6
|
+
repo: z.string().optional(),
|
|
6
7
|
});
|
|
7
8
|
export async function readCiLogsHandler(args) {
|
|
8
9
|
const params = ["repo", "ci-logs", args.prNumber.toString()];
|
|
9
10
|
if (args.all) {
|
|
10
11
|
params.push("--all");
|
|
11
12
|
}
|
|
13
|
+
if (args.repo) {
|
|
14
|
+
params.push("--repo", args.repo);
|
|
15
|
+
}
|
|
12
16
|
const output = await runTDCli(params);
|
|
13
17
|
return {
|
|
14
18
|
checks: output.checks,
|
|
@@ -5,8 +5,30 @@ export const RunTestsInputSchema = z.object({
|
|
|
5
5
|
timeoutSeconds: z.number().optional().default(300),
|
|
6
6
|
worktreePath: z.string().optional(),
|
|
7
7
|
});
|
|
8
|
+
function parseCommandArguments(fullCmd) {
|
|
9
|
+
const tokens = [];
|
|
10
|
+
const regex = /"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|(\S+)/g;
|
|
11
|
+
let match;
|
|
12
|
+
while ((match = regex.exec(fullCmd)) !== null) {
|
|
13
|
+
if (match[1] !== undefined) {
|
|
14
|
+
tokens.push(match[1].replace(/\\"/g, '"'));
|
|
15
|
+
}
|
|
16
|
+
else if (match[2] !== undefined) {
|
|
17
|
+
tokens.push(match[2].replace(/\\'/g, "'"));
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
tokens.push(match[3]);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (tokens.length === 0) {
|
|
24
|
+
return ["", []];
|
|
25
|
+
}
|
|
26
|
+
const [cmd, ...args] = tokens;
|
|
27
|
+
return [cmd, args];
|
|
28
|
+
}
|
|
8
29
|
export async function runTestsHandler(args) {
|
|
9
|
-
const
|
|
30
|
+
const parsed = RunTestsInputSchema.parse(args || {});
|
|
31
|
+
const commands = parsed.commands || [
|
|
10
32
|
"pnpm install --frozen-lockfile",
|
|
11
33
|
"pnpm lint",
|
|
12
34
|
"pnpm test",
|
|
@@ -15,11 +37,11 @@ export async function runTestsHandler(args) {
|
|
|
15
37
|
const results = [];
|
|
16
38
|
let success = true;
|
|
17
39
|
for (const fullCmd of commands) {
|
|
18
|
-
const [cmd,
|
|
40
|
+
const [cmd, cmdArgs] = parseCommandArguments(fullCmd);
|
|
19
41
|
try {
|
|
20
42
|
const res = await runCommand(cmd, cmdArgs, {
|
|
21
|
-
cwd:
|
|
22
|
-
timeout:
|
|
43
|
+
cwd: parsed.worktreePath,
|
|
44
|
+
timeout: parsed.timeoutSeconds * 1000
|
|
23
45
|
});
|
|
24
46
|
results.push(res);
|
|
25
47
|
if (res.exitCode !== 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arii/boomtick-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Boomtick MCP Guide: PR Rescue + Merge Conflict Agent",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@modelcontextprotocol/sdk": "^1.6.0",
|
|
20
|
-
"dotenv": "^17.4.2",
|
|
21
20
|
"zod": "^4.4.3"
|
|
22
21
|
},
|
|
23
22
|
"engines": {
|
|
@@ -27,17 +26,15 @@
|
|
|
27
26
|
"devDependencies": {
|
|
28
27
|
"@types/node": "^20.17.15",
|
|
29
28
|
"json-schema-to-zod": "^2.8.1",
|
|
30
|
-
"shx": "^0.4.0",
|
|
31
29
|
"tsx": "^4.22.4",
|
|
32
30
|
"typescript": "^5.7.3",
|
|
33
|
-
"vitest": "^4.1.9"
|
|
34
|
-
"yaml": "^2.9.0"
|
|
31
|
+
"vitest": "^4.1.9"
|
|
35
32
|
},
|
|
36
33
|
"scripts": {
|
|
37
|
-
"build": "tsx scripts/sync-contracts.ts && tsc &&
|
|
34
|
+
"build": "tsx scripts/sync-contracts.ts && tsc && tsx scripts/sync-mcp-schemas.ts",
|
|
38
35
|
"watch": "tsc -w",
|
|
39
36
|
"start": "node dist/index.js",
|
|
40
|
-
"test": "tsx scripts/sync-contracts.ts && vitest run",
|
|
37
|
+
"test": "tsx scripts/sync-contracts.ts && tsx scripts/sync-mcp-schemas.ts && vitest run",
|
|
41
38
|
"run-evals": "node dist/evals/run-evals.js",
|
|
42
39
|
"sync:mcp-schemas": "tsx scripts/sync-mcp-schemas.ts",
|
|
43
40
|
"release:patch": "pnpm version patch",
|
|
@@ -69,7 +69,12 @@ async function syncSchemas() {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
mcpTools.forEach((tool) => {
|
|
72
|
-
const
|
|
72
|
+
const exportedSchema = {
|
|
73
|
+
name: tool.name,
|
|
74
|
+
description: tool.description,
|
|
75
|
+
parameters: tool.inputSchema,
|
|
76
|
+
};
|
|
77
|
+
const schemaContent = JSON.stringify(exportedSchema, null, 2);
|
|
73
78
|
const fileName = `${tool.name}.json`;
|
|
74
79
|
|
|
75
80
|
validTargets.forEach(targetDir => {
|