@travisennis/acai 0.0.4 → 0.0.6

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 (433) hide show
  1. package/README.md +229 -8
  2. package/dist/agent/index.d.ts +119 -0
  3. package/dist/agent/index.d.ts.map +1 -0
  4. package/dist/agent/index.js +406 -0
  5. package/dist/agent/manual-loop.d.ts +41 -0
  6. package/dist/agent/manual-loop.d.ts.map +1 -0
  7. package/dist/agent/manual-loop.js +278 -0
  8. package/dist/api/exa/index.d.ts +177 -0
  9. package/dist/api/exa/index.d.ts.map +1 -0
  10. package/dist/api/exa/index.js +439 -0
  11. package/dist/cli.d.ts +5 -2
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +27 -33
  14. package/dist/commands/add-directory-command.d.ts +3 -0
  15. package/dist/commands/add-directory-command.d.ts.map +1 -0
  16. package/dist/commands/add-directory-command.js +85 -0
  17. package/dist/commands/application-log-command.d.ts +1 -0
  18. package/dist/commands/application-log-command.d.ts.map +1 -0
  19. package/dist/commands/application-log-command.js +39 -3
  20. package/dist/commands/clear-command.d.ts +1 -0
  21. package/dist/commands/clear-command.d.ts.map +1 -0
  22. package/dist/commands/clear-command.js +10 -3
  23. package/dist/commands/compact-command.d.ts +1 -0
  24. package/dist/commands/compact-command.d.ts.map +1 -0
  25. package/dist/commands/compact-command.js +16 -3
  26. package/dist/commands/context-command.d.ts +3 -0
  27. package/dist/commands/context-command.d.ts.map +1 -0
  28. package/dist/commands/context-command.js +183 -0
  29. package/dist/commands/copy-command.d.ts +1 -0
  30. package/dist/commands/copy-command.d.ts.map +1 -0
  31. package/dist/commands/copy-command.js +31 -2
  32. package/dist/commands/edit-command.d.ts +1 -0
  33. package/dist/commands/edit-command.d.ts.map +1 -0
  34. package/dist/commands/edit-command.js +40 -5
  35. package/dist/commands/edit-prompt-command.d.ts +2 -1
  36. package/dist/commands/edit-prompt-command.d.ts.map +1 -0
  37. package/dist/commands/edit-prompt-command.js +43 -7
  38. package/dist/commands/exit-command.d.ts +13 -2
  39. package/dist/commands/exit-command.d.ts.map +1 -0
  40. package/dist/commands/exit-command.js +34 -2
  41. package/dist/commands/files-command.d.ts +1 -0
  42. package/dist/commands/files-command.d.ts.map +1 -0
  43. package/dist/commands/files-command.js +66 -8
  44. package/dist/commands/generate-rules-command.d.ts +1 -0
  45. package/dist/commands/generate-rules-command.d.ts.map +1 -0
  46. package/dist/commands/generate-rules-command.js +315 -4
  47. package/dist/commands/handoff-command.d.ts +3 -0
  48. package/dist/commands/handoff-command.d.ts.map +1 -0
  49. package/dist/commands/handoff-command.js +202 -0
  50. package/dist/commands/health-command.d.ts +3 -1
  51. package/dist/commands/health-command.d.ts.map +1 -0
  52. package/dist/commands/health-command.js +160 -6
  53. package/dist/commands/help-command.d.ts +1 -0
  54. package/dist/commands/help-command.d.ts.map +1 -0
  55. package/dist/commands/help-command.js +30 -3
  56. package/dist/commands/history-command.d.ts +3 -0
  57. package/dist/commands/history-command.d.ts.map +1 -0
  58. package/dist/commands/history-command.js +534 -0
  59. package/dist/commands/init-command.d.ts +2 -1
  60. package/dist/commands/init-command.d.ts.map +1 -0
  61. package/dist/commands/init-command.js +56 -20
  62. package/dist/commands/last-log-command.d.ts +1 -0
  63. package/dist/commands/last-log-command.d.ts.map +1 -0
  64. package/dist/commands/last-log-command.js +39 -17
  65. package/dist/commands/list-directories-command.d.ts +3 -0
  66. package/dist/commands/list-directories-command.d.ts.map +1 -0
  67. package/dist/commands/list-directories-command.js +48 -0
  68. package/dist/commands/list-tools-command.d.ts +3 -0
  69. package/dist/commands/list-tools-command.d.ts.map +1 -0
  70. package/dist/commands/list-tools-command.js +124 -0
  71. package/dist/commands/manager.d.ts +20 -3
  72. package/dist/commands/manager.d.ts.map +1 -0
  73. package/dist/commands/manager.js +123 -26
  74. package/dist/commands/model-command.d.ts +23 -0
  75. package/dist/commands/model-command.d.ts.map +1 -0
  76. package/dist/commands/model-command.js +261 -5
  77. package/dist/commands/paste-command.d.ts +1 -0
  78. package/dist/commands/paste-command.d.ts.map +1 -0
  79. package/dist/commands/paste-command.js +98 -5
  80. package/dist/commands/pickup-command.d.ts +3 -0
  81. package/dist/commands/pickup-command.d.ts.map +1 -0
  82. package/dist/commands/pickup-command.js +161 -0
  83. package/dist/commands/prompt-command.d.ts +2 -1
  84. package/dist/commands/prompt-command.d.ts.map +1 -0
  85. package/dist/commands/prompt-command.js +178 -9
  86. package/dist/commands/remove-directory-command.d.ts +3 -0
  87. package/dist/commands/remove-directory-command.d.ts.map +1 -0
  88. package/dist/commands/remove-directory-command.js +87 -0
  89. package/dist/commands/reset-command.d.ts +2 -1
  90. package/dist/commands/reset-command.d.ts.map +1 -0
  91. package/dist/commands/reset-command.js +14 -3
  92. package/dist/commands/rules-command.d.ts +1 -0
  93. package/dist/commands/rules-command.d.ts.map +1 -0
  94. package/dist/commands/rules-command.js +70 -3
  95. package/dist/commands/save-command.d.ts +1 -0
  96. package/dist/commands/save-command.d.ts.map +1 -0
  97. package/dist/commands/save-command.js +13 -1
  98. package/dist/commands/shell-command.d.ts +3 -0
  99. package/dist/commands/shell-command.d.ts.map +1 -0
  100. package/dist/commands/shell-command.js +128 -0
  101. package/dist/commands/types.d.ts +16 -8
  102. package/dist/commands/types.d.ts.map +1 -0
  103. package/dist/commands/usage-command.d.ts +1 -0
  104. package/dist/commands/usage-command.d.ts.map +1 -0
  105. package/dist/commands/usage-command.js +24 -3
  106. package/dist/config.d.ts +21 -34
  107. package/dist/config.d.ts.map +1 -0
  108. package/dist/config.js +55 -15
  109. package/dist/dedent.d.ts +1 -0
  110. package/dist/dedent.d.ts.map +1 -0
  111. package/dist/execution/index.d.ts +112 -0
  112. package/dist/execution/index.d.ts.map +1 -0
  113. package/dist/execution/index.js +432 -0
  114. package/dist/formatting.d.ts +107 -10
  115. package/dist/formatting.d.ts.map +1 -0
  116. package/dist/formatting.js +150 -62
  117. package/dist/index.d.ts +8 -2
  118. package/dist/index.d.ts.map +1 -0
  119. package/dist/index.js +150 -38
  120. package/dist/logger.d.ts +1 -0
  121. package/dist/logger.d.ts.map +1 -0
  122. package/dist/logger.js +47 -18
  123. package/dist/mentions.d.ts +6 -1
  124. package/dist/mentions.d.ts.map +1 -0
  125. package/dist/mentions.js +58 -11
  126. package/dist/messages.d.ts +16 -20
  127. package/dist/messages.d.ts.map +1 -0
  128. package/dist/messages.js +89 -72
  129. package/dist/middleware/audit-message.d.ts +1 -0
  130. package/dist/middleware/audit-message.d.ts.map +1 -0
  131. package/dist/middleware/cache.d.ts +3 -0
  132. package/dist/middleware/cache.d.ts.map +1 -0
  133. package/dist/middleware/cache.js +53 -0
  134. package/dist/middleware/index.d.ts +2 -0
  135. package/dist/middleware/index.d.ts.map +1 -0
  136. package/dist/middleware/index.js +1 -0
  137. package/dist/middleware/rate-limit.d.ts +1 -0
  138. package/dist/middleware/rate-limit.d.ts.map +1 -0
  139. package/dist/models/ai-config.d.ts +5 -2
  140. package/dist/models/ai-config.d.ts.map +1 -0
  141. package/dist/models/ai-config.js +12 -2
  142. package/dist/models/anthropic-provider.d.ts +1 -0
  143. package/dist/models/anthropic-provider.d.ts.map +1 -0
  144. package/dist/models/anthropic-provider.js +3 -60
  145. package/dist/models/deepseek-provider.d.ts +1 -0
  146. package/dist/models/deepseek-provider.d.ts.map +1 -0
  147. package/dist/models/google-provider.d.ts +1 -0
  148. package/dist/models/google-provider.d.ts.map +1 -0
  149. package/dist/models/groq-provider.d.ts +20 -0
  150. package/dist/models/groq-provider.d.ts.map +1 -0
  151. package/dist/models/groq-provider.js +31 -0
  152. package/dist/models/manager.d.ts +3 -1
  153. package/dist/models/manager.d.ts.map +1 -0
  154. package/dist/models/manager.js +26 -2
  155. package/dist/models/openai-provider.d.ts +2 -1
  156. package/dist/models/openai-provider.d.ts.map +1 -0
  157. package/dist/models/openrouter-provider.d.ts +25 -23
  158. package/dist/models/openrouter-provider.d.ts.map +1 -0
  159. package/dist/models/openrouter-provider.js +181 -122
  160. package/dist/models/providers.d.ts +4 -5
  161. package/dist/models/providers.d.ts.map +1 -0
  162. package/dist/models/providers.js +7 -3
  163. package/dist/models/xai-provider.d.ts +1 -0
  164. package/dist/models/xai-provider.d.ts.map +1 -0
  165. package/dist/parsing.d.ts +2 -1
  166. package/dist/parsing.d.ts.map +1 -0
  167. package/dist/prompts/manager.d.ts +14 -2
  168. package/dist/prompts/manager.d.ts.map +1 -0
  169. package/dist/prompts.d.ts +2 -0
  170. package/dist/prompts.d.ts.map +1 -0
  171. package/dist/prompts.js +65 -12
  172. package/dist/repl/display-tool-messages.d.ts +4 -0
  173. package/dist/repl/display-tool-messages.d.ts.map +1 -0
  174. package/dist/repl/display-tool-messages.js +58 -0
  175. package/dist/repl/display-tool-use.d.ts +14 -0
  176. package/dist/repl/display-tool-use.d.ts.map +1 -0
  177. package/dist/repl/display-tool-use.js +63 -0
  178. package/dist/repl/get-prompt-header.d.ts +8 -0
  179. package/dist/repl/get-prompt-header.d.ts.map +1 -0
  180. package/dist/repl/get-prompt-header.js +9 -0
  181. package/dist/repl/project-status-line.d.ts +2 -0
  182. package/dist/repl/project-status-line.d.ts.map +1 -0
  183. package/dist/repl/project-status-line.js +31 -0
  184. package/dist/repl/prompt.d.ts +21 -0
  185. package/dist/repl/prompt.d.ts.map +1 -0
  186. package/dist/{repl-prompt.js → repl/prompt.js} +119 -22
  187. package/dist/repl/tool-call-repair.d.ts +4 -0
  188. package/dist/repl/tool-call-repair.d.ts.map +1 -0
  189. package/dist/repl/tool-call-repair.js +54 -0
  190. package/dist/repl-new.d.ts +53 -0
  191. package/dist/repl-new.d.ts.map +1 -0
  192. package/dist/repl-new.js +374 -0
  193. package/dist/repl.d.ts +9 -7
  194. package/dist/repl.d.ts.map +1 -0
  195. package/dist/repl.js +142 -378
  196. package/dist/terminal/ansi-styles.d.ts +77 -0
  197. package/dist/terminal/ansi-styles.d.ts.map +1 -0
  198. package/dist/terminal/ansi-styles.js +215 -0
  199. package/dist/terminal/checkbox-prompt.d.ts +36 -0
  200. package/dist/terminal/checkbox-prompt.d.ts.map +1 -0
  201. package/dist/terminal/checkbox-prompt.js +368 -0
  202. package/dist/terminal/default-theme.d.ts +6 -0
  203. package/dist/terminal/default-theme.d.ts.map +1 -0
  204. package/dist/terminal/default-theme.js +182 -0
  205. package/dist/terminal/east-asian-width.d.ts +8 -0
  206. package/dist/terminal/east-asian-width.d.ts.map +1 -0
  207. package/dist/terminal/east-asian-width.js +409 -0
  208. package/dist/terminal/editor-prompt.d.ts +10 -0
  209. package/dist/terminal/editor-prompt.d.ts.map +1 -0
  210. package/dist/terminal/editor-prompt.js +61 -0
  211. package/dist/terminal/errors.d.ts +19 -0
  212. package/dist/terminal/errors.d.ts.map +1 -0
  213. package/dist/terminal/errors.js +37 -0
  214. package/dist/terminal/formatting.d.ts +1 -11
  215. package/dist/terminal/formatting.d.ts.map +1 -0
  216. package/dist/terminal/formatting.js +4 -20
  217. package/dist/terminal/highlight/index.d.ts +53 -0
  218. package/dist/terminal/highlight/index.d.ts.map +1 -0
  219. package/dist/terminal/highlight/index.js +90 -0
  220. package/dist/terminal/highlight/theme.d.ts +233 -0
  221. package/dist/terminal/highlight/theme.d.ts.map +1 -0
  222. package/dist/terminal/highlight/theme.js +83 -0
  223. package/dist/terminal/index.d.ts +23 -9
  224. package/dist/terminal/index.d.ts.map +1 -0
  225. package/dist/terminal/index.js +136 -126
  226. package/dist/terminal/input-prompt.d.ts +17 -0
  227. package/dist/terminal/input-prompt.d.ts.map +1 -0
  228. package/dist/terminal/input-prompt.js +181 -0
  229. package/dist/terminal/markdown-utils.d.ts +1 -0
  230. package/dist/terminal/markdown-utils.d.ts.map +1 -0
  231. package/dist/terminal/markdown.d.ts +1 -0
  232. package/dist/terminal/markdown.d.ts.map +1 -0
  233. package/dist/terminal/markdown.js +20 -12
  234. package/dist/terminal/search-prompt.d.ts +20 -0
  235. package/dist/terminal/search-prompt.d.ts.map +1 -0
  236. package/dist/terminal/search-prompt.js +280 -0
  237. package/dist/terminal/select-prompt.d.ts +26 -0
  238. package/dist/terminal/select-prompt.d.ts.map +1 -0
  239. package/dist/terminal/select-prompt.js +306 -0
  240. package/dist/terminal/string-width.d.ts +7 -0
  241. package/dist/terminal/string-width.d.ts.map +1 -0
  242. package/dist/terminal/string-width.js +61 -0
  243. package/dist/terminal/strip-ansi.d.ts +2 -0
  244. package/dist/terminal/strip-ansi.d.ts.map +1 -0
  245. package/dist/terminal/strip-ansi.js +20 -0
  246. package/dist/terminal/style.d.ts +191 -0
  247. package/dist/terminal/style.d.ts.map +1 -0
  248. package/dist/terminal/style.js +259 -0
  249. package/dist/terminal/supports-color.d.ts +1 -0
  250. package/dist/terminal/supports-color.d.ts.map +1 -0
  251. package/dist/terminal/supports-hyperlinks.d.ts +1 -3
  252. package/dist/terminal/supports-hyperlinks.d.ts.map +1 -0
  253. package/dist/terminal/supports-hyperlinks.js +1 -1
  254. package/dist/terminal/types.d.ts +1 -37
  255. package/dist/terminal/types.d.ts.map +1 -0
  256. package/dist/terminal/wrap-ansi.d.ts +8 -0
  257. package/dist/terminal/wrap-ansi.d.ts.map +1 -0
  258. package/dist/terminal/wrap-ansi.js +190 -0
  259. package/dist/{token-utils.d.ts → tokens/counter.d.ts} +1 -0
  260. package/dist/tokens/counter.d.ts.map +1 -0
  261. package/dist/{token-utils.js → tokens/counter.js} +1 -1
  262. package/dist/tokens/threshold.d.ts +35 -0
  263. package/dist/tokens/threshold.d.ts.map +1 -0
  264. package/dist/tokens/threshold.js +85 -0
  265. package/dist/{token-tracker.d.ts → tokens/tracker.d.ts} +1 -0
  266. package/dist/tokens/tracker.d.ts.map +1 -0
  267. package/dist/tools/advanced-edit-file.d.ts +69 -0
  268. package/dist/tools/advanced-edit-file.d.ts.map +1 -0
  269. package/dist/tools/advanced-edit-file.js +281 -0
  270. package/dist/tools/agent.d.ts +19 -7
  271. package/dist/tools/agent.d.ts.map +1 -0
  272. package/dist/tools/agent.js +70 -54
  273. package/dist/tools/bash-utils.d.ts +7 -0
  274. package/dist/tools/bash-utils.d.ts.map +1 -0
  275. package/dist/tools/bash-utils.js +220 -0
  276. package/dist/tools/bash.d.ts +25 -14
  277. package/dist/tools/bash.d.ts.map +1 -0
  278. package/dist/tools/bash.js +87 -251
  279. package/dist/tools/code-interpreter.d.ts +22 -10
  280. package/dist/tools/code-interpreter.d.ts.map +1 -0
  281. package/dist/tools/code-interpreter.js +146 -210
  282. package/dist/tools/delete-file.d.ts +18 -9
  283. package/dist/tools/delete-file.d.ts.map +1 -0
  284. package/dist/tools/delete-file.js +55 -85
  285. package/dist/tools/directory-tree.d.ts +26 -6
  286. package/dist/tools/directory-tree.d.ts.map +1 -0
  287. package/dist/tools/directory-tree.js +109 -28
  288. package/dist/tools/dynamic-tool-loader.d.ts +22 -0
  289. package/dist/tools/dynamic-tool-loader.d.ts.map +1 -0
  290. package/dist/tools/dynamic-tool-loader.js +272 -0
  291. package/dist/tools/dynamic-tool-parser.d.ts +21 -0
  292. package/dist/tools/dynamic-tool-parser.d.ts.map +1 -0
  293. package/dist/tools/dynamic-tool-parser.js +22 -0
  294. package/dist/tools/edit-file.d.ts +41 -13
  295. package/dist/tools/edit-file.d.ts.map +1 -0
  296. package/dist/tools/edit-file.js +173 -96
  297. package/dist/tools/filesystem-utils.d.ts +7 -21
  298. package/dist/tools/filesystem-utils.d.ts.map +1 -0
  299. package/dist/tools/filesystem-utils.js +111 -149
  300. package/dist/tools/git-utils.d.ts +1 -0
  301. package/dist/tools/git-utils.d.ts.map +1 -0
  302. package/dist/tools/glob.d.ts +36 -0
  303. package/dist/tools/glob.d.ts.map +1 -0
  304. package/dist/tools/glob.js +143 -0
  305. package/dist/tools/grep.d.ts +76 -13
  306. package/dist/tools/grep.d.ts.map +1 -0
  307. package/dist/tools/grep.js +420 -135
  308. package/dist/tools/index.d.ts +207 -133
  309. package/dist/tools/index.d.ts.map +1 -0
  310. package/dist/tools/index.js +245 -127
  311. package/dist/tools/llm-edit-fixer.d.ts +25 -0
  312. package/dist/tools/llm-edit-fixer.d.ts.map +1 -0
  313. package/dist/tools/llm-edit-fixer.js +150 -0
  314. package/dist/tools/move-file.d.ts +20 -7
  315. package/dist/tools/move-file.d.ts.map +1 -0
  316. package/dist/tools/move-file.js +43 -29
  317. package/dist/tools/read-file.d.ts +49 -10
  318. package/dist/tools/read-file.d.ts.map +1 -0
  319. package/dist/tools/read-file.js +79 -67
  320. package/dist/tools/read-multiple-files.d.ts +19 -7
  321. package/dist/tools/read-multiple-files.d.ts.map +1 -0
  322. package/dist/tools/read-multiple-files.js +117 -33
  323. package/dist/tools/save-file.d.ts +46 -11
  324. package/dist/tools/save-file.d.ts.map +1 -0
  325. package/dist/tools/save-file.js +63 -78
  326. package/dist/tools/think.d.ts +16 -7
  327. package/dist/tools/think.d.ts.map +1 -0
  328. package/dist/tools/think.js +34 -22
  329. package/dist/tools/types.d.ts +18 -11
  330. package/dist/tools/types.d.ts.map +1 -0
  331. package/dist/tools/types.js +9 -0
  332. package/dist/tools/utils.d.ts +14 -0
  333. package/dist/tools/utils.d.ts.map +1 -0
  334. package/dist/tools/utils.js +16 -0
  335. package/dist/tools/web-fetch.d.ts +15 -6
  336. package/dist/tools/web-fetch.d.ts.map +1 -0
  337. package/dist/tools/web-fetch.js +40 -39
  338. package/dist/tools/web-search.d.ts +17 -7
  339. package/dist/tools/web-search.d.ts.map +1 -0
  340. package/dist/tools/web-search.js +86 -33
  341. package/dist/tui/autocomplete.d.ts +44 -0
  342. package/dist/tui/autocomplete.d.ts.map +1 -0
  343. package/dist/tui/autocomplete.js +466 -0
  344. package/dist/tui/components/assistant-message.d.ts +18 -0
  345. package/dist/tui/components/assistant-message.d.ts.map +1 -0
  346. package/dist/tui/components/assistant-message.js +29 -0
  347. package/dist/tui/components/editor.d.ts +51 -0
  348. package/dist/tui/components/editor.d.ts.map +1 -0
  349. package/dist/tui/components/editor.js +758 -0
  350. package/dist/tui/components/footer.d.ts +24 -0
  351. package/dist/tui/components/footer.d.ts.map +1 -0
  352. package/dist/tui/components/footer.js +197 -0
  353. package/dist/tui/components/input.d.ts +14 -0
  354. package/dist/tui/components/input.d.ts.map +1 -0
  355. package/dist/tui/components/input.js +122 -0
  356. package/dist/tui/components/loader.d.ts +19 -0
  357. package/dist/tui/components/loader.d.ts.map +1 -0
  358. package/dist/tui/components/loader.js +45 -0
  359. package/dist/tui/components/markdown.d.ts +103 -0
  360. package/dist/tui/components/markdown.d.ts.map +1 -0
  361. package/dist/tui/components/markdown.js +533 -0
  362. package/dist/tui/components/modal.d.ts +40 -0
  363. package/dist/tui/components/modal.d.ts.map +1 -0
  364. package/dist/tui/components/modal.js +292 -0
  365. package/dist/tui/components/prompt-status.d.ts +16 -0
  366. package/dist/tui/components/prompt-status.d.ts.map +1 -0
  367. package/dist/tui/components/prompt-status.js +21 -0
  368. package/dist/tui/components/select-list.d.ts +22 -0
  369. package/dist/tui/components/select-list.d.ts.map +1 -0
  370. package/dist/tui/components/select-list.js +143 -0
  371. package/dist/tui/components/spacer.d.ts +16 -0
  372. package/dist/tui/components/spacer.d.ts.map +1 -0
  373. package/dist/tui/components/spacer.js +27 -0
  374. package/dist/tui/components/text.d.ts +26 -0
  375. package/dist/tui/components/text.d.ts.map +1 -0
  376. package/dist/tui/components/text.js +143 -0
  377. package/dist/tui/components/thinking-block.d.ts +14 -0
  378. package/dist/tui/components/thinking-block.d.ts.map +1 -0
  379. package/dist/tui/components/thinking-block.js +30 -0
  380. package/dist/tui/components/tool-execution.d.ts +17 -0
  381. package/dist/tui/components/tool-execution.d.ts.map +1 -0
  382. package/dist/tui/components/tool-execution.js +153 -0
  383. package/dist/tui/components/user-message.d.ts +9 -0
  384. package/dist/tui/components/user-message.d.ts.map +1 -0
  385. package/dist/tui/components/user-message.js +21 -0
  386. package/dist/tui/components/welcome.d.ts +6 -0
  387. package/dist/tui/components/welcome.d.ts.map +1 -0
  388. package/dist/tui/components/welcome.js +30 -0
  389. package/dist/tui/index.d.ts +14 -0
  390. package/dist/tui/index.d.ts.map +1 -0
  391. package/dist/tui/index.js +18 -0
  392. package/dist/tui/terminal.d.ts +37 -0
  393. package/dist/tui/terminal.d.ts.map +1 -0
  394. package/dist/tui/terminal.js +104 -0
  395. package/dist/tui/tui.d.ts +67 -0
  396. package/dist/tui/tui.d.ts.map +1 -0
  397. package/dist/tui/tui.js +184 -0
  398. package/dist/tui/utils.d.ts +19 -0
  399. package/dist/tui/utils.d.ts.map +1 -0
  400. package/dist/tui/utils.js +31 -0
  401. package/dist/utils/filesystem.d.ts +23 -0
  402. package/dist/utils/filesystem.d.ts.map +1 -0
  403. package/dist/utils/filesystem.js +140 -0
  404. package/dist/utils/filetype-detection.d.ts +3 -0
  405. package/dist/utils/filetype-detection.d.ts.map +1 -0
  406. package/dist/utils/filetype-detection.js +112 -0
  407. package/dist/utils/generators.d.ts +3 -0
  408. package/dist/utils/generators.d.ts.map +1 -0
  409. package/dist/utils/generators.js +25 -0
  410. package/dist/utils/glob.d.ts +52 -0
  411. package/dist/utils/glob.d.ts.map +1 -0
  412. package/dist/utils/glob.js +376 -0
  413. package/dist/utils/ignore.d.ts +104 -0
  414. package/dist/utils/ignore.d.ts.map +1 -0
  415. package/dist/utils/ignore.js +649 -0
  416. package/dist/utils/iterables.d.ts +2 -0
  417. package/dist/utils/iterables.d.ts.map +1 -0
  418. package/dist/utils/iterables.js +6 -0
  419. package/dist/utils/process.d.ts +3 -2
  420. package/dist/utils/process.d.ts.map +1 -0
  421. package/dist/utils/process.js +17 -2
  422. package/dist/utils/zod-utils.d.ts +4 -0
  423. package/dist/utils/zod-utils.d.ts.map +1 -0
  424. package/dist/utils/zod-utils.js +7 -0
  425. package/dist/version.d.ts +1 -0
  426. package/dist/version.d.ts.map +1 -0
  427. package/package.json +34 -32
  428. package/dist/conversation-analyzer.d.ts +0 -10
  429. package/dist/conversation-analyzer.js +0 -88
  430. package/dist/repl-prompt.d.ts +0 -14
  431. package/dist/tools/command-validation.d.ts +0 -11
  432. package/dist/tools/command-validation.js +0 -45
  433. /package/dist/{token-tracker.js → tokens/tracker.js} +0 -0
