@travisennis/acai 0.0.1 → 0.0.3

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 (324) hide show
  1. package/README.md +3 -4
  2. package/dist/commands/health-command.d.ts +2 -0
  3. package/dist/commands/health-command.js +59 -0
  4. package/dist/commands/manager.js +2 -0
  5. package/dist/commands/paste-command.d.ts +1 -1
  6. package/dist/commands/paste-command.js +155 -11
  7. package/dist/commands/reset-command.js +1 -0
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.js +2 -1
  10. package/dist/models/openrouter-provider.d.ts +4 -1
  11. package/dist/models/openrouter-provider.js +46 -4
  12. package/dist/models/providers.d.ts +1 -1
  13. package/dist/prompts/manager.d.ts +1 -0
  14. package/dist/prompts/manager.js +10 -0
  15. package/dist/prompts.js +8 -6
  16. package/dist/repl.js +49 -26
  17. package/dist/terminal/formatting.d.ts +16 -5
  18. package/dist/terminal/formatting.js +40 -6
  19. package/dist/terminal/index.d.ts +1 -1
  20. package/dist/terminal/index.js +54 -14
  21. package/dist/terminal/markdown.js +0 -1
  22. package/dist/terminal/supports-color.d.ts +16 -0
  23. package/dist/terminal/supports-color.js +121 -0
  24. package/dist/terminal/supports-hyperlinks.d.ts +7 -0
  25. package/dist/terminal/supports-hyperlinks.js +98 -0
  26. package/dist/tools/bash.js +95 -117
  27. package/dist/tools/code-interpreter.js +11 -1
  28. package/dist/tools/command-validation.d.ts +7 -3
  29. package/dist/tools/command-validation.js +67 -23
  30. package/dist/tools/delete-file.d.ts +4 -1
  31. package/dist/tools/delete-file.js +47 -3
  32. package/dist/tools/git-utils.d.ts +6 -0
  33. package/dist/tools/git-utils.js +89 -12
  34. package/dist/tools/grep.d.ts +20 -0
  35. package/dist/tools/grep.js +128 -40
  36. package/dist/tools/index.d.ts +2 -18
  37. package/dist/tools/index.js +4 -18
  38. package/package.json +30 -20
  39. package/.acai/acai.json +0 -9
  40. package/.acai/prompts/add-openrouter-model.md +0 -13
  41. package/.acai/prompts/project-status.md +0 -4
  42. package/.acai/prompts/update-architecture-document.md +0 -9
  43. package/.acai/rules/learned-rules.md +0 -9
  44. package/.ai/docs/available-tools.txt +0 -3
  45. package/.ai/docs/cognitive_complexity_refactoring_progress.md +0 -65
  46. package/.ai/docs/deleted_tools.md +0 -168
  47. package/.ai/docs/deleted_tools_88ced9ef.md +0 -56
  48. package/.ai/docs/image-pasting.md +0 -46
  49. package/.ai/docs/initialize-app.md +0 -117
  50. package/.ai/docs/issue-4-plan.md +0 -44
  51. package/.ai/docs/marked-renderer-debug.md +0 -15
  52. package/.ai/docs/marked-renderer-refactor-plan.md +0 -64
  53. package/.ai/docs/memory-use-cases.md +0 -55
  54. package/.ai/docs/prompt-consistency.md +0 -31
  55. package/.ai/docs/refactoring-tools.md +0 -98
  56. package/.ai/docs/system-prompt-update.md +0 -174
  57. package/.ai/docs/system_prompt.txt +0 -210
  58. package/.ai/docs/tasks.md +0 -49
  59. package/.ai/plan.md +0 -131
  60. package/.ai/prompt.md +0 -1
  61. package/.ai/scripts/fetch_models.js +0 -27
  62. package/.ai/scripts/generateSystemPrompt.ts +0 -15
  63. package/.ai/scripts/list-tools.mjs +0 -4
  64. package/.ai/scripts/p5_geometric_shapes.js +0 -149
  65. package/.husky/commit-msg +0 -1
  66. package/.husky/pre-commit +0 -3
  67. package/.husky/pre-push +0 -1
  68. package/.ignore +0 -4
  69. package/AGENTS.md +0 -25
  70. package/ARCHITECTURE.md +0 -304
  71. package/TODO.md +0 -2
  72. package/biome.json +0 -61
  73. package/commitlint.config.js +0 -3
  74. package/dist/source/cli.d.ts +0 -19
  75. package/dist/source/cli.js +0 -116
  76. package/dist/source/commands/application-log-command.d.ts +0 -2
  77. package/dist/source/commands/application-log-command.js +0 -43
  78. package/dist/source/commands/clear-command.d.ts +0 -2
  79. package/dist/source/commands/clear-command.js +0 -12
  80. package/dist/source/commands/compact-command.d.ts +0 -2
  81. package/dist/source/commands/compact-command.js +0 -51
  82. package/dist/source/commands/copy-command.d.ts +0 -2
  83. package/dist/source/commands/copy-command.js +0 -51
  84. package/dist/source/commands/edit-command.d.ts +0 -2
  85. package/dist/source/commands/edit-command.js +0 -53
  86. package/dist/source/commands/edit-prompt-command.d.ts +0 -2
  87. package/dist/source/commands/edit-prompt-command.js +0 -25
  88. package/dist/source/commands/exit-command.d.ts +0 -2
  89. package/dist/source/commands/exit-command.js +0 -14
  90. package/dist/source/commands/files-command.d.ts +0 -2
  91. package/dist/source/commands/files-command.js +0 -63
  92. package/dist/source/commands/generate-rules-command.d.ts +0 -2
  93. package/dist/source/commands/generate-rules-command.js +0 -61
  94. package/dist/source/commands/help-command.d.ts +0 -2
  95. package/dist/source/commands/help-command.js +0 -19
  96. package/dist/source/commands/init-command.d.ts +0 -2
  97. package/dist/source/commands/init-command.js +0 -40
  98. package/dist/source/commands/last-log-command.d.ts +0 -2
  99. package/dist/source/commands/last-log-command.js +0 -76
  100. package/dist/source/commands/manager.d.ts +0 -22
  101. package/dist/source/commands/manager.js +0 -123
  102. package/dist/source/commands/model-command.d.ts +0 -2
  103. package/dist/source/commands/model-command.js +0 -84
  104. package/dist/source/commands/paste-command.d.ts +0 -2
  105. package/dist/source/commands/paste-command.js +0 -40
  106. package/dist/source/commands/prompt-command.d.ts +0 -2
  107. package/dist/source/commands/prompt-command.js +0 -111
  108. package/dist/source/commands/reset-command.d.ts +0 -2
  109. package/dist/source/commands/reset-command.js +0 -16
  110. package/dist/source/commands/rules-command.d.ts +0 -2
  111. package/dist/source/commands/rules-command.js +0 -68
  112. package/dist/source/commands/save-command.d.ts +0 -2
  113. package/dist/source/commands/save-command.js +0 -14
  114. package/dist/source/commands/types.d.ts +0 -26
  115. package/dist/source/commands/types.js +0 -1
  116. package/dist/source/commands/usage-command.d.ts +0 -2
  117. package/dist/source/commands/usage-command.js +0 -21
  118. package/dist/source/config.d.ts +0 -60
  119. package/dist/source/config.js +0 -193
  120. package/dist/source/conversation-analyzer.d.ts +0 -10
  121. package/dist/source/conversation-analyzer.js +0 -88
  122. package/dist/source/dedent.d.ts +0 -3
  123. package/dist/source/dedent.js +0 -38
  124. package/dist/source/formatting.d.ts +0 -17
  125. package/dist/source/formatting.js +0 -103
  126. package/dist/source/index.d.ts +0 -18
  127. package/dist/source/index.js +0 -213
  128. package/dist/source/logger.d.ts +0 -2
  129. package/dist/source/logger.js +0 -24
  130. package/dist/source/mentions.d.ts +0 -9
  131. package/dist/source/mentions.js +0 -182
  132. package/dist/source/messages.d.ts +0 -69
  133. package/dist/source/messages.js +0 -261
  134. package/dist/source/middleware/audit-message.d.ts +0 -5
  135. package/dist/source/middleware/audit-message.js +0 -95
  136. package/dist/source/middleware/index.d.ts +0 -2
  137. package/dist/source/middleware/index.js +0 -2
  138. package/dist/source/middleware/rate-limit.d.ts +0 -4
  139. package/dist/source/middleware/rate-limit.js +0 -17
  140. package/dist/source/models/ai-config.d.ts +0 -12
  141. package/dist/source/models/ai-config.js +0 -87
  142. package/dist/source/models/anthropic-provider.d.ts +0 -25
  143. package/dist/source/models/anthropic-provider.js +0 -184
  144. package/dist/source/models/deepseek-provider.d.ts +0 -20
  145. package/dist/source/models/deepseek-provider.js +0 -42
  146. package/dist/source/models/google-provider.d.ts +0 -19
  147. package/dist/source/models/google-provider.js +0 -56
  148. package/dist/source/models/manager.d.ts +0 -15
  149. package/dist/source/models/manager.js +0 -48
  150. package/dist/source/models/openai-provider.d.ts +0 -22
  151. package/dist/source/models/openai-provider.js +0 -70
  152. package/dist/source/models/openrouter-provider.d.ts +0 -36
  153. package/dist/source/models/openrouter-provider.js +0 -276
  154. package/dist/source/models/providers.d.ts +0 -33
  155. package/dist/source/models/providers.js +0 -116
  156. package/dist/source/models/xai-provider.d.ts +0 -20
  157. package/dist/source/models/xai-provider.js +0 -47
  158. package/dist/source/parsing.d.ts +0 -2
  159. package/dist/source/parsing.js +0 -18
  160. package/dist/source/prompts/manager.d.ts +0 -19
  161. package/dist/source/prompts/manager.js +0 -71
  162. package/dist/source/prompts.d.ts +0 -4
  163. package/dist/source/prompts.js +0 -158
  164. package/dist/source/repl-prompt.d.ts +0 -14
  165. package/dist/source/repl-prompt.js +0 -147
  166. package/dist/source/repl.d.ts +0 -27
  167. package/dist/source/repl.js +0 -431
  168. package/dist/source/terminal/formatting.d.ts +0 -37
  169. package/dist/source/terminal/formatting.js +0 -106
  170. package/dist/source/terminal/index.d.ts +0 -94
  171. package/dist/source/terminal/index.js +0 -420
  172. package/dist/source/terminal/markdown-utils.d.ts +0 -2
  173. package/dist/source/terminal/markdown-utils.js +0 -81
  174. package/dist/source/terminal/markdown.d.ts +0 -1
  175. package/dist/source/terminal/markdown.js +0 -111
  176. package/dist/source/terminal/types.d.ts +0 -71
  177. package/dist/source/terminal/types.js +0 -1
  178. package/dist/source/terminal-output.d.ts +0 -8
  179. package/dist/source/terminal-output.js +0 -213
  180. package/dist/source/terminal-output.test.d.ts +0 -8
  181. package/dist/source/terminal-output.test.js +0 -213
  182. package/dist/source/token-tracker.d.ts +0 -14
  183. package/dist/source/token-tracker.js +0 -53
  184. package/dist/source/token-utils.d.ts +0 -7
  185. package/dist/source/token-utils.js +0 -13
  186. package/dist/source/tools/agent.d.ts +0 -17
  187. package/dist/source/tools/agent.js +0 -87
  188. package/dist/source/tools/bash.d.ts +0 -19
  189. package/dist/source/tools/bash.js +0 -294
  190. package/dist/source/tools/code-interpreter.d.ts +0 -12
  191. package/dist/source/tools/code-interpreter.js +0 -131
  192. package/dist/source/tools/command-validation.d.ts +0 -8
  193. package/dist/source/tools/command-validation.js +0 -69
  194. package/dist/source/tools/delete-file.d.ts +0 -12
  195. package/dist/source/tools/delete-file.js +0 -56
  196. package/dist/source/tools/directory-tree.d.ts +0 -12
  197. package/dist/source/tools/directory-tree.js +0 -38
  198. package/dist/source/tools/edit-file.d.ts +0 -19
  199. package/dist/source/tools/edit-file.js +0 -107
  200. package/dist/source/tools/filesystem-utils.d.ts +0 -22
  201. package/dist/source/tools/filesystem-utils.js +0 -191
  202. package/dist/source/tools/git-utils.d.ts +0 -14
  203. package/dist/source/tools/git-utils.js +0 -64
  204. package/dist/source/tools/grep.d.ts +0 -17
  205. package/dist/source/tools/grep.js +0 -138
  206. package/dist/source/tools/index.d.ts +0 -161
  207. package/dist/source/tools/index.js +0 -209
  208. package/dist/source/tools/memory-read.d.ts +0 -13
  209. package/dist/source/tools/memory-read.js +0 -135
  210. package/dist/source/tools/memory-write.d.ts +0 -12
  211. package/dist/source/tools/memory-write.js +0 -83
  212. package/dist/source/tools/move-file.d.ts +0 -13
  213. package/dist/source/tools/move-file.js +0 -44
  214. package/dist/source/tools/read-file.d.ts +0 -17
  215. package/dist/source/tools/read-file.js +0 -86
  216. package/dist/source/tools/read-multiple-files.d.ts +0 -14
  217. package/dist/source/tools/read-multiple-files.js +0 -55
  218. package/dist/source/tools/save-file.d.ts +0 -17
  219. package/dist/source/tools/save-file.js +0 -98
  220. package/dist/source/tools/think.d.ts +0 -11
  221. package/dist/source/tools/think.js +0 -45
  222. package/dist/source/tools/types.d.ts +0 -29
  223. package/dist/source/tools/types.js +0 -14
  224. package/dist/source/tools/web-fetch.d.ts +0 -47
  225. package/dist/source/tools/web-fetch.js +0 -246
  226. package/dist/source/tools/web-search.d.ts +0 -13
  227. package/dist/source/tools/web-search.js +0 -80
  228. package/dist/source/utils/process.d.ts +0 -36
  229. package/dist/source/utils/process.js +0 -75
  230. package/dist/source/version.d.ts +0 -1
  231. package/dist/source/version.js +0 -21
  232. package/dist/terminal-output.d.ts +0 -8
  233. package/dist/terminal-output.js +0 -213
  234. package/dist/tools/memory-read.d.ts +0 -13
  235. package/dist/tools/memory-read.js +0 -135
  236. package/dist/tools/memory-write.d.ts +0 -12
  237. package/dist/tools/memory-write.js +0 -83
  238. package/knip.json +0 -5
  239. package/source/cli.ts +0 -172
  240. package/source/commands/application-log-command.ts +0 -53
  241. package/source/commands/clear-command.ts +0 -14
  242. package/source/commands/compact-command.ts +0 -64
  243. package/source/commands/copy-command.ts +0 -55
  244. package/source/commands/edit-command.ts +0 -63
  245. package/source/commands/edit-prompt-command.ts +0 -31
  246. package/source/commands/exit-command.ts +0 -18
  247. package/source/commands/files-command.ts +0 -85
  248. package/source/commands/generate-rules-command.ts +0 -82
  249. package/source/commands/help-command.ts +0 -27
  250. package/source/commands/init-command.ts +0 -48
  251. package/source/commands/last-log-command.ts +0 -88
  252. package/source/commands/manager.ts +0 -151
  253. package/source/commands/model-command.ts +0 -123
  254. package/source/commands/paste-command.ts +0 -62
  255. package/source/commands/prompt-command.ts +0 -150
  256. package/source/commands/reset-command.ts +0 -22
  257. package/source/commands/rules-command.ts +0 -76
  258. package/source/commands/save-command.ts +0 -20
  259. package/source/commands/types.ts +0 -28
  260. package/source/commands/usage-command.ts +0 -26
  261. package/source/config.ts +0 -223
  262. package/source/conversation-analyzer.ts +0 -115
  263. package/source/dedent.ts +0 -53
  264. package/source/formatting.ts +0 -132
  265. package/source/index.ts +0 -240
  266. package/source/logger.ts +0 -29
  267. package/source/mentions.ts +0 -227
  268. package/source/messages.ts +0 -360
  269. package/source/middleware/audit-message.ts +0 -133
  270. package/source/middleware/index.ts +0 -2
  271. package/source/middleware/rate-limit.ts +0 -24
  272. package/source/models/ai-config.ts +0 -109
  273. package/source/models/anthropic-provider.ts +0 -199
  274. package/source/models/deepseek-provider.ts +0 -53
  275. package/source/models/google-provider.ts +0 -68
  276. package/source/models/manager.ts +0 -84
  277. package/source/models/openai-provider.ts +0 -81
  278. package/source/models/openrouter-provider.ts +0 -288
  279. package/source/models/providers.ts +0 -197
  280. package/source/models/xai-provider.ts +0 -59
  281. package/source/parsing.ts +0 -20
  282. package/source/prompts/manager.ts +0 -90
  283. package/source/prompts.ts +0 -172
  284. package/source/repl-prompt.ts +0 -196
  285. package/source/repl.ts +0 -572
  286. package/source/terminal/formatting.ts +0 -121
  287. package/source/terminal/index.ts +0 -518
  288. package/source/terminal/markdown-utils.ts +0 -89
  289. package/source/terminal/markdown.ts +0 -155
  290. package/source/terminal/types.ts +0 -84
  291. package/source/terminal-output.test.ts +0 -266
  292. package/source/token-tracker.ts +0 -78
  293. package/source/token-utils.ts +0 -17
  294. package/source/tools/agent.ts +0 -107
  295. package/source/tools/bash.ts +0 -367
  296. package/source/tools/code-interpreter.ts +0 -172
  297. package/source/tools/command-validation.ts +0 -81
  298. package/source/tools/delete-file.ts +0 -71
  299. package/source/tools/directory-tree.ts +0 -54
  300. package/source/tools/edit-file.ts +0 -155
  301. package/source/tools/filesystem-utils.ts +0 -265
  302. package/source/tools/git-utils.ts +0 -70
  303. package/source/tools/grep.ts +0 -184
  304. package/source/tools/index.ts +0 -278
  305. package/source/tools/memory-read.ts +0 -174
  306. package/source/tools/memory-write.ts +0 -105
  307. package/source/tools/move-file.ts +0 -59
  308. package/source/tools/read-file.ts +0 -129
  309. package/source/tools/read-multiple-files.ts +0 -80
  310. package/source/tools/save-file.ts +0 -147
  311. package/source/tools/think.ts +0 -51
  312. package/source/tools/types.ts +0 -58
  313. package/source/tools/web-fetch.ts +0 -327
  314. package/source/tools/web-search.ts +0 -101
  315. package/source/utils/process.ts +0 -121
  316. package/source/version.ts +0 -21
  317. package/test/commands/copy-command.test.ts +0 -69
  318. package/test/config.test.ts +0 -200
  319. package/test/terminal/markdown-utils.test.ts +0 -124
  320. package/test/tools/bash-tool.test.ts +0 -58
  321. package/test/tools/code-interpreter.test.ts +0 -91
  322. package/test/tools/command-validation.test.ts +0 -48
  323. package/tsconfig.build.json +0 -9
  324. package/tsconfig.json +0 -30
