@arii/boomtick-mcp 0.3.5 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mcp/schemas/github.issue_update.json +5 -1
- package/.mcp/schemas/github.search_open_prs.json +8 -1
- package/CHANGELOG.md +18 -0
- package/actions/ai-review/action.yml +2 -2
- package/actions/audit/action.yml +1 -1
- package/actions/ci-validate/action.yml +1 -1
- package/actions/impact-analysis/action.yml +106 -36
- package/actions/lint-typecheck/action.yml +2 -2
- package/actions/setup/action.yml +1 -1
- package/actions/test-build/action.yml +1 -1
- package/dist/lib/td-cli.js +27 -0
- package/dist/tools/contract.js +1085 -151
- package/dist/tools/github.get_pr_diff.js +1 -1
- package/dist/tools/repo.read_agent_context.js +10 -14
- package/dist/tools/repo.read_agent_context.test.js +10 -0
- package/package.json +4 -3
- package/scripts/sync-contracts.ts +216 -6
- package/src/lib/td-cli.ts +35 -0
- package/src/tools/contract.ts +1844 -878
- package/src/tools/github.get_pr_diff.ts +1 -1
- package/src/tools/repo.read_agent_context.test.ts +11 -0
- package/src/tools/repo.read_agent_context.ts +10 -14
|
@@ -82,6 +82,10 @@
|
|
|
82
82
|
"state": {
|
|
83
83
|
"anyOf": [
|
|
84
84
|
{
|
|
85
|
+
"enum": [
|
|
86
|
+
"open",
|
|
87
|
+
"closed"
|
|
88
|
+
],
|
|
85
89
|
"type": "string"
|
|
86
90
|
},
|
|
87
91
|
{
|
|
@@ -89,7 +93,7 @@
|
|
|
89
93
|
}
|
|
90
94
|
],
|
|
91
95
|
"default": null,
|
|
92
|
-
"description": "The state to set the issue to
|
|
96
|
+
"description": "The state to set the issue to.",
|
|
93
97
|
"title": "State"
|
|
94
98
|
}
|
|
95
99
|
},
|
|
@@ -2,13 +2,20 @@
|
|
|
2
2
|
"properties": {
|
|
3
3
|
"state": {
|
|
4
4
|
"default": "open",
|
|
5
|
-
"description": "The state of the PRs to search for
|
|
5
|
+
"description": "The state of the PRs to search for.",
|
|
6
|
+
"enum": [
|
|
7
|
+
"open",
|
|
8
|
+
"closed",
|
|
9
|
+
"all"
|
|
10
|
+
],
|
|
6
11
|
"title": "State",
|
|
7
12
|
"type": "string"
|
|
8
13
|
},
|
|
9
14
|
"limit": {
|
|
10
15
|
"default": 100,
|
|
11
16
|
"description": "The maximum number of PRs to return (default: 100, range: 1-100).",
|
|
17
|
+
"maximum": 100,
|
|
18
|
+
"minimum": 1,
|
|
12
19
|
"title": "Limit",
|
|
13
20
|
"type": "integer"
|
|
14
21
|
},
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.7](https://github.com/arii/boomtick/compare/mcp-v0.3.6...mcp-v0.3.7) (2026-08-17)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **ci:** remove submodule-pinned action refs from impact-analysis ([#723](https://github.com/arii/boomtick/issues/723)) ([d6e0494](https://github.com/arii/boomtick/commit/d6e0494fe8ac781777fb1546bb3c0f0102074a5c))
|
|
9
|
+
* ensure impact analysis posts PR comments and sends Jules messages ([#724](https://github.com/arii/boomtick/issues/724)) ([a52cc8e](https://github.com/arii/boomtick/commit/a52cc8e220def8bf2057b4da1b596e80fc0eed1f))
|
|
10
|
+
* resolve script fallback paths in mcp impact-analysis action ([#716](https://github.com/arii/boomtick/issues/716)) ([b29dd06](https://github.com/arii/boomtick/commit/b29dd067da2b45fc8410ca26e34a8c74626fe937))
|
|
11
|
+
|
|
12
|
+
## [0.3.6](https://github.com/arii/boomtick/compare/mcp-v0.3.5...mcp-v0.3.6) (2026-08-13)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* **ai:** add gemini fallback logic to AIClient ([5cc87cc](https://github.com/arii/boomtick/commit/5cc87cc79e65df79db98bc27e679d14ffdd2d18d))
|
|
18
|
+
* **ai:** add gemini fallback logic to AIClient ([3ba37ec](https://github.com/arii/boomtick/commit/3ba37ec50a7447a9517e9f0029ff05ee01adf4d8))
|
|
19
|
+
* resolve td-cli not found in impact-analysis CI action ([#508](https://github.com/arii/boomtick/issues/508)) ([702643f](https://github.com/arii/boomtick/commit/702643f9d0d2447ae7baf20d22d553a7dfbae777))
|
|
20
|
+
|
|
3
21
|
## [0.3.5](https://github.com/arii/boomtick/compare/mcp-v0.3.4...mcp-v0.3.5) (2026-08-05)
|
|
4
22
|
|
|
5
23
|
|
|
@@ -7,7 +7,7 @@ inputs:
|
|
|
7
7
|
runs:
|
|
8
8
|
using: composite
|
|
9
9
|
steps:
|
|
10
|
-
- uses:
|
|
10
|
+
- uses: arii/boomtick/mcp/actions/setup@main
|
|
11
11
|
- name: AI review
|
|
12
12
|
env:
|
|
13
13
|
PR_NUMBER: ${{ inputs.pr_number }}
|
|
@@ -16,5 +16,5 @@ runs:
|
|
|
16
16
|
echo "::error::Invalid PR_NUMBER: must be numeric."
|
|
17
17
|
exit 1
|
|
18
18
|
fi
|
|
19
|
-
td-cli gh audit-pr "$PR_NUMBER" --fetch --audit
|
|
19
|
+
td-cli gh audit-pr "$PR_NUMBER" --fetch --audit --submit --execute
|
|
20
20
|
shell: bash
|
package/actions/audit/action.yml
CHANGED
|
@@ -16,16 +16,17 @@ inputs:
|
|
|
16
16
|
runs:
|
|
17
17
|
using: composite
|
|
18
18
|
steps:
|
|
19
|
-
-
|
|
20
|
-
|
|
21
|
-
- name: pnpm install
|
|
22
|
-
run: pnpm install --frozen-lockfile --prefer-offline
|
|
19
|
+
- name: Configure Git Safe Directory
|
|
20
|
+
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
|
23
21
|
shell: bash
|
|
24
22
|
|
|
25
23
|
- name: Validate PR Number
|
|
26
24
|
env:
|
|
27
25
|
PR_NUMBER: ${{ inputs.pr_number }}
|
|
28
26
|
run: |
|
|
27
|
+
if [[ -z "$PR_NUMBER" ]]; then
|
|
28
|
+
exit 0
|
|
29
|
+
fi
|
|
29
30
|
if [[ ! "$PR_NUMBER" =~ ^[0-9]+$ ]]; then
|
|
30
31
|
echo "::error::Invalid PR_NUMBER: must be numeric."
|
|
31
32
|
exit 1
|
|
@@ -170,8 +171,13 @@ runs:
|
|
|
170
171
|
echo "Executing check-visual-changes.ts at: $RESOLVED_PATH"
|
|
171
172
|
pnpm exec tsx "$RESOLVED_PATH" >> "$GITHUB_OUTPUT"
|
|
172
173
|
else
|
|
173
|
-
|
|
174
|
-
|
|
174
|
+
if [ -f "scripts/check-visual-changes.ts" ]; then
|
|
175
|
+
echo "Executing check-visual-changes.ts from current workspace."
|
|
176
|
+
pnpm exec tsx scripts/check-visual-changes.ts >> "$GITHUB_OUTPUT"
|
|
177
|
+
else
|
|
178
|
+
echo "⚠️ Skipping: check-visual-changes.ts script not found. Defaulting changed_routes=0"
|
|
179
|
+
echo "changed_routes=0" >> "$GITHUB_OUTPUT"
|
|
180
|
+
fi
|
|
175
181
|
fi
|
|
176
182
|
shell: bash
|
|
177
183
|
|
|
@@ -191,21 +197,6 @@ runs:
|
|
|
191
197
|
fi
|
|
192
198
|
shell: bash
|
|
193
199
|
|
|
194
|
-
- name: Visual review github models agent
|
|
195
|
-
if: always() && steps.check_visuals.outputs.changed_routes != '0' && steps.check_visuals.outputs.changed_routes != ''
|
|
196
|
-
env:
|
|
197
|
-
GITHUB_TOKEN: ${{ inputs.github_token }}
|
|
198
|
-
PR_NUMBER: ${{ inputs.pr_number }}
|
|
199
|
-
JULES_API_KEY: ${{ inputs.jules_api_key }}
|
|
200
|
-
GITHUB_REPOSITORY: ${{ github.repository }}
|
|
201
|
-
run: |
|
|
202
|
-
if node -e "const pkg = require('./package.json'); if (!pkg.scripts || !pkg.scripts['impact:github-models-review']) process.exit(1)" 2>/dev/null; then
|
|
203
|
-
pnpm run impact:github-models-review
|
|
204
|
-
else
|
|
205
|
-
echo "⚠️ Skipping: impact:github-models-review script not found"
|
|
206
|
-
fi
|
|
207
|
-
shell: bash
|
|
208
|
-
|
|
209
200
|
- name: Report Summary
|
|
210
201
|
if: always()
|
|
211
202
|
run: |
|
|
@@ -238,26 +229,20 @@ runs:
|
|
|
238
229
|
if [ -f artifacts/gemini-review.md ]; then
|
|
239
230
|
{ echo ""; cat artifacts/gemini-review.md; } >> "$GITHUB_STEP_SUMMARY"
|
|
240
231
|
fi
|
|
241
|
-
if [ -f artifacts/github-models-review.md ]; then
|
|
242
|
-
{ echo ""; cat artifacts/github-models-review.md; } >> "$GITHUB_STEP_SUMMARY"
|
|
243
|
-
fi
|
|
244
232
|
if [ -f artifacts/gemini-code-review.md ]; then
|
|
245
233
|
{ echo ""; cat artifacts/gemini-code-review.md; } >> "$GITHUB_STEP_SUMMARY"
|
|
246
234
|
fi
|
|
247
|
-
if [ -f artifacts/github-models-code-review.md ]; then
|
|
248
|
-
{ echo ""; cat artifacts/github-models-code-review.md; } >> "$GITHUB_STEP_SUMMARY"
|
|
249
|
-
fi
|
|
250
235
|
|
|
251
236
|
FAILED=0
|
|
252
|
-
# Expected verdicts: gemini-code-review
|
|
237
|
+
# Expected verdicts: gemini-code-review
|
|
253
238
|
# Visual reviews are expected only if visual changes exist
|
|
254
239
|
EXPECTED_REVIEWS=""
|
|
255
240
|
if [ "${{ steps.impact_analysis.outputs.has_impact }}" == "true" ]; then
|
|
256
241
|
if [ "${{ github.event_name }}" == "pull_request" ]; then
|
|
257
|
-
EXPECTED_REVIEWS="gemini-code-review
|
|
242
|
+
EXPECTED_REVIEWS="gemini-code-review"
|
|
258
243
|
fi
|
|
259
244
|
if [ "${{ steps.check_visuals.outputs.changed_routes }}" != "0" ] && [ "${{ steps.check_visuals.outputs.changed_routes }}" != "" ]; then
|
|
260
|
-
EXPECTED_REVIEWS="$EXPECTED_REVIEWS gemini-review
|
|
245
|
+
EXPECTED_REVIEWS="$EXPECTED_REVIEWS gemini-review"
|
|
261
246
|
fi
|
|
262
247
|
fi
|
|
263
248
|
|
|
@@ -275,7 +260,7 @@ runs:
|
|
|
275
260
|
|
|
276
261
|
if [[ "$VERDICT" == "warn" ]]; then
|
|
277
262
|
# Only allow specific safe-skip reasons for warnings to pass CI
|
|
278
|
-
IS_TRUNCATED=$(jq -r '.isTruncated //
|
|
263
|
+
IS_TRUNCATED=$(jq -r '.isTruncated // "false"' "$f" 2>/dev/null)
|
|
279
264
|
SKIP_REASON=$(jq -r '.skipReason // "NONE"' "$f" 2>/dev/null)
|
|
280
265
|
|
|
281
266
|
if [[ "$IS_TRUNCATED" == "true" || "$SKIP_REASON" == *"MISSING_API_KEY"* || "$SKIP_REASON" == *"RATE_LIMIT"* || "$SKIP_REASON" == *"UNHANDLED_ERROR"* || "$SKIP_REASON" == *"TRUNCATED"* || "$SKIP_REASON" == *"PARSE_ERROR"* || "$SKIP_REASON" == "NONE" ]]; then
|
|
@@ -325,19 +310,100 @@ runs:
|
|
|
325
310
|
if [ -f "$TARGET_PATH" ]; then
|
|
326
311
|
RESOLVED_PATH=$(realpath "$TARGET_PATH")
|
|
327
312
|
echo "Executing send-jules-impact.py at: $RESOLVED_PATH"
|
|
313
|
+
export PYTHONPATH="$(realpath "${{ github.action_path }}/../../../cli"):$PYTHONPATH"
|
|
328
314
|
python3 "$RESOLVED_PATH"
|
|
329
315
|
else
|
|
330
|
-
|
|
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
|
|
331
325
|
fi
|
|
332
326
|
shell: bash
|
|
333
327
|
|
|
334
328
|
- name: Update Impact Analysis Feedback (Final Result)
|
|
335
329
|
if: always()
|
|
336
|
-
uses:
|
|
330
|
+
uses: actions/github-script@v8 # nosemgrep: yaml.github-actions.security.github-actions-mutable-action-tag.github-actions-mutable-action-tag
|
|
331
|
+
env:
|
|
332
|
+
INPUT_STATUS_LINE: '✅ **Impact Analysis Complete**'
|
|
333
|
+
INPUT_IMPACT_BODY: ${{ env.IMPACT_BODY }}
|
|
337
334
|
with:
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
335
|
+
github-token: ${{ inputs.github_token }}
|
|
336
|
+
script: |
|
|
337
|
+
const owner = context.repo.owner;
|
|
338
|
+
const repo = context.repo.repo;
|
|
339
|
+
const branchName = context.ref.replace('refs/heads/', '');
|
|
340
|
+
|
|
341
|
+
const pstTime = new Date().toLocaleString('en-US', {
|
|
342
|
+
timeZone: 'America/Los_Angeles',
|
|
343
|
+
dateStyle: 'medium',
|
|
344
|
+
timeStyle: 'short'
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
if (branchName === 'main') return;
|
|
348
|
+
|
|
349
|
+
const pullRequests = await github.paginate(github.rest.pulls.list, {
|
|
350
|
+
owner,
|
|
351
|
+
repo,
|
|
352
|
+
state: 'open'
|
|
353
|
+
});
|
|
354
|
+
const matchingPRs = pullRequests.filter(pr => pr.head.ref === branchName);
|
|
355
|
+
|
|
356
|
+
const statusLine = process.env.INPUT_STATUS_LINE;
|
|
357
|
+
let impactBodyText = (process.env.INPUT_IMPACT_BODY || '')
|
|
358
|
+
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '[REMOVED SCRIPT]');
|
|
359
|
+
|
|
360
|
+
for (const pr of matchingPRs) {
|
|
361
|
+
const commentTag = `<!-- impact-analysis-comment-${branchName} -->`;
|
|
362
|
+
let bodyParts = [
|
|
363
|
+
`🚀 **Impact Analysis Details** (Last updated: ${pstTime} PST)`,
|
|
364
|
+
'',
|
|
365
|
+
statusLine,
|
|
366
|
+
`- **Commit SHA:** \`${process.env.GITHUB_SHA.slice(0, 7)}\``,
|
|
367
|
+
`- [Link to artifacts](https://github.com/${owner}/${repo}/actions/runs/${context.runId})`,
|
|
368
|
+
''
|
|
369
|
+
];
|
|
370
|
+
|
|
371
|
+
if (impactBodyText) {
|
|
372
|
+
bodyParts.push(impactBodyText, '');
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const MAX_LENGTH = 65536;
|
|
376
|
+
const truncationNotice = '\n\n... (truncated due to GitHub comment size limits)\n';
|
|
377
|
+
let currentContent = bodyParts.join('\n');
|
|
378
|
+
let projectedLength = currentContent.length + commentTag.length + 1;
|
|
379
|
+
|
|
380
|
+
if (projectedLength > MAX_LENGTH) {
|
|
381
|
+
const availableLength = MAX_LENGTH - commentTag.length - truncationNotice.length - 2;
|
|
382
|
+
let safeContent = currentContent.slice(0, availableLength);
|
|
383
|
+
let lastNewline = safeContent.lastIndexOf('\n');
|
|
384
|
+
if (lastNewline > availableLength * 0.9) {
|
|
385
|
+
safeContent = safeContent.slice(0, lastNewline);
|
|
386
|
+
}
|
|
387
|
+
bodyParts = [safeContent, truncationNotice];
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
bodyParts.push(commentTag);
|
|
391
|
+
const body = bodyParts.join('\n');
|
|
392
|
+
|
|
393
|
+
const comments = await github.paginate(github.rest.issues.listComments, {
|
|
394
|
+
owner,
|
|
395
|
+
repo,
|
|
396
|
+
issue_number: pr.number,
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
const existingComment = comments.find(c => c.body.includes(commentTag));
|
|
400
|
+
|
|
401
|
+
if (existingComment) {
|
|
402
|
+
await github.rest.issues.updateComment({ owner, repo, comment_id: existingComment.id, body });
|
|
403
|
+
} else {
|
|
404
|
+
await github.rest.issues.createComment({ issue_number: pr.number, owner, repo, body });
|
|
405
|
+
}
|
|
406
|
+
}
|
|
341
407
|
|
|
342
408
|
- name: Merge AI Logs into Artifacts
|
|
343
409
|
if: always()
|
|
@@ -372,6 +438,10 @@ runs:
|
|
|
372
438
|
env:
|
|
373
439
|
GITHUB_TOKEN: ${{ inputs.github_token }}
|
|
374
440
|
run: |
|
|
441
|
+
# Inject custom CLI path for td-cli resolution
|
|
442
|
+
PYTHON_SCRIPTS_PATH=$(python3 -c "import sysconfig; print(sysconfig.get_path('scripts'))")
|
|
443
|
+
export PATH="$PYTHON_SCRIPTS_PATH:$HOME/.local/bin:/github/home/.local/bin:$PATH"
|
|
444
|
+
|
|
375
445
|
# Resolve CLI dir for PYTHONPATH
|
|
376
446
|
if [ -d "cli" ]; then
|
|
377
447
|
export PYTHONPATH="cli:$PYTHONPATH"
|
|
@@ -10,10 +10,10 @@ inputs:
|
|
|
10
10
|
runs:
|
|
11
11
|
using: composite
|
|
12
12
|
steps:
|
|
13
|
-
- uses:
|
|
13
|
+
- uses: arii/boomtick/mcp/actions/setup@main
|
|
14
14
|
|
|
15
15
|
- name: Run Project Gate
|
|
16
|
-
uses:
|
|
16
|
+
uses: arii/boomtick/mcp/actions/run-project-gate@main
|
|
17
17
|
with:
|
|
18
18
|
target: ${{ inputs.target }}
|
|
19
19
|
github_token: ${{ inputs.github_token }}
|
package/actions/setup/action.yml
CHANGED
package/dist/lib/td-cli.js
CHANGED
|
@@ -1,4 +1,31 @@
|
|
|
1
1
|
import { runCommand } from "../lib/shell.js";
|
|
2
|
+
import { execSync } from "child_process";
|
|
3
|
+
import * as fs from "fs";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
export const loadStandaloneAgentContext = (repoRoot = process.cwd()) => {
|
|
6
|
+
const configPath = path.join(repoRoot, "project_config.json");
|
|
7
|
+
let config = {};
|
|
8
|
+
if (fs.existsSync(configPath)) {
|
|
9
|
+
try {
|
|
10
|
+
config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
11
|
+
}
|
|
12
|
+
catch (e) {
|
|
13
|
+
console.warn(`[MCP Warning] Failed to parse ${configPath}:`, e);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
let gitCommit = "unknown";
|
|
17
|
+
try {
|
|
18
|
+
gitCommit = execSync("git rev-parse HEAD", { cwd: repoRoot, encoding: "utf-8" }).trim();
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// Fallback if git is unavailable (e.g. Docker container without git)
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
packageName: config.packageName ?? "boomtick",
|
|
25
|
+
gitCommit,
|
|
26
|
+
config,
|
|
27
|
+
};
|
|
28
|
+
};
|
|
2
29
|
function tryParseJson(text) {
|
|
3
30
|
try {
|
|
4
31
|
return JSON.parse(text);
|