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,222 @@
1
+ /**
2
+ * Source for the opencode plugin we drop into the per-run tmpdir at
3
+ * `<XDG_CONFIG_HOME>/opencode/plugin/terramend-events.ts`. The harness already
4
+ * redirects `XDG_CONFIG_HOME` to `ctx.tmpdir/.config` (see `opencode.ts`
5
+ * `homeEnv`), so opencode's auto-discovery scans the tmpdir, never the user's
6
+ * working tree. opencode's `Global.Path.config` resolves to
7
+ * `path.join(xdgConfig, "opencode")` and the config layer auto-discovers
8
+ * plugins from every directory in its scan list — including
9
+ * `Global.Path.config` — by globbing `{plugin,plugins}/*.{ts,js}` via
10
+ * `ConfigPlugin.load(dir)`.
11
+ *
12
+ * We MUST NOT write into the user's repo working tree. The repo is a checkout
13
+ * the agent operates on; only the agent's own tools (gated by
14
+ * `OPENCODE_PERMISSION`) may modify it. The whole reason we redirect HOME and
15
+ * XDG_CONFIG_HOME is so harness-side files (config, plugins, scratch state)
16
+ * land in the tmpdir.
17
+ *
18
+ * Why the events plugin exists: opencode's `task` tool runs subagents
19
+ * in-process and the CLI's `cli/cmd/run.ts` event loop filters
20
+ * `part.sessionID !== sessionID`, so subagent-internal `message.part.updated`
21
+ * events are silently discarded before reaching our parent NDJSON stream.
22
+ * plugins, by contrast, receive EVERY bus event via `bus.subscribeAll()`
23
+ * regardless of session.
24
+ *
25
+ * The events plugin re-emits every relevant bus event onto opencode's stdout
26
+ * as a single JSON line wrapped in a sentinel envelope. our `runOpenCode`
27
+ * parser recognises the envelope, unpacks it, and routes the inner part
28
+ * through the existing handlers with a per-session label from `SessionLabeler`
29
+ * so each subagent's tool calls / text appear inline alongside the
30
+ * orchestrator's.
31
+ *
32
+ * The subagent gate (the `tool.execute.before` hook that hard-blocks
33
+ * state-mutating MCP tool calls from a subagent session) lives in a SEPARATE
34
+ * plugin — `TERRAMEND_OPENCODE_GATE_PLUGIN_SOURCE` below — because it's the
35
+ * load-bearing security fence and must ship into the opencode harness,
36
+ * whereas this events re-emitter was only needed by the legacy CLI-parsing
37
+ * path (the active `opencode.ts` reads subagent events directly off the SDK
38
+ * event stream, so it installs ONLY the gate plugin). Deny-list source of
39
+ * truth: `src/agents/subagentToolGates.ts`.
40
+ *
41
+ * Dumb plugin / smart parent split: the events plugin emits every part for
42
+ * every session. the parent dedupes against the orchestrator's own session id
43
+ * (which it already knows from the `init` event). this keeps the plugin trivial
44
+ * and keeps the per-session attribution logic on the parent side where the
45
+ * SessionLabeler already lives.
46
+ *
47
+ * Event-name prefixing: the wrapped event-type sentinel is
48
+ * `terramend_bus_event` — picked to be unmistakably ours so a future opencode
49
+ * release that introduces a coincidentally-named event type won't collide.
50
+ */
51
+
52
+ import { SUBAGENT_DENIED_TOOLS } from "#app/agents/subagentToolGates";
53
+
54
+ export const TERRAMEND_BUS_EVENT_TYPE = "terramend_bus_event" as const;
55
+
56
+ export const TERRAMEND_OPENCODE_PLUGIN_FILENAME = "terramend-events.ts" as const;
57
+
58
+ export const TERRAMEND_OPENCODE_GATE_PLUGIN_FILENAME = "terramend-subagent-gate.ts" as const;
59
+
60
+ /**
61
+ * Source written verbatim to `<XDG_CONFIG_HOME>/opencode/plugin/terramend-events.ts`.
62
+ *
63
+ * - Structural typing only (no runtime import of `@opencode-ai/plugin`):
64
+ * opencode installs that dep into the directory containing the plugin
65
+ * alongside discovery, but a) the dep isn't required for the structural
66
+ * shape we use, and b) keeping zero imports avoids any module-resolution
67
+ * coupling to opencode's plugin-loader internals across versions.
68
+ * - default export is the plugin factory (opencode's plugin loader accepts
69
+ * default exports as the server entrypoint).
70
+ * - we only forward `message.part.updated`. that's where the user-visible
71
+ * subagent activity (tool calls, text, step transitions) lives. add more
72
+ * event types here if the parent needs them.
73
+ * - JSON.stringify+single write keeps the line atomic up to PIPE_BUF (4KB on
74
+ * Linux). longer parts may interleave with concurrent stdout writers; the
75
+ * parser tolerates non-JSON lines (logs them at debug) so a torn line is a
76
+ * missed event, not a crash.
77
+ */
78
+ export const TERRAMEND_OPENCODE_PLUGIN_SOURCE = `// AUTOGENERATED by Terramend. do not edit; it'll be overwritten on the next run.
79
+ // surfaces opencode subagent activity that the CLI's run-loop discards. see
80
+ // action/agents/opencodePlugin.ts in terramend/app for why this exists. lives
81
+ // inside the per-run tmpdir (XDG_CONFIG_HOME/opencode/plugin/), never inside
82
+ // the user's working tree. the subagent gate ships as a separate plugin
83
+ // (terramend-subagent-gate.ts).
84
+
85
+ const TERRAMEND_BUS_EVENT_TYPE = ${JSON.stringify(TERRAMEND_BUS_EVENT_TYPE)};
86
+
87
+ // orchestratorSessionID locks to the first sessionID we observe on the bus
88
+ // (\`message.part.updated\`). opencode's run command creates exactly one
89
+ // top-level session before any subagent is dispatched, and the orchestrator
90
+ // emits at least one event before any subagent does — so first-seen-wins
91
+ // captures the orchestrator; every subagent part has a non-matching
92
+ // sessionID.
93
+ let orchestratorSessionID: string | undefined;
94
+
95
+ function isOrchestratorTaskDispatch(part: {
96
+ type?: string;
97
+ tool?: string;
98
+ state?: { status?: string };
99
+ } | undefined): boolean {
100
+ if (!part || part.type !== "tool") return false;
101
+ if (part.tool !== "task") return false;
102
+ // only forward at status="running" (not "pending"). at pending the
103
+ // state.input is still {} — the orchestrator has emitted the part shell
104
+ // but the LLM hasn't filled in description/subagent_type/prompt yet. by
105
+ // running, input is populated and recordTaskDispatch can derive the lens
106
+ // label correctly.
107
+ return part.state?.status === "running";
108
+ }
109
+
110
+ export default async function terramendEventsPlugin() {
111
+ return {
112
+ event: async (input: {
113
+ event: {
114
+ type: string;
115
+ properties?: {
116
+ part?: {
117
+ sessionID?: string;
118
+ type?: string;
119
+ tool?: string;
120
+ state?: { status?: string };
121
+ };
122
+ };
123
+ };
124
+ }) => {
125
+ const event = input?.event;
126
+ if (!event || typeof event !== "object") return;
127
+ if (event.type !== "message.part.updated") return;
128
+ const part = event.properties?.part;
129
+ const sessionID = part?.sessionID;
130
+ if (typeof sessionID !== "string" || sessionID.length === 0) return;
131
+ if (orchestratorSessionID === undefined) orchestratorSessionID = sessionID;
132
+
133
+ if (sessionID === orchestratorSessionID) {
134
+ // skip orchestrator events EXCEPT early task dispatches.
135
+ if (!isOrchestratorTaskDispatch(part)) return;
136
+ }
137
+
138
+ try {
139
+ const line = JSON.stringify({
140
+ type: TERRAMEND_BUS_EVENT_TYPE,
141
+ bus_event: event,
142
+ });
143
+ process.stdout.write(line + "\\n");
144
+ } catch {
145
+ // a circular reference or BigInt etc. would throw; swallow rather
146
+ // than letting a single bad event take down the plugin.
147
+ }
148
+ },
149
+ };
150
+ }
151
+ `;
152
+
153
+ /**
154
+ * Standalone subagent gate plugin written to
155
+ * `<XDG_CONFIG_HOME>/opencode/plugin/terramend-subagent-gate.ts`. Installed by
156
+ * the in-process `opencode.ts` harness — the gate is the load-bearing security
157
+ * fence, so it ships independently of the events re-emitter above (which the
158
+ * in-process harness doesn't need).
159
+ *
160
+ * Hard-blocks state-mutating MCP tool calls originating from a subagent
161
+ * session via `tool.execute.before`, complementing the runtime backstops from
162
+ * PR #796 (action/mcp/checkout.ts, action/mcp/git.ts). Deny-list source of
163
+ * truth: `action/agents/subagentToolGates.ts`.
164
+ */
165
+ export const TERRAMEND_OPENCODE_GATE_PLUGIN_SOURCE = `// AUTOGENERATED by Terramend. do not edit; it'll be overwritten on the next run.
166
+ // hard-blocks state-mutating MCP tool calls from opencode subagents (PR
167
+ // following terramend/app#796 — see action/agents/subagentToolGates.ts). see
168
+ // action/agents/opencodePlugin.ts in terramend/app for why this exists. lives
169
+ // inside the per-run tmpdir (XDG_CONFIG_HOME/opencode/plugin/), never inside
170
+ // the user's working tree.
171
+
172
+ // embedded at template-render time so the plugin source has no imports.
173
+ // kept in sync with action/agents/subagentToolGates.ts via a single re-export.
174
+ const SUBAGENT_DENIED_TOOLS = new Set(${JSON.stringify(SUBAGENT_DENIED_TOOLS)});
175
+
176
+ // opencode presents MCP tools as \`terramend_<bare>\`; strip that to compare
177
+ // against the canonical bare names in SUBAGENT_DENIED_TOOLS. native tools
178
+ // (read, write, bash, task, etc.) lack the prefix and never match the deny
179
+ // list anyway.
180
+ function stripTerramendPrefix(toolName: string): string {
181
+ if (toolName.startsWith("terramend_")) return toolName.slice("terramend_".length);
182
+ return toolName;
183
+ }
184
+
185
+ // orchestratorSessionID locks to the first sessionID we observe on
186
+ // \`tool.execute.before\`. the orchestrator must invoke a tool (e.g. its
187
+ // \`task\` dispatch) before any subagent session exists, so first-seen-wins
188
+ // always captures the orchestrator; every subagent call therefore has a
189
+ // non-matching sessionID and is subject to the deny check.
190
+ let orchestratorSessionID: string | undefined;
191
+
192
+ export default async function terramendSubagentGatePlugin() {
193
+ return {
194
+ "tool.execute.before": async (
195
+ input: { tool?: string; sessionID?: string; callID?: string },
196
+ _output: { args?: Record<string, unknown> }
197
+ ) => {
198
+ // input: { tool, sessionID, callID } — confirmed against
199
+ // anomalyco/opencode packages/opencode/src/session/tools.ts which
200
+ // calls plugin.trigger("tool.execute.before", { tool, sessionID,
201
+ // callID }, { args }). throwing here surfaces as a tool error to the
202
+ // model (Effect's run.promise propagates the rejection through the
203
+ // AI SDK's tool-runtime), so the subagent sees a denial and can
204
+ // pick a different action.
205
+ const sessionID = typeof input?.sessionID === "string" ? input.sessionID : "";
206
+ const tool = typeof input?.tool === "string" ? input.tool : "";
207
+ if (!sessionID || !tool) return;
208
+ if (orchestratorSessionID === undefined) orchestratorSessionID = sessionID;
209
+ if (sessionID === orchestratorSessionID) return;
210
+ const bare = stripTerramendPrefix(tool);
211
+ if (!SUBAGENT_DENIED_TOOLS.has(bare)) return;
212
+ throw new Error(
213
+ "subagent attempted to call denied tool '" + bare + "'. " +
214
+ "subagents share the orchestrator's in-process working tree and toolState; " +
215
+ "state-changing MCP tools (checkout_pr, push_branch, create_pull_request_review, " +
216
+ "report_progress, etc.) are reserved for the orchestrator. " +
217
+ "report findings back to the orchestrator and let it perform the mutation."
218
+ );
219
+ },
220
+ };
221
+ }
222
+ `;
@@ -0,0 +1,34 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { geminiHighThinkingOverrides } from "#app/agents/opencodeShared";
3
+ import { modelAliases } from "#app/models";
4
+
5
+ describe("geminiHighThinkingOverrides", () => {
6
+ // Expected truth pulled the same way the helper does — both must derive from
7
+ // the registry so the test exercises the wiring, not a hand-maintained list.
8
+ const expectedApiIds = modelAliases
9
+ .filter((a) => a.provider === "google")
10
+ .map((a) => a.resolve.replace(/^google\//, ""));
11
+ const overrides = geminiHighThinkingOverrides();
12
+
13
+ it("covers every direct-Google alias in the registry", () => {
14
+ expect(Object.keys(overrides).sort()).toEqual([...expectedApiIds].sort());
15
+ });
16
+
17
+ it("is non-empty (catches accidental whole-provider removal)", () => {
18
+ expect(Object.keys(overrides).length).toBeGreaterThan(0);
19
+ });
20
+
21
+ it("strips the `google/` prefix from each resolve to get the bare API id", () => {
22
+ for (const id of Object.keys(overrides)) {
23
+ expect(id).not.toMatch(/^google\//);
24
+ }
25
+ });
26
+
27
+ it("pins every entry to thinkingLevel: high", () => {
28
+ for (const [id, value] of Object.entries(overrides)) {
29
+ expect(value, `entry for ${id}`).toEqual({
30
+ options: { thinkingConfig: { thinkingLevel: "high" } },
31
+ });
32
+ }
33
+ });
34
+ });
@@ -0,0 +1,121 @@
1
+ // Shared helpers for the OpenCode agent harness (`./opencode.ts`). Pure config
2
+ // / model-registry / install glue — nothing here touches the SDK event loop.
3
+ // Kept as a separate module so this config surface stays unit-testable in
4
+ // isolation (see `./opencodeShared.test.ts`).
5
+
6
+ import { REVIEWER_AGENT_NAME, REVIEWER_SYSTEM_PROMPT } from "#app/agents/reviewer";
7
+ import { deriveSubagentModels } from "#app/agents/subagentModels";
8
+ import { modelAliases } from "#app/models";
9
+ import { log } from "#app/utils/cli";
10
+ import { installFromNpmTarball } from "#app/utils/install";
11
+ import { getAuthorizedModels } from "#app/utils/openCodeModels";
12
+ import { getDevDependencyVersion } from "#app/utils/version";
13
+
14
+ // ── config ─────────────────────────────────────────────────────────────────────
15
+
16
+ export type OpenCodeConfig = {
17
+ mcp?: Record<string, unknown>;
18
+ permission?: Record<string, unknown>;
19
+ provider?: Record<string, unknown>;
20
+ agent?: Record<string, unknown>;
21
+ experimental?: Record<string, unknown>;
22
+ model?: string;
23
+ enabled_providers?: string[];
24
+ [key: string]: unknown;
25
+ };
26
+
27
+ /**
28
+ * Build the `provider.google.models[id].options` map that pins every direct-Google
29
+ * Gemini alias to `thinkingLevel: "high"`. Sourced from the model registry so
30
+ * adding/renaming a Google alias in `action/models.ts` flows through automatically.
31
+ */
32
+ export function geminiHighThinkingOverrides(): Record<string, { options: object }> {
33
+ return Object.fromEntries(
34
+ modelAliases
35
+ .filter((a) => a.provider === "google")
36
+ .map((a) => [
37
+ a.resolve.replace(/^google\//, ""),
38
+ { options: { thinkingConfig: { thinkingLevel: "high" } } },
39
+ ]),
40
+ );
41
+ }
42
+
43
+ /**
44
+ * Read-only `reviewfrog` subagent for lens-based review. Non-mutative +
45
+ * non-recursive — enforced by the system prompt in reviewer.ts.
46
+ *
47
+ * Per-subagent `model:` override is driven by the registry in
48
+ * `action/models.ts` via each alias's `subagentModel` field. Currently wired:
49
+ * Anthropic opus → sonnet, OpenAI gpt-pro → gpt and gpt → gpt-5.4, Google
50
+ * gemini-pro → gemini-flash. Other providers inherit (no override).
51
+ */
52
+ export function buildReviewerAgentConfig(
53
+ orchestratorModel: string | undefined,
54
+ ): Record<string, unknown> {
55
+ const overrides = deriveSubagentModels(orchestratorModel);
56
+ return {
57
+ [REVIEWER_AGENT_NAME]: {
58
+ description:
59
+ "Read-only review subagent for lens-based code review (correctness, security, billing-subsystem, etc.). " +
60
+ "Reads only — no writes, no state-changing shell or MCP calls, no nested subagent dispatch.",
61
+ mode: "subagent",
62
+ prompt: REVIEWER_SYSTEM_PROMPT,
63
+ ...(overrides.reviewer !== undefined ? { model: overrides.reviewer } : {}),
64
+ },
65
+ };
66
+ }
67
+
68
+ // ── install ────────────────────────────────────────────────────────────────────
69
+
70
+ /**
71
+ * Install the opencode-ai npm tarball and return the path to the executable.
72
+ *
73
+ * The bin path differs by version: v1.4.x and earlier shipped `bin/opencode`;
74
+ * v1.14+ renames the platform-specific binary to `bin/opencode.exe` for every
75
+ * OS via the postinstall script. Callers pass the binPath that matches their
76
+ * pinned version so a v1↔v2 swap can't silently install the wrong file.
77
+ */
78
+ export async function installOpencodeCli(params: { binPath: string }): Promise<string> {
79
+ return await installFromNpmTarball({
80
+ packageName: "opencode-ai",
81
+ version: getDevDependencyVersion("opencode-ai"),
82
+ executablePath: params.binPath,
83
+ installDependencies: true,
84
+ });
85
+ }
86
+
87
+ // ── model auto-select fallback ──────────────────────────────────────────────────
88
+ //
89
+ // steps 1–2 of model resolution (TERRAMEND_MODEL env, slug resolution) happen
90
+ // in resolveModel() in utils/agent.ts before the agent runs. this is step 3:
91
+ // auto-select using the authorized model set captured in main.ts via
92
+ // `opencode models` introspection.
93
+
94
+ const AUTO_SELECT_WARNING =
95
+ "select a model explicitly in the Terramend console (https://terramend.com/console) to avoid this.";
96
+
97
+ export function autoSelectModel(): string | undefined {
98
+ const authorized = getAuthorizedModels();
99
+ if (authorized.size > 0) {
100
+ // skip hidden aliases (internal subagent-tier targets like
101
+ // opencode/gpt-5.4) — they should never surface as a user-facing
102
+ // orchestrator pick. mirrors the selectable-list filter in
103
+ // components/ModelSelector.tsx.
104
+ const match =
105
+ modelAliases.find((a) => !a.hidden && a.preferred && authorized.has(a.resolve)) ??
106
+ modelAliases.find((a) => !a.hidden && authorized.has(a.resolve));
107
+ if (match) {
108
+ log.info(
109
+ `» model: ${match.resolve} (auto-selected${match.preferred ? " — preferred" : ""} curated match)`,
110
+ );
111
+ log.warning(`» model auto-selected. ${AUTO_SELECT_WARNING}`);
112
+ return match.resolve;
113
+ }
114
+ log.info(
115
+ `» opencode has ${authorized.size} models but none match curated aliases — letting OpenCode auto-select`,
116
+ );
117
+ }
118
+
119
+ log.warning(`» no model resolved. letting OpenCode auto-select. ${AUTO_SELECT_WARNING}`);
120
+ return undefined;
121
+ }