infraweaver 0.1.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 (524) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +244 -0
  3. package/dist/agents/claude.d.ts +113 -0
  4. package/dist/agents/claudePretoolGate.d.ts +137 -0
  5. package/dist/agents/dispatchMetrics.d.ts +77 -0
  6. package/dist/agents/gateServer.d.ts +7 -0
  7. package/dist/agents/index.d.ts +6 -0
  8. package/dist/agents/nativeFsDenies.d.ts +46 -0
  9. package/dist/agents/opencode.d.ts +284 -0
  10. package/dist/agents/opencodePlugin.d.ts +96 -0
  11. package/dist/agents/opencodeShared.d.ts +40 -0
  12. package/dist/agents/postRun.d.ts +126 -0
  13. package/dist/agents/reviewer.d.ts +41 -0
  14. package/dist/agents/sessionLabeler.d.ts +97 -0
  15. package/dist/agents/shared.d.ts +246 -0
  16. package/dist/agents/subagentModels.d.ts +19 -0
  17. package/dist/agents/tokenQuota.d.ts +118 -0
  18. package/dist/agents/writeGateSource.d.ts +22 -0
  19. package/dist/agents/writePolicy.d.ts +79 -0
  20. package/dist/brand.d.ts +67 -0
  21. package/dist/cli.mjs +247846 -0
  22. package/dist/external.d.ts +227 -0
  23. package/dist/i18n/scaffolding.d.ts +59 -0
  24. package/dist/index.d.ts +6 -0
  25. package/dist/index.js +247061 -0
  26. package/dist/internal/index.d.ts +18 -0
  27. package/dist/internal.js +2398 -0
  28. package/dist/lifecycle.d.ts +2 -0
  29. package/dist/main.d.ts +8 -0
  30. package/dist/mcp/arkConfig.d.ts +1 -0
  31. package/dist/mcp/assess.d.ts +157 -0
  32. package/dist/mcp/capabilityContext.d.ts +71 -0
  33. package/dist/mcp/changeSummary.d.ts +52 -0
  34. package/dist/mcp/checkSuite.d.ts +27 -0
  35. package/dist/mcp/checkout.d.ts +92 -0
  36. package/dist/mcp/comment.d.ts +127 -0
  37. package/dist/mcp/commitInfo.d.ts +11 -0
  38. package/dist/mcp/crosswalk.d.ts +178 -0
  39. package/dist/mcp/crosswalkDigest.d.ts +1 -0
  40. package/dist/mcp/cyberEssentials.d.ts +24 -0
  41. package/dist/mcp/dashboard.d.ts +125 -0
  42. package/dist/mcp/dependencies.d.ts +12 -0
  43. package/dist/mcp/frameworks.d.ts +74 -0
  44. package/dist/mcp/geminiSanitizer.d.ts +28 -0
  45. package/dist/mcp/git.d.ts +60 -0
  46. package/dist/mcp/guardrails.d.ts +222 -0
  47. package/dist/mcp/issue.d.ts +20 -0
  48. package/dist/mcp/issueComments.d.ts +11 -0
  49. package/dist/mcp/issueEvents.d.ts +11 -0
  50. package/dist/mcp/issueInfo.d.ts +11 -0
  51. package/dist/mcp/labels.d.ts +14 -0
  52. package/dist/mcp/localContext.d.ts +26 -0
  53. package/dist/mcp/moduleExtraction.d.ts +79 -0
  54. package/dist/mcp/moduleTests.d.ts +106 -0
  55. package/dist/mcp/modules.d.ts +198 -0
  56. package/dist/mcp/output.d.ts +16 -0
  57. package/dist/mcp/pathSafety.d.ts +14 -0
  58. package/dist/mcp/policy.d.ts +50 -0
  59. package/dist/mcp/pr.d.ts +64 -0
  60. package/dist/mcp/prInfo.d.ts +11 -0
  61. package/dist/mcp/providerSchema.d.ts +52 -0
  62. package/dist/mcp/review.d.ts +212 -0
  63. package/dist/mcp/reviewComments.d.ts +245 -0
  64. package/dist/mcp/roots.d.ts +60 -0
  65. package/dist/mcp/scope.d.ts +26 -0
  66. package/dist/mcp/selectMode.d.ts +20 -0
  67. package/dist/mcp/server.d.ts +61 -0
  68. package/dist/mcp/shared.d.ts +62 -0
  69. package/dist/mcp/shell.d.ts +58 -0
  70. package/dist/mcp/staleFix.d.ts +138 -0
  71. package/dist/mcp/terraform/azurePrices.d.ts +8 -0
  72. package/dist/mcp/terraform/baseline.d.ts +57 -0
  73. package/dist/mcp/terraform/concernResult.d.ts +38 -0
  74. package/dist/mcp/terraform/cost.d.ts +55 -0
  75. package/dist/mcp/terraform/cspm.d.ts +80 -0
  76. package/dist/mcp/terraform/currency.d.ts +110 -0
  77. package/dist/mcp/terraform/decisions.d.ts +178 -0
  78. package/dist/mcp/terraform/eidas.d.ts +100 -0
  79. package/dist/mcp/terraform/evidence.d.ts +343 -0
  80. package/dist/mcp/terraform/findings.d.ts +76 -0
  81. package/dist/mcp/terraform/fixMemory.d.ts +81 -0
  82. package/dist/mcp/terraform/governance.d.ts +56 -0
  83. package/dist/mcp/terraform/hcl.d.ts +115 -0
  84. package/dist/mcp/terraform/iacLanguages.d.ts +120 -0
  85. package/dist/mcp/terraform/idleFloor.d.ts +78 -0
  86. package/dist/mcp/terraform/keyless.d.ts +68 -0
  87. package/dist/mcp/terraform/moduleDocs.d.ts +56 -0
  88. package/dist/mcp/terraform/nativeRules.d.ts +38 -0
  89. package/dist/mcp/terraform/nativeScan.d.ts +11 -0
  90. package/dist/mcp/terraform/noiseProfile.d.ts +100 -0
  91. package/dist/mcp/terraform/normalization.d.ts +52 -0
  92. package/dist/mcp/terraform/oscal.d.ts +103 -0
  93. package/dist/mcp/terraform/packs/azurerm-3-4.d.ts +2 -0
  94. package/dist/mcp/terraform/packs/cloudflare-4-5.d.ts +2 -0
  95. package/dist/mcp/terraform/paths.d.ts +28 -0
  96. package/dist/mcp/terraform/plan.d.ts +157 -0
  97. package/dist/mcp/terraform/planDrift.d.ts +199 -0
  98. package/dist/mcp/terraform/policyAuthor.d.ts +191 -0
  99. package/dist/mcp/terraform/prowlerOcsf.d.ts +136 -0
  100. package/dist/mcp/terraform/refactor.d.ts +183 -0
  101. package/dist/mcp/terraform/registry.d.ts +108 -0
  102. package/dist/mcp/terraform/risk.d.ts +41 -0
  103. package/dist/mcp/terraform/scanSession.d.ts +116 -0
  104. package/dist/mcp/terraform/scannerCache.d.ts +15 -0
  105. package/dist/mcp/terraform/scannerEgress.d.ts +72 -0
  106. package/dist/mcp/terraform/scannerJson.d.ts +13 -0
  107. package/dist/mcp/terraform/scanners.d.ts +205 -0
  108. package/dist/mcp/terraform/signing.d.ts +61 -0
  109. package/dist/mcp/terraform/stateLocking.d.ts +18 -0
  110. package/dist/mcp/terraform/subprocess.d.ts +43 -0
  111. package/dist/mcp/terraform/suppressions.d.ts +90 -0
  112. package/dist/mcp/terraform/taxonomy.d.ts +39 -0
  113. package/dist/mcp/terraform/tfquery.d.ts +47 -0
  114. package/dist/mcp/terraform/toolchainIntegrity.d.ts +82 -0
  115. package/dist/mcp/terraform/tools/authorPolicy.d.ts +59 -0
  116. package/dist/mcp/terraform/tools/detectPlanDrift.d.ts +20 -0
  117. package/dist/mcp/terraform/tools/emitCost.d.ts +11 -0
  118. package/dist/mcp/terraform/tools/emitSarif.d.ts +14 -0
  119. package/dist/mcp/terraform/tools/fixMemory.d.ts +19 -0
  120. package/dist/mcp/terraform/tools/infracostDiff.d.ts +22 -0
  121. package/dist/mcp/terraform/tools/ingestExternalFindings.d.ts +21 -0
  122. package/dist/mcp/terraform/tools/moduleLookup.d.ts +14 -0
  123. package/dist/mcp/terraform/tools/moduleSearch.d.ts +23 -0
  124. package/dist/mcp/terraform/tools/plan.d.ts +59 -0
  125. package/dist/mcp/terraform/tools/providerUpgrade.d.ts +55 -0
  126. package/dist/mcp/terraform/tools/readFindings.d.ts +17 -0
  127. package/dist/mcp/terraform/tools/scan.d.ts +114 -0
  128. package/dist/mcp/terraform/tools/validate.d.ts +31 -0
  129. package/dist/mcp/terraform/tools/verifyRemediation.d.ts +31 -0
  130. package/dist/mcp/terraform/tools/versionCurrency.d.ts +5 -0
  131. package/dist/mcp/terraform/tools.d.ts +16 -0
  132. package/dist/mcp/terraform/transformPacks.d.ts +267 -0
  133. package/dist/mcp/terraform/types.d.ts +226 -0
  134. package/dist/mcp/terraform/verification.d.ts +81 -0
  135. package/dist/mcp/terraform/vex.d.ts +115 -0
  136. package/dist/mcp/terraform/writeOnly.d.ts +67 -0
  137. package/dist/mcp/terraform.d.ts +51 -0
  138. package/dist/mcp/terratest.d.ts +85 -0
  139. package/dist/mcp/untrustedContent.d.ts +76 -0
  140. package/dist/mcp/upload.d.ts +8 -0
  141. package/dist/models.d.ts +181 -0
  142. package/dist/modes/address-reviews.d.ts +2 -0
  143. package/dist/modes/assess.d.ts +2 -0
  144. package/dist/modes/build.d.ts +2 -0
  145. package/dist/modes/compliance-audit.d.ts +2 -0
  146. package/dist/modes/cost-optimization.d.ts +2 -0
  147. package/dist/modes/drift-detect.d.ts +2 -0
  148. package/dist/modes/fix.d.ts +2 -0
  149. package/dist/modes/incremental-review.d.ts +2 -0
  150. package/dist/modes/index.d.ts +24 -0
  151. package/dist/modes/modernize-deprecated.d.ts +2 -0
  152. package/dist/modes/plan.d.ts +2 -0
  153. package/dist/modes/policy-gate.d.ts +2 -0
  154. package/dist/modes/prFormats.d.ts +3 -0
  155. package/dist/modes/refactor.d.ts +2 -0
  156. package/dist/modes/refresh-remediation.d.ts +2 -0
  157. package/dist/modes/remediate-and-refactor.d.ts +2 -0
  158. package/dist/modes/remediate.d.ts +2 -0
  159. package/dist/modes/resolve-conflicts.d.ts +2 -0
  160. package/dist/modes/review.d.ts +2 -0
  161. package/dist/modes/summarize-pr.d.ts +2 -0
  162. package/dist/modes/task.d.ts +2 -0
  163. package/dist/modes/terraform-code-review.d.ts +2 -0
  164. package/dist/modes/types.d.ts +9 -0
  165. package/dist/modes/update-dependencies.d.ts +2 -0
  166. package/dist/prep/index.d.ts +7 -0
  167. package/dist/prep/installNodeDependencies.d.ts +2 -0
  168. package/dist/prep/installPythonDependencies.d.ts +2 -0
  169. package/dist/prep/types.d.ts +31 -0
  170. package/dist/reviewQuality.d.ts +107 -0
  171. package/dist/skills/terraform-best-practices/SKILL.md +379 -0
  172. package/dist/toolState.d.ts +123 -0
  173. package/dist/utils/activity.d.ts +40 -0
  174. package/dist/utils/agent.d.ts +47 -0
  175. package/dist/utils/agentHangReport.d.ts +38 -0
  176. package/dist/utils/annotations.d.ts +17 -0
  177. package/dist/utils/apiFetch.d.ts +11 -0
  178. package/dist/utils/apiKeys.d.ts +48 -0
  179. package/dist/utils/apiUrl.d.ts +28 -0
  180. package/dist/utils/assets.d.ts +8 -0
  181. package/dist/utils/baseRefConfig.d.ts +121 -0
  182. package/dist/utils/billingErrors.d.ts +85 -0
  183. package/dist/utils/body.d.ts +34 -0
  184. package/dist/utils/buildInfraweaverFooter.d.ts +29 -0
  185. package/dist/utils/byokFallback.d.ts +85 -0
  186. package/dist/utils/changeImpact.d.ts +101 -0
  187. package/dist/utils/claudeSubscription.d.ts +30 -0
  188. package/dist/utils/cli.d.ts +10 -0
  189. package/dist/utils/cloudClient.d.ts +33 -0
  190. package/dist/utils/cloudFetchCore.d.ts +42 -0
  191. package/dist/utils/cloudReport.d.ts +114 -0
  192. package/dist/utils/codexHome.d.ts +29 -0
  193. package/dist/utils/codexOAuth.d.ts +60 -0
  194. package/dist/utils/diffCoverage.d.ts +63 -0
  195. package/dist/utils/env.d.ts +46 -0
  196. package/dist/utils/errorReport.d.ts +17 -0
  197. package/dist/utils/exitHandler.d.ts +8 -0
  198. package/dist/utils/fixDoubleEscapedString.d.ts +1 -0
  199. package/dist/utils/gitAuth.d.ts +84 -0
  200. package/dist/utils/gitAuthServer.d.ts +24 -0
  201. package/dist/utils/github.d.ts +104 -0
  202. package/dist/utils/globals.d.ts +3 -0
  203. package/dist/utils/infraweaverConfig.d.ts +56 -0
  204. package/dist/utils/install.d.ts +37 -0
  205. package/dist/utils/instructions.d.ts +48 -0
  206. package/dist/utils/keylessOidc.d.ts +28 -0
  207. package/dist/utils/leapingComment.d.ts +11 -0
  208. package/dist/utils/learnings.d.ts +62 -0
  209. package/dist/utils/learningsTruncate.d.ts +25 -0
  210. package/dist/utils/lifecycle.d.ts +57 -0
  211. package/dist/utils/log.d.ts +111 -0
  212. package/dist/utils/modelResolver.d.ts +63 -0
  213. package/dist/utils/moduleFetch.d.ts +39 -0
  214. package/dist/utils/normalizeEnv.d.ts +30 -0
  215. package/dist/utils/openCodeModels.d.ts +11 -0
  216. package/dist/utils/openaiCompatible.d.ts +26 -0
  217. package/dist/utils/opentofuMcp.d.ts +50 -0
  218. package/dist/utils/overrides.d.ts +40 -0
  219. package/dist/utils/packageManager.d.ts +49 -0
  220. package/dist/utils/patchWorkflowRunFields.d.ts +29 -0
  221. package/dist/utils/payload.d.ts +249 -0
  222. package/dist/utils/prSummary.d.ts +61 -0
  223. package/dist/utils/presets.d.ts +36 -0
  224. package/dist/utils/progressComment.d.ts +146 -0
  225. package/dist/utils/providerErrors.d.ts +31 -0
  226. package/dist/utils/proxyModel.d.ts +76 -0
  227. package/dist/utils/rangeDiff.d.ts +51 -0
  228. package/dist/utils/redact.d.ts +33 -0
  229. package/dist/utils/registryAuth.d.ts +52 -0
  230. package/dist/utils/remediationCommand.d.ts +58 -0
  231. package/dist/utils/retry.d.ts +38 -0
  232. package/dist/utils/reviewCleanup.d.ts +14 -0
  233. package/dist/utils/run.d.ts +9 -0
  234. package/dist/utils/runContext.d.ts +112 -0
  235. package/dist/utils/runContextData.d.ts +42 -0
  236. package/dist/utils/runErrorRenderer.d.ts +64 -0
  237. package/dist/utils/runLifecycle.d.ts +89 -0
  238. package/dist/utils/runStartupLog.d.ts +15 -0
  239. package/dist/utils/secrets.d.ts +34 -0
  240. package/dist/utils/setup.d.ts +90 -0
  241. package/dist/utils/shell.d.ts +44 -0
  242. package/dist/utils/skills.d.ts +10 -0
  243. package/dist/utils/subprocess.d.ts +81 -0
  244. package/dist/utils/terraformMcp.d.ts +46 -0
  245. package/dist/utils/time.d.ts +15 -0
  246. package/dist/utils/timer.d.ts +23 -0
  247. package/dist/utils/todoTracking.d.ts +16 -0
  248. package/dist/utils/token.d.ts +52 -0
  249. package/dist/utils/toolLicensing.d.ts +56 -0
  250. package/dist/utils/toolSelection.d.ts +73 -0
  251. package/dist/utils/toon.d.ts +16 -0
  252. package/dist/utils/version.d.ts +2 -0
  253. package/dist/utils/versioning.d.ts +7 -0
  254. package/dist/utils/vertex.d.ts +16 -0
  255. package/dist/utils/workflow.d.ts +13 -0
  256. package/package.json +132 -0
  257. package/src/agents/claude.ts +1588 -0
  258. package/src/agents/claudePretoolGate.ts +286 -0
  259. package/src/agents/dispatchMetrics.ts +162 -0
  260. package/src/agents/gateServer.ts +178 -0
  261. package/src/agents/index.ts +10 -0
  262. package/src/agents/nativeFsDenies.ts +164 -0
  263. package/src/agents/opencode.ts +1600 -0
  264. package/src/agents/opencodePlugin.ts +304 -0
  265. package/src/agents/opencodeShared.ts +134 -0
  266. package/src/agents/postRun.ts +615 -0
  267. package/src/agents/reviewer.ts +134 -0
  268. package/src/agents/sessionLabeler.ts +185 -0
  269. package/src/agents/shared.ts +377 -0
  270. package/src/agents/subagentModels.ts +40 -0
  271. package/src/agents/tokenQuota.ts +203 -0
  272. package/src/agents/writeGateSource.ts +148 -0
  273. package/src/agents/writePolicy.ts +132 -0
  274. package/src/brand.ts +84 -0
  275. package/src/cli.ts +117 -0
  276. package/src/commands/gha.ts +188 -0
  277. package/src/commands/mcp.ts +126 -0
  278. package/src/commands/verifyEvidence.ts +100 -0
  279. package/src/entry.ts +7 -0
  280. package/src/entryPost.ts +109 -0
  281. package/src/external.ts +308 -0
  282. package/src/i18n/scaffolding.ts +191 -0
  283. package/src/index.ts +7 -0
  284. package/src/internal/index.ts +74 -0
  285. package/src/lifecycle.ts +2 -0
  286. package/src/main.ts +815 -0
  287. package/src/mcp/__fixtures__/infraweaver-scratch-pr-49-review-3485940013.json +110 -0
  288. package/src/mcp/__fixtures__/infraweaver-scratch-pr-64-review-3531000326.json +14 -0
  289. package/src/mcp/__fixtures__/infraweaver-test-repo-pr-1.diff.json +67 -0
  290. package/src/mcp/__snapshots__/checkout.test.ts.snap +109 -0
  291. package/src/mcp/__snapshots__/reviewComments.test.ts.snap +71 -0
  292. package/src/mcp/arkConfig.ts +7 -0
  293. package/src/mcp/assess.ts +723 -0
  294. package/src/mcp/capabilityContext.ts +80 -0
  295. package/src/mcp/changeSummary.ts +145 -0
  296. package/src/mcp/checkSuite.ts +261 -0
  297. package/src/mcp/checkout.ts +1015 -0
  298. package/src/mcp/comment.ts +683 -0
  299. package/src/mcp/commitInfo.ts +57 -0
  300. package/src/mcp/crosswalk.ts +1253 -0
  301. package/src/mcp/crosswalkDigest.ts +5 -0
  302. package/src/mcp/cyberEssentials.ts +60 -0
  303. package/src/mcp/dashboard.ts +232 -0
  304. package/src/mcp/dependencies.ts +190 -0
  305. package/src/mcp/frameworks.ts +236 -0
  306. package/src/mcp/geminiSanitizer.ts +212 -0
  307. package/src/mcp/git.ts +1116 -0
  308. package/src/mcp/guardrails.ts +771 -0
  309. package/src/mcp/issue.ts +74 -0
  310. package/src/mcp/issueComments.ts +48 -0
  311. package/src/mcp/issueEvents.ts +100 -0
  312. package/src/mcp/issueInfo.ts +72 -0
  313. package/src/mcp/labels.ts +35 -0
  314. package/src/mcp/localContext.ts +65 -0
  315. package/src/mcp/localServer.ts +217 -0
  316. package/src/mcp/moduleExtraction.ts +368 -0
  317. package/src/mcp/moduleTests.ts +421 -0
  318. package/src/mcp/modules.ts +752 -0
  319. package/src/mcp/output.ts +71 -0
  320. package/src/mcp/pathSafety.ts +28 -0
  321. package/src/mcp/policy.ts +226 -0
  322. package/src/mcp/pr.ts +238 -0
  323. package/src/mcp/prInfo.ts +91 -0
  324. package/src/mcp/providerSchema.ts +175 -0
  325. package/src/mcp/review.ts +1081 -0
  326. package/src/mcp/reviewComments.ts +1137 -0
  327. package/src/mcp/roots.ts +217 -0
  328. package/src/mcp/scope.ts +82 -0
  329. package/src/mcp/selectMode.ts +207 -0
  330. package/src/mcp/server.ts +498 -0
  331. package/src/mcp/shared.ts +120 -0
  332. package/src/mcp/shell.ts +631 -0
  333. package/src/mcp/staleFix.ts +557 -0
  334. package/src/mcp/terraform/__snapshots__/moduleDocs.test.ts.snap +40 -0
  335. package/src/mcp/terraform/azurePrices.ts +38 -0
  336. package/src/mcp/terraform/baseline.ts +172 -0
  337. package/src/mcp/terraform/concernResult.ts +111 -0
  338. package/src/mcp/terraform/cost.ts +175 -0
  339. package/src/mcp/terraform/cspm.ts +326 -0
  340. package/src/mcp/terraform/currency.ts +354 -0
  341. package/src/mcp/terraform/decisions.ts +590 -0
  342. package/src/mcp/terraform/eidas.ts +134 -0
  343. package/src/mcp/terraform/evidence.ts +771 -0
  344. package/src/mcp/terraform/findings.ts +383 -0
  345. package/src/mcp/terraform/fixMemory.ts +253 -0
  346. package/src/mcp/terraform/governance.ts +231 -0
  347. package/src/mcp/terraform/hcl.ts +273 -0
  348. package/src/mcp/terraform/iacLanguages.ts +431 -0
  349. package/src/mcp/terraform/idleFloor.ts +304 -0
  350. package/src/mcp/terraform/keyless.ts +123 -0
  351. package/src/mcp/terraform/moduleDocs.ts +303 -0
  352. package/src/mcp/terraform/nativeRules.ts +152 -0
  353. package/src/mcp/terraform/nativeScan.ts +67 -0
  354. package/src/mcp/terraform/noiseProfile.ts +183 -0
  355. package/src/mcp/terraform/normalization.ts +265 -0
  356. package/src/mcp/terraform/oscal.ts +283 -0
  357. package/src/mcp/terraform/packs/azurerm-3-4.ts +382 -0
  358. package/src/mcp/terraform/packs/cloudflare-4-5.ts +114 -0
  359. package/src/mcp/terraform/paths.ts +59 -0
  360. package/src/mcp/terraform/plan.ts +377 -0
  361. package/src/mcp/terraform/planDrift.ts +520 -0
  362. package/src/mcp/terraform/policyAuthor.ts +886 -0
  363. package/src/mcp/terraform/prowlerOcsf.ts +380 -0
  364. package/src/mcp/terraform/refactor.ts +879 -0
  365. package/src/mcp/terraform/registry.ts +317 -0
  366. package/src/mcp/terraform/risk.ts +99 -0
  367. package/src/mcp/terraform/scanSession.ts +242 -0
  368. package/src/mcp/terraform/scannerCache.ts +86 -0
  369. package/src/mcp/terraform/scannerEgress.ts +141 -0
  370. package/src/mcp/terraform/scannerJson.ts +22 -0
  371. package/src/mcp/terraform/scanners.ts +1134 -0
  372. package/src/mcp/terraform/signing.ts +151 -0
  373. package/src/mcp/terraform/stateLocking.ts +106 -0
  374. package/src/mcp/terraform/subprocess.ts +102 -0
  375. package/src/mcp/terraform/suppressions.ts +551 -0
  376. package/src/mcp/terraform/taxonomy.ts +0 -0
  377. package/src/mcp/terraform/tfquery.ts +159 -0
  378. package/src/mcp/terraform/toolchainIntegrity.ts +151 -0
  379. package/src/mcp/terraform/tools/authorPolicy.ts +185 -0
  380. package/src/mcp/terraform/tools/detectPlanDrift.ts +279 -0
  381. package/src/mcp/terraform/tools/emitCost.ts +130 -0
  382. package/src/mcp/terraform/tools/emitSarif.ts +108 -0
  383. package/src/mcp/terraform/tools/fixMemory.ts +82 -0
  384. package/src/mcp/terraform/tools/infracostDiff.ts +110 -0
  385. package/src/mcp/terraform/tools/ingestExternalFindings.ts +259 -0
  386. package/src/mcp/terraform/tools/moduleLookup.ts +86 -0
  387. package/src/mcp/terraform/tools/moduleSearch.ts +46 -0
  388. package/src/mcp/terraform/tools/plan.ts +384 -0
  389. package/src/mcp/terraform/tools/providerUpgrade.ts +217 -0
  390. package/src/mcp/terraform/tools/readFindings.ts +138 -0
  391. package/src/mcp/terraform/tools/scan.ts +303 -0
  392. package/src/mcp/terraform/tools/validate.ts +160 -0
  393. package/src/mcp/terraform/tools/verifyRemediation.ts +190 -0
  394. package/src/mcp/terraform/tools/versionCurrency.ts +64 -0
  395. package/src/mcp/terraform/tools.ts +62 -0
  396. package/src/mcp/terraform/transformPacks.ts +594 -0
  397. package/src/mcp/terraform/types.ts +382 -0
  398. package/src/mcp/terraform/verification.ts +150 -0
  399. package/src/mcp/terraform/vex.ts +367 -0
  400. package/src/mcp/terraform/writeOnly.ts +252 -0
  401. package/src/mcp/terraform.ts +52 -0
  402. package/src/mcp/terratest.ts +215 -0
  403. package/src/mcp/untrustedContent.ts +126 -0
  404. package/src/mcp/upload.ts +123 -0
  405. package/src/models.ts +753 -0
  406. package/src/modes/address-reviews.ts +35 -0
  407. package/src/modes/assess.ts +27 -0
  408. package/src/modes/build.ts +86 -0
  409. package/src/modes/compliance-audit.ts +25 -0
  410. package/src/modes/cost-optimization.ts +36 -0
  411. package/src/modes/drift-detect.ts +31 -0
  412. package/src/modes/fix.ts +29 -0
  413. package/src/modes/incremental-review.ts +103 -0
  414. package/src/modes/index.ts +94 -0
  415. package/src/modes/modernize-deprecated.ts +36 -0
  416. package/src/modes/plan.ts +21 -0
  417. package/src/modes/policy-gate.ts +27 -0
  418. package/src/modes/prFormats.ts +325 -0
  419. package/src/modes/refactor.ts +74 -0
  420. package/src/modes/refresh-remediation.ts +40 -0
  421. package/src/modes/remediate-and-refactor.ts +42 -0
  422. package/src/modes/remediate.ts +78 -0
  423. package/src/modes/resolve-conflicts.ts +32 -0
  424. package/src/modes/review.ts +135 -0
  425. package/src/modes/summarize-pr.ts +42 -0
  426. package/src/modes/task.ts +26 -0
  427. package/src/modes/terraform-code-review.ts +68 -0
  428. package/src/modes/types.ts +17 -0
  429. package/src/modes/update-dependencies.ts +44 -0
  430. package/src/prep/index.ts +96 -0
  431. package/src/prep/installNodeDependencies.ts +251 -0
  432. package/src/prep/installPythonDependencies.ts +235 -0
  433. package/src/prep/types.ts +38 -0
  434. package/src/reviewQuality.ts +237 -0
  435. package/src/runCli.ts +335 -0
  436. package/src/skills/terraform-best-practices/SKILL.md +379 -0
  437. package/src/toolState.ts +241 -0
  438. package/src/utils/activity.ts +210 -0
  439. package/src/utils/agent.ts +245 -0
  440. package/src/utils/agentHangReport.ts +180 -0
  441. package/src/utils/annotations.ts +56 -0
  442. package/src/utils/apiFetch.ts +19 -0
  443. package/src/utils/apiKeys.ts +305 -0
  444. package/src/utils/apiUrl.ts +42 -0
  445. package/src/utils/assets.ts +123 -0
  446. package/src/utils/baseRefConfig.ts +254 -0
  447. package/src/utils/billingErrors.ts +210 -0
  448. package/src/utils/body.ts +168 -0
  449. package/src/utils/buildInfraweaverFooter.ts +104 -0
  450. package/src/utils/byokFallback.ts +137 -0
  451. package/src/utils/changeImpact.ts +330 -0
  452. package/src/utils/claudeSubscription.ts +93 -0
  453. package/src/utils/cli.ts +36 -0
  454. package/src/utils/cloudClient.ts +57 -0
  455. package/src/utils/cloudFetchCore.ts +139 -0
  456. package/src/utils/cloudReport.ts +316 -0
  457. package/src/utils/codexHome.ts +204 -0
  458. package/src/utils/codexOAuth.ts +154 -0
  459. package/src/utils/codexRefreshDetect.ts +36 -0
  460. package/src/utils/diffCoverage.ts +404 -0
  461. package/src/utils/env.ts +84 -0
  462. package/src/utils/errorReport.ts +94 -0
  463. package/src/utils/exitHandler.ts +35 -0
  464. package/src/utils/fixDoubleEscapedString.ts +9 -0
  465. package/src/utils/ghaCore.ts +13 -0
  466. package/src/utils/gitAuth.ts +268 -0
  467. package/src/utils/gitAuthServer.ts +186 -0
  468. package/src/utils/github.ts +676 -0
  469. package/src/utils/globals.ts +9 -0
  470. package/src/utils/humanEditCapture.ts +198 -0
  471. package/src/utils/infraweaverConfig.ts +202 -0
  472. package/src/utils/install.ts +279 -0
  473. package/src/utils/instructions.ts +640 -0
  474. package/src/utils/keylessOidc.ts +47 -0
  475. package/src/utils/leapingComment.ts +20 -0
  476. package/src/utils/learnings.ts +145 -0
  477. package/src/utils/learningsTruncate.ts +42 -0
  478. package/src/utils/lifecycle.ts +198 -0
  479. package/src/utils/log.ts +452 -0
  480. package/src/utils/modelResolver.ts +105 -0
  481. package/src/utils/moduleFetch.ts +138 -0
  482. package/src/utils/normalizeEnv.ts +106 -0
  483. package/src/utils/openCodeModels.ts +86 -0
  484. package/src/utils/openaiCompatible.ts +52 -0
  485. package/src/utils/opentofuMcp.ts +61 -0
  486. package/src/utils/overrides.ts +100 -0
  487. package/src/utils/packageManager.ts +257 -0
  488. package/src/utils/patchWorkflowRunFields.ts +173 -0
  489. package/src/utils/payload.ts +1206 -0
  490. package/src/utils/prSummary.ts +148 -0
  491. package/src/utils/presets.ts +105 -0
  492. package/src/utils/progressComment.ts +266 -0
  493. package/src/utils/providerErrors.ts +189 -0
  494. package/src/utils/proxyModel.ts +239 -0
  495. package/src/utils/rangeDiff.ts +182 -0
  496. package/src/utils/redact.ts +86 -0
  497. package/src/utils/registryAuth.ts +115 -0
  498. package/src/utils/remediationCommand.ts +144 -0
  499. package/src/utils/retry.ts +180 -0
  500. package/src/utils/reviewCleanup.ts +116 -0
  501. package/src/utils/run.ts +100 -0
  502. package/src/utils/runContext.ts +276 -0
  503. package/src/utils/runContextData.ts +121 -0
  504. package/src/utils/runErrorRenderer.ts +282 -0
  505. package/src/utils/runFixture.ts +91 -0
  506. package/src/utils/runLifecycle.ts +360 -0
  507. package/src/utils/runStartupLog.ts +67 -0
  508. package/src/utils/secrets.ts +208 -0
  509. package/src/utils/setup.ts +368 -0
  510. package/src/utils/shell.ts +132 -0
  511. package/src/utils/skills.ts +67 -0
  512. package/src/utils/subprocess.ts +474 -0
  513. package/src/utils/terraformMcp.ts +99 -0
  514. package/src/utils/time.ts +59 -0
  515. package/src/utils/timer.ts +72 -0
  516. package/src/utils/todoTracking.ts +168 -0
  517. package/src/utils/token.ts +294 -0
  518. package/src/utils/toolLicensing.ts +152 -0
  519. package/src/utils/toolSelection.ts +239 -0
  520. package/src/utils/toon.ts +76 -0
  521. package/src/utils/version.ts +10 -0
  522. package/src/utils/versioning.ts +44 -0
  523. package/src/utils/vertex.ts +94 -0
  524. package/src/utils/workflow.ts +25 -0
