@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.
Files changed (187) hide show
  1. package/.env.example +6 -0
  2. package/.mcp/schemas/agent.search_ddgs.json +1 -0
  3. package/.mcp/schemas/boomtick.health.json +1 -0
  4. package/.mcp/schemas/github.checkout_branch.json +1 -0
  5. package/.mcp/schemas/github.comment_triage_summary.json +1 -0
  6. package/.mcp/schemas/github.create_issue.json +1 -0
  7. package/.mcp/schemas/github.create_pull_request.json +1 -0
  8. package/.mcp/schemas/github.get_merge_conflict_files.json +1 -0
  9. package/.mcp/schemas/github.get_pr.json +1 -0
  10. package/.mcp/schemas/github.get_pr_diff.json +1 -0
  11. package/.mcp/schemas/github.issue_comment.json +1 -0
  12. package/.mcp/schemas/github.issue_update.json +1 -0
  13. package/.mcp/schemas/github.issue_view.json +1 -0
  14. package/.mcp/schemas/github.open_replacement_pr.json +1 -0
  15. package/.mcp/schemas/github.search_open_prs.json +1 -0
  16. package/.mcp/schemas/jules.cancel_session.json +1 -0
  17. package/.mcp/schemas/jules.create_session.json +1 -0
  18. package/.mcp/schemas/jules.get_messages.json +1 -0
  19. package/.mcp/schemas/jules.get_pr.json +1 -0
  20. package/.mcp/schemas/jules.get_session.json +1 -0
  21. package/.mcp/schemas/jules.list_sessions.json +1 -0
  22. package/.mcp/schemas/jules.send_message.json +1 -0
  23. package/.mcp/schemas/jules.trigger_feedback.json +1 -0
  24. package/.mcp/schemas/repo.commit_patch.json +1 -0
  25. package/.mcp/schemas/repo.create_branch.json +1 -0
  26. package/.mcp/schemas/repo.create_repair_branch.json +1 -0
  27. package/.mcp/schemas/repo.get_changed_files.json +1 -0
  28. package/.mcp/schemas/repo.get_command_schema.json +1 -0
  29. package/.mcp/schemas/repo.get_package_scripts.json +1 -0
  30. package/.mcp/schemas/repo.get_route_map.json +1 -0
  31. package/.mcp/schemas/repo.logs.json +1 -0
  32. package/.mcp/schemas/repo.read_agent_context.json +1 -0
  33. package/.mcp/schemas/repo.read_ci_logs.json +1 -0
  34. package/.mcp/schemas/repo.run_lighthouse.json +1 -0
  35. package/.mcp/schemas/repo.run_playwright.json +1 -0
  36. package/.mcp/schemas/repo.run_tests.json +1 -0
  37. package/CHANGELOG.md +50 -0
  38. package/README.md +108 -0
  39. package/actions/ai-review/action.yml +15 -0
  40. package/actions/audit/action.yml +37 -0
  41. package/actions/ci-validate/action.yml +9 -0
  42. package/actions/impact-analysis/action.yml +385 -0
  43. package/actions/lint-typecheck/action.yml +19 -0
  44. package/actions/run-project-gate/action.yml +163 -0
  45. package/actions/setup/action.yml +15 -0
  46. package/actions/setup-workspace/action.yml +179 -0
  47. package/actions/test-build/action.yml +71 -0
  48. package/actions/update-pr-comment/action.yml +101 -0
  49. package/dist/config.js +110 -0
  50. package/dist/evals/run-evals.js +38 -0
  51. package/dist/index.js +10 -0
  52. package/dist/lib/error_utils.js +8 -0
  53. package/dist/lib/git.js +33 -0
  54. package/dist/lib/result.js +21 -0
  55. package/dist/lib/shell.js +91 -0
  56. package/dist/lib/shell.test.js +10 -0
  57. package/dist/lib/td-cli.js +27 -0
  58. package/dist/lib/test-utils.js +20 -0
  59. package/dist/mcp/definitions.js +242 -0
  60. package/dist/mcp/server.js +317 -0
  61. package/dist/mcp/tools.js +18 -0
  62. package/dist/tools/contract.js +2069 -0
  63. package/dist/tools/ddgs.search.js +50 -0
  64. package/dist/tools/ddgs.search.test.js +67 -0
  65. package/dist/tools/ddgs_search.py +23 -0
  66. package/dist/tools/github.checkout_branch.js +20 -0
  67. package/dist/tools/github.comment_triage_summary.js +25 -0
  68. package/dist/tools/github.create_issue.js +28 -0
  69. package/dist/tools/github.create_issue.test.js +54 -0
  70. package/dist/tools/github.create_pull_request.js +44 -0
  71. package/dist/tools/github.get_merge_conflict_files.js +26 -0
  72. package/dist/tools/github.get_pr.js +18 -0
  73. package/dist/tools/github.get_pr_diff.js +24 -0
  74. package/dist/tools/github.issue_comment.js +24 -0
  75. package/dist/tools/github.issue_update.js +29 -0
  76. package/dist/tools/github.issue_view.js +28 -0
  77. package/dist/tools/github.open_replacement_pr.js +41 -0
  78. package/dist/tools/github.search_open_prs.js +31 -0
  79. package/dist/tools/github.search_open_prs.test.js +51 -0
  80. package/dist/tools/jules/cancel-session.js +20 -0
  81. package/dist/tools/jules/create-session.js +20 -0
  82. package/dist/tools/jules/create-session.test.js +99 -0
  83. package/dist/tools/jules/get-messages.js +19 -0
  84. package/dist/tools/jules/get-messages.test.js +45 -0
  85. package/dist/tools/jules/get-pr.js +14 -0
  86. package/dist/tools/jules/get-pr.test.js +37 -0
  87. package/dist/tools/jules/get-session.js +10 -0
  88. package/dist/tools/jules/list-sessions.js +43 -0
  89. package/dist/tools/jules/send-message.js +22 -0
  90. package/dist/tools/jules/send-message.test.js +56 -0
  91. package/dist/tools/jules/shared.js +26 -0
  92. package/dist/tools/jules/trigger-feedback.js +16 -0
  93. package/dist/tools/jules/trigger-feedback.test.js +42 -0
  94. package/dist/tools/repo.commit_patch.js +33 -0
  95. package/dist/tools/repo.create_branch.js +21 -0
  96. package/dist/tools/repo.create_branch.test.js +42 -0
  97. package/dist/tools/repo.create_repair_branch.js +38 -0
  98. package/dist/tools/repo.get_changed_files.js +21 -0
  99. package/dist/tools/repo.get_command_schema.js +27 -0
  100. package/dist/tools/repo.get_package_scripts.js +35 -0
  101. package/dist/tools/repo.get_package_scripts.test.js +39 -0
  102. package/dist/tools/repo.get_route_map.js +37 -0
  103. package/dist/tools/repo.logs.js +16 -0
  104. package/dist/tools/repo.read_agent_context.js +20 -0
  105. package/dist/tools/repo.read_ci_logs.js +18 -0
  106. package/dist/tools/repo.run_lighthouse.js +37 -0
  107. package/dist/tools/repo.run_playwright.js +29 -0
  108. package/dist/tools/repo.run_tests.js +43 -0
  109. package/dist/tools/types.js +1 -0
  110. package/docs/testing.md +73 -0
  111. package/package.json +45 -0
  112. package/scripts/create_instructions.sh +42 -0
  113. package/scripts/export-mcp-schema.ts +7 -0
  114. package/scripts/sync-contracts.ts +74 -0
  115. package/scripts/sync-mcp-schemas.ts +97 -0
  116. package/src/agents/conflict-scout.prompt.md +20 -0
  117. package/src/agents/pr-consolidation.prompt.md +32 -0
  118. package/src/agents/pr-writer.prompt.md +13 -0
  119. package/src/agents/repair-agent.prompt.md +18 -0
  120. package/src/agents/repo-context.prompt.md +20 -0
  121. package/src/agents/verifier-agent.prompt.md +22 -0
  122. package/src/config.ts +113 -0
  123. package/src/evals/fixtures/merge-conflict-simple/README.md +0 -0
  124. package/src/evals/run-evals.ts +40 -0
  125. package/src/index.ts +12 -0
  126. package/src/lib/error_utils.ts +8 -0
  127. package/src/lib/git.ts +41 -0
  128. package/src/lib/result.ts +28 -0
  129. package/src/lib/shell.test.ts +11 -0
  130. package/src/lib/shell.ts +118 -0
  131. package/src/lib/td-cli.ts +38 -0
  132. package/src/lib/test-utils.ts +23 -0
  133. package/src/mcp/definitions.ts +278 -0
  134. package/src/mcp/server.ts +359 -0
  135. package/src/mcp/tools.ts +20 -0
  136. package/src/tools/contract.ts +2245 -0
  137. package/src/tools/ddgs.search.test.ts +92 -0
  138. package/src/tools/ddgs.search.ts +58 -0
  139. package/src/tools/ddgs_search.py +23 -0
  140. package/src/tools/github.checkout_branch.ts +25 -0
  141. package/src/tools/github.comment_triage_summary.ts +30 -0
  142. package/src/tools/github.create_issue.test.ts +63 -0
  143. package/src/tools/github.create_issue.ts +34 -0
  144. package/src/tools/github.create_pull_request.ts +50 -0
  145. package/src/tools/github.get_merge_conflict_files.ts +31 -0
  146. package/src/tools/github.get_pr.ts +24 -0
  147. package/src/tools/github.get_pr_diff.ts +29 -0
  148. package/src/tools/github.issue_comment.ts +31 -0
  149. package/src/tools/github.issue_update.ts +31 -0
  150. package/src/tools/github.issue_view.ts +35 -0
  151. package/src/tools/github.open_replacement_pr.ts +48 -0
  152. package/src/tools/github.search_open_prs.test.ts +60 -0
  153. package/src/tools/github.search_open_prs.ts +40 -0
  154. package/src/tools/jules/cancel-session.ts +26 -0
  155. package/src/tools/jules/create-session.test.ts +114 -0
  156. package/src/tools/jules/create-session.ts +24 -0
  157. package/src/tools/jules/get-messages.test.ts +52 -0
  158. package/src/tools/jules/get-messages.ts +24 -0
  159. package/src/tools/jules/get-pr.test.ts +43 -0
  160. package/src/tools/jules/get-pr.ts +17 -0
  161. package/src/tools/jules/get-session.ts +12 -0
  162. package/src/tools/jules/list-sessions.ts +50 -0
  163. package/src/tools/jules/send-message.test.ts +66 -0
  164. package/src/tools/jules/send-message.ts +27 -0
  165. package/src/tools/jules/shared.ts +28 -0
  166. package/src/tools/jules/trigger-feedback.test.ts +50 -0
  167. package/src/tools/jules/trigger-feedback.ts +21 -0
  168. package/src/tools/repo.commit_patch.ts +41 -0
  169. package/src/tools/repo.create_branch.test.ts +55 -0
  170. package/src/tools/repo.create_branch.ts +29 -0
  171. package/src/tools/repo.create_repair_branch.ts +47 -0
  172. package/src/tools/repo.get_changed_files.ts +26 -0
  173. package/src/tools/repo.get_command_schema.ts +30 -0
  174. package/src/tools/repo.get_package_scripts.test.ts +51 -0
  175. package/src/tools/repo.get_package_scripts.ts +36 -0
  176. package/src/tools/repo.get_route_map.ts +41 -0
  177. package/src/tools/repo.logs.ts +21 -0
  178. package/src/tools/repo.read_agent_context.ts +22 -0
  179. package/src/tools/repo.read_ci_logs.ts +21 -0
  180. package/src/tools/repo.run_lighthouse.ts +40 -0
  181. package/src/tools/repo.run_playwright.ts +35 -0
  182. package/src/tools/repo.run_tests.ts +47 -0
  183. package/src/tools/types.ts +8 -0
  184. package/start_browsermcp.sh +14 -0
  185. package/start_github_mcp.sh +6 -0
  186. package/tsconfig.json +15 -0
  187. package/vitest.config.ts +9 -0
