@travisennis/acai 0.0.8 → 0.0.10

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 (386) hide show
  1. package/README.md +48 -729
  2. package/bin/acai +52 -0
  3. package/dist/agent/index.d.ts +12 -2
  4. package/dist/agent/index.d.ts.map +1 -1
  5. package/dist/agent/index.js +378 -168
  6. package/dist/agent/sub-agent.d.ts +23 -0
  7. package/dist/agent/sub-agent.d.ts.map +1 -0
  8. package/dist/agent/sub-agent.js +109 -0
  9. package/dist/cli/index.d.ts +26 -0
  10. package/dist/cli/index.d.ts.map +1 -0
  11. package/dist/{cli.js → cli/index.js} +84 -76
  12. package/dist/cli/stdin.d.ts +9 -0
  13. package/dist/cli/stdin.d.ts.map +1 -0
  14. package/dist/cli/stdin.js +37 -0
  15. package/dist/commands/copy/index.js +2 -2
  16. package/dist/commands/copy/utils.d.ts.map +1 -1
  17. package/dist/commands/copy/utils.js +15 -13
  18. package/dist/commands/generate-rules/index.d.ts +1 -1
  19. package/dist/commands/generate-rules/index.d.ts.map +1 -1
  20. package/dist/commands/generate-rules/index.js +16 -100
  21. package/dist/commands/generate-rules/service.d.ts +21 -0
  22. package/dist/commands/generate-rules/service.d.ts.map +1 -0
  23. package/dist/commands/generate-rules/service.js +103 -0
  24. package/dist/commands/handoff/index.js +2 -2
  25. package/dist/commands/health/index.js +1 -1
  26. package/dist/commands/health/utils.d.ts.map +1 -1
  27. package/dist/commands/health/utils.js +6 -0
  28. package/dist/commands/history/index.d.ts +1 -1
  29. package/dist/commands/history/index.d.ts.map +1 -1
  30. package/dist/commands/history/index.js +17 -18
  31. package/dist/commands/history/types.d.ts +38 -0
  32. package/dist/commands/history/types.d.ts.map +1 -1
  33. package/dist/commands/history/utils.d.ts.map +1 -1
  34. package/dist/commands/history/utils.js +63 -58
  35. package/dist/commands/init/index.d.ts.map +1 -1
  36. package/dist/commands/init/index.js +3 -8
  37. package/dist/commands/init-project/index.d.ts.map +1 -1
  38. package/dist/commands/init-project/index.js +3 -3
  39. package/dist/commands/init-project/utils.d.ts.map +1 -1
  40. package/dist/commands/init-project/utils.js +10 -2
  41. package/dist/commands/list-tools/index.d.ts.map +1 -1
  42. package/dist/commands/list-tools/index.js +7 -31
  43. package/dist/commands/manager.d.ts +2 -2
  44. package/dist/commands/manager.d.ts.map +1 -1
  45. package/dist/commands/manager.js +57 -33
  46. package/dist/commands/model/index.d.ts.map +1 -1
  47. package/dist/commands/model/index.js +20 -151
  48. package/dist/commands/model/model-panel.d.ts +4 -0
  49. package/dist/commands/model/model-panel.d.ts.map +1 -0
  50. package/dist/commands/model/model-panel.js +144 -0
  51. package/dist/commands/paste/index.d.ts.map +1 -1
  52. package/dist/commands/paste/index.js +59 -62
  53. package/dist/commands/paste/utils.d.ts.map +1 -1
  54. package/dist/commands/paste/utils.js +88 -58
  55. package/dist/commands/pickup/index.d.ts.map +1 -1
  56. package/dist/commands/pickup/index.js +6 -3
  57. package/dist/commands/pickup/utils.js +3 -3
  58. package/dist/commands/resources/index.d.ts.map +1 -1
  59. package/dist/commands/resources/index.js +33 -50
  60. package/dist/commands/review/index.d.ts.map +1 -1
  61. package/dist/commands/review/index.js +3 -117
  62. package/dist/commands/review/review-panel.d.ts +3 -0
  63. package/dist/commands/review/review-panel.d.ts.map +1 -0
  64. package/dist/commands/review/review-panel.js +186 -0
  65. package/dist/commands/review/utils.d.ts +9 -0
  66. package/dist/commands/review/utils.d.ts.map +1 -1
  67. package/dist/commands/review/utils.js +127 -68
  68. package/dist/commands/session/index.d.ts +1 -1
  69. package/dist/commands/session/index.d.ts.map +1 -1
  70. package/dist/commands/session/index.js +134 -112
  71. package/dist/commands/session/types.d.ts +7 -0
  72. package/dist/commands/session/types.d.ts.map +1 -1
  73. package/dist/commands/share/html-renderer.d.ts +25 -0
  74. package/dist/commands/share/html-renderer.d.ts.map +1 -0
  75. package/dist/commands/share/html-renderer.js +384 -0
  76. package/dist/commands/share/index.d.ts +3 -0
  77. package/dist/commands/share/index.d.ts.map +1 -0
  78. package/dist/commands/share/index.js +122 -0
  79. package/dist/commands/shell/index.d.ts.map +1 -1
  80. package/dist/commands/shell/index.js +16 -1
  81. package/dist/commands/types.d.ts +2 -2
  82. package/dist/commands/types.d.ts.map +1 -1
  83. package/dist/{config.d.ts → config/index.d.ts} +20 -9
  84. package/dist/config/index.d.ts.map +1 -0
  85. package/dist/{config.js → config/index.js} +43 -42
  86. package/dist/execution/index.d.ts.map +1 -1
  87. package/dist/execution/index.js +75 -55
  88. package/dist/index.d.ts +1 -0
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/index.js +170 -127
  91. package/dist/middleware/cache.d.ts.map +1 -1
  92. package/dist/middleware/cache.js +18 -36
  93. package/dist/models/ai-config.d.ts +1 -0
  94. package/dist/models/ai-config.d.ts.map +1 -1
  95. package/dist/models/ai-config.js +4 -3
  96. package/dist/models/anthropic-provider.d.ts +2 -5
  97. package/dist/models/anthropic-provider.d.ts.map +1 -1
  98. package/dist/models/anthropic-provider.js +3 -70
  99. package/dist/models/deepseek-provider.d.ts +1 -0
  100. package/dist/models/deepseek-provider.d.ts.map +1 -1
  101. package/dist/models/google-provider.d.ts +2 -3
  102. package/dist/models/google-provider.d.ts.map +1 -1
  103. package/dist/models/google-provider.js +0 -26
  104. package/dist/models/groq-provider.d.ts +1 -0
  105. package/dist/models/groq-provider.d.ts.map +1 -1
  106. package/dist/models/manager.d.ts +13 -2
  107. package/dist/models/manager.d.ts.map +1 -1
  108. package/dist/models/manager.js +20 -8
  109. package/dist/models/openai-provider.d.ts +5 -5
  110. package/dist/models/openai-provider.d.ts.map +1 -1
  111. package/dist/models/openai-provider.js +27 -40
  112. package/dist/models/opencode-zen-provider.d.ts +8 -3
  113. package/dist/models/opencode-zen-provider.d.ts.map +1 -1
  114. package/dist/models/opencode-zen-provider.js +68 -11
  115. package/dist/models/openrouter-provider.d.ts +24 -30
  116. package/dist/models/openrouter-provider.d.ts.map +1 -1
  117. package/dist/models/openrouter-provider.js +92 -177
  118. package/dist/models/providers.d.ts +1 -1
  119. package/dist/models/providers.d.ts.map +1 -1
  120. package/dist/models/xai-provider.d.ts +4 -3
  121. package/dist/models/xai-provider.d.ts.map +1 -1
  122. package/dist/models/xai-provider.js +18 -18
  123. package/dist/modes/manager.d.ts +23 -0
  124. package/dist/modes/manager.d.ts.map +1 -0
  125. package/dist/modes/manager.js +77 -0
  126. package/dist/modes/prompts.d.ts +2 -0
  127. package/dist/modes/prompts.d.ts.map +1 -0
  128. package/dist/modes/prompts.js +143 -0
  129. package/dist/prompts/mentions.d.ts +11 -0
  130. package/dist/prompts/mentions.d.ts.map +1 -0
  131. package/dist/{mentions.js → prompts/mentions.js} +21 -80
  132. package/dist/prompts/system-prompt.d.ts +26 -0
  133. package/dist/prompts/system-prompt.d.ts.map +1 -0
  134. package/dist/{prompts.js → prompts/system-prompt.js} +50 -22
  135. package/dist/repl/index.d.ts +174 -0
  136. package/dist/repl/index.d.ts.map +1 -0
  137. package/dist/{repl-new.js → repl/index.js} +399 -76
  138. package/dist/repl/project-status.d.ts +1 -0
  139. package/dist/repl/project-status.d.ts.map +1 -1
  140. package/dist/repl/project-status.js +4 -1
  141. package/dist/sessions/manager.d.ts +93 -1
  142. package/dist/sessions/manager.d.ts.map +1 -1
  143. package/dist/sessions/manager.js +264 -9
  144. package/dist/sessions/summary.d.ts +4 -0
  145. package/dist/sessions/summary.d.ts.map +1 -0
  146. package/dist/sessions/summary.js +30 -0
  147. package/dist/{skills.d.ts → skills/index.d.ts} +14 -2
  148. package/dist/skills/index.d.ts.map +1 -0
  149. package/dist/skills/index.js +294 -0
  150. package/dist/subagents/index.d.ts +15 -0
  151. package/dist/subagents/index.d.ts.map +1 -0
  152. package/dist/subagents/index.js +231 -0
  153. package/dist/terminal/control.d.ts +1 -1
  154. package/dist/terminal/control.d.ts.map +1 -1
  155. package/dist/terminal/control.js +30 -9
  156. package/dist/terminal/east-asian-width.d.ts.map +1 -1
  157. package/dist/terminal/east-asian-width.js +404 -351
  158. package/dist/terminal/keys.d.ts +17 -0
  159. package/dist/terminal/keys.d.ts.map +1 -1
  160. package/dist/terminal/keys.js +37 -0
  161. package/dist/terminal/select-prompt.d.ts.map +1 -1
  162. package/dist/terminal/select-prompt.js +24 -12
  163. package/dist/terminal/string-width.d.ts.map +1 -1
  164. package/dist/terminal/string-width.js +25 -27
  165. package/dist/terminal/style.d.ts.map +1 -1
  166. package/dist/terminal/style.js +4 -7
  167. package/dist/terminal/supports-color.d.ts.map +1 -1
  168. package/dist/terminal/supports-color.js +41 -27
  169. package/dist/terminal/table/cell.d.ts +12 -0
  170. package/dist/terminal/table/cell.d.ts.map +1 -1
  171. package/dist/terminal/table/cell.js +40 -25
  172. package/dist/terminal/table/layout-manager.d.ts.map +1 -1
  173. package/dist/terminal/table/layout-manager.js +100 -68
  174. package/dist/terminal/table/utils.d.ts.map +1 -1
  175. package/dist/terminal/table/utils.js +17 -10
  176. package/dist/terminal/wrap-ansi.d.ts.map +1 -1
  177. package/dist/terminal/wrap-ansi.js +172 -103
  178. package/dist/tokens/tracker.d.ts +1 -0
  179. package/dist/tokens/tracker.d.ts.map +1 -1
  180. package/dist/tokens/tracker.js +3 -0
  181. package/dist/tools/agent.d.ts +27 -0
  182. package/dist/tools/agent.d.ts.map +1 -0
  183. package/dist/tools/agent.js +81 -0
  184. package/dist/tools/bash.d.ts +4 -3
  185. package/dist/tools/bash.d.ts.map +1 -1
  186. package/dist/tools/bash.js +343 -121
  187. package/dist/tools/code-search.d.ts +41 -0
  188. package/dist/tools/code-search.d.ts.map +1 -0
  189. package/dist/tools/code-search.js +195 -0
  190. package/dist/tools/directory-tree.d.ts +3 -3
  191. package/dist/tools/directory-tree.d.ts.map +1 -1
  192. package/dist/tools/directory-tree.js +8 -5
  193. package/dist/tools/dynamic-tool-loader.d.ts +2 -5
  194. package/dist/tools/dynamic-tool-loader.d.ts.map +1 -1
  195. package/dist/tools/dynamic-tool-loader.js +20 -4
  196. package/dist/tools/edit-file.d.ts +7 -7
  197. package/dist/tools/edit-file.d.ts.map +1 -1
  198. package/dist/tools/edit-file.js +164 -66
  199. package/dist/tools/glob.d.ts +6 -6
  200. package/dist/tools/glob.d.ts.map +1 -1
  201. package/dist/tools/glob.js +95 -55
  202. package/dist/tools/grep.d.ts +15 -12
  203. package/dist/tools/grep.d.ts.map +1 -1
  204. package/dist/tools/grep.js +300 -192
  205. package/dist/tools/index.d.ts +143 -5
  206. package/dist/tools/index.d.ts.map +1 -1
  207. package/dist/tools/index.js +39 -24
  208. package/dist/tools/ls.d.ts +2 -2
  209. package/dist/tools/ls.d.ts.map +1 -1
  210. package/dist/tools/ls.js +7 -5
  211. package/dist/tools/read-file.d.ts +3 -3
  212. package/dist/tools/read-file.d.ts.map +1 -1
  213. package/dist/tools/read-file.js +74 -34
  214. package/dist/tools/save-file.d.ts +3 -3
  215. package/dist/tools/save-file.d.ts.map +1 -1
  216. package/dist/tools/save-file.js +11 -11
  217. package/dist/tools/skill.d.ts +23 -0
  218. package/dist/tools/skill.d.ts.map +1 -0
  219. package/dist/tools/skill.js +65 -0
  220. package/dist/tools/think.d.ts.map +1 -1
  221. package/dist/tools/think.js +2 -9
  222. package/dist/tools/utils.d.ts +2 -0
  223. package/dist/tools/utils.d.ts.map +1 -1
  224. package/dist/tools/utils.js +12 -0
  225. package/dist/tools/web-fetch.d.ts +62 -0
  226. package/dist/tools/web-fetch.d.ts.map +1 -0
  227. package/dist/tools/web-fetch.js +429 -0
  228. package/dist/tools/web-search.d.ts +62 -0
  229. package/dist/tools/web-search.d.ts.map +1 -0
  230. package/dist/tools/web-search.js +226 -0
  231. package/dist/tui/autocomplete/attachment-provider.d.ts +3 -6
  232. package/dist/tui/autocomplete/attachment-provider.d.ts.map +1 -1
  233. package/dist/tui/autocomplete/attachment-provider.js +25 -78
  234. package/dist/tui/autocomplete/base-provider.d.ts +1 -0
  235. package/dist/tui/autocomplete/base-provider.d.ts.map +1 -1
  236. package/dist/tui/autocomplete/combined-provider.d.ts +1 -4
  237. package/dist/tui/autocomplete/combined-provider.d.ts.map +1 -1
  238. package/dist/tui/autocomplete/combined-provider.js +3 -17
  239. package/dist/tui/autocomplete/command-provider.d.ts +1 -0
  240. package/dist/tui/autocomplete/command-provider.d.ts.map +1 -1
  241. package/dist/tui/autocomplete/command-provider.js +3 -0
  242. package/dist/tui/autocomplete/file-search-provider.d.ts +2 -1
  243. package/dist/tui/autocomplete/file-search-provider.d.ts.map +1 -1
  244. package/dist/tui/autocomplete/file-search-provider.js +36 -16
  245. package/dist/tui/autocomplete/skill-provider.d.ts +17 -0
  246. package/dist/tui/autocomplete/skill-provider.d.ts.map +1 -0
  247. package/dist/tui/autocomplete/skill-provider.js +49 -0
  248. package/dist/tui/autocomplete.d.ts +2 -2
  249. package/dist/tui/autocomplete.d.ts.map +1 -1
  250. package/dist/tui/autocomplete.js +3 -5
  251. package/dist/tui/components/assistant-message.d.ts.map +1 -1
  252. package/dist/tui/components/assistant-message.js +0 -4
  253. package/dist/tui/components/editor.d.ts +21 -2
  254. package/dist/tui/components/editor.d.ts.map +1 -1
  255. package/dist/tui/components/editor.js +228 -236
  256. package/dist/tui/components/footer.d.ts +6 -4
  257. package/dist/tui/components/footer.d.ts.map +1 -1
  258. package/dist/tui/components/footer.js +49 -25
  259. package/dist/tui/components/markdown.d.ts +8 -5
  260. package/dist/tui/components/markdown.d.ts.map +1 -1
  261. package/dist/tui/components/markdown.js +57 -39
  262. package/dist/tui/components/modal.d.ts.map +1 -1
  263. package/dist/tui/components/modal.js +35 -33
  264. package/dist/tui/components/notification.d.ts +13 -2
  265. package/dist/tui/components/notification.d.ts.map +1 -1
  266. package/dist/tui/components/notification.js +37 -2
  267. package/dist/tui/components/progress-bar.js +1 -1
  268. package/dist/tui/components/select-list.d.ts +1 -0
  269. package/dist/tui/components/select-list.d.ts.map +1 -1
  270. package/dist/tui/components/select-list.js +14 -11
  271. package/dist/tui/components/text.d.ts +16 -0
  272. package/dist/tui/components/text.d.ts.map +1 -1
  273. package/dist/tui/components/text.js +72 -57
  274. package/dist/tui/components/thinking-block.d.ts +9 -0
  275. package/dist/tui/components/thinking-block.d.ts.map +1 -1
  276. package/dist/tui/components/thinking-block.js +43 -11
  277. package/dist/tui/components/tool-execution.d.ts +5 -1
  278. package/dist/tui/components/tool-execution.d.ts.map +1 -1
  279. package/dist/tui/components/tool-execution.js +19 -10
  280. package/dist/tui/components/user-message.d.ts.map +1 -1
  281. package/dist/tui/components/user-message.js +0 -3
  282. package/dist/tui/components/welcome.js +2 -2
  283. package/dist/tui/editor-launcher.d.ts +13 -0
  284. package/dist/tui/editor-launcher.d.ts.map +1 -0
  285. package/dist/tui/editor-launcher.js +39 -0
  286. package/dist/tui/index.d.ts +3 -1
  287. package/dist/tui/index.d.ts.map +1 -1
  288. package/dist/tui/index.js +1 -0
  289. package/dist/tui/terminal.d.ts +27 -0
  290. package/dist/tui/terminal.d.ts.map +1 -1
  291. package/dist/tui/terminal.js +144 -15
  292. package/dist/tui/tui.d.ts +43 -0
  293. package/dist/tui/tui.d.ts.map +1 -1
  294. package/dist/tui/tui.js +172 -41
  295. package/dist/utils/bash/parse.d.ts +19 -0
  296. package/dist/utils/bash/parse.d.ts.map +1 -0
  297. package/dist/utils/bash/parse.js +223 -0
  298. package/dist/utils/bash/quote.d.ts +6 -0
  299. package/dist/utils/bash/quote.d.ts.map +1 -0
  300. package/dist/utils/bash/quote.js +23 -0
  301. package/dist/utils/bash.d.ts.map +1 -1
  302. package/dist/utils/bash.js +211 -126
  303. package/dist/utils/command-protection.d.ts +28 -0
  304. package/dist/utils/command-protection.d.ts.map +1 -0
  305. package/dist/utils/command-protection.js +324 -0
  306. package/dist/utils/dedent.d.ts.map +1 -0
  307. package/dist/utils/env-expand.d.ts +2 -0
  308. package/dist/utils/env-expand.d.ts.map +1 -0
  309. package/dist/utils/env-expand.js +8 -0
  310. package/dist/utils/filesystem/path-display.d.ts +11 -0
  311. package/dist/utils/filesystem/path-display.d.ts.map +1 -0
  312. package/dist/utils/filesystem/path-display.js +32 -0
  313. package/dist/utils/filesystem/security.d.ts +2 -2
  314. package/dist/utils/filesystem/security.d.ts.map +1 -1
  315. package/dist/utils/filesystem/security.js +32 -31
  316. package/dist/utils/formatting.d.ts.map +1 -0
  317. package/dist/{formatting.js → utils/formatting.js} +1 -1
  318. package/dist/utils/git.d.ts +4 -0
  319. package/dist/utils/git.d.ts.map +1 -1
  320. package/dist/utils/git.js +30 -0
  321. package/dist/utils/glob.d.ts +1 -1
  322. package/dist/utils/glob.d.ts.map +1 -1
  323. package/dist/utils/logger.d.ts.map +1 -0
  324. package/dist/{logger.js → utils/logger.js} +1 -1
  325. package/dist/utils/parsing.d.ts.map +1 -0
  326. package/dist/utils/process.d.ts.map +1 -1
  327. package/dist/utils/process.js +90 -37
  328. package/dist/utils/templates.d.ts +2 -0
  329. package/dist/utils/templates.d.ts.map +1 -0
  330. package/dist/utils/templates.js +24 -0
  331. package/dist/utils/version.d.ts.map +1 -0
  332. package/dist/{version.js → utils/version.js} +1 -1
  333. package/package.json +34 -25
  334. package/dist/cli.d.ts +0 -23
  335. package/dist/cli.d.ts.map +0 -1
  336. package/dist/commands/exit/index.d.ts +0 -10
  337. package/dist/commands/exit/index.d.ts.map +0 -1
  338. package/dist/commands/exit/index.js +0 -21
  339. package/dist/commands/exit/types.d.ts +0 -8
  340. package/dist/commands/exit/types.d.ts.map +0 -1
  341. package/dist/commands/exit/types.js +0 -1
  342. package/dist/commands/exit/utils.d.ts +0 -2
  343. package/dist/commands/exit/utils.d.ts.map +0 -1
  344. package/dist/commands/exit/utils.js +0 -13
  345. package/dist/commands/prompt/index.d.ts +0 -5
  346. package/dist/commands/prompt/index.d.ts.map +0 -1
  347. package/dist/commands/prompt/index.js +0 -126
  348. package/dist/commands/prompt/types.d.ts +0 -15
  349. package/dist/commands/prompt/types.d.ts.map +0 -1
  350. package/dist/commands/prompt/types.js +0 -1
  351. package/dist/commands/prompt/utils.d.ts +0 -12
  352. package/dist/commands/prompt/utils.d.ts.map +0 -1
  353. package/dist/commands/prompt/utils.js +0 -107
  354. package/dist/commands/reset/index.d.ts +0 -3
  355. package/dist/commands/reset/index.d.ts.map +0 -1
  356. package/dist/commands/reset/index.js +0 -25
  357. package/dist/commands/reset/types.d.ts +0 -1
  358. package/dist/commands/reset/types.d.ts.map +0 -1
  359. package/dist/commands/reset/types.js +0 -3
  360. package/dist/commands/save/index.d.ts +0 -3
  361. package/dist/commands/save/index.d.ts.map +0 -1
  362. package/dist/commands/save/index.js +0 -19
  363. package/dist/config.d.ts.map +0 -1
  364. package/dist/dedent.d.ts.map +0 -1
  365. package/dist/formatting.d.ts.map +0 -1
  366. package/dist/logger.d.ts.map +0 -1
  367. package/dist/mentions.d.ts +0 -14
  368. package/dist/mentions.d.ts.map +0 -1
  369. package/dist/parsing.d.ts.map +0 -1
  370. package/dist/prompts.d.ts +0 -10
  371. package/dist/prompts.d.ts.map +0 -1
  372. package/dist/repl-new.d.ts +0 -62
  373. package/dist/repl-new.d.ts.map +0 -1
  374. package/dist/skills.d.ts.map +0 -1
  375. package/dist/skills.js +0 -233
  376. package/dist/tui/autocomplete/path-provider.d.ts +0 -21
  377. package/dist/tui/autocomplete/path-provider.d.ts.map +0 -1
  378. package/dist/tui/autocomplete/path-provider.js +0 -164
  379. package/dist/version.d.ts.map +0 -1
  380. /package/dist/{dedent.d.ts → utils/dedent.d.ts} +0 -0
  381. /package/dist/{dedent.js → utils/dedent.js} +0 -0
  382. /package/dist/{formatting.d.ts → utils/formatting.d.ts} +0 -0
  383. /package/dist/{logger.d.ts → utils/logger.d.ts} +0 -0
  384. /package/dist/{parsing.d.ts → utils/parsing.d.ts} +0 -0
  385. /package/dist/{parsing.js → utils/parsing.js} +0 -0
  386. /package/dist/{version.d.ts → utils/version.d.ts} +0 -0