@@ -0,0 +1,683 @@
1
+ import { type } from "arktype";
2
+ import { chromeStrings } from "#app/i18n/scaffolding";
3
+ import type {
4
+ GitHubToolContext,
5
+ GitHubWriteToolContext,
6
+ } from "#app/mcp/capabilityContext";
7
+ import { assertTargetInScope } from "#app/mcp/scope";
8
+ import { execute, tool } from "#app/mcp/shared";
9
+ import { getApiUrl } from "#app/utils/apiUrl";
10
+ import {
11
+ buildInfraweaverFooter,
12
+ stripExistingFooter,
13
+ } from "#app/utils/buildInfraweaverFooter";
14
+ import { log } from "#app/utils/cli";
15
+ import { fixDoubleEscapedString } from "#app/utils/fixDoubleEscapedString";
16
+ import { patchWorkflowRunFields } from "#app/utils/patchWorkflowRunFields";
17
+ import {
18
+ createLeapingProgressComment,
19
+ deleteProgressCommentApi,
20
+ updateProgressComment,
21
+ } from "#app/utils/progressComment";
22
+ import { redactSecrets } from "#app/utils/redact";
23
+
24
+ // re-export for backward compat with anything importing the leaping helpers from mcp/comment
25
+ export {
26
+ isLeapingIntoActionCommentBody,
27
+ LEAPING_INTO_ACTION_PREFIX,
28
+ } from "#app/utils/leapingComment";
29
+
30
+ function isNotFoundError(error: unknown): boolean {
31
+ return error instanceof Error && error.message.includes("Not Found");
32
+ }
33
+
34
+ function buildCommentFooter(
35
+ ctx: GitHubToolContext,
36
+ customParts?: string[],
37
+ ): string {
38
+ return buildInfraweaverFooter({
39
+ triggeredBy: true,
40
+ customParts,
41
+ model: ctx.toolState.model,
42
+ fallbackFrom: ctx.toolState.modelFallback?.from,
43
+ language: ctx.payload.language,
44
+ });
45
+ }
46
+
47
+ function buildImplementPlanLink(
48
+ ctx: GitHubToolContext,
49
+ issueNumber: number,
50
+ commentId: number,
51
+ ): string {
52
+ const apiUrl = getApiUrl();
53
+ const label = chromeStrings(ctx.payload.language).implementPlan;
54
+ return `[${label} ➔](${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${issueNumber}?action=implement&comment_id=${commentId})`;
55
+ }
56
+
57
+ export function addFooter(ctx: GitHubToolContext, body: string): string {
58
+ if (/<br\s*\/?>[ \t]*\n(?!\s*\n)/i.test(body)) {
59
+ throw new Error(
60
+ "body contains <br/> followed by a non-blank line, which breaks GitHub markdown rendering. always add a blank line after <br/> tags.",
61
+ );
62
+ }
63
+ const bodyWithoutFooter = stripExistingFooter(fixDoubleEscapedString(body));
64
+ const footer = buildCommentFooter(ctx);
65
+ // LLM02: scrub secrets before the comment becomes a durable, public artifact.
66
+ return redactSecrets(`${bodyWithoutFooter}${footer}`);
67
+ }
68
+
69
+ export const Comment = type({
70
+ issueNumber: type.number.describe("the issue number to comment on"),
71
+ body: type.string.describe("the comment body content"),
72
+ type: type
73
+ .enumerated("Plan", "Comment")
74
+ .describe(
75
+ "Plan: record as the plan for this run. Comment: regular comment (default).",
76
+ )
77
+ .optional(),
78
+ });
79
+
80
+ export function CreateCommentTool(ctx: GitHubWriteToolContext) {
81
+ return tool({
82
+ mutates: true,
83
+ name: "create_issue_comment",
84
+ description:
85
+ "Create a comment on a GitHub issue or PR. " +
86
+ 'Example: `create_issue_comment({ issueNumber: 1234, body: "Thanks for the report." })`. ' +
87
+ "For progress/plan updates on the current run use report_progress instead — plan output (initial post AND revisions) is always posted via report_progress, never via this tool.",
88
+ parameters: Comment,
89
+ execute: execute(async ({ issueNumber, body, type: commentType }) => {
90
+ assertTargetInScope(ctx, issueNumber, "comment on");
91
+ const bodyWithFooter = addFooter(ctx, body);
92
+
93
+ const result = await ctx.octokit.rest.issues.createComment({
94
+ owner: ctx.repo.owner,
95
+ repo: ctx.repo.name,
96
+ issue_number: issueNumber,
97
+ body: bodyWithFooter,
98
+ });
99
+
100
+ ctx.toolState.wasUpdated = true;
101
+ log.info(`» created comment ${result.data.id}`);
102
+
103
+ // a standalone answer comment on the run's OWN target makes the sticky
104
+ // progress comment redundant — record it so run-end cleanup deletes the
105
+ // progress comment even after report_progress finalized (see runLifecycle).
106
+ // scoped to the target so commenting on a DIFFERENT issue/PR doesn't trip it,
107
+ // and excludes Plan comments (their own deliberate deliverable, not chrome).
108
+ if (
109
+ commentType !== "Plan" &&
110
+ issueNumber === ctx.payload.event.issue_number
111
+ ) {
112
+ ctx.toolState.answerCommentPosted = true;
113
+ }
114
+
115
+ if (commentType === "Plan") {
116
+ if (result.data.node_id) {
117
+ await patchWorkflowRunFields(ctx, {
118
+ planCommentNodeId: result.data.node_id,
119
+ });
120
+ }
121
+ // add "Implement plan" link (needs comment ID, so create-then-update)
122
+ const customParts = [
123
+ buildImplementPlanLink(ctx, issueNumber, result.data.id),
124
+ ];
125
+ const footer = buildCommentFooter(ctx, customParts);
126
+ // match the create path's addFooter: fix double-escaped `\n`/`\t`/`\"`
127
+ // before re-posting, so the final visible plan comment (this update
128
+ // overwrites the create) doesn't render escape sequences literally.
129
+ // LLM02: this Plan update bypasses addFooter, so redact here too.
130
+ const bodyWithPlanLink = redactSecrets(
131
+ `${stripExistingFooter(fixDoubleEscapedString(body))}${footer}`,
132
+ );
133
+
134
+ const updateResult = await ctx.octokit.rest.issues.updateComment({
135
+ owner: ctx.repo.owner,
136
+ repo: ctx.repo.name,
137
+ comment_id: result.data.id,
138
+ body: bodyWithPlanLink,
139
+ });
140
+ log.info(`» updated comment ${updateResult.data.id}`);
141
+
142
+ return {
143
+ success: true,
144
+ commentId: updateResult.data.id,
145
+ url: updateResult.data.html_url,
146
+ body: updateResult.data.body,
147
+ };
148
+ }
149
+
150
+ return {
151
+ success: true,
152
+ commentId: result.data.id,
153
+ url: result.data.html_url,
154
+ body: result.data.body,
155
+ };
156
+ }),
157
+ });
158
+ }
159
+
160
+ export const EditComment = type({
161
+ commentId: type.number.describe("the ID of the comment to edit"),
162
+ body: type.string.describe("the new comment body content"),
163
+ });
164
+
165
+ /** parse the issue/PR number a comment belongs to from its API `issue_url`
166
+ * (`https://api.github.com/repos/o/r/issues/123`). undefined when unparseable. */
167
+ function issueNumberFromUrl(url: string | undefined): number | undefined {
168
+ const m = url?.match(/\/issues\/(\d+)(?:$|[/?#])/);
169
+ return m ? Number(m[1]) : undefined;
170
+ }
171
+
172
+ /**
173
+ * SECURITY: bind edit_issue_comment to the run's scoped issue/PR. A comment id
174
+ * is repo-global, so resolve it to the issue/PR it lives on first (an injected
175
+ * agent must not be able to overwrite a comment on an UNRELATED issue/PR).
176
+ * Skips for standalone runs and, deliberately, fails OPEN when the lookup can't
177
+ * determine the issue — editing a comment is reversible and low-risk, so a
178
+ * transient API/permission hiccup must not block a legitimate edit. A definite
179
+ * out-of-scope match still throws.
180
+ */
181
+ async function assertCommentInScope(
182
+ ctx: GitHubWriteToolContext,
183
+ commentId: number,
184
+ ): Promise<void> {
185
+ if (ctx.payload?.event?.issue_number === undefined) return;
186
+ let issueNumber: number | undefined;
187
+ try {
188
+ const { data } = await ctx.octokit.rest.issues.getComment({
189
+ owner: ctx.repo.owner,
190
+ repo: ctx.repo.name,
191
+ comment_id: commentId,
192
+ });
193
+ issueNumber = issueNumberFromUrl(data.issue_url);
194
+ } catch (e) {
195
+ log.debug(
196
+ `edit_issue_comment scope lookup failed (allowing): ${e instanceof Error ? e.message : String(e)}`,
197
+ );
198
+ return;
199
+ }
200
+ if (issueNumber !== undefined) {
201
+ assertTargetInScope(ctx, issueNumber, "edit a comment on");
202
+ }
203
+ }
204
+
205
+ export function EditCommentTool(ctx: GitHubWriteToolContext) {
206
+ return tool({
207
+ mutates: true,
208
+ name: "edit_issue_comment",
209
+ description: "Edit a GitHub issue comment by its ID",
210
+ parameters: EditComment,
211
+ execute: execute(async ({ commentId, body }) => {
212
+ await assertCommentInScope(ctx, commentId);
213
+ const bodyWithFooter = addFooter(ctx, body);
214
+
215
+ const result = await ctx.octokit.rest.issues.updateComment({
216
+ owner: ctx.repo.owner,
217
+ repo: ctx.repo.name,
218
+ comment_id: commentId,
219
+ body: bodyWithFooter,
220
+ });
221
+ log.info(`» updated comment ${result.data.id}`);
222
+
223
+ return {
224
+ success: true,
225
+ commentId: result.data.id,
226
+ url: result.data.html_url,
227
+ body: result.data.body,
228
+ updatedAt: result.data.updated_at,
229
+ };
230
+ }),
231
+ });
232
+ }
233
+
234
+ export const ReportProgress = type({
235
+ body: type.string.describe("the progress update content to share"),
236
+ "target_plan_comment?": type("boolean").describe(
237
+ "for revising an existing plan comment ONLY. set to true only when the PlanEdit checklist from select_mode tells you to (i.e. a prior plan comment was found for this issue). NEVER set on the initial plan post — the initial plan reuses the run's progress comment and is posted by calling report_progress without this flag.",
238
+ ),
239
+ });
240
+
241
+ /**
242
+ * Report progress to a GitHub comment.
243
+ *
244
+ * progressComment has three states:
245
+ * - undefined: no comment yet — will create one if an issue/PR target exists
246
+ * - object: active comment — will update it in place via the right REST endpoint for its type
247
+ * - null: deliberately deleted (e.g. after submitting a PR review) — skips silently
248
+ *
249
+ * The body is tracked in lastProgressBody for the job summary regardless of comment state,
250
+ * EXCEPT for `liveProgress` (todo-tracker) writes — see the param note below.
251
+ *
252
+ * The "existing plan comment" path always targets a top-level issue comment (plan comments are
253
+ * created by create_issue_comment with type:"Plan", never as review-thread replies).
254
+ */
255
+ export async function reportProgress(
256
+ ctx: GitHubWriteToolContext,
257
+ params: {
258
+ body: string;
259
+ target_plan_comment?: boolean;
260
+ liveProgress?: boolean;
261
+ },
262
+ ): Promise<{
263
+ commentId?: number;
264
+ url?: string;
265
+ body: string;
266
+ action: "created" | "updated" | "skipped";
267
+ }> {
268
+ // LLM02: redact once at entry so every derived progress/plan body — and the
269
+ // salvaged `lastProgressBody` / job-summary copy below — is scrubbed before it
270
+ // becomes a durable comment. redactSecrets is idempotent + a no-op on clean text.
271
+ const { body: rawProgressBody, target_plan_comment } = params;
272
+ const body = redactSecrets(rawProgressBody);
273
+ // `liveProgress` marks the automatic todo-tracker checklist render — a live
274
+ // progress update, NOT the agent's deliberate final answer. such writes must
275
+ // not record `lastProgressBody` or flip `wasUpdated`: both signal "a real
276
+ // user-facing answer landed", and letting an auto checklist trip them masks
277
+ // the #868 salvage (it would post the checklist instead of the real output,
278
+ // or skip salvage entirely) and triggers stranded-comment deletion.
279
+ if (!params.liveProgress) {
280
+ ctx.toolState.lastProgressBody = body;
281
+ }
282
+
283
+ // silent events (e.g., auto-label, pr-summary Task) should never create or update progress comments.
284
+ // the body is still tracked above for the GitHub Actions job summary.
285
+ if (ctx.payload.event.silent) {
286
+ return { body, action: "skipped" };
287
+ }
288
+
289
+ const issueNumber =
290
+ ctx.payload.event.issue_number ?? ctx.toolState.issueNumber;
291
+ const isPlanMode = ctx.toolState.selectedMode === "Plan";
292
+ const apiCtx = {
293
+ octokit: ctx.octokit,
294
+ owner: ctx.repo.owner,
295
+ repo: ctx.repo.name,
296
+ };
297
+
298
+ // when editing existing plan: update the plan comment from tool state (set by select_mode)
299
+ if (
300
+ target_plan_comment === true &&
301
+ ctx.toolState.existingPlanCommentId === undefined
302
+ ) {
303
+ log.warning(
304
+ "target_plan_comment requested but no existingPlanCommentId in tool state",
305
+ );
306
+ }
307
+ if (
308
+ target_plan_comment === true &&
309
+ ctx.toolState.existingPlanCommentId !== undefined
310
+ ) {
311
+ const commentId = ctx.toolState.existingPlanCommentId;
312
+ const customParts =
313
+ issueNumber !== undefined
314
+ ? [buildImplementPlanLink(ctx, issueNumber, commentId)]
315
+ : undefined;
316
+ // fix double-escaped `\n`/`\t`/`\"` before re-posting, matching the create
317
+ // path's addFooter — an in-place update (final summary / plan revision) must
318
+ // not render escape sequences literally where the initial create wouldn't.
319
+ const bodyWithoutFooter = stripExistingFooter(fixDoubleEscapedString(body));
320
+ const footer = buildCommentFooter(ctx, customParts);
321
+ const bodyWithFooter = `${bodyWithoutFooter}${footer}`;
322
+
323
+ const result = await updateProgressComment(
324
+ apiCtx,
325
+ { id: commentId, type: "issue" },
326
+ bodyWithFooter,
327
+ );
328
+
329
+ if (!params.liveProgress) ctx.toolState.wasUpdated = true;
330
+
331
+ if (isPlanMode && result.node_id) {
332
+ await patchWorkflowRunFields(ctx, { planCommentNodeId: result.node_id });
333
+ }
334
+
335
+ return {
336
+ commentId: result.id,
337
+ url: result.html_url,
338
+ body: result.body || "",
339
+ action: "updated",
340
+ };
341
+ }
342
+
343
+ const existingComment = ctx.toolState.progressComment;
344
+
345
+ // if we already have a progress comment, update it
346
+ if (existingComment) {
347
+ const customParts =
348
+ isPlanMode && issueNumber !== undefined
349
+ ? [buildImplementPlanLink(ctx, issueNumber, existingComment.id)]
350
+ : undefined;
351
+
352
+ // fix double-escaped `\n`/`\t`/`\"` before re-posting, matching the create
353
+ // path's addFooter — an in-place update (final summary / plan revision) must
354
+ // not render escape sequences literally where the initial create wouldn't.
355
+ const bodyWithoutFooter = stripExistingFooter(fixDoubleEscapedString(body));
356
+ const footer = buildCommentFooter(ctx, customParts);
357
+ const bodyWithFooter = `${bodyWithoutFooter}${footer}`;
358
+
359
+ // a review-reply progress comment (seeded by the dispatch path) can become
360
+ // stale before final delivery — the thread is deleted or otherwise
361
+ // unreachable, so updateProgressComment 404s. rather than fail an
362
+ // already-completed run, fall back to a fresh top-level comment on the PR
363
+ // and retarget future writes there.
364
+ let result: Awaited<ReturnType<typeof updateProgressComment>>;
365
+ try {
366
+ result = await updateProgressComment(
367
+ apiCtx,
368
+ existingComment,
369
+ bodyWithFooter,
370
+ );
371
+ } catch (error) {
372
+ // only a deliberate write to a stale review-reply comment falls back. a
373
+ // liveProgress (todo-tracker) 404 rethrows — it must never create a
374
+ // user-facing comment, and the next deliberate report_progress recovers.
375
+ if (
376
+ params.liveProgress ||
377
+ existingComment.type !== "review" ||
378
+ !isNotFoundError(error) ||
379
+ issueNumber === undefined
380
+ ) {
381
+ throw error;
382
+ }
383
+ log.warning(
384
+ `progress review comment ${existingComment.id} is gone (404); posting a top-level comment on #${issueNumber} instead`,
385
+ );
386
+ const created = await createLeapingProgressComment(
387
+ apiCtx,
388
+ { kind: "issue", issueNumber },
389
+ bodyWithFooter,
390
+ );
391
+ ctx.toolState.progressComment = created.comment;
392
+ if (!params.liveProgress) ctx.toolState.wasUpdated = true;
393
+ return {
394
+ commentId: created.comment.id,
395
+ url: created.html_url,
396
+ body: created.body || "",
397
+ action: "created",
398
+ };
399
+ }
400
+
401
+ if (!params.liveProgress) ctx.toolState.wasUpdated = true;
402
+
403
+ if (isPlanMode && result.node_id) {
404
+ await patchWorkflowRunFields(ctx, { planCommentNodeId: result.node_id });
405
+ }
406
+
407
+ return {
408
+ commentId: result.id,
409
+ url: result.html_url,
410
+ body: result.body || "",
411
+ action: "updated",
412
+ };
413
+ }
414
+
415
+ // null = progress comment was deleted by stranded-comment cleanup in main.ts
416
+ if (existingComment === null) {
417
+ return { body, action: "skipped" };
418
+ }
419
+
420
+ // no existing comment - need an issue/PR to create one on
421
+ // use fallback chain: dynamically set context > event payload
422
+ if (issueNumber === undefined) {
423
+ // no-op: no comment target (e.g., workflow_dispatch events)
424
+ // body is already tracked for job summary
425
+ return { body, action: "skipped" };
426
+ }
427
+
428
+ // for new comments, we need to create first, then update with Plan link if in Plan mode
429
+ // self-created progress comments are always top-level issue comments — review-reply
430
+ // progress comments only originate from the dispatch path and arrive pre-created.
431
+ const initialBody = addFooter(ctx, body);
432
+ const created = await createLeapingProgressComment(
433
+ apiCtx,
434
+ { kind: "issue", issueNumber },
435
+ initialBody,
436
+ );
437
+
438
+ ctx.toolState.progressComment = created.comment;
439
+ if (!params.liveProgress) ctx.toolState.wasUpdated = true;
440
+
441
+ // if Plan mode, update the comment to add the "Implement plan" link
442
+ if (isPlanMode) {
443
+ const customParts = [
444
+ buildImplementPlanLink(ctx, issueNumber, created.comment.id),
445
+ ];
446
+ // fix double-escaped `\n`/`\t`/`\"` before re-posting, matching the create
447
+ // path's addFooter — an in-place update (final summary / plan revision) must
448
+ // not render escape sequences literally where the initial create wouldn't.
449
+ const bodyWithoutFooter = stripExistingFooter(fixDoubleEscapedString(body));
450
+ const footer = buildCommentFooter(ctx, customParts);
451
+ const bodyWithPlanLink = `${bodyWithoutFooter}${footer}`;
452
+
453
+ const updateResult = await updateProgressComment(
454
+ apiCtx,
455
+ created.comment,
456
+ bodyWithPlanLink,
457
+ );
458
+
459
+ if (updateResult.node_id) {
460
+ await patchWorkflowRunFields(ctx, {
461
+ planCommentNodeId: updateResult.node_id,
462
+ });
463
+ }
464
+
465
+ return {
466
+ commentId: updateResult.id,
467
+ url: updateResult.html_url,
468
+ body: updateResult.body || "",
469
+ action: "created",
470
+ };
471
+ }
472
+
473
+ return {
474
+ commentId: created.comment.id,
475
+ url: created.html_url,
476
+ body: created.body || "",
477
+ action: "created",
478
+ };
479
+ }
480
+
481
+ export function ReportProgressTool(ctx: GitHubWriteToolContext) {
482
+ return tool({
483
+ mutates: true,
484
+ name: "report_progress",
485
+ description:
486
+ "Share progress on the associated GitHub issue/PR. The first call creates a comment; subsequent calls update it in place. " +
487
+ 'Example: `report_progress({ body: "Implemented the auth check and added tests." })`. ' +
488
+ "Call this at the end of every run with a brief final summary (1-3 sentences) unless the mode guidance instructs otherwise. The current task list is automatically appended in a collapsible section — do not restate individual steps.",
489
+ parameters: ReportProgress,
490
+ execute: execute(async (params) => {
491
+ let body = params.body;
492
+
493
+ // for non-plan calls: stop auto-updates, wait for in-flight writes to settle,
494
+ // then append completed task list collapsible
495
+ if (!params.target_plan_comment && ctx.toolState.todoTracker) {
496
+ ctx.toolState.todoTracker.cancel();
497
+ await ctx.toolState.todoTracker.settled();
498
+ const collapsible = ctx.toolState.todoTracker.renderCollapsible({
499
+ completeInProgress: true,
500
+ });
501
+ if (collapsible) {
502
+ body = `${body}\n\n${collapsible}`;
503
+ }
504
+ }
505
+
506
+ const reportParams: { body: string; target_plan_comment?: boolean } = {
507
+ body,
508
+ };
509
+ if (params.target_plan_comment !== undefined) {
510
+ reportParams.target_plan_comment = params.target_plan_comment;
511
+ }
512
+ const result = await reportProgress(ctx, reportParams);
513
+
514
+ if (result.action === "skipped") {
515
+ return {
516
+ success: true,
517
+ message:
518
+ "progress recorded (no GitHub comment created - this may occur for workflow_dispatch events or when there is no associated issue/PR)",
519
+ };
520
+ }
521
+
522
+ if (result.commentId !== undefined) {
523
+ log.info(`» ${result.action} comment ${result.commentId}`);
524
+ }
525
+
526
+ if (!params.target_plan_comment) {
527
+ ctx.toolState.finalSummaryWritten = true;
528
+ }
529
+
530
+ return {
531
+ success: true,
532
+ ...result,
533
+ };
534
+ }),
535
+ });
536
+ }
537
+
538
+ /**
539
+ * Delete the progress comment if it exists.
540
+ * Used by main.ts for stranded-comment cleanup (orphaned "Leaping into action" or
541
+ * checklist left by the todo tracker when the agent didn't call report_progress).
542
+ * Sets progressComment to null so subsequent report_progress calls are no-ops.
543
+ */
544
+ export async function deleteProgressComment(
545
+ ctx: GitHubWriteToolContext,
546
+ ): Promise<boolean> {
547
+ const existing = ctx.toolState.progressComment;
548
+ if (!existing) {
549
+ return false;
550
+ }
551
+
552
+ try {
553
+ await deleteProgressCommentApi(
554
+ { octokit: ctx.octokit, owner: ctx.repo.owner, repo: ctx.repo.name },
555
+ existing,
556
+ );
557
+ } catch (error) {
558
+ // ignore 404 - comment already deleted
559
+ if (!isNotFoundError(error)) throw error;
560
+ }
561
+
562
+ // set to null (not undefined) so report_progress skips instead of creating a new comment
563
+ ctx.toolState.progressComment = null;
564
+
565
+ return true;
566
+ }
567
+
568
+ export const ReplyToReviewComment = type({
569
+ pull_number: type.number.describe("the pull request number"),
570
+ comment_id: type.number.describe("the ID of the review comment to reply to"),
571
+ body: type.string.describe(
572
+ "extremely brief reply (1 sentence max) explaining what was fixed, e.g. 'Fixed by renaming to X' or 'Added null check'",
573
+ ),
574
+ });
575
+
576
+ /**
577
+ * decision returned by `duplicateReplyDecision` when a session has already
578
+ * posted an identical reply to the same parent review comment.
579
+ */
580
+ export interface DuplicateReplyDecision {
581
+ kind: "already-replied";
582
+ commentId: number;
583
+ url: string | undefined;
584
+ reason: string;
585
+ }
586
+
587
+ /**
588
+ * decide whether a second reply_to_review_comment call in the same session
589
+ * is a duplicate of an earlier reply to the same parent comment.
590
+ *
591
+ * the agent is instructed to call reply_to_review_comment exactly once per
592
+ * parent comment per AddressReviews session, but in practice it sometimes
593
+ * emits the same call twice. PR #610 reproduced this with Kimi K2:
594
+ * identical body posted 3 seconds apart, only one tool_use event in the
595
+ * agent log. the second post is always redundant and clutters the PR thread.
596
+ *
597
+ * we key on (comment_id, bodyWithFooter) so a legitimate follow-up reply
598
+ * with different content still goes through. within a single run the
599
+ * footer is constant (workflow run + model + jobId), so byte-equal bodies
600
+ * catch the stutter without blocking real follow-ups.
601
+ *
602
+ * mirrors the shape of `duplicateReviewDecision` in mcp/review.ts.
603
+ */
604
+ export function duplicateReplyDecision(params: {
605
+ existing:
606
+ | { commentId: number; url: string | undefined; bodyWithFooter: string }
607
+ | undefined;
608
+ bodyWithFooter: string;
609
+ }): DuplicateReplyDecision | null {
610
+ const existing = params.existing;
611
+ if (!existing) return null;
612
+ if (existing.bodyWithFooter !== params.bodyWithFooter) return null;
613
+ return {
614
+ kind: "already-replied",
615
+ commentId: existing.commentId,
616
+ url: existing.url,
617
+ reason: `reply ${existing.commentId} with identical body was already posted in this session; ignoring duplicate call`,
618
+ };
619
+ }
620
+
621
+ export function ReplyToReviewCommentTool(ctx: GitHubWriteToolContext) {
622
+ return tool({
623
+ mutates: true,
624
+ name: "reply_to_review_comment",
625
+ description:
626
+ "Reply to a PR review comment thread (NOT issue comments — this only works for inline review comments on PR diffs). " +
627
+ 'Example: `reply_to_review_comment({ pull_number: 1234, comment_id: 567890, body: "Fixed by adding a null check." })`. ' +
628
+ "Call exactly ONCE per parent comment you address in AddressReviews mode — duplicate calls with the same body are a no-op. Keep replies extremely brief (1 sentence max).",
629
+ parameters: ReplyToReviewComment,
630
+ execute: execute(async ({ pull_number, comment_id, body }) => {
631
+ assertTargetInScope(ctx, pull_number, "reply to a review comment on");
632
+ const bodyWithFooter = addFooter(ctx, body);
633
+
634
+ // guard against duplicate reply submissions in the same session.
635
+ // see duplicateReplyDecision for the rationale.
636
+ const dup = duplicateReplyDecision({
637
+ existing: ctx.toolState.reviewReplies?.get(comment_id),
638
+ bodyWithFooter,
639
+ });
640
+ if (dup) {
641
+ log.info(`skipping duplicate review reply: ${dup.reason}`);
642
+ return {
643
+ success: true,
644
+ skipped: true,
645
+ reason: dup.reason,
646
+ commentId: dup.commentId,
647
+ url: dup.url,
648
+ };
649
+ }
650
+
651
+ const result = await ctx.octokit.rest.pulls.createReplyForReviewComment({
652
+ owner: ctx.repo.owner,
653
+ repo: ctx.repo.name,
654
+ pull_number,
655
+ comment_id,
656
+ body: bodyWithFooter,
657
+ });
658
+ log.info(
659
+ `» created review comment ${result.data.id} (in reply to ${comment_id})`,
660
+ );
661
+
662
+ // mark progress as updated so error reporting + run-result handling know
663
+ // a substantive write happened (used by reportErrorToComment / handleAgentResult)
664
+ ctx.toolState.wasUpdated = true;
665
+
666
+ // record this reply for in-session dedupe of subsequent identical calls.
667
+ ctx.toolState.reviewReplies ??= new Map();
668
+ ctx.toolState.reviewReplies.set(comment_id, {
669
+ commentId: result.data.id,
670
+ url: result.data.html_url,
671
+ bodyWithFooter,
672
+ });
673
+
674
+ return {
675
+ success: true,
676
+ commentId: result.data.id,
677
+ url: result.data.html_url,
678
+ body: result.data.body,
679
+ in_reply_to_id: result.data.in_reply_to_id,
680
+ };
681
+ }, "reply_to_review_comment"),
682
+ });
683
+ }