@travisennis/acai 0.0.5 → 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 (311) hide show
  1. package/README.md +4 -2
  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/cli.d.ts +2 -0
  9. package/dist/cli.d.ts.map +1 -1
  10. package/dist/cli.js +27 -33
  11. package/dist/commands/add-directory-command.d.ts +3 -0
  12. package/dist/commands/add-directory-command.d.ts.map +1 -0
  13. package/dist/commands/add-directory-command.js +85 -0
  14. package/dist/commands/application-log-command.d.ts.map +1 -1
  15. package/dist/commands/application-log-command.js +34 -0
  16. package/dist/commands/clear-command.d.ts.map +1 -1
  17. package/dist/commands/clear-command.js +8 -0
  18. package/dist/commands/compact-command.d.ts.map +1 -1
  19. package/dist/commands/compact-command.js +15 -2
  20. package/dist/commands/context-command.d.ts +3 -0
  21. package/dist/commands/context-command.d.ts.map +1 -0
  22. package/dist/commands/context-command.js +183 -0
  23. package/dist/commands/copy-command.d.ts.map +1 -1
  24. package/dist/commands/copy-command.js +28 -0
  25. package/dist/commands/edit-command.d.ts.map +1 -1
  26. package/dist/commands/edit-command.js +33 -0
  27. package/dist/commands/edit-prompt-command.d.ts.map +1 -1
  28. package/dist/commands/edit-prompt-command.js +28 -0
  29. package/dist/commands/exit-command.d.ts.map +1 -1
  30. package/dist/commands/exit-command.js +20 -0
  31. package/dist/commands/files-command.d.ts.map +1 -1
  32. package/dist/commands/files-command.js +57 -0
  33. package/dist/commands/generate-rules-command.d.ts.map +1 -1
  34. package/dist/commands/generate-rules-command.js +311 -1
  35. package/dist/commands/handoff-command.d.ts +3 -0
  36. package/dist/commands/handoff-command.d.ts.map +1 -0
  37. package/dist/commands/handoff-command.js +202 -0
  38. package/dist/commands/health-command.d.ts.map +1 -1
  39. package/dist/commands/health-command.js +119 -2
  40. package/dist/commands/help-command.d.ts.map +1 -1
  41. package/dist/commands/help-command.js +28 -0
  42. package/dist/commands/history-command.d.ts +3 -0
  43. package/dist/commands/history-command.d.ts.map +1 -0
  44. package/dist/commands/history-command.js +534 -0
  45. package/dist/commands/init-command.d.ts +1 -1
  46. package/dist/commands/init-command.d.ts.map +1 -1
  47. package/dist/commands/init-command.js +55 -18
  48. package/dist/commands/last-log-command.d.ts.map +1 -1
  49. package/dist/commands/last-log-command.js +27 -0
  50. package/dist/commands/list-directories-command.d.ts +3 -0
  51. package/dist/commands/list-directories-command.d.ts.map +1 -0
  52. package/dist/commands/list-directories-command.js +48 -0
  53. package/dist/commands/list-tools-command.d.ts.map +1 -1
  54. package/dist/commands/list-tools-command.js +66 -3
  55. package/dist/commands/manager.d.ts +15 -3
  56. package/dist/commands/manager.d.ts.map +1 -1
  57. package/dist/commands/manager.js +86 -26
  58. package/dist/commands/model-command.d.ts +22 -0
  59. package/dist/commands/model-command.d.ts.map +1 -1
  60. package/dist/commands/model-command.js +256 -0
  61. package/dist/commands/paste-command.d.ts.map +1 -1
  62. package/dist/commands/paste-command.js +92 -0
  63. package/dist/commands/pickup-command.d.ts +3 -0
  64. package/dist/commands/pickup-command.d.ts.map +1 -0
  65. package/dist/commands/pickup-command.js +161 -0
  66. package/dist/commands/prompt-command.d.ts +1 -1
  67. package/dist/commands/prompt-command.d.ts.map +1 -1
  68. package/dist/commands/prompt-command.js +117 -2
  69. package/dist/commands/remove-directory-command.d.ts +3 -0
  70. package/dist/commands/remove-directory-command.d.ts.map +1 -0
  71. package/dist/commands/remove-directory-command.js +87 -0
  72. package/dist/commands/reset-command.d.ts +1 -1
  73. package/dist/commands/reset-command.d.ts.map +1 -1
  74. package/dist/commands/reset-command.js +13 -2
  75. package/dist/commands/rules-command.d.ts.map +1 -1
  76. package/dist/commands/rules-command.js +65 -0
  77. package/dist/commands/save-command.d.ts.map +1 -1
  78. package/dist/commands/save-command.js +12 -0
  79. package/dist/commands/shell-command.d.ts.map +1 -1
  80. package/dist/commands/shell-command.js +68 -0
  81. package/dist/commands/types.d.ts +9 -4
  82. package/dist/commands/types.d.ts.map +1 -1
  83. package/dist/commands/usage-command.d.ts.map +1 -1
  84. package/dist/commands/usage-command.js +22 -0
  85. package/dist/config.d.ts +6 -7
  86. package/dist/config.d.ts.map +1 -1
  87. package/dist/config.js +23 -29
  88. package/dist/formatting.d.ts +108 -0
  89. package/dist/formatting.d.ts.map +1 -1
  90. package/dist/formatting.js +147 -0
  91. package/dist/index.d.ts +7 -2
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.js +140 -38
  94. package/dist/logger.d.ts.map +1 -1
  95. package/dist/logger.js +47 -18
  96. package/dist/mentions.d.ts +2 -1
  97. package/dist/mentions.d.ts.map +1 -1
  98. package/dist/mentions.js +16 -1
  99. package/dist/messages.d.ts +8 -0
  100. package/dist/messages.d.ts.map +1 -1
  101. package/dist/messages.js +56 -19
  102. package/dist/middleware/cache.d.ts +3 -0
  103. package/dist/middleware/cache.d.ts.map +1 -0
  104. package/dist/middleware/cache.js +53 -0
  105. package/dist/middleware/index.d.ts +1 -0
  106. package/dist/middleware/index.d.ts.map +1 -1
  107. package/dist/middleware/index.js +1 -0
  108. package/dist/models/ai-config.d.ts +4 -2
  109. package/dist/models/ai-config.d.ts.map +1 -1
  110. package/dist/models/ai-config.js +12 -2
  111. package/dist/models/anthropic-provider.d.ts.map +1 -1
  112. package/dist/models/anthropic-provider.js +3 -60
  113. package/dist/models/manager.d.ts +2 -1
  114. package/dist/models/manager.d.ts.map +1 -1
  115. package/dist/models/manager.js +26 -2
  116. package/dist/models/openrouter-provider.d.ts +7 -14
  117. package/dist/models/openrouter-provider.d.ts.map +1 -1
  118. package/dist/models/openrouter-provider.js +114 -169
  119. package/dist/models/providers.d.ts +1 -1
  120. package/dist/models/providers.d.ts.map +1 -1
  121. package/dist/prompts.d.ts +1 -0
  122. package/dist/prompts.d.ts.map +1 -1
  123. package/dist/prompts.js +53 -4
  124. package/dist/repl/display-tool-messages.d.ts +1 -1
  125. package/dist/repl/display-tool-messages.d.ts.map +1 -1
  126. package/dist/repl/display-tool-messages.js +47 -44
  127. package/dist/repl/get-prompt-header.d.ts.map +1 -1
  128. package/dist/repl/get-prompt-header.js +1 -30
  129. package/dist/repl/project-status-line.d.ts +2 -0
  130. package/dist/repl/project-status-line.d.ts.map +1 -0
  131. package/dist/repl/project-status-line.js +31 -0
  132. package/dist/repl/prompt.d.ts +21 -0
  133. package/dist/repl/prompt.d.ts.map +1 -0
  134. package/dist/{repl-prompt.js → repl/prompt.js} +119 -22
  135. package/dist/repl/tool-call-repair.d.ts.map +1 -1
  136. package/dist/repl/tool-call-repair.js +8 -4
  137. package/dist/repl-new.d.ts +53 -0
  138. package/dist/repl-new.d.ts.map +1 -0
  139. package/dist/repl-new.js +374 -0
  140. package/dist/repl.d.ts +3 -5
  141. package/dist/repl.d.ts.map +1 -1
  142. package/dist/repl.js +74 -166
  143. package/dist/terminal/checkbox-prompt.d.ts.map +1 -1
  144. package/dist/terminal/checkbox-prompt.js +10 -4
  145. package/dist/terminal/index.d.ts +7 -0
  146. package/dist/terminal/index.d.ts.map +1 -1
  147. package/dist/terminal/index.js +94 -0
  148. package/dist/terminal/input-prompt.d.ts +2 -1
  149. package/dist/terminal/input-prompt.d.ts.map +1 -1
  150. package/dist/terminal/markdown.js +3 -0
  151. package/dist/terminal/search-prompt.d.ts.map +1 -1
  152. package/dist/terminal/search-prompt.js +11 -10
  153. package/dist/terminal/select-prompt.d.ts +2 -2
  154. package/dist/terminal/select-prompt.d.ts.map +1 -1
  155. package/dist/terminal/select-prompt.js +47 -39
  156. package/dist/tokens/threshold.d.ts +35 -0
  157. package/dist/tokens/threshold.d.ts.map +1 -0
  158. package/dist/tokens/threshold.js +85 -0
  159. package/dist/tools/advanced-edit-file.d.ts +69 -0
  160. package/dist/tools/advanced-edit-file.d.ts.map +1 -0
  161. package/dist/tools/advanced-edit-file.js +281 -0
  162. package/dist/tools/agent.d.ts +16 -5
  163. package/dist/tools/agent.d.ts.map +1 -1
  164. package/dist/tools/agent.js +71 -58
  165. package/dist/tools/bash-utils.d.ts +1 -1
  166. package/dist/tools/bash-utils.d.ts.map +1 -1
  167. package/dist/tools/bash-utils.js +14 -6
  168. package/dist/tools/bash.d.ts +21 -12
  169. package/dist/tools/bash.d.ts.map +1 -1
  170. package/dist/tools/bash.js +88 -135
  171. package/dist/tools/code-interpreter.d.ts +21 -9
  172. package/dist/tools/code-interpreter.d.ts.map +1 -1
  173. package/dist/tools/code-interpreter.js +138 -137
  174. package/dist/tools/delete-file.d.ts +17 -10
  175. package/dist/tools/delete-file.d.ts.map +1 -1
  176. package/dist/tools/delete-file.js +51 -95
  177. package/dist/tools/directory-tree.d.ts +17 -6
  178. package/dist/tools/directory-tree.d.ts.map +1 -1
  179. package/dist/tools/directory-tree.js +47 -49
  180. package/dist/tools/dynamic-tool-loader.d.ts +18 -8
  181. package/dist/tools/dynamic-tool-loader.d.ts.map +1 -1
  182. package/dist/tools/dynamic-tool-loader.js +121 -129
  183. package/dist/tools/dynamic-tool-parser.d.ts +1 -0
  184. package/dist/tools/dynamic-tool-parser.d.ts.map +1 -1
  185. package/dist/tools/dynamic-tool-parser.js +1 -0
  186. package/dist/tools/edit-file.d.ts +35 -15
  187. package/dist/tools/edit-file.d.ts.map +1 -1
  188. package/dist/tools/edit-file.js +112 -112
  189. package/dist/tools/filesystem-utils.d.ts +2 -1
  190. package/dist/tools/filesystem-utils.d.ts.map +1 -1
  191. package/dist/tools/filesystem-utils.js +31 -17
  192. package/dist/tools/glob.d.ts +36 -0
  193. package/dist/tools/glob.d.ts.map +1 -0
  194. package/dist/tools/glob.js +143 -0
  195. package/dist/tools/grep.d.ts +73 -12
  196. package/dist/tools/grep.d.ts.map +1 -1
  197. package/dist/tools/grep.js +413 -168
  198. package/dist/tools/index.d.ts +204 -124
  199. package/dist/tools/index.d.ts.map +1 -1
  200. package/dist/tools/index.js +242 -135
  201. package/dist/tools/llm-edit-fixer.d.ts +25 -0
  202. package/dist/tools/llm-edit-fixer.d.ts.map +1 -0
  203. package/dist/tools/llm-edit-fixer.js +150 -0
  204. package/dist/tools/move-file.d.ts +19 -7
  205. package/dist/tools/move-file.d.ts.map +1 -1
  206. package/dist/tools/move-file.js +40 -33
  207. package/dist/tools/read-file.d.ts +47 -9
  208. package/dist/tools/read-file.d.ts.map +1 -1
  209. package/dist/tools/read-file.js +74 -69
  210. package/dist/tools/read-multiple-files.d.ts +17 -6
  211. package/dist/tools/read-multiple-files.d.ts.map +1 -1
  212. package/dist/tools/read-multiple-files.js +76 -73
  213. package/dist/tools/save-file.d.ts +45 -12
  214. package/dist/tools/save-file.d.ts.map +1 -1
  215. package/dist/tools/save-file.js +58 -101
  216. package/dist/tools/think.d.ts +15 -7
  217. package/dist/tools/think.d.ts.map +1 -1
  218. package/dist/tools/think.js +30 -22
  219. package/dist/tools/types.d.ts +4 -10
  220. package/dist/tools/types.d.ts.map +1 -1
  221. package/dist/tools/types.js +9 -0
  222. package/dist/tools/utils.d.ts +14 -0
  223. package/dist/tools/utils.d.ts.map +1 -0
  224. package/dist/tools/utils.js +16 -0
  225. package/dist/tools/web-fetch.d.ts +11 -4
  226. package/dist/tools/web-fetch.d.ts.map +1 -1
  227. package/dist/tools/web-fetch.js +39 -38
  228. package/dist/tools/web-search.d.ts +15 -6
  229. package/dist/tools/web-search.d.ts.map +1 -1
  230. package/dist/tools/web-search.js +50 -32
  231. package/dist/tui/autocomplete.d.ts +44 -0
  232. package/dist/tui/autocomplete.d.ts.map +1 -0
  233. package/dist/tui/autocomplete.js +466 -0
  234. package/dist/tui/components/assistant-message.d.ts +18 -0
  235. package/dist/tui/components/assistant-message.d.ts.map +1 -0
  236. package/dist/tui/components/assistant-message.js +29 -0
  237. package/dist/tui/components/editor.d.ts +51 -0
  238. package/dist/tui/components/editor.d.ts.map +1 -0
  239. package/dist/tui/components/editor.js +758 -0
  240. package/dist/tui/components/footer.d.ts +24 -0
  241. package/dist/tui/components/footer.d.ts.map +1 -0
  242. package/dist/tui/components/footer.js +197 -0
  243. package/dist/tui/components/input.d.ts +14 -0
  244. package/dist/tui/components/input.d.ts.map +1 -0
  245. package/dist/tui/components/input.js +122 -0
  246. package/dist/tui/components/loader.d.ts +19 -0
  247. package/dist/tui/components/loader.d.ts.map +1 -0
  248. package/dist/tui/components/loader.js +45 -0
  249. package/dist/tui/components/markdown.d.ts +103 -0
  250. package/dist/tui/components/markdown.d.ts.map +1 -0
  251. package/dist/tui/components/markdown.js +533 -0
  252. package/dist/tui/components/modal.d.ts +40 -0
  253. package/dist/tui/components/modal.d.ts.map +1 -0
  254. package/dist/tui/components/modal.js +292 -0
  255. package/dist/tui/components/prompt-status.d.ts +16 -0
  256. package/dist/tui/components/prompt-status.d.ts.map +1 -0
  257. package/dist/tui/components/prompt-status.js +21 -0
  258. package/dist/tui/components/select-list.d.ts +22 -0
  259. package/dist/tui/components/select-list.d.ts.map +1 -0
  260. package/dist/tui/components/select-list.js +143 -0
  261. package/dist/tui/components/spacer.d.ts +16 -0
  262. package/dist/tui/components/spacer.d.ts.map +1 -0
  263. package/dist/tui/components/spacer.js +27 -0
  264. package/dist/tui/components/text.d.ts +26 -0
  265. package/dist/tui/components/text.d.ts.map +1 -0
  266. package/dist/tui/components/text.js +143 -0
  267. package/dist/tui/components/thinking-block.d.ts +14 -0
  268. package/dist/tui/components/thinking-block.d.ts.map +1 -0
  269. package/dist/tui/components/thinking-block.js +30 -0
  270. package/dist/tui/components/tool-execution.d.ts +17 -0
  271. package/dist/tui/components/tool-execution.d.ts.map +1 -0
  272. package/dist/tui/components/tool-execution.js +153 -0
  273. package/dist/tui/components/user-message.d.ts +9 -0
  274. package/dist/tui/components/user-message.d.ts.map +1 -0
  275. package/dist/tui/components/user-message.js +21 -0
  276. package/dist/tui/components/welcome.d.ts +6 -0
  277. package/dist/tui/components/welcome.d.ts.map +1 -0
  278. package/dist/tui/components/welcome.js +30 -0
  279. package/dist/tui/index.d.ts +14 -0
  280. package/dist/tui/index.d.ts.map +1 -0
  281. package/dist/tui/index.js +18 -0
  282. package/dist/tui/terminal.d.ts +37 -0
  283. package/dist/tui/terminal.d.ts.map +1 -0
  284. package/dist/tui/terminal.js +104 -0
  285. package/dist/tui/tui.d.ts +67 -0
  286. package/dist/tui/tui.d.ts.map +1 -0
  287. package/dist/tui/tui.js +184 -0
  288. package/dist/tui/utils.d.ts +19 -0
  289. package/dist/tui/utils.d.ts.map +1 -0
  290. package/dist/tui/utils.js +31 -0
  291. package/dist/utils/generators.d.ts +3 -0
  292. package/dist/utils/generators.d.ts.map +1 -0
  293. package/dist/utils/generators.js +25 -0
  294. package/dist/utils/iterables.d.ts +2 -0
  295. package/dist/utils/iterables.d.ts.map +1 -0
  296. package/dist/utils/iterables.js +6 -0
  297. package/package.json +16 -16
  298. package/dist/conversation-analyzer.d.ts +0 -11
  299. package/dist/conversation-analyzer.d.ts.map +0 -1
  300. package/dist/conversation-analyzer.js +0 -88
  301. package/dist/repl-prompt.d.ts +0 -15
  302. package/dist/repl-prompt.d.ts.map +0 -1
  303. package/dist/tokens/manage-output.d.ts +0 -34
  304. package/dist/tokens/manage-output.d.ts.map +0 -1
  305. package/dist/tokens/manage-output.js +0 -44
  306. package/dist/tool-executor.d.ts +0 -28
  307. package/dist/tool-executor.d.ts.map +0 -1
  308. package/dist/tool-executor.js +0 -74
  309. package/dist/tools/file-editing-utils.d.ts +0 -2
  310. package/dist/tools/file-editing-utils.d.ts.map +0 -1
  311. package/dist/tools/file-editing-utils.js +0 -135
