@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.
Files changed (163) hide show
  1. package/.mcp/schemas/boomtick.health.json +14 -10
  2. package/.mcp/schemas/boomtick_health.json +16 -0
  3. package/.mcp/schemas/github.checkout_branch.json +28 -24
  4. package/.mcp/schemas/github.comment_triage_summary.json +21 -17
  5. package/.mcp/schemas/github.create_issue.json +55 -50
  6. package/.mcp/schemas/github.create_pull_request.json +53 -48
  7. package/.mcp/schemas/github.get_merge_conflict_files.json +21 -17
  8. package/.mcp/schemas/github.get_pr.json +30 -12
  9. package/.mcp/schemas/github.get_pr_commits.json +32 -0
  10. package/.mcp/schemas/github.get_pr_diff.json +30 -12
  11. package/.mcp/schemas/github.get_pr_reviews.json +32 -0
  12. package/.mcp/schemas/github.issue_comment.json +35 -17
  13. package/.mcp/schemas/github.issue_update.json +114 -96
  14. package/.mcp/schemas/github.issue_view.json +30 -12
  15. package/.mcp/schemas/github.open_replacement_pr.json +64 -60
  16. package/.mcp/schemas/github.search_open_prs.json +61 -43
  17. package/.mcp/schemas/github_checkout_branch.json +31 -0
  18. package/.mcp/schemas/github_comment_triage_summary.json +24 -0
  19. package/.mcp/schemas/github_create_issue.json +58 -0
  20. package/.mcp/schemas/github_create_pull_request.json +56 -0
  21. package/.mcp/schemas/github_get_merge_conflict_files.json +24 -0
  22. package/.mcp/schemas/github_get_pr.json +32 -0
  23. package/.mcp/schemas/github_get_pr_commits.json +32 -0
  24. package/.mcp/schemas/github_get_pr_diff.json +32 -0
  25. package/.mcp/schemas/github_get_pr_reviews.json +32 -0
  26. package/.mcp/schemas/github_issue_comment.json +38 -0
  27. package/.mcp/schemas/github_issue_update.json +123 -0
  28. package/.mcp/schemas/github_issue_view.json +32 -0
  29. package/.mcp/schemas/github_open_replacement_pr.json +67 -0
  30. package/.mcp/schemas/github_search_open_prs.json +65 -0
  31. package/.mcp/schemas/jules.cancel_session.json +16 -12
  32. package/.mcp/schemas/jules.create_session.json +41 -37
  33. package/.mcp/schemas/jules.get_messages.json +16 -12
  34. package/.mcp/schemas/jules.get_pr.json +16 -12
  35. package/.mcp/schemas/jules.get_session.json +16 -12
  36. package/.mcp/schemas/jules.list_sessions.json +33 -29
  37. package/.mcp/schemas/jules.send_message.json +30 -26
  38. package/.mcp/schemas/jules.trigger_feedback.json +16 -12
  39. package/.mcp/schemas/jules_cancel_session.json +18 -0
  40. package/.mcp/schemas/jules_create_session.json +44 -0
  41. package/.mcp/schemas/jules_get_messages.json +18 -0
  42. package/.mcp/schemas/jules_get_pr.json +18 -0
  43. package/.mcp/schemas/jules_get_session.json +18 -0
  44. package/.mcp/schemas/jules_list_sessions.json +36 -0
  45. package/.mcp/schemas/jules_send_message.json +35 -0
  46. package/.mcp/schemas/jules_trigger_feedback.json +18 -0
  47. package/.mcp/schemas/repo.commit_patch.json +34 -30
  48. package/.mcp/schemas/repo.create_branch.json +21 -17
  49. package/.mcp/schemas/repo.create_repair_branch.json +34 -30
  50. package/.mcp/schemas/repo.get_changed_files.json +19 -15
  51. package/.mcp/schemas/repo.get_command_schema.json +16 -12
  52. package/.mcp/schemas/repo.get_package_scripts.json +21 -17
  53. package/.mcp/schemas/repo.get_workflow_runs.json +50 -0
  54. package/.mcp/schemas/repo.logs.json +42 -24
  55. package/.mcp/schemas/repo.read_agent_context.json +7 -3
  56. package/.mcp/schemas/repo.read_ci_logs.json +35 -17
  57. package/.mcp/schemas/repo.run_tests.json +40 -36
  58. package/.mcp/schemas/repo_commit_patch.json +39 -0
  59. package/.mcp/schemas/repo_create_branch.json +24 -0
  60. package/.mcp/schemas/repo_create_repair_branch.json +37 -0
  61. package/.mcp/schemas/repo_get_changed_files.json +22 -0
  62. package/.mcp/schemas/repo_get_command_schema.json +18 -0
  63. package/.mcp/schemas/repo_get_package_scripts.json +23 -0
  64. package/.mcp/schemas/repo_get_workflow_runs.json +50 -0
  65. package/.mcp/schemas/repo_logs.json +45 -0
  66. package/.mcp/schemas/repo_read_agent_context.json +9 -0
  67. package/.mcp/schemas/repo_read_ci_logs.json +38 -0
  68. package/.mcp/schemas/repo_run_tests.json +45 -0
  69. package/CHANGELOG.md +16 -0
  70. package/README.md +0 -1
  71. package/actions/impact-analysis/action.yml +4 -16
  72. package/actions/run-project-gate/action.yml +2 -9
  73. package/actions/test-build/action.yml +1 -19
  74. package/dist/lib/git.js +74 -0
  75. package/dist/lib/git.test.js +82 -0
  76. package/dist/lib/param_utils.js +35 -0
  77. package/dist/lib/param_utils.test.js +44 -0
  78. package/dist/mcp/definitions.js +27 -40
  79. package/dist/mcp/schemas.test.js +28 -0
  80. package/dist/mcp/server.js +64 -74
  81. package/dist/mcp/server.test.js +0 -33
  82. package/dist/tools/contract.js +229 -10
  83. package/dist/tools/github.create_issue.js +3 -2
  84. package/dist/tools/github.create_issue.test.js +10 -0
  85. package/dist/tools/github.create_pull_request.js +4 -4
  86. package/dist/tools/github.get_merge_conflict_files.js +10 -18
  87. package/dist/tools/github.get_pr.js +4 -1
  88. package/dist/tools/github.get_pr_commits.js +21 -0
  89. package/dist/tools/github.get_pr_diff.js +6 -2
  90. package/dist/tools/github.get_pr_reviews.js +21 -0
  91. package/dist/tools/github.get_pr_reviews.test.js +99 -0
  92. package/dist/tools/github.issue_comment.js +8 -1
  93. package/dist/tools/github.issue_update.js +3 -0
  94. package/dist/tools/github.issue_view.js +4 -1
  95. package/dist/tools/github.search_open_prs.js +4 -0
  96. package/dist/tools/jules/create-session.js +1 -2
  97. package/dist/tools/jules/get-pr.js +1 -2
  98. package/dist/tools/jules/get-session.js +1 -2
  99. package/dist/tools/jules/shared.js +24 -0
  100. package/dist/tools/repo.commit_patch.js +30 -10
  101. package/dist/tools/repo.get_changed_files.js +2 -13
  102. package/dist/tools/repo.get_workflow_runs.js +29 -0
  103. package/dist/tools/repo.logs.js +5 -1
  104. package/dist/tools/repo.read_agent_context.js +27 -1
  105. package/dist/tools/repo.read_agent_context.test.js +1 -1
  106. package/dist/tools/repo.read_ci_logs.js +5 -1
  107. package/dist/tools/repo.run_tests.js +26 -4
  108. package/package.json +4 -7
  109. package/scripts/sync-mcp-schemas.ts +6 -1
  110. package/src/agents/verifier-agent.prompt.md +0 -4
  111. package/src/lib/git.test.ts +110 -0
  112. package/src/lib/git.ts +109 -0
  113. package/src/lib/param_utils.test.ts +49 -0
  114. package/src/lib/param_utils.ts +42 -0
  115. package/src/mcp/definitions.ts +30 -43
  116. package/src/mcp/schemas.test.ts +34 -0
  117. package/src/mcp/server.test.ts +0 -42
  118. package/src/mcp/server.ts +65 -75
  119. package/src/tools/contract.ts +241 -10
  120. package/src/tools/github.create_issue.test.ts +10 -0
  121. package/src/tools/github.create_issue.ts +3 -1
  122. package/src/tools/github.create_pull_request.ts +4 -5
  123. package/src/tools/github.get_merge_conflict_files.ts +10 -20
  124. package/src/tools/github.get_pr.ts +4 -1
  125. package/src/tools/github.get_pr_commits.ts +27 -0
  126. package/src/tools/github.get_pr_diff.ts +7 -2
  127. package/src/tools/github.get_pr_reviews.test.ts +110 -0
  128. package/src/tools/github.get_pr_reviews.ts +27 -0
  129. package/src/tools/github.issue_comment.ts +8 -1
  130. package/src/tools/github.issue_update.ts +4 -1
  131. package/src/tools/github.issue_view.ts +4 -1
  132. package/src/tools/github.search_open_prs.ts +4 -0
  133. package/src/tools/jules/create-session.ts +1 -2
  134. package/src/tools/jules/get-pr.ts +1 -2
  135. package/src/tools/jules/get-session.ts +1 -2
  136. package/src/tools/jules/shared.ts +35 -0
  137. package/src/tools/repo.commit_patch.ts +35 -11
  138. package/src/tools/repo.get_changed_files.ts +2 -16
  139. package/src/tools/repo.get_workflow_runs.ts +36 -0
  140. package/src/tools/repo.logs.ts +5 -1
  141. package/src/tools/repo.read_agent_context.test.ts +1 -1
  142. package/src/tools/repo.read_agent_context.ts +35 -1
  143. package/src/tools/repo.read_ci_logs.ts +5 -1
  144. package/src/tools/repo.run_tests.ts +28 -5
  145. package/.mcp/schemas/agent.search_ddgs.json +0 -27
  146. package/.mcp/schemas/repo.get_route_map.json +0 -19
  147. package/.mcp/schemas/repo.run_lighthouse.json +0 -25
  148. package/.mcp/schemas/repo.run_playwright.json +0 -32
  149. package/dist/lib/td-cli.js +0 -54
  150. package/dist/tools/ddgs.search.js +0 -50
  151. package/dist/tools/ddgs.search.test.js +0 -67
  152. package/dist/tools/ddgs_search.py +0 -23
  153. package/dist/tools/repo.get_route_map.js +0 -37
  154. package/dist/tools/repo.run_lighthouse.js +0 -37
  155. package/dist/tools/repo.run_playwright.js +0 -29
  156. package/scripts/export-mcp-schema.ts +0 -7
  157. package/src/lib/td-cli.ts +0 -73
  158. package/src/tools/ddgs.search.test.ts +0 -92
  159. package/src/tools/ddgs.search.ts +0 -58
  160. package/src/tools/ddgs_search.py +0 -23
  161. package/src/tools/repo.get_route_map.ts +0 -41
  162. package/src/tools/repo.run_lighthouse.ts +0 -40
  163. package/src/tools/repo.run_playwright.ts +0 -35
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.0](https://github.com/arii/boomtick/compare/mcp-v0.4.2...mcp-v0.5.0) (2026-10-19)
4
+
5
+
6
+ ### Features
7
+
8
+ * **tooling:** add PR reviews, PR commits, workflow runs inspection, and repo overrides ([#834](https://github.com/arii/boomtick/issues/834)) ([42a8638](https://github.com/arii/boomtick/commit/42a8638add4ce8278dedacd6d8d14acc9e0a3bb9))
9
+
10
+ ## [0.4.2](https://github.com/arii/boomtick/compare/mcp-v0.4.1...mcp-v0.4.2) (2026-10-12)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * **mcp:** map tool JSON Schema to parameters object in sync-mcp-schemas ([#802](https://github.com/arii/boomtick/issues/802)) ([a57a9ae](https://github.com/arii/boomtick/commit/a57a9aef32109242882d682fcbd1a9ff0a81cffe))
16
+ * **mcp:** resolve identifier regex, commit patch, timeout, and parameter casing bugs ([#815](https://github.com/arii/boomtick/issues/815)) ([1f3b232](https://github.com/arii/boomtick/commit/1f3b232d8550fa87eac2dd974bfd67252a8f4da1))
17
+ * **mcp:** support dynamic repo context resolution and --repo forwarding across workspaces ([#811](https://github.com/arii/boomtick/issues/811)) ([f7e1665](https://github.com/arii/boomtick/commit/f7e166594f170843b7f2e267fab119c631bf3c83))
18
+
3
19
  ## [0.4.1](https://github.com/arii/boomtick/compare/mcp-v0.4.0...mcp-v0.4.1) (2026-09-22)
4
20
 
5
21
 
package/README.md CHANGED
@@ -12,7 +12,6 @@ A Model Context Protocol (MCP) server designed to empower AI agents with structu
12
12
 
13
13
  ### 📄 Resources
14
14
  - `repo://package-json`: Access root package manifest.
15
- - `repo://routes`: Access application route-to-content mapping.
16
15
  - `repo://design-tokens`: Access UI design tokens.
17
16
  - `repo://diff/{prNumber}`: Access full PR diff text.
18
17
  - `repo://ci/{prNumber}`: Access detailed CI check results and logs.
@@ -306,22 +306,10 @@ runs:
306
306
  PR_NUMBER: ${{ inputs.pr_number }}
307
307
  GITHUB_REPOSITORY: ${{ github.repository }}
308
308
  run: |
309
- TARGET_PATH="${{ github.action_path }}/../../../scripts/send-jules-impact.py"
310
- if [ -f "$TARGET_PATH" ]; then
311
- RESOLVED_PATH=$(realpath "$TARGET_PATH")
312
- echo "Executing send-jules-impact.py at: $RESOLVED_PATH"
313
- export PYTHONPATH="$(realpath "${{ github.action_path }}/../../../cli"):$PYTHONPATH"
314
- python3 "$RESOLVED_PATH"
315
- else
316
- # Fallback check for docker run execution or direct python script in checkout workspace
317
- # Inside container execution, scripts/send-jules-impact.py should exist in workspace
318
- if [ -f "scripts/send-jules-impact.py" ]; then
319
- echo "Executing send-jules-impact.py from current workspace."
320
- export PYTHONPATH="cli:$PYTHONPATH"
321
- python3 scripts/send-jules-impact.py
322
- else
323
- echo "⚠️ Skipping: send-jules-impact.py script not found."
324
- fi
309
+ if command -v pnpm &> /dev/null; then
310
+ pnpm exec tsx -e "import { sendJulesImpactReport } from './lib/julesReviewClient'; (async () => { await sendJulesImpactReport(process.env.TASK_ID); })();"
311
+ elif command -v npx &> /dev/null; then
312
+ npx tsx -e "import { sendJulesImpactReport } from './lib/julesReviewClient'; (async () => { await sendJulesImpactReport(process.env.TASK_ID); })();"
325
313
  fi
326
314
  shell: bash
327
315
 
@@ -17,19 +17,12 @@ runs:
17
17
  env:
18
18
  TARGET: ${{ inputs.target }}
19
19
  run: |
20
- # Use centralized script for CLI path resolution
21
- if [ -f "scripts/resolve-cli.sh" ]; then
22
- CLI_PATH=$(bash scripts/resolve-cli.sh | sed "s|$(pwd)/||")
23
- elif [ -f "boomtick-pkg/scripts/resolve-cli.sh" ]; then
24
- CLI_PATH=$(bash boomtick-pkg/scripts/resolve-cli.sh | sed "s|$(pwd)/||")
25
- elif [ -f "boomtick/scripts/resolve-cli.sh" ]; then
26
- CLI_PATH=$(bash boomtick/scripts/resolve-cli.sh | sed "s|$(pwd)/||")
20
+ if [ -d "cli" ]; then
21
+ CLI_PATH="cli"
27
22
  elif [ -d "boomtick-pkg/cli" ]; then
28
23
  CLI_PATH="boomtick-pkg/cli"
29
24
  elif [ -d "boomtick/cli" ]; then
30
25
  CLI_PATH="boomtick/cli"
31
- elif [ -d "cli" ]; then
32
- CLI_PATH="cli"
33
26
  else
34
27
  CLI_PATH="cli"
35
28
  fi
@@ -1,13 +1,9 @@
1
1
  name: BoomTick Build & Test
2
- description: Encapsulates build, bundle size check, Playwright E2E, and Lighthouse CI
2
+ description: Encapsulates build, bundle size check, and Playwright E2E
3
3
  inputs:
4
4
  github_token:
5
5
  description: 'GitHub token'
6
6
  required: true
7
- run_lighthouse:
8
- description: 'Whether to run Lighthouse CI'
9
- required: false
10
- default: 'true'
11
7
  runs:
12
8
  using: composite
13
9
  steps:
@@ -36,20 +32,6 @@ runs:
36
32
  VITE_BASE_PATH=/ pnpm run test:e2e
37
33
  shell: bash
38
34
 
39
- - name: Run Lighthouse CI
40
- if: inputs.run_lighthouse == 'true'
41
- run: |
42
- set -eu
43
- VITE_BASE_PATH="/"
44
- CHROME_PATH="$(node -e "console.log(require('playwright').chromium.executablePath())")"
45
- export VITE_BASE_PATH
46
- export CHROME_PATH
47
- echo "CHROME_PATH=$CHROME_PATH"
48
- pnpm run lighthouse --collect.url="http://localhost:4173${VITE_BASE_PATH}"
49
- env:
50
- LHCI_GITHUB_TOKEN: ${{ inputs.github_token }}
51
- shell: bash
52
-
53
35
  - name: Check Playwright Report Exists
54
36
  id: check_report
55
37
  if: always()
package/dist/lib/git.js CHANGED
@@ -1,6 +1,36 @@
1
1
  import { runCommand } from "./shell.js";
2
+ import { config } from "../config.js";
2
3
  import path from "path";
3
4
  import fs from "fs/promises";
5
+ import { execSync } from "child_process";
6
+ const repoCache = new Map();
7
+ export function resolveRepo(explicitRepo, cwd) {
8
+ if (explicitRepo && explicitRepo.trim().length > 0) {
9
+ return explicitRepo.trim();
10
+ }
11
+ const targetCwd = cwd || process.cwd();
12
+ if (repoCache.has(targetCwd)) {
13
+ return repoCache.get(targetCwd);
14
+ }
15
+ try {
16
+ const url = execSync("git remote get-url origin", {
17
+ cwd: targetCwd,
18
+ encoding: "utf-8",
19
+ stdio: ["ignore", "pipe", "ignore"],
20
+ }).trim();
21
+ const match = url.match(/[:/]([^/]+\/[^/.]+)(\.git)?$/);
22
+ if (match) {
23
+ repoCache.set(targetCwd, match[1]);
24
+ return match[1];
25
+ }
26
+ }
27
+ catch (e) {
28
+ // Ignore error and fall back to static config
29
+ }
30
+ const fallback = `${config.githubOwner}/${config.githubRepo}`;
31
+ repoCache.set(targetCwd, fallback);
32
+ return fallback;
33
+ }
4
34
  function validatePrNumber(value) {
5
35
  const prNumber = Number(value);
6
36
  if (!Number.isInteger(prNumber) || prNumber <= 0) {
@@ -29,3 +59,47 @@ export async function removeWorktree(worktreePath) {
29
59
  await runCommand("git", ["worktree", "remove", "--force", worktreePath]);
30
60
  await runCommand("git", ["worktree", "prune"]);
31
61
  }
62
+ export async function getChangedFiles(base = "main", head = "HEAD", cwd) {
63
+ const result = await runCommand("git", ["diff", "--name-status", "-z", `${base}...${head}`], { cwd });
64
+ if (result.exitCode !== 0) {
65
+ throw new Error(`Failed to get changed files: ${result.stderr}`);
66
+ }
67
+ const tokens = result.stdout.split("\0").filter((t) => t.length > 0);
68
+ const files = [];
69
+ let i = 0;
70
+ while (i < tokens.length) {
71
+ const status = tokens[i];
72
+ if (status.startsWith("R") || status.startsWith("C")) {
73
+ const oldPath = tokens[i + 1];
74
+ const newPath = tokens[i + 2];
75
+ if (oldPath && newPath) {
76
+ files.push({ status, path: newPath, oldPath });
77
+ }
78
+ i += 3;
79
+ }
80
+ else {
81
+ const filePath = tokens[i + 1];
82
+ if (filePath) {
83
+ files.push({ status, path: filePath });
84
+ }
85
+ i += 2;
86
+ }
87
+ }
88
+ return files;
89
+ }
90
+ export async function getMergeConflictFiles(baseBranch = "main", headRef = "HEAD", cwd) {
91
+ const result = await runCommand("git", ["merge-tree", "--write-tree", "--name-only", baseBranch, headRef], { cwd });
92
+ // git merge-tree returns exit code 1 if conflicts are present, 0 if clean merge
93
+ if (result.exitCode !== 0 && result.exitCode !== 1) {
94
+ throw new Error(`Git merge-tree failed: ${result.stderr}`);
95
+ }
96
+ const lines = result.stdout
97
+ .split("\n")
98
+ .map((l) => l.trim())
99
+ .filter((l) => l.length > 0);
100
+ // First line is tree hash if successful or conflict output
101
+ // When conflicts exist, git merge-tree --write-tree --name-only outputs conflicted filenames
102
+ // Skip tree hash line if present (40 hex chars)
103
+ const files = lines.filter((line) => !/^[0-9a-f]{40}$/i.test(line));
104
+ return files;
105
+ }
@@ -0,0 +1,82 @@
1
+ import { describe, expect, it, vi, beforeEach } from "vitest";
2
+ import { getChangedFiles, getMergeConflictFiles } from "./git.js";
3
+ import * as shell from "./shell.js";
4
+ vi.mock("./shell.js", () => ({
5
+ runCommand: vi.fn(),
6
+ }));
7
+ describe("Git Helpers", () => {
8
+ beforeEach(() => {
9
+ vi.resetAllMocks();
10
+ });
11
+ describe("getChangedFiles", () => {
12
+ it("should parse null-delimited git diff output correctly", async () => {
13
+ // Mock git diff output with null character delimiters:
14
+ // Status M -> file1.txt
15
+ // Status A -> file2.txt
16
+ // Status R100 -> old.txt -> new.txt
17
+ const mockStdout = "M\0file1.txt\0A\0file2.txt\0R100\0old.txt\0new.txt\0";
18
+ vi.mocked(shell.runCommand).mockResolvedValueOnce({
19
+ exitCode: 0,
20
+ stdout: mockStdout,
21
+ stderr: "",
22
+ durationMs: 10,
23
+ command: "git diff --name-status -z main...HEAD",
24
+ });
25
+ const result = await getChangedFiles("main", "HEAD");
26
+ expect(shell.runCommand).toHaveBeenCalledWith("git", ["diff", "--name-status", "-z", "main...HEAD"], { cwd: undefined });
27
+ expect(result).toEqual([
28
+ { status: "M", path: "file1.txt" },
29
+ { status: "A", path: "file2.txt" },
30
+ { status: "R100", path: "new.txt", oldPath: "old.txt" },
31
+ ]);
32
+ });
33
+ it("should throw error if git diff fails", async () => {
34
+ vi.mocked(shell.runCommand).mockResolvedValueOnce({
35
+ exitCode: 1,
36
+ stdout: "",
37
+ stderr: "fatal: bad revision",
38
+ durationMs: 10,
39
+ command: "git diff --name-status -z invalid...HEAD",
40
+ });
41
+ await expect(getChangedFiles("invalid", "HEAD")).rejects.toThrow("Failed to get changed files: fatal: bad revision");
42
+ });
43
+ });
44
+ describe("getMergeConflictFiles", () => {
45
+ it("should parse git merge-tree output when conflicts exist", async () => {
46
+ // Output with 40-char tree hash followed by conflicting filenames
47
+ const mockStdout = "5d71843ed2e051b2be91361db7bea0fc4ee1e5a0\nfile1.txt\nsrc/file2.ts\n";
48
+ vi.mocked(shell.runCommand).mockResolvedValueOnce({
49
+ exitCode: 1, // merge-tree exits 1 on conflicts
50
+ stdout: mockStdout,
51
+ stderr: "",
52
+ durationMs: 10,
53
+ command: "git merge-tree --write-tree --name-only main feature",
54
+ });
55
+ const conflicts = await getMergeConflictFiles("main", "feature");
56
+ expect(shell.runCommand).toHaveBeenCalledWith("git", ["merge-tree", "--write-tree", "--name-only", "main", "feature"], { cwd: undefined });
57
+ expect(conflicts).toEqual(["file1.txt", "src/file2.ts"]);
58
+ });
59
+ it("should return empty array when no conflicts exist", async () => {
60
+ const mockStdout = "5d71843ed2e051b2be91361db7bea0fc4ee1e5a0\n";
61
+ vi.mocked(shell.runCommand).mockResolvedValueOnce({
62
+ exitCode: 0,
63
+ stdout: mockStdout,
64
+ stderr: "",
65
+ durationMs: 10,
66
+ command: "git merge-tree --write-tree --name-only main feature",
67
+ });
68
+ const conflicts = await getMergeConflictFiles("main", "feature");
69
+ expect(conflicts).toEqual([]);
70
+ });
71
+ it("should throw error if git merge-tree exits with unexpected exit code", async () => {
72
+ vi.mocked(shell.runCommand).mockResolvedValueOnce({
73
+ exitCode: 128,
74
+ stdout: "",
75
+ stderr: "fatal: not a valid object name",
76
+ durationMs: 10,
77
+ command: "git merge-tree --write-tree --name-only invalid feature",
78
+ });
79
+ await expect(getMergeConflictFiles("invalid", "feature")).rejects.toThrow("Git merge-tree failed: fatal: not a valid object name");
80
+ });
81
+ });
82
+ });
@@ -0,0 +1,35 @@
1
+ export function normalizeArguments(args) {
2
+ if (!args || typeof args !== "object") {
3
+ return {};
4
+ }
5
+ const result = {};
6
+ for (const [key, value] of Object.entries(args)) {
7
+ // Convert snake_case to camelCase
8
+ const camelKey = key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
9
+ // Normalize value recursively if it's an object/array
10
+ let normalizedValue = value;
11
+ if (Array.isArray(value)) {
12
+ normalizedValue = value.map((item) => item && typeof item === "object" ? normalizeArguments(item) : item);
13
+ }
14
+ else if (value && typeof value === "object" && !(value instanceof RegExp)) {
15
+ normalizedValue = normalizeArguments(value);
16
+ }
17
+ // Coerce numeric string to integer for prNumber / issueNumber if applicable
18
+ if ((camelKey === "prNumber" || camelKey === "issueNumber") && typeof normalizedValue === "string") {
19
+ const parsedNum = parseInt(normalizedValue, 10);
20
+ if (!isNaN(parsedNum)) {
21
+ normalizedValue = parsedNum;
22
+ }
23
+ }
24
+ result[camelKey] = normalizedValue;
25
+ }
26
+ // Parameter aliases harmonization
27
+ // branch vs branchName harmonization
28
+ if (result.branch !== undefined && result.branchName === undefined) {
29
+ result.branchName = result.branch;
30
+ }
31
+ else if (result.branchName !== undefined && result.branch === undefined) {
32
+ result.branch = result.branchName;
33
+ }
34
+ return result;
35
+ }
@@ -0,0 +1,44 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { normalizeArguments } from "./param_utils.js";
3
+ describe("param_utils.normalizeArguments", () => {
4
+ it("should convert snake_case keys to camelCase", () => {
5
+ const input = {
6
+ pr_number: 123,
7
+ issue_number: 456,
8
+ worktree_path: "/tmp/worktree",
9
+ base_branch: "main",
10
+ };
11
+ const output = normalizeArguments(input);
12
+ expect(output).toEqual({
13
+ prNumber: 123,
14
+ issueNumber: 456,
15
+ worktreePath: "/tmp/worktree",
16
+ baseBranch: "main",
17
+ });
18
+ });
19
+ it("should coerce numeric strings for prNumber and issueNumber", () => {
20
+ const input = {
21
+ pr_number: "789",
22
+ issue_number: "101",
23
+ };
24
+ const output = normalizeArguments(input);
25
+ expect(output).toEqual({
26
+ prNumber: 789,
27
+ issueNumber: 101,
28
+ });
29
+ });
30
+ it("should synchronize branch and branchName", () => {
31
+ expect(normalizeArguments({ branch: "feat-x" })).toEqual({
32
+ branch: "feat-x",
33
+ branchName: "feat-x",
34
+ });
35
+ expect(normalizeArguments({ branch_name: "feat-y" })).toEqual({
36
+ branchName: "feat-y",
37
+ branch: "feat-y",
38
+ });
39
+ });
40
+ it("should handle null or empty arguments safely", () => {
41
+ expect(normalizeArguments(null)).toEqual({});
42
+ expect(normalizeArguments(undefined)).toEqual({});
43
+ });
44
+ });
@@ -1,4 +1,4 @@
1
- import { HealthCheckInputJsonSchema, SearchPRsInputJsonSchema, GetPrDiffInputJsonSchema, CheckoutBranchInputJsonSchema, GetMergeConflictFilesInputJsonSchema, GetChangedFilesInputJsonSchema, GetPackageScriptsInputJsonSchema, GetRouteMapInputJsonSchema, ReadCiLogsInputJsonSchema, RepoLogsInputJsonSchema, CreateBranchInputJsonSchema, CreateRepairBranchInputJsonSchema, RunTestsInputJsonSchema, RunLighthouseInputJsonSchema, RunPlaywrightInputJsonSchema, CommitPatchInputJsonSchema, OpenReplacementPrInputJsonSchema, CreatePullRequestInputJsonSchema, CommentTriageSummaryInputJsonSchema, GetPrInputJsonSchema, IssueViewInputJsonSchema, IssueUpdateInputJsonSchema, IssueCommentInputJsonSchema, CreateIssueInputJsonSchema, GetCommandSchemaInputJsonSchema, ReadAgentContextInputJsonSchema as ReadAgentContextSchema, CreateJulesSessionInputJsonSchema, JulesSessionIdInputJsonSchema, JulesSendMessageInputJsonSchema, JulesListSessionsInputJsonSchema, SearchDdgsInputJsonSchema, } from "../tools/contract.js";
1
+ import { HealthCheckInputJsonSchema, SearchPRsInputJsonSchema, GetPrDiffInputJsonSchema, CheckoutBranchInputJsonSchema, GetMergeConflictFilesInputJsonSchema, GetChangedFilesInputJsonSchema, GetPackageScriptsInputJsonSchema, ReadCiLogsInputJsonSchema, RepoLogsInputJsonSchema, CreateBranchInputJsonSchema, CreateRepairBranchInputJsonSchema, RunTestsInputJsonSchema, CommitPatchInputJsonSchema, OpenReplacementPrInputJsonSchema, CreatePullRequestInputJsonSchema, CommentTriageSummaryInputJsonSchema, GetPrInputJsonSchema, IssueViewInputJsonSchema, IssueUpdateInputJsonSchema, IssueCommentInputJsonSchema, CreateIssueInputJsonSchema, GetCommandSchemaInputJsonSchema, ReadAgentContextInputJsonSchema as ReadAgentContextSchema, CreateJulesSessionInputJsonSchema, JulesSessionIdInputJsonSchema, JulesSendMessageInputJsonSchema, JulesListSessionsInputJsonSchema, GetPrReviewsInputJsonSchema, GetPrCommitsInputJsonSchema, GetWorkflowRunsInputJsonSchema, } from "../tools/contract.js";
2
2
  export const MCP_PROMPTS = [
3
3
  {
4
4
  name: "conflict-scout",
@@ -32,12 +32,6 @@ export const MCP_RESOURCES = [
32
32
  mimeType: "application/json",
33
33
  description: "The root package.json file of the repository.",
34
34
  },
35
- {
36
- uri: "repo://routes",
37
- name: "Route Map",
38
- mimeType: "application/json",
39
- description: "The mapping of application routes to content files.",
40
- },
41
35
  {
42
36
  uri: "repo://design-tokens",
43
37
  name: "Design Tokens",
@@ -50,20 +44,8 @@ export const MCP_RESOURCES = [
50
44
  mimeType: "application/json",
51
45
  description: "The validation report for a specific repair branch.",
52
46
  },
53
- {
54
- uri: "repo://lighthouse/{branch}",
55
- name: "Lighthouse Report",
56
- mimeType: "application/json",
57
- description: "Lighthouse CI report for a specific branch.",
58
- },
59
- {
60
- uri: "repo://playwright/{branch}",
61
- name: "Playwright Report",
62
- mimeType: "application/json",
63
- description: "Playwright test report for a specific branch.",
64
- },
65
47
  ];
66
- export const MCP_TOOLS = [
48
+ const BASE_MCP_TOOLS = [
67
49
  {
68
50
  name: "boomtick.health",
69
51
  description: "Check the health and configuration of the MCP server.",
@@ -109,11 +91,6 @@ export const MCP_TOOLS = [
109
91
  description: "Get the scripts defined in package.json.",
110
92
  inputSchema: GetPackageScriptsInputJsonSchema,
111
93
  },
112
- {
113
- name: "repo.get_route_map",
114
- description: "Get the mapping of routes to content files.",
115
- inputSchema: GetRouteMapInputJsonSchema,
116
- },
117
94
  {
118
95
  name: "repo.read_ci_logs",
119
96
  description: "Read CI logs for a given pull request.",
@@ -139,16 +116,6 @@ export const MCP_TOOLS = [
139
116
  description: "Run repository tests and checks.",
140
117
  inputSchema: RunTestsInputJsonSchema,
141
118
  },
142
- {
143
- name: "repo.run_lighthouse",
144
- description: "Run Lighthouse CI audits.",
145
- inputSchema: RunLighthouseInputJsonSchema,
146
- },
147
- {
148
- name: "repo.run_playwright",
149
- description: "Run Playwright E2E tests.",
150
- inputSchema: RunPlaywrightInputJsonSchema,
151
- },
152
119
  {
153
120
  name: "repo.commit_patch",
154
121
  description: "Commit verified repair changes in a worktree.",
@@ -174,6 +141,21 @@ export const MCP_TOOLS = [
174
141
  description: "Get details of a GitHub PR including title, state, and URLs.",
175
142
  inputSchema: GetPrInputJsonSchema,
176
143
  },
144
+ {
145
+ name: "github.get_pr_reviews",
146
+ description: "Get top-level reviews, verdicts, states, and bodies for a GitHub PR.",
147
+ inputSchema: GetPrReviewsInputJsonSchema,
148
+ },
149
+ {
150
+ name: "github.get_pr_commits",
151
+ description: "List commits and commit metadata (SHAs, authors, messages) for a GitHub PR.",
152
+ inputSchema: GetPrCommitsInputJsonSchema,
153
+ },
154
+ {
155
+ name: "repo.get_workflow_runs",
156
+ description: "List recent GitHub Actions workflow runs for the repository.",
157
+ inputSchema: GetWorkflowRunsInputJsonSchema,
158
+ },
177
159
  {
178
160
  name: "github.issue_view",
179
161
  description: "View details of a GitHub issue including title, body, and state.",
@@ -234,9 +216,14 @@ export const MCP_TOOLS = [
234
216
  description: "Automatically collect CI status/logs for the PR associated with a Jules session and send them back as feedback.",
235
217
  inputSchema: JulesSessionIdInputJsonSchema,
236
218
  },
237
- {
238
- name: "agent.search_ddgs",
239
- description: "Search the web using DuckDuckGo (via ddgs python library).",
240
- inputSchema: SearchDdgsInputJsonSchema,
241
- },
242
219
  ];
220
+ export const MCP_TOOLS = BASE_MCP_TOOLS.flatMap((tool) => {
221
+ const underscoreName = tool.name.replace(/\./g, "_");
222
+ if (underscoreName !== tool.name) {
223
+ return [
224
+ { ...tool, name: underscoreName },
225
+ tool,
226
+ ];
227
+ }
228
+ return [tool];
229
+ });
@@ -0,0 +1,28 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ import { execSync } from "child_process";
5
+ describe("MCP tool schemas export", () => {
6
+ it("should generate valid schema files with non-null parameters object", () => {
7
+ const schemasDir = path.resolve(process.cwd(), ".mcp/schemas");
8
+ if (!fs.existsSync(schemasDir)) {
9
+ execSync("npx tsx scripts/sync-mcp-schemas.ts", { cwd: process.cwd() });
10
+ }
11
+ expect(fs.existsSync(schemasDir)).toBe(true);
12
+ const files = fs.readdirSync(schemasDir).filter((f) => f.endsWith(".json"));
13
+ expect(files.length).toBeGreaterThan(0);
14
+ for (const file of files) {
15
+ const filePath = path.join(schemasDir, file);
16
+ const content = JSON.parse(fs.readFileSync(filePath, "utf-8"));
17
+ expect(content).toHaveProperty("name");
18
+ expect(typeof content.name).toBe("string");
19
+ expect(content).toHaveProperty("description");
20
+ expect(typeof content.description).toBe("string");
21
+ expect(content).toHaveProperty("parameters");
22
+ expect(content.parameters).not.toBeNull();
23
+ expect(typeof content.parameters).toBe("object");
24
+ expect(content.parameters.type).toBe("object");
25
+ expect(content.parameters.properties).toBeDefined();
26
+ }
27
+ });
28
+ });