terramend 0.2.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 (406) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +145 -0
  3. package/dist/agents/claude.d.ts +73 -0
  4. package/dist/agents/claudePretoolGate.d.ts +99 -0
  5. package/dist/agents/gateServer.d.ts +7 -0
  6. package/dist/agents/index.d.ts +6 -0
  7. package/dist/agents/nativeFsDenies.d.ts +28 -0
  8. package/dist/agents/opencode.d.ts +231 -0
  9. package/dist/agents/opencodePlugin.d.ts +85 -0
  10. package/dist/agents/opencodeShared.d.ts +40 -0
  11. package/dist/agents/postRun.d.ts +132 -0
  12. package/dist/agents/reviewer.d.ts +38 -0
  13. package/dist/agents/sessionLabeler.d.ts +97 -0
  14. package/dist/agents/shared.d.ts +189 -0
  15. package/dist/agents/subagentModels.d.ts +19 -0
  16. package/dist/agents/subagentToolGates.d.ts +55 -0
  17. package/dist/cli.mjs +197426 -0
  18. package/dist/external.d.ts +227 -0
  19. package/dist/index.d.ts +6 -0
  20. package/dist/index.js +196783 -0
  21. package/dist/internal/index.d.ts +18 -0
  22. package/dist/internal.js +1714 -0
  23. package/dist/lifecycle.d.ts +2 -0
  24. package/dist/main.d.ts +8 -0
  25. package/dist/mcp/arkConfig.d.ts +1 -0
  26. package/dist/mcp/checkSuite.d.ts +25 -0
  27. package/dist/mcp/checkout.d.ts +77 -0
  28. package/dist/mcp/comment.d.ts +119 -0
  29. package/dist/mcp/commitInfo.d.ts +9 -0
  30. package/dist/mcp/crosswalk.d.ts +105 -0
  31. package/dist/mcp/dependencies.d.ts +8 -0
  32. package/dist/mcp/geminiSanitizer.d.ts +28 -0
  33. package/dist/mcp/git.d.ts +46 -0
  34. package/dist/mcp/guardrails.d.ts +104 -0
  35. package/dist/mcp/issue.d.ts +18 -0
  36. package/dist/mcp/issueComments.d.ts +9 -0
  37. package/dist/mcp/issueEvents.d.ts +9 -0
  38. package/dist/mcp/issueInfo.d.ts +9 -0
  39. package/dist/mcp/labels.d.ts +12 -0
  40. package/dist/mcp/localContext.d.ts +19 -0
  41. package/dist/mcp/moduleExtraction.d.ts +71 -0
  42. package/dist/mcp/moduleTests.d.ts +104 -0
  43. package/dist/mcp/modules.d.ts +179 -0
  44. package/dist/mcp/output.d.ts +12 -0
  45. package/dist/mcp/pathSafety.d.ts +14 -0
  46. package/dist/mcp/policy.d.ts +48 -0
  47. package/dist/mcp/pr.d.ts +49 -0
  48. package/dist/mcp/prInfo.d.ts +9 -0
  49. package/dist/mcp/providerSchema.d.ts +50 -0
  50. package/dist/mcp/review.d.ts +199 -0
  51. package/dist/mcp/reviewComments.d.ts +178 -0
  52. package/dist/mcp/roots.d.ts +58 -0
  53. package/dist/mcp/scope.d.ts +15 -0
  54. package/dist/mcp/selectMode.d.ts +18 -0
  55. package/dist/mcp/server.d.ts +48 -0
  56. package/dist/mcp/shared.d.ts +47 -0
  57. package/dist/mcp/shell.d.ts +37 -0
  58. package/dist/mcp/staleFix.d.ts +51 -0
  59. package/dist/mcp/terraform/cost.d.ts +55 -0
  60. package/dist/mcp/terraform/currency.d.ts +94 -0
  61. package/dist/mcp/terraform/decisions.d.ts +178 -0
  62. package/dist/mcp/terraform/findings.d.ts +75 -0
  63. package/dist/mcp/terraform/plan.d.ts +157 -0
  64. package/dist/mcp/terraform/scanners.d.ts +131 -0
  65. package/dist/mcp/terraform/tools.d.ts +63 -0
  66. package/dist/mcp/terraform/types.d.ts +172 -0
  67. package/dist/mcp/terraform.d.ts +22 -0
  68. package/dist/mcp/terratest.d.ts +83 -0
  69. package/dist/mcp/upload.d.ts +6 -0
  70. package/dist/models.d.ts +171 -0
  71. package/dist/modes.d.ts +26 -0
  72. package/dist/prep/index.d.ts +7 -0
  73. package/dist/prep/installNodeDependencies.d.ts +2 -0
  74. package/dist/prep/installPythonDependencies.d.ts +2 -0
  75. package/dist/prep/types.d.ts +31 -0
  76. package/dist/reviewQuality.d.ts +64 -0
  77. package/dist/skills/terraform-best-practices/SKILL.md +369 -0
  78. package/dist/toolState.d.ts +135 -0
  79. package/dist/utils/activity.d.ts +40 -0
  80. package/dist/utils/agent.d.ts +20 -0
  81. package/dist/utils/agentHangReport.d.ts +38 -0
  82. package/dist/utils/apiFetch.d.ts +19 -0
  83. package/dist/utils/apiKeys.d.ts +41 -0
  84. package/dist/utils/apiUrl.d.ts +20 -0
  85. package/dist/utils/assets.d.ts +8 -0
  86. package/dist/utils/billingErrors.d.ts +85 -0
  87. package/dist/utils/body.d.ts +34 -0
  88. package/dist/utils/buildTerramendFooter.d.ts +25 -0
  89. package/dist/utils/byokFallback.d.ts +85 -0
  90. package/dist/utils/claudeSubscription.d.ts +30 -0
  91. package/dist/utils/cli.d.ts +10 -0
  92. package/dist/utils/codexHome.d.ts +29 -0
  93. package/dist/utils/codexOAuth.d.ts +60 -0
  94. package/dist/utils/diffCoverage.d.ts +63 -0
  95. package/dist/utils/errorReport.d.ts +17 -0
  96. package/dist/utils/exitHandler.d.ts +8 -0
  97. package/dist/utils/fixDoubleEscapedString.d.ts +1 -0
  98. package/dist/utils/gitAuth.d.ts +84 -0
  99. package/dist/utils/gitAuthServer.d.ts +24 -0
  100. package/dist/utils/github.d.ts +78 -0
  101. package/dist/utils/globals.d.ts +3 -0
  102. package/dist/utils/install.d.ts +60 -0
  103. package/dist/utils/instructions.d.ts +48 -0
  104. package/dist/utils/leapingComment.d.ts +11 -0
  105. package/dist/utils/learnings.d.ts +62 -0
  106. package/dist/utils/learningsTruncate.d.ts +25 -0
  107. package/dist/utils/lifecycle.d.ts +57 -0
  108. package/dist/utils/log.d.ts +111 -0
  109. package/dist/utils/normalizeEnv.d.ts +30 -0
  110. package/dist/utils/openCodeModels.d.ts +11 -0
  111. package/dist/utils/overrides.d.ts +40 -0
  112. package/dist/utils/packageManager.d.ts +49 -0
  113. package/dist/utils/patchWorkflowRunFields.d.ts +29 -0
  114. package/dist/utils/payload.d.ts +105 -0
  115. package/dist/utils/prSummary.d.ts +61 -0
  116. package/dist/utils/progressComment.d.ts +146 -0
  117. package/dist/utils/providerErrors.d.ts +31 -0
  118. package/dist/utils/rangeDiff.d.ts +51 -0
  119. package/dist/utils/remediationCommand.d.ts +55 -0
  120. package/dist/utils/retry.d.ts +13 -0
  121. package/dist/utils/reviewCleanup.d.ts +14 -0
  122. package/dist/utils/run.d.ts +9 -0
  123. package/dist/utils/runContext.d.ts +60 -0
  124. package/dist/utils/runContextData.d.ts +23 -0
  125. package/dist/utils/runErrorRenderer.d.ts +64 -0
  126. package/dist/utils/runLifecycle.d.ts +86 -0
  127. package/dist/utils/runStartupLog.d.ts +15 -0
  128. package/dist/utils/secrets.d.ts +22 -0
  129. package/dist/utils/setup.d.ts +90 -0
  130. package/dist/utils/shell.d.ts +32 -0
  131. package/dist/utils/skills.d.ts +10 -0
  132. package/dist/utils/subprocess.d.ts +80 -0
  133. package/dist/utils/terraformMcp.d.ts +42 -0
  134. package/dist/utils/time.d.ts +15 -0
  135. package/dist/utils/timer.d.ts +23 -0
  136. package/dist/utils/todoTracking.d.ts +16 -0
  137. package/dist/utils/token.d.ts +39 -0
  138. package/dist/utils/version.d.ts +2 -0
  139. package/dist/utils/versioning.d.ts +7 -0
  140. package/dist/utils/vertex.d.ts +16 -0
  141. package/dist/utils/workflow.d.ts +13 -0
  142. package/package.json +119 -0
  143. package/src/agents/claude.test.ts +1016 -0
  144. package/src/agents/claude.ts +1246 -0
  145. package/src/agents/claudePretoolGate.test.ts +28 -0
  146. package/src/agents/claudePretoolGate.ts +173 -0
  147. package/src/agents/gateServer.test.ts +204 -0
  148. package/src/agents/gateServer.ts +124 -0
  149. package/src/agents/index.ts +10 -0
  150. package/src/agents/nativeFsDenies.ts +82 -0
  151. package/src/agents/opencode.test.ts +1440 -0
  152. package/src/agents/opencode.ts +1312 -0
  153. package/src/agents/opencodePlugin.ts +222 -0
  154. package/src/agents/opencodeShared.test.ts +34 -0
  155. package/src/agents/opencodeShared.ts +121 -0
  156. package/src/agents/postRun.test.ts +549 -0
  157. package/src/agents/postRun.ts +535 -0
  158. package/src/agents/reviewer.ts +104 -0
  159. package/src/agents/sessionLabeler.test.ts +247 -0
  160. package/src/agents/sessionLabeler.ts +178 -0
  161. package/src/agents/shared.test.ts +76 -0
  162. package/src/agents/shared.ts +292 -0
  163. package/src/agents/subagentModels.test.ts +113 -0
  164. package/src/agents/subagentModels.ts +40 -0
  165. package/src/agents/subagentRegistration.test.ts +41 -0
  166. package/src/agents/subagentToolGates.ts +114 -0
  167. package/src/cli.test.ts +129 -0
  168. package/src/cli.ts +105 -0
  169. package/src/commands/gha.test.ts +192 -0
  170. package/src/commands/gha.ts +188 -0
  171. package/src/commands/mcp.ts +122 -0
  172. package/src/config.ts +1 -0
  173. package/src/entry.ts +7 -0
  174. package/src/entryPost.stdlibOnly.test.ts +109 -0
  175. package/src/entryPost.ts +99 -0
  176. package/src/external.test.ts +16 -0
  177. package/src/external.ts +302 -0
  178. package/src/index.ts +11 -0
  179. package/src/internal/index.ts +71 -0
  180. package/src/lifecycle.ts +2 -0
  181. package/src/main.test.ts +873 -0
  182. package/src/main.ts +712 -0
  183. package/src/mcp/__fixtures__/terramend-scratch-pr-49-review-3485940013.json +110 -0
  184. package/src/mcp/__fixtures__/terramend-scratch-pr-64-review-3531000326.json +14 -0
  185. package/src/mcp/__fixtures__/terramend-test-repo-pr-1.diff.json +67 -0
  186. package/src/mcp/__snapshots__/checkout.test.ts.snap +109 -0
  187. package/src/mcp/__snapshots__/reviewComments.test.ts.snap +71 -0
  188. package/src/mcp/arkConfig.ts +7 -0
  189. package/src/mcp/checkSuite.test.ts +245 -0
  190. package/src/mcp/checkSuite.ts +255 -0
  191. package/src/mcp/checkout.test.ts +752 -0
  192. package/src/mcp/checkout.ts +886 -0
  193. package/src/mcp/comment.test.ts +772 -0
  194. package/src/mcp/comment.ts +582 -0
  195. package/src/mcp/commitInfo.test.ts +127 -0
  196. package/src/mcp/commitInfo.ts +61 -0
  197. package/src/mcp/crosswalk.test.ts +106 -0
  198. package/src/mcp/crosswalk.ts +339 -0
  199. package/src/mcp/dependencies.test.ts +309 -0
  200. package/src/mcp/dependencies.ts +189 -0
  201. package/src/mcp/geminiSanitizer.test.ts +287 -0
  202. package/src/mcp/geminiSanitizer.ts +207 -0
  203. package/src/mcp/git.test.ts +1083 -0
  204. package/src/mcp/git.ts +890 -0
  205. package/src/mcp/guardrails.test.ts +705 -0
  206. package/src/mcp/guardrails.ts +465 -0
  207. package/src/mcp/issue.test.ts +113 -0
  208. package/src/mcp/issue.ts +73 -0
  209. package/src/mcp/issueComments.test.ts +69 -0
  210. package/src/mcp/issueComments.ts +48 -0
  211. package/src/mcp/issueEvents.test.ts +134 -0
  212. package/src/mcp/issueEvents.ts +100 -0
  213. package/src/mcp/issueInfo.test.ts +104 -0
  214. package/src/mcp/issueInfo.ts +72 -0
  215. package/src/mcp/labels.test.ts +52 -0
  216. package/src/mcp/labels.ts +34 -0
  217. package/src/mcp/localContext.ts +28 -0
  218. package/src/mcp/localServer.test.ts +75 -0
  219. package/src/mcp/localServer.ts +131 -0
  220. package/src/mcp/moduleExtraction.test.ts +261 -0
  221. package/src/mcp/moduleExtraction.ts +313 -0
  222. package/src/mcp/moduleTests.test.ts +269 -0
  223. package/src/mcp/moduleTests.ts +421 -0
  224. package/src/mcp/modules.test.ts +640 -0
  225. package/src/mcp/modules.ts +696 -0
  226. package/src/mcp/output.test.ts +96 -0
  227. package/src/mcp/output.ts +70 -0
  228. package/src/mcp/pathSafety.test.ts +44 -0
  229. package/src/mcp/pathSafety.ts +28 -0
  230. package/src/mcp/policy.test.ts +282 -0
  231. package/src/mcp/policy.ts +199 -0
  232. package/src/mcp/pr.test.ts +387 -0
  233. package/src/mcp/pr.ts +194 -0
  234. package/src/mcp/prInfo.test.ts +96 -0
  235. package/src/mcp/prInfo.ts +91 -0
  236. package/src/mcp/providerSchema.test.ts +85 -0
  237. package/src/mcp/providerSchema.ts +175 -0
  238. package/src/mcp/review.test.ts +936 -0
  239. package/src/mcp/review.ts +923 -0
  240. package/src/mcp/reviewComments.test.ts +549 -0
  241. package/src/mcp/reviewComments.ts +896 -0
  242. package/src/mcp/roots.test.ts +175 -0
  243. package/src/mcp/roots.ts +217 -0
  244. package/src/mcp/scope.test.ts +59 -0
  245. package/src/mcp/scope.ts +65 -0
  246. package/src/mcp/security.test.ts +720 -0
  247. package/src/mcp/selectMode.test.ts +210 -0
  248. package/src/mcp/selectMode.ts +181 -0
  249. package/src/mcp/server.test.ts +292 -0
  250. package/src/mcp/server.ts +403 -0
  251. package/src/mcp/shared.ts +100 -0
  252. package/src/mcp/shell.test.ts +520 -0
  253. package/src/mcp/shell.ts +505 -0
  254. package/src/mcp/staleFix.test.ts +237 -0
  255. package/src/mcp/staleFix.ts +277 -0
  256. package/src/mcp/terraform/cost.ts +163 -0
  257. package/src/mcp/terraform/currency.test.ts +338 -0
  258. package/src/mcp/terraform/currency.ts +336 -0
  259. package/src/mcp/terraform/decisions.ts +527 -0
  260. package/src/mcp/terraform/findings.ts +333 -0
  261. package/src/mcp/terraform/plan.ts +348 -0
  262. package/src/mcp/terraform/scanners.ts +809 -0
  263. package/src/mcp/terraform/tools.test.ts +1071 -0
  264. package/src/mcp/terraform/tools.ts +908 -0
  265. package/src/mcp/terraform/types.ts +305 -0
  266. package/src/mcp/terraform.test.ts +1957 -0
  267. package/src/mcp/terraform.ts +23 -0
  268. package/src/mcp/terratest.test.ts +105 -0
  269. package/src/mcp/terratest.ts +196 -0
  270. package/src/mcp/toolFiltering.test.ts +85 -0
  271. package/src/mcp/upload.test.ts +180 -0
  272. package/src/mcp/upload.ts +112 -0
  273. package/src/models.test.ts +300 -0
  274. package/src/models.ts +708 -0
  275. package/src/modes.test.ts +107 -0
  276. package/src/modes.ts +880 -0
  277. package/src/prep/index.ts +43 -0
  278. package/src/prep/installNodeDependencies.test.ts +298 -0
  279. package/src/prep/installNodeDependencies.ts +196 -0
  280. package/src/prep/installPythonDependencies.test.ts +268 -0
  281. package/src/prep/installPythonDependencies.ts +199 -0
  282. package/src/prep/types.ts +38 -0
  283. package/src/reviewQuality.test.ts +63 -0
  284. package/src/reviewQuality.ts +134 -0
  285. package/src/runCli.test.ts +214 -0
  286. package/src/runCli.ts +282 -0
  287. package/src/skills/terraform-best-practices/SKILL.md +369 -0
  288. package/src/toolState.test.ts +45 -0
  289. package/src/toolState.ts +252 -0
  290. package/src/utils/activity.test.ts +188 -0
  291. package/src/utils/activity.ts +210 -0
  292. package/src/utils/agent.test.ts +251 -0
  293. package/src/utils/agent.ts +139 -0
  294. package/src/utils/agentHangReport.test.ts +203 -0
  295. package/src/utils/agentHangReport.ts +170 -0
  296. package/src/utils/apiFetch.test.ts +115 -0
  297. package/src/utils/apiFetch.ts +62 -0
  298. package/src/utils/apiKeys.test.ts +344 -0
  299. package/src/utils/apiKeys.ts +206 -0
  300. package/src/utils/apiUrl.test.ts +30 -0
  301. package/src/utils/apiUrl.ts +59 -0
  302. package/src/utils/assets.test.ts +153 -0
  303. package/src/utils/assets.ts +107 -0
  304. package/src/utils/billingErrors.test.ts +121 -0
  305. package/src/utils/billingErrors.ts +189 -0
  306. package/src/utils/body.test.ts +217 -0
  307. package/src/utils/body.ts +168 -0
  308. package/src/utils/buildTerramendFooter.test.ts +38 -0
  309. package/src/utils/buildTerramendFooter.ts +82 -0
  310. package/src/utils/byokFallback.test.ts +205 -0
  311. package/src/utils/byokFallback.ts +128 -0
  312. package/src/utils/claudeSubscription.test.ts +179 -0
  313. package/src/utils/claudeSubscription.ts +93 -0
  314. package/src/utils/cli.ts +31 -0
  315. package/src/utils/codexHome.test.ts +190 -0
  316. package/src/utils/codexHome.ts +191 -0
  317. package/src/utils/codexOAuth.ts +147 -0
  318. package/src/utils/codexRefreshDetect.test.ts +85 -0
  319. package/src/utils/codexRefreshDetect.ts +35 -0
  320. package/src/utils/diffCoverage.test.ts +468 -0
  321. package/src/utils/diffCoverage.ts +404 -0
  322. package/src/utils/errorReport.test.ts +135 -0
  323. package/src/utils/errorReport.ts +83 -0
  324. package/src/utils/exitHandler.ts +35 -0
  325. package/src/utils/fixDoubleEscapedString.ts +9 -0
  326. package/src/utils/ghaCore.ts +13 -0
  327. package/src/utils/gitAuth.test.ts +322 -0
  328. package/src/utils/gitAuth.ts +263 -0
  329. package/src/utils/gitAuthServer.test.ts +260 -0
  330. package/src/utils/gitAuthServer.ts +182 -0
  331. package/src/utils/github.test.ts +615 -0
  332. package/src/utils/github.ts +538 -0
  333. package/src/utils/globals.ts +9 -0
  334. package/src/utils/humanEditCapture.test.ts +100 -0
  335. package/src/utils/humanEditCapture.ts +193 -0
  336. package/src/utils/install.test.ts +768 -0
  337. package/src/utils/install.ts +492 -0
  338. package/src/utils/instructions.test.ts +240 -0
  339. package/src/utils/instructions.ts +543 -0
  340. package/src/utils/leapingComment.test.ts +51 -0
  341. package/src/utils/leapingComment.ts +18 -0
  342. package/src/utils/learnings.test.ts +87 -0
  343. package/src/utils/learnings.ts +138 -0
  344. package/src/utils/learningsTocRender.test.ts +116 -0
  345. package/src/utils/learningsTruncate.test.ts +39 -0
  346. package/src/utils/learningsTruncate.ts +42 -0
  347. package/src/utils/lifecycle.test.ts +195 -0
  348. package/src/utils/lifecycle.ts +198 -0
  349. package/src/utils/log.test.ts +402 -0
  350. package/src/utils/log.ts +432 -0
  351. package/src/utils/normalizeEnv.test.ts +91 -0
  352. package/src/utils/normalizeEnv.ts +106 -0
  353. package/src/utils/openCodeModels.ts +82 -0
  354. package/src/utils/overrides.test.ts +89 -0
  355. package/src/utils/overrides.ts +98 -0
  356. package/src/utils/packageManager.test.ts +321 -0
  357. package/src/utils/packageManager.ts +257 -0
  358. package/src/utils/patchWorkflowRunFields.test.ts +92 -0
  359. package/src/utils/patchWorkflowRunFields.ts +150 -0
  360. package/src/utils/payload.test.ts +497 -0
  361. package/src/utils/payload.ts +371 -0
  362. package/src/utils/postApiFetch.ts +51 -0
  363. package/src/utils/prSummary.test.ts +224 -0
  364. package/src/utils/prSummary.ts +147 -0
  365. package/src/utils/progressComment.ts +261 -0
  366. package/src/utils/providerErrors.test.ts +315 -0
  367. package/src/utils/providerErrors.ts +172 -0
  368. package/src/utils/rangeDiff.test.ts +236 -0
  369. package/src/utils/rangeDiff.ts +182 -0
  370. package/src/utils/remediationCommand.test.ts +163 -0
  371. package/src/utils/remediationCommand.ts +119 -0
  372. package/src/utils/retry.test.ts +153 -0
  373. package/src/utils/retry.ts +58 -0
  374. package/src/utils/reviewCleanup.ts +106 -0
  375. package/src/utils/run.ts +99 -0
  376. package/src/utils/runContext.ts +145 -0
  377. package/src/utils/runContextData.ts +58 -0
  378. package/src/utils/runErrorRenderer.test.ts +95 -0
  379. package/src/utils/runErrorRenderer.ts +259 -0
  380. package/src/utils/runFixture.ts +76 -0
  381. package/src/utils/runLifecycle.ts +237 -0
  382. package/src/utils/runStartupLog.ts +60 -0
  383. package/src/utils/secrets.test.ts +103 -0
  384. package/src/utils/secrets.ts +177 -0
  385. package/src/utils/setup.test.ts +509 -0
  386. package/src/utils/setup.ts +352 -0
  387. package/src/utils/shell.ts +103 -0
  388. package/src/utils/skills.test.ts +46 -0
  389. package/src/utils/skills.ts +67 -0
  390. package/src/utils/subprocess.test.ts +170 -0
  391. package/src/utils/subprocess.ts +438 -0
  392. package/src/utils/terraformMcp.test.ts +63 -0
  393. package/src/utils/terraformMcp.ts +83 -0
  394. package/src/utils/time.test.ts +105 -0
  395. package/src/utils/time.ts +59 -0
  396. package/src/utils/timer.test.ts +91 -0
  397. package/src/utils/timer.ts +72 -0
  398. package/src/utils/todoTracking.test.ts +223 -0
  399. package/src/utils/todoTracking.ts +167 -0
  400. package/src/utils/token.test.ts +239 -0
  401. package/src/utils/token.ts +186 -0
  402. package/src/utils/version.ts +10 -0
  403. package/src/utils/versioning.test.ts +34 -0
  404. package/src/utils/versioning.ts +44 -0
  405. package/src/utils/vertex.ts +85 -0
  406. package/src/utils/workflow.ts +25 -0
