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,286 @@
1
+ /**
2
+ * Claude Code `PreToolUse` hook source — written into `ctx.tmpdir` at runtime
3
+ * and registered via a tmpdir-scoped `settings.json` referenced by
4
+ * `--settings <path>` (see action/agents/claude.ts).
5
+ *
6
+ * Closes the subagent → state-mutating MCP tool path that motivated the
7
+ * 2026-05-18 zed-industries/cloud incident (`terralens` lens called
8
+ * `checkout_pr` mid-review and the orchestrator's next push clobbered an
9
+ * unrelated branch). Pairs with the `tool.execute.before` hook in
10
+ * action/agents/opencodePlugin.ts; both runtimes embed the same per-run deny
11
+ * set, derived from each MCP tool's `mutates` flag (`subagentDeniedToolNames`
12
+ * in action/mcp/server.ts).
13
+ *
14
+ * PreToolUse hook contract (verified against yasasbanukaofficial/claude-code
15
+ * `src/utils/hooks/hooksConfigManager.ts` and `src/utils/hooks.ts`):
16
+ * - stdin: JSON with `hook_event_name: "PreToolUse"`, `tool_name`,
17
+ * `tool_input`, `tool_use_id`, `session_id`, `cwd`, `transcript_path`,
18
+ * and crucially `agent_id` / `agent_type` populated when the call
19
+ * originates from a subagent (set by the SDK when a Task/Agent
20
+ * dispatches a tool — see `createBaseHookInput` in claude-code source).
21
+ * - exit 0 → allow, no output shown
22
+ * - exit 2 → block tool call AND show stderr to model (this is the path
23
+ * we want for the deny case — the subagent gets a clear refusal it can
24
+ * reason about and pick a different action)
25
+ * - other → show stderr to user only, continue with tool call
26
+ *
27
+ * The hook itself is intentionally tiny: stdin → JSON → check `agent_id`
28
+ * presence + `tool_name` against the deny list → exit 0 or 2. No deps.
29
+ *
30
+ * Why the script source is a string template, not a separate `.ts` file
31
+ * shipped with the action: the action runs as a published npm package; at
32
+ * install time we don't have the source on disk in a stable place. Embedding
33
+ * the source into `dist/main.mjs` and writing it out per-run keeps the path
34
+ * inside `ctx.tmpdir` (where `--settings` can find it) and survives bundle
35
+ * minification.
36
+ */
37
+
38
+ import { WRITE_GATE_HELPERS_SOURCE } from "#app/agents/writeGateSource";
39
+ import { PRODUCT_NAME } from "#app/brand";
40
+
41
+ /**
42
+ * The pinned `@anthropic-ai/claude-code` version against which the subagent
43
+ * gate's `agent_id` discriminator was last verified (see the contract notes in
44
+ * the gate source below). The gate fails OPEN for subagents if claude-code ever
45
+ * stops populating `agent_id` in the PreToolUse hook payload, so a version bump
46
+ * must be paired with a re-verification of `createBaseHookInput`.
47
+ *
48
+ * `claudePretoolGate.test.ts` asserts this equals the version pinned in
49
+ * `package.json` — that test fails on any bump, forcing the re-verification
50
+ * before the pin and this constant are updated together.
51
+ *
52
+ * 2.1.220 verified 2026-07-28 against the schema embedded in the shipped
53
+ * binary: the base hook input still declares `agent_id` as
54
+ * `string().optional().describe("Subagent identifier. Present only when the
55
+ * hook fires from within a subagent (e.g., a tool called by an AgentTool
56
+ * worker). Absent for the main thread, even in --agent sessions. Use this
57
+ * field (not agent_type) to distinguish subagent calls from main-thread
58
+ * calls.")` — exactly the discriminator the gate relies on, byte-for-byte
59
+ * unchanged from the 2.1.198 verification (2026-07-10), itself unchanged from
60
+ * 2.1.195 (2026-07-01) and 2.1.190 (2026-06-25), which was more explicit than
61
+ * the 2.1.177 schema it replaced. The 2.1.220 pass additionally confirmed the
62
+ * field is still POPULATED and not merely declared — the minified bundle
63
+ * assigns `agent_id` from real identifier sources (`e.agentId`,
64
+ * `e.parentAgentId`, `e.data.agentId`), not a hardcoded empty value.
65
+ *
66
+ * Scope of this verification, stated honestly: it is static inspection of the
67
+ * shipped binary. No CI job exercises the Claude subagent gate against a live
68
+ * agent — `security-tests.yml` is deliberately opencode-only (free keyless
69
+ * model; Claude Code cannot use an `opencode/*` gateway model). So this pin,
70
+ * plus the tripwire test, IS the contract check. Keep both.
71
+ */
72
+ export const CLAUDE_CODE_AGENT_ID_VERIFIED_VERSION = "2.1.220" as const;
73
+
74
+ /**
75
+ * Source written to `<ctx.tmpdir>/infraweaver-pretool-gate.mjs`. Plain ESM,
76
+ * no TypeScript, no dependencies — node executes it directly via the
77
+ * `#!/usr/bin/env node` shebang and the executable bit set by the harness.
78
+ *
79
+ * `deniedTools` is the per-run deny set derived from each MCP tool's `mutates`
80
+ * flag (see `subagentDeniedToolNames`), embedded into the hook's `Set` literal so
81
+ * the gate fences exactly the mutating tools registered for this run — no
82
+ * hand-maintained list to drift.
83
+ */
84
+ export const CLAUDE_PRETOOL_GATE_FILENAME =
85
+ "infraweaver-pretool-gate.mjs" as const;
86
+
87
+ /**
88
+ * Claude Code's native file-writing tools. The gate intercepts these to enforce
89
+ * `allowed_paths` / `protected_paths` at WRITE time; without them the only
90
+ * enforcement is the push-time diff scan, which lets a disallowed file exist in
91
+ * the tree for the rest of the run (see action/agents/writePolicy.ts).
92
+ *
93
+ * `MultiEdit` is included even though recent claude-code folds it into `Edit` —
94
+ * an extra name in the matcher is inert, a missing one is a hole.
95
+ */
96
+ export const CLAUDE_WRITE_TOOL_NAMES = [
97
+ "Write",
98
+ "Edit",
99
+ "MultiEdit",
100
+ "NotebookEdit",
101
+ ] as const;
102
+
103
+ export function buildClaudePretoolGateSource(
104
+ deniedTools: readonly string[],
105
+ writePolicyAbsolutePath: string,
106
+ ): string {
107
+ return `#!/usr/bin/env node
108
+ // AUTOGENERATED by ${PRODUCT_NAME}. PreToolUse hook that (a) hard-blocks
109
+ // state-mutating MCP tool calls from subagents and (b) enforces the
110
+ // allowed_paths / protected_paths policy at write time.
111
+ // See action/agents/claudePretoolGate.ts.
112
+
113
+ const SUBAGENT_DENIED_TOOLS = new Set(${JSON.stringify(deniedTools)});
114
+ const WRITE_TOOLS = new Set(${JSON.stringify(CLAUDE_WRITE_TOOL_NAMES)});
115
+ const WRITE_POLICY_PATH = ${JSON.stringify(writePolicyAbsolutePath)};
116
+ ${WRITE_GATE_HELPERS_SOURCE}
117
+ function stripMcpPrefix(toolName) {
118
+ if (toolName.startsWith("mcp__infraweaver__")) return toolName.slice("mcp__infraweaver__".length);
119
+ return toolName;
120
+ }
121
+
122
+ /** the write path a given tool call targets, across the write-tool shapes. */
123
+ function targetPathOf(toolInput) {
124
+ if (!toolInput || typeof toolInput !== "object") return "";
125
+ if (typeof toolInput.file_path === "string") return toolInput.file_path;
126
+ if (typeof toolInput.notebook_path === "string") return toolInput.notebook_path;
127
+ return "";
128
+ }
129
+
130
+ let stdin = "";
131
+ process.stdin.setEncoding("utf8");
132
+ process.stdin.on("data", (chunk) => {
133
+ stdin += chunk;
134
+ });
135
+ process.stdin.on("end", () => {
136
+ let payload;
137
+ try {
138
+ payload = JSON.parse(stdin);
139
+ } catch {
140
+ // malformed input — exit non-blocking so the run continues. user-only
141
+ // stderr per the hook contract.
142
+ process.stderr.write("infraweaver-pretool-gate: malformed stdin JSON\\n");
143
+ process.exit(1);
144
+ }
145
+ const toolName = typeof payload?.tool_name === "string" ? payload.tool_name : "";
146
+
147
+ // claude-code populates agent_id whenever a tool call originates inside
148
+ // a Task/Agent subagent dispatch (createBaseHookInput in claude-code
149
+ // source) — see the full contract note above the MCP deny branch below.
150
+ const agentId = typeof payload?.agent_id === "string" ? payload.agent_id : "";
151
+
152
+ // write-path enforcement. deliberately BEFORE the subagent MCP deny
153
+ // below: a disallowed write is disallowed whoever makes it, and the
154
+ // orchestrator does most of the writing. no guarded mode selected yet (or a
155
+ // non-guarded mode) leaves ORCHESTRATOR writes untouched.
156
+ if (WRITE_TOOLS.has(toolName)) {
157
+ // subagents are read-only, unconditionally — before the path policy, which
158
+ // only constrains paths for guarded modes and would otherwise let a
159
+ // Task-dispatched lens Write/Edit the orchestrator's tree mid-review (the
160
+ // read-only contract was previously prose-only for native file writes).
161
+ if (agentId) {
162
+ process.stderr.write(
163
+ "subagent attempted a native file write via '" + toolName + "'. " +
164
+ "subagents are read-only: they share the orchestrator's in-process working tree, " +
165
+ "and file mutations are reserved for the orchestrator. " +
166
+ "report findings back to the orchestrator and let it perform the write.\\n"
167
+ );
168
+ process.exit(2);
169
+ }
170
+ let refusal = null;
171
+ try {
172
+ const policy = readWritePolicy(WRITE_POLICY_PATH);
173
+ if (!policy) process.exit(0);
174
+ const rawPath = targetPathOf(payload?.tool_input);
175
+ // a write tool with no resolvable path (an unexpected shape from a future
176
+ // claude-code version) is passed through — the push-time guardrails still
177
+ // catch it, and failing closed on an unknown shape would break the run.
178
+ if (!rawPath) process.exit(0);
179
+ refusal = refuseWrite(policy, rawPath, policy.root);
180
+ } catch (err) {
181
+ // an unexpected fault while DECIDING must not become an allow. exit 1
182
+ // would mean "show the user and continue with the tool call" per the
183
+ // hook contract, so refuse explicitly instead.
184
+ process.stderr.write("write blocked (guardrail fault): " + String(err && err.message) + "\\n");
185
+ process.exit(2);
186
+ }
187
+ if (!refusal) process.exit(0);
188
+ process.stderr.write(refusal + "\\n");
189
+ process.exit(2);
190
+ }
191
+
192
+ // subagent discriminator: on the orchestrator's main thread agent_id is
193
+ // undefined. agent_type can be set on the orchestrator itself via --agent,
194
+ // so it's not a reliable subagent discriminator on its own; agent_id is.
195
+ // contract verified against @anthropic-ai/claude-code 2.1.220 (pinned in
196
+ // package.json; see CLAUDE_CODE_AGENT_ID_VERIFIED_VERSION + the version
197
+ // tripwire in claudePretoolGate.test.ts); re-verify createBaseHookInput if
198
+ // that bumps — if agent_id ever stops being populated the gate fails OPEN.
199
+ if (!agentId) process.exit(0);
200
+ const bare = stripMcpPrefix(toolName);
201
+ if (!SUBAGENT_DENIED_TOOLS.has(bare)) process.exit(0);
202
+ process.stderr.write(
203
+ "subagent attempted to call denied tool '" + bare + "'. " +
204
+ "subagents share the orchestrator's in-process working tree and toolState; " +
205
+ "state-changing MCP tools (checkout_pr, push_branch, create_pull_request_review, " +
206
+ "report_progress, etc.) are reserved for the orchestrator. " +
207
+ "report findings back to the orchestrator and let it perform the mutation.\\n"
208
+ );
209
+ process.exit(2);
210
+ });
211
+ `;
212
+ }
213
+
214
+ /**
215
+ * Settings JSON shape registered via `claude --settings <path>`. The
216
+ * matcher `^mcp__infraweaver__` is treated as a regex by claude-code's
217
+ * `matchesPattern` helper (anything outside `[a-zA-Z0-9_|]` triggers the
218
+ * regex branch — verified in src/utils/hooks.ts), so this anchors at the
219
+ * start of the tool name and fires for every Infraweaver MCP tool. We narrow
220
+ * inside the script itself rather than declaring per-tool matchers because
221
+ * the deny list is the source of truth.
222
+ *
223
+ * The hook process inherits the parent's PATH, so `node` resolves to the
224
+ * runner's node binary; the `--settings` flag accepts either a path or a
225
+ * literal JSON string per claude-code source `src/main.tsx` (`Path to a
226
+ * settings JSON file or a JSON string`), but we use a path so the script
227
+ * and its config sit side-by-side under `ctx.tmpdir`.
228
+ *
229
+ * `execToolDenyRules` are the native exec tools (Bash/Monitor/REPL/Workflow +
230
+ * their `Agent(...)` forms) to deny at a settings-source rule — the
231
+ * authoritative, bypass-immune layer. `--disallowedTools` alone (a `cliArg`
232
+ * deny) was observed to leak under `--dangerously-skip-permissions`, so the
233
+ * deny is carried here too. Both consumers use both returned fields: the flag
234
+ * `--settings` JSON (covers non-CI runs) writes the whole object, and
235
+ * `buildManagedSettings` (CI, /etc managed settings) spreads `hooks` and folds
236
+ * `permissions.deny` into its richer deny list.
237
+ */
238
+ export function buildClaudePretoolGateSettings(
239
+ scriptAbsolutePath: string,
240
+ execToolDenyRules: string[],
241
+ ): {
242
+ hooks: {
243
+ PreToolUse: Array<{
244
+ matcher: string;
245
+ hooks: Array<{ type: "command"; command: string; timeout?: number }>;
246
+ }>;
247
+ /** layered in by writePretoolGateAssets so non-CI runs (no managed
248
+ * settings) still fire the gate-server Stop hook. */
249
+ Stop?: Array<{ hooks: Array<{ type: "command"; command: string }> }>;
250
+ };
251
+ permissions: { deny: string[] };
252
+ } {
253
+ // shell-quote-safe because tmpdir paths created via node:fs.mkdtempSync
254
+ // don't contain spaces, but pass via a literal that node parses as a single
255
+ // argv entry to be defensive against future tmpdir layout changes.
256
+ const invoke = [
257
+ {
258
+ type: "command" as const,
259
+ command: `node ${JSON.stringify(scriptAbsolutePath)}`,
260
+ timeout: 5,
261
+ },
262
+ ];
263
+ return {
264
+ hooks: {
265
+ PreToolUse: [
266
+ {
267
+ matcher: "^mcp__infraweaver__",
268
+ hooks: invoke,
269
+ },
270
+ {
271
+ // the native write tools, gated for allowed_paths / protected_paths.
272
+ // Built from CLAUDE_WRITE_TOOL_NAMES so the matcher and the set the
273
+ // script checks can't drift apart. Anchored `^(...)$` keeps the
274
+ // regex branch of claude-code's matchesPattern and avoids matching
275
+ // an unrelated tool that merely starts with "Edit".
276
+ matcher: `^(${CLAUDE_WRITE_TOOL_NAMES.join("|")})$`,
277
+ hooks: invoke,
278
+ },
279
+ ],
280
+ },
281
+ // copy so callers that extend the deny list (writePretoolGateAssets adds
282
+ // the harness-tmpdir fence) can't mutate the shared CLAUDE_EXEC_TOOL_DENY_RULES
283
+ // constant, which would grow it every run and leak into --disallowedTools.
284
+ permissions: { deny: [...execToolDenyRules] },
285
+ };
286
+ }
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Specialist-dispatch instrumentation.
3
+ *
4
+ * WHY THIS EXISTS. Review routing moved from a **count-parity** gate ("0 or 2+
5
+ * lenses, never exactly 1", gated on diff size) to **hypothesis coverage**
6
+ * (one specialist per falsifiable, load-bearing question, no size threshold).
7
+ * The evaluation that adopted it named its own risk plainly: the bar to
8
+ * dispatch *something* drops, each specialist re-reads the diff, so cost is
9
+ * "plausibly neutral-to-slightly-up" — and the recommendation was to ship it
10
+ * "behind a close watch on dispatch-rate/cost". See `docs/AGENT-HARDENING.md`.
11
+ *
12
+ * That watch never existed. Tokens were summed for the whole run with no split
13
+ * between the orchestrator and the subagents it dispatched, so the one number
14
+ * that would settle the question — what share of a run the specialists cost —
15
+ * was not observable at any point. "Plausibly neutral" was an assumption with
16
+ * no instrument behind it.
17
+ *
18
+ * WHAT IS MEASURED, AND HOW MUCH IT CAN BE TRUSTED. Two different qualities of
19
+ * signal, deliberately kept apart rather than blended into one number:
20
+ *
21
+ * - **Dispatch counts are exact.** They are recorded from the actual Task
22
+ * tool call, one per dispatch, not inferred from the event stream.
23
+ * - **The token split is robust but coarse.** Tokens are attributed to
24
+ * "orchestrator" vs "subagents", never per-lens. Per-session labels come
25
+ * from `SessionLabeler`, whose own contract says its FIFO dispatch↔session
26
+ * pairing is for log readability and is not depended on for correctness.
27
+ * A mis-paired label would corrupt a *per-lens* number — but both sides of
28
+ * a mis-pairing are still subagents, so the orchestrator/subagent split is
29
+ * unaffected by exactly the weakness the labeler warns about. That is why
30
+ * this file reports the split and not the breakdown.
31
+ *
32
+ * Deliberately NOT a database column. A new `WorkflowRun` field needs manual
33
+ * production DDL, and a metric nobody looks at is not a watch. This surfaces
34
+ * where the comparison actually gets made — the run's own log line and job
35
+ * summary — so "before/after the routing change" is two runs side by side.
36
+ */
37
+
38
+ import { ORCHESTRATOR_LABEL } from "#app/agents/sessionLabeler";
39
+
40
+ export interface DispatchTally {
41
+ /** subagent dispatches this run — exact, counted at the Task call. */
42
+ dispatches: number;
43
+ /** dispatch count per resolved label, e.g. `lens:iam-blast-radius`. */
44
+ byLabel: ReadonlyMap<string, number>;
45
+ /** billable tokens (input incl. cache + output) attributed to the orchestrator. */
46
+ orchestratorTokens: number;
47
+ /** …and to every subagent session combined. */
48
+ subagentTokens: number;
49
+ }
50
+
51
+ /**
52
+ * Per-run accumulator. Lives on `ToolState` and is written by whichever harness
53
+ * is driving, so the counts survive across agent invocations (post-run resume,
54
+ * transient-provider retry) the same way `usageEntries` does.
55
+ */
56
+ export class DispatchRecorder {
57
+ #dispatches = 0;
58
+ readonly #byLabel = new Map<string, number>();
59
+ #orchestratorTokens = 0;
60
+ #subagentTokens = 0;
61
+
62
+ /** Record one subagent dispatch. Call at the Task tool_use, not at completion:
63
+ * a dispatch that errors out still cost the decision and usually the tokens. */
64
+ recordDispatch(label: string): void {
65
+ this.#dispatches++;
66
+ this.#byLabel.set(label, (this.#byLabel.get(label) ?? 0) + 1);
67
+ }
68
+
69
+ /**
70
+ * Attribute billable tokens to a session label. Anything that is not the
71
+ * orchestrator counts as subagent — including a session the labeler could not
72
+ * name, because an unnamed session is by definition not the orchestrator's.
73
+ */
74
+ recordTokens(label: string, billableTokens: number): void {
75
+ if (!Number.isFinite(billableTokens) || billableTokens <= 0) return;
76
+ if (label === ORCHESTRATOR_LABEL) this.#orchestratorTokens += billableTokens;
77
+ else this.#subagentTokens += billableTokens;
78
+ }
79
+
80
+ tally(): DispatchTally {
81
+ return {
82
+ dispatches: this.#dispatches,
83
+ byLabel: new Map(this.#byLabel),
84
+ orchestratorTokens: this.#orchestratorTokens,
85
+ subagentTokens: this.#subagentTokens,
86
+ };
87
+ }
88
+ }
89
+
90
+ /** subagent share of attributed tokens, 0–1. Null when nothing was attributed —
91
+ * distinct from 0, which means specialists genuinely cost nothing. */
92
+ export function subagentTokenShare(tally: DispatchTally): number | null {
93
+ const total = tally.orchestratorTokens + tally.subagentTokens;
94
+ if (total <= 0) return null;
95
+ return tally.subagentTokens / total;
96
+ }
97
+
98
+ const pct = (share: number): string => `${(share * 100).toFixed(1)}%`;
99
+ const fmt = (n: number): string => n.toLocaleString("en-US");
100
+
101
+ /** One stdout line. Empty when the run dispatched nothing AND attributed nothing —
102
+ * a solo run has no routing to report and the line would be noise. */
103
+ export function describeDispatchTally(tally: DispatchTally): string {
104
+ const share = subagentTokenShare(tally);
105
+ if (tally.dispatches === 0 && share === null) return "";
106
+ if (tally.dispatches === 0) return "» specialist dispatch: none this run";
107
+ const labels = [...tally.byLabel.entries()]
108
+ .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
109
+ .map(([label, n]) => (n > 1 ? `${label}×${n}` : label))
110
+ .join(", ");
111
+ const cost =
112
+ share === null
113
+ ? "token split unavailable"
114
+ : `${pct(share)} of billable tokens (${fmt(tally.subagentTokens)} of ${fmt(
115
+ tally.orchestratorTokens + tally.subagentTokens,
116
+ )})`;
117
+ return `» specialist dispatch: ${tally.dispatches} (${labels}) — ${cost}`;
118
+ }
119
+
120
+ /**
121
+ * Markdown block for the GitHub job summary, rendered beside the usage table.
122
+ * Collapsed like the usage table so it costs nothing to ignore, and carries its
123
+ * own accuracy caveat: a reader comparing two runs needs to know the split is
124
+ * coarse before they draw a conclusion from it.
125
+ */
126
+ export function formatDispatchSummary(tally: DispatchTally): string {
127
+ const share = subagentTokenShare(tally);
128
+ if (tally.dispatches === 0 && share === null) return "";
129
+
130
+ const lines: string[] = [
131
+ "<details>",
132
+ "<summary>Specialist dispatch</summary>",
133
+ "",
134
+ `- **Dispatches:** ${tally.dispatches}`,
135
+ ];
136
+
137
+ if (tally.dispatches > 0) {
138
+ const rows = [...tally.byLabel.entries()].sort(
139
+ (a, b) => b[1] - a[1] || a[0].localeCompare(b[0]),
140
+ );
141
+ for (const [label, n] of rows) lines.push(` - \`${label}\` — ${n}`);
142
+ }
143
+
144
+ if (share === null) {
145
+ lines.push("- **Token split:** unavailable (no usage was attributed this run)");
146
+ } else {
147
+ lines.push(
148
+ `- **Orchestrator:** ${fmt(tally.orchestratorTokens)} billable tokens`,
149
+ `- **Subagents:** ${fmt(tally.subagentTokens)} billable tokens (${pct(share)})`,
150
+ );
151
+ }
152
+
153
+ lines.push(
154
+ "",
155
+ "> Dispatch counts are exact. The token split is orchestrator-vs-subagent only —",
156
+ "> per-lens attribution would rest on the session labeler's FIFO pairing, which is",
157
+ "> maintained for log readability and not relied on for correctness.",
158
+ "",
159
+ "</details>",
160
+ );
161
+ return lines.join("\n");
162
+ }
@@ -0,0 +1,178 @@
1
+ /**
2
+ * gate server — localhost HTTP sidecar that returns Stop-hook decisions for
3
+ * the Claude harness. the managed Stop hook (see `action/agents/claude.ts`)
4
+ * curls this server on every stop and writes back the resulting
5
+ * `{decision: "block", reason}` to inject a follow-up turn inside the live
6
+ * `queryLoop`, replacing the pre-PR-#795 `--resume <sessionId>` subprocess.
7
+ *
8
+ * the server captures `ctx` by closure so every request reads the latest
9
+ * mutations from MCP tool callbacks (which run in the same process). the
10
+ * counters live here, not on `toolState`, because gate-retry budget and
11
+ * reflection one-shot are harness concerns the literal `toolState` design
12
+ * rule (see `action/toolState.ts`) explicitly excludes.
13
+ *
14
+ * decision policy mirrors the old `runPostRunRetryLoop`:
15
+ * - gates dirty + budget remaining → block with combined gate prompt
16
+ * - gates clean + reflection pending → block with reflection prompt
17
+ * - otherwise → allow stop
18
+ * `summaryStale` is a one-shot nudge; once delivered the gate is suppressed
19
+ * so a deliberately-unchanged file doesn't burn the retry budget. when the
20
+ * budget is exhausted with hard-fail gates still failing, we allow the stop
21
+ * so `finalizeAgentResult` can render the terminal `AgentResult.success =
22
+ * false` — that state cannot be set from a stdout-only hook.
23
+ */
24
+ import { randomUUID } from "node:crypto";
25
+ import { createServer } from "node:http";
26
+ import {
27
+ buildLearningsReflectionPrompt,
28
+ buildPostRunPrompt,
29
+ collectPostRunIssues,
30
+ shouldRunReflection,
31
+ } from "#app/agents/postRun";
32
+ import { type AgentRunContext, hasPostRunIssues, MAX_POST_RUN_RETRIES } from "#app/agents/shared";
33
+ import { log } from "#app/utils/cli";
34
+
35
+ export interface GateServerHandle {
36
+ url: string;
37
+ // bearer token the Stop hook must present. Passed to the agent process via a
38
+ // `_TOKEN`-suffixed env var so filterEnv() strips it from the agent's shell
39
+ // sandbox — only the real Stop hook (a child of the agent process, inheriting
40
+ // its full env) can read it. Without this, any loopback caller (incl. the
41
+ // agent) could poison the retry budget by hitting GET /gates.
42
+ token: string;
43
+ [Symbol.asyncDispose]: () => Promise<void>;
44
+ }
45
+
46
+ export async function startGateServer(ctx: AgentRunContext): Promise<GateServerHandle> {
47
+ let blockCount = 0;
48
+ let reflectionDelivered = false;
49
+ let summaryStaleNudged = false;
50
+ // task output captured when the reflection nudge is delivered, restored on the
51
+ // next /gates call so a set_output during reflection can't clobber the result.
52
+ // a flag (not an undefined sentinel) because the snapshot value may itself be
53
+ // undefined and must still be restored over a reflection-set value.
54
+ let reflectionOutputPending = false;
55
+ let reflectionOutputSnapshot: string | undefined;
56
+ // set when a block decision was confirmed delivered; cleared when the Stop
57
+ // hook comes back for another decision. a run that ends with this still set
58
+ // means the hook received a block but claude never re-stopped — worth a log
59
+ // line, since it points at a lost injection rather than a served one.
60
+ let blockAwaitingRefetch = false;
61
+ const token = randomUUID();
62
+
63
+ const server = createServer((req, res) => {
64
+ void (async () => {
65
+ if (req.method !== "GET" || req.url !== "/gates") {
66
+ res.writeHead(404).end();
67
+ return;
68
+ }
69
+ // Authenticate before reading state or mutating counters — an unauthorized
70
+ // request must not consume the retry budget.
71
+ if (req.headers.authorization !== `Bearer ${token}`) {
72
+ res.writeHead(403).end();
73
+ return;
74
+ }
75
+ blockAwaitingRefetch = false;
76
+ // send a decision and run `commit` (the state mutation that decision
77
+ // implies — budget consumed, one-shots latched) only once the response
78
+ // has actually flushed to the hook. the Stop hook's curl has a 30s
79
+ // timeout and an `|| {"block":false}` fallback, so mutating BEFORE
80
+ // delivery meant one hiccup silently burned a retry or the reflection
81
+ // one-shot with no log of the mismatch. an undelivered decision now
82
+ // leaves the state untouched and is retried on the next stop.
83
+ const send = (body: string, commit?: () => void): void => {
84
+ res.writeHead(200, { "content-type": "application/json" });
85
+ res.on("close", () => {
86
+ if (res.writableFinished) {
87
+ commit?.();
88
+ } else {
89
+ log.warning(
90
+ "» gate-server: decision response was not delivered (connection closed early) — state not mutated, will re-decide on the next stop",
91
+ );
92
+ }
93
+ });
94
+ res.end(body);
95
+ };
96
+ try {
97
+ const issues = await collectPostRunIssues(ctx, { skipSummaryStale: summaryStaleNudged });
98
+ if (hasPostRunIssues(issues)) {
99
+ if (blockCount >= MAX_POST_RUN_RETRIES) {
100
+ log.info("» gate-server: retry budget exhausted, allowing stop for terminal hard-fail");
101
+ send('{"block":false}');
102
+ return;
103
+ }
104
+ const reason = buildPostRunPrompt(issues);
105
+ const attempt = blockCount + 1;
106
+ log.info(`» gate-server: blocking (attempt ${attempt}/${MAX_POST_RUN_RETRIES})`);
107
+ send(JSON.stringify({ block: true, reason }), () => {
108
+ if (issues.summaryStale) summaryStaleNudged = true;
109
+ blockCount = attempt;
110
+ blockAwaitingRefetch = true;
111
+ });
112
+ return;
113
+ }
114
+ // the reflection turn just delivered (previous /gates call) may have
115
+ // clobbered the task output: some models re-trigger the "call set_output
116
+ // when done" instruction during reflection and overwrite the real result
117
+ // with "done". restore the snapshot taken when we delivered it — the
118
+ // same defense the opencode runPostRunRetryLoop applies. The prompt-level
119
+ // "do NOT call set_output" instruction is defense one; this is defense two.
120
+ if (reflectionOutputPending) {
121
+ ctx.toolState.output = reflectionOutputSnapshot;
122
+ reflectionOutputPending = false;
123
+ }
124
+ const learningsPath = ctx.toolState.learningsFilePath;
125
+ if (
126
+ !reflectionDelivered &&
127
+ learningsPath &&
128
+ shouldRunReflection(ctx.toolState.selectedMode)
129
+ ) {
130
+ const reason = buildLearningsReflectionPrompt(learningsPath);
131
+ log.info("» gate-server: delivering reflection nudge");
132
+ send(JSON.stringify({ block: true, reason }), () => {
133
+ reflectionDelivered = true;
134
+ reflectionOutputSnapshot = ctx.toolState.output;
135
+ reflectionOutputPending = true;
136
+ blockAwaitingRefetch = true;
137
+ });
138
+ return;
139
+ }
140
+ send('{"block":false}');
141
+ } catch (err) {
142
+ const msg = err instanceof Error ? err.message : String(err);
143
+ log.warning(`» gate-server handler error: ${msg}`);
144
+ // never block claude on a server-side fault — allow the stop and
145
+ // let the harness's terminal hard-fail path own the decision.
146
+ send('{"block":false}');
147
+ }
148
+ })();
149
+ });
150
+
151
+ await new Promise<void>((resolve, reject) => {
152
+ server.once("error", reject);
153
+ server.listen(0, "127.0.0.1", () => resolve());
154
+ });
155
+ const addr = server.address();
156
+ if (!addr || typeof addr === "string") {
157
+ throw new Error("gate-server: failed to bind localhost port");
158
+ }
159
+ const url = `http://127.0.0.1:${addr.port}/gates`;
160
+ log.debug(`» gate-server listening at ${url}`);
161
+
162
+ return {
163
+ url,
164
+ token,
165
+ [Symbol.asyncDispose]: async () => {
166
+ if (blockAwaitingRefetch) {
167
+ // the hook confirmed receipt of a block decision but claude never
168
+ // stopped again — the injected turn was lost downstream (e.g. the
169
+ // hook script failed after the curl). budget was already consumed;
170
+ // surface the mismatch instead of losing it silently.
171
+ log.warning(
172
+ "» gate-server: a delivered block decision was never followed by another stop — the injected follow-up turn may have been lost",
173
+ );
174
+ }
175
+ await new Promise<void>((resolve) => server.close(() => resolve()));
176
+ },
177
+ };
178
+ }
@@ -0,0 +1,10 @@
1
+ import { claude } from "#app/agents/claude";
2
+ // In-process OpenCode harness, adapted to opencode-ai >=1.14.x SDK-v2 /
3
+ // Effect-ts CLI rewrite. (The legacy CLI-subprocess harness was removed; see
4
+ // git history if a revert is ever needed.)
5
+ import { opencode } from "#app/agents/opencode";
6
+ import type { Agent } from "#app/agents/shared";
7
+
8
+ export type { Agent, AgentUsage } from "#app/agents/shared";
9
+
10
+ export const agents = { claude, opencode } satisfies Record<string, Agent>;