@travisennis/acai 0.0.1

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 (439) hide show
  1. package/.acai/acai.json +9 -0
  2. package/.acai/prompts/add-openrouter-model.md +13 -0
  3. package/.acai/prompts/project-status.md +4 -0
  4. package/.acai/prompts/update-architecture-document.md +9 -0
  5. package/.acai/rules/learned-rules.md +9 -0
  6. package/.ai/docs/available-tools.txt +3 -0
  7. package/.ai/docs/cognitive_complexity_refactoring_progress.md +65 -0
  8. package/.ai/docs/deleted_tools.md +168 -0
  9. package/.ai/docs/deleted_tools_88ced9ef.md +56 -0
  10. package/.ai/docs/image-pasting.md +46 -0
  11. package/.ai/docs/initialize-app.md +117 -0
  12. package/.ai/docs/issue-4-plan.md +44 -0
  13. package/.ai/docs/marked-renderer-debug.md +15 -0
  14. package/.ai/docs/marked-renderer-refactor-plan.md +64 -0
  15. package/.ai/docs/memory-use-cases.md +55 -0
  16. package/.ai/docs/prompt-consistency.md +31 -0
  17. package/.ai/docs/refactoring-tools.md +98 -0
  18. package/.ai/docs/system-prompt-update.md +174 -0
  19. package/.ai/docs/system_prompt.txt +210 -0
  20. package/.ai/docs/tasks.md +49 -0
  21. package/.ai/plan.md +131 -0
  22. package/.ai/prompt.md +1 -0
  23. package/.ai/scripts/fetch_models.js +27 -0
  24. package/.ai/scripts/generateSystemPrompt.ts +15 -0
  25. package/.ai/scripts/list-tools.mjs +4 -0
  26. package/.ai/scripts/p5_geometric_shapes.js +149 -0
  27. package/.husky/commit-msg +1 -0
  28. package/.husky/pre-commit +3 -0
  29. package/.husky/pre-push +1 -0
  30. package/.ignore +4 -0
  31. package/AGENTS.md +25 -0
  32. package/ARCHITECTURE.md +304 -0
  33. package/LICENSE +21 -0
  34. package/README.md +392 -0
  35. package/TODO.md +2 -0
  36. package/biome.json +61 -0
  37. package/commitlint.config.js +3 -0
  38. package/dist/cli.d.ts +19 -0
  39. package/dist/cli.js +116 -0
  40. package/dist/commands/application-log-command.d.ts +2 -0
  41. package/dist/commands/application-log-command.js +43 -0
  42. package/dist/commands/clear-command.d.ts +2 -0
  43. package/dist/commands/clear-command.js +12 -0
  44. package/dist/commands/compact-command.d.ts +2 -0
  45. package/dist/commands/compact-command.js +51 -0
  46. package/dist/commands/copy-command.d.ts +2 -0
  47. package/dist/commands/copy-command.js +51 -0
  48. package/dist/commands/edit-command.d.ts +2 -0
  49. package/dist/commands/edit-command.js +53 -0
  50. package/dist/commands/edit-prompt-command.d.ts +2 -0
  51. package/dist/commands/edit-prompt-command.js +25 -0
  52. package/dist/commands/exit-command.d.ts +2 -0
  53. package/dist/commands/exit-command.js +14 -0
  54. package/dist/commands/files-command.d.ts +2 -0
  55. package/dist/commands/files-command.js +63 -0
  56. package/dist/commands/generate-rules-command.d.ts +2 -0
  57. package/dist/commands/generate-rules-command.js +61 -0
  58. package/dist/commands/help-command.d.ts +2 -0
  59. package/dist/commands/help-command.js +19 -0
  60. package/dist/commands/init-command.d.ts +2 -0
  61. package/dist/commands/init-command.js +40 -0
  62. package/dist/commands/last-log-command.d.ts +2 -0
  63. package/dist/commands/last-log-command.js +76 -0
  64. package/dist/commands/manager.d.ts +22 -0
  65. package/dist/commands/manager.js +123 -0
  66. package/dist/commands/model-command.d.ts +2 -0
  67. package/dist/commands/model-command.js +84 -0
  68. package/dist/commands/paste-command.d.ts +2 -0
  69. package/dist/commands/paste-command.js +40 -0
  70. package/dist/commands/prompt-command.d.ts +2 -0
  71. package/dist/commands/prompt-command.js +111 -0
  72. package/dist/commands/reset-command.d.ts +2 -0
  73. package/dist/commands/reset-command.js +16 -0
  74. package/dist/commands/rules-command.d.ts +2 -0
  75. package/dist/commands/rules-command.js +68 -0
  76. package/dist/commands/save-command.d.ts +2 -0
  77. package/dist/commands/save-command.js +14 -0
  78. package/dist/commands/types.d.ts +26 -0
  79. package/dist/commands/types.js +1 -0
  80. package/dist/commands/usage-command.d.ts +2 -0
  81. package/dist/commands/usage-command.js +21 -0
  82. package/dist/config.d.ts +60 -0
  83. package/dist/config.js +193 -0
  84. package/dist/conversation-analyzer.d.ts +10 -0
  85. package/dist/conversation-analyzer.js +88 -0
  86. package/dist/dedent.d.ts +3 -0
  87. package/dist/dedent.js +38 -0
  88. package/dist/formatting.d.ts +17 -0
  89. package/dist/formatting.js +103 -0
  90. package/dist/index.d.ts +18 -0
  91. package/dist/index.js +213 -0
  92. package/dist/logger.d.ts +2 -0
  93. package/dist/logger.js +24 -0
  94. package/dist/mentions.d.ts +9 -0
  95. package/dist/mentions.js +182 -0
  96. package/dist/messages.d.ts +69 -0
  97. package/dist/messages.js +261 -0
  98. package/dist/middleware/audit-message.d.ts +5 -0
  99. package/dist/middleware/audit-message.js +95 -0
  100. package/dist/middleware/index.d.ts +2 -0
  101. package/dist/middleware/index.js +2 -0
  102. package/dist/middleware/rate-limit.d.ts +4 -0
  103. package/dist/middleware/rate-limit.js +17 -0
  104. package/dist/models/ai-config.d.ts +12 -0
  105. package/dist/models/ai-config.js +87 -0
  106. package/dist/models/anthropic-provider.d.ts +25 -0
  107. package/dist/models/anthropic-provider.js +184 -0
  108. package/dist/models/deepseek-provider.d.ts +20 -0
  109. package/dist/models/deepseek-provider.js +42 -0
  110. package/dist/models/google-provider.d.ts +19 -0
  111. package/dist/models/google-provider.js +56 -0
  112. package/dist/models/manager.d.ts +15 -0
  113. package/dist/models/manager.js +48 -0
  114. package/dist/models/openai-provider.d.ts +22 -0
  115. package/dist/models/openai-provider.js +70 -0
  116. package/dist/models/openrouter-provider.d.ts +36 -0
  117. package/dist/models/openrouter-provider.js +276 -0
  118. package/dist/models/providers.d.ts +33 -0
  119. package/dist/models/providers.js +116 -0
  120. package/dist/models/xai-provider.d.ts +20 -0
  121. package/dist/models/xai-provider.js +47 -0
  122. package/dist/parsing.d.ts +2 -0
  123. package/dist/parsing.js +18 -0
  124. package/dist/prompts/manager.d.ts +19 -0
  125. package/dist/prompts/manager.js +71 -0
  126. package/dist/prompts.d.ts +4 -0
  127. package/dist/prompts.js +158 -0
  128. package/dist/repl-prompt.d.ts +14 -0
  129. package/dist/repl-prompt.js +147 -0
  130. package/dist/repl.d.ts +27 -0
  131. package/dist/repl.js +431 -0
  132. package/dist/source/cli.d.ts +19 -0
  133. package/dist/source/cli.js +116 -0
  134. package/dist/source/commands/application-log-command.d.ts +2 -0
  135. package/dist/source/commands/application-log-command.js +43 -0
  136. package/dist/source/commands/clear-command.d.ts +2 -0
  137. package/dist/source/commands/clear-command.js +12 -0
  138. package/dist/source/commands/compact-command.d.ts +2 -0
  139. package/dist/source/commands/compact-command.js +51 -0
  140. package/dist/source/commands/copy-command.d.ts +2 -0
  141. package/dist/source/commands/copy-command.js +51 -0
  142. package/dist/source/commands/edit-command.d.ts +2 -0
  143. package/dist/source/commands/edit-command.js +53 -0
  144. package/dist/source/commands/edit-prompt-command.d.ts +2 -0
  145. package/dist/source/commands/edit-prompt-command.js +25 -0
  146. package/dist/source/commands/exit-command.d.ts +2 -0
  147. package/dist/source/commands/exit-command.js +14 -0
  148. package/dist/source/commands/files-command.d.ts +2 -0
  149. package/dist/source/commands/files-command.js +63 -0
  150. package/dist/source/commands/generate-rules-command.d.ts +2 -0
  151. package/dist/source/commands/generate-rules-command.js +61 -0
  152. package/dist/source/commands/help-command.d.ts +2 -0
  153. package/dist/source/commands/help-command.js +19 -0
  154. package/dist/source/commands/init-command.d.ts +2 -0
  155. package/dist/source/commands/init-command.js +40 -0
  156. package/dist/source/commands/last-log-command.d.ts +2 -0
  157. package/dist/source/commands/last-log-command.js +76 -0
  158. package/dist/source/commands/manager.d.ts +22 -0
  159. package/dist/source/commands/manager.js +123 -0
  160. package/dist/source/commands/model-command.d.ts +2 -0
  161. package/dist/source/commands/model-command.js +84 -0
  162. package/dist/source/commands/paste-command.d.ts +2 -0
  163. package/dist/source/commands/paste-command.js +40 -0
  164. package/dist/source/commands/prompt-command.d.ts +2 -0
  165. package/dist/source/commands/prompt-command.js +111 -0
  166. package/dist/source/commands/reset-command.d.ts +2 -0
  167. package/dist/source/commands/reset-command.js +16 -0
  168. package/dist/source/commands/rules-command.d.ts +2 -0
  169. package/dist/source/commands/rules-command.js +68 -0
  170. package/dist/source/commands/save-command.d.ts +2 -0
  171. package/dist/source/commands/save-command.js +14 -0
  172. package/dist/source/commands/types.d.ts +26 -0
  173. package/dist/source/commands/types.js +1 -0
  174. package/dist/source/commands/usage-command.d.ts +2 -0
  175. package/dist/source/commands/usage-command.js +21 -0
  176. package/dist/source/config.d.ts +60 -0
  177. package/dist/source/config.js +193 -0
  178. package/dist/source/conversation-analyzer.d.ts +10 -0
  179. package/dist/source/conversation-analyzer.js +88 -0
  180. package/dist/source/dedent.d.ts +3 -0
  181. package/dist/source/dedent.js +38 -0
  182. package/dist/source/formatting.d.ts +17 -0
  183. package/dist/source/formatting.js +103 -0
  184. package/dist/source/index.d.ts +18 -0
  185. package/dist/source/index.js +213 -0
  186. package/dist/source/logger.d.ts +2 -0
  187. package/dist/source/logger.js +24 -0
  188. package/dist/source/mentions.d.ts +9 -0
  189. package/dist/source/mentions.js +182 -0
  190. package/dist/source/messages.d.ts +69 -0
  191. package/dist/source/messages.js +261 -0
  192. package/dist/source/middleware/audit-message.d.ts +5 -0
  193. package/dist/source/middleware/audit-message.js +95 -0
  194. package/dist/source/middleware/index.d.ts +2 -0
  195. package/dist/source/middleware/index.js +2 -0
  196. package/dist/source/middleware/rate-limit.d.ts +4 -0
  197. package/dist/source/middleware/rate-limit.js +17 -0
  198. package/dist/source/models/ai-config.d.ts +12 -0
  199. package/dist/source/models/ai-config.js +87 -0
  200. package/dist/source/models/anthropic-provider.d.ts +25 -0
  201. package/dist/source/models/anthropic-provider.js +184 -0
  202. package/dist/source/models/deepseek-provider.d.ts +20 -0
  203. package/dist/source/models/deepseek-provider.js +42 -0
  204. package/dist/source/models/google-provider.d.ts +19 -0
  205. package/dist/source/models/google-provider.js +56 -0
  206. package/dist/source/models/manager.d.ts +15 -0
  207. package/dist/source/models/manager.js +48 -0
  208. package/dist/source/models/openai-provider.d.ts +22 -0
  209. package/dist/source/models/openai-provider.js +70 -0
  210. package/dist/source/models/openrouter-provider.d.ts +36 -0
  211. package/dist/source/models/openrouter-provider.js +276 -0
  212. package/dist/source/models/providers.d.ts +33 -0
  213. package/dist/source/models/providers.js +116 -0
  214. package/dist/source/models/xai-provider.d.ts +20 -0
  215. package/dist/source/models/xai-provider.js +47 -0
  216. package/dist/source/parsing.d.ts +2 -0
  217. package/dist/source/parsing.js +18 -0
  218. package/dist/source/prompts/manager.d.ts +19 -0
  219. package/dist/source/prompts/manager.js +71 -0
  220. package/dist/source/prompts.d.ts +4 -0
  221. package/dist/source/prompts.js +158 -0
  222. package/dist/source/repl-prompt.d.ts +14 -0
  223. package/dist/source/repl-prompt.js +147 -0
  224. package/dist/source/repl.d.ts +27 -0
  225. package/dist/source/repl.js +431 -0
  226. package/dist/source/terminal/formatting.d.ts +37 -0
  227. package/dist/source/terminal/formatting.js +106 -0
  228. package/dist/source/terminal/index.d.ts +94 -0
  229. package/dist/source/terminal/index.js +420 -0
  230. package/dist/source/terminal/markdown-utils.d.ts +2 -0
  231. package/dist/source/terminal/markdown-utils.js +81 -0
  232. package/dist/source/terminal/markdown.d.ts +1 -0
  233. package/dist/source/terminal/markdown.js +111 -0
  234. package/dist/source/terminal/types.d.ts +71 -0
  235. package/dist/source/terminal/types.js +1 -0
  236. package/dist/source/terminal-output.d.ts +8 -0
  237. package/dist/source/terminal-output.js +213 -0
  238. package/dist/source/terminal-output.test.d.ts +8 -0
  239. package/dist/source/terminal-output.test.js +213 -0
  240. package/dist/source/token-tracker.d.ts +14 -0
  241. package/dist/source/token-tracker.js +53 -0
  242. package/dist/source/token-utils.d.ts +7 -0
  243. package/dist/source/token-utils.js +13 -0
  244. package/dist/source/tools/agent.d.ts +17 -0
  245. package/dist/source/tools/agent.js +87 -0
  246. package/dist/source/tools/bash.d.ts +19 -0
  247. package/dist/source/tools/bash.js +294 -0
  248. package/dist/source/tools/code-interpreter.d.ts +12 -0
  249. package/dist/source/tools/code-interpreter.js +131 -0
  250. package/dist/source/tools/command-validation.d.ts +8 -0
  251. package/dist/source/tools/command-validation.js +69 -0
  252. package/dist/source/tools/delete-file.d.ts +12 -0
  253. package/dist/source/tools/delete-file.js +56 -0
  254. package/dist/source/tools/directory-tree.d.ts +12 -0
  255. package/dist/source/tools/directory-tree.js +38 -0
  256. package/dist/source/tools/edit-file.d.ts +19 -0
  257. package/dist/source/tools/edit-file.js +107 -0
  258. package/dist/source/tools/filesystem-utils.d.ts +22 -0
  259. package/dist/source/tools/filesystem-utils.js +191 -0
  260. package/dist/source/tools/git-utils.d.ts +14 -0
  261. package/dist/source/tools/git-utils.js +64 -0
  262. package/dist/source/tools/grep.d.ts +17 -0
  263. package/dist/source/tools/grep.js +138 -0
  264. package/dist/source/tools/index.d.ts +161 -0
  265. package/dist/source/tools/index.js +209 -0
  266. package/dist/source/tools/memory-read.d.ts +13 -0
  267. package/dist/source/tools/memory-read.js +135 -0
  268. package/dist/source/tools/memory-write.d.ts +12 -0
  269. package/dist/source/tools/memory-write.js +83 -0
  270. package/dist/source/tools/move-file.d.ts +13 -0
  271. package/dist/source/tools/move-file.js +44 -0
  272. package/dist/source/tools/read-file.d.ts +17 -0
  273. package/dist/source/tools/read-file.js +86 -0
  274. package/dist/source/tools/read-multiple-files.d.ts +14 -0
  275. package/dist/source/tools/read-multiple-files.js +55 -0
  276. package/dist/source/tools/save-file.d.ts +17 -0
  277. package/dist/source/tools/save-file.js +98 -0
  278. package/dist/source/tools/think.d.ts +11 -0
  279. package/dist/source/tools/think.js +45 -0
  280. package/dist/source/tools/types.d.ts +29 -0
  281. package/dist/source/tools/types.js +14 -0
  282. package/dist/source/tools/web-fetch.d.ts +47 -0
  283. package/dist/source/tools/web-fetch.js +246 -0
  284. package/dist/source/tools/web-search.d.ts +13 -0
  285. package/dist/source/tools/web-search.js +80 -0
  286. package/dist/source/utils/process.d.ts +36 -0
  287. package/dist/source/utils/process.js +75 -0
  288. package/dist/source/version.d.ts +1 -0
  289. package/dist/source/version.js +21 -0
  290. package/dist/terminal/formatting.d.ts +37 -0
  291. package/dist/terminal/formatting.js +106 -0
  292. package/dist/terminal/index.d.ts +94 -0
  293. package/dist/terminal/index.js +420 -0
  294. package/dist/terminal/markdown-utils.d.ts +2 -0
  295. package/dist/terminal/markdown-utils.js +81 -0
  296. package/dist/terminal/markdown.d.ts +1 -0
  297. package/dist/terminal/markdown.js +111 -0
  298. package/dist/terminal/types.d.ts +71 -0
  299. package/dist/terminal/types.js +1 -0
  300. package/dist/terminal-output.d.ts +8 -0
  301. package/dist/terminal-output.js +213 -0
  302. package/dist/token-tracker.d.ts +14 -0
  303. package/dist/token-tracker.js +53 -0
  304. package/dist/token-utils.d.ts +7 -0
  305. package/dist/token-utils.js +13 -0
  306. package/dist/tools/agent.d.ts +17 -0
  307. package/dist/tools/agent.js +87 -0
  308. package/dist/tools/bash.d.ts +19 -0
  309. package/dist/tools/bash.js +294 -0
  310. package/dist/tools/code-interpreter.d.ts +12 -0
  311. package/dist/tools/code-interpreter.js +131 -0
  312. package/dist/tools/command-validation.d.ts +8 -0
  313. package/dist/tools/command-validation.js +69 -0
  314. package/dist/tools/delete-file.d.ts +12 -0
  315. package/dist/tools/delete-file.js +56 -0
  316. package/dist/tools/directory-tree.d.ts +12 -0
  317. package/dist/tools/directory-tree.js +38 -0
  318. package/dist/tools/edit-file.d.ts +19 -0
  319. package/dist/tools/edit-file.js +107 -0
  320. package/dist/tools/filesystem-utils.d.ts +22 -0
  321. package/dist/tools/filesystem-utils.js +191 -0
  322. package/dist/tools/git-utils.d.ts +14 -0
  323. package/dist/tools/git-utils.js +64 -0
  324. package/dist/tools/grep.d.ts +17 -0
  325. package/dist/tools/grep.js +138 -0
  326. package/dist/tools/index.d.ts +161 -0
  327. package/dist/tools/index.js +209 -0
  328. package/dist/tools/memory-read.d.ts +13 -0
  329. package/dist/tools/memory-read.js +135 -0
  330. package/dist/tools/memory-write.d.ts +12 -0
  331. package/dist/tools/memory-write.js +83 -0
  332. package/dist/tools/move-file.d.ts +13 -0
  333. package/dist/tools/move-file.js +44 -0
  334. package/dist/tools/read-file.d.ts +17 -0
  335. package/dist/tools/read-file.js +86 -0
  336. package/dist/tools/read-multiple-files.d.ts +14 -0
  337. package/dist/tools/read-multiple-files.js +55 -0
  338. package/dist/tools/save-file.d.ts +17 -0
  339. package/dist/tools/save-file.js +98 -0
  340. package/dist/tools/think.d.ts +11 -0
  341. package/dist/tools/think.js +45 -0
  342. package/dist/tools/types.d.ts +29 -0
  343. package/dist/tools/types.js +14 -0
  344. package/dist/tools/web-fetch.d.ts +47 -0
  345. package/dist/tools/web-fetch.js +246 -0
  346. package/dist/tools/web-search.d.ts +13 -0
  347. package/dist/tools/web-search.js +80 -0
  348. package/dist/utils/process.d.ts +36 -0
  349. package/dist/utils/process.js +75 -0
  350. package/dist/version.d.ts +1 -0
  351. package/dist/version.js +21 -0
  352. package/knip.json +5 -0
  353. package/package.json +83 -0
  354. package/source/cli.ts +172 -0
  355. package/source/commands/application-log-command.ts +53 -0
  356. package/source/commands/clear-command.ts +14 -0
  357. package/source/commands/compact-command.ts +64 -0
  358. package/source/commands/copy-command.ts +55 -0
  359. package/source/commands/edit-command.ts +63 -0
  360. package/source/commands/edit-prompt-command.ts +31 -0
  361. package/source/commands/exit-command.ts +18 -0
  362. package/source/commands/files-command.ts +85 -0
  363. package/source/commands/generate-rules-command.ts +82 -0
  364. package/source/commands/help-command.ts +27 -0
  365. package/source/commands/init-command.ts +48 -0
  366. package/source/commands/last-log-command.ts +88 -0
  367. package/source/commands/manager.ts +151 -0
  368. package/source/commands/model-command.ts +123 -0
  369. package/source/commands/paste-command.ts +62 -0
  370. package/source/commands/prompt-command.ts +150 -0
  371. package/source/commands/reset-command.ts +22 -0
  372. package/source/commands/rules-command.ts +76 -0
  373. package/source/commands/save-command.ts +20 -0
  374. package/source/commands/types.ts +28 -0
  375. package/source/commands/usage-command.ts +26 -0
  376. package/source/config.ts +223 -0
  377. package/source/conversation-analyzer.ts +115 -0
  378. package/source/dedent.ts +53 -0
  379. package/source/formatting.ts +132 -0
  380. package/source/index.ts +240 -0
  381. package/source/logger.ts +29 -0
  382. package/source/mentions.ts +227 -0
  383. package/source/messages.ts +360 -0
  384. package/source/middleware/audit-message.ts +133 -0
  385. package/source/middleware/index.ts +2 -0
  386. package/source/middleware/rate-limit.ts +24 -0
  387. package/source/models/ai-config.ts +109 -0
  388. package/source/models/anthropic-provider.ts +199 -0
  389. package/source/models/deepseek-provider.ts +53 -0
  390. package/source/models/google-provider.ts +68 -0
  391. package/source/models/manager.ts +84 -0
  392. package/source/models/openai-provider.ts +81 -0
  393. package/source/models/openrouter-provider.ts +288 -0
  394. package/source/models/providers.ts +197 -0
  395. package/source/models/xai-provider.ts +59 -0
  396. package/source/parsing.ts +20 -0
  397. package/source/prompts/manager.ts +90 -0
  398. package/source/prompts.ts +172 -0
  399. package/source/repl-prompt.ts +196 -0
  400. package/source/repl.ts +572 -0
  401. package/source/terminal/formatting.ts +121 -0
  402. package/source/terminal/index.ts +518 -0
  403. package/source/terminal/markdown-utils.ts +89 -0
  404. package/source/terminal/markdown.ts +155 -0
  405. package/source/terminal/types.ts +84 -0
  406. package/source/terminal-output.test.ts +266 -0
  407. package/source/token-tracker.ts +78 -0
  408. package/source/token-utils.ts +17 -0
  409. package/source/tools/agent.ts +107 -0
  410. package/source/tools/bash.ts +367 -0
  411. package/source/tools/code-interpreter.ts +172 -0
  412. package/source/tools/command-validation.ts +81 -0
  413. package/source/tools/delete-file.ts +71 -0
  414. package/source/tools/directory-tree.ts +54 -0
  415. package/source/tools/edit-file.ts +155 -0
  416. package/source/tools/filesystem-utils.ts +265 -0
  417. package/source/tools/git-utils.ts +70 -0
  418. package/source/tools/grep.ts +184 -0
  419. package/source/tools/index.ts +278 -0
  420. package/source/tools/memory-read.ts +174 -0
  421. package/source/tools/memory-write.ts +105 -0
  422. package/source/tools/move-file.ts +59 -0
  423. package/source/tools/read-file.ts +129 -0
  424. package/source/tools/read-multiple-files.ts +80 -0
  425. package/source/tools/save-file.ts +147 -0
  426. package/source/tools/think.ts +51 -0
  427. package/source/tools/types.ts +58 -0
  428. package/source/tools/web-fetch.ts +327 -0
  429. package/source/tools/web-search.ts +101 -0
  430. package/source/utils/process.ts +121 -0
  431. package/source/version.ts +21 -0
  432. package/test/commands/copy-command.test.ts +69 -0
  433. package/test/config.test.ts +200 -0
  434. package/test/terminal/markdown-utils.test.ts +124 -0
  435. package/test/tools/bash-tool.test.ts +58 -0
  436. package/test/tools/code-interpreter.test.ts +91 -0
  437. package/test/tools/command-validation.test.ts +48 -0
  438. package/tsconfig.build.json +9 -0
  439. package/tsconfig.json +30 -0