@@ -0,0 +1,81 @@
1
+ import { z } from "zod";
2
+ import { SubAgent } from "../agent/sub-agent.js";
3
+ import { isSupportedModel } from "../models/providers.js";
4
+ import { environmentInfo } from "../prompts/system-prompt.js";
5
+ import { getSubagent, loadSubagents } from "../subagents/index.js";
6
+ import style from "../terminal/style.js";
7
+ export const AgentTool = {
8
+ name: "Agent",
9
+ };
10
+ async function getToolDescription() {
11
+ return "Delegate a task to a specialized subagent.";
12
+ }
13
+ const inputSchema = z.object({
14
+ prompt: z.string().describe("The task for the agent to perform"),
15
+ type: z.string().describe("The subagent type to use (matches subagent name)"),
16
+ timeout: z
17
+ .number()
18
+ .optional()
19
+ .describe("Override default timeout in seconds"),
20
+ });
21
+ async function loadSubAgentDefinition(type) {
22
+ const subagent = await getSubagent(type);
23
+ if (!subagent) {
24
+ const available = await loadSubagents();
25
+ const names = available.map((s) => s.name).join(", ");
26
+ throw new Error(`Unknown subagent type: "${type}". Available: ${names}`);
27
+ }
28
+ return {
29
+ model: subagent.model ?? "",
30
+ system: subagent.systemPrompt,
31
+ tools: subagent.tools,
32
+ timeout: subagent.timeout,
33
+ };
34
+ }
35
+ export const createAgentTools = async (options) => {
36
+ const description = await getToolDescription();
37
+ const toolDef = {
38
+ description,
39
+ inputSchema,
40
+ };
41
+ function display({ prompt, type }) {
42
+ return `${style.cyan(type)} - ${style.dim(prompt.substring(0, 25))}`;
43
+ }
44
+ async function execute({ prompt, type, timeout }, { abortSignal }) {
45
+ if (abortSignal?.aborted) {
46
+ throw new Error("Agent execution aborted");
47
+ }
48
+ const { model, system, tools, timeout: defaultTimeout, } = await loadSubAgentDefinition(type);
49
+ const systemPrompt = `${system}
50
+
51
+ ${await environmentInfo(options.workspace.primaryDir, options.workspace.allowedDirs)}`;
52
+ const subagent = new SubAgent({ workspace: options.workspace });
53
+ const effectiveTimeout = timeout ?? defaultTimeout;
54
+ try {
55
+ const result = await subagent.execute({
56
+ model: isSupportedModel(model) ? model : "opencode:minimax-m2.5-free",
57
+ system: systemPrompt,
58
+ prompt,
59
+ abortSignal,
60
+ allowedTools: tools,
61
+ timeout: effectiveTimeout,
62
+ });
63
+ return result;
64
+ }
65
+ catch (error) {
66
+ const err = error;
67
+ const message = err.message || "Unknown error";
68
+ if (message.includes("timed out") ||
69
+ err.name === "AbortError" ||
70
+ err.name === "TimeoutError") {
71
+ return `Agent failed: ${message}. The timeout was ${effectiveTimeout} seconds. Consider increasing the timeout or breaking the task into smaller subtasks.`;
72
+ }
73
+ return `Agent failed: ${message}`;
74
+ }
75
+ }
76
+ return {
77
+ toolDef,
78
+ display,
79
+ execute,
80
+ };
81
+ };
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import type { WorkspaceContext } from "../index.ts";
2
3
  import type { ToolExecutionOptions } from "./types.ts";