@@ -1,68 +1,121 @@
1
- import { tool } from "ai";
2
- import chalk from "chalk";
3
1
  import { SafeSearchType, search } from "duck-duck-scrape";
4
- import Exa from "exa-js";
5
2
  import { z } from "zod";
3
+ import Exa from "../api/exa/index.js";
4
+ import style from "../terminal/style.js";
5
+ import { manageTokenLimit } from "../tokens/threshold.js";
6
6
  export const WebSearchTool = {
7
7
  name: "webSearch",
8
8
  };
9
- export const createWebSearchTool = ({ sendData, tokenCounter, }) => {
9
+ const inputSchema = z.object({
10
+ query: z.string().describe("The search query."),
11
+ });
12
+ export const createWebSearchTool = ({ tokenCounter, }) => {
13
+ const toolDef = {
14
+ description: "Searches the web and returns match documents with their title, url, and text content. The query should be formulated as a natural language question.",
15
+ inputSchema,
16
+ };
17
+ const execute = async function* ({ query }, { toolCallId, abortSignal }) {
18
+ try {
19
+ // Check if execution has been aborted
20
+ if (abortSignal?.aborted) {
21
+ throw new Error("Web search aborted");
22
+ }
23
+ yield {
24
+ name: WebSearchTool.name,
25
+ event: "tool-init",
26
+ id: toolCallId,
27
+ data: `${style.cyan(query)}`,
28
+ };
29
+ if (abortSignal?.aborted) {
30
+ throw new Error("Web search aborted before search execution");
31
+ }
32
+ const result = await performSearch(query, abortSignal);
33
+ const sources = result.results.map((source) => `## ${source.title}\nURL: ${source.url}\n\n${source.text}`);
34
+ const resultText = `# Search Results:\n\n${sources.join("\n\n")}`;
35
+ const searchResult = await manageTokenLimit(resultText, tokenCounter, "WebSearch", "Use more specific search queries or reduce number of results");
36
+ yield {
37
+ name: WebSearchTool.name,
38
+ event: "tool-completion",
39
+ id: toolCallId,
40
+ data: `Found ${result.results.length} results (${searchResult.tokenCount} tokens)`,
41
+ };
42
+ yield searchResult.content;
43
+ }
44
+ catch (error) {
45
+ const errorMessage = error instanceof Error ? error.message : String(error);
46
+ yield {
47
+ name: WebSearchTool.name,
48
+ event: "tool-error",
49
+ id: toolCallId,
50
+ data: errorMessage,
51
+ };
52
+ yield errorMessage;
53
+ }
54
+ };
10
55
  return {
11
- [WebSearchTool.name]: tool({
12
- description: "Searches the web and returns match documents with their title, url, and text content. The query should be formulated as a natural language question.",
13
- inputSchema: z.object({
14
- query: z.string().describe("The search query."),
15
- }),
16
- execute: async ({ query }, { toolCallId }) => {
17
- sendData?.({
18
- id: toolCallId,
19
- event: "tool-init",
20
- data: `Web search: ${chalk.cyan(query)}`,
21
- });
22
- const result = await performSearch(query);
23
- const sources = result.results.map((source) => `## ${source.title}\nURL: ${source.url}\n\n${source.text}`);
24
- const resultText = `# Search Results:\n\n${sources.join("\n\n")}`;
25
- const tokenCount = tokenCounter.count(resultText);
26
- sendData?.({
27
- id: toolCallId,
28
- event: "tool-completion",
29
- data: `Found ${result.results.length} results. (${tokenCount} tokens)`,
30
- });
31
- return resultText;
32
- },
33
- }),
56
+ toolDef,
57
+ execute,
58
+ // No ask method needed for read-only tool
34
59
  };
35
60
  };
36
- async function performSearch(query) {
61
+ async function performSearch(query, abortSignal) {
37
62
  // Check if EXA API key is available
38
63
  const hasExaApiKey = process.env["EXA_API_KEY"] && process.env["EXA_API_KEY"].trim() !== "";
39
64
  if (hasExaApiKey) {
40
65
  // Use Exa search
41
66
  try {
67
+ if (abortSignal?.aborted) {
68
+ throw new Error("Web search aborted before Exa search");
69
+ }
42
70
  const exa = new Exa(process.env["EXA_API_KEY"]);
43
- const result = await exa.searchAndContents(query, {
71
+ // Create a promise that races with the abort signal
72
+ const searchPromise = exa.searchAndContents(query, {
44
73
  numResults: 5,
45
74
  text: true,
46
75
  });
76
+ const result = await Promise.race([
77
+ searchPromise,
78
+ new Promise((_, reject) => {
79
+ if (abortSignal) {
80
+ abortSignal.addEventListener("abort", () => {
81
+ reject(new Error("Web search aborted during Exa search"));
82
+ });
83
+ }
84
+ }),
85
+ ]);
47
86
  return result;
48
87
  }
49
88
  catch (error) {
50
89
  // If Exa fails, fall back to duck duck scrape
51
90
  console.info("Exa search failed, falling back to DuckDuckGo:", error);
52
- return await searchWithDuckDuckGo(query);
91
+ return await searchWithDuckDuckGo(query, abortSignal);
53
92
  }
54
93
  }
55
94
  else {
56
95
  // Use DuckDuckGo search as fallback
57
96
  console.info("EXA_API_KEY not set, using DuckDuckGo search");
58
- return await searchWithDuckDuckGo(query);
97
+ return await searchWithDuckDuckGo(query, abortSignal);
59
98
  }
60
99
  }
61
- async function searchWithDuckDuckGo(query) {
100
+ async function searchWithDuckDuckGo(query, abortSignal) {
62
101
  try {
63
- const searchResults = await search(query, {
102
+ if (abortSignal?.aborted) {
103
+ throw new Error("Web search aborted before DuckDuckGo search");
104
+ }
105
+ // Create a promise that races with the abort signal
106
+ const searchPromise = search(query, {
64
107
  safeSearch: SafeSearchType.MODERATE,
65
108
  });
109
+ const searchResults = await Promise.race([
110
+ searchPromise,
111
+ new Promise((_, reject) => {
112
+ if (abortSignal) {
113
+ abortSignal.addEventListener("abort", () => {
114
+ reject(new Error("Web search aborted during DuckDuckGo search"));
115
+ });
116
+ }
117
+ }),
118
+ ]);
66
119
  // Transform duck-duck-scrape results to match Exa format
67
120
  // Take only first 5 results to match Exa behavior
68
121
  const results = searchResults.results
@@ -0,0 +1,44 @@
1
+ export interface AutocompleteItem {
2
+ value: string;
3
+ label: string;
4
+ description?: string;
5
+ }
6
+ export interface SlashCommand {
7
+ name: string;
8
+ description?: string;
9
+ getArgumentCompletions?(argumentPrefix: string): AutocompleteItem[] | null;
10
+ }
11
+ export interface AutocompleteProvider {
12
+ getSuggestions(lines: string[], cursorLine: number, cursorCol: number): Promise<{
13
+ items: AutocompleteItem[];
14
+ prefix: string;
15
+ } | null>;
16
+ applyCompletion(lines: string[], cursorLine: number, cursorCol: number, item: AutocompleteItem, prefix: string): {
17
+ lines: string[];
18
+ cursorLine: number;
19
+ cursorCol: number;
20
+ };
21
+ }
22
+ export declare class CombinedAutocompleteProvider implements AutocompleteProvider {
23
+ private commands;
24
+ private allowedDirs;
25
+ constructor(commands?: (SlashCommand | AutocompleteItem)[], allowedDirs?: string[]);
26
+ getSuggestions(lines: string[], cursorLine: number, cursorCol: number): Promise<{
27
+ items: AutocompleteItem[];
28
+ prefix: string;
29
+ } | null>;
30
+ applyCompletion(lines: string[], cursorLine: number, cursorCol: number, item: AutocompleteItem, prefix: string): {
31
+ lines: string[];
32
+ cursorLine: number;
33
+ cursorCol: number;
34
+ };
35
+ private extractPathPrefix;
36
+ private isPathWithinAllowedDirs;
37
+ private getFileSuggestions;
38
+ getForceFileSuggestions(lines: string[], cursorLine: number, cursorCol: number): Promise<{
39
+ items: AutocompleteItem[];
40
+ prefix: string;
41
+ } | null>;
42
+ shouldTriggerFileCompletion(lines: string[], cursorLine: number, cursorCol: number): boolean;
43
+ }
44
+ //# sourceMappingURL=autocomplete.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autocomplete.d.ts","sourceRoot":"","sources":["../../source/tui/autocomplete.ts"],"names":[],"mappings":"AAyHA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IAGrB,sBAAsB,CAAC,CAAC,cAAc,EAAE,MAAM,GAAG,gBAAgB,EAAE,GAAG,IAAI,CAAC;CAC5E;AAED,MAAM,WAAW,oBAAoB;IAGnC,cAAc,CACZ,KAAK,EAAE,MAAM,EAAE,EACf,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;QACT,KAAK,EAAE,gBAAgB,EAAE,CAAC;QAC1B,MAAM,EAAE,MAAM,CAAC;KAChB,GAAG,IAAI,CAAC,CAAC;IAIV,eAAe,CACb,KAAK,EAAE,MAAM,EAAE,EACf,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,gBAAgB,EACtB,MAAM,EAAE,MAAM,GACb;QACD,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAGD,qBAAa,4BAA6B,YAAW,oBAAoB;IACvE,OAAO,CAAC,QAAQ,CAAsC;IACtD,OAAO,CAAC,WAAW,CAAW;gBAG5B,QAAQ,GAAE,CAAC,YAAY,GAAG,gBAAgB,CAAC,EAAO,EAClD,WAAW,GAAE,MAAM,EAAoB;IAMnC,cAAc,CAClB,KAAK,EAAE,MAAM,EAAE,EACf,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;QAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAyEhE,eAAe,CACb,KAAK,EAAE,MAAM,EAAE,EACf,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,gBAAgB,EACtB,MAAM,EAAE,MAAM,GACb;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;IA6D7D,OAAO,CAAC,iBAAiB;YA8EX,uBAAuB;YAyBvB,kBAAkB;IAgJ1B,uBAAuB,CAC3B,KAAK,EAAE,MAAM,EAAE,EACf,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;QAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAyBhE,2BAA2B,CACzB,KAAK,EAAE,MAAM,EAAE,EACf,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO;CAWX"}
@@ -0,0 +1,466 @@
1
+ import { readdir, realpath, stat } from "node:fs/promises";
2
+ import { basename, dirname, extname, isAbsolute, join, relative, resolve, } from "node:path";
3
+ // Cache for directory listings to improve performance
4
+ class DirectoryCache {
5
+ cache = new Map();
6
+ ttl = 3000; // 3 seconds
7
+ async get(dir) {
8
+ const cached = this.cache.get(dir);
9
+ if (cached && Date.now() - cached.timestamp < this.ttl) {
10
+ return cached.entries;
11
+ }
12
+ return null;
13
+ }
14
+ set(dir, entries) {
15
+ this.cache.set(dir, { entries, timestamp: Date.now() });
16
+ }
17
+ clear() {
18
+ this.cache.clear();
19
+ }
20
+ }
21
+ const directoryCache = new DirectoryCache();
22
+ // Helper function to get directory entries with caching and timeout
23
+ async function getDirectoryEntries(dir) {
24
+ const cached = await directoryCache.get(dir);
25
+ if (cached) {
26
+ return cached;
27
+ }
28
+ try {
29
+ // Add timeout to prevent hanging on slow file systems
30
+ const entries = await Promise.race([
31
+ readdir(dir, { withFileTypes: true }),
32
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Directory read timeout")), 2000)),
33
+ ]);
34
+ directoryCache.set(dir, entries);
35
+ return entries;
36
+ }
37
+ catch (_e) {
38
+ return [];
39
+ }
40
+ }
41
+ function isAttachableFile(filePath) {
42
+ // Check file extension for common text files that might be misidentified
43
+ const textExtensions = [
44
+ ".txt",
45
+ ".md",
46
+ ".markdown",
47
+ ".js",
48
+ ".ts",
49
+ ".tsx",
50
+ ".jsx",
51
+ ".py",
52
+ ".java",
53
+ ".c",
54
+ ".cpp",
55
+ ".h",
56
+ ".hpp",
57
+ ".cs",
58
+ ".php",
59
+ ".rb",
60
+ ".go",
61
+ ".rs",
62
+ ".swift",
63
+ ".kt",
64
+ ".scala",
65
+ ".sh",
66
+ ".bash",
67
+ ".zsh",
68
+ ".fish",
69
+ ".html",
70
+ ".htm",
71
+ ".css",
72
+ ".scss",
73
+ ".sass",
74
+ ".less",
75
+ ".xml",
76
+ ".json",
77
+ ".yaml",
78
+ ".yml",
79
+ ".toml",
80
+ ".ini",
81
+ ".cfg",
82
+ ".conf",
83
+ ".log",
84
+ ".sql",
85
+ ".r",
86
+ ".R",
87
+ ".m",
88
+ ".pl",
89
+ ".lua",
90
+ ".vim",
91
+ ".dockerfile",
92
+ ".makefile",
93
+ ".cmake",
94
+ ".gradle",
95
+ ".maven",
96
+ ".properties",
97
+ ".env",
98
+ ];
99
+ const ext = extname(filePath).toLowerCase();
100
+ return textExtensions.includes(ext);
101
+ }
102
+ // Combined provider that handles both slash commands and file paths
103
+ export class CombinedAutocompleteProvider {
104
+ commands;
105
+ allowedDirs;
106
+ constructor(commands = [], allowedDirs = [process.cwd()]) {
107
+ this.commands = commands;
108
+ this.allowedDirs = allowedDirs;
109
+ }
110
+ async getSuggestions(lines, cursorLine, cursorCol) {
111
+ const currentLine = lines[cursorLine] || "";
112
+ const textBeforeCursor = currentLine.slice(0, cursorCol);
113
+ // Check for slash commands
114
+ if (textBeforeCursor.startsWith("/")) {
115
+ const spaceIndex = textBeforeCursor.indexOf(" ");
116
+ if (spaceIndex === -1) {
117
+ // No space yet - complete command names
118
+ const prefix = textBeforeCursor.slice(1); // Remove the "/"
119
+ const filtered = this.commands
120
+ .filter((cmd) => {
121
+ const name = "name" in cmd ? cmd.name : cmd.value; // Check if SlashCommand or AutocompleteItem
122
+ return name?.toLowerCase().startsWith(prefix.toLowerCase());
123
+ })
124
+ .map((cmd) => ({
125
+ value: "name" in cmd ? cmd.name : cmd.value,
126
+ label: "name" in cmd ? cmd.name : cmd.label,
127
+ ...(cmd.description && { description: cmd.description }),
128
+ }));
129
+ if (filtered.length === 0)
130
+ return null;
131
+ return {
132
+ items: filtered,
133
+ prefix: prefix, // Return the actual prefix used for filtering (without "/")
134
+ };
135
+ }
136
+ // Space found - complete command arguments
137
+ const commandName = textBeforeCursor.slice(1, spaceIndex); // Command without "/"
138
+ const argumentText = textBeforeCursor.slice(spaceIndex + 1); // Text after space
139
+ const command = this.commands.find((cmd) => {
140
+ const name = "name" in cmd ? cmd.name : cmd.value;
141
+ return name === commandName;
142
+ });
143
+ if (!command ||
144
+ !("getArgumentCompletions" in command) ||
145
+ !command.getArgumentCompletions) {
146
+ return null; // No argument completion for this command
147
+ }
148
+ const argumentSuggestions = command.getArgumentCompletions?.(argumentText);
149
+ if (!argumentSuggestions || argumentSuggestions.length === 0) {
150
+ return null;
151
+ }
152
+ return {
153
+ items: argumentSuggestions,
154
+ prefix: argumentText,
155
+ };
156
+ }
157
+ // Check for file paths - triggered by Tab or if we detect a path pattern
158
+ const pathMatch = this.extractPathPrefix(textBeforeCursor, false);
159
+ if (pathMatch !== null) {
160
+ const suggestions = await this.getFileSuggestions(pathMatch);
161
+ if (suggestions.length === 0)
162
+ return null;
163
+ return {
164
+ items: suggestions,
165
+ prefix: pathMatch,
166
+ };
167
+ }
168
+ return null;
169
+ }
170
+ applyCompletion(lines, cursorLine, cursorCol, item, prefix) {
171
+ const currentLine = lines[cursorLine] || "";
172
+ const beforePrefix = currentLine.slice(0, cursorCol - prefix.length);
173
+ const afterCursor = currentLine.slice(cursorCol);
174
+ const textBeforeCursor = currentLine.slice(0, cursorCol);
175
+ // Check if we're completing a slash command (prefix doesn't start with "/" but we're in slash command context)
176
+ if (textBeforeCursor.startsWith("/") && !textBeforeCursor.includes(" ")) {
177
+ // This is a command name completion
178
+ const newLine = `${beforePrefix}${item.value} ${afterCursor}`;
179
+ const newLines = [...lines];
180
+ newLines[cursorLine] = newLine;
181
+ return {
182
+ lines: newLines,
183
+ cursorLine,
184
+ cursorCol: beforePrefix.length + item.value.length + 1, // +1 for space
185
+ };
186
+ }
187
+ // Check if we're completing a file attachment (prefix starts with "@")
188
+ if (prefix.startsWith("@")) {
189
+ // This is a file attachment completion
190
+ const newLine = `${beforePrefix + item.value} ${afterCursor}`;
191
+ const newLines = [...lines];
192
+ newLines[cursorLine] = newLine;
193
+ return {
194
+ lines: newLines,
195
+ cursorLine,
196
+ cursorCol: beforePrefix.length + item.value.length + 1, // +1 for space
197
+ };
198
+ }
199
+ // Check if we're in a slash command context (beforePrefix contains "/command ")
200
+ if (textBeforeCursor.includes("/") && textBeforeCursor.includes(" ")) {
201
+ // This is likely a command argument completion
202
+ const newLine = beforePrefix + item.value + afterCursor;
203
+ const newLines = [...lines];
204
+ newLines[cursorLine] = newLine;
205
+ return {
206
+ lines: newLines,
207
+ cursorLine,
208
+ cursorCol: beforePrefix.length + item.value.length,
209
+ };
210
+ }
211
+ // For file paths, complete the path
212
+ const newLine = beforePrefix + item.value + afterCursor;
213
+ const newLines = [...lines];
214
+ newLines[cursorLine] = newLine;
215
+ return {
216
+ lines: newLines,
217
+ cursorLine,
218
+ cursorCol: beforePrefix.length + item.value.length,
219
+ };
220
+ }
221
+ // Extract a path-like prefix from the text before cursor
222
+ extractPathPrefix(text, forceExtract = false) {
223
+ // Check for @ file attachment syntax first
224
+ const atMatch = text.match(/@([^\s]*)$/);
225
+ if (atMatch) {
226
+ // For forced extraction, always return the @ prefix
227
+ if (forceExtract) {
228
+ return atMatch[0];
229
+ }
230
+ // For natural triggers, always return @ prefixes (they're always file-related)
231
+ return atMatch[0];
232
+ }
233
+ // Match paths - more conservative approach to avoid matching already completed paths
234
+ // This regex captures:
235
+ // - Paths starting from beginning of line or after space
236
+ // - Optional ./ or ../ or ~/ prefix
237
+ // - The path itself (must contain at least one / or start with ./ or ../ or ~/)
238
+ const matches = text.match(/(?:^|\s)((?:\.{1,2}\/|~\/)?(?:[^\s]*\/)*[^\s/]*)$/);
239
+ if (!matches) {
240
+ // If forced extraction and no matches, return empty string to trigger from current dir
241
+ return forceExtract ? "" : null;
242
+ }
243
+ const pathPrefix = matches[1] || "";
244
+ // For forced extraction (Tab key), always return something
245
+ if (forceExtract) {
246
+ // If we're not in a clear path context and we're at the end of a word,
247
+ // return empty string to complete from current directory
248
+ if (!pathPrefix.includes("/") &&
249
+ !pathPrefix.endsWith("/") &&
250
+ !pathPrefix.startsWith(".") &&
251
+ !pathPrefix.startsWith("~/")) {
252
+ // Only return empty string if we're at the beginning or after space
253
+ // This prevents completing "source" as empty string
254
+ if (text === "" || text.endsWith(" ")) {
255
+ return "";
256
+ }
257
+ }
258
+ return pathPrefix;
259
+ }
260
+ // For natural triggers, be more conservative:
261
+ // Only trigger if we have a clear path indicator
262
+ const hasPathIndicator = pathPrefix.includes("/") ||
263
+ pathPrefix.endsWith("/") ||
264
+ pathPrefix.startsWith(".") ||
265
+ pathPrefix.startsWith("~/");
266
+ if (!hasPathIndicator) {
267
+ return null;
268
+ }
269
+ // Additional check: don't trigger if the path looks like it's already completed
270
+ // (i.e., doesn't end with a partial filename)
271
+ // Only apply this check for paths that don't have clear path indicators
272
+ // and look like single directory names (no path separators)
273
+ if (!pathPrefix.includes("/") &&
274
+ !pathPrefix.includes(".") &&
275
+ !pathPrefix.startsWith("./") &&
276
+ !pathPrefix.startsWith("../") &&
277
+ !pathPrefix.startsWith("~/") &&
278
+ pathPrefix.length > 3) {
279
+ // This might be a completed directory name, not a partial path
280
+ return null;
281
+ }
282
+ return pathPrefix;
283
+ }
284
+ // Check if a path is within any allowed directory
285
+ async isPathWithinAllowedDirs(requestedPath) {
286
+ for (const allowedDir of this.allowedDirs) {
287
+ // Resolve both paths to handle relative paths and symlinks
288
+ const absRequested = resolve(requestedPath);
289
+ const absAllowed = resolve(allowedDir);
290
+ let target = absRequested;
291
+ try {
292
+ // Try to resolve symlinks for the target path
293
+ target = await realpath(absRequested);
294
+ }
295
+ catch {
296
+ // If target doesn't exist, use the intended path
297
+ }
298
+ const rel = relative(absAllowed, target);
299
+ if (rel === "" || (!rel.startsWith("..") && !isAbsolute(rel))) {
300
+ return true;
301
+ }
302
+ }
303
+ return false;
304
+ }
305
+ // Get file/directory suggestions for a given path prefix
306
+ async getFileSuggestions(prefix) {
307
+ try {
308
+ let searchDir;
309
+ let searchPrefix;
310
+ let expandedPrefix = prefix;
311
+ let isAtPrefix = false;
312
+ // Handle @ file attachment prefix
313
+ if (prefix.startsWith("@")) {
314
+ isAtPrefix = true;
315
+ expandedPrefix = prefix.slice(1); // Remove the @
316
+ }
317
+ // Remove home directory expansion - not allowed
318
+ if (expandedPrefix.startsWith("~")) {
319
+ // Home directory access not allowed - return empty suggestions
320
+ return [];
321
+ }
322
+ // Check if we're trying to access root - not allowed
323
+ if (expandedPrefix.startsWith("/")) {
324
+ // Root access not allowed - return empty suggestions
325
+ return [];
326
+ }
327
+ if (expandedPrefix === "" ||
328
+ expandedPrefix === "./" ||
329
+ expandedPrefix === "../" ||
330
+ prefix === "@") {
331
+ // Start from primary directory
332
+ searchDir = this.allowedDirs[0];
333
+ searchPrefix = "";
334
+ }
335
+ else if (expandedPrefix.endsWith("/")) {
336
+ // If prefix ends with /, show contents of that directory
337
+ // Try to find which allowed directory this path belongs to
338
+ const resolvedPath = join(this.allowedDirs[0], expandedPrefix);
339
+ const isWithinAllowed = await this.isPathWithinAllowedDirs(resolvedPath);
340
+ if (!isWithinAllowed) {
341
+ return [];
342
+ }
343
+ searchDir = resolvedPath;
344
+ searchPrefix = "";
345
+ }
346
+ else {
347
+ // Split into directory and file prefix
348
+ const dir = dirname(expandedPrefix);
349
+ const file = basename(expandedPrefix);
350
+ const resolvedPath = join(this.allowedDirs[0], dir);
351
+ // Check if the resolved path is within allowed directories
352
+ const isWithinAllowed = await this.isPathWithinAllowedDirs(resolvedPath);
353
+ if (!isWithinAllowed) {
354
+ return [];
355
+ }
356
+ searchDir = resolvedPath;
357
+ searchPrefix = file;
358
+ }
359
+ const entries = await getDirectoryEntries(searchDir);
360
+ const suggestions = [];
361
+ for (const entry of entries) {
362
+ const entryName = entry.name;
363
+ if (!entryName.toLowerCase().startsWith(searchPrefix.toLowerCase())) {
364
+ continue;
365
+ }
366
+ const fullPath = join(searchDir, entryName);
367
+ let isDirectory = false;
368
+ try {
369
+ // Add timeout to prevent hanging on slow file systems
370
+ const stats = await Promise.race([
371
+ stat(fullPath),
372
+ new Promise((_, reject) => setTimeout(() => reject(new Error("File stat timeout")), 1000)),
373
+ ]);
374
+ isDirectory = stats.isDirectory();
375
+ }
376
+ catch {
377
+ // If stat fails or times out, skip this entry
378
+ continue;
379
+ }
380
+ // For @ prefix, filter to only show directories and attachable files
381
+ if (isAtPrefix && !isDirectory && !isAttachableFile(fullPath)) {
382
+ continue;
383
+ }
384
+ // Check if the resulting path is within allowed directories
385
+ if (!(await this.isPathWithinAllowedDirs(fullPath))) {
386
+ continue;
387
+ }
388
+ let relativePath;
389
+ // Handle @ prefix path construction
390
+ if (isAtPrefix) {
391
+ const pathWithoutAt = expandedPrefix;
392
+ if (pathWithoutAt.endsWith("/")) {
393
+ relativePath = `@${pathWithoutAt}${entryName}`;
394
+ }
395
+ else if (pathWithoutAt.includes("/")) {
396
+ relativePath = `@${join(dirname(pathWithoutAt), entryName)}`;
397
+ }
398
+ else {
399
+ relativePath = `@${entryName}`;
400
+ }
401
+ }
402
+ else if (prefix.endsWith("/")) {
403
+ // If prefix ends with /, append entry to the prefix
404
+ relativePath = prefix + entryName;
405
+ }
406
+ else if (prefix.includes("/")) {
407
+ relativePath = join(dirname(prefix), entryName);
408
+ }
409
+ else {
410
+ relativePath = entryName;
411
+ }
412
+ suggestions.push({
413
+ value: isDirectory ? `${relativePath}/` : relativePath,
414
+ label: entryName,
415
+ description: isDirectory ? "directory" : "file",
416
+ });
417
+ }
418
+ // Sort directories first, then alphabetically
419
+ suggestions.sort((a, b) => {
420
+ const aIsDir = a.description === "directory";
421
+ const bIsDir = b.description === "directory";
422
+ if (aIsDir && !bIsDir)
423
+ return -1;
424
+ if (!aIsDir && bIsDir)
425
+ return 1;
426
+ return a.label.localeCompare(b.label);
427
+ });
428
+ return suggestions;
429
+ }
430
+ catch (_e) {
431
+ // Directory doesn't exist or not accessible
432
+ return [];
433
+ }
434
+ }
435
+ // Force file completion (called on Tab key) - always returns suggestions
436
+ async getForceFileSuggestions(lines, cursorLine, cursorCol) {
437
+ const currentLine = lines[cursorLine] || "";
438
+ const textBeforeCursor = currentLine.slice(0, cursorCol);
439
+ // Don't trigger if we're in a slash command
440
+ if (textBeforeCursor.startsWith("/") && !textBeforeCursor.includes(" ")) {
441
+ return null;
442
+ }
443
+ // Force extract path prefix - this will always return something
444
+ const pathMatch = this.extractPathPrefix(textBeforeCursor, true);
445
+ if (pathMatch !== null) {
446
+ const suggestions = await this.getFileSuggestions(pathMatch);
447
+ if (suggestions.length === 0)
448
+ return null;
449
+ return {
450
+ items: suggestions,
451
+ prefix: pathMatch,
452
+ };
453
+ }
454
+ return null;
455
+ }
456
+ // Check if we should trigger file completion (called on Tab key)
457
+ shouldTriggerFileCompletion(lines, cursorLine, cursorCol) {
458
+ const currentLine = lines[cursorLine] || "";
459
+ const textBeforeCursor = currentLine.slice(0, cursorCol);
460
+ // Don't trigger if we're in a slash command
461
+ if (textBeforeCursor.startsWith("/") && !textBeforeCursor.includes(" ")) {
462
+ return false;
463
+ }
464
+ return true;
465
+ }
466
+ }