@@ -0,0 +1,385 @@
1
+ name: BoomTick Impact Analysis
2
+ description: Comprehensive deployment impact analysis, diffing, AI reviews, and reporting
3
+ inputs:
4
+ github_token:
5
+ description: 'GitHub token'
6
+ required: true
7
+ gemini_api_key:
8
+ description: 'Gemini API key'
9
+ required: true
10
+ pr_number:
11
+ description: 'PR number'
12
+ required: true
13
+ jules_api_key:
14
+ description: 'Jules API key required for authenticating with the Jules API to send impact analysis results. Ensure this key is stored securely and not exposed in logs.'
15
+ required: true
16
+ runs:
17
+ using: composite
18
+ steps:
19
+ - uses: ./mcp/actions/setup
20
+
21
+ - run: pnpm install --frozen-lockfile --prefer-offline
22
+ shell: bash
23
+
24
+ - name: Deployment Impact Analysis
25
+ id: impact_analysis
26
+ env:
27
+ GITHUB_TOKEN: ${{ inputs.github_token }}
28
+ PR_NUMBER: ${{ inputs.pr_number }}
29
+ GITHUB_REPOSITORY: ${{ github.repository }}
30
+ JULES_API_KEY: ${{ inputs.jules_api_key }}
31
+ run: |
32
+ if ! node -e "const pkg = require('./package.json'); if (!pkg.scripts || !pkg.scripts['impact:analysis']) process.exit(1)" 2>/dev/null; then
33
+ echo "⚠️ Skipping: impact:analysis script not found in package.json"
34
+ echo "has_impact=false" >> "$GITHUB_OUTPUT"
35
+ exit 0
36
+ fi
37
+
38
+ pnpm run impact:analysis
39
+
40
+ # Determine has_impact based on generated report
41
+ if [ -f artifacts/impact-analysis/impact.json ]; then
42
+ ROUTES_COUNT=$(jq '.routes | length' artifacts/impact-analysis/impact.json 2>/dev/null || echo "0")
43
+ FILES_COUNT=$(jq '.changedFiles | length' artifacts/impact-analysis/impact.json 2>/dev/null || echo "0")
44
+ if [ "$ROUTES_COUNT" -gt 0 ] || [ "$FILES_COUNT" -gt 0 ]; then
45
+ echo "has_impact=true" >> "$GITHUB_OUTPUT"
46
+ else
47
+ echo "has_impact=false" >> "$GITHUB_OUTPUT"
48
+ fi
49
+ else
50
+ echo "has_impact=true" >> "$GITHUB_OUTPUT"
51
+ fi
52
+ shell: bash
53
+
54
+ - name: Build Main
55
+ if: steps.impact_analysis.outputs.has_impact == 'true'
56
+ env:
57
+ DISABLE_MINIFY: true
58
+ GITHUB_TOKEN: ${{ inputs.github_token }}
59
+ PR_NUMBER: ${{ inputs.pr_number }}
60
+ GITHUB_REPOSITORY: ${{ github.repository }}
61
+ JULES_API_KEY: ${{ inputs.jules_api_key }}
62
+ run: |
63
+ if node -e "const pkg = require('./package.json'); if (!pkg.scripts || !pkg.scripts['impact:build-main']) process.exit(1)" 2>/dev/null; then
64
+ pnpm run impact:build-main
65
+ else
66
+ echo "⚠️ Skipping: impact:build-main script not found"
67
+ fi
68
+ shell: bash
69
+
70
+ - name: Build PR
71
+ if: steps.impact_analysis.outputs.has_impact == 'true'
72
+ env:
73
+ VITE_BASE_PATH: /
74
+ DISABLE_MINIFY: true
75
+ GITHUB_TOKEN: ${{ inputs.github_token }}
76
+ PR_NUMBER: ${{ inputs.pr_number }}
77
+ GITHUB_REPOSITORY: ${{ github.repository }}
78
+ JULES_API_KEY: ${{ inputs.jules_api_key }}
79
+ run: |
80
+ if node -e "const pkg = require('./package.json'); if (!pkg.scripts || !pkg.scripts['build:review']) process.exit(1)" 2>/dev/null; then
81
+ pnpm run build:review
82
+ else
83
+ echo "⚠️ Skipping: build:review script not found"
84
+ fi
85
+ shell: bash
86
+
87
+ - name: Code review agent
88
+ if: always() && github.event_name == 'pull_request' && steps.impact_analysis.outputs.has_impact == 'true'
89
+ continue-on-error: true
90
+ env:
91
+ GEMINI_API_KEY: ${{ inputs.gemini_api_key }}
92
+ GITHUB_TOKEN: ${{ inputs.github_token }}
93
+ PR_NUMBER: ${{ inputs.pr_number }}
94
+ GITHUB_REPOSITORY: ${{ github.repository }}
95
+ JULES_API_KEY: ${{ inputs.jules_api_key }}
96
+ run: |
97
+ if node -e "const pkg = require('./package.json'); if (!pkg.scripts || !pkg.scripts['impact:gemini-code-review']) process.exit(1)" 2>/dev/null; then
98
+ pnpm run impact:gemini-code-review
99
+ else
100
+ echo "⚠️ Skipping: impact:gemini-code-review script not found"
101
+ fi
102
+ shell: bash
103
+
104
+ - name: Code review github models agent
105
+ if: always() && github.event_name == 'pull_request' && steps.impact_analysis.outputs.has_impact == 'true'
106
+ continue-on-error: true
107
+ env:
108
+ GITHUB_TOKEN: ${{ inputs.github_token }}
109
+ PR_NUMBER: ${{ inputs.pr_number }}
110
+ GITHUB_REPOSITORY: ${{ github.repository }}
111
+ JULES_API_KEY: ${{ inputs.jules_api_key }}
112
+ run: |
113
+ if node -e "const pkg = require('./package.json'); if (!pkg.scripts || !pkg.scripts['impact:github-models-code-review']) process.exit(1)" 2>/dev/null; then
114
+ pnpm run impact:github-models-code-review
115
+ else
116
+ echo "⚠️ Skipping: impact:github-models-code-review script not found"
117
+ fi
118
+ shell: bash
119
+
120
+ - name: Visual Diff
121
+ if: steps.impact_analysis.outputs.has_impact == 'true'
122
+ env:
123
+ GITHUB_TOKEN: ${{ inputs.github_token }}
124
+ PR_NUMBER: ${{ inputs.pr_number }}
125
+ GITHUB_REPOSITORY: ${{ github.repository }}
126
+ JULES_API_KEY: ${{ inputs.jules_api_key }}
127
+ run: |
128
+ if node -e "const pkg = require('./package.json'); if (!pkg.scripts || !pkg.scripts['impact:visual-diff']) process.exit(1)" 2>/dev/null; then
129
+ pnpm run impact:visual-diff
130
+ else
131
+ echo "⚠️ Skipping: impact:visual-diff script not found"
132
+ fi
133
+ shell: bash
134
+
135
+ - name: DOM Diff
136
+ if: steps.impact_analysis.outputs.has_impact == 'true'
137
+ env:
138
+ GITHUB_TOKEN: ${{ inputs.github_token }}
139
+ PR_NUMBER: ${{ inputs.pr_number }}
140
+ GITHUB_REPOSITORY: ${{ github.repository }}
141
+ JULES_API_KEY: ${{ inputs.jules_api_key }}
142
+ run: |
143
+ if node -e "const pkg = require('./package.json'); if (!pkg.scripts || !pkg.scripts['impact:dom-diff']) process.exit(1)" 2>/dev/null; then
144
+ pnpm run impact:dom-diff
145
+ else
146
+ echo "⚠️ Skipping: impact:dom-diff script not found"
147
+ fi
148
+ shell: bash
149
+
150
+ - name: Check if visual changes exist
151
+ if: steps.impact_analysis.outputs.has_impact == 'true'
152
+ id: check_visuals
153
+ env:
154
+ VISUAL_DIFF_THRESHOLD: 1.5
155
+ run: |
156
+ SCRIPT_PATH=""
157
+ if [ -f "scripts/check-visual-changes.ts" ]; then
158
+ SCRIPT_PATH="scripts/check-visual-changes.ts"
159
+ elif [ -f "${{ github.action_path }}/../../../scripts/check-visual-changes.ts" ]; then
160
+ SCRIPT_PATH="${{ github.action_path }}/../../../scripts/check-visual-changes.ts"
161
+ fi
162
+
163
+ if [[ -n "$SCRIPT_PATH" && ( "$SCRIPT_PATH" == *"/scripts/check-visual-changes.ts" || "$SCRIPT_PATH" == "scripts/check-visual-changes.ts" ) ]]; then
164
+ RESOLVED_PATH=$(realpath "$SCRIPT_PATH")
165
+ if [[ -f "$RESOLVED_PATH" && "$RESOLVED_PATH" == *"/scripts/check-visual-changes.ts" ]]; then
166
+ echo "Executing check-visual-changes.ts at: $RESOLVED_PATH"
167
+ pnpm exec tsx "$RESOLVED_PATH" >> "$GITHUB_OUTPUT"
168
+ else
169
+ echo "❌ Error: Resolved path validation failed for $RESOLVED_PATH. Defaulting changed_routes=0"
170
+ echo "changed_routes=0" >> "$GITHUB_OUTPUT"
171
+ fi
172
+ else
173
+ echo "⚠️ Skipping: check-visual-changes.ts script not found or validation failed. Defaulting changed_routes=0"
174
+ echo "changed_routes=0" >> "$GITHUB_OUTPUT"
175
+ fi
176
+ shell: bash
177
+
178
+ - name: Visual review agent
179
+ if: always() && steps.check_visuals.outputs.changed_routes != '0' && steps.check_visuals.outputs.changed_routes != ''
180
+ env:
181
+ GEMINI_API_KEY: ${{ inputs.gemini_api_key }}
182
+ GITHUB_TOKEN: ${{ inputs.github_token }}
183
+ PR_NUMBER: ${{ inputs.pr_number }}
184
+ JULES_API_KEY: ${{ inputs.jules_api_key }}
185
+ GITHUB_REPOSITORY: ${{ github.repository }}
186
+ run: |
187
+ if node -e "const pkg = require('./package.json'); if (!pkg.scripts || !pkg.scripts['impact:gemini-review']) process.exit(1)" 2>/dev/null; then
188
+ pnpm run impact:gemini-review
189
+ else
190
+ echo "⚠️ Skipping: impact:gemini-review script not found"
191
+ fi
192
+ shell: bash
193
+
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
+ - name: Report Summary
210
+ if: always()
211
+ run: |
212
+ if [ -f artifacts/deployment-review.md ]; then
213
+ cat artifacts/deployment-review.md >> "$GITHUB_STEP_SUMMARY"
214
+ {
215
+ echo "IMPACT_BODY<<EOF"
216
+ cat artifacts/deployment-review.md
217
+ echo "EOF"
218
+ } >> "$GITHUB_ENV"
219
+ elif [ -f artifacts/impact-analysis/impact.md ]; then
220
+ cat artifacts/impact-analysis/impact.md >> "$GITHUB_STEP_SUMMARY"
221
+ {
222
+ echo "IMPACT_BODY<<EOF"
223
+ cat artifacts/impact-analysis/impact.md
224
+ echo "EOF"
225
+ } >> "$GITHUB_ENV"
226
+ else
227
+ {
228
+ echo "## Deployment Impact Analysis"
229
+ echo "No impact report found."
230
+ } >> "$GITHUB_STEP_SUMMARY"
231
+ {
232
+ echo "IMPACT_BODY<<EOF"
233
+ echo "No impact report found."
234
+ echo "EOF"
235
+ } >> "$GITHUB_ENV"
236
+ fi
237
+
238
+ if [ -f artifacts/gemini-review.md ]; then
239
+ { echo ""; cat artifacts/gemini-review.md; } >> "$GITHUB_STEP_SUMMARY"
240
+ fi
241
+ if [ -f artifacts/github-models-review.md ]; then
242
+ { echo ""; cat artifacts/github-models-review.md; } >> "$GITHUB_STEP_SUMMARY"
243
+ fi
244
+ if [ -f artifacts/gemini-code-review.md ]; then
245
+ { echo ""; cat artifacts/gemini-code-review.md; } >> "$GITHUB_STEP_SUMMARY"
246
+ 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
+
251
+ FAILED=0
252
+ # Expected verdicts: gemini-code-review, github-models-code-review
253
+ # Visual reviews are expected only if visual changes exist
254
+ EXPECTED_REVIEWS=""
255
+ if [ "${{ steps.impact_analysis.outputs.has_impact }}" == "true" ]; then
256
+ if [ "${{ github.event_name }}" == "pull_request" ]; then
257
+ EXPECTED_REVIEWS="gemini-code-review github-models-code-review"
258
+ fi
259
+ if [ "${{ steps.check_visuals.outputs.changed_routes }}" != "0" ] && [ "${{ steps.check_visuals.outputs.changed_routes }}" != "" ]; then
260
+ EXPECTED_REVIEWS="$EXPECTED_REVIEWS gemini-review github-models-review"
261
+ fi
262
+ fi
263
+
264
+ for r in $EXPECTED_REVIEWS; do
265
+ f="artifacts/$r-verdict.json"
266
+ if [ ! -f "$f" ]; then
267
+ echo "❌ Expected review verdict file missing: $f. The review script may have crashed." >&2
268
+ FAILED=1
269
+ continue
270
+ fi
271
+
272
+ PASSED=$(jq -r '.passed | if type == "null" or type == "nan" then "error" else . end' "$f" 2>/dev/null || echo "error")
273
+ VERDICT=$(jq -r '.llmVerdict | if type == "null" or type == "nan" then "unknown" else . end' "$f" 2>/dev/null || echo "unknown")
274
+ HIGH=$(jq -r '.highCount | if type == "null" or type == "nan" then "0" else . end' "$f" 2>/dev/null || echo "0")
275
+
276
+ if [[ "$VERDICT" == "warn" ]]; then
277
+ # Only allow specific safe-skip reasons for warnings to pass CI
278
+ IS_TRUNCATED=$(jq -r '.isTruncated // .truncated // "false"' "$f" 2>/dev/null)
279
+ SKIP_REASON=$(jq -r '.skipReason // "NONE"' "$f" 2>/dev/null)
280
+
281
+ 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
282
+ echo "⚠️ Review Warning: Legitimate skip/truncation ($f, reason: $SKIP_REASON, truncated: $IS_TRUNCATED). Manual review recommended."
283
+ else
284
+ echo "❌ Review failed: Unexpected warning state ($f, reason: $SKIP_REASON)" >&2
285
+ FAILED=1
286
+ fi
287
+ fi
288
+
289
+ if [[ "$PASSED" == "false" || "$VERDICT" == "fail" ]]; then
290
+ echo "❌ Review failed: $HIGH HIGH severity issues found ($f)" >&2
291
+ FAILED=1
292
+ elif [ "$PASSED" = "error" ]; then
293
+ echo "❌ Review failed: Could not parse verdict file ($f). Review may be malformed." >&2
294
+ FAILED=1
295
+ fi
296
+ done
297
+ exit $FAILED
298
+ shell: bash
299
+
300
+ - name: Parse Jules session ID from PR description
301
+ if: always()
302
+ id: parse_jules
303
+ env:
304
+ PR_BODY: ${{ github.event.pull_request.body }}
305
+ run: |
306
+ TASK_ID=$(echo "$PR_BODY" | grep -oP 'jules\.google\.com/task/\K[0-9]+' | head -1 || true)
307
+ if [ -z "$TASK_ID" ]; then
308
+ echo "No Jules task ID found in PR description. Skipping."
309
+ exit 0
310
+ fi
311
+ echo "Found Jules task ID: $TASK_ID"
312
+ echo "task_id=$TASK_ID" >> "$GITHUB_OUTPUT"
313
+ shell: bash
314
+
315
+ - name: Send impact analysis to Jules session
316
+ if: always() && steps.parse_jules.outputs.task_id != ''
317
+ env:
318
+ TASK_ID: ${{ steps.parse_jules.outputs.task_id }}
319
+ JULES_API_KEY: ${{ inputs.jules_api_key }}
320
+ GITHUB_TOKEN: ${{ inputs.github_token }}
321
+ PR_NUMBER: ${{ inputs.pr_number }}
322
+ GITHUB_REPOSITORY: ${{ github.repository }}
323
+ run: |
324
+ if [ -f "scripts/send-jules-impact.py" ]; then
325
+ python3 scripts/send-jules-impact.py
326
+ elif [ -f "${{ github.action_path }}/../../../scripts/send-jules-impact.py" ]; then
327
+ python3 "${{ github.action_path }}/../../../scripts/send-jules-impact.py"
328
+ else
329
+ echo "⚠️ Skipping: send-jules-impact.py script not found"
330
+ fi
331
+ shell: bash
332
+
333
+ - name: Update Impact Analysis Feedback (Final Result)
334
+ if: always()
335
+ uses: ./mcp/actions/update-pr-comment
336
+ with:
337
+ github_token: ${{ inputs.github_token }}
338
+ status_line: '✅ **Impact Analysis Complete**'
339
+ impact_body: ${{ env.IMPACT_BODY }}
340
+
341
+ - name: Merge AI Logs into Artifacts
342
+ if: always()
343
+ run: |
344
+ # Ensure log directories exist
345
+ mkdir -p .boomtick/logs/ai
346
+ mkdir -p artifacts/logs/ai
347
+
348
+ # Sync from the TS logger location to the workspace log location expected by Python CLI
349
+ # Try both submodule and standalone paths
350
+ for log_dir in "cli/logs/ai"; do
351
+ if [ -d "$log_dir" ] && [ "$(ls -A "$log_dir" 2>/dev/null)" ]; then
352
+ cp -r "$log_dir"/* .boomtick/logs/ai/ 2>/dev/null || true
353
+ fi
354
+ done
355
+
356
+ # Sync to artifacts for upload
357
+ if [ -d .boomtick/logs/ai ] && [ "$(ls -A .boomtick/logs/ai 2>/dev/null)" ]; then
358
+ cp -r .boomtick/logs/ai/* artifacts/logs/ai/ 2>/dev/null || true
359
+ fi
360
+ shell: bash
361
+
362
+ - name: Upload Deployment Review Artifacts
363
+ if: always()
364
+ uses: actions/upload-artifact@v7 # nosemgrep: yaml.github-actions.security.github-actions-mutable-action-tag.github-actions-mutable-action-tag
365
+ with:
366
+ name: deployment-review
367
+ path: artifacts/
368
+
369
+ - name: Verify CI Metrics
370
+ if: always()
371
+ env:
372
+ GITHUB_TOKEN: ${{ inputs.github_token }}
373
+ run: |
374
+ # Resolve CLI dir for PYTHONPATH
375
+ if [ -d "cli" ]; then
376
+ export PYTHONPATH="cli:$PYTHONPATH"
377
+ fi
378
+
379
+ mkdir -p .boomtick/logs/ai
380
+ python3 -m dev_tools.cli gh summary-report >> "$GITHUB_STEP_SUMMARY"
381
+ if ! python3 -m dev_tools.cli gh verify-metrics; then
382
+ echo "CI Metrics verification failed."
383
+ exit 1
384
+ fi
385
+ shell: bash
@@ -0,0 +1,19 @@
1
+ name: BoomTick Lint & Typecheck
2
+ description: Runs linting and typechecking via project gate
3
+ inputs:
4
+ target:
5
+ description: 'Target to check (root or boomtick-mcp)'
6
+ required: true
7
+ github_token:
8
+ description: 'GitHub token'
9
+ required: true
10
+ runs:
11
+ using: composite
12
+ steps:
13
+ - uses: ./mcp/actions/setup
14
+
15
+ - name: Run Project Gate
16
+ uses: ./mcp/actions/run-project-gate
17
+ with:
18
+ target: ${{ inputs.target }}
19
+ github_token: ${{ inputs.github_token }}
@@ -0,0 +1,163 @@
1
+ name: "Run Project Gate"
2
+ description: "Abstracted logic for building and testing monorepo sub-projects"
3
+ inputs:
4
+ target:
5
+ description: "Target project to validate (e.g., 'root', 'boomtick-mcp')"
6
+ required: true
7
+ github_token:
8
+ description: "GitHub token for API access (e.g., ratchet-any)"
9
+ required: false
10
+
11
+ runs:
12
+ using: "composite"
13
+ steps:
14
+ - name: Resolve Project Paths
15
+ id: resolve-paths
16
+ shell: bash
17
+ env:
18
+ TARGET: ${{ inputs.target }}
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)/||")
27
+ elif [ -d "boomtick-pkg/cli" ]; then
28
+ CLI_PATH="boomtick-pkg/cli"
29
+ elif [ -d "boomtick/cli" ]; then
30
+ CLI_PATH="boomtick/cli"
31
+ elif [ -d "cli" ]; then
32
+ CLI_PATH="cli"
33
+ else
34
+ CLI_PATH="cli"
35
+ fi
36
+ echo "CLI_PATH=$CLI_PATH" >> "$GITHUB_OUTPUT"
37
+
38
+ case "$TARGET" in
39
+ root)
40
+ echo "FILTER_PATH=." >> "$GITHUB_OUTPUT"
41
+ ;;
42
+ boomtick-mcp)
43
+ if [ -d "mcp" ]; then
44
+ echo "FILTER_PATH=./mcp" >> "$GITHUB_OUTPUT"
45
+ else
46
+ echo "FILTER_PATH=./mcp" >> "$GITHUB_OUTPUT"
47
+ fi
48
+ ;;
49
+ *)
50
+ echo "::error::Invalid target: $TARGET. Must be 'root' or 'boomtick-mcp'."
51
+ exit 1
52
+ ;;
53
+ esac
54
+
55
+ - name: Validate Runtime
56
+ if: inputs.target == 'root'
57
+ run: |
58
+ echo "::group::Validate Runtime"
59
+ trap 'echo "::endgroup::"' EXIT
60
+ pnpm run check:runtime-files
61
+ pnpm run doctor
62
+ shell: bash
63
+
64
+ - name: pnpm install
65
+ env:
66
+ TARGET: ${{ inputs.target }}
67
+ FILTER_PATH: ${{ steps.resolve-paths.outputs.FILTER_PATH }}
68
+ run: |
69
+ echo "::group::pnpm install"
70
+ trap 'echo "::endgroup::"' EXIT
71
+ if [ "$TARGET" = "root" ]; then
72
+ pnpm install --frozen-lockfile --engine-strict=false
73
+ else
74
+ pnpm install --frozen-lockfile --filter "$FILTER_PATH" --engine-strict=false
75
+ fi
76
+ shell: bash
77
+
78
+ - name: Verify lockfile integrity (root only)
79
+ if: inputs.target == 'root'
80
+ run: |
81
+ if ! git diff --quiet pnpm-lock.yaml; then
82
+ echo "::error::pnpm-lock.yaml is out of date. Run pnpm install and commit."
83
+ exit 1
84
+ fi
85
+ shell: bash
86
+
87
+ - name: Lint
88
+ if: inputs.target == 'root'
89
+ run: |
90
+ pnpm run verify:schemas
91
+ pnpm run lint
92
+ shell: bash
93
+
94
+ - name: Type-check / Build
95
+ env:
96
+ TARGET: ${{ inputs.target }}
97
+ CLI_PATH: ${{ steps.resolve-paths.outputs.CLI_PATH }}
98
+ FILTER_PATH: ${{ steps.resolve-paths.outputs.FILTER_PATH }}
99
+ run: |
100
+ if [ "$TARGET" = "boomtick-mcp" ]; then
101
+ PYTHONPATH="$CLI_PATH" python3 "$CLI_PATH/dev_tools/schema_gen.py"
102
+ npx tsx "$FILTER_PATH/scripts/sync-contracts.ts"
103
+ pnpm --filter "$FILTER_PATH" run sync:mcp-schemas
104
+ fi
105
+
106
+ if [ "$TARGET" = "root" ]; then
107
+ pnpm run type-check
108
+ else
109
+ pnpm --filter "$FILTER_PATH" run build
110
+ fi
111
+ shell: bash
112
+
113
+ - name: Python Linting
114
+ if: inputs.target == 'root'
115
+ continue-on-error: true
116
+ run: |
117
+ echo "::group::Python Linting"
118
+ trap 'echo "::endgroup::"' EXIT
119
+ if [ -f "requirements-dev.txt" ]; then
120
+ pip install -r requirements-dev.txt
121
+ fi
122
+ pnpm run lint:python
123
+ pnpm run type-check:python
124
+ shell: bash
125
+
126
+ - name: Run Unit Tests
127
+ env:
128
+ TARGET: ${{ inputs.target }}
129
+ FILTER_PATH: ${{ steps.resolve-paths.outputs.FILTER_PATH }}
130
+ run: |
131
+ if [ "$TARGET" = "root" ]; then
132
+ pnpm test
133
+ else
134
+ pnpm --filter "$FILTER_PATH" run test
135
+ fi
136
+ shell: bash
137
+
138
+ - name: Run CLI Unit Tests
139
+ if: inputs.target == 'root' || inputs.target == 'boomtick-mcp'
140
+ env:
141
+ CLI_PATH: ${{ steps.resolve-paths.outputs.CLI_PATH }}
142
+ run: |
143
+ echo "::group::Pytest"
144
+ trap 'echo "::endgroup::"' EXIT
145
+ PYTHONPATH="$CLI_PATH:$CLI_PATH/dev_tools" python3 -m pytest "$CLI_PATH/tests/"
146
+ shell: bash
147
+
148
+ - name: Check for dead code
149
+ if: inputs.target == 'root'
150
+ run: pnpm run knip
151
+ shell: bash
152
+
153
+ - name: TypeScript 'any' Ratchet
154
+ if: inputs.target == 'root'
155
+ run: |
156
+ echo "::group::Ratchet Any"
157
+ trap 'echo "::endgroup::"' EXIT
158
+ export PYTHONPATH="${{ steps.resolve-paths.outputs.CLI_PATH }}:${{ steps.resolve-paths.outputs.CLI_PATH }}/dev_tools"
159
+ export PATH="$HOME/.local/bin:/github/home/.local/bin:$PATH"
160
+ python3 -m dev_tools.cli gh ratchet-any
161
+ env:
162
+ GITHUB_TOKEN: ${{ inputs.github_token }}
163
+ shell: bash
@@ -0,0 +1,15 @@
1
+ name: BoomTick Setup
2
+ description: Comprehensive setup including checkout, node/pnpm, workspace, and MCP build
3
+ runs:
4
+ using: composite
5
+ steps:
6
+ - name: Configure Git Safe Directory
7
+ run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
8
+ shell: bash
9
+
10
+ - name: Setup Workspace
11
+ uses: ./mcp/actions/setup-workspace
12
+
13
+ - name: Verify setup
14
+ run: python3 -m dev_tools.cli doctor
15
+ shell: bash