3
4
  export declare const BashTool: {
4
5
  name: "Bash";
@@ -10,9 +11,9 @@ declare const inputSchema: z.ZodObject<{
10
11
  background: z.ZodOptional<z.ZodBoolean>;
11
12
  }, z.core.$strip>;
12
13
  type BashInputSchema = z.infer<typeof inputSchema>;
13
- export declare const createBashTool: ({ baseDir, allowedDirs, }: {
14
- baseDir: string;
15
- allowedDirs?: string[];
14
+ export declare const createBashTool: (options: {
15
+ workspace: WorkspaceContext;
16
+ env?: Record<string, string>;
16
17
  }) => Promise<{
17
18
  toolDef: {
18
19
  description: string;
@@ -1 +1 @@
1
- {"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../source/tools/bash.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,eAAO,MAAM,QAAQ;;CAEpB,CAAC;AAYF,QAAA,MAAM,WAAW;;;;;iBAkBf,CAAC;AAEH,KAAK,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEnD,eAAO,MAAM,cAAc,GAAU,2BAGlC;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;;;;;;;;;;yBAQwB,eAAe;mDAIK,eAAe,mBACrC,oBAAoB,GACpC,OAAO,CAAC,MAAM,CAAC;EA0FrB,CAAC"}
1
+ {"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../source/tools/bash.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAUpD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AA0CvD,eAAO,MAAM,QAAQ;;CAEpB,CAAC;AAyLF,QAAA,MAAM,WAAW;;;;;iBAkBf,CAAC;AAEH,KAAK,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEnD,eAAO,MAAM,cAAc,GAAU,SAAS;IAC5C,SAAS,EAAE,gBAAgB,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;;;;;;;;;;yBAqFwB,eAAe;mDAIK,eAAe,mBACrC,oBAAoB,GACpC,OAAO,CAAC,MAAM,CAAC;EA6BrB,CAAC"}
@@ -1,25 +1,219 @@
1
- import { execSync } from "node:child_process";
1
+ // import { execSync } from "node:child_process";
2
+ import { randomBytes } from "node:crypto";
3
+ import { mkdirSync, writeFileSync } from "node:fs";
4
+ import { dirname } from "node:path";
2
5
  import { z } from "zod";
3
6
  import { initExecutionEnvironment } from "../execution/index.js";
4
- import { logger } from "../logger.js";
5
7
  import style from "../terminal/style.js";
6
8
  import { resolveCwd, validatePaths } from "../utils/bash.js";
9
+ import { detectDestructiveCommand, formatBlockedCommandMessage, } from "../utils/command-protection.js";
10
+ import { expandEnvVars } from "../utils/env-expand.js";
11
+ import { logger } from "../utils/logger.js";
7
12
  import { convertNullString } from "../utils/zod.js";
13
+ /**
14
+ * Detects git commit commands with multi-line -m messages that will fail in shell.
15
+ * Writes the message to a temp file and returns an error with the file path.
16
+ * Returns null if the command is safe.
17
+ */
18
+ function detectMultilineGitCommit(command) {
19
+ const trimmed = command.trim();
20
+ // Check if it's a git commit command
21
+ if (!trimmed.startsWith("git commit") && !trimmed.startsWith("git ")) {
22
+ return null;
23
+ }
24
+ // Look for -m or -am flags with a message containing newlines
25
+ // Match patterns like: git commit -m "message\nwith\nnewlines"
26
+ // or: git commit -am "message\nwith\nnewlines"
27
+ // Using [\s\S] instead of [^] to match any character including newlines
28
+ const messageMatch = trimmed.match(/-am?\s+["']([\s\S]*?)["']/);
29
+ if (!messageMatch) {
30
+ return null;
31
+ }
32
+ const message = messageMatch[1];
33
+ if (message.includes("\n")) {
34
+ const randomId = randomBytes(4).toString("hex");
35
+ const commitMsgPath = `/tmp/acai/commit-msg-${randomId}.txt`;
36
+ try {
37
+ mkdirSync(dirname(commitMsgPath), { recursive: true });
38
+ writeFileSync(commitMsgPath, message, "utf-8");
39
+ }
40
+ catch (error) {
41
+ logger.error(error, "Failed to write commit message to temp file");
42
+ }
43
+ return `Multi-line commit messages with -m flag cause shell parsing errors. The commit message has been written to:
44
+ ${commitMsgPath}
45
+ Use: git commit -F ${commitMsgPath}`;
46
+ }
47
+ return null;
48
+ }
8
49
  export const BashTool = {
9
50
  name: "Bash",
10
51
  };
11
- const installedTools = getInstalledTools();
12
- const toolDescription = `Execute commands in a shell. Commands can execute only within the allowed directories. Always use absolute paths.
13
-
14
- Tools available:
15
- ${installedTools}`;
52
+ // const installedTools = getInstalledTools();
53
+ // const toolDescription = `Execute commands in a shell. Commands can execute only within the allowed directories. Always use absolute paths. Working directory persists between commands; shell state (everything else) does not. The shell environment is initialized from the user's profile (bash or zsh).
54
+ // IMPORTANT: This tool is for terminal operations like git, npm, docker, etc. DO NOT use it for file operations (reading, writing, editing, searching, finding files) - use the specialized tools for this instead.
55
+ // Before executing the command, please follow these steps:
56
+ // 1. Directory Verification:
57
+ // - If the command will create new directories or files, first use \`ls\` to verify the parent directory exists and is the correct location
58
+ // - For example, before running "mkdir foo/bar", first use \`ls foo\` to check that "foo" exists and is the intended parent directory
59
+ // 2. Command Execution:
60
+ // - Always quote file paths that contain spaces with double quotes (e.g., cd "path with spaces/file.txt")
61
+ // - Examples of proper quoting:
62
+ // - cd "/Users/name/My Documents" (correct)
63
+ // - cd /Users/name/My Documents (incorrect - will fail)
64
+ // - python "/path/with spaces/script.py" (correct)
65
+ // - python /path/with spaces/script.py (incorrect - will fail)
66
+ // - After ensuring proper quoting, execute the command.
67
+ // - Capture the output of the command.
68
+ // Usage notes:
69
+ // - The command argument is required.
70
+ // - You can specify an optional timeout in milliseconds (up to 600000ms / 10 minutes). If not specified, commands will timeout after 120000ms (2 minutes).
71
+ // - It is very helpful if you write a clear, concise description of what this command does. For simple commands, keep it brief (5-10 words). For complex commands (piped commands, obscure flags, or anything hard to understand at a glance), add enough context to clarify what it does.
72
+ // - If the output exceeds 30000 characters, output will be truncated before being returned to you.
73
+ // - You can use the \`background\` parameter to run the command in the background. Only use this if you don't need the result immediately and are OK being notified when the command completes later. You do not need to check the output right away - you'll be notified when it finishes. You do not need to use '&' at the end of the command when using this parameter.
74
+ // - Avoid using Bash with the \`find\`, \`grep\`, \`cat\`, \`head\`, \`tail\`, \`sed\`, \`awk\`, or \`echo\` commands, unless explicitly instructed or when these commands are truly necessary for the task. Instead, always prefer using the dedicated tools for these commands:
75
+ // - File listing: Use LS or DirectoryTree (not ls)
76
+ // - File search: Use Glob (NOT find or ls)
77
+ // - Content search: Use Grep (NOT grep or rg)
78
+ // - Read files: Use Read (NOT cat/head/tail)
79
+ // - Edit files: Use Edit (NOT sed/awk)
80
+ // - Write files: Use Write (NOT echo >/cat <<EOF)
81
+ // - Communication: Output text directly (NOT echo/printf)
82
+ // - When issuing multiple commands:
83
+ // - If the commands are independent and can run in parallel, make multiple Bash tool calls in a single message. For example, if you need to run "git status" and "git diff", send a single message with two Bash tool calls in parallel.
84
+ // - If the commands depend on each other and must run sequentially, use a single Bash call with '&&' to chain them together (e.g., \`git add . && git commit -m "message" && git push\`). For instance, if one operation must complete before another starts (like mkdir before cp, Write before Bash for git operations, or git add before git commit), run these operations sequentially instead.
85
+ // - Use ';' only when you need to run commands sequentially but don't care if earlier commands fail
86
+ // - DO NOT use newlines to separate commands (newlines are ok in quoted strings)
87
+ // - Commands execute in the project root directory by default. Do NOT prepend \`cd <project-root> &&\` to commands—it's unnecessary.
88
+ // - Try to maintain your current working directory throughout the session by using absolute paths and avoiding usage of \`cd\`. You may use \`cd\` if the User explicitly requests it.
89
+ // <good-example>
90
+ // pytest /foo/bar/tests
91
+ // </good-example>
92
+ // <bad-example>
93
+ // cd /foo/bar && pytest tests
94
+ // </bad-example>
95
+ // ### Committing changes with git
96
+ // Only create commits when requested by the user. If unclear, ask first. When the user asks you to create a new git commit, follow these steps carefully:
97
+ // Git Safety Protocol:
98
+ // - NEVER update the git config
99
+ // - NEVER run destructive git commands (push --force, reset --hard, checkout ., restore ., clean -f, branch -D) unless the user explicitly requests these actions. Taking unauthorized destructive actions is unhelpful and can result in lost work, so it's best to ONLY run these commands when given direct instructions
100
+ // - NEVER skip hooks (--no-verify, --no-gpg-sign, etc) unless the user explicitly requests it
101
+ // - NEVER run force push to main/master, warn the user if they request it
102
+ // - CRITICAL: Always create NEW commits rather than amending, unless the user explicitly requests a git amend. When a pre-commit hook fails, the commit did NOT happen — so --amend would modify the PREVIOUS commit, which may result in destroying work or losing previous changes. Instead, after hook failure, fix the issue, re-stage, and create a NEW commit
103
+ // - When staging files, prefer adding specific files by name rather than using "git add -A" or "git add .", which can accidentally include sensitive files (.env, credentials) or large binaries
104
+ // - NEVER commit changes unless the user explicitly asks you to. It is VERY IMPORTANT to only commit when explicitly asked, otherwise the user will feel that you are being too proactive
105
+ // 1. You can call multiple tools in a single response. When multiple independent pieces of information are requested and all commands are likely to succeed, run multiple tool calls in parallel for optimal performance. run the following bash commands in parallel, each using the Bash tool:
106
+ // - Run a git status command to see all untracked files. IMPORTANT: Never use the -uall flag as it can cause memory issues on large repos.
107
+ // - Run a git diff command to see both staged and unstaged changes that will be committed.
108
+ // - Run a git log command to see recent commit messages, so that you can follow this repository's commit message style.
109
+ // 2. Analyze all staged changes (both previously staged and newly added) and draft a commit message:
110
+ // - Summarize the nature of the changes (eg. new feature, enhancement to an existing feature, bug fix, refactoring, test, docs, etc.). Ensure the message accurately reflects the changes and their purpose (i.e. "add" means a wholly new feature, "update" means an enhancement to an existing feature, "fix" means a bug fix, etc.).
111
+ // - Do not commit files that likely contain secrets (.env, credentials.json, etc). Warn the user if they specifically request to commit those files
112
+ // - Draft a concise (1-2 sentences) commit message that focuses on the "why" rather than the "what"
113
+ // - Ensure it accurately reflects the changes and their purpose
114
+ // 3. You can call multiple tools in a single response. When multiple independent pieces of information are requested and all commands are likely to succeed, run multiple tool calls in parallel for optimal performance. run the following commands:
115
+ // - Add relevant untracked files to the staging area.
116
+ // - Run git status after the commit completes to verify success.
117
+ // Note: git status depends on the commit completing, so run it sequentially after the commit.
118
+ // 4. If the commit fails due to pre-commit hook: fix the issue and create a NEW commit
119
+ // Important notes:
120
+ // - NEVER run additional commands to read or explore code, besides git bash commands
121
+ // - DO NOT push to the remote repository unless the user explicitly asks you to do so
122
+ // - IMPORTANT: Never use git commands with the -i flag (like git rebase -i or git add -i) since they require interactive input which is not supported.
123
+ // - IMPORTANT: Do not use --no-edit with git rebase commands, as the --no-edit flag is not a valid option for git rebase.
124
+ // - If there are no changes to commit (i.e., no untracked files and no modifications), do not create an empty commit
125
+ // - In order to ensure good formatting, ALWAYS pass the commit message via a HEREDOC, a la this example:
126
+ // <example>
127
+ // git commit -m "$(cat <<'EOF'
128
+ // Commit message here.
129
+ // EOF
130
+ // )"
131
+ // </example>
132
+ // ### Creating pull requests
133
+ // Use the gh command via the Bash tool for ALL GitHub-related tasks including working with issues, pull requests, checks, and releases. If given a Github URL use the gh command to get the information needed.
134
+ // IMPORTANT: When the user asks you to create a pull request, follow these steps carefully:
135
+ // 1. You can call multiple tools in a single response. When multiple independent pieces of information are requested and all commands are likely to succeed, run multiple tool calls in parallel for optimal performance. run the following bash commands in parallel using the Bash tool, in order to understand the current state of the branch since it diverged from the main branch:
136
+ // - Run a git status command to see all untracked files (never use -uall flag)
137
+ // - Run a git diff command to see both staged and unstaged changes that will be committed
138
+ // - Check if the current branch tracks a remote branch and is up to date with the remote, so you know if you need to push to the remote
139
+ // - Run a git log command and \`git diff [base-branch]...HEAD\` to understand the full commit history for the current branch (from the time it diverged from the base branch)
140
+ // 2. Analyze all changes that will be included in the pull request, making sure to look at all relevant commits (NOT just the latest commit, but ALL commits that will be included in the pull request!!!), and draft a pull request title and summary:
141
+ // - Keep the PR title short (under 70 characters)
142
+ // - Use the description/body for details, not the title
143
+ // 3. You can call multiple tools in a single response. When multiple independent pieces of information are requested and all commands are likely to succeed, run multiple tool calls in parallel for optimal performance. run the following commands in parallel:
144
+ // - Create new branch if needed
145
+ // - Push to remote with -u flag if needed
146
+ // - Create PR using gh pr create with the format below. Use a HEREDOC to pass the body to ensure correct formatting.
147
+ // <example>
148
+ // gh pr create --title "the pr title" --body "$(cat <<'EOF'
149
+ // #### Summary
150
+ // <1-3 bullet points>
151
+ // #### Test plan
152
+ // [Bulleted markdown checklist of TODOs for testing the pull request...]
153
+ // EOF
154
+ // )"
155
+ // </example>
156
+ // Important:
157
+ // - Return the PR URL when you're done, so the user can see it
158
+ // ### Other common operations
159
+ // - View comments on a Github PR: gh api repos/foo/bar/pulls/123/comments
160
+ // {
161
+ // "$schema": "https://json-schema.org/draft/2020-12/schema",
162
+ // "type": "object",
163
+ // "properties": {
164
+ // "command": {
165
+ // "description": "The command to execute",
166
+ // "type": "string"
167
+ // },
168
+ // "timeout": {
169
+ // "description": "Optional timeout in milliseconds (max 600000)",
170
+ // "type": "number"
171
+ // },
172
+ // "description": {
173
+ // "description": "Clear, concise description of what this command does in active voice. Never use words like "complex" or "risk" in the description - just describe what it does.\n\nFor simple commands (git, npm, standard CLI tools), keep it brief (5-10 words):\n- ls → "List files in current directory"\n- git status → "Show working tree status"\n- npm install → "Install package dependencies"\n\nFor commands that are harder to parse at a glance (piped commands, obscure flags, etc.), add enough context to clarify what it does:\n- find . -name "*.tmp" -exec rm {} \\; → "Find and delete all .tmp files recursively"\n- git reset --hard origin/main → "Discard all local changes and match remote main"\n- curl -s url | jq '.data[]' → "Fetch JSON from URL and extract data array elements"",
174
+ // "type": "string"
175
+ // },
176
+ // "run_in_background": {
177
+ // "description": "Set to true to run this command in the background. Use TaskOutput to read the output later.",
178
+ // "type": "boolean"
179
+ // },
180
+ // "dangerouslyDisableSandbox": {
181
+ // "description": "Set this to true to dangerously override sandbox mode and run commands without sandboxing.",
182
+ // "type": "boolean"
183
+ // },
184
+ // "_simulatedSedEdit": {
185
+ // "description": "Internal: pre-computed sed edit result from preview",
186
+ // "type": "object",
187
+ // "properties": {
188
+ // "filePath": {
189
+ // "type": "string"
190
+ // },
191
+ // "newContent": {
192
+ // "type": "string"
193
+ // }
194
+ // },
195
+ // "required": [
196
+ // "filePath",
197
+ // "newContent"
198
+ // ],
199
+ // "additionalProperties": false
200
+ // }
201
+ // },
202
+ // "required": [
203
+ // "command"
204
+ // ],
205
+ // "additionalProperties": false
206
+ // }
207
+ // Tools available:
208
+ // ${installedTools}`;
209
+ const simpleDescription = "Run terminal commands.";
16
210
  // Command execution timeout in milliseconds
17
211
  const DEFAULT_TIMEOUT = 1.5 * 60 * 1000; // 1.5 minutes
18
212
  const inputSchema = z.object({
19
213
  command: z.string().describe("Full CLI command to execute."),
20
214
  cwd: z
21
215
  .preprocess((val) => convertNullString(val), z.string().nullable())
22
- .describe("Working directory file path (default: project root). Must be within the project directory. Required but nullable."),
216
+ .describe("Optional working directory. Commands execute in the project root by default. Only specify if you need a different directory. Must be within allowed directories."),
23
217
  timeout: z
24
218
  .preprocess((val) => convertNullString(val), z.coerce.number().nullable())
25
219
  .describe(`Command execution timeout in milliseconds. Required but nullable. If null, the default value is ${DEFAULT_TIMEOUT}ms`),
@@ -28,138 +222,154 @@ const inputSchema = z.object({
28
222
  .optional()
29
223
  .describe("Run command in background. If true, command will run until program exit."),
30
224
  });
31
- export const createBashTool = async ({ baseDir, allowedDirs, }) => {
32
- const execEnv = await initExecutionEnvironment();
33
- const allowedDirectories = allowedDirs ?? [baseDir];
225
+ export const createBashTool = async (options) => {
226
+ const { primaryDir, allowedDirs } = options.workspace;
227
+ const configEnv = options.env ? expandEnvVars(options.env) : {};
228
+ const execEnv = await initExecutionEnvironment({
229
+ execution: {
230
+ env: {
231
+ ...configEnv,
232
+ },
233
+ },
234
+ });
235
+ const allowedDirectories = allowedDirs ?? [primaryDir];
236
+ function validateCommand(command, allowedDirs, cwd) {
237
+ const pathValidation = validatePaths(command, allowedDirs, cwd);
238
+ if (!pathValidation.isValid) {
239
+ throw new Error(pathValidation.error ?? "Unknown error.");
240
+ }
241
+ const multilineError = detectMultilineGitCommit(command);
242
+ if (multilineError) {
243
+ throw new Error(multilineError);
244
+ }
245
+ const destructiveCheck = detectDestructiveCommand(command);
246
+ if (destructiveCheck.blocked) {
247
+ throw new Error(formatBlockedCommandMessage(destructiveCheck));
248
+ }
249
+ }
250
+ function processCommand(cmd, isBackground) {
251
+ const stripped = stripTrailingAmpersand(cmd, isBackground);
252
+ return fixRgCommand(stripped);
253
+ }
254
+ function executeBackground(cmd, cwd, signal) {
255
+ const proc = execEnv.executeCommandInBackground(cmd, {
256
+ cwd,
257
+ abortSignal: signal,
258
+ onOutput: (output) => {
259
+ logger.debug({ output }, "Background command output:");
260
+ },
261
+ onError: (error) => {
262
+ logger.debug({ error }, "Background command error:");
263
+ },
264
+ onExit: (code) => {
265
+ logger.debug(`Background command exited with code ${code}`);
266
+ },
267
+ });
268
+ return `Background process started with PID: ${proc.pid}`;
269
+ }
270
+ async function executeSync(cmd, cwd, timeout, signal) {
271
+ const { output, exitCode } = await execEnv.executeCommand(cmd, {
272
+ cwd,
273
+ timeout,
274
+ abortSignal: signal,
275
+ preserveOutputOnError: true,
276
+ captureStderr: true,
277
+ throwOnError: false,
278
+ });
279
+ if (exitCode !== 0) {
280
+ throw new Error(output);
281
+ }
282
+ return output;
283
+ }
34
284
  return {
35
285
  toolDef: {
36
- description: toolDescription,
286
+ description: simpleDescription,
37
287
  inputSchema,
38
288
  },
39
289
  display({ command }) {
40
- return `\n> ${style.cyan(command)}`;
290
+ return `${style.cyan(command)}`;
41
291
  },
42
292
  async execute({ command, cwd, timeout, background }, { abortSignal }) {
43
293
  if (abortSignal?.aborted) {
44
294
  throw new Error("Command execution aborted");
45
295
  }
46
- // grok doesn't follow my instructions
47
296
  const safeCwd = cwd === "null" ? null : cwd;
48
- const resolvedCwd = resolveCwd(safeCwd, baseDir, allowedDirectories);
297
+ const resolvedCwd = resolveCwd(safeCwd, primaryDir, allowedDirectories);
49
298
  const safeTimeout = timeout ?? DEFAULT_TIMEOUT;
50
- const pathValidation = validatePaths(command, allowedDirectories, resolvedCwd);
51
- if (!pathValidation.isValid) {
52
- throw new Error(pathValidation.error ?? "Unknown error.");
53
- }
299
+ validateCommand(command, allowedDirectories, resolvedCwd);
54
300
  if (abortSignal?.aborted) {
55
301
  throw new Error("Command execution aborted before running the command");
56
302
  }
57
- // Handle background execution
303
+ const processedCommand = processCommand(command, background ?? false);
58
304
  if (background) {
59
- // Strip any existing & from command to avoid double backgrounding
60
- let processedCommand = command.trim();
61
- if (processedCommand.endsWith("&")) {
62
- logger.warn(`Stripping '&' from command since background=true: ${command}`);
63
- processedCommand = processedCommand.slice(0, -1).trim();
64
- }
65
- // Fix rg commands that don't have an explicit path
66
- processedCommand = fixRgCommand(processedCommand);
67
- const backgroundProcess = execEnv.executeCommandInBackground(processedCommand, {
68
- cwd: resolvedCwd,
69
- abortSignal,
70
- onOutput: (output) => {
71
- logger.debug({ output }, "Background command output:");
72
- },
73
- onError: (error) => {
74
- logger.debug({ error }, "Background command error:");
75
- },
76
- onExit: (code) => {
77
- logger.debug(`Background command exited with code ${code}`);
78
- },
79
- });
80
- return `Background process started with PID: ${backgroundProcess.pid}`;
81
- }
82
- // Handle regular synchronous execution
83
- // Strip & if present to ensure synchronous behavior
84
- let processedCommand = command.trim();
85
- if (processedCommand.endsWith("&")) {
86
- logger.warn(`Stripping '&' from command since background=false: ${command}`);
87
- processedCommand = processedCommand.slice(0, -1).trim();
88
- }
89
- // Fix rg commands that don't have an explicit path
90
- // rg hangs when stdin is a socket and no path is given
91
- processedCommand = fixRgCommand(processedCommand);
92
- const { output, exitCode } = await execEnv.executeCommand(processedCommand, {
93
- cwd: resolvedCwd,
94
- timeout: safeTimeout,
95
- abortSignal,
96
- preserveOutputOnError: true,
97
- captureStderr: true,
98
- throwOnError: false,
99
- });
100
- if (exitCode !== 0) {
101
- throw new Error(output);
305
+ return executeBackground(processedCommand, resolvedCwd, abortSignal);
102
306
  }
103
- return output;
307
+ return executeSync(processedCommand, resolvedCwd, safeTimeout, abortSignal);
104
308
  },
105
309
  };
106
310
  };
107
- function getInstalledTools() {
108
- // Check for required bash tools
109
- const tools = [
110
- {
111
- name: "git",
112
- command: "git --version",
113
- description: "Version control system - used for cloning repositories, checking out branches, committing changes, viewing history, and managing code versions",
114
- },
115
- {
116
- name: "gh",
117
- command: "gh --version",
118
- description: "GitHub CLI - used for creating pull requests, managing issues, interacting with GitHub API, and automating GitHub workflows",
119
- },
120
- {
121
- name: "rg",
122
- command: "rg --version",
123
- description: "ripgrep - fast text search tool for searching code patterns, file contents, and regular expressions across the codebase (use this instead of grep)",
124
- },
125
- {
126
- name: "fd",
127
- command: "fd --version",
128
- description: "Fast file finder - alternative to find command, used for finding files by name, pattern, or type with intuitive syntax (use this instead of find)",
129
- },
130
- {
131
- name: "ast-grep",
132
- command: "ast-grep --version",
133
- description: "AST-based code search - used for structural code search, refactoring, finding patterns in abstract syntax trees, and code transformations",
134
- },
135
- {
136
- name: "jq",
137
- command: "jq --version",
138
- description: "JSON processor - used for parsing, filtering, and manipulating JSON output from APIs, commands, and configuration files",
139
- },
140
- {
141
- name: "yq",
142
- command: "yq --version",
143
- description: "YAML processor - used for parsing and manipulating YAML files (configs, CI/CD pipelines, Kubernetes manifests) with jq-like syntax",
144
- },
145
- ];
146
- const toolStatus = tools
147
- .map((tool) => {
148
- let status = false;
149
- try {
150
- execSync(tool.command, { stdio: "ignore", timeout: 5000 });
151
- status = true;
152
- }
153
- catch (_error) {
154
- // Ignore error, tool is not installed
155
- }
156
- return { name: tool.name, description: tool.description, status };
157
- })
158
- .filter((tool) => tool.status)
159
- .map((tool) => `- **${tool.name}**: ${tool.description}`)
160
- .join("\n");
161
- return toolStatus;
162
- }
311
+ // function getInstalledTools() {
312
+ // // Check for required bash tools
313
+ // const tools = [
314
+ // {
315
+ // name: "git",
316
+ // command: "git --version",
317
+ // description:
318
+ // "Version control system - used for cloning repositories, checking out branches, committing changes, viewing history, and managing code versions",
319
+ // },
320
+ // {
321
+ // name: "gh",
322
+ // command: "gh --version",
323
+ // description:
324
+ // "GitHub CLI - used for creating pull requests, managing issues, interacting with GitHub API, and automating GitHub workflows",
325
+ // },
326
+ // {
327
+ // name: "rg",
328
+ // command: "rg --version",
329
+ // description:
330
+ // "ripgrep - fast text search tool for searching code patterns, file contents, and regular expressions across the codebase (use this instead of grep)",
331
+ // },
332
+ // {
333
+ // name: "fd",
334
+ // command: "fd --version",
335
+ // description:
336
+ // "Fast file finder - alternative to find command, used for finding files by name, pattern, or type with intuitive syntax (use this instead of find)",
337
+ // },
338
+ // {
339
+ // name: "ast-grep",
340
+ // command: "ast-grep --version",
341
+ // description:
342
+ // "AST-based code search - used for structural code search, refactoring, finding patterns in abstract syntax trees, and code transformations",
343
+ // },
344
+ // {
345
+ // name: "jq",
346
+ // command: "jq --version",
347
+ // description:
348
+ // "JSON processor - used for parsing, filtering, and manipulating JSON output from APIs, commands, and configuration files",
349
+ // },
350
+ // {
351
+ // name: "yq",
352
+ // command: "yq --version",
353
+ // description:
354
+ // "YAML processor - used for parsing and manipulating YAML files (configs, CI/CD pipelines, Kubernetes manifests) with jq-like syntax",
355
+ // },
356
+ // ];
357
+ // const toolStatus = tools
358
+ // .map((tool) => {
359
+ // let status = false;
360
+ // try {
361
+ // execSync(tool.command, { stdio: "ignore", timeout: 5000 });
362
+ // status = true;
363
+ // } catch (_error) {
364
+ // // Ignore error, tool is not installed
365
+ // }
366
+ // return { name: tool.name, description: tool.description, status };
367
+ // })
368
+ // .filter((tool) => tool.status)
369
+ // .map((tool) => `- **${tool.name}**: ${tool.description}`)
370
+ // .join("\n");
371
+ // return toolStatus;
372
+ // }
163
373
  /**
164
374
  * Fix rg commands that don't have an explicit path
165
375
  * rg hangs when stdin is a socket and no path is given
@@ -179,7 +389,7 @@ function fixRgCommand(command) {
179
389
  }
180
390
  // Simple heuristic: if last token starts with -, add .
181
391
  // This handles cases like: rg -l pattern --type ts --type js
182
- const tokens = trimmed.split(/\\s+/);
392
+ const tokens = trimmed.split(/\s+/);
183
393
  const lastToken = tokens[tokens.length - 1];
184
394
  if (lastToken?.startsWith("-")) {
185
395
  // Command ends with an option, need to add path
@@ -214,3 +424,15 @@ function fixRgCommand(command) {
214
424
  logger.debug(`Adding '.' to rg command: ${command}`);
215
425
  return `${command} .`;
216
426
  }
427
+ /**
428
+ * Strips trailing '&' from command and logs a warning.
429
+ * Returns the processed command.
430
+ */
431
+ function stripTrailingAmpersand(command, isBackground) {
432
+ let processedCommand = command.trim();
433
+ if (processedCommand.endsWith("&")) {
434
+ logger.warn(`Stripping '&' from command since background=${String(isBackground)}: ${command}`);
435
+ processedCommand = processedCommand.slice(0, -1).trim();
436
+ }
437
+ return processedCommand;
438
+ }