terramend 0.2.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 (406) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +145 -0
  3. package/dist/agents/claude.d.ts +73 -0
  4. package/dist/agents/claudePretoolGate.d.ts +99 -0
  5. package/dist/agents/gateServer.d.ts +7 -0
  6. package/dist/agents/index.d.ts +6 -0
  7. package/dist/agents/nativeFsDenies.d.ts +28 -0
  8. package/dist/agents/opencode.d.ts +231 -0
  9. package/dist/agents/opencodePlugin.d.ts +85 -0
  10. package/dist/agents/opencodeShared.d.ts +40 -0
  11. package/dist/agents/postRun.d.ts +132 -0
  12. package/dist/agents/reviewer.d.ts +38 -0
  13. package/dist/agents/sessionLabeler.d.ts +97 -0
  14. package/dist/agents/shared.d.ts +189 -0
  15. package/dist/agents/subagentModels.d.ts +19 -0
  16. package/dist/agents/subagentToolGates.d.ts +55 -0
  17. package/dist/cli.mjs +197426 -0
  18. package/dist/external.d.ts +227 -0
  19. package/dist/index.d.ts +6 -0
  20. package/dist/index.js +196783 -0
  21. package/dist/internal/index.d.ts +18 -0
  22. package/dist/internal.js +1714 -0
  23. package/dist/lifecycle.d.ts +2 -0
  24. package/dist/main.d.ts +8 -0
  25. package/dist/mcp/arkConfig.d.ts +1 -0
  26. package/dist/mcp/checkSuite.d.ts +25 -0
  27. package/dist/mcp/checkout.d.ts +77 -0
  28. package/dist/mcp/comment.d.ts +119 -0
  29. package/dist/mcp/commitInfo.d.ts +9 -0
  30. package/dist/mcp/crosswalk.d.ts +105 -0
  31. package/dist/mcp/dependencies.d.ts +8 -0
  32. package/dist/mcp/geminiSanitizer.d.ts +28 -0
  33. package/dist/mcp/git.d.ts +46 -0
  34. package/dist/mcp/guardrails.d.ts +104 -0
  35. package/dist/mcp/issue.d.ts +18 -0
  36. package/dist/mcp/issueComments.d.ts +9 -0
  37. package/dist/mcp/issueEvents.d.ts +9 -0
  38. package/dist/mcp/issueInfo.d.ts +9 -0
  39. package/dist/mcp/labels.d.ts +12 -0
  40. package/dist/mcp/localContext.d.ts +19 -0
  41. package/dist/mcp/moduleExtraction.d.ts +71 -0
  42. package/dist/mcp/moduleTests.d.ts +104 -0
  43. package/dist/mcp/modules.d.ts +179 -0
  44. package/dist/mcp/output.d.ts +12 -0
  45. package/dist/mcp/pathSafety.d.ts +14 -0
  46. package/dist/mcp/policy.d.ts +48 -0
  47. package/dist/mcp/pr.d.ts +49 -0
  48. package/dist/mcp/prInfo.d.ts +9 -0
  49. package/dist/mcp/providerSchema.d.ts +50 -0
  50. package/dist/mcp/review.d.ts +199 -0
  51. package/dist/mcp/reviewComments.d.ts +178 -0
  52. package/dist/mcp/roots.d.ts +58 -0
  53. package/dist/mcp/scope.d.ts +15 -0
  54. package/dist/mcp/selectMode.d.ts +18 -0
  55. package/dist/mcp/server.d.ts +48 -0
  56. package/dist/mcp/shared.d.ts +47 -0
  57. package/dist/mcp/shell.d.ts +37 -0
  58. package/dist/mcp/staleFix.d.ts +51 -0
  59. package/dist/mcp/terraform/cost.d.ts +55 -0
  60. package/dist/mcp/terraform/currency.d.ts +94 -0
  61. package/dist/mcp/terraform/decisions.d.ts +178 -0
  62. package/dist/mcp/terraform/findings.d.ts +75 -0
  63. package/dist/mcp/terraform/plan.d.ts +157 -0
  64. package/dist/mcp/terraform/scanners.d.ts +131 -0
  65. package/dist/mcp/terraform/tools.d.ts +63 -0
  66. package/dist/mcp/terraform/types.d.ts +172 -0
  67. package/dist/mcp/terraform.d.ts +22 -0
  68. package/dist/mcp/terratest.d.ts +83 -0
  69. package/dist/mcp/upload.d.ts +6 -0
  70. package/dist/models.d.ts +171 -0
  71. package/dist/modes.d.ts +26 -0
  72. package/dist/prep/index.d.ts +7 -0
  73. package/dist/prep/installNodeDependencies.d.ts +2 -0
  74. package/dist/prep/installPythonDependencies.d.ts +2 -0
  75. package/dist/prep/types.d.ts +31 -0
  76. package/dist/reviewQuality.d.ts +64 -0
  77. package/dist/skills/terraform-best-practices/SKILL.md +369 -0
  78. package/dist/toolState.d.ts +135 -0
  79. package/dist/utils/activity.d.ts +40 -0
  80. package/dist/utils/agent.d.ts +20 -0
  81. package/dist/utils/agentHangReport.d.ts +38 -0
  82. package/dist/utils/apiFetch.d.ts +19 -0
  83. package/dist/utils/apiKeys.d.ts +41 -0
  84. package/dist/utils/apiUrl.d.ts +20 -0
  85. package/dist/utils/assets.d.ts +8 -0
  86. package/dist/utils/billingErrors.d.ts +85 -0
  87. package/dist/utils/body.d.ts +34 -0
  88. package/dist/utils/buildTerramendFooter.d.ts +25 -0
  89. package/dist/utils/byokFallback.d.ts +85 -0
  90. package/dist/utils/claudeSubscription.d.ts +30 -0
  91. package/dist/utils/cli.d.ts +10 -0
  92. package/dist/utils/codexHome.d.ts +29 -0
  93. package/dist/utils/codexOAuth.d.ts +60 -0
  94. package/dist/utils/diffCoverage.d.ts +63 -0
  95. package/dist/utils/errorReport.d.ts +17 -0
  96. package/dist/utils/exitHandler.d.ts +8 -0
  97. package/dist/utils/fixDoubleEscapedString.d.ts +1 -0
  98. package/dist/utils/gitAuth.d.ts +84 -0
  99. package/dist/utils/gitAuthServer.d.ts +24 -0
  100. package/dist/utils/github.d.ts +78 -0
  101. package/dist/utils/globals.d.ts +3 -0
  102. package/dist/utils/install.d.ts +60 -0
  103. package/dist/utils/instructions.d.ts +48 -0
  104. package/dist/utils/leapingComment.d.ts +11 -0
  105. package/dist/utils/learnings.d.ts +62 -0
  106. package/dist/utils/learningsTruncate.d.ts +25 -0
  107. package/dist/utils/lifecycle.d.ts +57 -0
  108. package/dist/utils/log.d.ts +111 -0
  109. package/dist/utils/normalizeEnv.d.ts +30 -0
  110. package/dist/utils/openCodeModels.d.ts +11 -0
  111. package/dist/utils/overrides.d.ts +40 -0
  112. package/dist/utils/packageManager.d.ts +49 -0
  113. package/dist/utils/patchWorkflowRunFields.d.ts +29 -0
  114. package/dist/utils/payload.d.ts +105 -0
  115. package/dist/utils/prSummary.d.ts +61 -0
  116. package/dist/utils/progressComment.d.ts +146 -0
  117. package/dist/utils/providerErrors.d.ts +31 -0
  118. package/dist/utils/rangeDiff.d.ts +51 -0
  119. package/dist/utils/remediationCommand.d.ts +55 -0
  120. package/dist/utils/retry.d.ts +13 -0
  121. package/dist/utils/reviewCleanup.d.ts +14 -0
  122. package/dist/utils/run.d.ts +9 -0
  123. package/dist/utils/runContext.d.ts +60 -0
  124. package/dist/utils/runContextData.d.ts +23 -0
  125. package/dist/utils/runErrorRenderer.d.ts +64 -0
  126. package/dist/utils/runLifecycle.d.ts +86 -0
  127. package/dist/utils/runStartupLog.d.ts +15 -0
  128. package/dist/utils/secrets.d.ts +22 -0
  129. package/dist/utils/setup.d.ts +90 -0
  130. package/dist/utils/shell.d.ts +32 -0
  131. package/dist/utils/skills.d.ts +10 -0
  132. package/dist/utils/subprocess.d.ts +80 -0
  133. package/dist/utils/terraformMcp.d.ts +42 -0
  134. package/dist/utils/time.d.ts +15 -0
  135. package/dist/utils/timer.d.ts +23 -0
  136. package/dist/utils/todoTracking.d.ts +16 -0
  137. package/dist/utils/token.d.ts +39 -0
  138. package/dist/utils/version.d.ts +2 -0
  139. package/dist/utils/versioning.d.ts +7 -0
  140. package/dist/utils/vertex.d.ts +16 -0
  141. package/dist/utils/workflow.d.ts +13 -0
  142. package/package.json +119 -0
  143. package/src/agents/claude.test.ts +1016 -0
  144. package/src/agents/claude.ts +1246 -0
  145. package/src/agents/claudePretoolGate.test.ts +28 -0
  146. package/src/agents/claudePretoolGate.ts +173 -0
  147. package/src/agents/gateServer.test.ts +204 -0
  148. package/src/agents/gateServer.ts +124 -0
  149. package/src/agents/index.ts +10 -0
  150. package/src/agents/nativeFsDenies.ts +82 -0
  151. package/src/agents/opencode.test.ts +1440 -0
  152. package/src/agents/opencode.ts +1312 -0
  153. package/src/agents/opencodePlugin.ts +222 -0
  154. package/src/agents/opencodeShared.test.ts +34 -0
  155. package/src/agents/opencodeShared.ts +121 -0
  156. package/src/agents/postRun.test.ts +549 -0
  157. package/src/agents/postRun.ts +535 -0
  158. package/src/agents/reviewer.ts +104 -0
  159. package/src/agents/sessionLabeler.test.ts +247 -0
  160. package/src/agents/sessionLabeler.ts +178 -0
  161. package/src/agents/shared.test.ts +76 -0
  162. package/src/agents/shared.ts +292 -0
  163. package/src/agents/subagentModels.test.ts +113 -0
  164. package/src/agents/subagentModels.ts +40 -0
  165. package/src/agents/subagentRegistration.test.ts +41 -0
  166. package/src/agents/subagentToolGates.ts +114 -0
  167. package/src/cli.test.ts +129 -0
  168. package/src/cli.ts +105 -0
  169. package/src/commands/gha.test.ts +192 -0
  170. package/src/commands/gha.ts +188 -0
  171. package/src/commands/mcp.ts +122 -0
  172. package/src/config.ts +1 -0
  173. package/src/entry.ts +7 -0
  174. package/src/entryPost.stdlibOnly.test.ts +109 -0
  175. package/src/entryPost.ts +99 -0
  176. package/src/external.test.ts +16 -0
  177. package/src/external.ts +302 -0
  178. package/src/index.ts +11 -0
  179. package/src/internal/index.ts +71 -0
  180. package/src/lifecycle.ts +2 -0
  181. package/src/main.test.ts +873 -0
  182. package/src/main.ts +712 -0
  183. package/src/mcp/__fixtures__/terramend-scratch-pr-49-review-3485940013.json +110 -0
  184. package/src/mcp/__fixtures__/terramend-scratch-pr-64-review-3531000326.json +14 -0
  185. package/src/mcp/__fixtures__/terramend-test-repo-pr-1.diff.json +67 -0
  186. package/src/mcp/__snapshots__/checkout.test.ts.snap +109 -0
  187. package/src/mcp/__snapshots__/reviewComments.test.ts.snap +71 -0
  188. package/src/mcp/arkConfig.ts +7 -0
  189. package/src/mcp/checkSuite.test.ts +245 -0
  190. package/src/mcp/checkSuite.ts +255 -0
  191. package/src/mcp/checkout.test.ts +752 -0
  192. package/src/mcp/checkout.ts +886 -0
  193. package/src/mcp/comment.test.ts +772 -0
  194. package/src/mcp/comment.ts +582 -0
  195. package/src/mcp/commitInfo.test.ts +127 -0
  196. package/src/mcp/commitInfo.ts +61 -0
  197. package/src/mcp/crosswalk.test.ts +106 -0
  198. package/src/mcp/crosswalk.ts +339 -0
  199. package/src/mcp/dependencies.test.ts +309 -0
  200. package/src/mcp/dependencies.ts +189 -0
  201. package/src/mcp/geminiSanitizer.test.ts +287 -0
  202. package/src/mcp/geminiSanitizer.ts +207 -0
  203. package/src/mcp/git.test.ts +1083 -0
  204. package/src/mcp/git.ts +890 -0
  205. package/src/mcp/guardrails.test.ts +705 -0
  206. package/src/mcp/guardrails.ts +465 -0
  207. package/src/mcp/issue.test.ts +113 -0
  208. package/src/mcp/issue.ts +73 -0
  209. package/src/mcp/issueComments.test.ts +69 -0
  210. package/src/mcp/issueComments.ts +48 -0
  211. package/src/mcp/issueEvents.test.ts +134 -0
  212. package/src/mcp/issueEvents.ts +100 -0
  213. package/src/mcp/issueInfo.test.ts +104 -0
  214. package/src/mcp/issueInfo.ts +72 -0
  215. package/src/mcp/labels.test.ts +52 -0
  216. package/src/mcp/labels.ts +34 -0
  217. package/src/mcp/localContext.ts +28 -0
  218. package/src/mcp/localServer.test.ts +75 -0
  219. package/src/mcp/localServer.ts +131 -0
  220. package/src/mcp/moduleExtraction.test.ts +261 -0
  221. package/src/mcp/moduleExtraction.ts +313 -0
  222. package/src/mcp/moduleTests.test.ts +269 -0
  223. package/src/mcp/moduleTests.ts +421 -0
  224. package/src/mcp/modules.test.ts +640 -0
  225. package/src/mcp/modules.ts +696 -0
  226. package/src/mcp/output.test.ts +96 -0
  227. package/src/mcp/output.ts +70 -0
  228. package/src/mcp/pathSafety.test.ts +44 -0
  229. package/src/mcp/pathSafety.ts +28 -0
  230. package/src/mcp/policy.test.ts +282 -0
  231. package/src/mcp/policy.ts +199 -0
  232. package/src/mcp/pr.test.ts +387 -0
  233. package/src/mcp/pr.ts +194 -0
  234. package/src/mcp/prInfo.test.ts +96 -0
  235. package/src/mcp/prInfo.ts +91 -0
  236. package/src/mcp/providerSchema.test.ts +85 -0
  237. package/src/mcp/providerSchema.ts +175 -0
  238. package/src/mcp/review.test.ts +936 -0
  239. package/src/mcp/review.ts +923 -0
  240. package/src/mcp/reviewComments.test.ts +549 -0
  241. package/src/mcp/reviewComments.ts +896 -0
  242. package/src/mcp/roots.test.ts +175 -0
  243. package/src/mcp/roots.ts +217 -0
  244. package/src/mcp/scope.test.ts +59 -0
  245. package/src/mcp/scope.ts +65 -0
  246. package/src/mcp/security.test.ts +720 -0
  247. package/src/mcp/selectMode.test.ts +210 -0
  248. package/src/mcp/selectMode.ts +181 -0
  249. package/src/mcp/server.test.ts +292 -0
  250. package/src/mcp/server.ts +403 -0
  251. package/src/mcp/shared.ts +100 -0
  252. package/src/mcp/shell.test.ts +520 -0
  253. package/src/mcp/shell.ts +505 -0
  254. package/src/mcp/staleFix.test.ts +237 -0
  255. package/src/mcp/staleFix.ts +277 -0
  256. package/src/mcp/terraform/cost.ts +163 -0
  257. package/src/mcp/terraform/currency.test.ts +338 -0
  258. package/src/mcp/terraform/currency.ts +336 -0
  259. package/src/mcp/terraform/decisions.ts +527 -0
  260. package/src/mcp/terraform/findings.ts +333 -0
  261. package/src/mcp/terraform/plan.ts +348 -0
  262. package/src/mcp/terraform/scanners.ts +809 -0
  263. package/src/mcp/terraform/tools.test.ts +1071 -0
  264. package/src/mcp/terraform/tools.ts +908 -0
  265. package/src/mcp/terraform/types.ts +305 -0
  266. package/src/mcp/terraform.test.ts +1957 -0
  267. package/src/mcp/terraform.ts +23 -0
  268. package/src/mcp/terratest.test.ts +105 -0
  269. package/src/mcp/terratest.ts +196 -0
  270. package/src/mcp/toolFiltering.test.ts +85 -0
  271. package/src/mcp/upload.test.ts +180 -0
  272. package/src/mcp/upload.ts +112 -0
  273. package/src/models.test.ts +300 -0
  274. package/src/models.ts +708 -0
  275. package/src/modes.test.ts +107 -0
  276. package/src/modes.ts +880 -0
  277. package/src/prep/index.ts +43 -0
  278. package/src/prep/installNodeDependencies.test.ts +298 -0
  279. package/src/prep/installNodeDependencies.ts +196 -0
  280. package/src/prep/installPythonDependencies.test.ts +268 -0
  281. package/src/prep/installPythonDependencies.ts +199 -0
  282. package/src/prep/types.ts +38 -0
  283. package/src/reviewQuality.test.ts +63 -0
  284. package/src/reviewQuality.ts +134 -0
  285. package/src/runCli.test.ts +214 -0
  286. package/src/runCli.ts +282 -0
  287. package/src/skills/terraform-best-practices/SKILL.md +369 -0
  288. package/src/toolState.test.ts +45 -0
  289. package/src/toolState.ts +252 -0
  290. package/src/utils/activity.test.ts +188 -0
  291. package/src/utils/activity.ts +210 -0
  292. package/src/utils/agent.test.ts +251 -0
  293. package/src/utils/agent.ts +139 -0
  294. package/src/utils/agentHangReport.test.ts +203 -0
  295. package/src/utils/agentHangReport.ts +170 -0
  296. package/src/utils/apiFetch.test.ts +115 -0
  297. package/src/utils/apiFetch.ts +62 -0
  298. package/src/utils/apiKeys.test.ts +344 -0
  299. package/src/utils/apiKeys.ts +206 -0
  300. package/src/utils/apiUrl.test.ts +30 -0
  301. package/src/utils/apiUrl.ts +59 -0
  302. package/src/utils/assets.test.ts +153 -0
  303. package/src/utils/assets.ts +107 -0
  304. package/src/utils/billingErrors.test.ts +121 -0
  305. package/src/utils/billingErrors.ts +189 -0
  306. package/src/utils/body.test.ts +217 -0
  307. package/src/utils/body.ts +168 -0
  308. package/src/utils/buildTerramendFooter.test.ts +38 -0
  309. package/src/utils/buildTerramendFooter.ts +82 -0
  310. package/src/utils/byokFallback.test.ts +205 -0
  311. package/src/utils/byokFallback.ts +128 -0
  312. package/src/utils/claudeSubscription.test.ts +179 -0
  313. package/src/utils/claudeSubscription.ts +93 -0
  314. package/src/utils/cli.ts +31 -0
  315. package/src/utils/codexHome.test.ts +190 -0
  316. package/src/utils/codexHome.ts +191 -0
  317. package/src/utils/codexOAuth.ts +147 -0
  318. package/src/utils/codexRefreshDetect.test.ts +85 -0
  319. package/src/utils/codexRefreshDetect.ts +35 -0
  320. package/src/utils/diffCoverage.test.ts +468 -0
  321. package/src/utils/diffCoverage.ts +404 -0
  322. package/src/utils/errorReport.test.ts +135 -0
  323. package/src/utils/errorReport.ts +83 -0
  324. package/src/utils/exitHandler.ts +35 -0
  325. package/src/utils/fixDoubleEscapedString.ts +9 -0
  326. package/src/utils/ghaCore.ts +13 -0
  327. package/src/utils/gitAuth.test.ts +322 -0
  328. package/src/utils/gitAuth.ts +263 -0
  329. package/src/utils/gitAuthServer.test.ts +260 -0
  330. package/src/utils/gitAuthServer.ts +182 -0
  331. package/src/utils/github.test.ts +615 -0
  332. package/src/utils/github.ts +538 -0
  333. package/src/utils/globals.ts +9 -0
  334. package/src/utils/humanEditCapture.test.ts +100 -0
  335. package/src/utils/humanEditCapture.ts +193 -0
  336. package/src/utils/install.test.ts +768 -0
  337. package/src/utils/install.ts +492 -0
  338. package/src/utils/instructions.test.ts +240 -0
  339. package/src/utils/instructions.ts +543 -0
  340. package/src/utils/leapingComment.test.ts +51 -0
  341. package/src/utils/leapingComment.ts +18 -0
  342. package/src/utils/learnings.test.ts +87 -0
  343. package/src/utils/learnings.ts +138 -0
  344. package/src/utils/learningsTocRender.test.ts +116 -0
  345. package/src/utils/learningsTruncate.test.ts +39 -0
  346. package/src/utils/learningsTruncate.ts +42 -0
  347. package/src/utils/lifecycle.test.ts +195 -0
  348. package/src/utils/lifecycle.ts +198 -0
  349. package/src/utils/log.test.ts +402 -0
  350. package/src/utils/log.ts +432 -0
  351. package/src/utils/normalizeEnv.test.ts +91 -0
  352. package/src/utils/normalizeEnv.ts +106 -0
  353. package/src/utils/openCodeModels.ts +82 -0
  354. package/src/utils/overrides.test.ts +89 -0
  355. package/src/utils/overrides.ts +98 -0
  356. package/src/utils/packageManager.test.ts +321 -0
  357. package/src/utils/packageManager.ts +257 -0
  358. package/src/utils/patchWorkflowRunFields.test.ts +92 -0
  359. package/src/utils/patchWorkflowRunFields.ts +150 -0
  360. package/src/utils/payload.test.ts +497 -0
  361. package/src/utils/payload.ts +371 -0
  362. package/src/utils/postApiFetch.ts +51 -0
  363. package/src/utils/prSummary.test.ts +224 -0
  364. package/src/utils/prSummary.ts +147 -0
  365. package/src/utils/progressComment.ts +261 -0
  366. package/src/utils/providerErrors.test.ts +315 -0
  367. package/src/utils/providerErrors.ts +172 -0
  368. package/src/utils/rangeDiff.test.ts +236 -0
  369. package/src/utils/rangeDiff.ts +182 -0
  370. package/src/utils/remediationCommand.test.ts +163 -0
  371. package/src/utils/remediationCommand.ts +119 -0
  372. package/src/utils/retry.test.ts +153 -0
  373. package/src/utils/retry.ts +58 -0
  374. package/src/utils/reviewCleanup.ts +106 -0
  375. package/src/utils/run.ts +99 -0
  376. package/src/utils/runContext.ts +145 -0
  377. package/src/utils/runContextData.ts +58 -0
  378. package/src/utils/runErrorRenderer.test.ts +95 -0
  379. package/src/utils/runErrorRenderer.ts +259 -0
  380. package/src/utils/runFixture.ts +76 -0
  381. package/src/utils/runLifecycle.ts +237 -0
  382. package/src/utils/runStartupLog.ts +60 -0
  383. package/src/utils/secrets.test.ts +103 -0
  384. package/src/utils/secrets.ts +177 -0
  385. package/src/utils/setup.test.ts +509 -0
  386. package/src/utils/setup.ts +352 -0
  387. package/src/utils/shell.ts +103 -0
  388. package/src/utils/skills.test.ts +46 -0
  389. package/src/utils/skills.ts +67 -0
  390. package/src/utils/subprocess.test.ts +170 -0
  391. package/src/utils/subprocess.ts +438 -0
  392. package/src/utils/terraformMcp.test.ts +63 -0
  393. package/src/utils/terraformMcp.ts +83 -0
  394. package/src/utils/time.test.ts +105 -0
  395. package/src/utils/time.ts +59 -0
  396. package/src/utils/timer.test.ts +91 -0
  397. package/src/utils/timer.ts +72 -0
  398. package/src/utils/todoTracking.test.ts +223 -0
  399. package/src/utils/todoTracking.ts +167 -0
  400. package/src/utils/token.test.ts +239 -0
  401. package/src/utils/token.ts +186 -0
  402. package/src/utils/version.ts +10 -0
  403. package/src/utils/versioning.test.ts +34 -0
  404. package/src/utils/versioning.ts +44 -0
  405. package/src/utils/vertex.ts +85 -0
  406. package/src/utils/workflow.ts +25 -0
