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,305 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { createHash } from "node:crypto";
3
+ import { join } from "node:path";
4
+ import { discoverTerraformRoots } from "#app/mcp/roots";
5
+ import { toolSkip } from "#app/mcp/shared";
6
+ import { resolveEnv } from "#app/utils/secrets";
7
+
8
+ /**
9
+ * A degrade-green skip result carrying the structured §3.5 envelope
10
+ * (`ok: false` + machine `code` + human `detail`) PLUS the legacy alias the tool
11
+ * returned before it converged on the envelope — `ran`/`found` and
12
+ * `skipped_reason`/`reason` — so existing prompt + test contracts keep working.
13
+ * Additive, never breaking. `extra` folds in any tool-specific fields (e.g.
14
+ * read_findings' empty `concerns`/`groups`).
15
+ */
16
+ export function skipResult(
17
+ code: string,
18
+ detail: string,
19
+ opts: {
20
+ key?: "ran" | "found";
21
+ reasonKey?: "skipped_reason" | "reason";
22
+ extra?: Record<string, any>;
23
+ } = {},
24
+ ): Record<string, any> {
25
+ const key = opts.key ?? "ran";
26
+ const reasonKey = opts.reasonKey ?? "skipped_reason";
27
+ return { ...toolSkip(code, detail), [key]: false, [reasonKey]: detail, ...(opts.extra ?? {}) };
28
+ }
29
+
30
+ /**
31
+ * Internal "concern" model — the Remediator's ground truth for "what is not
32
+ * best practice". Produced by `terraform_scan` from the fork's own deterministic
33
+ * Terraform check tools (fmt / validate / tflint / trivy / checkov).
34
+ *
35
+ * This is a deliberate SUBSET of the reviewer's `findings.schema.json` v1.0
36
+ * (../terraform-reviewer/schemas/findings.schema.json). The reviewer's findings
37
+ * add `lens` / `standard` / `control_id` / `state` on top. Keeping the shape a
38
+ * subset means a future reviewer integration (read_findings) can emit the same
39
+ * Concern[] with no change to the modes or the rest of the tools — only the
40
+ * SOURCE of concerns swaps.
41
+ */
42
+ export interface Concern {
43
+ /** stable content id: sha1(source|rule_id|file|line). idempotency key for branch/PR naming. */
44
+ id: string;
45
+ /** producing tool. `reviewer` marks a concern loaded from a terraform-reviewer
46
+ * findings.json whose original tool isn't one Terramend re-runs (tfsec / infracost
47
+ * / llm) — its provenance lives in `rule_id`. */
48
+ source: "terraform-fmt" | "terraform-validate" | "tflint" | "trivy" | "checkov" | "reviewer";
49
+ /** original namespaced rule, e.g. "trivy:AVD-AWS-0088" */
50
+ rule_id: string;
51
+ severity: Severity;
52
+ category: "security" | "style" | "correctness" | "cost";
53
+ /** the scanner message — what is wrong */
54
+ evidence: string;
55
+ location: { file: string; line: number | null };
56
+ remediation_hint: string | null;
57
+ }
58
+
59
+ export const SEVERITIES = ["critical", "high", "medium", "low", "info"] as const;
60
+ export type Severity = (typeof SEVERITIES)[number];
61
+
62
+ export const SEVERITY_RANK: Record<Severity, number> = {
63
+ critical: 4,
64
+ high: 3,
65
+ medium: 2,
66
+ low: 1,
67
+ info: 0,
68
+ };
69
+
70
+ export function concernId(
71
+ source: string,
72
+ ruleId: string,
73
+ file: string,
74
+ line: number | null,
75
+ ): string {
76
+ return createHash("sha1")
77
+ .update(`${source}|${ruleId}|${file}|${line ?? ""}`)
78
+ .digest("hex")
79
+ .slice(0, 12);
80
+ }
81
+
82
+ /**
83
+ * Normalize a scanner-reported path to a repo-relative POSIX path. Each scanner
84
+ * reports the file differently — tflint gives `main.tf` (relative), trivy a
85
+ * scan-dir-relative `Target`, terraform an absolute path (`/repo/main.tf` or
86
+ * `D:\repo\main.tf`), checkov a leading-slash path (`/main.tf`). Left
87
+ * unnormalized, these leak into `location.file` AND the
88
+ * content-derived `concernId`, making the id (and the `remediate/<id>` branch)
89
+ * machine-dependent and breaking the ✗→✓ re-scan id match across environments.
90
+ * So normalize BEFORE building any Concern.
91
+ */
92
+ export function toRepoRelative(raw: string | undefined, cwd: string): string {
93
+ if (!raw) return "(unknown)";
94
+ const posix = raw.replace(/\\/g, "/");
95
+ const cwdPosix = cwd.replace(/\\/g, "/").replace(/\/+$/, "");
96
+ let rel = posix;
97
+ if (cwdPosix && posix.toLowerCase().startsWith(`${cwdPosix.toLowerCase()}/`)) {
98
+ rel = posix.slice(cwdPosix.length + 1);
99
+ }
100
+ // strip any leading "./" or "/" (checkov reports paths relative to its -d root
101
+ // with a leading separator).
102
+ rel = rel.replace(/^(?:\.\/|\/)+/, "");
103
+ return rel || "(unknown)";
104
+ }
105
+
106
+ export type RunResult = { status: number; stdout: string; stderr: string; missing: boolean };
107
+
108
+ /**
109
+ * Hard wall-clock cap on any single scanner/terraform invocation. Bounds a hung
110
+ * subprocess (e.g. `terraform init`/`plan` stalling on a private registry, a
111
+ * tflint plugin fetch that never returns) so it can't block the run forever.
112
+ * Generous (5 min) so it only ever fires on a genuine hang, never a slow-but-
113
+ * progressing plan. A timeout surfaces as a non-zero/`-1` status the caller
114
+ * already treats as "this scanner did not produce results".
115
+ */
116
+ export const SUBPROCESS_TIMEOUT_MS = 300_000;
117
+
118
+ /**
119
+ * Run a scanner without throwing. Terraform tools exit non-zero when they FIND
120
+ * issues, so a non-zero status is normal, not an error. `missing` is set when
121
+ * the binary isn't on PATH (ENOENT) — the scanner then degrades to "skipped"
122
+ * rather than failing the run (plan §9.2: a tool being absent must not block).
123
+ *
124
+ * `extraEnv` opts a specific command back into a needed credential: the scanners
125
+ * run with a restricted env that strips every `*_KEY`/secret, but infracost
126
+ * legitimately needs its `INFRACOST_API_KEY`. resolveEnv(object) merges the
127
+ * restricted base with the explicit vars, so only the named keys get through.
128
+ */
129
+ export function run(
130
+ cmd: string,
131
+ args: string[],
132
+ cwd: string,
133
+ extraEnv?: Record<string, string>,
134
+ ): RunResult {
135
+ const result = spawnSync(cmd, args, {
136
+ cwd,
137
+ encoding: "utf-8",
138
+ // restricted env: keeps PATH/HOME, strips secrets so a scanner (or a tflint
139
+ // plugin) can't exfiltrate credentials. `extraEnv` re-admits only the named vars.
140
+ env: resolveEnv(extraEnv ?? "restricted") as NodeJS.ProcessEnv,
141
+ stdio: ["ignore", "pipe", "pipe"],
142
+ maxBuffer: 64 * 1024 * 1024,
143
+ timeout: SUBPROCESS_TIMEOUT_MS,
144
+ });
145
+ if (result.error) {
146
+ // ENOENT = binary absent (degrade to "skipped"); a timeout (ETIMEDOUT, or a
147
+ // SIGTERM kill on timeout) is a real failure surfaced as status -1, which the
148
+ // scanner callers already treat as "no results from this tool".
149
+ const missing = (result.error as NodeJS.ErrnoException).code === "ENOENT";
150
+ return { status: -1, stdout: "", stderr: result.error.message, missing };
151
+ }
152
+ return {
153
+ status: result.status ?? -1,
154
+ stdout: result.stdout ?? "",
155
+ stderr: result.stderr ?? "",
156
+ missing: false,
157
+ };
158
+ }
159
+
160
+ export type ScannerOutcome = {
161
+ source: Concern["source"];
162
+ ran: boolean;
163
+ skipped_reason?: string;
164
+ concerns: Concern[];
165
+ /**
166
+ * For `terraform validate` only: count of roots where terraform RAN but its
167
+ * `-json` output could not be parsed (a real CLI-level failure, not a missing
168
+ * binary). Lets the validate tool report `passed` as fail-closed instead of
169
+ * silently treating an un-validated root as clean. Undefined/0 elsewhere.
170
+ */
171
+ unvalidated?: number;
172
+ };
173
+
174
+ export function skipped(source: Concern["source"], reason: string): ScannerOutcome {
175
+ return { source, ran: false, skipped_reason: reason, concerns: [] };
176
+ }
177
+
178
+ export interface ResolvedRoot {
179
+ /** absolute dir the per-root command (init/plan/validate) runs in. */
180
+ absDir: string;
181
+ /** that root's path relative to the scan `cwd` ("" when the root IS cwd) —
182
+ * prepended to per-root concern files so they stay cwd-relative. */
183
+ relDir: string;
184
+ }
185
+
186
+ /**
187
+ * The Terraform root modules to operate on under `cwd`. A repo can hold several
188
+ * roots (hepcare: `terraform/` + `terraform/core/`); `terraform validate` /
189
+ * `plan` are per-root, so they must run in EACH. Falls back to `cwd` itself as a
190
+ * single root when none is detected — so a normal single-root repo behaves
191
+ * exactly as before (no rebasing, one iteration).
192
+ */
193
+ export function resolveRoots(cwd: string): ResolvedRoot[] {
194
+ const discovered = discoverTerraformRoots(cwd);
195
+ if (discovered.length === 0) return [{ absDir: cwd, relDir: "" }];
196
+ return discovered.map((r) => ({ absDir: r.dir ? join(cwd, r.dir) : cwd, relDir: r.dir }));
197
+ }
198
+
199
+ /**
200
+ * Re-base a concern produced by a per-root command (its file is relative to the
201
+ * root dir) onto the scan `cwd` by prefixing the root's `relDir`, and recompute
202
+ * the content id so it stays consistent with the cwd-relative scanners (✗→✓).
203
+ * A no-op when `relDir` is "" (the root IS cwd).
204
+ */
205
+ export function rebaseConcern(c: Concern, relDir: string): Concern {
206
+ if (!relDir) return c;
207
+ const file = `${relDir}/${c.location.file}`.replace(/\/+/g, "/");
208
+ const prefix = `${c.source}:`;
209
+ const bareRule = c.rule_id.startsWith(prefix) ? c.rule_id.slice(prefix.length) : c.rule_id;
210
+ return {
211
+ ...c,
212
+ location: { ...c.location, file },
213
+ id: concernId(c.source, bareRule, file, c.location.line),
214
+ };
215
+ }
216
+
217
+ /** true when a path is a Terraform source file Terramend may remediate. */
218
+ export function isTerraformFile(file: string): boolean {
219
+ const f = file.toLowerCase();
220
+ return f.endsWith(".tf") || f.endsWith(".tfvars");
221
+ }
222
+
223
+ /**
224
+ * Terramend is Terraform-only. A concern in a non-`.tf`/`.tfvars` file can never
225
+ * be remediated (the `allowed_paths` push guardrail blocks it) and is pure noise,
226
+ * so any scanner that also inspects other IaC — checkov's github_actions, trivy's
227
+ * dockerfile/kubernetes — gets filtered down to Terraform here. This is the
228
+ * catch-all backstop; `scanCheckov` also scopes itself with `--framework terraform`.
229
+ */
230
+ export function isTerraformConcern(c: Concern): boolean {
231
+ return isTerraformFile(c.location.file);
232
+ }
233
+
234
+ export function dedupe(concerns: Concern[]): Concern[] {
235
+ const seen = new Set<string>();
236
+ const out: Concern[] = [];
237
+ for (const c of concerns) {
238
+ if (seen.has(c.id)) continue;
239
+ seen.add(c.id);
240
+ out.push(c);
241
+ }
242
+ return out;
243
+ }
244
+
245
+ export function sortConcerns(concerns: Concern[]): Concern[] {
246
+ return [...concerns].sort((a, b) => {
247
+ const sev = SEVERITY_RANK[b.severity] - SEVERITY_RANK[a.severity];
248
+ if (sev !== 0) return sev;
249
+ return a.id.localeCompare(b.id);
250
+ });
251
+ }
252
+
253
+ export function lowerSeverity(s: string | undefined): Severity {
254
+ const v = (s ?? "").toLowerCase();
255
+ return (SEVERITIES as readonly string[]).includes(v) ? (v as Severity) : "medium";
256
+ }
257
+
258
+ /** the repo's base ref for diff-scope, or null when one can't be determined. */
259
+ export function resolveBaseRef(cwd: string): string | null {
260
+ const head = run("git", ["rev-parse", "--abbrev-ref", "origin/HEAD"], cwd);
261
+ if (head.status === 0 && head.stdout.trim()) return head.stdout.trim();
262
+ for (const ref of ["origin/main", "origin/master"]) {
263
+ const verify = run("git", ["rev-parse", "--verify", "--quiet", ref], cwd);
264
+ if (verify.status === 0 && verify.stdout.trim()) return ref;
265
+ }
266
+ return null;
267
+ }
268
+
269
+ /**
270
+ * A scoped unit of work = all concerns in one file. Different scanners flag the
271
+ * same underlying defect under different rule ids (trivy ∩ checkov overlap
272
+ * heavily on e.g. S3 buckets), so per-concern PRs would spam many PRs for one
273
+ * bad file. Remediate acts on ONE group per PR (branch `remediate/<group.id>`),
274
+ * fixing every concern in the file together and proving them all cleared (✗→✓).
275
+ */
276
+ export interface ConcernGroup {
277
+ /** stable id — the remediation branch/PR key (`remediate/<id>`). Derived from
278
+ * the file (by-file grouping) or the rule (by-rule grouping). */
279
+ id: string;
280
+ /** the group's primary file (by-file) or a human label like "3 files"
281
+ * (by-rule); `files` carries the full list for by-rule groups. */
282
+ file: string;
283
+ /** §3.11 — every file the group spans. one entry for a by-file group; the
284
+ * full set for a by-rule group (the agent must fix the rule in all of them). */
285
+ files?: string[];
286
+ /** how the group was formed — `file` (default) or `rule` (§3.11). */
287
+ grouping?: "file" | "rule";
288
+ /** highest severity among the group's concerns. */
289
+ severity: Severity;
290
+ concern_count: number;
291
+ /** distinct rule ids in the group, for the PR body. */
292
+ rule_ids: string[];
293
+ /** the concern ids the re-scan must confirm are gone to call this ✓. */
294
+ concern_ids: string[];
295
+ /** §3.9 — `auto` (open a normal PR) or `needs-human` (escalate). attached by
296
+ * the scan tool from the group's concerns, not by `groupConcerns` (which has
297
+ * no autonomy threshold). undefined until the scan tool annotates it. */
298
+ autonomy?: Autonomy;
299
+ /** §3.9 — why the group was escalated (empty/absent for `auto`). */
300
+ autonomy_reasons?: string[];
301
+ }
302
+
303
+ export type Autonomy = "auto" | "needs-human";
304
+
305
+ export type BlastTier = "low" | "medium" | "high";