@@ -0,0 +1,534 @@
1
+ import { writeFile } from "node:fs/promises";
2
+ import { generateText } from "ai";
3
+ import { MessageHistory } from "../messages.js";
4
+ import { getTerminalSize } from "../terminal/formatting.js";
5
+ import { select } from "../terminal/select-prompt.js";
6
+ import style from "../terminal/style.js";
7
+ import { Container, Input, Spacer, Text } from "../tui/index.js";
8
+ async function exportConversation(history, terminal) {
9
+ const sanitizedTitle = history.title
10
+ .replace(/[^a-zA-Z0-9\s-_]/g, "")
11
+ .replace(/\s+/g, "-")
12
+ .toLowerCase();
13
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, -5);
14
+ const filename = `${sanitizedTitle}_${timestamp}.md`;
15
+ const markdownContent = generateMarkdown(history);
16
+ try {
17
+ await writeFile(filename, markdownContent);
18
+ terminal.info(`Conversation exported to: ${filename}`);
19
+ }
20
+ catch (error) {
21
+ terminal.error(`Failed to export conversation: ${error}`);
22
+ throw error;
23
+ }
24
+ }
25
+ function generateMarkdown(history) {
26
+ const lines = [];
27
+ // Header
28
+ lines.push(`# ${history.title}`);
29
+ lines.push("");
30
+ lines.push("## Conversation Metadata");
31
+ lines.push(`- **Session ID**: ${history.sessionId}`);
32
+ lines.push(`- **Model**: ${history.modelId}`);
33
+ lines.push(`- **Created**: ${history.createdAt.toISOString()}`);
34
+ lines.push(`- **Last Updated**: ${history.updatedAt.toISOString()}`);
35
+ lines.push(`- **Total Messages**: ${history.messages.length}`);
36
+ lines.push("");
37
+ // Messages
38
+ lines.push("## Conversation History");
39
+ lines.push("");
40
+ history.messages.forEach((message, index) => {
41
+ const role = message.role.toUpperCase();
42
+ lines.push(`### ${role} (Message ${index + 1})`);
43
+ lines.push("");
44
+ if (Array.isArray(message.content)) {
45
+ message.content.forEach((part) => {
46
+ if (part.type === "text" && part.text?.trim()) {
47
+ lines.push(part.text);
48
+ lines.push("");
49
+ }
50
+ else if (part.type === "tool-call") {
51
+ lines.push(`**Tool Call**: ${part.toolName}`);
52
+ lines.push(`**Call ID**: ${part.toolCallId}`);
53
+ lines.push("**Input**:");
54
+ lines.push("```json");
55
+ lines.push(JSON.stringify(part.input, null, 2));
56
+ lines.push("```");
57
+ lines.push("");
58
+ }
59
+ else if (part.type === "tool-result") {
60
+ lines.push(`**Tool Result**: ${part.toolName}`);
61
+ lines.push(`**Call ID**: ${part.toolCallId}`);
62
+ lines.push("**Output**:");
63
+ if (typeof part.output === "object" &&
64
+ part.output !== null &&
65
+ "type" in part.output &&
66
+ part.output.type === "text" &&
67
+ "text" in part.output) {
68
+ lines.push("```");
69
+ lines.push(String(part.output.text));
70
+ lines.push("```");
71
+ }
72
+ else {
73
+ lines.push("```json");
74
+ lines.push(JSON.stringify(part.output, null, 2));
75
+ lines.push("```");
76
+ }
77
+ lines.push("");
78
+ }
79
+ else if (part.type === "tool-error") {
80
+ lines.push(`**Tool Error**: ${part.toolName}`);
81
+ lines.push(`**Call ID**: ${part.toolCallId}`);
82
+ lines.push("**Error**:");
83
+ lines.push("```");
84
+ lines.push(String(part.output));
85
+ lines.push("```");
86
+ lines.push("");
87
+ }
88
+ });
89
+ }
90
+ else if (typeof message.content === "string" && message.content.trim()) {
91
+ lines.push(message.content);
92
+ lines.push("");
93
+ }
94
+ lines.push("---");
95
+ lines.push("");
96
+ });
97
+ return lines.join("\n");
98
+ }
99
+ async function summarizeConversation(history, terminal, modelManager, tokenTracker) {
100
+ const systemPrompt = `You are an expert at summarizing conversations between a coding assistant and a user. Your task is to provide a clear, concise summary of the conversation that captures:
101
+
102
+ 1. The main topic and objectives
103
+ 2. Key decisions and solutions discussed
104
+ 3. Tools and techniques used
105
+ 4. Overall outcome or current status
106
+
107
+ Keep the summary focused and informative, around 3-5 paragraphs. Use plain text without markdown formatting.`;
108
+ const conversationText = history.messages
109
+ .map((message) => {
110
+ const role = message.role.toUpperCase();
111
+ let content = "";
112
+ if (Array.isArray(message.content)) {
113
+ content = message.content
114
+ .filter((part) => part.type === "text" && part.text?.trim())
115
+ .map((part) => part.text)
116
+ .join("\n");
117
+ }
118
+ else if (typeof message.content === "string") {
119
+ content = message.content;
120
+ }
121
+ return `${role}: ${content}`;
122
+ })
123
+ .filter((text) => text?.trim())
124
+ .join("\n\n");
125
+ try {
126
+ const { text, usage } = await generateText({
127
+ model: modelManager.getModel("conversation-summarizer"),
128
+ system: systemPrompt,
129
+ prompt: `Please summarize this conversation:\n\n${conversationText}`,
130
+ });
131
+ tokenTracker.trackUsage("conversation-summarizer", usage);
132
+ terminal.writeln(`Summary of "${history.title}":`);
133
+ terminal.lineBreak();
134
+ terminal.display(text);
135
+ terminal.lineBreak();
136
+ }
137
+ catch (error) {
138
+ terminal.error(`Failed to generate summary: ${error}`);
139
+ throw error;
140
+ }
141
+ }
142
+ export const historyCommand = ({ messageHistory, terminal, config, modelManager, tokenTracker, }) => {
143
+ return {
144
+ command: "/history",
145
+ description: "Browse and manage previous conversations.",
146
+ getSubCommands: () => Promise.resolve([]),
147
+ execute: async () => {
148
+ const appDir = config.app;
149
+ const messageHistoryDir = await appDir.ensurePath("message-history");
150
+ // Load all histories (use a large number to get all)
151
+ const histories = await MessageHistory.load(messageHistoryDir, 1000);
152
+ if (histories.length === 0) {
153
+ terminal.info("No previous conversations found.");
154
+ return "continue";
155
+ }
156
+ try {
157
+ // Step 1: Select conversation
158
+ const conversationChoice = await select({
159
+ message: "Select a conversation:",
160
+ choices: histories.map((h, index) => ({
161
+ name: `${index + 1}: ${h.title} (${h.updatedAt.toLocaleString()})`,
162
+ value: index,
163
+ description: `${h.messages.length} messages`,
164
+ })),
165
+ pageSize: 15,
166
+ });
167
+ const selectedHistory = histories.at(conversationChoice);
168
+ if (!selectedHistory) {
169
+ terminal.error("Selected history index out of bounds.");
170
+ return "continue";
171
+ }
172
+ // Step 2: Select action
173
+ const actionChoice = await select({
174
+ message: `What would you like to do with "${selectedHistory.title}"?`,
175
+ choices: [
176
+ {
177
+ name: "Resume - Continue this conversation",
178
+ value: "resume",
179
+ },
180
+ {
181
+ name: "Export - Save as markdown file",
182
+ value: "export",
183
+ },
184
+ {
185
+ name: "Summarize - Generate AI summary of conversation",
186
+ value: "summarize",
187
+ },
188
+ ],
189
+ pageSize: 5,
190
+ });
191
+ switch (actionChoice) {
192
+ case "resume":
193
+ messageHistory.restore(selectedHistory);
194
+ terminal.info(`Resuming conversation: ${selectedHistory.title}`);
195
+ terminal.setTitle(selectedHistory.title || `acai: ${process.cwd()}`);
196
+ break;
197
+ case "export":
198
+ await exportConversation(selectedHistory, terminal);
199
+ break;
200
+ case "summarize":
201
+ await summarizeConversation(selectedHistory, terminal, modelManager, tokenTracker);
202
+ break;
203
+ }
204
+ }
205
+ catch (error) {
206
+ // Handle Ctrl-C cancellation
207
+ if (error instanceof Error &&
208
+ "isCanceled" in error &&
209
+ error.isCanceled === true) {
210
+ terminal.info("Operation cancelled.");
211
+ return "continue";
212
+ }
213
+ // Re-throw other errors
214
+ throw error;
215
+ }
216
+ return "continue";
217
+ },
218
+ async handle(_args, { tui, container, inputContainer, editor, }) {
219
+ const appDir = config.app;
220
+ const messageHistoryDir = await appDir.ensurePath("message-history");
221
+ // Load all histories (use a large number to get all)
222
+ const histories = await MessageHistory.load(messageHistoryDir, 1000);
223
+ if (histories.length === 0) {
224
+ container.addChild(new Text(style.yellow("No previous conversations found."), 0, 1));
225
+ tui.requestRender();
226
+ editor.setText("");
227
+ return "continue";
228
+ }
229
+ // Create conversation selector
230
+ const conversationSelector = new ConversationSelectorComponent(histories, async (conversation) => {
231
+ // Handle conversation selection
232
+ const actionSelector = new ActionSelectorComponent(conversation, async (action) => {
233
+ // Handle action selection
234
+ switch (action) {
235
+ case "resume":
236
+ messageHistory.restore(conversation);
237
+ container.addChild(new Text(style.green(`Resuming conversation: ${conversation.title}`), 0, 1));
238
+ terminal.setTitle(conversation.title || `acai: ${process.cwd()}`);
239
+ break;
240
+ case "export":
241
+ try {
242
+ await exportConversation(conversation, terminal);
243
+ container.addChild(new Text(style.green("Conversation exported successfully"), 1, 0));
244
+ }
245
+ catch (error) {
246
+ container.addChild(new Text(style.red(`Failed to export conversation: ${error}`), 1, 0));
247
+ }
248
+ break;
249
+ case "summarize":
250
+ try {
251
+ await summarizeConversation(conversation, terminal, modelManager, tokenTracker);
252
+ container.addChild(new Text(style.green("Conversation summarized"), 0, 1));
253
+ }
254
+ catch (error) {
255
+ container.addChild(new Text(style.red(`Failed to summarize conversation: ${error}`), 1, 0));
256
+ }
257
+ break;
258
+ }
259
+ // Hide selectors and show editor again
260
+ hideHistorySelectors(inputContainer, editor, tui);
261
+ tui.requestRender();
262
+ }, () => {
263
+ // Cancel action selection - go back to conversation selection
264
+ hideHistorySelectors(inputContainer, editor, tui);
265
+ inputContainer.addChild(conversationSelector);
266
+ tui.setFocus(conversationSelector);
267
+ tui.requestRender();
268
+ });
269
+ // Replace conversation selector with action selector
270
+ inputContainer.clear();
271
+ inputContainer.addChild(actionSelector);
272
+ tui.setFocus(actionSelector);
273
+ tui.requestRender();
274
+ }, () => {
275
+ // Cancel conversation selection - just hide selector
276
+ hideHistorySelectors(inputContainer, editor, tui);
277
+ tui.requestRender();
278
+ });
279
+ // Replace editor with conversation selector
280
+ inputContainer.clear();
281
+ inputContainer.addChild(conversationSelector);
282
+ tui.setFocus(conversationSelector);
283
+ tui.requestRender();
284
+ return "continue";
285
+ },
286
+ };
287
+ };
288
+ function hideHistorySelectors(editorContainer, editor, tui) {
289
+ // Replace selector with editor in the container
290
+ editorContainer.clear();
291
+ editorContainer.addChild(editor);
292
+ tui.setFocus(editor);
293
+ }
294
+ /**
295
+ * Component that renders a conversation selector with search
296
+ */
297
+ class ConversationSelectorComponent extends Container {
298
+ searchInput;
299
+ listContainer;
300
+ allConversations = [];
301
+ filteredConversations = [];
302
+ selectedIndex = 0;
303
+ onSelectCallback;
304
+ onCancelCallback;
305
+ constructor(conversations, onSelect, onCancel) {
306
+ super();
307
+ this.onSelectCallback = onSelect;
308
+ this.onCancelCallback = onCancel;
309
+ // Load all conversations
310
+ this.allConversations = conversations;
311
+ this.filteredConversations = conversations;
312
+ const { columns } = getTerminalSize();
313
+ // Add top border
314
+ this.addChild(new Text(style.blue("─".repeat(columns)), 0, 0));
315
+ this.addChild(new Spacer(1));
316
+ // Create search input
317
+ this.searchInput = new Input();
318
+ // Note: setPlaceholder not available on Input component
319
+ this.searchInput.onSubmit = () => {
320
+ // Enter on search input selects the first filtered item
321
+ if (this.filteredConversations[this.selectedIndex]) {
322
+ this.handleSelect(this.filteredConversations[this.selectedIndex]);
323
+ }
324
+ };
325
+ this.addChild(this.searchInput);
326
+ this.addChild(new Spacer(1));
327
+ // Create list container
328
+ this.listContainer = new Container();
329
+ this.addChild(this.listContainer);
330
+ this.addChild(new Spacer(1));
331
+ // Add bottom border
332
+ this.addChild(new Text(style.blue("─".repeat(columns)), 0, 0));
333
+ // Initial render
334
+ this.updateList();
335
+ }
336
+ filterConversations(query) {
337
+ if (!query.trim()) {
338
+ this.filteredConversations = this.allConversations;
339
+ }
340
+ else {
341
+ const searchTokens = query
342
+ .toLowerCase()
343
+ .split(/\s+/)
344
+ .filter((t) => t);
345
+ this.filteredConversations = this.allConversations.filter((conversation) => {
346
+ const searchText = `${conversation.title} ${conversation.sessionId}`.toLowerCase();
347
+ return searchTokens.every((token) => searchText.includes(token));
348
+ });
349
+ }
350
+ this.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredConversations.length - 1));
351
+ this.updateList();
352
+ }
353
+ updateList() {
354
+ this.listContainer.clear();
355
+ const maxVisible = 10;
356
+ const startIndex = Math.max(0, Math.min(this.selectedIndex - Math.floor(maxVisible / 2), this.filteredConversations.length - maxVisible));
357
+ const endIndex = Math.min(startIndex + maxVisible, this.filteredConversations.length);
358
+ // Show visible slice of filtered conversations
359
+ for (let i = startIndex; i < endIndex; i++) {
360
+ const conversation = this.filteredConversations[i];
361
+ if (!conversation)
362
+ continue;
363
+ const isSelected = i === this.selectedIndex;
364
+ let line = "";
365
+ if (isSelected) {
366
+ const prefix = style.blue("→ ");
367
+ const title = conversation.title;
368
+ const date = conversation.updatedAt.toLocaleString();
369
+ const messages = conversation.messages.length;
370
+ line = `${prefix + style.blue(title)} ${style.gray(`(${date}) - ${messages} messages`)}`;
371
+ }
372
+ else {
373
+ const title = ` ${conversation.title}`;
374
+ const date = conversation.updatedAt.toLocaleString();
375
+ const messages = conversation.messages.length;
376
+ line = `${title} ${style.gray(`(${date}) - ${messages} messages`)}`;
377
+ }
378
+ this.listContainer.addChild(new Text(line, 0, 0));
379
+ }
380
+ // Add scroll indicator if needed
381
+ if (startIndex > 0 || endIndex < this.filteredConversations.length) {
382
+ const scrollInfo = style.gray(` (${this.selectedIndex + 1}/${this.filteredConversations.length})`);
383
+ this.listContainer.addChild(new Text(scrollInfo, 0, 0));
384
+ }
385
+ // Show "no results" if empty
386
+ if (this.filteredConversations.length === 0) {
387
+ this.listContainer.addChild(new Text(style.gray(" No matching conversations"), 0, 0));
388
+ }
389
+ }
390
+ handleInput(keyData) {
391
+ // Up arrow
392
+ if (keyData === "\x1b[A") {
393
+ this.selectedIndex = Math.max(0, this.selectedIndex - 1);
394
+ this.updateList();
395
+ }
396
+ // Down arrow
397
+ else if (keyData === "\x1b[B") {
398
+ this.selectedIndex = Math.min(this.filteredConversations.length - 1, this.selectedIndex + 1);
399
+ this.updateList();
400
+ }
401
+ // Enter
402
+ else if (keyData === "\r") {
403
+ const selectedConversation = this.filteredConversations[this.selectedIndex];
404
+ if (selectedConversation) {
405
+ this.handleSelect(selectedConversation);
406
+ }
407
+ }
408
+ // Escape
409
+ else if (keyData === "\x1b") {
410
+ this.onCancelCallback();
411
+ }
412
+ // Pass everything else to search input
413
+ else {
414
+ this.searchInput.handleInput(keyData);
415
+ this.filterConversations(this.searchInput.getValue());
416
+ }
417
+ }
418
+ handleSelect(conversation) {
419
+ this.onSelectCallback(conversation);
420
+ }
421
+ getSearchInput() {
422
+ return this.searchInput;
423
+ }
424
+ }
425
+ /**
426
+ * Component that renders an action selector for a conversation
427
+ */
428
+ class ActionSelectorComponent extends Container {
429
+ selectedIndex = 0;
430
+ conversationTitle;
431
+ onSelectCallback;
432
+ onCancelCallback;
433
+ constructor(conversation, onSelect, onCancel) {
434
+ super();
435
+ this.conversationTitle = conversation.title;
436
+ this.onSelectCallback = onSelect;
437
+ this.onCancelCallback = onCancel;
438
+ const { columns } = getTerminalSize();
439
+ // Add top border
440
+ this.addChild(new Text(style.blue("─".repeat(columns)), 0, 0));
441
+ this.addChild(new Spacer(1));
442
+ // Show conversation title
443
+ this.addChild(new Text(`Selected: ${conversation.title}`, 0, 0));
444
+ this.addChild(new Spacer(1));
445
+ // Create action list
446
+ this.addChild(new Text("Choose an action:", 0, 0));
447
+ this.addChild(new Spacer(1));
448
+ const actions = [
449
+ { name: "Resume - Continue this conversation", value: "resume" },
450
+ { name: "Export - Save as markdown file", value: "export" },
451
+ {
452
+ name: "Summarize - Generate AI summary of conversation",
453
+ value: "summarize",
454
+ },
455
+ ];
456
+ actions.forEach((action, index) => {
457
+ const isSelected = index === this.selectedIndex;
458
+ let line = "";
459
+ if (isSelected) {
460
+ line = `${style.blue("→ ") + style.blue(action.name)}`;
461
+ }
462
+ else {
463
+ line = ` ${action.name}`;
464
+ }
465
+ this.addChild(new Text(line, 0, 0));
466
+ });
467
+ this.addChild(new Spacer(1));
468
+ // Add bottom border
469
+ this.addChild(new Text(style.blue("─".repeat(columns)), 0, 0));
470
+ }
471
+ handleInput(keyData) {
472
+ // Up arrow
473
+ if (keyData === "\x1b[A") {
474
+ this.selectedIndex = Math.max(0, this.selectedIndex - 1);
475
+ this.updateList();
476
+ }
477
+ // Down arrow
478
+ else if (keyData === "\x1b[B") {
479
+ this.selectedIndex = Math.min(2, this.selectedIndex + 1);
480
+ this.updateList();
481
+ }
482
+ // Enter
483
+ else if (keyData === "\r") {
484
+ const actions = ["resume", "export", "summarize"];
485
+ const selectedAction = actions[this.selectedIndex];
486
+ if (selectedAction) {
487
+ this.handleSelect(selectedAction);
488
+ }
489
+ }
490
+ // Escape
491
+ else if (keyData === "\x1b") {
492
+ this.onCancelCallback();
493
+ }
494
+ }
495
+ updateList() {
496
+ // Clear and rebuild the action list
497
+ this.clear();
498
+ const { columns } = getTerminalSize();
499
+ // Add top border
500
+ this.addChild(new Text(style.blue("─".repeat(columns)), 0, 0));
501
+ this.addChild(new Spacer(1));
502
+ // Show conversation title
503
+ this.addChild(new Text(`Selected: ${this.conversationTitle}`, 0, 0));
504
+ this.addChild(new Spacer(1));
505
+ // Create action list
506
+ this.addChild(new Text("Choose an action:", 0, 0));
507
+ this.addChild(new Spacer(1));
508
+ const actions = [
509
+ { name: "Resume - Continue this conversation", value: "resume" },
510
+ { name: "Export - Save as markdown file", value: "export" },
511
+ {
512
+ name: "Summarize - Generate AI summary of conversation",
513
+ value: "summarize",
514
+ },
515
+ ];
516
+ actions.forEach((action, index) => {
517
+ const isSelected = index === this.selectedIndex;
518
+ let line = "";
519
+ if (isSelected) {
520
+ line = `${style.blue("→ ") + style.blue(action.name)}`;
521
+ }
522
+ else {
523
+ line = ` ${action.name}`;
524
+ }
525
+ this.addChild(new Text(line, 0, 0));
526
+ });
527
+ this.addChild(new Spacer(1));
528
+ // Add bottom border
529
+ this.addChild(new Text(style.blue("─".repeat(columns)), 0, 0));
530
+ }
531
+ handleSelect(action) {
532
+ this.onSelectCallback(action);
533
+ }
534
+ }
@@ -1,3 +1,3 @@
1
1
  import type { CommandOptions, ReplCommand } from "./types.ts";
