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,557 @@
1
+ import { type } from "arktype";
2
+ import { PRODUCT_NAME, PRODUCT_SLUG } from "#app/brand";
3
+ import type { GitHubToolContext } from "#app/mcp/capabilityContext";
4
+ import { addFooter } from "#app/mcp/comment";
5
+ import { assertTargetInScope } from "#app/mcp/scope";
6
+ import { execute, tool, toolOk } from "#app/mcp/shared";
7
+ import { log } from "#app/utils/cli";
8
+
9
+ /**
10
+ * Stale-fix self-healing. A Infraweaver remediation PR is "base + a minimal,
11
+ * proven fix". When the base branch advances after the PR is opened, the PR goes
12
+ * stale: its diff is computed against an old base, the concern may already be
13
+ * resolved upstream (a human fixed it, or the base changed the file), or the
14
+ * branch simply needs re-deriving on the new base. A scheduled `RefreshRemediation`
15
+ * run sweeps the open remediation PRs and, per PR, either re-derives the fix on
16
+ * the current base and force-updates it, closes it as already-resolved, or leaves
17
+ * it for a human when someone has added their own commits.
18
+ *
19
+ * This file is the GitHub-orchestration seam: a pure classifier (`assessStaleFix`)
20
+ * that decides what to do from git facts, the read tool that surfaces the open
21
+ * remediation PRs with their staleness, and the focused write tool that closes a
22
+ * now-redundant PR. The actual re-derive/validate/verify loop is driven by the
23
+ * mode prompt using the existing scan/validate/verify/push tools — no git merge
24
+ * is performed (re-deriving on the fresh base avoids conflict resolution entirely
25
+ * and keeps the PR diff to exactly the fix).
26
+ */
27
+
28
+ // the branch-name conventions Infraweaver opens PRs under: `remediate/<group-id>`
29
+ // (incl. `remediate/batch-<hash>`) for a fix and `infraweaver/generate-<slug>` for
30
+ // a generation. The naming is the primary "this is Infraweaver's PR" signal.
31
+ const REMEDIATION_BRANCH = /^(?:remediate\/|infraweaver\/generate-)/;
32
+
33
+ /** true when `branch` is a Infraweaver remediation/generation branch. */
34
+ export function isRemediationBranch(branch: string): boolean {
35
+ return REMEDIATION_BRANCH.test(branch);
36
+ }
37
+
38
+ /** the `<group-id>` of a `remediate/<group-id>` branch (the scan group id that
39
+ * keys the fix), or null for a generation branch / non-remediation branch. */
40
+ export function groupIdFromBranch(branch: string): string | null {
41
+ return branch.match(/^remediate\/(.+)$/)?.[1] ?? null;
42
+ }
43
+
44
+ /** core matcher: a NON-EMPTY identity string (a GitHub login or a git author
45
+ * name) that belongs to a bot — Infraweaver's own, or any `[bot]` account. */
46
+ function matchesBotIdentity(identity: string): boolean {
47
+ const normalized = identity.replace(/\[bot\]$/i, "").toLowerCase();
48
+ return (
49
+ normalized === PRODUCT_SLUG ||
50
+ normalized === `${PRODUCT_SLUG}dev` ||
51
+ /\[bot\]$/i.test(identity)
52
+ );
53
+ }
54
+
55
+ /** a git author email belonging to a bot noreply account, e.g.
56
+ * `infraweaver[bot]@users.noreply.github.com`. */
57
+ function isBotEmail(email: string): boolean {
58
+ return /\[bot\]@/i.test(email);
59
+ }
60
+
61
+ /** true when a commit-author LOGIN is Infraweaver's bot (so a commit by it is NOT
62
+ * a human edit). A null/absent login is treated as non-human here for backward
63
+ * compatibility of this login-only helper — but prefer {@link isBotCommit}, which
64
+ * resolves an absent login via the commit's git identity rather than assuming
65
+ * bot (the assumption that caused a human commit with an unlinked email to be
66
+ * force-overwritten). */
67
+ export function isBotActor(login: string | null | undefined): boolean {
68
+ if (!login) return true;
69
+ return matchesBotIdentity(login);
70
+ }
71
+
72
+ /** the authorship slice of a compareCommits entry that {@link isBotCommit} reads. */
73
+ export interface CommitAuthorship {
74
+ /** the GitHub user GitHub resolved from the commit's author email, or null when
75
+ * the email isn't linked to any account — true for Infraweaver's own `[bot]`
76
+ * noreply email AND for a human committing under an unverified email. */
77
+ author?: { login?: string | null } | null;
78
+ /** the raw git author identity from the commit object, present even when the
79
+ * GitHub `author` account above is null. */
80
+ commit?: {
81
+ author?: { name?: string | null; email?: string | null } | null;
82
+ } | null;
83
+ }
84
+
85
+ /**
86
+ * Decide whether a remediation-branch commit is Infraweaver's own (a bot commit,
87
+ * safe to force-overwrite on refresh) or a human/third-party edit (must be
88
+ * preserved — escalate instead of refresh).
89
+ *
90
+ * A resolved GitHub `author.login` is authoritative: a positive bot match is a
91
+ * bot, a real login is a human. When the login is ABSENT — which happens both for
92
+ * Infraweaver's own commits (its `infraweaver[bot]` noreply email doesn't map to a
93
+ * GitHub account) and for a human committing under an unverified email — fall
94
+ * back to the commit's git identity: only a recognised bot name/email
95
+ * (`infraweaver[bot]`, any `[bot]`) counts as a bot; ANY other identity is treated
96
+ * as a human edit. This is the conservative direction on purpose — a missed bot
97
+ * commit costs one skipped auto-refresh, whereas a missed human commit is a
98
+ * force-push that destroys their work.
99
+ */
100
+ export function isBotCommit(commit: CommitAuthorship): boolean {
101
+ const login = commit.author?.login;
102
+ if (login) return isBotActor(login);
103
+ const name = commit.commit?.author?.name ?? "";
104
+ const email = commit.commit?.author?.email ?? "";
105
+ return matchesBotIdentity(name) || isBotEmail(email);
106
+ }
107
+
108
+ export type StaleFixStatus =
109
+ | "current"
110
+ | "stale"
111
+ | "human_touched"
112
+ | "checks_failing";
113
+ export type StaleFixAction = "skip" | "refresh" | "escalate";
114
+
115
+ /** the latest CI conclusion on a remediation PR's head, used to self-heal a PR
116
+ * whose own checks are red (a failed `terraform plan`, a failing module test, a
117
+ * policy gate) even when its base hasn't moved. `unknown` = no checks reported or
118
+ * the read failed (treated as "no signal" — never manufactures a refresh). */
119
+ export type ChecksState = "passing" | "failing" | "pending" | "unknown";
120
+
121
+ /** check-run conclusions that count as a hard CI failure for self-heal purposes.
122
+ * `neutral`/`skipped`/`stale`/`cancelled`/`success` are NOT failures. */
123
+ const FAILING_CONCLUSIONS = new Set([
124
+ "failure",
125
+ "timed_out",
126
+ "action_required",
127
+ ]);
128
+
129
+ export interface StaleFixAssessment {
130
+ status: StaleFixStatus;
131
+ action: StaleFixAction;
132
+ reason: string;
133
+ /** a likely zombie: stale (base advanced), untouched by humans, and open past
134
+ * the staleness age threshold — surface it so the sweep prioritizes closing it
135
+ * once the concern is confirmed resolved. Advisory; the action stays `refresh`. */
136
+ zombie: boolean;
137
+ }
138
+
139
+ /** age (in days, PR open duration) past which a stale, untouched remediation PR
140
+ * is flagged as a likely zombie for prioritized cleanup. */
141
+ export const DEFAULT_STALE_AFTER_DAYS = 30;
142
+
143
+ /**
144
+ * How many times the sweep will auto re-derive a remediation PR to clear a FAILING
145
+ * CI check before it gives up and escalates to a human. Self-heal is on by default,
146
+ * but a fix the engine proves green locally yet that keeps failing the consumer's
147
+ * CI (a cloud-credentialled `terraform plan`, a flaky integration test) must not
148
+ * loop forever — past the cap the PR is labelled needs-human and left alone. Only
149
+ * the checks-failing path is capped; a genuine base move is always a fresh refresh.
150
+ */
151
+ export const DEFAULT_SELF_HEAL_MAX_ATTEMPTS = 3;
152
+
153
+ /** the per-attempt label the sweep stamps on a PR each time it self-heals a red
154
+ * CI, so the retry budget survives across stateless runs. Accumulating (add-only):
155
+ * the attempt count is how many are present. */
156
+ const SELF_HEAL_ATTEMPT_LABEL = /^infraweaver-self-heal-attempt-\d+$/;
157
+
158
+ /** count of self-heal attempts already recorded on a PR, from its accumulated
159
+ * `infraweaver-self-heal-attempt-N` labels. */
160
+ export function selfHealAttempts(labels: string[]): number {
161
+ return labels.filter((l) => SELF_HEAL_ATTEMPT_LABEL.test(l)).length;
162
+ }
163
+
164
+ /** the label to stamp when recording the next self-heal attempt (1-based). */
165
+ export function nextSelfHealLabel(attempts: number): string {
166
+ return `infraweaver-self-heal-attempt-${attempts + 1}`;
167
+ }
168
+
169
+ /**
170
+ * Decide what a refresh sweep should do with one open remediation PR, from git +
171
+ * CI facts alone. Pure.
172
+ * - a branch carrying NON-bot commits is `human_touched` → `escalate` (never
173
+ * force-overwrite a human's work; leave it for review).
174
+ * - a branch whose base has NOT advanced AND whose checks are not failing is
175
+ * `current` → `skip` (the fix is still derived against the live base).
176
+ * - a branch whose own CI is red (and base unchanged) is `checks_failing` →
177
+ * `refresh`: re-derive the fix on the current base and re-prove it (the
178
+ * self-heal case — a failed plan/test/policy on Infraweaver's own PR). Two guards
179
+ * stop this looping forever: (1) if it is ALREADY labelled needs-human and the
180
+ * base hasn't moved, re-deriving would only reproduce the same failing fix, so
181
+ * `skip`; (2) once `selfHealAttempts` reaches `maxSelfHealAttempts` (default 3)
182
+ * the sweep gives up and `escalate`s instead of re-deriving again.
183
+ * - otherwise the base moved → `stale` → `refresh`: re-derive the fix on the
184
+ * current base, then close it if the concern is already resolved or update it.
185
+ *
186
+ * `ageDays` (PR open duration) is advisory: a stale, untouched PR open past
187
+ * `staleAfterDays` is additionally flagged `zombie` so the sweep can prioritize
188
+ * clearing the long-rotting ones. It never changes the action — a fix is only
189
+ * ever closed once a re-scan confirms its concern is actually resolved.
190
+ */
191
+ export function assessStaleFix(input: {
192
+ baseBehindBy: number;
193
+ hasNonBotCommits: boolean;
194
+ /** the PR's CI is red (a failed plan/test/policy) — triggers a self-heal. */
195
+ checksFailing?: boolean;
196
+ /** the PR already carries `needs-human` — used as the self-heal loop guard. */
197
+ alreadyEscalated?: boolean;
198
+ /** self-heal attempts already recorded on this PR (from its attempt labels). */
199
+ selfHealAttempts?: number;
200
+ /** the retry ceiling for self-heal (default {@link DEFAULT_SELF_HEAL_MAX_ATTEMPTS}). */
201
+ maxSelfHealAttempts?: number;
202
+ ageDays?: number;
203
+ staleAfterDays?: number;
204
+ }): StaleFixAssessment {
205
+ if (input.hasNonBotCommits) {
206
+ return {
207
+ status: "human_touched",
208
+ action: "escalate",
209
+ reason: `the PR branch has commit(s) not authored by ${PRODUCT_SLUG} — auto-refresh would overwrite a human's work; label it needs-human and leave it for review`,
210
+ zombie: false,
211
+ };
212
+ }
213
+ const baseMoved = input.baseBehindBy > 0;
214
+ const checksFailing = input.checksFailing === true;
215
+
216
+ if (!baseMoved && !checksFailing) {
217
+ return {
218
+ status: "current",
219
+ action: "skip",
220
+ reason:
221
+ "the base has not advanced since the PR was opened — the fix is still current",
222
+ zombie: false,
223
+ };
224
+ }
225
+
226
+ // self-heal loop guard: a still-failing PR we already escalated, with an
227
+ // unchanged base, would only re-derive the same failing fix — leave it.
228
+ if (!baseMoved && checksFailing && input.alreadyEscalated) {
229
+ return {
230
+ status: "checks_failing",
231
+ action: "skip",
232
+ reason:
233
+ "the PR's checks are failing but it is already labelled needs-human and the base has not moved — left for a human to avoid a refresh loop",
234
+ zombie: false,
235
+ };
236
+ }
237
+
238
+ // checks-failing only (base unchanged): self-heal — re-derive + re-prove. Bounded
239
+ // by the retry cap so a fix the engine proves green locally but that keeps failing
240
+ // the consumer's CI can't loop forever.
241
+ if (!baseMoved && checksFailing) {
242
+ const attempts = input.selfHealAttempts ?? 0;
243
+ const max = input.maxSelfHealAttempts ?? DEFAULT_SELF_HEAL_MAX_ATTEMPTS;
244
+ if (attempts >= max) {
245
+ return {
246
+ status: "checks_failing",
247
+ action: "escalate",
248
+ reason: `self-heal exhausted its retry budget (${attempts}/${max} attempts) without clearing CI — escalate to needs-human and STOP; no further auto-retry on this PR`,
249
+ zombie: false,
250
+ };
251
+ }
252
+ return {
253
+ status: "checks_failing",
254
+ action: "refresh",
255
+ reason:
256
+ `the PR's checks are failing while the base is unchanged — self-heal attempt ${attempts + 1} of ${max}: ` +
257
+ "re-derive the fix on the current base and re-prove it (✗→✓), then stamp the attempt label. " +
258
+ "If the re-derived fix still can't be proven green, escalate to needs-human instead of force-pushing a known-failing fix.",
259
+ zombie: false,
260
+ };
261
+ }
262
+
263
+ // base moved (the original staleness case; checks may also be red).
264
+ const threshold = input.staleAfterDays ?? DEFAULT_STALE_AFTER_DAYS;
265
+ const ageDays = input.ageDays;
266
+ const zombie = ageDays !== undefined && ageDays >= threshold;
267
+ const ageNote =
268
+ zombie && ageDays !== undefined
269
+ ? ` It has been open ${Math.floor(ageDays)} day(s) (≥ ${threshold}) with no human commits — a likely zombie; prioritize closing it once the concern is confirmed resolved.`
270
+ : "";
271
+ return {
272
+ status: "stale",
273
+ action: "refresh",
274
+ reason:
275
+ `the base advanced ${input.baseBehindBy} commit(s) since the PR was opened — re-scan on the current base, ` +
276
+ "then close the PR if the concern is already resolved or re-derive + force-update the fix otherwise." +
277
+ (checksFailing
278
+ ? " Its checks are also failing — re-prove the fix is green before force-updating."
279
+ : "") +
280
+ ageNote,
281
+ zombie,
282
+ };
283
+ }
284
+
285
+ /** Minimal shape of `octokit.rest.checks.listForRef` — typed as optional so a
286
+ * mock/older client without the Checks API degrades to `unknown` rather than
287
+ * throwing. */
288
+ type ChecksListForRef = (args: {
289
+ owner: string;
290
+ repo: string;
291
+ ref: string;
292
+ per_page?: number;
293
+ }) => Promise<{
294
+ data: { check_runs?: Array<{ status?: string; conclusion?: string | null }> };
295
+ }>;
296
+
297
+ /**
298
+ * Best-effort read of the latest CI conclusion on a PR head SHA via the Checks
299
+ * API, for self-healing a remediation PR whose own checks are red. A missing
300
+ * `sha`, an absent Checks API, or any read error returns `unknown` — a failed
301
+ * read must never manufacture a refresh.
302
+ */
303
+ async function readChecksState(
304
+ ctx: GitHubToolContext,
305
+ owner: string,
306
+ repo: string,
307
+ sha: string | undefined,
308
+ ): Promise<ChecksState> {
309
+ const listForRef = (
310
+ ctx.octokit.rest.checks as { listForRef?: ChecksListForRef } | undefined
311
+ )?.listForRef;
312
+ if (!sha || !listForRef) return "unknown";
313
+ try {
314
+ const res = await listForRef({ owner, repo, ref: sha, per_page: 100 });
315
+ const runs = res.data?.check_runs ?? [];
316
+ if (runs.length === 0) return "unknown";
317
+ if (runs.some((c) => c.status !== "completed")) return "pending";
318
+ if (runs.some((c) => FAILING_CONCLUSIONS.has(c.conclusion ?? "")))
319
+ return "failing";
320
+ return "passing";
321
+ } catch (e) {
322
+ log.info(
323
+ `» checks read skipped for ${sha.slice(0, 7)}: ${e instanceof Error ? e.message : e}`,
324
+ );
325
+ return "unknown";
326
+ }
327
+ }
328
+
329
+ interface AssessedRemediationPr {
330
+ number: number;
331
+ url: string;
332
+ title: string;
333
+ branch: string;
334
+ base: string;
335
+ group_id: string | null;
336
+ base_behind_by: number;
337
+ head_ahead_by: number;
338
+ has_non_bot_commits: boolean;
339
+ commit_authors: string[];
340
+ labels: string[];
341
+ checks_state: ChecksState;
342
+ /** self-heal attempts already recorded (from the accumulated attempt labels). The
343
+ * sweep stops auto-retrying a red PR once this hits the cap (default 3). */
344
+ self_heal_attempts: number;
345
+ age_days: number;
346
+ status: StaleFixStatus;
347
+ recommended_action: StaleFixAction;
348
+ zombie: boolean;
349
+ reason: string;
350
+ }
351
+
352
+ export const ListRemediationPrsParams = type({
353
+ "limit?": type.number.describe(
354
+ "max open remediation PRs to assess (default 30). Each is one compare-commits API call.",
355
+ ),
356
+ });
357
+
358
+ export function ListRemediationPrsTool(ctx: GitHubToolContext) {
359
+ return tool({
360
+ name: "list_remediation_prs",
361
+ description:
362
+ `List the repo's OPEN ${PRODUCT_NAME} remediation/generation PRs (branches \`remediate/<id>\` or ` +
363
+ "`infraweaver/generate-<slug>`) with their staleness, so a refresh sweep knows which to act on. For each " +
364
+ "PR it compares the head branch against its base and returns `base_behind_by` (how many commits the " +
365
+ "base advanced since the PR was opened), `head_ahead_by`, `has_non_bot_commits` (a human pushed to the " +
366
+ "branch), the `group_id`, labels, and a `recommended_action`: `skip` (still current), `refresh` (base " +
367
+ "moved — re-derive the fix on the current base, then close-if-resolved or force-update), or `escalate` " +
368
+ "(human-touched — label needs-human, don't overwrite). It also returns `age_days` and a `zombie` flag " +
369
+ "(stale + no human commits + open ≥ 30 days) so you can prioritize clearing long-rotting PRs. It also " +
370
+ "reports each PR's `checks_state` (passing/failing/pending/unknown): a PR whose OWN CI is failing (a red " +
371
+ "`terraform plan`, a failing module test, a policy gate) is recommended for `refresh` even when its base " +
372
+ "hasn't moved — self-heal — unless it's already labelled needs-human (then it's left for a human to avoid " +
373
+ "a loop). Self-heal is bounded: each PR carries `self_heal_attempts` (counted from its " +
374
+ "`infraweaver-self-heal-attempt-N` labels) and after the cap (3) a still-red PR is recommended for `escalate`, " +
375
+ "not `refresh`, so it can't loop forever. Read-only. Use it as the first step of RefreshRemediation.",
376
+ parameters: ListRemediationPrsParams,
377
+ execute: execute(async ({ limit }) => {
378
+ const cap = limit ?? 30;
379
+ const owner = ctx.repo.owner;
380
+ const repo = ctx.repo.name;
381
+
382
+ const open = await ctx.octokit.paginate(ctx.octokit.rest.pulls.list, {
383
+ owner,
384
+ repo,
385
+ state: "open",
386
+ per_page: 100,
387
+ });
388
+ const remediation = open
389
+ .filter((pr) => isRemediationBranch(pr.head.ref))
390
+ .slice(0, cap);
391
+
392
+ const prs: AssessedRemediationPr[] = [];
393
+ const errors: { number: number; error: string }[] = [];
394
+ for (const pr of remediation) {
395
+ try {
396
+ // base..head: ahead_by = the PR's own commits, behind_by = commits on
397
+ // the base the PR lacks (how far the base moved since the fork point).
398
+ const cmp = await ctx.octokit.rest.repos.compareCommits({
399
+ owner,
400
+ repo,
401
+ base: pr.base.ref,
402
+ head: pr.head.ref,
403
+ });
404
+ const commitAuthors = [
405
+ ...new Set(
406
+ (cmp.data.commits ?? [])
407
+ .map((c) => c.author?.login ?? c.commit.author?.name ?? null)
408
+ .filter((a): a is string => a !== null),
409
+ ),
410
+ ];
411
+ // resolve each commit via login when present, else its git identity —
412
+ // a human commit whose email isn't linked to a GitHub account (null
413
+ // login) must NOT be mistaken for Infraweaver's bot and force-overwritten.
414
+ const hasNonBotCommits = (cmp.data.commits ?? []).some(
415
+ (c) => !isBotCommit(c),
416
+ );
417
+ const labels = pr.labels
418
+ .map((l) => (typeof l === "string" ? l : l.name))
419
+ .filter(Boolean);
420
+ const checksState = await readChecksState(
421
+ ctx,
422
+ owner,
423
+ repo,
424
+ pr.head.sha,
425
+ );
426
+ const attempts = selfHealAttempts(labels);
427
+ const createdMs = new Date(pr.created_at).getTime();
428
+ const ageDays = Number.isFinite(createdMs)
429
+ ? (Date.now() - createdMs) / 86_400_000
430
+ : 0;
431
+ const assessment = assessStaleFix({
432
+ baseBehindBy: cmp.data.behind_by,
433
+ hasNonBotCommits,
434
+ checksFailing: checksState === "failing",
435
+ alreadyEscalated: labels.includes("needs-human"),
436
+ selfHealAttempts: attempts,
437
+ ageDays,
438
+ });
439
+ prs.push({
440
+ number: pr.number,
441
+ url: pr.html_url,
442
+ title: pr.title,
443
+ branch: pr.head.ref,
444
+ base: pr.base.ref,
445
+ group_id: groupIdFromBranch(pr.head.ref),
446
+ base_behind_by: cmp.data.behind_by,
447
+ head_ahead_by: cmp.data.ahead_by,
448
+ has_non_bot_commits: hasNonBotCommits,
449
+ commit_authors: commitAuthors,
450
+ labels,
451
+ checks_state: checksState,
452
+ self_heal_attempts: attempts,
453
+ age_days: Math.round(ageDays),
454
+ status: assessment.status,
455
+ recommended_action: assessment.action,
456
+ zombie: assessment.zombie,
457
+ reason: assessment.reason,
458
+ });
459
+ } catch (e) {
460
+ errors.push({
461
+ number: pr.number,
462
+ error: e instanceof Error ? e.message : String(e),
463
+ });
464
+ }
465
+ }
466
+
467
+ const counts = {
468
+ refresh: prs.filter((p) => p.recommended_action === "refresh").length,
469
+ skip: prs.filter((p) => p.recommended_action === "skip").length,
470
+ escalate: prs.filter((p) => p.recommended_action === "escalate").length,
471
+ checks_failing: prs.filter((p) => p.checks_state === "failing").length,
472
+ zombie: prs.filter((p) => p.zombie).length,
473
+ };
474
+ log.info(
475
+ `» list_remediation_prs: ${prs.length} open remediation PR(s) — ` +
476
+ `${counts.refresh} refresh, ${counts.skip} skip, ${counts.escalate} escalate` +
477
+ (counts.checks_failing
478
+ ? `, ${counts.checks_failing} checks-failing`
479
+ : "") +
480
+ (counts.zombie ? `, ${counts.zombie} zombie` : "") +
481
+ (errors.length ? ` (${errors.length} errored)` : ""),
482
+ );
483
+ return toolOk({
484
+ count: prs.length,
485
+ action_counts: counts,
486
+ pull_requests: prs,
487
+ ...(errors.length ? { errors } : {}),
488
+ note:
489
+ prs.length === 0
490
+ ? `No open ${PRODUCT_NAME} remediation PRs to refresh.`
491
+ : "Act on the `refresh` PRs (re-derive on the current base, then close-if-resolved or force-update — a `checks_failing` PR is refreshed to self-heal its red CI: re-prove the fix is green, else escalate to needs-human); `escalate` PRs get a needs-human label and are left for a human; `skip` PRs are already current.",
492
+ });
493
+ }),
494
+ });
495
+ }
496
+
497
+ export const ClosePullRequest = type({
498
+ pull_number: type.number.describe(
499
+ "the pull request number to close (not merge).",
500
+ ),
501
+ "comment?": type.string.describe(
502
+ "an optional comment explaining why it's being closed — posted before closing (e.g. 'concern already resolved on the base; this fix is now redundant').",
503
+ ),
504
+ });
505
+
506
+ export function ClosePullRequestTool(ctx: GitHubToolContext) {
507
+ return tool({
508
+ mutates: true,
509
+ name: "close_pull_request",
510
+ description:
511
+ `Close (NOT merge) a pull request. Use: a ${PRODUCT_NAME} remediation PR whose concern is already ` +
512
+ "resolved on the current base — the fix is redundant, so close it (optionally with an explanatory " +
513
+ "comment) instead of leaving a stale PR open. Blocked under `push: disabled`, and bound to the run's " +
514
+ "scope (a comment-triggered run can only close the PR it was triggered on or one it opened; a " +
515
+ "standalone scheduled sweep may close any). Never merges — closing is reversible.",
516
+ parameters: ClosePullRequest,
517
+ execute: execute(async ({ pull_number, comment }) => {
518
+ // permission gate: closing a PR is a repo write — block it under read-only.
519
+ if (ctx.payload.push === "disabled") {
520
+ throw new Error(
521
+ "Closing a pull request is disabled. This repository is configured for read-only access (push: disabled).",
522
+ );
523
+ }
524
+ // scope gate: same binding as comment/label/PR-body writes (mcp/scope.ts).
525
+ assertTargetInScope(ctx, pull_number, "close");
526
+
527
+ const owner = ctx.repo.owner;
528
+ const repo = ctx.repo.name;
529
+
530
+ if (comment) {
531
+ await ctx.octokit.rest.issues.createComment({
532
+ owner,
533
+ repo,
534
+ issue_number: pull_number,
535
+ body: addFooter(ctx, comment),
536
+ });
537
+ log.info(`» commented before closing PR #${pull_number}`);
538
+ }
539
+
540
+ const result = await ctx.octokit.rest.pulls.update({
541
+ owner,
542
+ repo,
543
+ pull_number,
544
+ state: "closed",
545
+ });
546
+ ctx.toolState.wasUpdated = true;
547
+ log.info(`» closed pull request #${result.data.number}`);
548
+
549
+ return {
550
+ success: true,
551
+ number: result.data.number,
552
+ state: result.data.state,
553
+ url: result.data.html_url,
554
+ };
555
+ }),
556
+ });
557
+ }
@@ -0,0 +1,40 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`buildModuleReadme > is deterministic — identical inputs produce byte-identical output (snapshot) 1`] = `
4
+ "# \`logging\` module
5
+
6
+ Evidence-backed documentation generated by Infraweaver from the module's parsed interface and the refactor's recorded proof. Descriptions are quoted verbatim from the HCL (empty when the HCL has none).
7
+
8
+ ## Interface
9
+
10
+ ### Inputs
11
+
12
+ | Name | Type | Required | Description |
13
+ | ---- | ---- | :------: | ----------- |
14
+ | \`bucket_name\` | \`string\` | **yes** | Name of the log bucket |
15
+ | \`retention_days\` | \`number\` | no | |
16
+
17
+ ### Outputs
18
+
19
+ | Name | Description |
20
+ | ---- | ----------- |
21
+ | \`bucket_arn\` | ARN of the created bucket |
22
+
23
+ ## Refactor provenance
24
+
25
+ Verdict: \`equivalence-proven\`
26
+
27
+ The relocation was **proven behaviour-preserving** by \`terraform_equivalence_check\`: same resource set, identical arguments, every relocated address covered by a \`moved {}\` block, validate + fmt clean.
28
+
29
+ Relocated addresses:
30
+
31
+ - \`aws_s3_bucket.logs\` → \`module.logging.aws_s3_bucket.logs\`
32
+
33
+ <!--
34
+ generated-by: infraweaver
35
+ infraweaver-version: 0.3.0
36
+ git-head: abc1234def5678
37
+ diagram: placeholder — link a Infragram-rendered architecture diagram of this module here
38
+ -->
39
+ "
40
+ `;
@@ -0,0 +1,38 @@
1
+ // Generated by `pnpm run gen:azure-prices` — do not edit by hand.
2
+ // Public list prices from the unauthenticated Azure Retail Prices API
3
+ // (https://prices.azure.com), pinned so scan-time is offline with zero egress.
4
+ // Every entry is a FIXED hourly charge that accrues at zero traffic — the
5
+ // deterministic "idle floor". Usage-metered lines are deliberately not here.
6
+
7
+ /** Region these list prices were captured for. */
8
+ export const AZURE_PRICE_REGION = "westeurope";
9
+ /** Currency of every price below. */
10
+ export const AZURE_PRICE_CURRENCY = "EUR";
11
+ /** ISO date the snapshot was captured — surfaced in cost.md so staleness is visible. */
12
+ export const AZURE_PRICE_FETCHED_AT = "2026-07-25";
13
+
14
+ /** meter key → list price per HOUR. */
15
+ export const AZURE_HOURLY_PRICES: Record<string, number> = {
16
+ "app-service-p0v3": 0.0781,
17
+ "app-service-p1v3": 0.1562,
18
+ "app-service-p2v3": 0.3124,
19
+ "app-service-p3v3": 0.6248,
20
+ "bastion-basic": 0.1667,
21
+ "bastion-premium": 0.3949,
22
+ "bastion-standard": 0.2545,
23
+ "firewall-basic": 0.3466,
24
+ "firewall-premium": 1.5358,
25
+ "firewall-standard": 1.097,
26
+ "redis-c0": 0.0241,
27
+ "redis-c1": 0.0606,
28
+ "redis-c2": 0.0983,
29
+ "redis-c3": 0.1975,
30
+ "redis-p1": 0.2431,
31
+ "redis-p2": 0.4871,
32
+ "redis-p3": 0.9732,
33
+ "vpn-gateway-vpngw1": 0.1667,
34
+ "vpn-gateway-vpngw2": 0.43,
35
+ "vpn-gateway-vpngw3": 1.097,
36
+ "vpn-gateway-vpngw4": 1.8429,
37
+ "vpn-gateway-vpngw5": 3.2032,
38
+ };