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,631 @@
1
+ // changes to shell security (filterEnv, spawnShell) should be reflected in wiki/security.md and docs/security.mdx
2
+ import {
3
+ type ChildProcess,
4
+ type StdioOptions,
5
+ spawn,
6
+ spawnSync,
7
+ } from "node:child_process";
8
+ import { randomUUID } from "node:crypto";
9
+ import { closeSync, openSync, writeFileSync } from "node:fs";
10
+ import { userInfo } from "node:os";
11
+ import { join } from "node:path";
12
+ import { setTimeout as sleep } from "node:timers/promises";
13
+ import { type } from "arktype";
14
+ import type { ToolContext } from "#app/mcp/server";
15
+ import { execute, tool } from "#app/mcp/shared";
16
+ import { getTempDir } from "#app/utils/env";
17
+ import { log } from "#app/utils/log";
18
+ import { resolveEnv } from "#app/utils/secrets";
19
+
20
+ export const ShellParams = type({
21
+ command: "string",
22
+ description: "string",
23
+ "timeout?": type.number.describe(
24
+ "Timeout in MILLISECONDS (not seconds). Default 30000 (30s), max 120000 (2m). e.g. timeout: 180000 for 3 minutes; timeout: 180 means 180ms and will kill the process almost immediately.",
25
+ ),
26
+ "working_directory?": "string",
27
+ "background?": "boolean",
28
+ });
29
+
30
+ type SpawnParams = {
31
+ command: string;
32
+ env: Record<string, string | undefined>;
33
+ cwd: string;
34
+ stdio: StdioOptions;
35
+ /** foreground timeout in ms. Under sudo-unshare it's enforced by a coreutils
36
+ * `timeout` wrapping the command INSIDE the PID namespace (see spawnShell) —
37
+ * the JS-side killProcessGroup can't reach that root-owned group. Omit for
38
+ * background procs (they run until kill_background). */
39
+ timeoutMs?: number;
40
+ };
41
+
42
+ /** wrap `s` as a single-quoted shell token, escaping any embedded single quote
43
+ * (`'` → `'\''`: close, literal-quote, reopen). */
44
+ function shSingleQuote(s: string): string {
45
+ return `'${s.replace(/'/g, "'\\''")}'`;
46
+ }
47
+
48
+ export type SandboxMethod = "unshare" | "sudo-unshare" | "none";
49
+
50
+ /** cached result of sandbox capability check */
51
+ let detectedSandboxMethod: SandboxMethod | undefined;
52
+
53
+ /** get the current sandbox method (for testing/diagnostics) */
54
+ export function getSandboxMethod(): SandboxMethod {
55
+ return detectSandboxMethod();
56
+ }
57
+
58
+ /** detect which sandbox method is available on this system */
59
+ function detectSandboxMethod(): SandboxMethod {
60
+ if (detectedSandboxMethod !== undefined) {
61
+ return detectedSandboxMethod;
62
+ }
63
+
64
+ // only attempt in CI environments - sandbox has overhead and is primarily for untrusted code
65
+ if (process.env.CI !== "true") {
66
+ detectedSandboxMethod = "none";
67
+ log.debug("sandbox disabled (CI !== true)");
68
+ return "none";
69
+ }
70
+
71
+ // try unprivileged unshare first (works on some systems)
72
+ try {
73
+ const result = spawnSync(
74
+ "unshare",
75
+ ["--pid", "--fork", "--mount-proc", "true"],
76
+ {
77
+ timeout: 5000,
78
+ stdio: "ignore",
79
+ },
80
+ );
81
+ if (result.status === 0) {
82
+ detectedSandboxMethod = "unshare";
83
+ log.debug("PID namespace isolation enabled (unprivileged unshare)");
84
+ return "unshare";
85
+ }
86
+ } catch {
87
+ // continue to try sudo
88
+ }
89
+
90
+ // sudo unshare (works on GHA runners)
91
+ try {
92
+ const result = spawnSync(
93
+ "sudo",
94
+ ["unshare", "--pid", "--fork", "--mount-proc", "true"],
95
+ {
96
+ timeout: 5000,
97
+ stdio: "ignore",
98
+ },
99
+ );
100
+ if (result.status === 0) {
101
+ detectedSandboxMethod = "sudo-unshare";
102
+ log.debug("PID namespace isolation enabled (sudo unshare)");
103
+ return "sudo-unshare";
104
+ }
105
+ } catch {
106
+ // no sandbox available
107
+ }
108
+
109
+ detectedSandboxMethod = "none";
110
+ log.info("PID namespace isolation not available");
111
+ return "none";
112
+ }
113
+
114
+ // strip inherited proc mount that sits underneath --mount-proc's overlay.
115
+ // --mount-proc mounts fresh proc on top, but `umount /proc` peels it off and exposes the
116
+ // host's proc with all host PIDs — allowing /proc/<pid>/environ exfiltration.
117
+ // double-umount removes both layers, then a clean mount gives only sandbox PIDs.
118
+ // on unprivileged systems where umount fails, --mount-proc still provides isolation
119
+ // (the agent also can't umount in that case).
120
+ const PROC_CLEANUP =
121
+ "umount /proc 2>/dev/null; umount /proc 2>/dev/null; mount -t proc proc /proc 2>/dev/null;";
122
+
123
+ // block container-runtime sockets that would otherwise grant a PID-namespace
124
+ // escape: `docker run --pid=host --privileged busybox cat /proc/<pid>/environ`
125
+ // reads the parent action process's env (which contains user secrets) even
126
+ // though the sandbox itself is unsharing PIDs. GHA `ubuntu-latest` puts the
127
+ // `runner` user in the `docker` group by default, so the socket is reachable
128
+ // without sudo. bind-mounting /dev/null on top inside the sandbox's mount
129
+ // namespace makes the socket unreachable from sandboxed shells without
130
+ // touching the host runner (so it doesn't break user workflow steps that
131
+ // run before/after infraweaver and legitimately need docker). same trick for
132
+ // podman/containerd/cri-o sockets — all silent-fail if the path is missing.
133
+ const SOCKET_CLEANUP = [
134
+ "/var/run/docker.sock",
135
+ "/run/docker.sock",
136
+ "/var/run/podman/podman.sock",
137
+ "/run/podman/podman.sock",
138
+ "/run/containerd/containerd.sock",
139
+ "/var/run/crio/crio.sock",
140
+ ]
141
+ .map((path) => `mount --bind /dev/null ${path} 2>/dev/null;`)
142
+ .join(" ");
143
+
144
+ // extend the mount-namespace isolation that PROC_CLEANUP and SOCKET_CLEANUP
145
+ // already establish. these mounts hide infraweaver-managed on-disk secrets,
146
+ // block env-injection into subsequent workflow steps, and make git's
147
+ // code-execution config read-only inside the bash subprocess.
148
+ //
149
+ // 1. tmpfs over /var/lib/infraweaver/ — codex auth.json and any future
150
+ // infraweaver-managed on-disk secret live here (see action/utils/codexHome.ts
151
+ // INFRAWEAVER_DATA_DIR). opencode's internal auth module runs in the agent
152
+ // process outside this namespace and reads the real file via bypass of
153
+ // external_directory; bash sees an empty tmpfs. mkdir -p the path
154
+ // first so the tmpfs always engages — without that, runs without
155
+ // CODEX_AUTH_JSON wouldn't have bootstrapped the dir, the mountpoint
156
+ // wouldn't exist, and `mount -t tmpfs` would silent-fail. precreate
157
+ // keeps the overlay active for any future on-disk secret that lands
158
+ // under /var/lib/infraweaver regardless of which install path created it.
159
+ // 2. tmpfs over $RUNNER_TEMP/_runner_file_commands/ — anything bash writes
160
+ // to $GITHUB_ENV / $GITHUB_PATH / $GITHUB_OUTPUT / $GITHUB_STATE lands in
161
+ // a per-namespace tmpfs that the GHA runner never sees. our own action
162
+ // process writes core.setOutput / core.saveState outside the namespace,
163
+ // so legitimate outputs are unaffected. requires RUNNER_TEMP to be set.
164
+ // 3. self-bind + remount-ro on the ENTIRE <repoRoot>/.git directory.
165
+ // A blanket ro-bind is free: nothing legitimately writes .git from
166
+ // bash (commits go through our $git(), whose binary runs OUTSIDE this
167
+ // namespace, so it's unaffected; bash `git` is already blocked). It
168
+ // robustly covers every code-exec surface an enumerated list would miss
169
+ // — .git/config, .git/config.worktree, .git/modules/*/config (all carry
170
+ // core.hooksPath / filter / alias / credential.helper exec vectors),
171
+ // plus .git/hooks/* and .git/info/attributes. Prevents agent-planted git
172
+ // filters / hooks from firing in downstream workflow steps (the threat
173
+ // survives ASKPASS because hooks fire after auth and our $git() uses -c
174
+ // core.hooksPath to override its own hooks, but downstream `git`
175
+ // invocations in later steps DON'T get that protection — see
176
+ // wiki/security.md "Filesystem Sandbox"). CONSEQUENCE: .git/info/exclude
177
+ // (a legit per-repo ignore file) is now read-only too — accepted, the
178
+ // narrow earlier bind list left it writable. Does not cover `~/.gitconfig`
179
+ // or `/etc/gitconfig` — see "Scope and Limitations" in wiki/security.md.
180
+ //
181
+ // these mounts run as root inside the namespace (before `exec su -p` drops
182
+ // to runner). after the drop, runner has no CAP_SYS_ADMIN in the host, so
183
+ // can't undo from outside. intra-namespace sudo undo is theoretically
184
+ // possible — same risk profile as SOCKET_CLEANUP, accepted per wiki/security.md
185
+ // "why sudo inside sandbox doesn't break security".
186
+ //
187
+ // in the unprivileged-unshare path (Docker --privileged test environments),
188
+ // the user retains CAP_SYS_ADMIN inside the user namespace and could
189
+ // `umount` these. production uses sudo-unshare where the drop seals them.
190
+ //
191
+ // repoDir is interpolated by the action process from resolveRepoRoot() —
192
+ // NOT $PWD — because spawnShell's cwd is agent-controllable via
193
+ // `working_directory` AND the action's process.cwd() may have been chdir'd
194
+ // to payload.cwd (monorepo subdir support in main.ts). using either would
195
+ // let the agent bypass the .git/* binds. resolveRepoRoot pins the actual
196
+ // repo root once at startup via $GITHUB_WORKSPACE or `git rev-parse`.
197
+ function buildFsMounts(repoDir: string): string {
198
+ // shell-escape via single-quote wrap; bash interprets \' as the escape
199
+ // for a single quote inside a single-quoted string by closing-and-reopening.
200
+ // repoDir paths in practice are GHA workspace paths (no quotes), but the
201
+ // escape keeps us correct against arbitrary user-configured workspaces.
202
+ const escaped = repoDir.replace(/'/g, "'\\''");
203
+ return [
204
+ `mkdir -p /var/lib/infraweaver 2>/dev/null;`,
205
+ `mount -t tmpfs tmpfs /var/lib/infraweaver 2>/dev/null;`,
206
+ `[ -n "$RUNNER_TEMP" ] && [ -d "$RUNNER_TEMP/_runner_file_commands" ] && mount -t tmpfs tmpfs "$RUNNER_TEMP/_runner_file_commands" 2>/dev/null;`,
207
+ `[ -e '${escaped}/.git' ] && mount --bind '${escaped}/.git' '${escaped}/.git' 2>/dev/null && mount -o remount,bind,ro '${escaped}/.git' 2>/dev/null;`,
208
+ ].join(" ");
209
+ }
210
+
211
+ /** locate the repo root once at action startup. process.cwd() is unreliable
212
+ * because main.ts may `process.chdir(payload.cwd)` for monorepo subdirs;
213
+ * the agent's `working_directory` shell param also moves spawn cwd. we need
214
+ * the actual git working tree root for the .git/* binds. memoized for the
215
+ * lifetime of the action process. */
216
+ let _repoRoot: string | undefined;
217
+ export function resolveRepoRoot(): string {
218
+ if (_repoRoot) return _repoRoot;
219
+ const fromEnv = process.env.GITHUB_WORKSPACE;
220
+ if (fromEnv) {
221
+ _repoRoot = fromEnv;
222
+ return _repoRoot;
223
+ }
224
+ // fallback: `git rev-parse --show-toplevel` from process.cwd(). only used
225
+ // outside GHA (local dev, custom runners). swallow errors and fall back
226
+ // to process.cwd() so we never throw from the shell-tool init path.
227
+ try {
228
+ _repoRoot = spawnSync("git", ["rev-parse", "--show-toplevel"], {
229
+ cwd: process.cwd(),
230
+ stdio: ["ignore", "pipe", "ignore"],
231
+ encoding: "utf-8",
232
+ }).stdout?.trim();
233
+ } catch {
234
+ // intentionally empty — fall through to process.cwd()
235
+ }
236
+ if (!_repoRoot) _repoRoot = process.cwd();
237
+ return _repoRoot;
238
+ }
239
+
240
+ function spawnShell(params: SpawnParams): ChildProcess {
241
+ const spawnOpts = {
242
+ env: params.env,
243
+ cwd: params.cwd,
244
+ stdio: params.stdio,
245
+ detached: true,
246
+ };
247
+ const sandboxMethod = detectSandboxMethod();
248
+ const ci = process.env.CI === "true";
249
+
250
+ if (ci && sandboxMethod === "none") {
251
+ throw new Error(
252
+ "pid namespace isolation is required in CI but unavailable (both unshare and sudo unshare failed)",
253
+ );
254
+ }
255
+
256
+ // resolve the actual git repo root (NOT params.cwd which is
257
+ // agent-controllable via `working_directory`, NOT process.cwd() which may
258
+ // have been chdir'd to payload.cwd in main.ts). resolveRepoRoot prefers
259
+ // $GITHUB_WORKSPACE in CI and falls back to `git rev-parse`.
260
+ const repoRoot = resolveRepoRoot();
261
+ const fsMounts = buildFsMounts(repoRoot);
262
+
263
+ if (sandboxMethod === "unshare") {
264
+ return spawn(
265
+ "unshare",
266
+ [
267
+ "--pid",
268
+ "--fork",
269
+ "--mount-proc",
270
+ "bash",
271
+ "-c",
272
+ `${PROC_CLEANUP} ${SOCKET_CLEANUP} ${fsMounts} ${params.command}`,
273
+ ],
274
+ spawnOpts,
275
+ );
276
+ }
277
+
278
+ if (sandboxMethod === "sudo-unshare") {
279
+ const envArgs: string[] = [];
280
+ for (const [k, v] of Object.entries(params.env)) {
281
+ if (v !== undefined) {
282
+ envArgs.push(`${k}=${v}`);
283
+ }
284
+ }
285
+ // drop back to original user after PROC_CLEANUP / FS_MOUNTS so files aren't
286
+ // owned by root. sudo is only needed for unshare + the mount setup; the
287
+ // actual command should run as the normal user to avoid ownership
288
+ // mismatches with files created by the Node.js parent process.
289
+ const username = userInfo().username;
290
+ // su -p resets PATH on many Linux systems (ALWAYS_SET_PATH in /etc/login.defs).
291
+ // restore it from the SANDBOX_PATH env var that survives the su transition.
292
+ // biome-ignore lint/suspicious/noTemplateCurlyInString: we need to restore the PATH variable
293
+ const pathRestore = 'export PATH="${SANDBOX_PATH:-$PATH}"; ';
294
+ // enforce the timeout INSIDE the namespace with coreutils `timeout`. The
295
+ // process group here descends from root-owned `sudo`, so the unprivileged
296
+ // Node parent's killProcessGroup() gets EPERM and can't stop a runaway child
297
+ // — it survives to (and past) the JS ceiling. `timeout` runs as the same
298
+ // unprivileged user as the command, kills reliably, and --kill-after=5
299
+ // escalates to SIGKILL 5s after the initial SIGTERM if the child ignores it.
300
+ // Background procs pass no timeoutMs (they run until kill_background).
301
+ const userCommand =
302
+ params.timeoutMs && params.timeoutMs > 0
303
+ ? `timeout --kill-after=5 --signal=TERM ${Math.ceil(params.timeoutMs / 1000)}s bash -c ${shSingleQuote(params.command)}`
304
+ : params.command;
305
+ const escaped = (pathRestore + userCommand).replace(/'/g, "'\\''");
306
+ envArgs.push(`SANDBOX_PATH=${params.env.PATH ?? ""}`);
307
+ return spawn(
308
+ "sudo",
309
+ [
310
+ "env",
311
+ ...envArgs,
312
+ "unshare",
313
+ "--pid",
314
+ "--fork",
315
+ "--mount-proc",
316
+ "bash",
317
+ "-c",
318
+ `${PROC_CLEANUP} ${SOCKET_CLEANUP} ${fsMounts} exec su -p -s /bin/bash ${username} -c '${escaped}'`,
319
+ ],
320
+ { ...spawnOpts, env: {} },
321
+ );
322
+ }
323
+
324
+ return spawn("bash", ["-c", params.command], spawnOpts);
325
+ }
326
+
327
+ /** signal a process group through `sudo`. Background procs spawned under
328
+ * sudo-unshare belong to a root-owned group, so the unprivileged Node parent's
329
+ * process.kill(-pid) gets EPERM; `sudo kill` (already proven available by the
330
+ * sudo-unshare probe) reaches it. A negative pid targets the whole group,
331
+ * matching process.kill(-pid) semantics. Best-effort — swallows failures. */
332
+ function sudoKillGroup(pid: number, signal: "SIGTERM" | "SIGKILL"): void {
333
+ const flag = signal === "SIGKILL" ? "-KILL" : "-TERM";
334
+ try {
335
+ spawnSync("sudo", ["kill", flag, `-${pid}`], {
336
+ timeout: 5000,
337
+ stdio: "ignore",
338
+ });
339
+ } catch {
340
+ /* nothing more we can do from here */
341
+ }
342
+ }
343
+
344
+ /** kill process and its entire process group */
345
+ async function killProcessGroup(proc: ChildProcess): Promise<void> {
346
+ if (!proc.pid) return;
347
+ try {
348
+ process.kill(-proc.pid, "SIGTERM");
349
+ await new Promise((r) => setTimeout(r, 200));
350
+ process.kill(-proc.pid, "SIGKILL");
351
+ } catch {
352
+ try {
353
+ proc.kill("SIGKILL");
354
+ } catch {
355
+ /* already dead */
356
+ }
357
+ }
358
+ }
359
+
360
+ /** chars of shell output kept inline in the agent reply. anything past this
361
+ * blows the agent's context budget on commands that dump big logs (test
362
+ * runners, build tools, grep on large trees), so the overflow is spilled
363
+ * to a tempfile the agent can re-read selectively (cat/tail/grep). */
364
+ export const MAX_OUTPUT_CHARS = 5000;
365
+
366
+ /** if `output` exceeds `MAX_OUTPUT_CHARS`, persist the full body to a
367
+ * tempfile and return the last `MAX_OUTPUT_CHARS` prefixed with a sentinel
368
+ * pointing at the saved path. otherwise return as-is. */
369
+ function capOutput(output: string): string {
370
+ if (output.length <= MAX_OUTPUT_CHARS) return output;
371
+ const fullPath = join(getTempDir(), `shell-${randomUUID().slice(0, 8)}.log`);
372
+ writeFileSync(fullPath, output);
373
+ const elided = output.length - MAX_OUTPUT_CHARS;
374
+ return `... [${elided} chars truncated; full output saved to ${fullPath}] ...\n${output.slice(-MAX_OUTPUT_CHARS)}`;
375
+ }
376
+
377
+ /** hard ceiling on the stdout/stderr we hold in memory WHILE a command runs.
378
+ * `capOutput` only spills to a tempfile AFTER exit, so without this the live
379
+ * accumulator grows by string-concat on every `data` event — a child that streams
380
+ * hundreds of MB before exiting (runaway build/test, `cat` of a huge file) can OOM the
381
+ * runner first. Tail-preferring: the newest output, where errors surface, wins —
382
+ * which also matches `capOutput` keeping the tail. Set far above MAX_OUTPUT_CHARS so it
383
+ * never interferes with normal output, only bounds the pathological case. */
384
+ export const MAX_CAPTURE_CHARS = 5_000_000;
385
+
386
+ /** A string accumulator bounded to ~`limit` chars, keeping the TAIL. The trim is
387
+ * amortized (the buffer is allowed to reach 2× before slicing back) so steady-state
388
+ * appends stay O(1). `droppedChars()` reports how much of the head was discarded. */
389
+ export function createCappedBuffer(limit: number) {
390
+ let buf = "";
391
+ let dropped = 0;
392
+ const slack = limit * 2;
393
+ const trim = (): void => {
394
+ if (buf.length > limit) {
395
+ const cut = buf.length - limit;
396
+ buf = buf.slice(cut);
397
+ dropped += cut;
398
+ }
399
+ };
400
+ return {
401
+ push(chunk: string): void {
402
+ buf += chunk;
403
+ if (buf.length > slack) trim();
404
+ },
405
+ value(): string {
406
+ trim();
407
+ return buf;
408
+ },
409
+ droppedChars(): number {
410
+ trim();
411
+ return dropped;
412
+ },
413
+ };
414
+ }
415
+
416
+ // detect `git` as a command invocation in any position a shell would start a new
417
+ // command: at the start, after a separator (`;`, `&`, `|`, newline, carriage
418
+ // return), or inside a subshell / command substitution / brace group (`(`,
419
+ // backtick, `$(`, `{`). Tolerates one or more launcher prefixes
420
+ // (`sudo`/`env`/`command`/`exec`/`nohup`). Matches `git` only when followed by
421
+ // whitespace or end-of-string, so it never fires on `.gitignore`, `digit`,
422
+ // `legit`, `git-lfs`, etc.
423
+ //
424
+ // NOTE: this is a UX redirect to the dedicated git tools, NOT a security
425
+ // boundary — in restricted mode the shell runs in a stripped, token-free sandbox
426
+ // with `.git` mounted read-only, so a `git` the agent slips past this still has
427
+ // no credentials and can't tamper with repo config. The detection is kept broad
428
+ // so the redirect isn't trivially defeated, but it is NOT relied on to contain a
429
+ // hostile `git` — that is the sandbox's job.
430
+ const GIT_INVOCATION =
431
+ /(?:^|\$\(|[\n\r;&|`({])\s*(?:(?:sudo|env|command|exec|nohup)\s+)*git(?:\s|$)/;
432
+
433
+ function isGitCommand(command: string): boolean {
434
+ return GIT_INVOCATION.test(command.trim());
435
+ }
436
+
437
+ export function ShellTool(ctx: ToolContext) {
438
+ return tool({
439
+ name: "shell",
440
+ timeoutMs: 120_000,
441
+ description: `Execute shell commands securely. Environment is filtered to remove API keys and secrets.
442
+
443
+ Example: \`shell({ command: "pnpm test", description: "run the test suite" })\`.
444
+
445
+ Use this tool to:
446
+ - Run shell commands (ls, cat, grep, find, etc.)
447
+ - Execute build tools (npm, pnpm, cargo, make, etc.)
448
+ - Run tests and linters
449
+
450
+ Output is capped at ${MAX_OUTPUT_CHARS} chars: if exceeded, only the tail is returned and the full body is saved to a tempfile (path included in the response). Re-read the tempfile with cat/tail/grep when you need more.
451
+
452
+ Do NOT use this tool for git commands — use the dedicated git tools instead.`,
453
+ parameters: ShellParams,
454
+ execute: execute(async (params) => {
455
+ if (isGitCommand(params.command)) {
456
+ throw new Error(
457
+ "git commands are not allowed in the shell tool. use the dedicated git tools instead:\n" +
458
+ "- git: local operations (status, log, diff, add, commit, checkout, merge, rebase, etc.)\n" +
459
+ "- push_branch: push to remote (handles authentication)\n" +
460
+ "- git_fetch: fetch from remote (handles authentication)\n" +
461
+ "- checkout_pr: check out PR branches",
462
+ );
463
+ }
464
+
465
+ const timeout = Math.min(params.timeout ?? 30000, 120000);
466
+ const cwd = params.working_directory ?? process.cwd();
467
+ // "enabled" widens the command surface, not credential access: inherit
468
+ // still denies secret-bearing names, so no shell mode exposes a secret.
469
+ const env = resolveEnv(
470
+ ctx.payload.shell === "enabled" ? "inherit" : "restricted",
471
+ );
472
+
473
+ if (params.background) {
474
+ const tempDir = getTempDir();
475
+ const handle = `bg-${randomUUID().slice(0, 8)}`;
476
+ const outputPath = join(tempDir, `${handle}.log`);
477
+ const pidPath = join(tempDir, `${handle}.pid`);
478
+ const logFd = openSync(outputPath, "a");
479
+ let proc: ChildProcess;
480
+ try {
481
+ proc = spawnShell({
482
+ command: params.command,
483
+ env,
484
+ cwd,
485
+ stdio: ["ignore", logFd, logFd],
486
+ });
487
+ } finally {
488
+ closeSync(logFd);
489
+ }
490
+ if (!proc.pid) {
491
+ throw new Error("failed to start background process");
492
+ }
493
+ proc.unref();
494
+ writeFileSync(pidPath, `${proc.pid}\n`);
495
+ ctx.toolState.backgroundProcesses.set(handle, {
496
+ pid: proc.pid,
497
+ outputPath,
498
+ pidPath,
499
+ });
500
+ return {
501
+ handle,
502
+ outputPath,
503
+ pidPath,
504
+ message: `started background process ${handle} (pid ${proc.pid})`,
505
+ };
506
+ }
507
+
508
+ const proc = spawnShell({
509
+ command: params.command,
510
+ env,
511
+ cwd,
512
+ stdio: ["ignore", "pipe", "pipe"],
513
+ timeoutMs: timeout,
514
+ });
515
+
516
+ // bound the in-memory accumulators so a child that streams more than
517
+ // MAX_CAPTURE_CHARS before exiting can't OOM the runner before capOutput runs.
518
+ const stdoutBuf = createCappedBuffer(MAX_CAPTURE_CHARS);
519
+ const stderrBuf = createCappedBuffer(MAX_CAPTURE_CHARS);
520
+ let timedOut = false,
521
+ exited = false;
522
+ proc.stdout?.on("data", (chunk: Buffer) => {
523
+ stdoutBuf.push(chunk.toString());
524
+ });
525
+ proc.stderr?.on("data", (chunk: Buffer) => {
526
+ stderrBuf.push(chunk.toString());
527
+ });
528
+
529
+ const timeoutId = setTimeout(() => {
530
+ if (exited) return;
531
+ timedOut = true;
532
+ // setTimeout can't await the callback, so observe the kill promise here —
533
+ // a kill failure is logged instead of becoming a silent unhandled rejection.
534
+ // The exit race still resolves the call either way.
535
+ void killProcessGroup(proc).catch((err) => {
536
+ log.warning(
537
+ `shell timeout: failed to kill process group: ${err instanceof Error ? err.message : String(err)}`,
538
+ );
539
+ });
540
+ }, timeout);
541
+
542
+ const exitCode = await new Promise<number | null>((resolve) => {
543
+ const done = (code: number | null) => {
544
+ exited = true;
545
+ clearTimeout(timeoutId);
546
+ resolve(code);
547
+ };
548
+ proc.on("exit", done);
549
+ proc.on("error", () => done(null));
550
+ });
551
+
552
+ const stdout = stdoutBuf.value();
553
+ const stderr = stderrBuf.value();
554
+ let output = stderr ? (stdout ? `${stdout}\n${stderr}` : stderr) : stdout;
555
+ // Note the head we discarded to bound memory. Appended at the END so it
556
+ // survives capOutput's tail-keep and is visible inline.
557
+ const droppedChars = stdoutBuf.droppedChars() + stderrBuf.droppedChars();
558
+ if (droppedChars > 0)
559
+ output = `${output}\n[${droppedChars} chars of earlier output dropped to bound runner memory]`;
560
+ if (timedOut)
561
+ output = output
562
+ ? `${output}\n[timed out after ${timeout}ms]`
563
+ : `[timed out after ${timeout}ms]`;
564
+
565
+ const finalExitCode = exitCode ?? (timedOut ? 124 : -1);
566
+ const trimmed = output.trim();
567
+ if (finalExitCode !== 0) {
568
+ log.info(
569
+ `shell command failed with exit code ${finalExitCode}: ${params.command}`,
570
+ );
571
+ if (trimmed) log.info(`output: ${trimmed}`);
572
+ }
573
+
574
+ return {
575
+ output: capOutput(trimmed),
576
+ exit_code: finalExitCode,
577
+ timed_out: timedOut,
578
+ };
579
+ }),
580
+ });
581
+ }
582
+
583
+ export const KillBackgroundParams = type({
584
+ handle: type.string.describe(
585
+ "The handle of the background process to kill (e.g., bg-a1b2c3d4)",
586
+ ),
587
+ });
588
+
589
+ export function KillBackgroundTool(ctx: ToolContext) {
590
+ return tool({
591
+ mutates: true,
592
+ name: "kill_background",
593
+ description: `Kill a background process by its handle. Use this to stop dev servers or other long-running processes started with shell({ background: true }).`,
594
+ parameters: KillBackgroundParams,
595
+ execute: execute(async (params) => {
596
+ const proc = ctx.toolState.backgroundProcesses.get(params.handle);
597
+ if (!proc) {
598
+ return {
599
+ success: false,
600
+ message: `no background process with handle ${params.handle}`,
601
+ };
602
+ }
603
+
604
+ // under sudo-unshare the group is root-owned — process.kill(-pid) EPERMs,
605
+ // so escalate straight through sudo. otherwise the unprivileged kill works.
606
+ if (getSandboxMethod() === "sudo-unshare") {
607
+ sudoKillGroup(proc.pid, "SIGTERM");
608
+ await sleep(200);
609
+ sudoKillGroup(proc.pid, "SIGKILL");
610
+ } else {
611
+ try {
612
+ process.kill(-proc.pid, "SIGTERM");
613
+ } catch {
614
+ // already dead
615
+ }
616
+ await sleep(200);
617
+ try {
618
+ process.kill(-proc.pid, "SIGKILL");
619
+ } catch {
620
+ // already dead
621
+ }
622
+ }
623
+
624
+ ctx.toolState.backgroundProcesses.delete(params.handle);
625
+ return {
626
+ success: true,
627
+ message: `killed background process ${params.handle} (pid ${proc.pid})`,
628
+ };
629
+ }),
630
+ });
631
+ }