2
- export declare const initCommand: ({ terminal, modelManager, tokenCounter, toolEvents, }: CommandOptions) => ReplCommand;
2
+ export declare const initCommand: ({ terminal, modelManager, tokenCounter, workspace, }: CommandOptions) => ReplCommand;
3
3
  //# sourceMappingURL=init-command.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"init-command.d.ts","sourceRoot":"","sources":["../../source/commands/init-command.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9D,eAAO,MAAM,WAAW,GAAI,uDAKzB,cAAc,KAAG,WAoCnB,CAAC"}
1
+ {"version":3,"file":"init-command.d.ts","sourceRoot":"","sources":["../../source/commands/init-command.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAqB9D,eAAO,MAAM,WAAW,GAAI,sDAKzB,cAAc,KAAG,WAyEnB,CAAC"}
@@ -1,8 +1,28 @@
1
1
  import { platform } from "node:os";
2
2
  import { stepCountIs, streamText } from "ai";
3
+ import style from "../terminal/style.js";
3
4
  import { inGitDirectory } from "../tools/git-utils.js";
4
- import { initTools } from "../tools/index.js";
5
- export const initCommand = ({ terminal, modelManager, tokenCounter, toolEvents, }) => {
5
+ import { initCliTools } from "../tools/index.js";
6
+ import { Markdown, Spacer, Text } from "../tui/index.js";
7
+ const initPrompt = `Please analyze this codebase and create a AGENTS.md file containing:
8
+ 1. An overview of the project including how the project is structured and the tech stack used.
9
+ 2. Build/lint/test commands - especially for running a single test
10
+ 3. Directions on how to run the app
11
+ 4. Custom tools defined in ./.acai/tools
12
+ 3. Code style guidelines including imports, formatting, types, naming conventions, error handling, etc.
13
+ 4. Commit format (does the repository use Conventional Commits?)
14
+ 5. Branch strategy
15
+ 6. PR requirements
16
+
17
+ The file you create will be given to agentic coding agents (such as yourself) that operate in this repository. Make it about 50 lines long.
18
+
19
+ If there's already a AGENTS.md, improve it.
20
+ If there are Cursor rules (in .cursor/rules/ or .cursorrules), Copilot rules (in .github/copilot-instructions.md or .github/instructions/), or Windsurf rules (in .windsurf/rules), make sure to include them.
21
+
22
+ Your current working directory is ${process.cwd()}
23
+ Is directory a git repo: ${(await inGitDirectory()) ? "Yes" : "No"}
24
+ Platform: ${platform()}`;
25
+ export const initCommand = ({ terminal, modelManager, tokenCounter, workspace, }) => {
6
26
  return {
7
27
  command: "/init",
8
28
  description: "Creates the AGENTS.md file.",
@@ -11,29 +31,46 @@ export const initCommand = ({ terminal, modelManager, tokenCounter, toolEvents,
11
31
  const result = streamText({
12
32
  model: modelManager.getModel("init-project"),
13
33
  temperature: 0.5,
14
- prompt: `Please analyze this codebase and create a AGENTS.md file containing:
15
- 1. Build/lint/test commands - especially for running a single test
16
- 2. Code style guidelines including imports, formatting, types, naming conventions, error handling, etc.
17
-
18
- The file you create will be given to agentic coding agents (such as yourself) that operate in this repository. Make it about 20 lines long.
19
-
20
- If there's already a AGENTS.md, improve it.
21
- If there are Cursor rules (in .cursor/rules/ or .cursorrules), Copilot rules (in .github/copilot-instructions.md), or Windsurf rules (in .windsurf/rules), make sure to include them.
22
-
23
- Your current working directory is ${process.cwd()}
24
- Is directory a git repo: ${(await inGitDirectory()) ? "Yes" : "No"}
25
- Platform: ${platform()}`,
34
+ prompt: initPrompt,
26
35
  stopWhen: stepCountIs(40),
27
- tools: await initTools({
28
- terminal,
36
+ tools: (await initCliTools({
29
37
  tokenCounter,
30
- events: toolEvents,
31
- }),
38
+ workspace,
39
+ })).toolDefs,
32
40
  });
33
41
  for await (const text of result.textStream) {
34
42
  terminal.write(text);
35
43
  }
36
44
  return "continue";
37
45
  },
46
+ async handle(_args, { tui, container, editor, }) {
47
+ container.addChild(new Text("Initializing project and creating AGENTS.md...", 1, 1));
48
+ tui.requestRender();
49
+ const result = streamText({
50
+ model: modelManager.getModel("init-project"),
51
+ temperature: 0.5,
52
+ prompt: initPrompt,
53
+ stopWhen: stepCountIs(40),
54
+ tools: (await initCliTools({
55
+ tokenCounter,
56
+ workspace,
57
+ })).toolDefs,
58
+ });
59
+ container.addChild(new Spacer(1));
60
+ let output = "";
61
+ const t = new Markdown(output, undefined, undefined, undefined, 1, 0);
62
+ container.addChild(t);
63
+ for await (const text of result.textStream) {
64
+ output += text;
65
+ // Update the display with the latest output
66
+ t.setText(output);
67
+ tui.requestRender();
68
+ }
69
+ container.addChild(new Spacer(1));
70
+ container.addChild(new Text(style.green("AGENTS.md file created successfully"), 1, 0));
71
+ tui.requestRender();
72
+ editor.setText("");
73
+ return "continue";
74
+ },
38
75
  };
39
76
  };
@@ -1 +1 @@
1
- {"version":3,"file":"last-log-command.d.ts","sourceRoot":"","sources":["../../source/commands/last-log-command.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAyC9D,eAAO,MAAM,cAAc,GAAI,cAAc,cAAc,KAAG,WAqC7D,CAAC"}
1
+ {"version":3,"file":"last-log-command.d.ts","sourceRoot":"","sources":["../../source/commands/last-log-command.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAyC9D,eAAO,MAAM,cAAc,GAAI,cAAc,cAAc,KAAG,WA8F7D,CAAC"}
@@ -3,6 +3,7 @@ import { join } from "node:path";
3
3
  import { config } from "../config.js";
4
4
  import { editor } from "../terminal/editor-prompt.js";
5
5
  import style from "../terminal/style.js";
6
+ import { Text } from "../tui/index.js";
6
7
  import { glob } from "../utils/glob.js";
7
8
  const isoDateRegex = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)/;
8
9
  // Function to find the most recent log file
@@ -67,5 +68,31 @@ export const lastLogCommand = ({ terminal }) => {
67
68
  return "continue";
68
69
  }
69
70
  },
71
+ async handle(_args, { tui, container, editor, }) {
72
+ const logDir = config.app.ensurePathSync("audit");
73
+ const mostRecentLog = await findMostRecentLog(logDir);
74
+ if (!mostRecentLog) {
75
+ container.addChild(new Text(style.red(`No REPL audit logs found in '${logDir}'.`), 0, 1));
76
+ tui.requestRender();
77
+ editor.setText("");
78
+ return "continue";
79
+ }
80
+ try {
81
+ const content = await readFile(mostRecentLog, { encoding: "utf8" });
82
+ // For TUI mode, we'll just display a message since we can't use the editor prompt
83
+ container.addChild(new Text(`Viewing most recent log: ${style.blue(mostRecentLog)}`, 1, 0));
84
+ container.addChild(new Text(`Content length: ${content.length} characters`, 2, 0));
85
+ container.addChild(new Text(style.dim("Note: Full log viewing not available in TUI mode"), 3, 0));
86
+ tui.requestRender();
87
+ editor.setText("");
88
+ return "continue";
89
+ }
90
+ catch (error) {
91
+ container.addChild(new Text(style.red(`Error reading log file ${mostRecentLog}: ${error}`), 1, 0));
92
+ tui.requestRender();
93
+ editor.setText("");
94
+ return "continue";
95
+ }
96
+ },
70
97
  };
71
98
  };