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
@@ -0,0 +1,369 @@
1
+ ---
2
+ name: terraform-best-practices
3
+ description: Fix Terraform to best practice from a scanner concern — the minimal, correct change for a trivy/checkov/tflint/fmt/validate finding, plus the security, structure, and naming conventions a good fix must follow. Use when remediating Terraform, applying a `terraform_scan` concern, or generating new HCL that must start compliant.
4
+ ---
5
+
6
+ # Terraform best-practice remediation
7
+
8
+ You are fixing Terraform against a **concern** emitted by `terraform_scan` (or
9
+ `terraform_validate`). Each concern names the producing `source`, a `rule_id`,
10
+ the `location` (file + line), an `evidence` string (what's wrong), and often a
11
+ `remediation_hint`. Your job is the **smallest correct change** that clears that
12
+ concern — nothing more.
13
+
14
+ ## The remediation loop
15
+
16
+ 1. **Read the concern.** The `rule_id` tells you the class of problem; the
17
+ `evidence` tells you the specifics; `location.file:line` tells you where.
18
+ 2. **Open the file and understand the surrounding resource** before editing.
19
+ Don't fix a line in isolation — know which `resource` / `module` / `variable`
20
+ block it belongs to.
21
+ 3. **Apply the minimal fix** (see the catalogue below). Touch only `*.tf` /
22
+ `*.tfvars`. Do not reformat, reorder, or "improve" unrelated code — that
23
+ buries the real fix and breaks the one-concern-per-PR contract.
24
+ 4. **Re-validate** with `terraform_validate`. If it doesn't pass, your fix is
25
+ incomplete or introduced a new problem — fix that before opening a PR.
26
+ 5. **Confirm the concern cleared** by re-running `terraform_scan` on the branch.
27
+ The concern's `id` must be gone. If it isn't, say so honestly.
28
+
29
+ ## What a good fix looks like, by source
30
+
31
+ - **`terraform-fmt:unformatted`** — run `terraform fmt` on the named file. This
32
+ is whitespace/alignment only; never combine it with a behavioural change in
33
+ the same PR.
34
+ - **`tflint:*`** — idiomatic-HCL and provider-rule issues. Common fixes: remove
35
+ unused `variable`/`local`/`data` declarations; pin deprecated syntax forward;
36
+ add missing required provider/version constraints. Follow the rule's link.
37
+ - **`trivy:*` / `checkov:*`** — security misconfiguration. These are the
38
+ high-value fixes. Apply the **secure default**, e.g.:
39
+ - **encryption at rest** — add the `server_side_encryption_configuration` /
40
+ `encryption` block (S3, RDS, EBS, etc.); prefer a CMK where the rule asks.
41
+ - **no public access** — set `block_public_acls`/`block_public_policy` etc. to
42
+ `true`; remove `acl = "public-read"`; tighten `0.0.0.0/0` ingress to the
43
+ real CIDR or a referenced security group.
44
+ - **least privilege** — replace `"*"` actions/resources in IAM policies with
45
+ the specific actions/ARNs actually needed.
46
+ - **logging / versioning** — add the access-logging, audit, or versioning
47
+ block the rule requires.
48
+ - **`terraform-validate:*`** — a correctness error (bad reference, type
49
+ mismatch, missing required argument). Fix the actual HCL so `terraform
50
+ validate` passes.
51
+
52
+ ## Secure-default catalogue (by problem class)
53
+
54
+ Copy-pasteable shapes for the highest-frequency security concerns. These target
55
+ the **AWS provider v5+** layout (encryption / versioning / ACL / public-access
56
+ are standalone resources, not inline `aws_s3_bucket` blocks). Adapt resource and
57
+ attribute names to the block the concern points at — don't paste blindly.
58
+
59
+ ### Encryption at rest
60
+
61
+ S3 — server-side encryption with a customer-managed key (preferred over `AES256`
62
+ when the rule asks for a CMK):
63
+
64
+ ```hcl
65
+ resource "aws_s3_bucket_server_side_encryption_configuration" "this" {
66
+ bucket = aws_s3_bucket.this.id
67
+ rule {
68
+ apply_server_side_encryption_by_default {
69
+ sse_algorithm = "aws:kms"
70
+ kms_master_key_id = aws_kms_key.this.arn
71
+ }
72
+ bucket_key_enabled = true
73
+ }
74
+ }
75
+ ```
76
+
77
+ EBS volume / root device: `encrypted = true` (add `kms_key_id` when a CMK is
78
+ required). RDS: `storage_encrypted = true` (+ `kms_key_id`). When `aws:kms` needs
79
+ a key and none exists, reference an existing `aws_kms_key`/`var.*`; only add a new
80
+ `aws_kms_key` resource if the stack genuinely has none — keep `AES256` if the rule
81
+ is satisfied by SSE-S3 alone.
82
+
83
+ ### Block public access
84
+
85
+ S3 — the four-flag public-access block is the canonical fix; wire it to the same
86
+ bucket:
87
+
88
+ ```hcl
89
+ resource "aws_s3_bucket_public_access_block" "this" {
90
+ bucket = aws_s3_bucket.this.id
91
+ block_public_acls = true
92
+ block_public_policy = true
93
+ ignore_public_acls = true
94
+ restrict_public_buckets = true
95
+ }
96
+ ```
97
+
98
+ Also remove any `acl = "public-read"` (move to a private `aws_s3_bucket_acl` if an
99
+ ACL is needed at all).
100
+
101
+ ### Network ingress
102
+
103
+ Replace world-open ingress with the real source. Never leave `0.0.0.0/0` on admin
104
+ ports (22/3389/database ports):
105
+
106
+ ```hcl
107
+ ingress {
108
+ from_port = 443
109
+ to_port = 443
110
+ protocol = "tcp"
111
+ cidr_blocks = [var.allowed_cidr] # or: security_groups = [aws_security_group.lb.id]
112
+ }
113
+ ```
114
+
115
+ ### IAM least privilege
116
+
117
+ Replace wildcard `Action`/`Resource` with the specific actions and ARNs the
118
+ workload actually uses. If you can't determine the exact set from the surrounding
119
+ code, this is a human decision — report it rather than guessing a narrow policy
120
+ that breaks the stack.
121
+
122
+ ### Versioning & logging
123
+
124
+ S3 versioning and access logging as standalone resources:
125
+
126
+ ```hcl
127
+ resource "aws_s3_bucket_versioning" "this" {
128
+ bucket = aws_s3_bucket.this.id
129
+ versioning_configuration { status = "Enabled" }
130
+ }
131
+ ```
132
+
133
+ ### EC2 instance metadata (IMDSv2)
134
+
135
+ Require token-backed metadata to close the SSRF→credential path:
136
+
137
+ ```hcl
138
+ metadata_options {
139
+ http_endpoint = "enabled"
140
+ http_tokens = "required"
141
+ }
142
+ ```
143
+
144
+ ### Deprecations & style
145
+
146
+ - **Provider v4→v5 S3 split** — inline `server_side_encryption_configuration` /
147
+ `versioning` / `acl` / `logging` blocks on `aws_s3_bucket` are deprecated; move
148
+ each to its standalone resource (above). Don't bump the provider major version
149
+ as a side effect of a remediation — if a fix genuinely requires a newer
150
+ provider, report that as a blocker.
151
+ - **`terraform-fmt:unformatted`** — run `terraform fmt` on the named file only;
152
+ never fold a formatting pass into a behavioural fix.
153
+
154
+ ## Don't over-reach
155
+
156
+ - **Smallest change that clears the concern.** Add the missing block; don't
157
+ refactor the resource, rename it, or restructure the file around it.
158
+ - **Don't add speculative hardening** the concern didn't ask for (extra KMS keys,
159
+ new modules, blanket tagging) — that's scope creep and buries the real fix.
160
+ - **Don't widen or bump version pins** to reach a resource or attribute.
161
+ - **One concern's blast radius only.** If the secure default would break the
162
+ stack or needs a human call (a real CIDR, an IAM action set, a CMK policy),
163
+ stop and report it instead of opening a broken or guessed PR.
164
+
165
+ ## Conventions every fix must honour
166
+
167
+ - **Variables over hardcoded values.** Don't bake an account id, region, CIDR,
168
+ or ARN into a fix — reference an existing `var.*`/`local.*`, or add a typed
169
+ `variable` with a sensible `default` and `description` when one is genuinely
170
+ needed.
171
+ - **Pin versions.** When adding a provider or module, include a version
172
+ constraint. Never widen an existing pin as a side effect.
173
+ - **Approved modules first.** Call `list_modules` — if a catalogue is configured,
174
+ prefer the catalogue module (a registry module or a local/house module) + its
175
+ exact variable names over inlining a raw resource, and pin its `version`. See
176
+ *Using Terraform modules* below.
177
+ - **No secrets in HCL or state.** Never introduce a literal credential. If the
178
+ fix needs a secret, reference a variable or a secrets data source.
179
+ - **Idempotent, reviewable diffs.** The diff should read so a senior engineer
180
+ approves it without hesitation: one concern, one rationale, no churn.
181
+
182
+ ## Gold standards (the bar every fix is measured against)
183
+
184
+ These are the non-negotiable defaults a "good" Terraform change embodies. A fix
185
+ should never *move away* from any of these, and should move *toward* them when
186
+ the concern is adjacent:
187
+
188
+ - **Encrypt everything, at rest and in transit.** Storage encrypted (CMK where
189
+ the data is sensitive); TLS-only endpoints; no plaintext in state.
190
+ - **Private by default.** No `0.0.0.0/0` to admin/database ports; public access
191
+ blocked unless the resource's whole purpose is to be public (and then scoped).
192
+ - **Least privilege.** No `"*"` IAM actions/resources; scope to what the workload
193
+ uses. Prefer a referenced role/policy over an inline wildcard.
194
+ - **Pinned + reproducible.** `required_version` and every `required_providers` /
195
+ module `version` constrained (`~>`); no floating `latest`.
196
+ - **Parameterised, not hardcoded** (see below).
197
+ - **Tagged + named consistently.** Match the repo's existing tag keys and naming
198
+ scheme; don't invent a new convention.
199
+ - **Observable.** Logging / versioning / audit trails enabled where the provider
200
+ supports them and the concern is about data durability or traceability.
201
+ - **Idempotent + deterministic.** No `timestamp()`/`uuid()`/unkeyed `random_*`
202
+ driving a value that lands in state — it produces a perpetual diff.
203
+
204
+ When in doubt, the secure/idiomatic default from the catalogue above IS the gold
205
+ standard. Don't gold-plate beyond the concern, but never regress one of these.
206
+
207
+ ## Parameterize, don't hardcode (§4.13)
208
+
209
+ A value that varies by environment, account, or deployment must be a `variable`
210
+ or `local`, never a literal baked into a resource:
211
+
212
+ - **Reuse first.** If the repo already exposes `var.region` / `local.tags` /
213
+ `var.vpc_cidr`, reference it — don't introduce a parallel one.
214
+ - **Introduce a typed variable** when none fits: give it a `type`, a
215
+ `description`, and a safe `default` only when a default is genuinely sane
216
+ (secrets and account-specific ids get NO default). Match the repo's existing
217
+ file layout — add to `variables.tf` if the repo separates them, otherwise keep
218
+ it next to the resource as the repo does.
219
+ - **Derive with `locals`.** Computed/repeated values (a name prefix, a merged tag
220
+ map) belong in `locals`, referenced everywhere — not copy-pasted.
221
+ - **Never inline a secret, account id, ARN, or CIDR.** A "fix" that pastes a
222
+ literal credential is itself a finding (and the secret-scan guardrail will
223
+ block the push). Reference a variable or a secrets data source.
224
+
225
+ ## Using Terraform modules (registry, private git libraries, your own)
226
+
227
+ Prefer a well-formed module over a pile of raw resources when one cleanly fits —
228
+ it carries the secure defaults for you. **Always call `list_modules` first**; it
229
+ returns three things: the operator's `module_catalogue`, the
230
+ `discovered_house_modules` (local modules already used in THIS repo), and a note.
231
+
232
+ Three source kinds you'll encounter, each pinned differently:
233
+
234
+ - **Public registry module** — `terraform-aws-modules/vpc/aws`. Pin with a
235
+ `version = "~> 5.0"` argument. The big public collections
236
+ ([terraform-aws-modules](https://registry.terraform.io/namespaces/terraform-aws-modules):
237
+ `vpc`, `s3-bucket`, `rds`, `eks`, …) are the default when nothing is configured.
238
+ A registry submodule is `…/aws//modules/log-group`.
239
+ - **Private git module library** — e.g.
240
+ `git::https://github.com/acme/tf-modules.git//aws/s3?ref=s3-v0.1.2`. The
241
+ `//aws/s3` selects the module within the repo and **`?ref=s3-v0.1.2` IS the
242
+ version pin** (git modules have no `version` argument). Keep the exact `ref`;
243
+ never float it. Many orgs (e.g. UKHSA's `data-integration-terraform-modules`)
244
+ ship a whole library this way, tagged per-module.
245
+ - **House module** — one of the repo's own `modules/<name>` dirs. `list_modules`
246
+ surfaces these under `discovered_house_modules` with their caller files — reuse
247
+ the existing one with its **real variable names** (read its `variables.tf`)
248
+ rather than re-implementing it.
249
+
250
+ Rules: use the module's **exact variable names**, set the secure-relevant inputs
251
+ the concern is about, and keep the version **pinned**. Don't introduce a module
252
+ mid-remediation just to "improve" things — using a module is right when
253
+ *generating* new infra or when the fix is genuinely a module swap; for a one-line
254
+ security fix on an existing raw resource, fix the resource in place.
255
+
256
+ When the **`terraform` MCP server** is registered (the `terraform_mcp` input),
257
+ prefer querying it for the current module version and provider argument shapes
258
+ over recalling them from memory — registry knowledge moves faster than any
259
+ training data, and a fix written against a remembered-but-renamed argument
260
+ breaks `plan`. Without it, `terraform_version_currency` (versions) and
261
+ `terraform_provider_schema` (installed-provider arguments) are the fallbacks.
262
+
263
+ ### Authoring a reusable module (standard layout)
264
+
265
+ When you GENERATE a reusable module, follow the conventional layout real module
266
+ libraries use so it's drop-in familiar:
267
+
268
+ - `main.tf` (resources), `variables.tf` (every input typed, with a `description`
269
+ and a `validation` block where it helps), `outputs.tf`, `versions.tf` /
270
+ `providers.tf` (`required_version` + `required_providers` pinned), `README.md`
271
+ (usage + inputs/outputs), and a `CHANGELOG.md` if the repo versions modules.
272
+ - Do **not** generate `examples/` fixtures. Document usage in the module's
273
+ `README.md` instead; test coverage comes from the opt-in terratest scaffold
274
+ (see below), which plans the module directly.
275
+
276
+ ### Module-source-aware fixes (§4.14)
277
+
278
+ Before fixing a concern, call `terraform_module_graph` to see where the file
279
+ lives in the module call-graph:
280
+
281
+ - **Concern inside a LOCAL module dir** (listed in `local_module_dirs`): fix it
282
+ **once at the module source**. The fix propagates to every caller — do not
283
+ patch each call site. Note the callers in the PR body so a reviewer sees the
284
+ blast radius.
285
+ - **Concern that would require editing a REGISTRY / git / remote module**: that
286
+ source lives outside this repo — you **cannot** fix it here. Do **not** vendor
287
+ the module or fork it inline. Instead report it (open an issue / PR comment)
288
+ naming the upstream module + version and the concern, so a human routes it.
289
+
290
+ ### Modularization as remediation (M2)
291
+
292
+ `module_extraction_candidates` finds clusters of raw resources that should be a
293
+ module call, each matched against the repo's house modules (real resource-type
294
+ signature + `required_variables`) and the operator's `module_catalogue`. Turn a
295
+ candidate into a refactor PR under this contract:
296
+
297
+ - **One PR per cluster**, branch `remediate/modularize-<cluster file/prefix>` —
298
+ never mix two clusters or a behavioural fix into a modularization PR.
299
+ - Replace the cluster's raw resources with ONE `module` block calling the
300
+ candidate (pin the version for registry sources; wire the module's REAL
301
+ variable names from `required_variables` / `terraform_module_interface`).
302
+ - **Preserve state with `moved {}` blocks** — one per resource, mapping the old
303
+ address to its new `module.<name>.` address:
304
+
305
+ ```hcl
306
+ moved {
307
+ from = aws_s3_bucket.logs
308
+ to = module.logging.aws_s3_bucket.this
309
+ }
310
+ ```
311
+
312
+ - **The gate:** the PR may proceed only when `terraform_validate` passes AND
313
+ `terraform_plan` reports `refactor_safe: true` (a pure-move plan — zero
314
+ add/change/destroy). Anything else means a moved block is wrong or the module
315
+ diverges from the raw resources — fix that or report it; never accept
316
+ resource churn as "the refactor".
317
+ - A required variable you can't derive from the existing attributes is a
318
+ **PR question for the reviewer**, never a guessed value.
319
+
320
+ ### Version currency (provider + module upgrades, M3)
321
+
322
+ `terraform_version_currency` reports which pinned providers and registry modules
323
+ trail the registry's latest stable version, and which registry modules are
324
+ unpinned. Turn its rows into upgrade PRs under this contract:
325
+
326
+ - **One `chore(deps)` PR per upgrade group** — never mix a version bump with a
327
+ behavioural fix; a bump PR must read as "only the constraint changed".
328
+ - **Minor/patch bumps** (`outdated` with `majors_behind: 0`) may proceed
329
+ autonomously: update the `version` constraint to admit `newest_satisfying` →
330
+ `latest`, then prove it with `terraform_validate` (and `terraform_plan` when
331
+ credentials exist).
332
+ - **Major bumps** (`majors_behind > 0`) mean the provider/module interface may
333
+ have changed — apply only with the `needs-human` label, and consult
334
+ `terraform_module_interface` / `terraform_provider_schema` for what moved.
335
+ - **Unpinned registry modules**: pin to the reported `latest` (`version = "~> X.Y"`),
336
+ one PR for all unpinned modules in a file group — pinning is low-risk and sweeps well.
337
+ - An upgrade whose `terraform_plan` shows **destructive changes is never auto** —
338
+ escalate with the plan excerpt in the PR body.
339
+
340
+ ## Tests for modules (§28)
341
+
342
+ Terramend does **not** create or edit `examples/` fixtures — leave any the repo
343
+ already ships untouched. Module test coverage is **opt-in** via the `terratest`
344
+ input:
345
+
346
+ - **Terratest (opt-in).** When the `terratest` input is enabled, call
347
+ `scaffold_terratest` (module name + dir) to generate a plan-only Go
348
+ [Terratest](https://terratest.gruntwork.io/) smoke test (`test/<name>_test.go`)
349
+ **and** a Terraform-native `*.tftest.hcl` in the module's own `tests/` dir.
350
+ Both plan the **module directly** (no example fixture). Write the returned files
351
+ (the input also widens `allowed_paths` to permit them). If the repo already has
352
+ a Terratest suite, update its options/assertions to match the new interface
353
+ instead.
354
+
355
+ In all cases: Terramend **never runs** the tests — it holds no cloud credentials,
356
+ and Terratest needs Go + a real `apply`. It keeps the test source consistent with
357
+ the module and flags in the PR that the suite should be run in the user's
358
+ pipeline. **Never weaken an assertion or delete a test to go green.**
359
+
360
+ ## Hard rules
361
+
362
+ - Only modify `*.tf` / `*.tfvars` (and, when `allowed_paths` permits — i.e. the
363
+ `terratest` input is on — that module's test files). Never touch CI, application
364
+ code, or unrelated config in a remediation PR.
365
+ - One concern per PR. If you notice other issues, leave them for their own runs.
366
+ - Never auto-merge. The PR is for a human to review.
367
+ - If you cannot fix a concern cleanly (it needs a human decision, or the secure
368
+ default would break the stack), do **not** open a broken PR — report it
369
+ instead with what's blocking.
@@ -0,0 +1,45 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { initToolState } from "#app/toolState";
3
+ import { log } from "#app/utils/cli";
4
+
5
+ vi.mock("#app/utils/cli", () => ({
6
+ log: { info: vi.fn(), warning: vi.fn(), error: vi.fn(), debug: vi.fn() },
7
+ }));
8
+
9
+ describe("initToolState", () => {
10
+ it("returns the literal base state without a progress comment", () => {
11
+ const state = initToolState({ progressComment: undefined });
12
+
13
+ expect(state.progressComment).toBeUndefined();
14
+ expect(state.hadProgressComment).toBe(false);
15
+ expect(state.prepushFailureCount).toBe(0);
16
+ expect(state.backgroundProcesses).toEqual(new Map());
17
+ expect(state.usageEntries).toEqual([]);
18
+ expect(log.info).not.toHaveBeenCalled();
19
+ });
20
+
21
+ it("parses a pre-created progress comment and logs it", () => {
22
+ const state = initToolState({ progressComment: { id: "123", type: "issue" } });
23
+
24
+ expect(state.progressComment).toEqual({ id: 123, type: "issue" });
25
+ expect(state.hadProgressComment).toBe(true);
26
+ expect(log.info).toHaveBeenCalledWith("» using pre-created progress comment: 123 (issue)");
27
+ });
28
+
29
+ it("treats an unparseable progress comment id as absent", () => {
30
+ expect(initToolState({ progressComment: { id: "abc", type: "issue" } })).toMatchObject({
31
+ progressComment: undefined,
32
+ hadProgressComment: false,
33
+ });
34
+ expect(initToolState({ progressComment: { id: "0", type: "review" } })).toMatchObject({
35
+ progressComment: undefined,
36
+ hadProgressComment: false,
37
+ });
38
+ });
39
+
40
+ it("preserves the review comment type", () => {
41
+ const state = initToolState({ progressComment: { id: "77", type: "review" } });
42
+
43
+ expect(state.progressComment).toEqual({ id: 77, type: "review" });
44
+ });
45
+ });
@@ -0,0 +1,252 @@
1
+ import type { AgentUsage } from "#app/agents/shared";
2
+ import type { Concern } from "#app/mcp/terraform/types";
3
+ import type { PrepResult } from "#app/prep/types";
4
+ import type { AgentDiagnostic } from "#app/utils/agentHangReport";
5
+ import { log } from "#app/utils/cli";
6
+ import type { DiffCoverageState } from "#app/utils/diffCoverage";
7
+ import {
8
+ type ProgressComment,
9
+ type ProgressCommentType,
10
+ parseProgressComment,
11
+ } from "#app/utils/progressComment";
12
+ import type { TodoTracker } from "#app/utils/todoTracking";
13
+
14
+ export type BackgroundProcess = {
15
+ pid: number;
16
+ outputPath: string;
17
+ pidPath: string;
18
+ };
19
+
20
+ export type StoredPushDest = {
21
+ remoteName: string;
22
+ remoteBranch: string;
23
+ localBranch: string;
24
+ };
25
+
26
+ /**
27
+ * Valid inline-comment anchor lines per side at a particular checkout SHA.
28
+ * Lives here (not in `mcp/review.ts`) so `ToolState` — which caches
29
+ * `Map<path, CommentableLines>` per checkout — does not pull the MCP server
30
+ * graph into every consumer of run state (the action's main loop, agent
31
+ * harnesses, cf-worker indexing).
32
+ */
33
+ export type CommentableLines = { RIGHT: Set<number>; LEFT: Set<number> };
34
+
35
+ /**
36
+ * mutable per-run record of facts that occurred during execution. shared
37
+ * between the action process and the MCP server (one process — toolState is
38
+ * just a JS object passed by reference into both surfaces).
39
+ *
40
+ * design rule: ToolState is LITERAL. each field records a thing that
41
+ * happened — `review` is set when `create_pull_request_review` succeeded,
42
+ * `finalSummaryWritten` flips when `report_progress` wrote a non-plan body,
43
+ * `selectedMode` is set when `select_mode` was called. fields should never
44
+ * encode the absence of an event ("unsubmittedReview", "missingArtifact"),
45
+ * speculative state, or values derived from other fields.
46
+ *
47
+ * any predicate the rest of the code needs ("the agent picked review mode but
48
+ * never produced a review or progress write") is computed inline at the call
49
+ * site, not stored. derived state in this struct invariably drifts from the
50
+ * literal fields under refactors and is the wrong layer for the check.
51
+ *
52
+ * write narrowly: prefer adding state inside the tool that mutates it (e.g.
53
+ * `create_pull_request_review` populates `toolState.review`) and reading
54
+ * narrowly elsewhere. don't introduce flags from main.ts that mirror what an
55
+ * MCP tool already records.
56
+ */
57
+ export interface ToolState {
58
+ // where we're allowed to push - base repo initially, fork URL for fork PRs
59
+ // set by setupGit, updated by checkout_pr. always set before push validation.
60
+ pushUrl?: string;
61
+ // push destination set by checkout_pr - used as primary source in push_branch
62
+ // because git config reads can fail in certain environments
63
+ pushDest?: StoredPushDest;
64
+ // HEAD identity captured by setupGit at run start. load-bearing for the
65
+ // checkout_pr initial-branch invariant: the only sanctioned HEAD positions
66
+ // when calling checkout_pr are the run-entry HEAD or the target `pr-N`.
67
+ // blocks the zed-style cross-PR clobber where a subagent left HEAD on
68
+ // someone else's `pr-X` and the orchestrator's next checkout_pr inherited
69
+ // that position.
70
+ //
71
+ // discriminated by `kind` because `git rev-parse --abbrev-ref HEAD` returns
72
+ // the literal sentinel string `"HEAD"` on detached entry, which is the
73
+ // default state from `actions/checkout` on `pull_request` events (it
74
+ // checks out the merge commit as a detached SHA). without the kind tag,
75
+ // detached-entry runs would trivially accept any future detached state.
76
+ initialHead?: { kind: "branch"; name: string } | { kind: "detached"; sha: string };
77
+ // issue or PR number (same number space in GitHub)
78
+ issueNumber?: number;
79
+ // PR/issue numbers this run CREATED (create_pull_request / create_issue).
80
+ // read by the REST-write scope guard (mcp/scope.ts) so the agent may edit the
81
+ // body of / comment on a PR or issue it opened this run, even though that
82
+ // number differs from the run's triggering issue_number. a merely checked-out
83
+ // PR is intentionally NOT recorded here — checkout_pr is agent-controlled, so
84
+ // letting it widen write scope would defeat the guard.
85
+ createdTargets?: Set<number>;
86
+ // PR HEAD sha at checkout time — used to detect new commits pushed during a review
87
+ checkoutSha?: string;
88
+ // commentable lines per file at checkoutSha — captured during checkout_pr so
89
+ // review-time inline-comment validation matches the diff GitHub will anchor
90
+ // to (commit_id=checkoutSha). without this, a PR update between checkout and
91
+ // review would make listFiles (latest HEAD) disagree with the anchor,
92
+ // silently dropping valid comments or letting invalid ones through.
93
+ //
94
+ // commentableLinesPullNumber records WHICH PR this snapshot belongs to. if
95
+ // the agent checks out PR B and then reviews PR A in the same session, the
96
+ // cached snapshot for B would silently mis-validate A's comments — keying
97
+ // by PR number forces a re-fetch when the target changes.
98
+ //
99
+ // commentableLinesCheckoutSha pins the snapshot to the SHA it was built
100
+ // against. if a second checkout_pr for the SAME PR bumps checkoutSha but
101
+ // fails before repopulating the cache (e.g., listFiles rate-limits), the
102
+ // stale snapshot would silently mis-validate comments against the new SHA.
103
+ // comparing both fields forces a re-fetch when either moves.
104
+ commentableLinesByFile?: Map<string, CommentableLines>;
105
+ commentableLinesPullNumber?: number;
106
+ commentableLinesCheckoutSha?: string | undefined;
107
+ // SHA to diff incrementally against — set from event payload on first checkout,
108
+ // then from checkoutSha when review.ts detects new commits mid-review
109
+ beforeSha?: string;
110
+ selectedMode?: string;
111
+ // number of remediation PRs opened this run. set only in Remediate mode by
112
+ // create_pull_request; read by the max_prs guardrail (mcp/guardrails.ts).
113
+ remediationPrsOpened?: number;
114
+ // destructive resources the most recent terraform_plan reported, partitioned
115
+ // into stateful (data-bearing, high-risk) and ephemeral. set by
116
+ // terraform_plan; read by the destroy-block guardrail (mcp/guardrails.ts) at
117
+ // push time so a fix that would delete/replace a datastore is blocked on the
118
+ // plan's evidence rather than the agent's self-report.
119
+ plannedDestroy?: {
120
+ stateful: { address: string; action: string; type: string }[];
121
+ ephemeral: { address: string; action: string; type: string }[];
122
+ };
123
+ // full pre-fix scan id set (the deduped union of every scanner's concern ids,
124
+ // unfiltered by severity), captured by terraform_scan. read by
125
+ // terraform_verify_remediation to compute §1.4 regressions = current −
126
+ // baseline (concern ids the fix INTRODUCED). undefined until the first scan.
127
+ baselineConcernIds?: string[];
128
+ // the most recent terraform_scan's reported concern set (post scope/severity
129
+ // filtering — what the run acted on). read at end-of-run by
130
+ // finalizeSuccessRun to emit the SARIF artifact + findings-count output
131
+ // (§5.4). undefined until a scan ran; [] means the scan came back clean.
132
+ lastScanConcerns?: Concern[];
133
+ // absolute path of a SARIF file the agent wrote via terraform_emit_sarif.
134
+ // set by that tool on a successful write. read by finalizeSuccessRun's
135
+ // findings-output step so the end-of-run safety-net emit does NOT clobber an
136
+ // agent-emitted report (which may use a lower threshold / custom path) — it
137
+ // points findings-sarif-path at this file instead of rewriting.
138
+ emittedSarifPath?: string;
139
+ // verification signals the confidence label (§5.19) aggregates, each recorded
140
+ // by the tool that produced it: terraform_plan sets blastTier + idempotent
141
+ // (undefined when no plan ran), infracost_diff sets costDirection. read by
142
+ // terraform_verify_remediation's computeConfidence so the PR's confidence is
143
+ // computed from real evidence, not the agent's word.
144
+ lastBlastTier?: "low" | "medium" | "high";
145
+ lastIdempotent?: boolean;
146
+ lastCostDirection?: "increase" | "decrease" | "no-change" | "unknown";
147
+ // number of prepush hook failures this run. push_branch runs the hook
148
+ // while this is 0 and skips it once non-zero; never decremented within
149
+ // a run.
150
+ prepushFailureCount: number;
151
+ backgroundProcesses: Map<string, BackgroundProcess>;
152
+ review?: {
153
+ id: number;
154
+ nodeId: string;
155
+ reviewedSha: string | undefined;
156
+ };
157
+ // dedupe key: parent review comment_id → most-recent reply written this
158
+ // session by reply_to_review_comment. used by duplicateReplyDecision to
159
+ // skip identical-body re-emissions of the same call (PR #610 root cause).
160
+ // body-keyed (not just id-keyed) so legitimate follow-up replies with
161
+ // different content still go through.
162
+ reviewReplies?: Map<
163
+ number,
164
+ { commentId: number; url: string | undefined; bodyWithFooter: string }
165
+ >;
166
+ dependencyInstallation?: {
167
+ status: "not_started" | "in_progress" | "completed" | "failed";
168
+ promise: Promise<PrepResult[]> | undefined;
169
+ results: PrepResult[] | undefined;
170
+ };
171
+ // undefined = no comment yet, object = active comment, null = deliberately deleted
172
+ progressComment: ProgressComment | null | undefined;
173
+ // immutable snapshot: true if a progress comment was pre-created at init time.
174
+ // survives deleteProgressComment so handleAgentResult can still detect "expected but never reported".
175
+ hadProgressComment: boolean;
176
+ lastProgressBody?: string;
177
+ wasUpdated?: boolean;
178
+ // set after a non-plan report_progress successfully writes the final summary.
179
+ // decoupled from todoTracker.enabled so cleanup detection survives API failures.
180
+ finalSummaryWritten?: boolean;
181
+ // set by select_mode when Plan + issue_number and plan-comment API returns existing plan (for report_progress target_plan_comment)
182
+ existingPlanCommentId?: number;
183
+ previousPlanBody?: string;
184
+ // absolute path to the PR summary markdown file the agent edits in place.
185
+ // seeded by main.ts before the agent starts when payload.generateSummary is set;
186
+ // read back at end-of-run to persist to DB.
187
+ summaryFilePath?: string;
188
+ // exact bytes of the seeded snapshot file at run start. compared against
189
+ // the file content at end-of-run to detect "agent never touched it" — in
190
+ // that case persistSummary skips the DB write (saving the seed verbatim
191
+ // would either re-write what the DB already has, on incremental runs, or
192
+ // serialize the placeholder scaffold, on first runs).
193
+ summarySeed?: string;
194
+ // set to true after persistSummary completes once. prevents the error-path
195
+ // call (which exists so a successful agent edit before a crash still gets
196
+ // persisted) from redundantly re-running the DB PATCH on the
197
+ // success-then-late-throw path.
198
+ summaryPersistAttempted?: boolean;
199
+ // absolute path to the rolling repo-level learnings markdown file the
200
+ // agent reads at startup and may edit at end-of-run. seeded by main.ts
201
+ // for every run from `Repo.learnings` (empty file when no learnings
202
+ // exist yet); read back at end-of-run to persist any edits.
203
+ learningsFilePath?: string;
204
+ // exact bytes of the seeded learnings file at run start. compared
205
+ // against the file content at end-of-run to detect "agent never touched
206
+ // it" — in that case persistLearnings skips the DB PATCH (saving the
207
+ // identical content would be a no-op write that wastes a LearningsRevision
208
+ // row and the API round-trip).
209
+ learningsSeed?: string;
210
+ // mirror of `summaryPersistAttempted` for the learnings tmpfile — guards
211
+ // the error-path / exit-signal callers from a redundant second PATCH
212
+ // after the success path already persisted.
213
+ learningsPersistAttempted?: boolean;
214
+ output?: string | undefined;
215
+ usageEntries: AgentUsage[];
216
+ model?: string | undefined;
217
+ // set by main.ts when the BYOK fallback engaged (configured model needed
218
+ // a provider key the runner didn't have). carried into PR-comment footers
219
+ // so users can see "Using <free model> (credentials for <configured> not
220
+ // configured)" rather than just being silently downgraded. literal record
221
+ // of an event that happened — matches the ToolState design rule.
222
+ modelFallback?: { from: string } | undefined;
223
+ todoTracker?: TodoTracker | undefined;
224
+ diffCoverage?: DiffCoverageState | undefined;
225
+ // mutable handle the agent harness writes to as a run progresses (recent
226
+ // stderr ring buffer reference, last provider-error label, event count).
227
+ // read by main.ts's outer catch so a watchdog-fired activity timeout still
228
+ // surfaces the same agent-side context the harness's own catch path returns
229
+ // via `result.error`. see `utils/agentHangReport.ts`.
230
+ agentDiagnostic?: AgentDiagnostic | undefined;
231
+ }
232
+
233
+ interface InitToolStateParams {
234
+ progressComment: { id: string; type: ProgressCommentType } | undefined;
235
+ }
236
+
237
+ export function initToolState(params: InitToolStateParams): ToolState {
238
+ const resolved = parseProgressComment(params.progressComment);
239
+
240
+ if (resolved) {
241
+ log.info(`» using pre-created progress comment: ${resolved.id} (${resolved.type})`);
242
+ }
243
+
244
+ return {
245
+ progressComment: resolved,
246
+ hadProgressComment: !!resolved,
247
+ prepushFailureCount: 0,
248
+ backgroundProcesses: new Map(),
249
+ createdTargets: new Set(),
250
+ usageEntries: [],
251
+ };
252
+ }