@@ -0,0 +1,327 @@
1
+ import { tool } from "ai";
2
+ import chalk from "chalk";
3
+ import { type CheerioAPI, load } from "cheerio";
4
+ import { z } from "zod";
5
+ import { logger } from "../logger.ts";
6
+ import type { TokenCounter } from "../token-utils.ts";
7
+ import type { SendData } from "./types.ts";
8
+
9
+ export const WebFetchTool = {
10
+ name: "webFetch" as const,
11
+ };
12
+
13
+ export const createWebFetchTool = (options: {
14
+ sendData?: SendData | undefined;
15
+ tokenCounter: TokenCounter;
16
+ }) => {
17
+ const { sendData } = options;
18
+ return {
19
+ [WebFetchTool.name]: tool({
20
+ description:
21
+ "Fetches the content of a given URL. It intelligently handles HTML content by attempting to use a specialized service for cleaner extraction, falling back to local cleaning if needed. For non-HTML content (like plain text or markdown), it fetches the raw content directly. IMPORTANT: Does not retrieve binary files.",
22
+ inputSchema: z.object({
23
+ url: z.string().describe("The URL to fetch content from."),
24
+ }),
25
+ execute: async ({ url }, { toolCallId, abortSignal }) => {
26
+ try {
27
+ sendData?.({
28
+ event: "tool-init",
29
+ id: toolCallId,
30
+ data: `Reading URL: ${chalk.cyan(url)}`,
31
+ });
32
+ logger.info(`Initiating fetch for URL: ${url}`);
33
+ const result = await readUrl(url, abortSignal);
34
+ const urlContent = result.data;
35
+ const tokenCount = options.tokenCounter.count(urlContent);
36
+ sendData?.({
37
+ event: "tool-completion",
38
+ id: toolCallId,
39
+ data: `Read URL successfully (${tokenCount} tokens)`,
40
+ });
41
+ logger.info(`Successfully read URL: ${url} (${tokenCount} tokens)`);
42
+ return urlContent;
43
+ } catch (error) {
44
+ const errorMessage = (error as Error).message;
45
+ sendData?.({
46
+ event: "tool-error",
47
+ id: toolCallId,
48
+ data: `Error reading URL ${url}: ${errorMessage}`,
49
+ });
50
+ logger.error(`Error reading URL ${url}: ${errorMessage}`);
51
+ // Return the error message so the LLM knows the tool failed.
52
+ return `Failed to read URL: ${errorMessage}`;
53
+ }
54
+ },
55
+ }),
56
+ };
57
+ };
58
+
59
+ export type ContentType =
60
+ | "text/plain"
61
+ | "text/html"
62
+ | "text/markdown"
63
+ | "application/json"
64
+ | "application/xml"
65
+ | "application/pdf"
66
+ | "image/png"
67
+ | "image/jpeg"
68
+ | "image/gif"
69
+ | "image/webp"
70
+ | "image/svg+xml"
71
+ | "audio/mpeg"
72
+ | "audio/wav"
73
+ | "video/mp4"
74
+ | "video/webm"
75
+ | "application/zip"
76
+ | "application/octet-stream";
77
+
78
+ export type ReadUrlResult = { contentType: ContentType; data: string };
79
+
80
+ export async function readUrl(
81
+ url: string,
82
+ abortSignal?: AbortSignal | undefined,
83
+ ): Promise<ReadUrlResult> {
84
+ let initialResponse: Response;
85
+ try {
86
+ // Initial fetch to check content type and potentially use directly
87
+ logger.debug(`Performing initial fetch for: ${url}`);
88
+ initialResponse = await fetch(url, { signal: abortSignal });
89
+ if (!initialResponse.ok) {
90
+ throw new Error(
91
+ `HTTP error! status: ${initialResponse.status} ${initialResponse.statusText}`,
92
+ );
93
+ }
94
+ logger.debug(
95
+ `Initial fetch successful for: ${url}, Status: ${initialResponse.status}`,
96
+ );
97
+ } catch (error) {
98
+ // If the initial fetch fails entirely, rethrow
99
+ logger.error(`Initial fetch failed for ${url}: ${error}`);
100
+ throw new Error(`Error fetching initial data for ${url}: ${error}`);
101
+ }
102
+
103
+ const contentType: ContentType =
104
+ (initialResponse.headers.get("content-type") as ContentType) ??
105
+ "text/plain";
106
+ logger.debug(`Content-Type for ${url}: ${contentType}`);
107
+
108
+ // If content type is HTML, try Jina first
109
+ if (contentType.includes("text/html")) {
110
+ logger.info(`Detected HTML content for ${url}. Attempting Jina AI fetch.`);
111
+ try {
112
+ const apiKey = process.env["JINA_READER_API_KEY"];
113
+ if (!apiKey) {
114
+ logger.warn("JINA_READER_API_KEY not set. Skipping Jina fetch.");
115
+ throw new Error("Jina API key not available"); // Skip to fallback
116
+ }
117
+ const jinaReadUrl = `https://r.jina.ai/${url}`;
118
+ logger.debug(`Fetching with Jina: ${jinaReadUrl}`);
119
+ const jinaResponse = await fetch(jinaReadUrl, {
120
+ method: "GET",
121
+ headers: {
122
+ // biome-ignore lint/style/useNamingConvention: API requirement
123
+ Authorization: `Bearer ${apiKey}`,
124
+ "X-With-Generated-Alt": "true", // Optional: Ask Jina to include image descriptions
125
+ "X-With-Links-Summary": "true", // Optional: Ask Jina for a summary of links
126
+ },
127
+ signal: abortSignal,
128
+ });
129
+
130
+ if (jinaResponse.ok) {
131
+ const data = await jinaResponse.text();
132
+ logger.info(
133
+ `Successfully fetched and processed HTML URL with Jina: ${url}`,
134
+ );
135
+ return {
136
+ contentType,
137
+ data,
138
+ };
139
+ }
140
+ logger.warn(
141
+ `Jina fetch failed for ${url} with status ${jinaResponse.status}: ${jinaResponse.statusText}. Falling back to direct fetch and clean.`,
142
+ );
143
+ // Fall through to use the initialResponse if Jina fails
144
+ } catch (error) {
145
+ logger.warn(
146
+ `Error fetching from Jina for ${url}: ${(error as Error).message}. Falling back to direct fetch and clean.`,
147
+ );
148
+ // Fall through to use the initialResponse if Jina fails
149
+ }
150
+
151
+ // Fallback for HTML: Use the initial response and clean it
152
+ try {
153
+ logger.warn(
154
+ `Falling back to direct fetch and cleaning for HTML URL: ${url}`,
155
+ );
156
+ const htmlText = await initialResponse.text();
157
+ logger.debug(
158
+ `Cleaning HTML content for ${url} (length: ${htmlText.length})`,
159
+ );
160
+ const cleaner = HtmlCleaner.new(htmlText);
161
+ const processedText = cleaner.clean();
162
+ logger.info(
163
+ `Successfully cleaned HTML content for ${url} (length: ${processedText.length})`,
164
+ );
165
+ return {
166
+ contentType,
167
+ data: processedText,
168
+ };
169
+ } catch (cleanError) {
170
+ logger.error(
171
+ `Error cleaning HTML from fallback fetch for ${url}: ${cleanError}`,
172
+ );
173
+ throw new Error(
174
+ `Error cleaning HTML from fallback fetch for ${url}: ${cleanError}`,
175
+ );
176
+ }
177
+ } else {
178
+ // If not HTML, return the text directly from the initial response
179
+ logger.info(
180
+ `Fetched non-HTML content directly: ${url} (Content-Type: ${contentType})`,
181
+ );
182
+ try {
183
+ if (contentType.startsWith("image/")) {
184
+ const arrayBuffer = await initialResponse.arrayBuffer();
185
+ const base64 = Buffer.from(arrayBuffer).toString("base64");
186
+ const base64Url = `data:${contentType};base64,${base64}`;
187
+ logger.debug(
188
+ `Returning base64 image data for ${url} (length: ${base64.length})`,
189
+ );
190
+ return {
191
+ contentType,
192
+ data: base64Url,
193
+ };
194
+ }
195
+ const textContent = await initialResponse.text();
196
+ logger.debug(
197
+ `Returning raw text content for ${url} (length: ${textContent.length})`,
198
+ );
199
+ return {
200
+ contentType,
201
+ data: textContent,
202
+ };
203
+ } catch (textError) {
204
+ logger.error(`Error reading response for ${url}: ${textError}`);
205
+ throw new Error(`Error reading response for ${url}: ${textError}`);
206
+ }
207
+ }
208
+ }
209
+
210
+ export class HtmlCleaner {
211
+ static new(html: string): HtmlCleaner {
212
+ return new HtmlCleaner(html);
213
+ }
214
+
215
+ private html: string;
216
+
217
+ private constructor(html: string) {
218
+ this.html = html;
219
+ }
220
+
221
+ /**
222
+ * Cleans HTML content by removing unnecessary elements and simplifying structure
223
+ * @param {Object} [options] - Configuration options for cleaning
224
+ * @param {boolean} [options.simplify=true] - Whether to simplify HTML structure by removing redundant elements
225
+ * @param {boolean} [options.empty=true] - Whether to remove empty elements from the HTML
226
+ * @returns {string} Cleaned HTML content with removed whitespace and line breaks
227
+ */
228
+ clean(options?: { simplify?: boolean; empty?: boolean }): string {
229
+ const { simplify = true, empty = true } = options ?? {};
230
+
231
+ const $ = load(this.html);
232
+
233
+ // Remove scripts, styles, and comments
234
+ this.removeUnnecessaryElements($);
235
+
236
+ // Simplify HTML structure
237
+ if (simplify) {
238
+ this.simplifyStructure($);
239
+ }
240
+
241
+ // Remove empty elements
242
+ if (empty) {
243
+ this.removeEmptyElements($);
244
+ }
245
+
246
+ // Get cleaned HTML
247
+ return $.html()
248
+ .trim()
249
+ .replace(/^\s*[\r\n]/gm, "");
250
+ }
251
+ /**
252
+ * Removes scripts, styles, and comments from HTML
253
+ */
254
+ private removeUnnecessaryElements($: CheerioAPI): void {
255
+ // Remove all script tags
256
+ $("script").remove();
257
+
258
+ // Remove all noscript tags
259
+ $("noscript").remove();
260
+
261
+ // Remove all style tags
262
+ $("style").remove();
263
+
264
+ // Remove all link tags (external stylesheets)
265
+ $('link[rel="stylesheet"]').remove();
266
+
267
+ // Remove all preload link tags
268
+ $('link[rel="preload"]').remove();
269
+
270
+ // Remove all link tags
271
+ $("link").remove();
272
+
273
+ // Remove all forms
274
+ $("form").remove();
275
+
276
+ // Remove comments
277
+ $("*")
278
+ .contents()
279
+ .each((_, element) => {
280
+ if (element.type === "comment") {
281
+ $(element).remove();
282
+ }
283
+ });
284
+
285
+ // Remove all inline styles
286
+ $("[style]").removeAttr("style");
287
+
288
+ // Remove all class attributes
289
+ $("[class]").removeAttr("class");
290
+
291
+ // Remove all id attributes
292
+ $("[id]").removeAttr("id");
293
+ }
294
+
295
+ /**
296
+ * Simplifies HTML structure by merging redundant tags
297
+ */
298
+ private simplifyStructure($: CheerioAPI): void {
299
+ // Merge nested divs
300
+ $("div div").each((_, element) => {
301
+ const $element = $(element);
302
+ const parent = $element.parent();
303
+
304
+ if (parent.children().length === 1 && parent.get(0)?.tagName === "div") {
305
+ $element.unwrap();
306
+ }
307
+ });
308
+
309
+ // Remove redundant spans
310
+ $("span").each((_, element) => {
311
+ const $element = $(element);
312
+ if (!$element.attr() || Object.keys($element.attr() ?? {}).length === 0) {
313
+ const h = $element.html();
314
+ if (h) {
315
+ $element.replaceWith(h);
316
+ }
317
+ }
318
+ });
319
+ }
320
+
321
+ /**
322
+ * Removes empty elements from HTML
323
+ */
324
+ private removeEmptyElements($: CheerioAPI): void {
325
+ $(":empty").remove();
326
+ }
327
+ }
@@ -0,0 +1,101 @@
1
+ import { tool } from "ai";
2
+ import chalk from "chalk";
3
+ import { SafeSearchType, type SearchResult, search } from "duck-duck-scrape";
4
+ import Exa from "exa-js";
5
+ import { z } from "zod";
6
+ import type { TokenCounter } from "../token-utils.ts";
7
+ import type { SendData } from "./types.ts";
8
+
9
+ export const WebSearchTool = {
10
+ name: "webSearch" as const,
11
+ };
12
+
13
+ export const createWebSearchTool = ({
14
+ sendData,
15
+ tokenCounter,
16
+ }: {
17
+ sendData?: SendData;
18
+ tokenCounter: TokenCounter;
19
+ }) => {
20
+ return {
21
+ [WebSearchTool.name]: tool({
22
+ description:
23
+ "Searches the web and returns match documents with their title, url, and text content. The query should be formulated as a natural language question.",
24
+ inputSchema: z.object({
25
+ query: z.string().describe("The search query."),
26
+ }),
27
+ execute: async ({ query }, { toolCallId }) => {
28
+ sendData?.({
29
+ id: toolCallId,
30
+ event: "tool-init",
31
+ data: `Web search: ${chalk.cyan(query)}`,
32
+ });
33
+
34
+ const result = await performSearch(query);
35
+
36
+ const sources = result.results.map(
37
+ (source) =>
38
+ `## ${source.title}\nURL: ${source.url}\n\n${source.text}`,
39
+ );
40
+ const resultText = `# Search Results:\n\n${sources.join("\n\n")}`;
41
+ const tokenCount = tokenCounter.count(resultText);
42
+
43
+ sendData?.({
44
+ id: toolCallId,
45
+ event: "tool-completion",
46
+ data: `Found ${result.results.length} results. (${tokenCount} tokens)`,
47
+ });
48
+
49
+ return resultText;
50
+ },
51
+ }),
52
+ };
53
+ };
54
+
55
+ async function performSearch(query: string) {
56
+ // Check if EXA API key is available
57
+ const hasExaApiKey =
58
+ process.env["EXA_API_KEY"] && process.env["EXA_API_KEY"].trim() !== "";
59
+
60
+ if (hasExaApiKey) {
61
+ // Use Exa search
62
+ try {
63
+ const exa = new Exa(process.env["EXA_API_KEY"]);
64
+ const result = await exa.searchAndContents(query, {
65
+ numResults: 5,
66
+ text: true,
67
+ });
68
+ return result;
69
+ } catch (error) {
70
+ // If Exa fails, fall back to duck duck scrape
71
+ console.info("Exa search failed, falling back to DuckDuckGo:", error);
72
+ return await searchWithDuckDuckGo(query);
73
+ }
74
+ } else {
75
+ // Use DuckDuckGo search as fallback
76
+ console.info("EXA_API_KEY not set, using DuckDuckGo search");
77
+ return await searchWithDuckDuckGo(query);
78
+ }
79
+ }
80
+
81
+ async function searchWithDuckDuckGo(query: string) {
82
+ try {
83
+ const searchResults = await search(query, {
84
+ safeSearch: SafeSearchType.MODERATE,
85
+ });
86
+
87
+ // Transform duck-duck-scrape results to match Exa format
88
+ // Take only first 5 results to match Exa behavior
89
+ const results = searchResults.results
90
+ .slice(0, 5)
91
+ .map((result: SearchResult) => ({
92
+ title: result.title,
93
+ url: result.url,
94
+ text: result.description || "",
95
+ }));
96
+
97
+ return { results };
98
+ } catch (error) {
99
+ throw new Error(`Failed to perform web search: ${error}`);
100
+ }
101
+ }
@@ -0,0 +1,121 @@
1
+ import { execFile } from "node:child_process";
2
+ import { isUndefined } from "@travisennis/stdlib/typeguards";
3
+
4
+ const MS_IN_SECOND = 1000;
5
+ const SECONDS_IN_MINUTE = 60;
6
+ const DEFAULT_TIMEOUT = 2 * SECONDS_IN_MINUTE * MS_IN_SECOND;
7
+
8
+ interface ExecuteOptions {
9
+ /** Working directory where the command will be executed */
10
+ cwd?: string;
11
+ /** Timeout in milliseconds before killing the process */
12
+ timeout?: number;
13
+ /** AbortSignal to cancel the execution */
14
+ abortSignal?: AbortSignal;
15
+ /** Whether to use shell syntax (defaults to false) */
16
+ shell?: boolean;
17
+ /** Whether to throw an error on non-zero exit codes (defaults to false) */
18
+ throwOnError?: boolean;
19
+ /** Whether to include stdout/stderr in the result even when there's an error (defaults to true) */
20
+ preserveOutputOnError?: boolean;
21
+ /** Maximum buffer size in bytes (defaults to 1MB) */
22
+ maxBuffer?: number;
23
+ }
24
+
25
+ export interface ExecuteResult {
26
+ /** Standard output from the command */
27
+ stdout: string;
28
+ /** Standard error from the command */
29
+ stderr: string;
30
+ /** Exit code (0 for success, non-zero for errors) */
31
+ code: number;
32
+ /** The signal that terminated the process, if any */
33
+ signal?: NodeJS.Signals;
34
+ }
35
+
36
+ /**
37
+ * Executes a command and returns the result, providing unified error handling
38
+ *
39
+ * @param command Either a string command with arguments or an array where the first item is the command
40
+ * and the rest are arguments
41
+ * @param options Execution options
42
+ * @returns Promise resolving to an object containing stdout, stderr, and exit code
43
+ */
44
+ export function executeCommand(
45
+ command: string | [string, ...string[]],
46
+ options?: ExecuteOptions,
47
+ ): Promise<ExecuteResult> {
48
+ const {
49
+ cwd = process.cwd(),
50
+ timeout = DEFAULT_TIMEOUT,
51
+ abortSignal,
52
+ shell = false,
53
+ throwOnError = false,
54
+ preserveOutputOnError = true,
55
+ maxBuffer = 1_000_000,
56
+ } = options || {};
57
+
58
+ let cmd: string;
59
+ let args: string[];
60
+
61
+ if (Array.isArray(command)) {
62
+ [cmd, ...args] = command;
63
+ } else {
64
+ const parts = command.split(" ");
65
+ cmd = parts[0] ?? "";
66
+ args = parts.slice(1);
67
+ }
68
+
69
+ if (isUndefined(cmd) || cmd.trim() === "") {
70
+ const result: ExecuteResult = {
71
+ stdout: "",
72
+ stderr: "Missing command",
73
+ code: 1,
74
+ };
75
+ return throwOnError
76
+ ? Promise.reject(new Error("Missing command"))
77
+ : Promise.resolve(result);
78
+ }
79
+
80
+ return new Promise<ExecuteResult>((resolve, reject) => {
81
+ try {
82
+ execFile(
83
+ cmd,
84
+ args,
85
+ {
86
+ cwd,
87
+ timeout,
88
+ signal: abortSignal,
89
+ shell,
90
+ maxBuffer,
91
+ },
92
+ (error, stdout, stderr) => {
93
+ if (error) {
94
+ const errorCode = typeof error.code === "number" ? error.code : 1;
95
+ const result: ExecuteResult = {
96
+ stdout: preserveOutputOnError ? stdout : "",
97
+ stderr: preserveOutputOnError ? stderr : "",
98
+ code: errorCode,
99
+ signal: error.signal ?? undefined,
100
+ };
101
+
102
+ if (throwOnError) {
103
+ reject(Object.assign(error, { result }));
104
+ } else {
105
+ resolve(result);
106
+ }
107
+ } else {
108
+ resolve({ stdout, stderr, code: 0 });
109
+ }
110
+ },
111
+ );
112
+ } catch (error) {
113
+ const result: ExecuteResult = { stdout: "", stderr: "", code: 1 };
114
+ if (throwOnError) {
115
+ reject(error);
116
+ } else {
117
+ resolve(result);
118
+ }
119
+ }
120
+ });
121
+ }
@@ -0,0 +1,21 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "@travisennis/stdlib/desm";
3
+
4
+ export function getPackageVersion(fallback = "unavailable"): string {
5
+ try {
6
+ const pkgPath = join(import.meta.url, "..", "package.json");
7
+ const pkgRaw = readFileSync(pkgPath, "utf8");
8
+ const parsed = JSON.parse(pkgRaw) as { version?: unknown };
9
+ const v = typeof parsed.version === "string" ? parsed.version : undefined;
10
+ if (v && v.length > 0) {
11
+ return v;
12
+ }
13
+ } catch {
14
+ // ignore
15
+ }
16
+ const envV = process.env["npm_package_version"];
17
+ if (typeof envV === "string" && envV.length > 0) {
18
+ return envV;
19
+ }
20
+ return fallback;
21
+ }
@@ -0,0 +1,69 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+ import type { ModelMessage, TextPart } from "ai";
4
+ import { copyCommand } from "../../source/commands/copy-command.ts";
5
+ import type { CommandOptions } from "../../source/commands/types.ts";
6
+
7
+ function makeAssistant(text: string): ModelMessage {
8
+ return {
9
+ role: "assistant",
10
+ content: [{ type: "text", text } as TextPart],
11
+ } as ModelMessage;
12
+ }
13
+
14
+ function makeUser(text: string): ModelMessage {
15
+ return {
16
+ role: "user",
17
+ content: [{ type: "text", text } as TextPart],
18
+ } as ModelMessage;
19
+ }
20
+
21
+ describe("/copy command", () => {
22
+ it("returns info when no assistant response exists", async () => {
23
+ const outputs: string[] = [];
24
+ const options = {
25
+ terminal: {
26
+ info: (msg: string) => outputs.push(`info:${msg}`),
27
+ success: (_msg: string) => outputs.push("success"),
28
+ error: (_msg: string) => outputs.push("error"),
29
+ },
30
+ messageHistory: {
31
+ get: () => [makeUser("hello")] as ModelMessage[],
32
+ },
33
+ } as unknown as CommandOptions;
34
+
35
+ const cmd = copyCommand(options);
36
+ await cmd.execute([]);
37
+
38
+ assert(outputs.some((o) => o.startsWith("info:")));
39
+ });
40
+
41
+ it("copies last assistant text via clipboard and reports success", async (_t) => {
42
+ // Temporarily replace writeToClipboard by monkey-patching module function via dynamic import cache
43
+ const outputs: string[] = [];
44
+ const options = {
45
+ terminal: {
46
+ info: (msg: string) => outputs.push(`info:${msg}`),
47
+ success: (msg: string) => outputs.push(`success:${msg}`),
48
+ error: (msg: string) => outputs.push(`error:${msg}`),
49
+ },
50
+ messageHistory: {
51
+ get: () =>
52
+ [makeUser("hello"), makeAssistant("world")] as ModelMessage[],
53
+ },
54
+ } as unknown as CommandOptions;
55
+
56
+ // We cannot easily mock child_process spawn without a mocking framework; this test focuses on flow.
57
+ // Execute should attempt clipboard; since environment may not have tools, we only assert it didn't crash synchronously.
58
+ const cmd = copyCommand(options);
59
+
60
+ try {
61
+ await cmd.execute([]);
62
+ } catch {
63
+ // Ignore runtime env clipboard errors
64
+ }
65
+
66
+ // Should either success or error, but not be silent
67
+ assert(outputs.length > 0);
68
+ });
69
+ });