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,474 @@
1
+ import { type ChildProcess, spawn as nodeSpawn } from "node:child_process";
2
+ import { performance } from "node:perf_hooks";
3
+ import {
4
+ DEFAULT_ACTIVITY_CHECK_INTERVAL_MS,
5
+ DEFAULT_ACTIVITY_TIMEOUT_MS,
6
+ } from "#app/utils/activity";
7
+ import { log } from "#app/utils/cli";
8
+ import { onExitSignal } from "#app/utils/exitHandler";
9
+
10
+ export type TrackChildOptions = {
11
+ child: ChildProcess;
12
+ // if true, kill the entire process group (requires detached spawn)
13
+ killGroup?: boolean;
14
+ };
15
+
16
+ // sentinel codes for timeout rejections — callers (e.g. lifecycle.ts) use
17
+ // these to distinguish timeouts from other errors without string-matching
18
+ // on the error message, which is fragile to rewording.
19
+ export const SPAWN_TIMEOUT_CODE = "E_SPAWN_TIMEOUT";
20
+ export const SPAWN_ACTIVITY_TIMEOUT_CODE = "E_SPAWN_ACTIVITY_TIMEOUT";
21
+
22
+ export class SpawnTimeoutError extends Error {
23
+ readonly code: typeof SPAWN_TIMEOUT_CODE | typeof SPAWN_ACTIVITY_TIMEOUT_CODE;
24
+ constructor(
25
+ message: string,
26
+ code: typeof SPAWN_TIMEOUT_CODE | typeof SPAWN_ACTIVITY_TIMEOUT_CODE,
27
+ ) {
28
+ super(message);
29
+ this.name = "SpawnTimeoutError";
30
+ this.code = code;
31
+ }
32
+ }
33
+
34
+ // track all spawned child processes for cleanup on Ctrl+C
35
+ const activeChildren = new Map<ChildProcess, boolean>();
36
+
37
+ // signal handler override (used by test runner for graceful shutdown)
38
+ export type SignalHandler = (signal: NodeJS.Signals) => void;
39
+ let externalSignalHandler: SignalHandler | null = null;
40
+
41
+ // track a child process for cleanup on Ctrl+C
42
+ export function trackChild(options: TrackChildOptions): void {
43
+ // the signal handler cleans up all tracked children
44
+ // so we only have to install it once some child gets tracked
45
+ installSignalHandler();
46
+ activeChildren.set(options.child, options.killGroup ?? false);
47
+ }
48
+
49
+ // untrack a child process
50
+ export function untrackChild(child: ChildProcess): void {
51
+ activeChildren.delete(child);
52
+ }
53
+
54
+ // allow callers to override default signal handling
55
+ export function setSignalHandler(handler: SignalHandler | null): void {
56
+ externalSignalHandler = handler;
57
+ }
58
+
59
+ // kill all tracked children without exiting
60
+ export function killTrackedChildren() {
61
+ for (const entry of activeChildren) {
62
+ const child = entry[0];
63
+ const killGroup = entry[1];
64
+ if (killGroup && child.pid) {
65
+ try {
66
+ process.kill(-child.pid, "SIGKILL");
67
+ continue;
68
+ } catch {
69
+ // fall through to direct kill
70
+ }
71
+ }
72
+ child.kill("SIGKILL");
73
+ }
74
+ }
75
+
76
+ // install signal handlers once (call early in process lifecycle)
77
+ let handlersInstalled = false;
78
+ function installSignalHandler(): void {
79
+ if (handlersInstalled) return;
80
+ handlersInstalled = true;
81
+ onExitSignal((signal) => {
82
+ if (externalSignalHandler) {
83
+ externalSignalHandler(signal);
84
+ return;
85
+ }
86
+ const count = activeChildren.size;
87
+ if (count > 0) {
88
+ log.info(`» received ${signal}, killing ${count} subprocess(es)...`);
89
+ }
90
+ killTrackedChildren();
91
+ });
92
+ }
93
+
94
+ /**
95
+ * Controls what the wrapper retains in memory across the child's lifetime
96
+ * for the post-hoc `SpawnResult.stdout` / `SpawnResult.stderr` snapshots.
97
+ *
98
+ * Streaming callbacks (`onStdout` / `onStderr`) fire regardless — `retain`
99
+ * only governs the buffered snapshot returned in `SpawnResult`.
100
+ *
101
+ * - `"tail"` (default): keep the last `maxRetainedBytes` UTF-16 code units
102
+ * of each stream. Once the cap is exceeded, oldest bytes are sliced off
103
+ * and the result is prefixed with a `... [N MiB truncated] ...` sentinel.
104
+ * Right default for short-lived commands whose failure mode is in their
105
+ * final output (git errors, install failures, hook scripts).
106
+ * - `"none"`: skip the buffer entirely. `SpawnResult.stdout` / `.stderr`
107
+ * are empty strings. Use this for long-lived streaming agents that already
108
+ * drain via `onStdout` / `onStderr` and never read the buffered snapshot.
109
+ *
110
+ * Default cap is 8 MiB — well below V8's ~1 GiB `kMaxLength` so `+= chunk`
111
+ * can never throw `RangeError: Invalid string length`.
112
+ */
113
+ export type RetainMode = "tail" | "none";
114
+
115
+ export const DEFAULT_MAX_RETAINED_BYTES = 8 * 1024 * 1024;
116
+
117
+ export interface SpawnOptions {
118
+ cmd: string;
119
+ args: string[];
120
+ env?: NodeJS.ProcessEnv;
121
+ input?: string;
122
+ timeout?: number;
123
+ // activity timeout: kill process if no stdout for this many ms (default:
124
+ // DEFAULT_ACTIVITY_TIMEOUT_MS, 0 to disable). only stdout resets the timer —
125
+ // stderr (e.g. provider error retries) does not count as progress.
126
+ activityTimeout?: number;
127
+ // fired synchronously when the activity timeout kills the process. used by
128
+ // callers (main.ts) to tear down shared resources like the MCP HTTP server
129
+ // so that lingering SSE reconnects don't keep the outer activity timer
130
+ // alive after the subprocess is already dead.
131
+ onActivityTimeout?: (() => void) | undefined;
132
+ // kill the process (group) when this signal aborts. the activity timer bounds
133
+ // IDLENESS; this is the seam for bounding anything else the caller can only
134
+ // observe from outside — today the per-run token quota (LLM10), whose whole
135
+ // point is that a runaway loop is never idle. `reason` (an Error) becomes the
136
+ // spawn failure message, so the caller's diagnosis reaches the operator
137
+ // instead of a bare "killed by signal".
138
+ abortSignal?: AbortSignal | undefined;
139
+ cwd?: string;
140
+ stdio?: ("pipe" | "ignore" | "inherit")[];
141
+ onStdout?: (chunk: string) => void;
142
+ onStderr?: (chunk: string) => void;
143
+ // when true, spawn the child detached (its own process group) and route all
144
+ // kill paths (timeout, activity timeout, ctrl-c) through `process.kill(-pid, ...)`
145
+ // so signals reach grandchildren too. critical for binaries that fork through
146
+ // a shim (e.g. node_modules/opencode-ai/bin/opencode is a Node shim that
147
+ // spawnSync's the native binary; without killGroup, SIGKILL only hits the
148
+ // shim and the native binary is reparented to PID 1, holds our stdout pipe
149
+ // open, keeps emitting NDJSON, and `child.on("close")` never fires —
150
+ // producing zombie runs that hang until the GitHub Actions job timeout).
151
+ killGroup?: boolean;
152
+ retain?: RetainMode;
153
+ maxRetainedBytes?: number;
154
+ }
155
+
156
+ /**
157
+ * Bounded string accumulator that keeps the tail of appended chunks.
158
+ * Once the cap is exceeded, oldest bytes are sliced off and `toString()`
159
+ * prefixes the survivors with a sentinel describing the elided byte count.
160
+ *
161
+ * Exported because long-lived agent runtimes (opencode, claude) also
162
+ * accumulate per-run narration strings independently of the spawn wrapper
163
+ * and need the same protection against V8's `kMaxLength`.
164
+ */
165
+ export class TailBuffer {
166
+ // explicit field declarations rather than constructor parameter properties:
167
+ // node's strip-only TS loader (used by action/test/run.ts in CI) rejects
168
+ // `constructor(private readonly cap: number)` with ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX.
169
+ private readonly cap: number;
170
+ private buffer = "";
171
+ private truncatedBytes = 0;
172
+
173
+ constructor(cap: number) {
174
+ this.cap = cap;
175
+ }
176
+
177
+ append(chunk: string): void {
178
+ if (this.cap <= 0) return;
179
+ this.buffer += chunk;
180
+ if (this.buffer.length > this.cap) {
181
+ const drop = this.buffer.length - this.cap;
182
+ this.truncatedBytes += drop;
183
+ this.buffer = this.buffer.slice(drop);
184
+ }
185
+ }
186
+
187
+ toString(): string {
188
+ if (this.truncatedBytes === 0) return this.buffer;
189
+ const mib = (this.truncatedBytes / 1024 / 1024).toFixed(1);
190
+ return `... [${mib} MiB truncated by retain:tail cap] ...\n${this.buffer}`;
191
+ }
192
+ }
193
+
194
+ export interface SpawnResult {
195
+ stdout: string;
196
+ stderr: string;
197
+ exitCode: number;
198
+ durationMs: number;
199
+ }
200
+
201
+ /**
202
+ * Spawn a subprocess with streaming callbacks and buffered results
203
+ */
204
+ export async function spawn(options: SpawnOptions): Promise<SpawnResult> {
205
+ const activityTimeoutMs = options.activityTimeout ?? DEFAULT_ACTIVITY_TIMEOUT_MS;
206
+
207
+ installSignalHandler();
208
+
209
+ const startTime = performance.now();
210
+ // capped accumulators — unbounded `+= chunk` previously crashed the wrapper
211
+ // with `RangeError: Invalid string length` once V8's ~1 GiB kMaxLength was
212
+ // breached on long-lived agent subprocesses (e.g. multi-lens opencode
213
+ // Reviews on large monorepos). retain:"none" skips the buffer entirely
214
+ // for callers that already drain via onStdout/onStderr.
215
+ const retain: RetainMode = options.retain ?? "tail";
216
+ const cap = options.maxRetainedBytes ?? DEFAULT_MAX_RETAINED_BYTES;
217
+ const stdoutBuffer = retain === "none" ? null : new TailBuffer(cap);
218
+ const stderrBuffer = retain === "none" ? null : new TailBuffer(cap);
219
+
220
+ const killGroup = options.killGroup ?? false;
221
+
222
+ return new Promise((resolve, reject) => {
223
+ // security: caller must provide complete env object, not merged with process.env
224
+ const child = nodeSpawn(options.cmd, options.args, {
225
+ env: options.env || {
226
+ PATH: process.env.PATH || "",
227
+ HOME: process.env.HOME || "",
228
+ },
229
+ stdio: options.stdio || ["pipe", "pipe", "pipe"],
230
+ cwd: options.cwd || process.cwd(),
231
+ detached: killGroup,
232
+ });
233
+
234
+ // sends `signal` to the entire process group when killGroup is set, so
235
+ // grandchildren (e.g. the native opencode binary spawned by the
236
+ // opencode-ai Node shim) die with the parent. falls back to a direct
237
+ // child kill if the process-group send fails (common when the child
238
+ // already exited or was never made a process group leader).
239
+ const killSelf = (signal: NodeJS.Signals): void => {
240
+ if (killGroup && child.pid) {
241
+ try {
242
+ process.kill(-child.pid, signal);
243
+ return;
244
+ } catch {
245
+ // fall through to direct kill
246
+ }
247
+ }
248
+ child.kill(signal);
249
+ };
250
+
251
+ // track child for cleanup on Ctrl+C
252
+ trackChild({ child, killGroup });
253
+
254
+ let timeoutId: NodeJS.Timeout | undefined;
255
+ let sigkillEscalatorId: NodeJS.Timeout | undefined;
256
+ let activityCheckIntervalId: NodeJS.Timeout | undefined;
257
+ let isTimedOut = false;
258
+ let isActivityTimedOut = false;
259
+ let lastActivityTime = performance.now();
260
+ // idle-ms snapshot taken at the moment the activity timer decides to kill.
261
+ // we reuse it when composing the SpawnTimeoutError so a final stdout chunk
262
+ // that races with `close` (and resets lastActivityTime via updateActivity)
263
+ // can't make the error message contradict the "no output for Ns" log line.
264
+ let killedAtIdleMs: number | undefined;
265
+
266
+ // overall timeout
267
+ if (options.timeout) {
268
+ timeoutId = setTimeout(() => {
269
+ isTimedOut = true;
270
+ killSelf("SIGTERM");
271
+
272
+ // track the escalator so a graceful SIGTERM response (close fires
273
+ // before the 5s elapses) can clear it. without capture, this timer
274
+ // was orphaned in the event loop and kept node alive for up to 5s
275
+ // past a timed-out subprocess's clean exit.
276
+ sigkillEscalatorId = setTimeout(() => {
277
+ if (!child.killed) {
278
+ killSelf("SIGKILL");
279
+ }
280
+ }, 5000);
281
+ }, options.timeout);
282
+ }
283
+
284
+ // activity timeout: kill if no output for too long
285
+ if (activityTimeoutMs > 0) {
286
+ log.debug(
287
+ `spawn activity timer: pid=${child.pid} cmd=${options.cmd} timeout=${activityTimeoutMs}ms`,
288
+ );
289
+ activityCheckIntervalId = setInterval(() => {
290
+ const idleMs = performance.now() - lastActivityTime;
291
+ log.debug(
292
+ `spawn activity check: pid=${child.pid} idle=${Math.round(idleMs)}ms / ${activityTimeoutMs}ms`,
293
+ );
294
+ if (idleMs > activityTimeoutMs) {
295
+ isActivityTimedOut = true;
296
+ killedAtIdleMs = idleMs;
297
+ const idleSec = Math.round(idleMs / 1000);
298
+ log.info(
299
+ `no output for ${idleSec}s from pid=${child.pid} (${options.cmd}), killing process${killGroup ? " group" : ""}`,
300
+ );
301
+ killSelf("SIGKILL");
302
+ clearInterval(activityCheckIntervalId);
303
+ try {
304
+ options.onActivityTimeout?.();
305
+ } catch (err) {
306
+ log.debug(
307
+ `spawn onActivityTimeout handler threw: ${err instanceof Error ? err.message : String(err)}`,
308
+ );
309
+ }
310
+ }
311
+ }, DEFAULT_ACTIVITY_CHECK_INTERVAL_MS);
312
+ }
313
+
314
+ // caller-driven abort (today: the per-run token quota). SIGKILL rather than
315
+ // SIGTERM because the reasons for aborting here are ones where letting the
316
+ // child keep working is the failure — a graceful window would be more of
317
+ // exactly what we are stopping.
318
+ let abortReason: Error | undefined;
319
+ const onAbort = () => {
320
+ if (abortReason) return;
321
+ const reason = options.abortSignal?.reason;
322
+ abortReason = reason instanceof Error ? reason : new Error(String(reason ?? "aborted"));
323
+ log.info(
324
+ `aborting pid=${child.pid} (${options.cmd})${killGroup ? " group" : ""}: ${abortReason.message}`,
325
+ );
326
+ killSelf("SIGKILL");
327
+ };
328
+ if (options.abortSignal) {
329
+ if (options.abortSignal.aborted) onAbort();
330
+ else options.abortSignal.addEventListener("abort", onAbort, { once: true });
331
+ }
332
+
333
+ function updateActivity(): void {
334
+ lastActivityTime = performance.now();
335
+ }
336
+
337
+ // wrap handlers in try/catch as defense in depth for synchronous throws
338
+ // inside the listener body. the historical `+= chunk` RangeError was such
339
+ // a throw — synchronous and fatal under node's default uncaught-exception
340
+ // policy. with the TailBuffer cap in place the wrapper-side `append` can
341
+ // no longer throw, but the catch keeps protecting against any future
342
+ // synchronous regression in this path.
343
+ //
344
+ // note: this does NOT catch rejections from async user callbacks —
345
+ // `options.onStdout?.(chunk)` returns a Promise in the agent callers
346
+ // (claude.ts, opencode.ts) and a throw inside an async callback surfaces
347
+ // as an unhandled Promise rejection, not a synchronous exception. agent
348
+ // callers handle their own NDJSON-parse failures internally; the
349
+ // synchronous protection here is what matters for the RangeError class
350
+ // of bugs (issue #680).
351
+ if (child.stdout) {
352
+ child.stdout.on("data", (data: Buffer) => {
353
+ try {
354
+ updateActivity();
355
+ const chunk = data.toString();
356
+ stdoutBuffer?.append(chunk);
357
+ options.onStdout?.(chunk);
358
+ } catch (err) {
359
+ log.debug(
360
+ `spawn stdout handler threw: ${err instanceof Error ? err.message : String(err)}`,
361
+ );
362
+ }
363
+ });
364
+ }
365
+
366
+ if (child.stderr) {
367
+ child.stderr.on("data", (data: Buffer) => {
368
+ try {
369
+ const chunk = data.toString();
370
+ stderrBuffer?.append(chunk);
371
+ options.onStderr?.(chunk);
372
+ } catch (err) {
373
+ log.debug(
374
+ `spawn stderr handler threw: ${err instanceof Error ? err.message : String(err)}`,
375
+ );
376
+ }
377
+ });
378
+ }
379
+
380
+ child.on("close", (exitCode, signal) => {
381
+ const durationMs = performance.now() - startTime;
382
+
383
+ untrackChild(child);
384
+ if (timeoutId) clearTimeout(timeoutId);
385
+ if (sigkillEscalatorId) clearTimeout(sigkillEscalatorId);
386
+ if (activityCheckIntervalId) clearInterval(activityCheckIntervalId);
387
+ options.abortSignal?.removeEventListener("abort", onAbort);
388
+
389
+ // checked before the timeout branches: an abort kills the child, which
390
+ // then stops producing output, so a slow close would otherwise be
391
+ // reported as an activity timeout and bury the real reason.
392
+ if (abortReason) {
393
+ reject(abortReason);
394
+ return;
395
+ }
396
+
397
+ if (isTimedOut) {
398
+ reject(
399
+ new SpawnTimeoutError(`process timed out after ${options.timeout}ms`, SPAWN_TIMEOUT_CODE),
400
+ );
401
+ return;
402
+ }
403
+
404
+ if (isActivityTimedOut) {
405
+ // prefer the idle-ms captured when the kill fired (killedAtIdleMs).
406
+ // recomputing from lastActivityTime here would be wrong if the child
407
+ // emitted one final stdout chunk between SIGKILL and close — the
408
+ // chunk's updateActivity() would reset lastActivityTime and the error
409
+ // would report near-zero idle, contradicting the kill-site log line.
410
+ const idleMs = killedAtIdleMs ?? performance.now() - lastActivityTime;
411
+ const idleSec = Math.round(idleMs / 1000);
412
+ reject(
413
+ new SpawnTimeoutError(
414
+ `activity timeout: no output for ${idleSec}s`,
415
+ SPAWN_ACTIVITY_TIMEOUT_CODE,
416
+ ),
417
+ );
418
+ return;
419
+ }
420
+
421
+ // when a child is killed by signal (OOM, segfault, external SIGTERM),
422
+ // node delivers (code=null, signal=<name>). without this branch,
423
+ // `exitCode || 0` coerced null to 0 and lifecycle hooks silently
424
+ // appeared to succeed when they'd actually been killed — caller
425
+ // checked `result.exitCode !== 0` and moved on.
426
+ let resolvedExitCode = exitCode ?? 0;
427
+ let resolvedStderr = stderrBuffer?.toString() ?? "";
428
+ if (exitCode === null && signal) {
429
+ const killMsg = `[spawn] ${options.cmd}: killed by signal ${signal}`;
430
+ resolvedStderr = resolvedStderr ? `${resolvedStderr}\n${killMsg}` : killMsg;
431
+ resolvedExitCode = 1;
432
+ }
433
+
434
+ resolve({
435
+ stdout: stdoutBuffer?.toString() ?? "",
436
+ stderr: resolvedStderr,
437
+ exitCode: resolvedExitCode,
438
+ durationMs,
439
+ });
440
+ });
441
+
442
+ child.on("error", (error) => {
443
+ const durationMs = performance.now() - startTime;
444
+
445
+ untrackChild(child);
446
+ if (timeoutId) clearTimeout(timeoutId);
447
+ if (sigkillEscalatorId) clearTimeout(sigkillEscalatorId);
448
+ if (activityCheckIntervalId) clearInterval(activityCheckIntervalId);
449
+ options.abortSignal?.removeEventListener("abort", onAbort);
450
+
451
+ // surface the spawn error in stderr so callers (e.g. lifecycle hook
452
+ // warnings) don't just see "exit code 1, output: (empty)" when the
453
+ // command was misspelled, missing, or unexecutable. without this a
454
+ // user with a bad postCheckout script got an opaque failure, retried
455
+ // per the guidance, and hit the same wall every run.
456
+ const errMsg = `[spawn] ${options.cmd}: ${error.message}`;
457
+ console.error(errMsg);
458
+ const existingStderr = stderrBuffer?.toString() ?? "";
459
+ const finalStderr = existingStderr ? `${existingStderr}\n${errMsg}` : errMsg;
460
+
461
+ resolve({
462
+ stdout: stdoutBuffer?.toString() ?? "",
463
+ stderr: finalStderr,
464
+ exitCode: 1,
465
+ durationMs,
466
+ });
467
+ });
468
+
469
+ if (options.input && child.stdin && options.stdio?.[0] !== "ignore") {
470
+ child.stdin.write(options.input);
471
+ child.stdin.end();
472
+ }
473
+ });
474
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * HashiCorp terraform-mcp-server integration.
3
+ *
4
+ * When the `terraform_mcp` input is on, the agent harness registers a SECOND
5
+ * MCP server next to infraweaver's: HashiCorp's terraform-mcp-server, run as a
6
+ * Docker container over stdio. It gives the fixing agent live Terraform
7
+ * Registry knowledge — current module versions, provider argument shapes —
8
+ * which directly powers module-source-aware fixes and generation.
9
+ *
10
+ * Security posture:
11
+ * - the image is VERSION+DIGEST-PINNED (`TERRAFORM_MCP_IMAGE`): the `:0.5.2`
12
+ * tag is human-readable, the `@sha256:` digest is the authoritative pin, so
13
+ * a re-tagged or compromised `0.5.2` upstream can never be pulled. Bump both
14
+ * deliberately (resolve the new digest with `docker buildx imagetools inspect
15
+ * hashicorp/terraform-mcp-server:<ver>` or the Docker Hub tags API).
16
+ * - only the read-only `registry` toolset is enabled — no TFE operations,
17
+ * and no TFE_TOKEN is ever passed.
18
+ * - degrades green: docker absent → a log note, never a failed run.
19
+ */
20
+
21
+ import { spawnSync } from "node:child_process";
22
+ import {
23
+ resolveToolSelection,
24
+ type ToolSelectionFlags,
25
+ } from "#app/utils/toolSelection";
26
+
27
+ /** pinned release of hashicorp/terraform-mcp-server — tag for humans, digest for
28
+ * integrity (the multi-arch index digest of `0.5.2`). Bump both deliberately. */
29
+ export const TERRAFORM_MCP_IMAGE =
30
+ "hashicorp/terraform-mcp-server:0.5.2@sha256:67b4d3d39aa2c32c5ee280b21d67fb6513dab463af6c93c2ae0cdd4af09d4603";
31
+
32
+ /** the registry name the server is registered under in agent MCP configs —
33
+ * matches HashiCorp's own client-config examples, so agent guidance written
34
+ * against "the terraform MCP server" finds it under the expected key. */
35
+ export const TERRAFORM_MCP_SERVER_NAME = "terraform";
36
+
37
+ /** stdio invocation, registry toolset ONLY (module/provider knowledge — the
38
+ * agent must never get TFE workspace operations from this surface). */
39
+ const TERRAFORM_MCP_DOCKER_ARGS = [
40
+ "run",
41
+ "-i",
42
+ "--rm",
43
+ TERRAFORM_MCP_IMAGE,
44
+ "--toolsets=registry",
45
+ ] as const;
46
+
47
+ export type TerraformMcpResolution =
48
+ | { kind: "disabled" }
49
+ | { kind: "docker_missing"; note: string }
50
+ | { kind: "available"; command: "docker"; args: string[] };
51
+
52
+ let cachedDockerAvailable: boolean | undefined;
53
+
54
+ /** test hook — the docker probe is cached per process. */
55
+ export function _clearDockerProbeCache(): void {
56
+ cachedDockerAvailable = undefined;
57
+ }
58
+
59
+ function dockerAvailable(): boolean {
60
+ if (cachedDockerAvailable === undefined) {
61
+ const probe = spawnSync("docker", ["--version"], {
62
+ stdio: "pipe",
63
+ encoding: "utf-8",
64
+ timeout: 10_000,
65
+ });
66
+ cachedDockerAvailable = !probe.error && probe.status === 0;
67
+ }
68
+ return cachedDockerAvailable;
69
+ }
70
+
71
+ /**
72
+ * Decide whether the run gets the terraform-mcp-server, as a discriminated
73
+ * union so each harness handles all three outcomes explicitly: register the
74
+ * server (`available`), log the degrade-green note (`docker_missing`), or do
75
+ * nothing (`disabled`).
76
+ */
77
+ export function resolveTerraformMcp(
78
+ payload: ToolSelectionFlags,
79
+ ): TerraformMcpResolution {
80
+ // terraform-mcp-server is licence-gated (HashiCorp): on via the
81
+ // `terraform_mcp` input OR by naming "terraform_mcp" in tools_enabled; an
82
+ // explicit `-terraform_mcp` there turns it off.
83
+ if (!resolveToolSelection(payload).enabled("terraform_mcp"))
84
+ return { kind: "disabled" };
85
+ if (!dockerAvailable()) {
86
+ return {
87
+ kind: "docker_missing",
88
+ note:
89
+ "terraform_mcp requested but docker is not available on this runner — " +
90
+ "continuing without Terraform Registry MCP (module/provider knowledge falls " +
91
+ "back to the registry HTTP lookups in terraform_version_currency)",
92
+ };
93
+ }
94
+ return {
95
+ kind: "available",
96
+ command: "docker",
97
+ args: [...TERRAFORM_MCP_DOCKER_ARGS],
98
+ };
99
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * time string parsing utilities for timeout configuration.
3
+ * supports formats like "10m", "1h30m", "10m12s", "30s".
4
+ */
5
+
6
+ // special value indicating timeout is explicitly disabled via --notimeout flag
7
+ export const TIMEOUT_DISABLED = "none";
8
+
9
+ // time string regex: supports formats like "10m", "1h30m", "10m12s", "30s"
10
+ // at least one component (hours, minutes, or seconds) is required
11
+ const TIME_STRING_REGEX = /^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/;
12
+
13
+ /**
14
+ * parse a time string like "10m", "1h30m", "10m12s" into milliseconds.
15
+ * returns null if the string is not a valid time format.
16
+ */
17
+ export function parseTimeString(input: string): number | null {
18
+ const match = input.match(TIME_STRING_REGEX);
19
+ if (!match || (!match[1] && !match[2] && !match[3])) return null;
20
+
21
+ const hours = parseInt(match[1] || "0", 10);
22
+ const minutes = parseInt(match[2] || "0", 10);
23
+ const seconds = parseInt(match[3] || "0", 10);
24
+
25
+ return (hours * 3600 + minutes * 60 + seconds) * 1000;
26
+ }
27
+
28
+ /**
29
+ * check if a string is a valid time format.
30
+ */
31
+ export function isValidTimeString(input: string): boolean {
32
+ return parseTimeString(input) !== null;
33
+ }
34
+
35
+ /**
36
+ * resolve a user-supplied timeout string into a setTimeout-safe number of
37
+ * milliseconds, returning null when the input is unusable.
38
+ *
39
+ * "unusable" covers three cases that all cause setTimeout to misbehave if
40
+ * passed through naively:
41
+ * - unparseable ("abc", "10x") — parseTimeString returns null.
42
+ * - zero ("0m", "0s") — setTimeout fires immediately, so the run would
43
+ * look like an insta-fail with the confusing message "timed out after 0m".
44
+ * - overflow (e.g. "999h") — node clamps any delay above 2^31-1 ms
45
+ * (~24.8 days) to 1 ms, so a user who asked for "596h" or more would
46
+ * get a timeout in a single tick instead of the multi-day window they
47
+ * requested. user almost certainly meant --notimeout.
48
+ *
49
+ * the caller should warn and fall back to its own default when this returns
50
+ * null; the reason is always "the input can't be honored" regardless of
51
+ * which branch triggered it.
52
+ */
53
+ const TIMEOUT_MAX_MS = 2_147_483_647;
54
+ export function resolveTimeoutMs(input: string | undefined): number | null {
55
+ if (!input) return null;
56
+ const parsed = parseTimeString(input);
57
+ if (parsed === null || parsed <= 0 || parsed > TIMEOUT_MAX_MS) return null;
58
+ return parsed;
59
+ }