@arii/boomtick-mcp 0.3.3
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/.env.example +6 -0
- package/.mcp/schemas/agent.search_ddgs.json +1 -0
- package/.mcp/schemas/boomtick.health.json +1 -0
- package/.mcp/schemas/github.checkout_branch.json +1 -0
- package/.mcp/schemas/github.comment_triage_summary.json +1 -0
- package/.mcp/schemas/github.create_issue.json +1 -0
- package/.mcp/schemas/github.create_pull_request.json +1 -0
- package/.mcp/schemas/github.get_merge_conflict_files.json +1 -0
- package/.mcp/schemas/github.get_pr.json +1 -0
- package/.mcp/schemas/github.get_pr_diff.json +1 -0
- package/.mcp/schemas/github.issue_comment.json +1 -0
- package/.mcp/schemas/github.issue_update.json +1 -0
- package/.mcp/schemas/github.issue_view.json +1 -0
- package/.mcp/schemas/github.open_replacement_pr.json +1 -0
- package/.mcp/schemas/github.search_open_prs.json +1 -0
- package/.mcp/schemas/jules.cancel_session.json +1 -0
- package/.mcp/schemas/jules.create_session.json +1 -0
- package/.mcp/schemas/jules.get_messages.json +1 -0
- package/.mcp/schemas/jules.get_pr.json +1 -0
- package/.mcp/schemas/jules.get_session.json +1 -0
- package/.mcp/schemas/jules.list_sessions.json +1 -0
- package/.mcp/schemas/jules.send_message.json +1 -0
- package/.mcp/schemas/jules.trigger_feedback.json +1 -0
- package/.mcp/schemas/repo.commit_patch.json +1 -0
- package/.mcp/schemas/repo.create_branch.json +1 -0
- package/.mcp/schemas/repo.create_repair_branch.json +1 -0
- package/.mcp/schemas/repo.get_changed_files.json +1 -0
- package/.mcp/schemas/repo.get_command_schema.json +1 -0
- package/.mcp/schemas/repo.get_package_scripts.json +1 -0
- package/.mcp/schemas/repo.get_route_map.json +1 -0
- package/.mcp/schemas/repo.logs.json +1 -0
- package/.mcp/schemas/repo.read_agent_context.json +1 -0
- package/.mcp/schemas/repo.read_ci_logs.json +1 -0
- package/.mcp/schemas/repo.run_lighthouse.json +1 -0
- package/.mcp/schemas/repo.run_playwright.json +1 -0
- package/.mcp/schemas/repo.run_tests.json +1 -0
- package/CHANGELOG.md +50 -0
- package/README.md +108 -0
- package/actions/ai-review/action.yml +15 -0
- package/actions/audit/action.yml +37 -0
- package/actions/ci-validate/action.yml +9 -0
- package/actions/impact-analysis/action.yml +385 -0
- package/actions/lint-typecheck/action.yml +19 -0
- package/actions/run-project-gate/action.yml +163 -0
- package/actions/setup/action.yml +15 -0
- package/actions/setup-workspace/action.yml +179 -0
- package/actions/test-build/action.yml +71 -0
- package/actions/update-pr-comment/action.yml +101 -0
- package/dist/config.js +110 -0
- package/dist/evals/run-evals.js +38 -0
- package/dist/index.js +10 -0
- package/dist/lib/error_utils.js +8 -0
- package/dist/lib/git.js +33 -0
- package/dist/lib/result.js +21 -0
- package/dist/lib/shell.js +91 -0
- package/dist/lib/shell.test.js +10 -0
- package/dist/lib/td-cli.js +27 -0
- package/dist/lib/test-utils.js +20 -0
- package/dist/mcp/definitions.js +242 -0
- package/dist/mcp/server.js +317 -0
- package/dist/mcp/tools.js +18 -0
- package/dist/tools/contract.js +2069 -0
- package/dist/tools/ddgs.search.js +50 -0
- package/dist/tools/ddgs.search.test.js +67 -0
- package/dist/tools/ddgs_search.py +23 -0
- package/dist/tools/github.checkout_branch.js +20 -0
- package/dist/tools/github.comment_triage_summary.js +25 -0
- package/dist/tools/github.create_issue.js +28 -0
- package/dist/tools/github.create_issue.test.js +54 -0
- package/dist/tools/github.create_pull_request.js +44 -0
- package/dist/tools/github.get_merge_conflict_files.js +26 -0
- package/dist/tools/github.get_pr.js +18 -0
- package/dist/tools/github.get_pr_diff.js +24 -0
- package/dist/tools/github.issue_comment.js +24 -0
- package/dist/tools/github.issue_update.js +29 -0
- package/dist/tools/github.issue_view.js +28 -0
- package/dist/tools/github.open_replacement_pr.js +41 -0
- package/dist/tools/github.search_open_prs.js +31 -0
- package/dist/tools/github.search_open_prs.test.js +51 -0
- package/dist/tools/jules/cancel-session.js +20 -0
- package/dist/tools/jules/create-session.js +20 -0
- package/dist/tools/jules/create-session.test.js +99 -0
- package/dist/tools/jules/get-messages.js +19 -0
- package/dist/tools/jules/get-messages.test.js +45 -0
- package/dist/tools/jules/get-pr.js +14 -0
- package/dist/tools/jules/get-pr.test.js +37 -0
- package/dist/tools/jules/get-session.js +10 -0
- package/dist/tools/jules/list-sessions.js +43 -0
- package/dist/tools/jules/send-message.js +22 -0
- package/dist/tools/jules/send-message.test.js +56 -0
- package/dist/tools/jules/shared.js +26 -0
- package/dist/tools/jules/trigger-feedback.js +16 -0
- package/dist/tools/jules/trigger-feedback.test.js +42 -0
- package/dist/tools/repo.commit_patch.js +33 -0
- package/dist/tools/repo.create_branch.js +21 -0
- package/dist/tools/repo.create_branch.test.js +42 -0
- package/dist/tools/repo.create_repair_branch.js +38 -0
- package/dist/tools/repo.get_changed_files.js +21 -0
- package/dist/tools/repo.get_command_schema.js +27 -0
- package/dist/tools/repo.get_package_scripts.js +35 -0
- package/dist/tools/repo.get_package_scripts.test.js +39 -0
- package/dist/tools/repo.get_route_map.js +37 -0
- package/dist/tools/repo.logs.js +16 -0
- package/dist/tools/repo.read_agent_context.js +20 -0
- package/dist/tools/repo.read_ci_logs.js +18 -0
- package/dist/tools/repo.run_lighthouse.js +37 -0
- package/dist/tools/repo.run_playwright.js +29 -0
- package/dist/tools/repo.run_tests.js +43 -0
- package/dist/tools/types.js +1 -0
- package/docs/testing.md +73 -0
- package/package.json +45 -0
- package/scripts/create_instructions.sh +42 -0
- package/scripts/export-mcp-schema.ts +7 -0
- package/scripts/sync-contracts.ts +74 -0
- package/scripts/sync-mcp-schemas.ts +97 -0
- package/src/agents/conflict-scout.prompt.md +20 -0
- package/src/agents/pr-consolidation.prompt.md +32 -0
- package/src/agents/pr-writer.prompt.md +13 -0
- package/src/agents/repair-agent.prompt.md +18 -0
- package/src/agents/repo-context.prompt.md +20 -0
- package/src/agents/verifier-agent.prompt.md +22 -0
- package/src/config.ts +113 -0
- package/src/evals/fixtures/merge-conflict-simple/README.md +0 -0
- package/src/evals/run-evals.ts +40 -0
- package/src/index.ts +12 -0
- package/src/lib/error_utils.ts +8 -0
- package/src/lib/git.ts +41 -0
- package/src/lib/result.ts +28 -0
- package/src/lib/shell.test.ts +11 -0
- package/src/lib/shell.ts +118 -0
- package/src/lib/td-cli.ts +38 -0
- package/src/lib/test-utils.ts +23 -0
- package/src/mcp/definitions.ts +278 -0
- package/src/mcp/server.ts +359 -0
- package/src/mcp/tools.ts +20 -0
- package/src/tools/contract.ts +2245 -0
- package/src/tools/ddgs.search.test.ts +92 -0
- package/src/tools/ddgs.search.ts +58 -0
- package/src/tools/ddgs_search.py +23 -0
- package/src/tools/github.checkout_branch.ts +25 -0
- package/src/tools/github.comment_triage_summary.ts +30 -0
- package/src/tools/github.create_issue.test.ts +63 -0
- package/src/tools/github.create_issue.ts +34 -0
- package/src/tools/github.create_pull_request.ts +50 -0
- package/src/tools/github.get_merge_conflict_files.ts +31 -0
- package/src/tools/github.get_pr.ts +24 -0
- package/src/tools/github.get_pr_diff.ts +29 -0
- package/src/tools/github.issue_comment.ts +31 -0
- package/src/tools/github.issue_update.ts +31 -0
- package/src/tools/github.issue_view.ts +35 -0
- package/src/tools/github.open_replacement_pr.ts +48 -0
- package/src/tools/github.search_open_prs.test.ts +60 -0
- package/src/tools/github.search_open_prs.ts +40 -0
- package/src/tools/jules/cancel-session.ts +26 -0
- package/src/tools/jules/create-session.test.ts +114 -0
- package/src/tools/jules/create-session.ts +24 -0
- package/src/tools/jules/get-messages.test.ts +52 -0
- package/src/tools/jules/get-messages.ts +24 -0
- package/src/tools/jules/get-pr.test.ts +43 -0
- package/src/tools/jules/get-pr.ts +17 -0
- package/src/tools/jules/get-session.ts +12 -0
- package/src/tools/jules/list-sessions.ts +50 -0
- package/src/tools/jules/send-message.test.ts +66 -0
- package/src/tools/jules/send-message.ts +27 -0
- package/src/tools/jules/shared.ts +28 -0
- package/src/tools/jules/trigger-feedback.test.ts +50 -0
- package/src/tools/jules/trigger-feedback.ts +21 -0
- package/src/tools/repo.commit_patch.ts +41 -0
- package/src/tools/repo.create_branch.test.ts +55 -0
- package/src/tools/repo.create_branch.ts +29 -0
- package/src/tools/repo.create_repair_branch.ts +47 -0
- package/src/tools/repo.get_changed_files.ts +26 -0
- package/src/tools/repo.get_command_schema.ts +30 -0
- package/src/tools/repo.get_package_scripts.test.ts +51 -0
- package/src/tools/repo.get_package_scripts.ts +36 -0
- package/src/tools/repo.get_route_map.ts +41 -0
- package/src/tools/repo.logs.ts +21 -0
- package/src/tools/repo.read_agent_context.ts +22 -0
- package/src/tools/repo.read_ci_logs.ts +21 -0
- package/src/tools/repo.run_lighthouse.ts +40 -0
- package/src/tools/repo.run_playwright.ts +35 -0
- package/src/tools/repo.run_tests.ts +47 -0
- package/src/tools/types.ts +8 -0
- package/start_browsermcp.sh +14 -0
- package/start_github_mcp.sh +6 -0
- package/tsconfig.json +15 -0
- package/vitest.config.ts +9 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import fs from "fs/promises";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { config } from "../config.js";
|
|
5
|
+
export const GetPackageScriptsInputSchema = z.object({
|
|
6
|
+
filter: z.string().optional(),
|
|
7
|
+
});
|
|
8
|
+
export async function getPackageScriptsHandler(args) {
|
|
9
|
+
GetPackageScriptsInputSchema.parse(args);
|
|
10
|
+
const packageJsonPath = path.join(config.repoPath, "package.json");
|
|
11
|
+
try {
|
|
12
|
+
const content = await fs.readFile(packageJsonPath, "utf-8");
|
|
13
|
+
let pkg;
|
|
14
|
+
try {
|
|
15
|
+
pkg = JSON.parse(content);
|
|
16
|
+
}
|
|
17
|
+
catch (e) {
|
|
18
|
+
throw new Error(`Malformed package.json: ${e instanceof Error ? e.message : String(e)}`);
|
|
19
|
+
}
|
|
20
|
+
let scripts = pkg.scripts || {};
|
|
21
|
+
if (args.filter) {
|
|
22
|
+
const filtered = {};
|
|
23
|
+
for (const [name, cmd] of Object.entries(scripts)) {
|
|
24
|
+
if (name.includes(args.filter)) {
|
|
25
|
+
filtered[name] = cmd;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
scripts = filtered;
|
|
29
|
+
}
|
|
30
|
+
return { scripts };
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
throw new Error(`Failed to read package.json: ${error instanceof Error ? error.message : String(error)}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { getPackageScriptsHandler } from "./repo.get_package_scripts.js";
|
|
3
|
+
import fs from "fs/promises";
|
|
4
|
+
vi.mock("fs/promises");
|
|
5
|
+
describe("repo.get_package_scripts", () => {
|
|
6
|
+
it("should return scripts from package.json", async () => {
|
|
7
|
+
const mockPkg = {
|
|
8
|
+
scripts: { test: "vitest" }
|
|
9
|
+
};
|
|
10
|
+
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(mockPkg));
|
|
11
|
+
const result = await getPackageScriptsHandler({});
|
|
12
|
+
expect(result.scripts.test).toBe("vitest");
|
|
13
|
+
});
|
|
14
|
+
it("should handle missing scripts", async () => {
|
|
15
|
+
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify({}));
|
|
16
|
+
const result = await getPackageScriptsHandler({});
|
|
17
|
+
expect(result.scripts).toEqual({});
|
|
18
|
+
});
|
|
19
|
+
it("should throw error when file not found", async () => {
|
|
20
|
+
vi.mocked(fs.readFile).mockRejectedValue(new Error("File not found"));
|
|
21
|
+
await expect(getPackageScriptsHandler({})).rejects.toThrow("Failed to read package.json: File not found");
|
|
22
|
+
});
|
|
23
|
+
it("should filter scripts by pattern", async () => {
|
|
24
|
+
const mockPkg = {
|
|
25
|
+
scripts: {
|
|
26
|
+
test: "vitest",
|
|
27
|
+
build: "vite build",
|
|
28
|
+
"test:ui": "vitest --ui"
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(mockPkg));
|
|
32
|
+
const result = await getPackageScriptsHandler({ filter: "test" });
|
|
33
|
+
expect(result.scripts).toEqual({
|
|
34
|
+
test: "vitest",
|
|
35
|
+
"test:ui": "vitest --ui"
|
|
36
|
+
});
|
|
37
|
+
expect(result.scripts.build).toBeUndefined();
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCommand } from "../lib/shell.js";
|
|
3
|
+
import { config } from "../config.js";
|
|
4
|
+
import path from "path";
|
|
5
|
+
export const GetRouteMapInputSchema = z.object({
|
|
6
|
+
includeStatic: z.boolean().optional(),
|
|
7
|
+
});
|
|
8
|
+
export async function getRouteMapHandler(args) {
|
|
9
|
+
GetRouteMapInputSchema.parse(args);
|
|
10
|
+
// Logic based on tech-dancer repo structure: src/config/routes.ts and content/
|
|
11
|
+
const routesPath = path.join(config.repoPath, "src/config/routes.ts");
|
|
12
|
+
// For simplicity in MVP, we'll try to find routes by listing content files
|
|
13
|
+
// and reading the main routes file if it exists.
|
|
14
|
+
let routeMap = {};
|
|
15
|
+
try {
|
|
16
|
+
const gitFiles = await runCommand("git", ["ls-files", "content/"]);
|
|
17
|
+
const contentFiles = gitFiles.stdout.trim().split("\n");
|
|
18
|
+
for (const file of contentFiles) {
|
|
19
|
+
if (file.endsWith(".md")) {
|
|
20
|
+
const slug = path.basename(file, ".md");
|
|
21
|
+
if (file.includes("/posts/")) {
|
|
22
|
+
routeMap[`/blog/${slug}`] = file;
|
|
23
|
+
}
|
|
24
|
+
else if (file.includes("/resources/")) {
|
|
25
|
+
routeMap[`/resources/${slug}`] = file;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
// If no content files, that's fine
|
|
32
|
+
}
|
|
33
|
+
if (args.includeStatic) {
|
|
34
|
+
routeMap["/favicon.ico"] = "public/favicon.ico";
|
|
35
|
+
}
|
|
36
|
+
return { routeMap };
|
|
37
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runTDCli } from "../lib/td-cli.js";
|
|
3
|
+
export const RepoLogsInputSchema = z.object({
|
|
4
|
+
prNumber: z.number(),
|
|
5
|
+
grep: z.string().optional(),
|
|
6
|
+
});
|
|
7
|
+
export async function repoLogsHandler(args) {
|
|
8
|
+
const params = ["repo", "logs", args.prNumber.toString()];
|
|
9
|
+
if (args.grep) {
|
|
10
|
+
params.push("--grep", args.grep);
|
|
11
|
+
}
|
|
12
|
+
const output = await runTDCli(params);
|
|
13
|
+
return {
|
|
14
|
+
logs: output.logs || (output.data ? output.data.logs : undefined)
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import fs from "fs/promises";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { config } from "../config.js";
|
|
5
|
+
export const ReadAgentContextInputSchema = z.object({});
|
|
6
|
+
export async function readAgentContextHandler(_args) {
|
|
7
|
+
const contextPath = path.join(config.repoPath, ".agent-context.json");
|
|
8
|
+
try {
|
|
9
|
+
const content = await fs.readFile(contextPath, "utf-8");
|
|
10
|
+
const parsed = JSON.parse(content);
|
|
11
|
+
return {
|
|
12
|
+
status: "success",
|
|
13
|
+
...parsed
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
catch (e) {
|
|
17
|
+
console.error("Failed to read agent context:", e.message);
|
|
18
|
+
throw new Error(`Failed to read agent context from ${contextPath}. Ensure it exists by running 'td-cli context-warm'.`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runTDCli } from "../lib/td-cli.js";
|
|
3
|
+
export const ReadCiLogsInputSchema = z.object({
|
|
4
|
+
prNumber: z.number(),
|
|
5
|
+
all: z.boolean().optional(),
|
|
6
|
+
});
|
|
7
|
+
export async function readCiLogsHandler(args) {
|
|
8
|
+
const params = ["repo", "ci-logs", args.prNumber.toString()];
|
|
9
|
+
if (args.all) {
|
|
10
|
+
params.push("--all");
|
|
11
|
+
}
|
|
12
|
+
const output = await runTDCli(params);
|
|
13
|
+
return {
|
|
14
|
+
checks: output.checks,
|
|
15
|
+
failedChecks: output.failedChecks,
|
|
16
|
+
logs: output.logs
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCommand } from "../lib/shell.js";
|
|
3
|
+
export const RunLighthouseInputSchema = z.object({
|
|
4
|
+
route: z.string().optional().default("/"),
|
|
5
|
+
worktreePath: z.string().optional(),
|
|
6
|
+
});
|
|
7
|
+
export async function runLighthouseHandler(args) {
|
|
8
|
+
const results = await runCommand("pnpm", ["lhci", "autorun", "--json"], { cwd: args.worktreePath });
|
|
9
|
+
let summary = {};
|
|
10
|
+
const failures = [];
|
|
11
|
+
try {
|
|
12
|
+
// Attempt to parse summary from LHCI output if available
|
|
13
|
+
// For MVP, we'll provide a structured placeholder if actual JSON isn't present
|
|
14
|
+
if (results.stdout.includes("{")) {
|
|
15
|
+
const possibleJson = results.stdout.substring(results.stdout.indexOf("{"));
|
|
16
|
+
summary = JSON.parse(possibleJson);
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
summary = {
|
|
20
|
+
performance: 0,
|
|
21
|
+
accessibility: 0,
|
|
22
|
+
bestPractices: 0,
|
|
23
|
+
seo: 0
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
failures.push("Failed to parse Lighthouse report");
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
success: results.exitCode === 0,
|
|
32
|
+
route: args.route,
|
|
33
|
+
summary,
|
|
34
|
+
failures,
|
|
35
|
+
command: results.command
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCommand } from "../lib/shell.js";
|
|
3
|
+
export const RunPlaywrightInputSchema = z.object({
|
|
4
|
+
grep: z.string().optional(),
|
|
5
|
+
worktreePath: z.string().optional(),
|
|
6
|
+
});
|
|
7
|
+
export async function runPlaywrightHandler(args) {
|
|
8
|
+
const tdArgs = ["repo", "run-playwright"];
|
|
9
|
+
if (args.grep) {
|
|
10
|
+
tdArgs.push("--grep", args.grep);
|
|
11
|
+
}
|
|
12
|
+
if (args.worktreePath) {
|
|
13
|
+
tdArgs.push("--worktree", args.worktreePath);
|
|
14
|
+
}
|
|
15
|
+
const result = await runCommand("td-cli", tdArgs);
|
|
16
|
+
if (result.exitCode !== 0) {
|
|
17
|
+
// Playwright might exit with non-zero if tests fail, but our td-cli should ideally handle it
|
|
18
|
+
// and return a JSON with success: false.
|
|
19
|
+
}
|
|
20
|
+
const output = JSON.parse(result.stdout);
|
|
21
|
+
if (output.status === "error") {
|
|
22
|
+
throw new Error(`Failed to run Playwright: ${output.message}`);
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
success: output.success,
|
|
26
|
+
command: output.command,
|
|
27
|
+
failedTests: output.failedTests
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCommand } from "../lib/shell.js";
|
|
3
|
+
export const RunTestsInputSchema = z.object({
|
|
4
|
+
commands: z.array(z.string()).optional(),
|
|
5
|
+
timeoutSeconds: z.number().optional().default(300),
|
|
6
|
+
worktreePath: z.string().optional(),
|
|
7
|
+
});
|
|
8
|
+
export async function runTestsHandler(args) {
|
|
9
|
+
const commands = args.commands || [
|
|
10
|
+
"pnpm install --frozen-lockfile",
|
|
11
|
+
"pnpm lint",
|
|
12
|
+
"pnpm test",
|
|
13
|
+
"pnpm build"
|
|
14
|
+
];
|
|
15
|
+
const results = [];
|
|
16
|
+
let success = true;
|
|
17
|
+
for (const fullCmd of commands) {
|
|
18
|
+
const [cmd, ...cmdArgs] = fullCmd.split(" ");
|
|
19
|
+
try {
|
|
20
|
+
const res = await runCommand(cmd, cmdArgs, {
|
|
21
|
+
cwd: args.worktreePath,
|
|
22
|
+
timeout: args.timeoutSeconds * 1000
|
|
23
|
+
});
|
|
24
|
+
results.push(res);
|
|
25
|
+
if (res.exitCode !== 0) {
|
|
26
|
+
success = false;
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
success = false;
|
|
32
|
+
results.push({
|
|
33
|
+
command: fullCmd,
|
|
34
|
+
stdout: "",
|
|
35
|
+
stderr: e instanceof Error ? e.message : String(e),
|
|
36
|
+
exitCode: 1,
|
|
37
|
+
durationMs: 0
|
|
38
|
+
});
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return { success, results };
|
|
43
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/docs/testing.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Boomtick MCP Testing Plan
|
|
2
|
+
|
|
3
|
+
This document outlines the steps to verify the Boomtick MCP server functionality locally.
|
|
4
|
+
|
|
5
|
+
## 1. Automated Unit Tests
|
|
6
|
+
Run the unit test suite to verify core tool handlers and utilities.
|
|
7
|
+
```bash
|
|
8
|
+
cd boomtick-mcp
|
|
9
|
+
pnpm test
|
|
10
|
+
```
|
|
11
|
+
**Expected Result**: All tests in `src/lib/shell.test.ts`, `src/tools/github.search_open_prs.test.ts`, and `src/tools/repo.get_package_scripts.test.ts` should pass.
|
|
12
|
+
|
|
13
|
+
## 2. Build Verification
|
|
14
|
+
Ensure the TypeScript source compiles correctly to ESM.
|
|
15
|
+
```bash
|
|
16
|
+
cd boomtick-mcp
|
|
17
|
+
pnpm run build
|
|
18
|
+
```
|
|
19
|
+
**Expected Result**: The `dist/` directory is populated with `.js` files and no compilation errors occur.
|
|
20
|
+
|
|
21
|
+
## 3. Evaluation Suite
|
|
22
|
+
Run the internal evaluation runner which simulates tool calls in a real (or mocked) environment.
|
|
23
|
+
```bash
|
|
24
|
+
cd boomtick-mcp
|
|
25
|
+
# Note: Ensure you have a valid .env or appropriate environment variables for repo paths
|
|
26
|
+
pnpm run run-evals
|
|
27
|
+
```
|
|
28
|
+
**Expected Result**:
|
|
29
|
+
- Health check returns status "ok".
|
|
30
|
+
- Repository scripts are successfully extracted.
|
|
31
|
+
- GitHub PR search is attempted (may report auth error if token is missing, which is expected behavior).
|
|
32
|
+
|
|
33
|
+
## 4. MCP Capability Discovery (JSON-RPC)
|
|
34
|
+
Verify that the server correctly registers and exposes its capabilities via the MCP protocol.
|
|
35
|
+
|
|
36
|
+
### List Tools
|
|
37
|
+
```bash
|
|
38
|
+
echo '{ "jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {} }' | node dist/index.js
|
|
39
|
+
```
|
|
40
|
+
**Expected Result**: A JSON response containing the registered tools including `jules.create_session`, `jules.get_session`, etc.
|
|
41
|
+
|
|
42
|
+
### List Resources
|
|
43
|
+
```bash
|
|
44
|
+
echo '{ "jsonrpc": "2.0", "id": 1, "method": "resources/list", "params": {} }' | node dist/index.js
|
|
45
|
+
```
|
|
46
|
+
**Expected Result**: A JSON response containing 8 resources.
|
|
47
|
+
|
|
48
|
+
### List Prompts
|
|
49
|
+
```bash
|
|
50
|
+
echo '{ "jsonrpc": "2.0", "id": 1, "method": "prompts/list", "params": {} }' | node dist/index.js
|
|
51
|
+
```
|
|
52
|
+
**Expected Result**: A JSON response containing 5 prompts.
|
|
53
|
+
|
|
54
|
+
## 5. Manual Resource Access
|
|
55
|
+
Verify that the server can read repository files via the Resource protocol.
|
|
56
|
+
```bash
|
|
57
|
+
echo '{ "jsonrpc": "2.0", "id": 1, "method": "resources/read", "params": { "uri": "repo://package-json" } }' | node dist/index.js
|
|
58
|
+
```
|
|
59
|
+
**Expected Result**: The content of the repository's `package.json` is returned in the `text` field.
|
|
60
|
+
|
|
61
|
+
## 6. Manual Prompt Access
|
|
62
|
+
Verify that agent instructions are correctly retrieved.
|
|
63
|
+
```bash
|
|
64
|
+
echo '{ "jsonrpc": "2.0", "id": 1, "method": "prompts/get", "params": { "name": "conflict-scout" } }' | node dist/index.js
|
|
65
|
+
```
|
|
66
|
+
**Expected Result**: The markdown content of `src/agents/conflict-scout.prompt.md` is returned.
|
|
67
|
+
|
|
68
|
+
## 7. Safety Verification (Write Guards)
|
|
69
|
+
Verify that mutating tools refuse to run without explicit flags.
|
|
70
|
+
```bash
|
|
71
|
+
echo '{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "repo.create_repair_branch", "arguments": { "prNumber": 1 } } }' | node dist/index.js
|
|
72
|
+
```
|
|
73
|
+
**Expected Result**: An error message stating `writeMode must be true`.
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@arii/boomtick-mcp",
|
|
3
|
+
"version": "0.3.3",
|
|
4
|
+
"description": "Boomtick MCP Guide: PR Rescue + Merge Conflict Agent",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "dist/index.js",
|
|
10
|
+
"bin": {
|
|
11
|
+
"boomtick-mcp": "dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsx scripts/sync-contracts.ts && tsc && shx cp src/tools/ddgs_search.py dist/tools/ && tsx scripts/sync-mcp-schemas.ts",
|
|
15
|
+
"watch": "tsc -w",
|
|
16
|
+
"start": "node dist/index.js",
|
|
17
|
+
"test": "tsx scripts/sync-contracts.ts && vitest run",
|
|
18
|
+
"run-evals": "node dist/evals/run-evals.js",
|
|
19
|
+
"sync:mcp-schemas": "tsx scripts/sync-mcp-schemas.ts",
|
|
20
|
+
"release:patch": "pnpm version patch",
|
|
21
|
+
"release:minor": "pnpm version minor",
|
|
22
|
+
"release:major": "pnpm version major",
|
|
23
|
+
"sync-contracts": "tsx scripts/sync-contracts.ts",
|
|
24
|
+
"type-check": "tsx scripts/sync-contracts.ts && tsc",
|
|
25
|
+
"lint": "tsx scripts/sync-contracts.ts && tsc --noEmit"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@modelcontextprotocol/sdk": "^1.6.0",
|
|
29
|
+
"dotenv": "^17.4.2",
|
|
30
|
+
"zod": "^4.4.3"
|
|
31
|
+
},
|
|
32
|
+
"packageManager": "pnpm@10.28.2",
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": "24.x",
|
|
35
|
+
"pnpm": "10.28.2"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^20.17.15",
|
|
39
|
+
"json-schema-to-zod": "^2.8.1",
|
|
40
|
+
"tsx": "^4.22.4",
|
|
41
|
+
"shx": "^0.4.0",
|
|
42
|
+
"typescript": "^5.7.3",
|
|
43
|
+
"vitest": "^4.1.9"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
OUTPUT_FILE="${1:-src/agents/pr-consolidation.prompt.md}"
|
|
5
|
+
mkdir -p "$(dirname "$OUTPUT_FILE")"
|
|
6
|
+
|
|
7
|
+
cat << 'MARKDOWN' > "$OUTPUT_FILE"
|
|
8
|
+
# PR Consolidation Guidelines
|
|
9
|
+
|
|
10
|
+
## Objective
|
|
11
|
+
Identify and propose consolidation of Pull Requests (PRs) that demonstrate high levels of functional or structural overlap to minimize code conflicts and streamline review cycles.
|
|
12
|
+
|
|
13
|
+
## Analysis Criteria
|
|
14
|
+
When reviewing the output of the `pr_overlap.py` analysis, evaluate candidates based on the following:
|
|
15
|
+
|
|
16
|
+
### 1. File Overlap Density
|
|
17
|
+
* **High Overlap:** PRs sharing identical files or logical components (e.g., core styles, shared UI components). These are primary candidates for consolidation.
|
|
18
|
+
* **Context:** Check if the overlapping files are `design-tokens.ts` or `ci.yml`. Broad overlaps in infrastructure files might indicate a need for coordination rather than full consolidation.
|
|
19
|
+
|
|
20
|
+
### 2. Functional Alignment (PR Titles & Descriptions)
|
|
21
|
+
* Review the PR titles to determine if they share a common feature set (e.g., "Mobile UI" or "Home Page Performance").
|
|
22
|
+
* Look for duplicate effort; if two PRs address the same UI improvement, they should be merged into a single branch.
|
|
23
|
+
|
|
24
|
+
### 3. Linked Issue Dependency
|
|
25
|
+
* Cross-reference the `(Fixes: #...)` metadata.
|
|
26
|
+
* If multiple PRs resolve related issues (e.g., #101 and #102 are both part of the same UX audit), suggest consolidating these to ensure the full feature set is verified as one unit.
|
|
27
|
+
|
|
28
|
+
## Actionable Recommendations
|
|
29
|
+
For every identified cluster:
|
|
30
|
+
1. **Draft a Proposal:** Write a summary identifying the involved PRs and the common files shared.
|
|
31
|
+
2. **Assign Ownership:** Determine if one author should take lead on the consolidated PR, or if a pair-programming session is required.
|
|
32
|
+
3. **Conflict Resolution:** Explicitly state the plan for handling conflicting logic in shared files identified in the report.
|
|
33
|
+
|
|
34
|
+
## Submission
|
|
35
|
+
Present the findings in the following format:
|
|
36
|
+
- **Cluster ID:** [List of PR Numbers]
|
|
37
|
+
- **Primary Overlap:** [List of critical shared files]
|
|
38
|
+
- **Recommendation:** [Merge/Coordinate/Separate]
|
|
39
|
+
- **Rationale:** [Brief explanation referencing issues fixed]
|
|
40
|
+
MARKDOWN
|
|
41
|
+
|
|
42
|
+
echo "Successfully created instructions at $OUTPUT_FILE"
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Synchronizes contracts from JSON schema to Zod.
|
|
6
|
+
*/
|
|
7
|
+
async function syncContracts() {
|
|
8
|
+
let jsonSchemaToZod;
|
|
9
|
+
try {
|
|
10
|
+
const module = await import('json-schema-to-zod');
|
|
11
|
+
jsonSchemaToZod = module.jsonSchemaToZod;
|
|
12
|
+
} catch (err: unknown) {
|
|
13
|
+
const error = err as { code?: string; message?: string };
|
|
14
|
+
if (error.code === 'ERR_MODULE_NOT_FOUND' || error.message?.includes('Cannot find package')) {
|
|
15
|
+
console.error('❌ Error: json-schema-to-zod not found.');
|
|
16
|
+
console.error(' Please run `pnpm install` in the root directory.');
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
throw err;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let schemaPath = path.join(process.cwd(), 'cli/dev_tools/cli-schema.json');
|
|
23
|
+
let outputPath = path.join(process.cwd(), 'mcp/src/tools/contract.ts');
|
|
24
|
+
|
|
25
|
+
if (!fs.existsSync(schemaPath)) {
|
|
26
|
+
// Try relative to package root if in extracted mode (run from root)
|
|
27
|
+
schemaPath = path.join(process.cwd(), 'cli/dev_tools/cli-schema.json');
|
|
28
|
+
outputPath = path.join(process.cwd(), 'mcp/src/tools/contract.ts');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!fs.existsSync(schemaPath)) {
|
|
32
|
+
// Try if run from mcp directory
|
|
33
|
+
schemaPath = path.join(process.cwd(), '../cli/dev_tools/cli-schema.json');
|
|
34
|
+
outputPath = path.join(process.cwd(), 'src/tools/contract.ts');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!fs.existsSync(schemaPath)) {
|
|
38
|
+
console.error(`Schema not found at ${schemaPath}`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const rawSchema = JSON.parse(fs.readFileSync(schemaPath, 'utf-8')) as { models?: Record<string, unknown> };
|
|
43
|
+
const models = rawSchema.models || {};
|
|
44
|
+
|
|
45
|
+
let tsContent = `// Automatically generated by sync-contracts.ts. Do not edit manually.\n`;
|
|
46
|
+
tsContent += `import { z } from "zod";\n\n`;
|
|
47
|
+
|
|
48
|
+
for (const [name, schema] of Object.entries(models)) {
|
|
49
|
+
// Get the raw Zod expression without any variable assignment or imports
|
|
50
|
+
const zodExpression = jsonSchemaToZod(schema as any, { module: 'none' });
|
|
51
|
+
|
|
52
|
+
tsContent += `export const ${name}Schema = ${zodExpression};\n\n`;
|
|
53
|
+
tsContent += `export type ${name} = z.infer<typeof ${name}Schema>;\n\n`;
|
|
54
|
+
|
|
55
|
+
// Also export the raw JSON schema for MCP definitions
|
|
56
|
+
tsContent += `export const ${name}JsonSchema = ${JSON.stringify(schema, null, 2)} as const;\n\n`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Normalize newlines to \n and ensure exactly one trailing newline at EOF
|
|
60
|
+
const finalContent = tsContent.replace(/\r\n/g, '\n').trimEnd() + '\n';
|
|
61
|
+
|
|
62
|
+
fs.writeFileSync(outputPath, finalContent);
|
|
63
|
+
console.log(`Generated ${outputPath}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Ensure execution is awaited and errors are caught
|
|
67
|
+
(async () => {
|
|
68
|
+
try {
|
|
69
|
+
await syncContracts();
|
|
70
|
+
} catch (err) {
|
|
71
|
+
console.error('❌ Unexpected error during contract synchronization:', err);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
})();
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || '';
|
|
5
|
+
const globalTargetDir = homeDir ? path.join(homeDir, '.gemini', 'antigravity-cli', 'mcp', 'boomtick-mcp') : null;
|
|
6
|
+
const projectTargetDir = path.join(process.cwd(), '.mcp', 'schemas');
|
|
7
|
+
|
|
8
|
+
function isWritable(dir: string): boolean {
|
|
9
|
+
try {
|
|
10
|
+
const testFile = path.join(dir, '.write_test');
|
|
11
|
+
fs.writeFileSync(testFile, '');
|
|
12
|
+
fs.unlinkSync(testFile);
|
|
13
|
+
return true;
|
|
14
|
+
} catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface ToolDefinition {
|
|
20
|
+
name: string;
|
|
21
|
+
inputSchema: Record<string, unknown>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Synchronizes MCP tool schemas to target locations.
|
|
26
|
+
*/
|
|
27
|
+
async function syncSchemas() {
|
|
28
|
+
console.log('🔄 Synchronizing MCP tool schemas...');
|
|
29
|
+
|
|
30
|
+
let mcpToolsModule;
|
|
31
|
+
try {
|
|
32
|
+
mcpToolsModule = await import('../src/mcp/definitions.js');
|
|
33
|
+
} catch (err: unknown) {
|
|
34
|
+
const error = err as { code?: string; message?: string };
|
|
35
|
+
if (error.code === 'ERR_MODULE_NOT_FOUND' || error.message?.includes('Cannot find package')) {
|
|
36
|
+
console.error('❌ Error: MCP dependencies or source files not found.');
|
|
37
|
+
console.error(' Please run `pnpm install` in the root directory.');
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
throw err;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const mcpTools = mcpToolsModule.MCP_TOOLS as ToolDefinition[];
|
|
44
|
+
|
|
45
|
+
const targets = [projectTargetDir];
|
|
46
|
+
if (globalTargetDir) targets.push(globalTargetDir);
|
|
47
|
+
|
|
48
|
+
const validTargets: string[] = [];
|
|
49
|
+
|
|
50
|
+
targets.forEach(dir => {
|
|
51
|
+
try {
|
|
52
|
+
if (!fs.existsSync(dir)) {
|
|
53
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
54
|
+
console.log(`📁 Created directory: ${dir}`);
|
|
55
|
+
}
|
|
56
|
+
if (isWritable(dir)) {
|
|
57
|
+
validTargets.push(dir);
|
|
58
|
+
} else {
|
|
59
|
+
console.warn(`⚠️ Directory is not writable, skipping: ${dir}`);
|
|
60
|
+
}
|
|
61
|
+
} catch (err) {
|
|
62
|
+
console.warn(`⚠️ Failed to prepare directory, skipping: ${dir}`, err);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (validTargets.length === 0) {
|
|
67
|
+
console.warn('⚠️ No valid target directories available for schema synchronization.');
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
mcpTools.forEach((tool) => {
|
|
72
|
+
const schemaContent = JSON.stringify(tool.inputSchema, null, 2);
|
|
73
|
+
const fileName = `${tool.name}.json`;
|
|
74
|
+
|
|
75
|
+
validTargets.forEach(targetDir => {
|
|
76
|
+
const filePath = path.join(targetDir, fileName);
|
|
77
|
+
try {
|
|
78
|
+
fs.writeFileSync(filePath, schemaContent);
|
|
79
|
+
} catch (err) {
|
|
80
|
+
console.error(`❌ Failed to write schema to ${filePath}:`, err);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
console.log(`✅ Synchronized ${mcpTools.length} tool schemas to ${validTargets.length} locations.`);
|
|
86
|
+
validTargets.forEach(target => console.log(` - ${target}`));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Ensure execution is awaited and errors are caught
|
|
90
|
+
(async () => {
|
|
91
|
+
try {
|
|
92
|
+
await syncSchemas();
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.error('❌ Unexpected error during MCP schema synchronization:', error);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
})();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
You are the Boomtick Conflict Scout Agent.
|
|
2
|
+
|
|
3
|
+
Your job is to find open PRs that may need rescue.
|
|
4
|
+
|
|
5
|
+
Rules:
|
|
6
|
+
- Use read-only tools only.
|
|
7
|
+
- Do not create branches.
|
|
8
|
+
- Do not comment.
|
|
9
|
+
- Do not edit files.
|
|
10
|
+
- Prefer PRs that are small, stale, conflicted, or failing checks.
|
|
11
|
+
- Ignore draft PRs unless explicitly configured.
|
|
12
|
+
- Produce a ranked rescue queue.
|
|
13
|
+
|
|
14
|
+
For each candidate, include:
|
|
15
|
+
- PR number
|
|
16
|
+
- title
|
|
17
|
+
- branch
|
|
18
|
+
- reason selected
|
|
19
|
+
- risk level
|
|
20
|
+
- recommended next action
|