@@ -1,367 +0,0 @@
1
- import path from "node:path";
2
- import { input, select } from "@inquirer/prompts";
3
- import { tool } from "ai";
4
- import chalk from "chalk";
5
- import { z } from "zod";
6
- import { config } from "../config.ts";
7
- import type { Terminal } from "../terminal/index.ts";
8
- import type { TokenCounter } from "../token-utils.ts";
9
- import { executeCommand } from "../utils/process.ts";
10
- import { CommandValidation } from "./command-validation.ts";
11
- import type { SendData } from "./types.ts";
12
-
13
- export const BashTool = {
14
- name: "bash" as const,
15
- };
16
-
17
- type Token = { raw: string; unquoted: string };
18
-
19
- function tokenize(inputStr: string): Token[] {
20
- const tokens: Token[] = [];
21
- let current = "";
22
- let inSingle = false;
23
- let inDouble = false;
24
- for (let i = 0; i < inputStr.length; i++) {
25
- const ch: string = inputStr[i] as string;
26
- if (ch === "'" && !inDouble) {
27
- inSingle = !inSingle;
28
- current += ch;
29
- continue;
30
- }
31
- if (ch === '"' && !inSingle) {
32
- inDouble = !inDouble;
33
- current += ch;
34
- continue;
35
- }
36
- if (!inSingle && !inDouble && /\s/.test(ch)) {
37
- if (current.length > 0) {
38
- const raw = current;
39
- const unquoted = raw.replace(/^['"]|['"]$/g, "");
40
- tokens.push({ raw, unquoted });
41
- current = "";
42
- }
43
- continue;
44
- }
45
- if (ch === "\\" && inDouble && i + 1 < inputStr.length) {
46
- const next = inputStr[i + 1];
47
- if (next === '"' || next === "\\") {
48
- current += next;
49
- i++;
50
- continue;
51
- }
52
- }
53
- current += ch;
54
- }
55
- if (current.length > 0) {
56
- const raw = current;
57
- const unquoted = raw.replace(/^['"]|['"]$/g, "");
58
- tokens.push({ raw, unquoted });
59
- }
60
- return tokens;
61
- }
62
-
63
- function shouldSkipPathValidation(tokens: Token[], index: number): boolean {
64
- if (index === 0) return false;
65
- const cmd = tokens[0]?.unquoted;
66
- if (cmd !== "git") return false;
67
- const sub = tokens[1]?.unquoted;
68
- if (
69
- sub !== "commit" &&
70
- sub !== "tag" &&
71
- !(sub === "notes" && tokens[2]?.unquoted === "add")
72
- ) {
73
- return false;
74
- }
75
- const prev = tokens[index - 1]?.unquoted;
76
- if (prev === "-m" || prev === "--message") return true;
77
- return false;
78
- }
79
-
80
- function looksLikeUrl(str: string): boolean {
81
- return str.startsWith("http://") || str.startsWith("https://");
82
- }
83
-
84
- // Whitelist of allowed commands
85
- const ALLOWED_COMMANDS = [
86
- "chmod",
87
- "ls",
88
- "pwd",
89
- "cat",
90
- "grep",
91
- "find",
92
- "echo",
93
- "mkdir",
94
- "touch",
95
- "cp",
96
- "mv",
97
- "pwd",
98
- "wc",
99
- "diff",
100
- "sort",
101
- "head",
102
- "tail",
103
- "sleep",
104
- "npm",
105
- "npx",
106
- "node",
107
- "git",
108
- "gh",
109
- "rg",
110
- "jq",
111
- "sed",
112
- "awk",
113
- ];
114
-
115
- // Command execution timeout in milliseconds
116
- const DEFAULT_TIMEOUT = 1.5 * 60 * 1000; // 1.5 minutes
117
-
118
- // Initialize command validator with allowed commands
119
- const commandValidator = new CommandValidation(ALLOWED_COMMANDS);
120
-
121
- // Ensure path is within base directory
122
- function isPathWithinBaseDir(requestedPath: string, baseDir: string): boolean {
123
- const normalizedRequestedPath = path.normalize(requestedPath);
124
- const normalizedBaseDir = path.normalize(baseDir);
125
-
126
- return normalizedRequestedPath.startsWith(normalizedBaseDir);
127
- }
128
-
129
- export const createBashTool = ({
130
- baseDir,
131
- sendData,
132
- tokenCounter,
133
- terminal,
134
- autoAcceptAll,
135
- }: {
136
- baseDir: string;
137
- sendData?: SendData | undefined;
138
- tokenCounter: TokenCounter;
139
- terminal?: Terminal;
140
- autoAcceptAll: boolean;
141
- }) => {
142
- let autoAcceptCommands = autoAcceptAll;
143
- return {
144
- [BashTool.name]: tool({
145
- description: `Execute bash commands and return their output. Limited to a whitelist of safe commands: ${ALLOWED_COMMANDS.join(", ")}. Commands will only execute within the project directory for security. Always specify absolute paths to avoid errors.`,
146
- inputSchema: z.object({
147
- command: z
148
- .string()
149
- .describe(
150
- "Full CLI command to execute. Must be from the allowed list without chaining operators.",
151
- ),
152
- cwd: z
153
- .string()
154
- .nullable()
155
- .describe(
156
- "Working directory (default: project root). Must be within the project directory.",
157
- ),
158
- timeout: z
159
- .number()
160
- .nullable()
161
- .describe(
162
- `Command execution timeout in milliseconds. Default: ${DEFAULT_TIMEOUT}ms`,
163
- ),
164
- }),
165
- execute: async ({ command, cwd, timeout }, { toolCallId }) => {
166
- // Guard against null cwd and timeout
167
- const safeCwd = cwd == null ? baseDir : cwd;
168
- const safeTimeout = timeout == null ? DEFAULT_TIMEOUT : timeout;
169
-
170
- sendData?.({
171
- event: "tool-init",
172
- id: toolCallId,
173
- data: `Executing: ${chalk.cyan(command)} in ${chalk.cyan(safeCwd)}`,
174
- });
175
-
176
- // Validate command using CommandValidation
177
- if (!commandValidator.isValid(command)) {
178
- const errorMsg = `Command not allowed. Ensure all sub-commands are in the approved list: ${ALLOWED_COMMANDS.join(", ")} and no unsafe operators (>, <, \`, $()) are used.`;
179
- sendData?.({ event: "tool-error", id: toolCallId, data: errorMsg });
180
- return errorMsg;
181
- }
182
-
183
- // Validate working directory
184
- if (!isPathWithinBaseDir(safeCwd, baseDir)) {
185
- const errorMsg = `Working directory must be within the project directory: ${baseDir}`;
186
- sendData?.({ event: "tool-error", id: toolCallId, data: errorMsg });
187
- return errorMsg;
188
- }
189
-
190
- // Validate command arguments for paths outside baseDir
191
- const tokens = tokenize(command);
192
- for (let i = 0; i < tokens.length; i++) {
193
- const token = tokens[i];
194
- if (token == null) continue;
195
- const part = token.unquoted;
196
-
197
- if (shouldSkipPathValidation(tokens, i)) {
198
- continue;
199
- }
200
-
201
- if (looksLikeUrl(part)) {
202
- continue;
203
- }
204
-
205
- if (
206
- part.startsWith("/") ||
207
- part.includes("../") ||
208
- part.includes("./") ||
209
- (part.includes("/") && !part.startsWith("-"))
210
- ) {
211
- try {
212
- const resolvedPath = path.resolve(safeCwd, part);
213
- if (!isPathWithinBaseDir(resolvedPath, baseDir)) {
214
- const errorMsg = `Command argument references path outside the project directory: ${part} (resolved to ${resolvedPath})`;
215
- sendData?.({
216
- event: "tool-error",
217
- id: toolCallId,
218
- data: errorMsg,
219
- });
220
- return errorMsg;
221
- }
222
- } catch (e) {
223
- console.info(
224
- `Could not resolve potential path argument: ${part}`,
225
- e as unknown as string,
226
- );
227
- }
228
- }
229
- }
230
-
231
- // Prompt user for command execution approval (only in interactive mode)
232
- if (terminal) {
233
- if (!autoAcceptCommands) {
234
- await new Promise((resolve) => setTimeout(resolve, 1000));
235
- }
236
- terminal.lineBreak();
237
- terminal.writeln(
238
- `${chalk.blue.bold("●")} About to execute command: ${chalk.cyan(command)}`,
239
- );
240
- terminal.writeln(`${chalk.gray("Working directory:")} ${safeCwd}`);
241
- terminal.lineBreak();
242
-
243
- let userChoice: string;
244
- if (autoAcceptCommands) {
245
- terminal.writeln(
246
- chalk.green(
247
- "✓ Auto-accepting command (all future commands will be accepted)",
248
- ),
249
- );
250
- userChoice = "accept";
251
- } else {
252
- userChoice = await select({
253
- message: "What would you like to do with this command?",
254
- choices: [
255
- { name: "Execute this command", value: "accept" },
256
- {
257
- name: "Execute all future commands (including this)",
258
- value: "accept-all",
259
- },
260
- { name: "Reject this command", value: "reject" },
261
- ],
262
- default: "accept",
263
- });
264
- }
265
-
266
- terminal.lineBreak();
267
-
268
- if (userChoice === "accept-all") {
269
- autoAcceptCommands = true;
270
- terminal.writeln(
271
- chalk.yellow(
272
- "✓ Auto-accept mode enabled for all future commands",
273
- ),
274
- );
275
- terminal.lineBreak();
276
- }
277
-
278
- if (userChoice === "reject") {
279
- const reason = await input({ message: "Feedback: " });
280
- terminal.lineBreak();
281
-
282
- const rejectionMsg = `Command rejected by user. Reason: ${reason}`;
283
- sendData?.({
284
- event: "tool-completion",
285
- id: toolCallId,
286
- data: rejectionMsg,
287
- });
288
- return rejectionMsg;
289
- }
290
- }
291
-
292
- try {
293
- const result = await executeCommand(command, {
294
- cwd: safeCwd,
295
- timeout: safeTimeout,
296
- shell: true,
297
- throwOnError: false,
298
- });
299
-
300
- if (result.signal === "SIGTERM") {
301
- const timeoutMessage = `Command timed out after ${safeTimeout}ms. This might be because the command is waiting for input.`;
302
- sendData?.({
303
- event: "tool-error",
304
- id: toolCallId,
305
- data: timeoutMessage,
306
- });
307
- return timeoutMessage;
308
- }
309
-
310
- const formattedResult = format(result);
311
-
312
- sendData?.({
313
- event: "tool-update",
314
- id: toolCallId,
315
- data: {
316
- primary: "Result",
317
- secondary: formattedResult.split("\n").slice(-5),
318
- },
319
- });
320
-
321
- let tokenCount = 0;
322
- try {
323
- tokenCount = tokenCounter.count(formattedResult);
324
- } catch (tokenError) {
325
- console.error("Error calculating token count:", tokenError);
326
- // Log or handle error, but don't block file return
327
- }
328
-
329
- const maxTokens = (await config.readProjectConfig()).tools.maxTokens;
330
- // Adjust max token check message if line selection was used
331
- const maxTokenMessage = `Output of commmand (${tokenCount} tokens) exceeds maximum allowed tokens (${maxTokens}). Please adjust how you call the command to get back more specific results`;
332
-
333
- const finalResult =
334
- tokenCount <= maxTokens ? formattedResult : maxTokenMessage;
335
-
336
- sendData?.({
337
- event: "tool-completion",
338
- id: toolCallId,
339
- data:
340
- tokenCount <= maxTokens
341
- ? "Command executed successfully."
342
- : `Output of commmand (${tokenCount} tokens) exceeds maximum allowed tokens (${maxTokens}).`,
343
- });
344
- return finalResult;
345
- } catch (error) {
346
- sendData?.({
347
- event: "tool-error",
348
- id: toolCallId,
349
- data: `Command failed: ${(error as Error).message}`,
350
- });
351
- return `Command failed: ${(error as Error).message}`;
352
- }
353
- },
354
- }),
355
- };
356
- };
357
-
358
- function format({
359
- stdout,
360
- stderr,
361
- }: {
362
- stdout: string;
363
- stderr: string;
364
- code: number;
365
- }) {
366
- return `${stdout}\n${stderr}`;
367
- }
@@ -1,172 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- import { randomUUID } from "node:crypto";
3
- import { mkdir, rm, writeFile } from "node:fs/promises";
4
- import { join } from "node:path";
5
- import process from "node:process";
6
- import { tool } from "ai";
7
- import { z } from "zod";
8
- import type { SendData } from "./types.ts";
9
-
10
- export const CodeInterpreterTool = {
11
- name: "codeInterpreter" as const,
12
- };
13
-
14
- export const createCodeInterpreterTool = ({
15
- sendData,
16
- }: Readonly<{
17
- sendData?: SendData | undefined;
18
- }>) => {
19
- return {
20
- [CodeInterpreterTool.name]: tool({
21
- description:
22
- "Executes JavaScript code in a separate Node.js process using Node's Permission Model. By default, the child process has no permissions except read/write within the current working directory. The tool returns stdout, stderr, and exitCode. Use console.log/console.error to produce output. Timeout defaults to 5 seconds and can be extended up to 60 seconds.",
23
- inputSchema: z.object({
24
- code: z.string().describe("JavaScript code to be executed."),
25
- timeoutSeconds: z
26
- .number()
27
- .int()
28
- .min(1)
29
- .max(60)
30
- .nullable()
31
- .describe("Execution timeout in seconds (1-60). Default 5."),
32
- }),
33
- execute: async ({ code, timeoutSeconds }, { toolCallId }) => {
34
- const workingDirectory = process.cwd();
35
-
36
- try {
37
- sendData?.({
38
- event: "tool-init",
39
- id: toolCallId,
40
- data: "Initializing code interpreter environment",
41
- });
42
-
43
- sendData?.({
44
- event: "tool-update",
45
- id: toolCallId,
46
- data: {
47
- primary: "Executing...",
48
- secondary: [
49
- `${"`".repeat(3)} javascript\n${code.slice(0, 500)}${"`".repeat(3)}`,
50
- ],
51
- },
52
- });
53
-
54
- if (code.trim().length === 0) {
55
- throw new Error("No code provided");
56
- }
57
-
58
- const timeoutMs = Math.min(
59
- Math.max((timeoutSeconds ?? 5) * 1000, 1000),
60
- 60000,
61
- );
62
-
63
- const tmpBase = join(workingDirectory, ".acai-ci-tmp");
64
- await mkdir(tmpBase, { recursive: true });
65
- const scriptPath = join(
66
- tmpBase,
67
- `temp_script_${Date.now()}_${randomUUID()}.mjs`,
68
- );
69
-
70
- await writeFile(scriptPath, code, { encoding: "utf8" });
71
-
72
- const args = [
73
- "--permission",
74
- `--allow-fs-read=${workingDirectory}`,
75
- `--allow-fs-write=${workingDirectory}`,
76
- scriptPath,
77
- ];
78
-
79
- const child = spawn(process.execPath, args, {
80
- cwd: workingDirectory,
81
- // do not rely solely on spawn's timeout; we implement manual timeout below
82
- stdio: "pipe",
83
- env: Object.assign({}, process.env, {
84
- // biome-ignore lint/style/useNamingConvention: Environment variable keys are uppercase by convention
85
- NO_COLOR: "true",
86
- // biome-ignore lint/style/useNamingConvention: Environment variable keys are uppercase by convention
87
- NODE_OPTIONS: "",
88
- } as Record<string, string>),
89
- });
90
-
91
- let stdout = "";
92
- let stderr = "";
93
- let timedOut = false;
94
-
95
- const timer = setTimeout(() => {
96
- timedOut = true;
97
- try {
98
- child.kill("SIGKILL");
99
- } catch {}
100
- }, timeoutMs);
101
-
102
- child.stdout.setEncoding("utf8");
103
- child.stderr.setEncoding("utf8");
104
-
105
- child.stdout.on("data", (chunk) => {
106
- stdout += String(chunk);
107
- });
108
- child.stderr.on("data", (chunk) => {
109
- stderr += String(chunk);
110
- });
111
-
112
- const completed = await new Promise<{
113
- code: number | null;
114
- signal: NodeJS.Signals | null;
115
- }>((resolve, reject) => {
116
- child.on("error", (err) => reject(err));
117
- child.on("close", (code, signal) => resolve({ code, signal }));
118
- });
119
-
120
- clearTimeout(timer);
121
-
122
- // Cleanup temp file/directory
123
- await rm(scriptPath, { force: true });
124
- await rm(tmpBase, { force: true, recursive: true });
125
-
126
- if (timedOut) {
127
- throw new Error("Script timed out");
128
- }
129
-
130
- if (completed.code === null) {
131
- throw new Error(
132
- `Process terminated by signal ${completed.signal ?? "unknown"}`,
133
- );
134
- }
135
-
136
- if (completed.code !== 0) {
137
- const message = `Process exited with code ${completed.code}. Stderr: ${stderr.trim()}`;
138
- throw new Error(message);
139
- }
140
-
141
- const result = {
142
- stdout: stdout.trim(),
143
- stderr: stderr.trim(),
144
- exitCode: completed.code ?? -1,
145
- };
146
-
147
- sendData?.({
148
- event: "tool-completion",
149
- id: toolCallId,
150
- data: "Code execution completed successfully",
151
- });
152
-
153
- return JSON.stringify(result, null, 2);
154
- } catch (err) {
155
- const errorMessage =
156
- (err as Error).name === "ETIMEDOUT" ||
157
- (err as Error).message.includes("timed out")
158
- ? "Script timed out"
159
- : `Error: ${(err as Error).message}`;
160
-
161
- sendData?.({
162
- event: "tool-error",
163
- id: toolCallId,
164
- data: errorMessage,
165
- });
166
-
167
- return errorMessage;
168
- }
169
- },
170
- }),
171
- };
172
- };
@@ -1,81 +0,0 @@
1
- export class CommandValidation {
2
- private readonly allowedCommands: string[];
3
- private readonly unsafeOperatorPatterns: RegExp[];
4
-
5
- constructor(allowedCommands: string[]) {
6
- this.allowedCommands = allowedCommands;
7
- this.unsafeOperatorPatterns = [
8
- /`/, // backticks
9
- /\$\(/, // $(
10
- />/, // redirect out
11
- /</, // redirect in
12
- ];
13
- }
14
-
15
- private isCommandAllowed(command: string): boolean {
16
- const baseCommand = command.split(" ")[0] || "";
17
- return this.allowedCommands.includes(baseCommand);
18
- }
19
-
20
- private hasUnsafeOperators(command: string): boolean {
21
- // Remove all quoted segments first
22
- const stripped = command
23
- .replace(/'([^'\\]|\\.)*'/g, "")
24
- .replace(/"([^"\\]|\\.)*"/g, "");
25
-
26
- // Check for unsafe operators only in unquoted portions
27
- return this.unsafeOperatorPatterns.some((re) => re.test(stripped));
28
- }
29
-
30
- isValid(command: string): boolean {
31
- if (!command.trim()) return false;
32
-
33
- // First check for unsafe operators in unquoted portions
34
- if (this.hasUnsafeOperators(command)) {
35
- return false;
36
- }
37
-
38
- // Process command while preserving quoted strings
39
- const subCommands: string[] = [];
40
- let currentSegment = "";
41
- let inSingleQuote = false;
42
- let inDoubleQuote = false;
43
-
44
- for (let i = 0; i < command.length; i++) {
45
- const char = command[i];
46
-
47
- // Handle quote states
48
- if (char === "'" && !inDoubleQuote) inSingleQuote = !inSingleQuote;
49
- if (char === '"' && !inSingleQuote) inDoubleQuote = !inDoubleQuote;
50
-
51
- // Split on operators only when not in quotes
52
- if (
53
- !inSingleQuote &&
54
- !inDoubleQuote &&
55
- (char === "&" || char === "|" || char === ";")
56
- ) {
57
- if (currentSegment.trim()) {
58
- subCommands.push(currentSegment.trim());
59
- currentSegment = "";
60
- }
61
- // Skip the operator and any subsequent same operators (like && or ||)
62
- while (
63
- i + 1 < command.length &&
64
- ["&", "|", ";"].includes(command[i + 1] ?? "")
65
- ) {
66
- i++;
67
- }
68
- } else {
69
- currentSegment += char;
70
- }
71
- }
72
-
73
- // Add the last segment
74
- if (currentSegment.trim()) {
75
- subCommands.push(currentSegment.trim());
76
- }
77
-
78
- // Validate all sub-commands
79
- return subCommands.every((cmd) => this.isCommandAllowed(cmd));
80
- }
81
- }
@@ -1,71 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import fs from "node:fs/promises";
3
- import { tool } from "ai";
4
- import chalk from "chalk";
5
- import { z } from "zod";
6
- import { joinWorkingDir, validatePath } from "./filesystem-utils.ts";
7
- import type { SendData } from "./types.ts";
8
-
9
- export const DeleteFileTool = {
10
- name: "deleteFile" as const,
11
- };
12
-
13
- export const createDeleteFileTool = async ({
14
- workingDir,
15
- sendData,
16
- }: {
17
- workingDir: string;
18
- sendData?: SendData;
19
- }) => {
20
- const allowedDirectory = workingDir;
21
- return {
22
- [DeleteFileTool.name]: tool({
23
- description: "Delete a file permanently.",
24
- inputSchema: z.object({
25
- path: z.string().describe("Absolute path to the file to delete"),
26
- }),
27
- execute: async ({ path: userPath }, { toolCallId }) => {
28
- sendData?.({
29
- id: toolCallId,
30
- event: "tool-init",
31
- data: `Deleting file: ${chalk.cyan(userPath)}`,
32
- });
33
- try {
34
- const filePath = await validatePath(
35
- joinWorkingDir(userPath, workingDir),
36
- allowedDirectory,
37
- );
38
-
39
- // Check if file exists before attempting delete
40
- if (!existsSync(filePath)) {
41
- throw new Error(`File not found: ${filePath}`);
42
- }
43
-
44
- // Ensure it's a file, not a directory
45
- const stats = await fs.stat(filePath);
46
- if (stats.isDirectory()) {
47
- throw new Error(`Path is a directory, not a file: ${filePath}`);
48
- }
49
-
50
- // Delete the original file
51
- await fs.unlink(filePath);
52
-
53
- sendData?.({
54
- id: toolCallId,
55
- event: "tool-completion",
56
- data: `File deleted successfully: ${userPath}`,
57
- });
58
- return `Successfully deleted ${filePath}`;
59
- } catch (error) {
60
- const errorMessage = `Failed to delete file: ${(error as Error).message}`;
61
- sendData?.({
62
- id: toolCallId,
63
- event: "tool-error",
64
- data: errorMessage,
65
- });
66
- return errorMessage;
67
- }
68
- },
69
- }),
70
- };
71
- };