@@ -0,0 +1,110 @@
1
+ {
2
+ "owner": "terramend",
3
+ "name": "scratch",
4
+ "pullNumber": 49,
5
+ "reviewId": 3485940013,
6
+ "review": {
7
+ "body": "### This is the final PR Bugbot will review for you during this billing cycle\n\nYour free Bugbot reviews will reset on November 30\n\n<details>\n<summary>Details</summary>\n\nYour team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.\n\nTo receive Bugbot reviews on all of your PRs, visit the [Cursor dashboard](https://www.cursor.com/dashboard?tab=bugbot) to activate Pro and start your 14-day free trial.\n</details>\n\n",
8
+ "user": {
9
+ "login": "cursor[bot]"
10
+ }
11
+ },
12
+ "threads": [
13
+ {
14
+ "id": "PRRT_kwDOPaxxp85iysVl",
15
+ "path": ".github/workflows/test.yml",
16
+ "line": null,
17
+ "startLine": null,
18
+ "diffSide": "RIGHT",
19
+ "isResolved": true,
20
+ "isOutdated": true,
21
+ "comments": {
22
+ "nodes": [
23
+ {
24
+ "fullDatabaseId": "2544544046",
25
+ "body": "### Bug: GitHub Actions workflow triggered for wrong branch\n\n<!-- **High Severity** -->\n\n<!-- DESCRIPTION START -->\nThe `pull_request` trigger specifies `branches: [mainc]`, but the `push` trigger specifies `branches: [main]`. This mismatch means pull requests will only trigger tests if targeting a non-existent `mainc` branch rather than the actual `main` development branch, preventing CI from running on most pull requests.\n<!-- DESCRIPTION END -->\n\n<!-- LOCATIONS START\n.github/workflows/test.yml#L6-L7\nLOCATIONS END -->\n<a href=\"https://cursor.com/open?data=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImJ1Z2JvdC12MSJ9.eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9DVVJTT1IiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90OjllMTgyY2U2LWY0YWMtNDAwNS1hMzQ4LWIyYzJkZTk4OGM1ZSIsImVuY3J5cHRpb25LZXkiOiJmSW93NEdsUGUwYlYtd3M2UC1UNHdHT1JmMGZjakxfWVZEdC00SWNveXo0IiwiYnJhbmNoIjoiZGl2aWRlIn0sImlhdCI6MTc2MzYyMDgxOSwiZXhwIjoxNzY0MjI1NjE5fQ.BjkWsTqiNriojI5v10JcveUY2M50f9eflTNDgWAdjdW9w7E0EEY4GJfyzBrA72neco3qAlc34WipASNuEQbTD1fZvwtJY-TeNTDzoKmwA6gtwICB8t7qT87GPvcbDrdGGWdC8kW1jf-LntTmD0k7gt0AeENRAdRSiD3dbqYFN0huXHaB8f2Y48mpmLcnnUpoaaZe7By-Y0DnILyHppwx3AH75nKE_ZeAee3rQNGX4cwcHgB5emTSM93pMDQhT1vbIRYHMaFkOaW2-kDOA8H2QqxD4mT8VzY3skvxIo5HNZCvqE84NtEygHqkBv88g2EEijOPAAeskfsdp087yIzV9g\"><picture><source media=\"(prefers-color-scheme: dark)\" srcset=\"https://cursor.com/fix-in-cursor-dark.svg\"><source media=\"(prefers-color-scheme: light)\" srcset=\"https://cursor.com/fix-in-cursor-light.svg\"><img alt=\"Fix in Cursor\" src=\"https://cursor.com/fix-in-cursor.svg\"></picture></a>&nbsp;<a href=\"https://cursor.com/agents?data=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImJ1Z2JvdC12MSJ9.eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9XRUIiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90OjllMTgyY2U2LWY0YWMtNDAwNS1hMzQ4LWIyYzJkZTk4OGM1ZSIsImVuY3J5cHRpb25LZXkiOiJmSW93NEdsUGUwYlYtd3M2UC1UNHdHT1JmMGZjakxfWVZEdC00SWNveXo0IiwiYnJhbmNoIjoiZGl2aWRlIiwicmVwb093bmVyIjoicHVsbGZyb2dhaSIsInJlcG9OYW1lIjoic2NyYXRjaCIsInByTnVtYmVyIjo0OSwiY29tbWl0U2hhIjoiNThiOGJmNmQ1MWE1Mjg4OGFjNGFkNzA5YWVmYTk2MWFkZDMyNDBiMSJ9LCJpYXQiOjE3NjM2MjA4MTksImV4cCI6MTc2NDIyNTYxOX0.SFDZe8R9uwhPjS55J4i_mV2ybsSZoQYM6YzdUOava4IKy1IK2OrVkVsG3-8p4rRaMBXdDZZ4ObPbtk70KqdAiLEDKBqaFcWqELc49lr0XRKUmu4F6EhESFQOvt7MLSVDIOgee8YRlhS6xtoPDqsRiV2KGOwyLEdCeYdrYz9i1DanIswWSoMRVvkjxZ6GUBYVAUg_JsgAXoKVJ-L9Q5Ygho6acVAr5NlGeBp2f6g49GX4GfDOPeV3SORQS1CjxQVRbjI-g0rW55NIisBEl8279VwG6-dTISNbyasZOB6R3eEmC4vmyAAGJjUsMwqhMPw1oaMMmYNSbtZLDESxME9IUg\"><picture><source media=\"(prefers-color-scheme: dark)\" srcset=\"https://cursor.com/fix-in-web-dark.svg\"><source media=\"(prefers-color-scheme: light)\" srcset=\"https://cursor.com/fix-in-web-light.svg\"><img alt=\"Fix in Web\" src=\"https://cursor.com/fix-in-web.svg\"></picture></a>\n\n",
26
+ "createdAt": "2025-11-20T06:40:19Z",
27
+ "diffHunk": "@@ -0,0 +1,36 @@\n+name: Test\n+\n+on:\n+ push:\n+ branches: [main]\n+ pull_request:\n+ branches: [mainc]",
28
+ "line": null,
29
+ "startLine": null,
30
+ "originalLine": 7,
31
+ "originalStartLine": null,
32
+ "author": {
33
+ "login": "cursor"
34
+ },
35
+ "pullRequestReview": {
36
+ "databaseId": 3485940013,
37
+ "author": {
38
+ "login": "cursor"
39
+ }
40
+ },
41
+ "reactionGroups": [
42
+ {
43
+ "content": "THUMBS_UP",
44
+ "reactors": {
45
+ "nodes": []
46
+ }
47
+ },
48
+ {
49
+ "content": "THUMBS_DOWN",
50
+ "reactors": {
51
+ "nodes": []
52
+ }
53
+ },
54
+ {
55
+ "content": "LAUGH",
56
+ "reactors": {
57
+ "nodes": []
58
+ }
59
+ },
60
+ {
61
+ "content": "HOORAY",
62
+ "reactors": {
63
+ "nodes": []
64
+ }
65
+ },
66
+ {
67
+ "content": "CONFUSED",
68
+ "reactors": {
69
+ "nodes": []
70
+ }
71
+ },
72
+ {
73
+ "content": "HEART",
74
+ "reactors": {
75
+ "nodes": []
76
+ }
77
+ },
78
+ {
79
+ "content": "ROCKET",
80
+ "reactors": {
81
+ "nodes": []
82
+ }
83
+ },
84
+ {
85
+ "content": "EYES",
86
+ "reactors": {
87
+ "nodes": []
88
+ }
89
+ }
90
+ ]
91
+ }
92
+ ]
93
+ }
94
+ }
95
+ ],
96
+ "prFiles": [
97
+ {
98
+ "filename": ".github/workflows/test.yml",
99
+ "patch": "@@ -0,0 +1,36 @@\n+name: Test\n+\n+on:\n+ push:\n+ branches: [main]\n+ pull_request:\n+ branches: [main]\n+\n+jobs:\n+ test:\n+ runs-on: ubuntu-latest\n+\n+ strategy:\n+ matrix:\n+ node-version: [22.x]\n+\n+ steps:\n+ - name: Checkout code\n+ uses: actions/checkout@v4\n+\n+ - name: Setup pnpm\n+ uses: pnpm/action-setup@v2\n+ with:\n+ version: 8\n+\n+ - name: Setup Node.js ${{ matrix.node-version }}\n+ uses: actions/setup-node@v4\n+ with:\n+ node-version: ${{ matrix.node-version }}\n+ cache: 'pnpm'\n+\n+ - name: Install dependencies\n+ run: pnpm install\n+\n+ - name: Run tests\n+ run: pnpm test"
100
+ },
101
+ {
102
+ "filename": "index.test.ts",
103
+ "patch": "@@ -1,5 +1,5 @@\n import { describe, it, expect } from 'vitest'\n-import { add } from './index.js'\n+import { add, multiply, subtract, divide } from './index.js'\n \n describe('add function', () => {\n it('should add two positive numbers correctly', () => {\n@@ -25,3 +25,51 @@ describe('add function', () => {\n expect(add(0.1, 0.2)).toBeCloseTo(0.3)\n })\n })\n+\n+describe('multiply function', () => {\n+ it('should multiply two positive numbers correctly', () => {\n+ expect(multiply(3, 4)).toBe(12)\n+ })\n+\n+ it('should multiply negative numbers correctly', () => {\n+ expect(multiply(-2, 3)).toBe(-6)\n+ expect(multiply(-2, -3)).toBe(6)\n+ })\n+\n+ it('should handle zero correctly', () => {\n+ expect(multiply(5, 0)).toBe(0)\n+ expect(multiply(0, 5)).toBe(0)\n+ })\n+})\n+\n+describe('subtract function', () => {\n+ it('should subtract two positive numbers correctly', () => {\n+ expect(subtract(10, 3)).toBe(7)\n+ })\n+\n+ it('should handle negative numbers correctly', () => {\n+ expect(subtract(5, -3)).toBe(8)\n+ expect(subtract(-5, 3)).toBe(-8)\n+ })\n+\n+ it('should handle zero correctly', () => {\n+ expect(subtract(5, 0)).toBe(5)\n+ expect(subtract(0, 5)).toBe(-5)\n+ })\n+})\n+\n+describe('divide function', () => {\n+ it('should divide two positive numbers correctly', () => {\n+ expect(divide(10, 2)).toBe(5)\n+ })\n+\n+ it('should handle negative numbers correctly', () => {\n+ expect(divide(-10, 2)).toBe(-5)\n+ expect(divide(10, -2)).toBe(-5)\n+ })\n+\n+ it('should handle decimal results correctly', () => {\n+ expect(divide(10, 3)).toBeCloseTo(3.333, 2)\n+ expect(divide(7, 2)).toBe(3.5)\n+ })\n+})"
104
+ },
105
+ {
106
+ "filename": "index.ts",
107
+ "patch": "@@ -3,11 +3,13 @@ export function add(a: number, b: number) {\n }\n \n export function multiply(a: number, b: number) {\n- // Bug: accidentally adding 1 to the result\n- return a * b + 1;\n+ return a * b;\n }\n \n export function subtract(a: number, b: number) {\n- // Bug: accidentally adding instead of subtracting\n- return a + b;\n+ return a - b;\n+}\n+\n+export function divide(a: number, b: number) {\n+ return a / b;\n }"
108
+ }
109
+ ]
110
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "owner": "terramend",
3
+ "name": "scratch",
4
+ "pullNumber": 64,
5
+ "reviewId": 3531000326,
6
+ "review": {
7
+ "body": "This PR looks great. The retry logic is well-implemented and the tests are comprehensive.",
8
+ "user": {
9
+ "login": "terramend[bot]"
10
+ }
11
+ },
12
+ "threads": [],
13
+ "prFiles": []
14
+ }
@@ -0,0 +1,67 @@
1
+ {
2
+ "owner": "terramend",
3
+ "name": "test-repo",
4
+ "pullNumber": 1,
5
+ "files": [
6
+ {
7
+ "sha": "a2d9c355792f1883c26d43d219db006b05781e4c",
8
+ "filename": "src/format.ts",
9
+ "status": "modified",
10
+ "additions": 12,
11
+ "deletions": 2,
12
+ "changes": 14,
13
+ "blob_url": "https://github.com/terramend/test-repo/blob/0311c0fb58fc7faa46e51c174394a4468f379681/src%2Fformat.ts",
14
+ "raw_url": "https://github.com/terramend/test-repo/raw/0311c0fb58fc7faa46e51c174394a4468f379681/src%2Fformat.ts",
15
+ "contents_url": "https://api.github.com/repos/terramend/test-repo/contents/src%2Fformat.ts?ref=0311c0fb58fc7faa46e51c174394a4468f379681",
16
+ "patch": "@@ -1,7 +1,17 @@\n-export function formatCurrency(amount: number) {\n- return `$${amount.toFixed(2)}`;\n+export function formatCurrency(amount: number, currency = \"USD\") {\n+ return new Intl.NumberFormat(\"en-US\", {\n+ style: \"currency\",\n+ currency,\n+ }).format(amount);\n }\n \n export function formatPercent(value: number) {\n return `${(value * 100).toFixed(1)}%`;\n }\n+\n+export function formatNumber(value: number, decimals = 2) {\n+ return new Intl.NumberFormat(\"en-US\", {\n+ minimumFractionDigits: decimals,\n+ maximumFractionDigits: decimals,\n+ }).format(value);\n+}"
17
+ },
18
+ {
19
+ "sha": "0786b9ce6870e65c644673745266e87eef057ce4",
20
+ "filename": "src/math.ts",
21
+ "status": "modified",
22
+ "additions": 5,
23
+ "deletions": 2,
24
+ "changes": 7,
25
+ "blob_url": "https://github.com/terramend/test-repo/blob/0311c0fb58fc7faa46e51c174394a4468f379681/src%2Fmath.ts",
26
+ "raw_url": "https://github.com/terramend/test-repo/raw/0311c0fb58fc7faa46e51c174394a4468f379681/src%2Fmath.ts",
27
+ "contents_url": "https://api.github.com/repos/terramend/test-repo/contents/src%2Fmath.ts?ref=0311c0fb58fc7faa46e51c174394a4468f379681",
28
+ "patch": "@@ -3,13 +3,16 @@ export function add(a: number, b: number) {\n }\n \n export function subtract(a: number, b: number) {\n- return a + b; // bug: should be a - b\n+ return a - b;\n }\n \n export function multiply(a: number, b: number) {\n- return a * b + 1; // bug: off by one\n+ return a * b;\n }\n \n export function divide(a: number, b: number) {\n+ if (b === 0) {\n+ throw new Error(\"division by zero\");\n+ }\n return a / b;\n }"
29
+ },
30
+ {
31
+ "sha": "cf92d8f6562c1be779506fec1049f38c9206c869",
32
+ "filename": "src/old-module.ts",
33
+ "status": "removed",
34
+ "additions": 0,
35
+ "deletions": 4,
36
+ "changes": 4,
37
+ "blob_url": "https://github.com/terramend/test-repo/blob/91ef1048326ef786fbcf95f29b3e2555506d2d54/src%2Fold-module.ts",
38
+ "raw_url": "https://github.com/terramend/test-repo/raw/91ef1048326ef786fbcf95f29b3e2555506d2d54/src%2Fold-module.ts",
39
+ "contents_url": "https://api.github.com/repos/terramend/test-repo/contents/src%2Fold-module.ts?ref=91ef1048326ef786fbcf95f29b3e2555506d2d54",
40
+ "patch": "@@ -1,4 +0,0 @@\n-// this module is deprecated and will be removed\n-export function legacyHelper() {\n- return \"old\";\n-}"
41
+ },
42
+ {
43
+ "sha": "a5bfb8a1be72e4f0816a5c4c83ee784a06559629",
44
+ "filename": "src/validate.ts",
45
+ "status": "added",
46
+ "additions": 11,
47
+ "deletions": 0,
48
+ "changes": 11,
49
+ "blob_url": "https://github.com/terramend/test-repo/blob/0311c0fb58fc7faa46e51c174394a4468f379681/src%2Fvalidate.ts",
50
+ "raw_url": "https://github.com/terramend/test-repo/raw/0311c0fb58fc7faa46e51c174394a4468f379681/src%2Fvalidate.ts",
51
+ "contents_url": "https://api.github.com/repos/terramend/test-repo/contents/src%2Fvalidate.ts?ref=0311c0fb58fc7faa46e51c174394a4468f379681",
52
+ "patch": "@@ -0,0 +1,11 @@\n+export function isPositive(n: number) {\n+ return n > 0;\n+}\n+\n+export function isInRange(value: number, min: number, max: number) {\n+ return value >= min && value <= max;\n+}\n+\n+export function isInteger(n: number) {\n+ return Number.isInteger(n);\n+}"
53
+ },
54
+ {
55
+ "sha": "5815895211d8e3355fdb77b9e216e73a248644d9",
56
+ "filename": "test/math.test.ts",
57
+ "status": "modified",
58
+ "additions": 4,
59
+ "deletions": 0,
60
+ "changes": 4,
61
+ "blob_url": "https://github.com/terramend/test-repo/blob/0311c0fb58fc7faa46e51c174394a4468f379681/test%2Fmath.test.ts",
62
+ "raw_url": "https://github.com/terramend/test-repo/raw/0311c0fb58fc7faa46e51c174394a4468f379681/test%2Fmath.test.ts",
63
+ "contents_url": "https://api.github.com/repos/terramend/test-repo/contents/test%2Fmath.test.ts?ref=0311c0fb58fc7faa46e51c174394a4468f379681",
64
+ "patch": "@@ -17,4 +17,8 @@ describe(\"math\", () => {\n it(\"divides\", () => {\n expect(divide(10, 2)).toBe(5);\n });\n+\n+ it(\"throws on division by zero\", () => {\n+ expect(() => divide(1, 0)).toThrow(\"division by zero\");\n+ });\n });"
65
+ }
66
+ ]
67
+ }
@@ -0,0 +1,109 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`formatFilesWithLineNumbers > generates accurate TOC line numbers for terramend/test-repo#1 > content 1`] = `
4
+ "## Files (5)
5
+ - src/format.ts → lines 9-32 · diff-41c7b3ac268a3a1ae5c7be92f1230f600013b7170e44a693570ccbdb183ea36b
6
+ - src/math.ts → lines 33-55 · diff-9c6e445a719b33e276684bdf95c69e617f0303638d44cf90d61295f2720ecc63
7
+ - src/old-module.ts → lines 56-64 · diff-b02fb28f45ef1227002b260c46ae6b16e080d58f65ed2a035bb58d05e2e2df5c
8
+ - src/validate.ts → lines 65-80 · diff-04b485505a31584d0a838375545a6d1f0044cd9601cd84ed98f75b42a88ea051
9
+ - test/math.test.ts → lines 81-93 · diff-44b3f515a5c787743d239052db11d740d691e8bef711c2427bb2b9752a4103a9
10
+
11
+ ---
12
+ diff --git a/src/format.ts b/src/format.ts
13
+ --- a/src/format.ts
14
+ +++ b/src/format.ts
15
+ @@ -1,7 +1,17 @@
16
+ | 1 | | - | export function formatCurrency(amount: number) {
17
+ | 2 | | - | return \`$\${amount.toFixed(2)}\`;
18
+ | | 1 | + | export function formatCurrency(amount: number, currency = "USD") {
19
+ | | 2 | + | return new Intl.NumberFormat("en-US", {
20
+ | | 3 | + | style: "currency",
21
+ | | 4 | + | currency,
22
+ | | 5 | + | }).format(amount);
23
+ | 3 | 6 | | }
24
+ | 4 | 7 | |
25
+ | 5 | 8 | | export function formatPercent(value: number) {
26
+ | 6 | 9 | | return \`\${(value * 100).toFixed(1)}%\`;
27
+ | 7 | 10 | | }
28
+ | | 11 | + |
29
+ | | 12 | + | export function formatNumber(value: number, decimals = 2) {
30
+ | | 13 | + | return new Intl.NumberFormat("en-US", {
31
+ | | 14 | + | minimumFractionDigits: decimals,
32
+ | | 15 | + | maximumFractionDigits: decimals,
33
+ | | 16 | + | }).format(value);
34
+ | | 17 | + | }
35
+
36
+ diff --git a/src/math.ts b/src/math.ts
37
+ --- a/src/math.ts
38
+ +++ b/src/math.ts
39
+ @@ -3,13 +3,16 @@ export function add(a: number, b: number) {
40
+ | 3 | 3 | | }
41
+ | 4 | 4 | |
42
+ | 5 | 5 | | export function subtract(a: number, b: number) {
43
+ | 6 | | - | return a + b; // bug: should be a - b
44
+ | | 6 | + | return a - b;
45
+ | 7 | 7 | | }
46
+ | 8 | 8 | |
47
+ | 9 | 9 | | export function multiply(a: number, b: number) {
48
+ | 10 | | - | return a * b + 1; // bug: off by one
49
+ | | 10 | + | return a * b;
50
+ | 11 | 11 | | }
51
+ | 12 | 12 | |
52
+ | 13 | 13 | | export function divide(a: number, b: number) {
53
+ | | 14 | + | if (b === 0) {
54
+ | | 15 | + | throw new Error("division by zero");
55
+ | | 16 | + | }
56
+ | 14 | 17 | | return a / b;
57
+ | 15 | 18 | | }
58
+
59
+ diff --git a/src/old-module.ts b/src/old-module.ts
60
+ --- a/src/old-module.ts
61
+ +++ b/src/old-module.ts
62
+ @@ -1,4 +0,0 @@
63
+ | 1 | | - | // this module is deprecated and will be removed
64
+ | 2 | | - | export function legacyHelper() {
65
+ | 3 | | - | return "old";
66
+ | 4 | | - | }
67
+
68
+ diff --git a/src/validate.ts b/src/validate.ts
69
+ --- a/src/validate.ts
70
+ +++ b/src/validate.ts
71
+ @@ -0,0 +1,11 @@
72
+ | | 1 | + | export function isPositive(n: number) {
73
+ | | 2 | + | return n > 0;
74
+ | | 3 | + | }
75
+ | | 4 | + |
76
+ | | 5 | + | export function isInRange(value: number, min: number, max: number) {
77
+ | | 6 | + | return value >= min && value <= max;
78
+ | | 7 | + | }
79
+ | | 8 | + |
80
+ | | 9 | + | export function isInteger(n: number) {
81
+ | | 10 | + | return Number.isInteger(n);
82
+ | | 11 | + | }
83
+
84
+ diff --git a/test/math.test.ts b/test/math.test.ts
85
+ --- a/test/math.test.ts
86
+ +++ b/test/math.test.ts
87
+ @@ -17,4 +17,8 @@ describe("math", () => {
88
+ | 17 | 17 | | it("divides", () => {
89
+ | 18 | 18 | | expect(divide(10, 2)).toBe(5);
90
+ | 19 | 19 | | });
91
+ | | 20 | + |
92
+ | | 21 | + | it("throws on division by zero", () => {
93
+ | | 22 | + | expect(() => divide(1, 0)).toThrow("division by zero");
94
+ | | 23 | + | });
95
+ | 20 | 24 | | });
96
+ "
97
+ `;
98
+
99
+ exports[`formatFilesWithLineNumbers > generates accurate TOC line numbers for terramend/test-repo#1 > toc 1`] = `
100
+ "## Files (5)
101
+ - src/format.ts → lines 9-32 · diff-41c7b3ac268a3a1ae5c7be92f1230f600013b7170e44a693570ccbdb183ea36b
102
+ - src/math.ts → lines 33-55 · diff-9c6e445a719b33e276684bdf95c69e617f0303638d44cf90d61295f2720ecc63
103
+ - src/old-module.ts → lines 56-64 · diff-b02fb28f45ef1227002b260c46ae6b16e080d58f65ed2a035bb58d05e2e2df5c
104
+ - src/validate.ts → lines 65-80 · diff-04b485505a31584d0a838375545a6d1f0044cd9601cd84ed98f75b42a88ea051
105
+ - test/math.test.ts → lines 81-93 · diff-44b3f515a5c787743d239052db11d740d691e8bef711c2427bb2b9752a4103a9
106
+
107
+ ---
108
+ "
109
+ `;
@@ -0,0 +1,71 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`formatReviewData > formats body-only review > content 1`] = `
4
+ "# Review Threads (0) for PR #64 - Review 3531000326 by terramend[bot]
5
+
6
+ ## Review Body
7
+
8
+ This PR looks great. The retry logic is well-implemented and the tests are comprehensive.
9
+
10
+ ---
11
+ "
12
+ `;
13
+
14
+ exports[`formatReviewData > formats body-only review > toc 1`] = `""`;
15
+
16
+ exports[`formatReviewData > formats thread blocks with TOC and correct line numbers > content 1`] = `
17
+ "# Review Threads (1) for PR #49 - Review 3485940013 by cursor[bot]
18
+
19
+ ## TOC
20
+
21
+ - .github/workflows/test.yml:7 → lines 25-52
22
+
23
+ ## Review Body
24
+
25
+ ### This is the final PR Bugbot will review for you during this billing cycle
26
+
27
+ Your free Bugbot reviews will reset on November 30
28
+
29
+ <details>
30
+ <summary>Details</summary>
31
+
32
+ Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.
33
+
34
+ To receive Bugbot reviews on all of your PRs, visit the [Cursor dashboard](https://www.cursor.com/dashboard?tab=bugbot) to activate Pro and start your 14-day free trial.
35
+ </details>
36
+
37
+
38
+
39
+ ---
40
+
41
+ ## .github/workflows/test.yml:7 [RESOLVED]
42
+
43
+ \`\`\`\`comment author=cursor id=2544544046 review=3485940013 thread=PRRT_kwDOPaxxp85iysVl *
44
+ ### Bug: GitHub Actions workflow triggered for wrong branch
45
+
46
+ <!-- **High Severity** -->
47
+
48
+ <!-- DESCRIPTION START -->
49
+ The \`pull_request\` trigger specifies \`branches: [mainc]\`, but the \`push\` trigger specifies \`branches: [main]\`. This mismatch means pull requests will only trigger tests if targeting a non-existent \`mainc\` branch rather than the actual \`main\` development branch, preventing CI from running on most pull requests.
50
+ <!-- DESCRIPTION END -->
51
+
52
+ <!-- LOCATIONS START
53
+ .github/workflows/test.yml#L6-L7
54
+ LOCATIONS END -->
55
+ <a href="https://cursor.com/open?data=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImJ1Z2JvdC12MSJ9.eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9DVVJTT1IiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90OjllMTgyY2U2LWY0YWMtNDAwNS1hMzQ4LWIyYzJkZTk4OGM1ZSIsImVuY3J5cHRpb25LZXkiOiJmSW93NEdsUGUwYlYtd3M2UC1UNHdHT1JmMGZjakxfWVZEdC00SWNveXo0IiwiYnJhbmNoIjoiZGl2aWRlIn0sImlhdCI6MTc2MzYyMDgxOSwiZXhwIjoxNzY0MjI1NjE5fQ.BjkWsTqiNriojI5v10JcveUY2M50f9eflTNDgWAdjdW9w7E0EEY4GJfyzBrA72neco3qAlc34WipASNuEQbTD1fZvwtJY-TeNTDzoKmwA6gtwICB8t7qT87GPvcbDrdGGWdC8kW1jf-LntTmD0k7gt0AeENRAdRSiD3dbqYFN0huXHaB8f2Y48mpmLcnnUpoaaZe7By-Y0DnILyHppwx3AH75nKE_ZeAee3rQNGX4cwcHgB5emTSM93pMDQhT1vbIRYHMaFkOaW2-kDOA8H2QqxD4mT8VzY3skvxIo5HNZCvqE84NtEygHqkBv88g2EEijOPAAeskfsdp087yIzV9g"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/fix-in-cursor-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/fix-in-cursor-light.svg"><img alt="Fix in Cursor" src="https://cursor.com/fix-in-cursor.svg"></picture></a>&nbsp;<a href="https://cursor.com/agents?data=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImJ1Z2JvdC12MSJ9.eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9XRUIiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90OjllMTgyY2U2LWY0YWMtNDAwNS1hMzQ4LWIyYzJkZTk4OGM1ZSIsImVuY3J5cHRpb25LZXkiOiJmSW93NEdsUGUwYlYtd3M2UC1UNHdHT1JmMGZjakxfWVZEdC00SWNveXo0IiwiYnJhbmNoIjoiZGl2aWRlIiwicmVwb093bmVyIjoicHVsbGZyb2dhaSIsInJlcG9OYW1lIjoic2NyYXRjaCIsInByTnVtYmVyIjo0OSwiY29tbWl0U2hhIjoiNThiOGJmNmQ1MWE1Mjg4OGFjNGFkNzA5YWVmYTk2MWFkZDMyNDBiMSJ9LCJpYXQiOjE3NjM2MjA4MTksImV4cCI6MTc2NDIyNTYxOX0.SFDZe8R9uwhPjS55J4i_mV2ybsSZoQYM6YzdUOava4IKy1IK2OrVkVsG3-8p4rRaMBXdDZZ4ObPbtk70KqdAiLEDKBqaFcWqELc49lr0XRKUmu4F6EhESFQOvt7MLSVDIOgee8YRlhS6xtoPDqsRiV2KGOwyLEdCeYdrYz9i1DanIswWSoMRVvkjxZ6GUBYVAUg_JsgAXoKVJ-L9Q5Ygho6acVAr5NlGeBp2f6g49GX4GfDOPeV3SORQS1CjxQVRbjI-g0rW55NIisBEl8279VwG6-dTISNbyasZOB6R3eEmC4vmyAAGJjUsMwqhMPw1oaMMmYNSbtZLDESxME9IUg"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/fix-in-web-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/fix-in-web-light.svg"><img alt="Fix in Web" src="https://cursor.com/fix-in-web.svg"></picture></a>
56
+
57
+
58
+ \`\`\`\`
59
+
60
+ \`\`\`diff file=.github/workflows/test.yml lines=7 side=RIGHT
61
+ @@ -0,0 +1,36 @@
62
+ ... (3 lines above) ...
63
+ + push:
64
+ + branches: [main]
65
+ + pull_request:
66
+ + branches: [main]
67
+ \`\`\`
68
+ "
69
+ `;
70
+
71
+ exports[`formatReviewData > formats thread blocks with TOC and correct line numbers > toc 1`] = `"- .github/workflows/test.yml:7 → lines 25-52"`;
@@ -0,0 +1,7 @@
1
+ import { configure } from "arktype/config";
2
+
3
+ configure({
4
+ toJsonSchema: {
5
+ dialect: null,
6
+ },
7
+ });
@@ -0,0 +1,245 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4
+ import { analyzeLog, GetCheckSuiteLogsTool } from "#app/mcp/checkSuite";
5
+ import type { ToolContext } from "#app/mcp/server";
6
+
7
+ vi.mock("node:fs", async (importOriginal) => {
8
+ const actual = await importOriginal<typeof import("node:fs")>();
9
+ return { ...actual, default: actual, mkdirSync: vi.fn(), writeFileSync: vi.fn() };
10
+ });
11
+
12
+ type ToolResultShape = { content: [{ type: "text"; text: string }]; isError?: boolean };
13
+
14
+ async function runTool(t: { execute?: unknown }, params: unknown): Promise<ToolResultShape> {
15
+ const exec = t.execute as (p: unknown, c: unknown) => Promise<ToolResultShape>;
16
+ return exec(params, {});
17
+ }
18
+
19
+ type WorkflowRun = { id: number; conclusion: string };
20
+ type Job = {
21
+ id: number;
22
+ name: string;
23
+ conclusion: string;
24
+ html_url?: string | null;
25
+ steps?: { number: number; name: string; conclusion: string }[];
26
+ };
27
+
28
+ function makeCtx(runs: WorkflowRun[], jobs: Job[]) {
29
+ const rest = {
30
+ actions: {
31
+ listWorkflowRunsForRepo: { endpoint: "runs" },
32
+ listJobsForWorkflowRun: { endpoint: "jobs" },
33
+ downloadJobLogsForWorkflowRun: vi.fn(async (_p: unknown) => ({
34
+ url: "https://logs.example/job",
35
+ })),
36
+ },
37
+ };
38
+ const paginate = vi.fn(async (endpoint: unknown) => {
39
+ if (endpoint === rest.actions.listWorkflowRunsForRepo) return runs;
40
+ if (endpoint === rest.actions.listJobsForWorkflowRun) return jobs;
41
+ throw new Error("unexpected paginate endpoint");
42
+ });
43
+ const octokit = { rest, paginate };
44
+ const ctx = {
45
+ octokit,
46
+ repo: { owner: "octo", name: "repo" },
47
+ } as unknown as ToolContext;
48
+ return { ctx, octokit };
49
+ }
50
+
51
+ beforeEach(() => {
52
+ vi.clearAllMocks();
53
+ vi.stubEnv("TERRAMEND_TEMP_DIR", join("/tmp", "terramend-test"));
54
+ vi.stubGlobal(
55
+ "fetch",
56
+ vi.fn(async () => ({ ok: true, text: async () => "##[error] boom" })),
57
+ );
58
+ });
59
+
60
+ afterEach(() => {
61
+ vi.unstubAllEnvs();
62
+ vi.unstubAllGlobals();
63
+ });
64
+
65
+ describe("analyzeLog", () => {
66
+ it("indexes error, warning, failure, and trace lines with 1-based line numbers", () => {
67
+ const logs = [
68
+ "setup ok",
69
+ "##[error]Process completed with exit code 1.",
70
+ "##[warning]node version is old",
71
+ "Tests: 2 failed, 5 passed",
72
+ " at Object.<anonymous> (test.ts:1:1)",
73
+ ].join("\n");
74
+ const { index } = analyzeLog(logs);
75
+ expect(index).toEqual([
76
+ { line: 2, content: "##[error]Process completed with exit code 1.", type: "error" },
77
+ { line: 3, content: "##[warning]node version is old", type: "warning" },
78
+ { line: 4, content: "Tests: 2 failed, 5 passed", type: "failure" },
79
+ { line: 5, content: "at Object.<anonymous> (test.ts:1:1)", type: "trace" },
80
+ ]);
81
+ });
82
+
83
+ it("strips ANSI escape codes before matching", () => {
84
+ const logs = "\x1b[31mError: red alert\x1b[0m";
85
+ const { index } = analyzeLog(logs);
86
+ expect(index).toEqual([{ line: 1, content: "Error: red alert", type: "error" }]);
87
+ });
88
+
89
+ it("skips apt/dpkg WARN noise but keeps real warnings", () => {
90
+ const logs = ["WARN apt does not have a stable CLI", "WARN deprecated dependency"].join("\n");
91
+ const { index } = analyzeLog(logs);
92
+ expect(index).toEqual([{ line: 2, content: "WARN deprecated dependency", type: "warning" }]);
93
+ });
94
+
95
+ it("dedupes consecutive stack-trace lines", () => {
96
+ const logs = [
97
+ "Error: kaput",
98
+ " at first (a.ts:1:1)",
99
+ " at second (b.ts:2:2)",
100
+ "FAIL src/x.test.ts",
101
+ " at third (c.ts:3:3)",
102
+ ].join("\n");
103
+ const { index } = analyzeLog(logs);
104
+ const traces = index.filter((l) => l.type === "trace");
105
+ expect(traces).toHaveLength(2);
106
+ expect(traces[0]?.content).toContain("first");
107
+ expect(traces[1]?.content).toContain("third");
108
+ });
109
+
110
+ it("truncates indexed lines longer than 120 characters", () => {
111
+ const long = `Error: ${"x".repeat(150)}`;
112
+ const { index } = analyzeLog(long);
113
+ expect(index[0]?.content.length).toBeLessThanOrEqual(120);
114
+ expect(index[0]?.content.endsWith("...")).toBe(true);
115
+ });
116
+
117
+ it("centers the excerpt on the LAST ##[error] line", () => {
118
+ const lines = Array.from({ length: 200 }, (_, i) => `line ${i + 1}`);
119
+ lines[49] = "##[error]first failure";
120
+ lines[149] = "##[error]second failure";
121
+ const { excerpt } = analyzeLog(lines.join("\n"), 80);
122
+ // last error at index 149 → start = 149 - 75 = 74 (line 75), end = 149 + 5 = 154
123
+ expect(excerpt.startLine).toBe(75);
124
+ expect(excerpt.endLine).toBe(154);
125
+ expect(excerpt.content).toContain("##[error]second failure");
126
+ expect(excerpt.content).not.toContain("##[error]first failure");
127
+ });
128
+
129
+ it("falls back to the tail window when no ##[error] exists", () => {
130
+ const lines = Array.from({ length: 100 }, (_, i) => `line ${i + 1}`);
131
+ const result = analyzeLog(lines.join("\n"), 10);
132
+ expect(result.totalLines).toBe(100);
133
+ expect(result.excerpt.startLine).toBe(91);
134
+ expect(result.excerpt.endLine).toBe(100);
135
+ expect(result.excerpt.content).toContain("line 100");
136
+ });
137
+
138
+ it("clamps the excerpt start for an error near the top of the log", () => {
139
+ const logs = ["##[error]early", "after"].join("\n");
140
+ const { excerpt } = analyzeLog(logs, 80);
141
+ expect(excerpt.startLine).toBe(1);
142
+ });
143
+ });
144
+
145
+ describe("GetCheckSuiteLogsTool", () => {
146
+ it("returns a no-failures message when no workflow run failed", async () => {
147
+ const { ctx, octokit } = makeCtx([{ id: 1, conclusion: "success" }], []);
148
+ const result = await runTool(GetCheckSuiteLogsTool(ctx), { check_suite_id: 77 });
149
+
150
+ expect(result.isError).toBeUndefined();
151
+ expect(result.content[0].text).toContain("no failed workflow runs found");
152
+ expect(octokit.paginate).toHaveBeenCalledTimes(1);
153
+ expect(mkdirSync).not.toHaveBeenCalled();
154
+ });
155
+
156
+ it("errors when TERRAMEND_TEMP_DIR is not set", async () => {
157
+ vi.stubEnv("TERRAMEND_TEMP_DIR", "");
158
+ const { ctx } = makeCtx([{ id: 1, conclusion: "failure" }], []);
159
+ const result = await runTool(GetCheckSuiteLogsTool(ctx), { check_suite_id: 77 });
160
+
161
+ expect(result.isError).toBe(true);
162
+ expect(result.content[0].text).toContain("TERRAMEND_TEMP_DIR not set");
163
+ });
164
+
165
+ it("downloads, persists, and analyzes the logs of each failed job", async () => {
166
+ const { ctx, octokit } = makeCtx(
167
+ [
168
+ { id: 1, conclusion: "failure" },
169
+ { id: 2, conclusion: "success" },
170
+ ],
171
+ [
172
+ {
173
+ id: 11,
174
+ name: "build",
175
+ conclusion: "failure",
176
+ html_url: "https://gh/job/11",
177
+ steps: [
178
+ { number: 1, name: "checkout", conclusion: "success" },
179
+ { number: 2, name: "test", conclusion: "failure" },
180
+ ],
181
+ },
182
+ { id: 12, name: "lint", conclusion: "success" },
183
+ ],
184
+ );
185
+ const result = await runTool(GetCheckSuiteLogsTool(ctx), { check_suite_id: 77 });
186
+
187
+ expect(result.isError).toBeUndefined();
188
+ expect(octokit.rest.actions.downloadJobLogsForWorkflowRun).toHaveBeenCalledTimes(1);
189
+ expect(octokit.rest.actions.downloadJobLogsForWorkflowRun).toHaveBeenCalledWith(
190
+ expect.objectContaining({ owner: "octo", repo: "repo", job_id: 11 }),
191
+ );
192
+ const logPath = join(join("/tmp", "terramend-test"), "ci-logs", "job-11.log");
193
+ expect(mkdirSync).toHaveBeenCalledWith(join(join("/tmp", "terramend-test"), "ci-logs"), {
194
+ recursive: true,
195
+ });
196
+ expect(writeFileSync).toHaveBeenCalledWith(logPath, "##[error] boom");
197
+ const text = result.content[0].text;
198
+ expect(text).toContain("build");
199
+ expect(text).toContain("Step 2: test");
200
+ expect(text).not.toContain("Step 1: checkout");
201
+ expect(text).toContain("job-11.log");
202
+ expect(text).toContain("##[error] boom");
203
+ });
204
+
205
+ it("defaults html_url and failed_steps when the job omits them", async () => {
206
+ const { ctx } = makeCtx(
207
+ [{ id: 1, conclusion: "failure" }],
208
+ [{ id: 11, name: "build", conclusion: "failure", html_url: null }],
209
+ );
210
+ const result = await runTool(GetCheckSuiteLogsTool(ctx), { check_suite_id: 77 });
211
+
212
+ expect(result.isError).toBeUndefined();
213
+ expect(result.content[0].text).toContain("failed_steps: []");
214
+ });
215
+
216
+ it("skips a job whose log fetch returns a non-OK response", async () => {
217
+ vi.stubGlobal(
218
+ "fetch",
219
+ vi.fn(async () => ({ ok: false, status: 410, statusText: "Gone", text: async () => "" })),
220
+ );
221
+ const { ctx } = makeCtx(
222
+ [{ id: 1, conclusion: "failure" }],
223
+ [{ id: 11, name: "build", conclusion: "failure" }],
224
+ );
225
+ const result = await runTool(GetCheckSuiteLogsTool(ctx), { check_suite_id: 77 });
226
+
227
+ expect(result.isError).toBeUndefined();
228
+ expect(result.content[0].text).toContain("failed_jobs: []");
229
+ expect(writeFileSync).not.toHaveBeenCalled();
230
+ });
231
+
232
+ it("skips a job whose log-URL request rejects", async () => {
233
+ const { ctx, octokit } = makeCtx(
234
+ [{ id: 1, conclusion: "failure" }],
235
+ [{ id: 11, name: "build", conclusion: "failure" }],
236
+ );
237
+ octokit.rest.actions.downloadJobLogsForWorkflowRun.mockRejectedValueOnce(
238
+ new Error("api exploded"),
239
+ );
240
+ const result = await runTool(GetCheckSuiteLogsTool(ctx), { check_suite_id: 77 });
241
+
242
+ expect(result.isError).toBeUndefined();
243
+ expect(result.content[0].text).toContain("failed_jobs: []");
244
+ });
245
+ });