package/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # Terramend
2
+
3
+ [![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](LICENSE)
4
+ [![CI](https://github.com/terramend/terramend/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/terramend/terramend/actions/workflows/test.yml)
5
+ [![Release](https://img.shields.io/github/v/release/terramend/terramend?sort=semver)](https://github.com/terramend/terramend/releases)
6
+ [![Use this GitHub Action](https://img.shields.io/badge/GitHub%20Marketplace-Use%20this%20Action-2ea44f?logo=github)](https://github.com/marketplace/actions/terramend)
7
+
8
+ **Terramend brings your Terraform up to best practice — automatically, as reviewable pull requests.**
9
+
10
+ Terramend is an open-source ([AGPL-3.0](#licence)) GitHub Action and agent runtime. Point it at a
11
+ repository and it scans the Terraform with standard deterministic tools, then opens **one scoped,
12
+ reviewable pull request per concern** that fixes the issue and **proves it fixed** by re-scanning the
13
+ branch (✗ → ✓). It never auto-merges — a human always reviews.
14
+
15
+ - **It proves its own fixes.** The PR body records `✗ → ✓ <rule> resolved`, produced by re-running the
16
+ same deterministic scanners on the branch. Anyone can reproduce it — evidence, not a claim.
17
+ - **Tools decide, the LLM assists.** Findings come from `terraform fmt`/`validate`, tflint, Trivy and
18
+ Checkov — not the model's opinion. The agent only applies the minimal, constrained fix.
19
+ - **One scoped PR per concern.** Small, reviewable diffs on stable `remediate/<id>` branches. Re-runs
20
+ update the existing PR rather than opening duplicates.
21
+ - **Guardrails enforced in code, not prompts.** Terraform-only edits, no inlined secrets, no destroying
22
+ stateful data, never auto-merges — all fail-closed at push time.
23
+ - **Module-aware.** Fixes land at the module source, version upgrades arrive as scoped `chore(deps)`
24
+ PRs, and resource piles become module calls only when a pure-`moved` plan proves the refactor is a
25
+ no-op.
26
+ - **Bring your own key, no hosted backend.** Supply your own LLM key, pointed at an approved endpoint
27
+ where data residency matters. Nothing leaves your runner that you didn't configure.
28
+
29
+ ## Quickstart
30
+
31
+ ```yaml
32
+ name: Terramend — Terraform remediation
33
+ on:
34
+ workflow_dispatch:
35
+ schedule:
36
+ - cron: "0 6 * * 1" # weekly drift sweep
37
+
38
+ permissions:
39
+ contents: write # push the remediation branch
40
+ pull-requests: write # open the PR
41
+
42
+ jobs:
43
+ remediate:
44
+ runs-on: ubuntu-latest
45
+ steps:
46
+ # Pin third-party actions to a commit SHA — a tag can be force-repointed.
47
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
48
+
49
+ # install the Terraform best-practice toolchain (absent tools are skipped, never fatal)
50
+ - uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3
51
+ - uses: terraform-linters/setup-tflint@90f302c255ef959cbfb4bd10581afecdb7ece3e6 # v4
52
+ - uses: aquasecurity/setup-trivy@81e514348e19b6112ce2a7e3ecbafe19c1e1f567 # v0.3.1
53
+ - run: pipx install checkov
54
+
55
+ - name: Run Terramend
56
+ uses: terramend/terramend@v0
57
+ with:
58
+ mode: remediate
59
+ severity_threshold: medium # only act on medium+ concerns
60
+ max_prs: 1 # one scoped PR per run
61
+ env:
62
+ # bring your own LLM key (BYOK)
63
+ ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
64
+ GITHUB_TOKEN: ${{ github.token }}
65
+ ```
66
+
67
+ > **Ready-to-use workflows:** [`examples/`](examples/) has copy-pasteable workflows — scheduled
68
+ > [remediation](examples/remediate.yml), [generation](examples/generate-terraform.yml),
69
+ > [comment-triggered fixes](examples/comment-fix.yml), and the full
70
+ > [SARIF + plan-gate + policy setup](examples/remediate-advanced.yml).
71
+
72
+ ## How it works
73
+
74
+ ```mermaid
75
+ flowchart LR
76
+ A[Scan<br/>fmt · validate · tflint<br/>Trivy · Checkov] --> B[Concerns<br/>severity-ranked,<br/>grouped]
77
+ B --> C[Fix<br/>minimal change,<br/>Terraform-only]
78
+ C --> D[Validate<br/>fmt · validate · tflint]
79
+ D --> E[Plan gate<br/>optional, with creds]
80
+ E --> F[Open one<br/>scoped PR]
81
+ F --> G[Re-scan branch<br/>✗ → ✓ proof]
82
+ G -.->|regression / needs-human| H[Label for a human]
83
+ ```
84
+
85
+ **Scanners find the problem, the agent applies the minimal fix, and the scanners verify it** before a
86
+ single PR is opened. Coverage is inherited, not reinvented: findings come from the scanners Terramend
87
+ runs (Checkov's 1,000+ policies, Trivy's AVD checks, tflint's provider rulesets, `fmt`/`validate`), so
88
+ new upstream checks show up the day you update the scanner. The PR's **Validation (✗ → ✓)** section is
89
+ the part you can trust without trusting Terramend — re-run the same scanners on the branch and
90
+ reproduce it. Higher-risk fixes (a regression, a stateful destroy/replace, a large blast radius, a
91
+ non-deterministic plan) get a `> [!CAUTION]` banner and a `needs-human` label.
92
+
93
+ ## How Terramend compares
94
+
95
+ | | Reports findings | Fixes the code | Proves the fix | Opens a PR | Auto-merges |
96
+ | --- | :---: | :---: | :---: | :---: | :---: |
97
+ | **Scanners** (Checkov, Trivy, tfsec, tflint) | ✅ | ❌ | ❌ | ❌ | — |
98
+ | **Plan orchestrators** (Atlantis, Digger) | ❌ | ❌ | ❌ | comments on yours | ❌ |
99
+ | **Dependency bots** (Dependabot, Renovate) | ✅ (deps) | ✅ (version bumps) | ❌ | ✅ | optional |
100
+ | **Auto-fix AI bots** | partial | ✅ | rarely | ✅ | often |
101
+ | **Terramend** | ✅ | ✅ | ✅ (✗ → ✓ re-scan) | ✅ (one per concern) | **never** |
102
+
103
+ ## Documentation
104
+
105
+ | Doc | What's in it |
106
+ | --- | --- |
107
+ | [Action inputs & outputs](docs/action-inputs.md) | The complete `action.yml` reference (generated — never drifts) |
108
+ | [Configuration](docs/configuration.md) | Modes, comment-scoped runs, scoping out findings, the plan gate & OIDC roles, BYOK, SARIF, modules |
109
+ | [Security model](docs/security-model.md) | The code-level guardrails and the trust/data-privacy story |
110
+ | [MCP server](docs/mcp.md) | `terramend mcp` in your IDE + pairing with HashiCorp's terraform-mcp-server |
111
+ | [Tools](docs/tools.md) | Every MCP tool the agent uses, and the CLI binaries they shell out to |
112
+ | [Supported models](docs/models.md) | The model catalog and how selection works (generated) |
113
+
114
+ ## Support
115
+
116
+ - **Getting started / usage** — this README, the [docs](docs/), and the [`examples/`](examples/) workflows.
117
+ - **Bug reports & feature requests** — open a [GitHub issue](https://github.com/terramend/terramend/issues).
118
+ - **Security vulnerabilities** — **don't** use a public issue; see [Security](#security) below.
119
+
120
+ ## Contributing
121
+
122
+ Contributions are welcome. Terramend standardises on **Node 24** and **pnpm 11**:
123
+
124
+ ```bash
125
+ corepack enable
126
+ pnpm install --frozen-lockfile
127
+ pnpm typecheck
128
+ pnpm test
129
+ ```
130
+
131
+ All contributions are accepted under the [Contributor License Agreement](CLA.md) (enforced by the CLA
132
+ Assistant on your first PR), and releases are automated from [Conventional Commits](https://www.conventionalcommits.org)
133
+ via release-please. See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the full development, commit, and
134
+ action-pinning conventions.
135
+
136
+ ## Security
137
+
138
+ Terramend runs AI coding agents with write access to repositories and CI secrets, and is positioned for
139
+ security- and compliance-sensitive use. **Please don't open public issues for vulnerabilities** — report
140
+ them privately via [GitHub Security Advisories](https://github.com/terramend/terramend/security/advisories/new).
141
+ See [`SECURITY.md`](SECURITY.md) for scope, supported versions, and response targets.
142
+
143
+ ## Licence
144
+
145
+ Terramend is licensed under the **GNU Affero General Public License v3.0 or later** (AGPL-3.0-or-later).
@@ -0,0 +1,73 @@
1
+ import { type AgentResult, type AgentRunContext } from "#app/agents/shared";
2
+ import type { TodoTracker } from "#app/utils/todoTracking";
3
+ export declare const CLAUDE_EXEC_TOOL_DENY_RULES: string[];
4
+ export declare function writeMcpConfig(ctx: AgentRunContext): string;
5
+ /**
6
+ * Build the `--agents` JSON definition for the `reviewfrog` subagent.
7
+ *
8
+ * The Claude Code path always runs against an Anthropic model (see
9
+ * resolveAgent), so we hardcode the cheaper-sibling downshift: lenses run
10
+ * on Sonnet, the orchestrator stays on whatever model `--model` was passed.
11
+ *
12
+ * Per-call model override is also possible (Task tool's `model` arg accepts
13
+ * 'sonnet' | 'opus' | 'haiku') and takes precedence over what's set here —
14
+ * we don't pass it; the per-subagent `model` field is the right default.
15
+ *
16
+ * The non-mutative + non-recursive contract is enforced by the prose system
17
+ * prompt baked into the agent — see action/agents/reviewer.ts for why we
18
+ * no longer wire per-agent `disallowedTools` here.
19
+ */
20
+ export declare function buildAgentsJson(): string;
21
+ export declare function stripProviderPrefix(specifier: string): string;
22
+ type RunParams = {
23
+ label: string;
24
+ cmd: string;
25
+ args: string[];
26
+ cwd: string;
27
+ env: Record<string, string | undefined>;
28
+ todoTracker?: TodoTracker | undefined;
29
+ onActivityTimeout?: (() => void) | undefined;
30
+ onToolUse?: ((event: {
31
+ toolName: string;
32
+ input: unknown;
33
+ }) => void) | undefined;
34
+ };
35
+ type ClaudeRunResult = AgentResult & {
36
+ sessionId?: string | undefined;
37
+ };
38
+ /**
39
+ * Return the tail of `text` capped at `maxCodeUnits` UTF-16 code units,
40
+ * dropping any partial first line. used in the exit-non-zero stdout fallback
41
+ * so we never surface a truncated NDJSON event to operators —
42
+ * `result.stdout.slice(-2048)` would otherwise cut mid-line and produce a
43
+ * syntactically broken JSON fragment. code units rather than bytes because
44
+ * `String.prototype.slice` operates on UTF-16 units; for multi-byte UTF-8
45
+ * content the effective byte budget can be up to 4× the nominal limit.
46
+ */
47
+ export declare function tailLines(text: string, maxCodeUnits: number): string;
48
+ export declare function runClaude(params: RunParams): Promise<ClaudeRunResult>;
49
+ /**
50
+ * managed Stop hook. swaps the old `--resume <sessionId>` follow-up
51
+ * subprocesses (reflection + every gate retry — cost audit on PR #792
52
+ * showed reflection alone burned ~$0.85 / 111K cache_write per Opus run,
53
+ * almost all of it wasted re-running `getAttachmentMessages` in the fresh
54
+ * process) for a `{decision: "block", reason: ...}` injection inside the
55
+ * live `queryLoop`. existing session context is already in the prompt
56
+ * cache so only the new reason text is fresh cache_write.
57
+ *
58
+ * the script is intentionally minimal — all decision logic lives in the
59
+ * sidecar gate server (`gateServer.ts`), which reads live `ctx.toolState`
60
+ * mutations from the same process the MCP server runs in. budget +
61
+ * one-shot tracking lives there too, so re-fires across multiple stops in
62
+ * one session are safe. claude-code's 8-consecutive-block override is the
63
+ * last-line backstop.
64
+ */
65
+ export declare function buildStopHookScript(): string;
66
+ export interface ManagedSettingsParams {
67
+ ctx: AgentRunContext;
68
+ stopHookPath: string | null;
69
+ pretoolGateScriptPath: string;
70
+ }
71
+ export declare function buildManagedSettings(params: ManagedSettingsParams): Record<string, unknown>;
72
+ export declare const claude: import("#app/agents/shared").Agent;
73
+ export {};
@@ -0,0 +1,99 @@
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 (`reviewfrog` 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 share the deny list at
11
+ * action/agents/subagentToolGates.ts.
12
+ *
13
+ * PreToolUse hook contract (verified against yasasbanukaofficial/claude-code
14
+ * `src/utils/hooks/hooksConfigManager.ts` and `src/utils/hooks.ts`):
15
+ * - stdin: JSON with `hook_event_name: "PreToolUse"`, `tool_name`,
16
+ * `tool_input`, `tool_use_id`, `session_id`, `cwd`, `transcript_path`,
17
+ * and crucially `agent_id` / `agent_type` populated when the call
18
+ * originates from a subagent (set by the SDK when a Task/Agent
19
+ * dispatches a tool — see `createBaseHookInput` in claude-code source).
20
+ * - exit 0 → allow, no output shown
21
+ * - exit 2 → block tool call AND show stderr to model (this is the path
22
+ * we want for the deny case — the subagent gets a clear refusal it can
23
+ * reason about and pick a different action)
24
+ * - other → show stderr to user only, continue with tool call
25
+ *
26
+ * The hook itself is intentionally tiny: stdin → JSON → check `agent_id`
27
+ * presence + `tool_name` against the deny list → exit 0 or 2. No deps.
28
+ *
29
+ * Why the script source is a string template, not a separate `.ts` file
30
+ * shipped with the action: the action runs as a published npm package; at
31
+ * install time we don't have the source on disk in a stable place. Embedding
32
+ * the source into `dist/main.mjs` and writing it out per-run keeps the path
33
+ * inside `ctx.tmpdir` (where `--settings` can find it) and survives bundle
34
+ * minification.
35
+ */
36
+ /**
37
+ * The pinned `@anthropic-ai/claude-code` version against which the subagent
38
+ * gate's `agent_id` discriminator was last verified (see the contract notes in
39
+ * the gate source below). The gate fails OPEN for subagents if claude-code ever
40
+ * stops populating `agent_id` in the PreToolUse hook payload, so a version bump
41
+ * must be paired with a re-verification of `createBaseHookInput`.
42
+ *
43
+ * `claudePretoolGate.test.ts` asserts this equals the version pinned in
44
+ * `package.json` — that test fails on any bump, forcing the re-verification
45
+ * before the pin and this constant are updated together.
46
+ *
47
+ * 2.1.170 verified 2026-06-10 against the schema embedded in the shipped
48
+ * binary: the base hook input declares `agent_id` as optional with the
49
+ * describe-text "Present only when the hook fires from within a subagent
50
+ * (e.g., a tool called by an AgentTool worker). Absent for the main thread,
51
+ * even in --agent sessions." — exactly the discriminator the gate relies on.
52
+ */
53
+ export declare const CLAUDE_CODE_AGENT_ID_VERIFIED_VERSION: "2.1.170";
54
+ /**
55
+ * Source written to `<ctx.tmpdir>/terramend-pretool-gate.mjs`. Plain ESM,
56
+ * no TypeScript, no dependencies — node executes it directly via the
57
+ * `#!/usr/bin/env node` shebang and the executable bit set by the harness.
58
+ */
59
+ export declare const CLAUDE_PRETOOL_GATE_FILENAME: "terramend-pretool-gate.mjs";
60
+ export declare const CLAUDE_PRETOOL_GATE_SOURCE: string;
61
+ /**
62
+ * Settings JSON shape registered via `claude --settings <path>`. The
63
+ * matcher `^mcp__terramend__` is treated as a regex by claude-code's
64
+ * `matchesPattern` helper (anything outside `[a-zA-Z0-9_|]` triggers the
65
+ * regex branch — verified in src/utils/hooks.ts), so this anchors at the
66
+ * start of the tool name and fires for every Terramend MCP tool. We narrow
67
+ * inside the script itself rather than declaring per-tool matchers because
68
+ * the deny list is the source of truth.
69
+ *
70
+ * The hook process inherits the parent's PATH, so `node` resolves to the
71
+ * runner's node binary; the `--settings` flag accepts either a path or a
72
+ * literal JSON string per claude-code source `src/main.tsx` (`Path to a
73
+ * settings JSON file or a JSON string`), but we use a path so the script
74
+ * and its config sit side-by-side under `ctx.tmpdir`.
75
+ *
76
+ * `execToolDenyRules` are the native exec tools (Bash/Monitor/REPL/Workflow +
77
+ * their `Agent(...)` forms) to deny at a settings-source rule — the
78
+ * authoritative, bypass-immune layer. `--disallowedTools` alone (a `cliArg`
79
+ * deny) was observed to leak under `--dangerously-skip-permissions`, so the
80
+ * deny is carried here too. Both consumers use both returned fields: the flag
81
+ * `--settings` JSON (covers non-CI runs) writes the whole object, and
82
+ * `buildManagedSettings` (CI, /etc managed settings) spreads `hooks` and folds
83
+ * `permissions.deny` into its richer deny list.
84
+ */
85
+ export declare function buildClaudePretoolGateSettings(scriptAbsolutePath: string, execToolDenyRules: string[]): {
86
+ hooks: {
87
+ PreToolUse: Array<{
88
+ matcher: string;
89
+ hooks: Array<{
90
+ type: "command";
91
+ command: string;
92
+ timeout?: number;
93
+ }>;
94
+ }>;
95
+ };
96
+ permissions: {
97
+ deny: string[];
98
+ };
99
+ };
@@ -0,0 +1,7 @@
1
+ import { type AgentRunContext } from "#app/agents/shared";
2
+ export interface GateServerHandle {
3
+ url: string;
4
+ token: string;
5
+ [Symbol.asyncDispose]: () => Promise<void>;
6
+ }
7
+ export declare function startGateServer(ctx: AgentRunContext): Promise<GateServerHandle>;
@@ -0,0 +1,6 @@
1
+ import type { Agent } from "#app/agents/shared";
2
+ export type { Agent, AgentUsage } from "#app/agents/shared";
3
+ export declare const agents: {
4
+ claude: Agent;
5
+ opencode: Agent;
6
+ };
@@ -0,0 +1,28 @@
1
+ /** worktree-relative blanket WRITE deny for the entire `.git` tree, in
2
+ * OpenCode Wildcard dialect (`*` compiles to regex `.*`, matching `/`
3
+ * recursively — see packages/core/src/util/wildcard.ts). spread into the
4
+ * `edit` ruleset after a `"*": "allow"` baseline — `evaluate` is
5
+ * last-match-wins by key order, so the deny keys must follow the wildcard
6
+ * allow.
7
+ *
8
+ * four patterns, because the root-anchored descendants glob only matches
9
+ * paths under a root `.git` *directory* — it misses `.git` when it's a gitfile
10
+ * (worktree / submodule layouts: a regular file whose `gitdir:` line redirects
11
+ * git metadata) and misses nested gitfiles (a `.git` inside a subdirectory).
12
+ * rewriting either pointer is the same code-exec surface (`core.hooksPath`,
13
+ * clean/smudge filters, credential.helper) the blanket deny exists to seal, so
14
+ * we cover the gitfile itself and any nested `.git` too. */
15
+ export declare const GIT_NATIVE_WRITE_DENY_OPENCODE: Record<string, "deny">;
16
+ /** worktree-relative narrow READ deny (`.git/config` only), in OpenCode
17
+ * Wildcard dialect. spread into the `read` ruleset after the `"*": "allow"`
18
+ * baseline. */
19
+ export declare const GIT_NATIVE_READ_DENY_OPENCODE: Record<string, "deny">;
20
+ /** Claude `permissions.deny` entries for the blanket `.git` WRITE deny —
21
+ * mirrors {@link GIT_NATIVE_WRITE_DENY_OPENCODE}. `**` is recursive. the exact
22
+ * `.git` entry plus the recursive-prefix gitfile entry cover the gitfile
23
+ * pointer (root + nested) that the root-anchored descendants glob alone misses;
24
+ * the recursive-prefix descendants entry covers nested gitdirs. */
25
+ export declare const GIT_NATIVE_WRITE_DENY_CLAUDE: string[];
26
+ /** Claude `permissions.deny` entries for the narrow `.git/config` READ deny,
27
+ * one per read/enumerate tool — mirrors {@link GIT_NATIVE_READ_DENY_OPENCODE}. */
28
+ export declare const GIT_NATIVE_READ_DENY_CLAUDE: string[];
@@ -0,0 +1,231 @@
1
+ /**
2
+ * OpenCode agent — in-process harness (opencode-ai >=1.14.x SDK-v2 / Effect-ts
3
+ * CLI rewrite).
4
+ *
5
+ * Architecture, post v2-in-process migration:
6
+ *
7
+ * 1. Spawn ONE `opencode serve --port <p>` subprocess per Terramend run via
8
+ * `node:child_process.spawn` directly (NOT our `spawn()` wrapper — see
9
+ * `bootOpencodeServer` for why: long-lived stdio streaming, manual
10
+ * activity gating against the SDK event loop, killGroup teardown).
11
+ * 2. Talk to it over loopback HTTP via the typed `@opencode-ai/sdk/v2`
12
+ * `createOpencodeClient({ baseUrl })` — no `Server.Default()` embed,
13
+ * no `createOpencode()` SDK lifecycle (would re-wrap our subprocess).
14
+ * 3. Create ONE session up front (`client.session.create`).
15
+ * 4. Subscribe to events once (`client.event.subscribe`) and pump them
16
+ * through a single per-run handler set for live logging + activity
17
+ * tracking + subagent labeling.
18
+ * 5. Run the initial prompt via `client.session.prompt({ sessionID, parts })`.
19
+ * Every post-run gate retry AND the reflection turn re-enter the same
20
+ * session via another `client.session.prompt()` call. Warm MCP, warm
21
+ * plugins, warm provider connections, same context window — no
22
+ * `--continue` subprocess respawn.
23
+ * 6. Close the server in a finally.
24
+ *
25
+ * What that replaces (vs the pre-migration v2 harness):
26
+ * - The per-run `opencode run --format json --print-logs --thinking` CLI
27
+ * subprocess that emitted NDJSON envelopes.
28
+ * - The `runOpenCode(... args: [...baseArgs, "--continue", c.prompt] ...)`
29
+ * resume callback that booted a SECOND opencode process (fresh MCP,
30
+ * fresh plugins, cold cache) for each gate retry / reflection turn.
31
+ * - The `opencodePlugin.ts` bus-event re-emitter — we subscribe to the
32
+ * global event stream now, so subagent events arrive naturally without
33
+ * a stdout sentinel envelope.
34
+ *
35
+ * What stays identical:
36
+ * - bash: "deny" via OPENCODE_CONFIG_CONTENT
37
+ * - OPENCODE_PERMISSION filesystem sandbox — deny-all + allow /tmp
38
+ * - MCP Terramend server injected via `mcp.<name> = { type: "remote", url }`
39
+ * - ASKPASS for git auth
40
+ * - codex auth materialization + post-hook writeback
41
+ * - reviewfrog subagent config / model derivation
42
+ * - bedrock model prefix routing
43
+ * - skills install
44
+ * - todo tracker / onToolUse forwarding
45
+ */
46
+ import { type ChildProcess } from "node:child_process";
47
+ import { type AssistantMessage, type EventSubscribeResponse, type OpencodeClient, type Part } from "@opencode-ai/sdk/v2";
48
+ import { SessionLabeler } from "#app/agents/sessionLabeler";
49
+ import { type AgentResult, type AgentRunContext, type AgentUsage } from "#app/agents/shared";
50
+ import type { ToolState } from "#app/toolState";
51
+ import type { AgentDiagnostic } from "#app/utils/agentHangReport";
52
+ import type { TodoTracker } from "#app/utils/todoTracking";
53
+ export declare function buildSecurityConfig(ctx: AgentRunContext, model: string | undefined): string;
54
+ /** split `<providerID>/<modelID>` into the SDK's prompt model shape. */
55
+ export declare function parseModel(value: string | undefined): {
56
+ providerID: string;
57
+ modelID: string;
58
+ } | undefined;
59
+ interface ServerHandle {
60
+ baseUrl: string;
61
+ proc: ChildProcess;
62
+ /** kill the server; idempotent. */
63
+ close: () => Promise<void>;
64
+ /** rolling tail of server stderr for diagnostics. */
65
+ recentStderr: string[];
66
+ }
67
+ /**
68
+ * Spawn `<cliPath> serve --port 0 --hostname 127.0.0.1` and wait for the
69
+ * "opencode server listening on http://..." stdout line.
70
+ *
71
+ * Direct node:child_process.spawn instead of our `spawn()` wrapper because
72
+ * the wrapper's contract is "Promise<SpawnResult> that resolves on exit" —
73
+ * we need a handle that stays alive across many session.prompt() calls.
74
+ * We still register with `trackChild()` so Ctrl-C kills the server alongside
75
+ * everything else.
76
+ */
77
+ export declare function bootOpencodeServer(params: {
78
+ cliPath: string;
79
+ env: NodeJS.ProcessEnv;
80
+ cwd: string;
81
+ }): Promise<ServerHandle>;
82
+ /**
83
+ * What we collect during a single session.prompt() turn so we can render a
84
+ * unified AgentResult at the end. Per-turn snapshot is reset between turns
85
+ * inside the event loop via `beginTurn()` / `endTurn()`.
86
+ */
87
+ export interface TurnAccumulator {
88
+ finalText: string;
89
+ /**
90
+ * Aggregate token totals from step-finish parts across the orchestrator AND
91
+ * any subagent sessions dispatched during the turn (e.g. reviewfrog).
92
+ * Mirrors v1's `accumulatedTokens` semantics so production billing/audit
93
+ * numbers stay apples-to-apples across the migration.
94
+ */
95
+ tokens: {
96
+ input: number;
97
+ output: number;
98
+ cacheRead: number;
99
+ cacheWrite: number;
100
+ };
101
+ costUsd: number;
102
+ sessionError: string | null;
103
+ /** populated when a tool_use part on the orchestrator session reports error. */
104
+ lastToolError: string | null;
105
+ }
106
+ export declare function newTurn(): TurnAccumulator;
107
+ export interface RunnerContext {
108
+ client: OpencodeClient;
109
+ sessionID: string;
110
+ label: string;
111
+ orchestratorSessionID: string;
112
+ labeler: SessionLabeler;
113
+ toolState: ToolState;
114
+ todoTracker?: TodoTracker | undefined;
115
+ onActivityTimeout?: (() => void) | undefined;
116
+ onToolUse?: ((event: {
117
+ toolName: string;
118
+ input: unknown;
119
+ }) => void) | undefined;
120
+ /** current per-turn aggregator; nullable between turns. */
121
+ currentTurn: TurnAccumulator | null;
122
+ /** monotonic event count for diagnostics. */
123
+ eventCount: number;
124
+ /** last activity timestamp (event-stream silence detector). */
125
+ lastEventAt: number;
126
+ /** active task dispatch metadata keyed by callID (for subagent timing). */
127
+ taskDispatchByCallID: Map<string, {
128
+ label: string;
129
+ startedAt: number;
130
+ }>;
131
+ /**
132
+ * orchestrator tool callIDs already surfaced via `log.info(» ${tool}(...))`,
133
+ * tracked so the end-of-turn fallback can re-emit only the calls the live
134
+ * event stream missed. closes the SSE-connect race against the first
135
+ * `session.prompt()` (the SDK opens the SSE lazily on first iteration; by
136
+ * then the server may already have emitted the turn's tool part-updated
137
+ * events). without the fallback those calls never appear in stdout, which
138
+ * breaks every validator that greps for tool-call shape.
139
+ */
140
+ loggedToolCallIDs: Set<string>;
141
+ /** rolling stderr tail from the server process (for diagnostics). */
142
+ recentStderr: string[];
143
+ diagnostic: AgentDiagnostic;
144
+ }
145
+ /**
146
+ * orchestrate the event stream consumer for the entire server lifetime.
147
+ *
148
+ * NB: the SDK subscribe is lazy — the SSE fetch only opens on the first
149
+ * iteration. so the first turn's tool part-updated events can race the
150
+ * connect and be missed. live-stream logging is best-effort; see the
151
+ * end-of-turn `logUnseenToolCalls` fallback for the guarantee.
152
+ */
153
+ export declare function consumeEvents(ctx: RunnerContext, signal: AbortSignal): Promise<void>;
154
+ export declare function dispatchEvent(ctx: RunnerContext, event: EventSubscribeResponse): Promise<void>;
155
+ /**
156
+ * shared terminal bookkeeping for a tool part: log line, dedup callID, run
157
+ * orchestrator-side hooks (`onToolUse` → diff-coverage tracker; `todowrite` /
158
+ * `report_progress` → todo tracker; tool-error → `lastToolError`), and emit
159
+ * subagent-finish summary on `task` returns.
160
+ *
161
+ * called from both the live SSE path (`onToolPart`) and the end-of-turn
162
+ * fallback (`logUnseenToolCalls`) — `loggedToolCallIDs` is the dedup guard
163
+ * so each call's side effects fire exactly once across both paths. critical
164
+ * for diff-coverage: a first-turn `Read` that races SSE attach would
165
+ * otherwise be missed by `recordDiffReadFromToolUse`, and the subsequent
166
+ * `create_pull_request_review` pre-flight would reject the review.
167
+ */
168
+ export declare function processTerminalToolPart(ctx: RunnerContext, part: Extract<Part, {
169
+ type: "tool";
170
+ }>, label: string, isOrchestrator: boolean): void;
171
+ export declare function formatPartDuration(time: {
172
+ start?: number;
173
+ end?: number;
174
+ } | undefined): string;
175
+ /**
176
+ * Run a single prompt turn against the persistent server. Resets the per-turn
177
+ * accumulator, calls `client.session.prompt()`, then assembles an AgentResult
178
+ * from the returned AssistantMessage + accumulated event state.
179
+ *
180
+ * Token / cost: `AssistantMessage.tokens` and `.cost` are authoritative for
181
+ * the turn. The event-stream accumulator is a fallback / sanity-check path
182
+ * used when the response is missing (e.g. abort, transport error) — and as
183
+ * the only source of per-step subagent attribution if we ever surface it.
184
+ */
185
+ export declare function runPromptTurn(ctx: RunnerContext, params: {
186
+ text: string;
187
+ model: {
188
+ providerID: string;
189
+ modelID: string;
190
+ } | undefined;
191
+ signal: AbortSignal;
192
+ }): Promise<AgentResult>;
193
+ export declare function buildUsage(turn: TurnAccumulator, assistant: AssistantMessage | undefined): AgentUsage | undefined;
194
+ export declare function extractTextFromParts(parts: Part[] | undefined): string | undefined;
195
+ export declare function formatPromptError(error: unknown): string;
196
+ /**
197
+ * Start an event-silence watchdog. The outer process-level activity timer
198
+ * (main.ts `createProcessOutputActivityTimeout`) watches `process.stdout.write`
199
+ * which our harness log lines drive — but it doesn't see SSE event silence
200
+ * when the harness is itself quiet. This inner timer specifically watches
201
+ * `ctx.lastEventAt` and fires `onActivityTimeout` so main.ts can tear down
202
+ * the MCP server early, mirroring the per-spawn watchdog in `subprocess.ts`.
203
+ *
204
+ * `ctx.lastEventAt` is refreshed only on meaningful progress (token/tool
205
+ * part.updated), so any prolonged gap with no progress advances the clock —
206
+ * including a long in-flight tool call. the budget is the same flat idle
207
+ * timeout as the outer watchdog, sized to exceed the worst-case legitimate
208
+ * silent tool window (#760), so a real tool can't trip it; a genuinely stalled
209
+ * provider or a hung tool does, at the flat budget.
210
+ */
211
+ export declare function startInnerActivityWatchdog(params: {
212
+ ctx: RunnerContext;
213
+ timeoutMs: number;
214
+ abortController: AbortController;
215
+ }): {
216
+ stop: () => void;
217
+ };
218
+ export declare const opencode: import("#app/agents/shared").Agent;
219
+ /**
220
+ * Safety net around a single turn: convert any unexpected throw that escapes
221
+ * `runPromptTurn` into a `success: false` result so the post-run gate loop
222
+ * (which expects a result, not a rejection) can surface it through the generic
223
+ * renderer.
224
+ *
225
+ * Watchdog-fired aborts do NOT reach here — `runPromptTurn` owns the abort
226
+ * signal, catches the aborted `session.prompt` rejection internally, and
227
+ * classifies it as an `activity timeout` error itself. This wrapper must not
228
+ * re-classify, since a stray post-prompt throw is not a hang.
229
+ */
230
+ export declare function runTurnGuarded(ctx: RunnerContext, fn: () => Promise<AgentResult>): Promise<AgentResult>;
231
+ export {};