@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
@@ -1,4 +1,7 @@
1
1
  import { formatModelInfo, getModelsByCategory, isValidModel, modelRegistry, models, providers, } from "../models/providers.js";
2
+ import { getTerminalSize } from "../terminal/formatting.js";
3
+ import style from "../terminal/style.js";
4
+ import { Container, Input, Spacer, Text, } from "../tui/index.js";
2
5
  export function modelCommand(options) {
3
6
  const { terminal, modelManager } = options;
4
7
  function switchModel(newModelName) {
@@ -80,5 +83,258 @@ export function modelCommand(options) {
80
83
  terminal.info("Usage: /model [provider:model-name|category|provider]");
81
84
  return "continue";
82
85
  },
86
+ async handle(args, { tui, container, inputContainer, editor, }) {
87
+ const arg = args.join(" ").trim();
88
+ const modelConfig = modelManager.getModelMetadata("repl");
89
+ // No args - display current model and list available models by category
90
+ if (!arg) {
91
+ // Create model selector with current model
92
+ const modelSelector = new ModelSelectorComponent(modelConfig.id, (model) => {
93
+ // Apply the selected model
94
+ modelManager.setModel("repl", model);
95
+ // Hide selector and show editor again
96
+ hideModelSelector(inputContainer, editor, tui);
97
+ tui.requestRender();
98
+ }, () => {
99
+ // Just hide the selector
100
+ hideModelSelector(inputContainer, editor, tui);
101
+ tui.requestRender();
102
+ });
103
+ // Replace editor with selector
104
+ inputContainer.clear();
105
+ inputContainer.addChild(modelSelector);
106
+ tui.setFocus(modelSelector);
107
+ tui.requestRender();
108
+ return "continue";
109
+ }
110
+ // Switch to a specific model
111
+ if (isValidModel(arg)) {
112
+ try {
113
+ // Get current and new model configs
114
+ const currentModelConfig = modelManager.getModelMetadata("repl");
115
+ const newModelConfig = modelRegistry[arg];
116
+ if (!newModelConfig) {
117
+ container.addChild(new Text(style.red(`Model configuration not found for: ${arg}`), 1, 0));
118
+ tui.requestRender();
119
+ editor.setText("");
120
+ return "continue";
121
+ }
122
+ // Check for capability differences
123
+ if (currentModelConfig.supportsToolCalling &&
124
+ !newModelConfig.supportsToolCalling) {
125
+ container.addChild(new Text(style.yellow("The new model doesn't support tool calling, which may limit functionality."), 1, 0));
126
+ }
127
+ if (currentModelConfig.supportsReasoning &&
128
+ !newModelConfig.supportsReasoning) {
129
+ container.addChild(new Text(style.yellow("The new model doesn't support reasoning, which may change response quality."), 2, 0));
130
+ }
131
+ // Update model in ModelManager
132
+ modelManager.setModel("repl", arg);
133
+ // container.addChild(
134
+ // new Text(style.green(`Model set to ${arg}.`), 3, 0),
135
+ // );
136
+ tui.requestRender();
137
+ editor.setText("");
138
+ return "continue";
139
+ }
140
+ catch (error) {
141
+ container.addChild(new Text(style.red(`Failed to switch model: ${error.message}`), 0, 0));
142
+ tui.requestRender();
143
+ editor.setText("");
144
+ return "continue";
145
+ }
146
+ }
147
+ // // Display models by category
148
+ // const categories = ["fast", "balanced", "powerful"];
149
+ // if (categories.includes(arg)) {
150
+ // container.addChild(
151
+ // new Text(
152
+ // `${arg.charAt(0).toUpperCase() + arg.slice(1)} models:`,
153
+ // 1,
154
+ // 0,
155
+ // ),
156
+ // );
157
+ // let lineIndex = 2;
158
+ // for (const model of getModelsByCategory(
159
+ // arg as "fast" | "balanced" | "powerful",
160
+ // )) {
161
+ // container.addChild(new Text(formatModelInfo(model), lineIndex, 0));
162
+ // lineIndex++;
163
+ // }
164
+ // tui.requestRender();
165
+ // editor.setText("");
166
+ // return "continue";
167
+ // }
168
+ // // Display models by provider
169
+ // if (providers.includes(arg as ModelProvider)) {
170
+ // container.addChild(new Text(`Models from ${arg}:`, 0, 1));
171
+ // let lineIndex = 2;
172
+ // for (const model of Object.values(modelRegistry).filter(
173
+ // (m) => m.provider === arg,
174
+ // )) {
175
+ // container.addChild(new Text(formatModelInfo(model), lineIndex, 0));
176
+ // lineIndex++;
177
+ // }
178
+ // tui.requestRender();
179
+ // editor.setText("");
180
+ // return "continue";
181
+ // }
182
+ // // Invalid model name
183
+ // container.addChild(
184
+ // new Text(style.red(`Invalid model name or category: ${arg}`), 0, 1),
185
+ // );
186
+ // container.addChild(
187
+ // new Text(
188
+ // style.dim("Usage: /model [provider:model-name|category|provider]"),
189
+ // 2,
190
+ // 0,
191
+ // ),
192
+ // );
193
+ tui.requestRender();
194
+ editor.setText("");
195
+ return "continue";
196
+ },
83
197
  };
84
198
  }
199
+ function hideModelSelector(editorContainer, editor, tui) {
200
+ // Replace selector with editor in the container
201
+ editorContainer.clear();
202
+ editorContainer.addChild(editor);
203
+ tui.setFocus(editor);
204
+ }
205
+ /**
206
+ * Component that renders a model selector with search
207
+ */
208
+ export class ModelSelectorComponent extends Container {
209
+ searchInput;
210
+ listContainer;
211
+ allModels = [];
212
+ filteredModels = [];
213
+ selectedIndex = 0;
214
+ currentModel;
215
+ onSelectCallback;
216
+ onCancelCallback;
217
+ constructor(currentModel, onSelect, onCancel) {
218
+ super();
219
+ this.currentModel = currentModel;
220
+ this.onSelectCallback = onSelect;
221
+ this.onCancelCallback = onCancel;
222
+ // Load all models
223
+ this.loadModels();
224
+ const { columns } = getTerminalSize();
225
+ // Add top border
226
+ this.addChild(new Text(style.blue("─".repeat(columns)), 0, 0));
227
+ this.addChild(new Spacer(1));
228
+ // Create search input
229
+ this.searchInput = new Input();
230
+ this.searchInput.onSubmit = () => {
231
+ // Enter on search input selects the first filtered item
232
+ if (this.filteredModels[this.selectedIndex]) {
233
+ this.handleSelect(this.filteredModels[this.selectedIndex]);
234
+ }
235
+ };
236
+ this.addChild(this.searchInput);
237
+ this.addChild(new Spacer(1));
238
+ // Create list container
239
+ this.listContainer = new Container();
240
+ this.addChild(this.listContainer);
241
+ this.addChild(new Spacer(1));
242
+ // Add bottom border
243
+ this.addChild(new Text(style.blue("─".repeat(columns)), 0, 0));
244
+ // Initial render
245
+ this.updateList();
246
+ }
247
+ loadModels() {
248
+ this.allModels = models;
249
+ this.filteredModels = models;
250
+ }
251
+ filterModels(query) {
252
+ if (!query.trim()) {
253
+ this.filteredModels = this.allModels;
254
+ }
255
+ else {
256
+ const searchTokens = query
257
+ .toLowerCase()
258
+ .split(/\s+/)
259
+ .filter((t) => t);
260
+ this.filteredModels = this.allModels.filter((model) => {
261
+ const searchText = `${model}`.toLowerCase();
262
+ return searchTokens.every((token) => searchText.includes(token));
263
+ });
264
+ }
265
+ this.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));
266
+ this.updateList();
267
+ }
268
+ updateList() {
269
+ this.listContainer.clear();
270
+ const maxVisible = 10;
271
+ const startIndex = Math.max(0, Math.min(this.selectedIndex - Math.floor(maxVisible / 2), this.filteredModels.length - maxVisible));
272
+ const endIndex = Math.min(startIndex + maxVisible, this.filteredModels.length);
273
+ // Show visible slice of filtered models
274
+ for (let i = startIndex; i < endIndex; i++) {
275
+ const item = this.filteredModels[i];
276
+ if (!item)
277
+ continue;
278
+ const isSelected = i === this.selectedIndex;
279
+ const isCurrent = this.currentModel === item;
280
+ let line = "";
281
+ if (isSelected) {
282
+ const prefix = style.blue("→ ");
283
+ const modelText = `${item}`;
284
+ // const providerBadge = style.gray(`[${item.provider}]`);
285
+ const checkmark = isCurrent ? style.green(" ✓") : "";
286
+ line = `${prefix + style.blue(modelText)} ${checkmark}`;
287
+ }
288
+ else {
289
+ const modelText = ` ${item}`;
290
+ // const providerBadge = style.gray(`[${item.provider}]`);
291
+ const checkmark = isCurrent ? style.green(" ✓") : "";
292
+ line = `${modelText} ${checkmark}`;
293
+ }
294
+ this.listContainer.addChild(new Text(line, 0, 0));
295
+ }
296
+ // Add scroll indicator if needed
297
+ if (startIndex > 0 || endIndex < this.filteredModels.length) {
298
+ const scrollInfo = style.gray(` (${this.selectedIndex + 1}/${this.filteredModels.length})`);
299
+ this.listContainer.addChild(new Text(scrollInfo, 0, 0));
300
+ }
301
+ // Show "no results" if empty
302
+ if (this.filteredModels.length === 0) {
303
+ this.listContainer.addChild(new Text(style.gray(" No matching models"), 0, 0));
304
+ }
305
+ }
306
+ handleInput(keyData) {
307
+ // Up arrow
308
+ if (keyData === "\x1b[A") {
309
+ this.selectedIndex = Math.max(0, this.selectedIndex - 1);
310
+ this.updateList();
311
+ }
312
+ // Down arrow
313
+ else if (keyData === "\x1b[B") {
314
+ this.selectedIndex = Math.min(this.filteredModels.length - 1, this.selectedIndex + 1);
315
+ this.updateList();
316
+ }
317
+ // Enter
318
+ else if (keyData === "\r") {
319
+ const selectedModel = this.filteredModels[this.selectedIndex];
320
+ if (selectedModel) {
321
+ this.handleSelect(selectedModel);
322
+ }
323
+ }
324
+ // Escape
325
+ else if (keyData === "\x1b") {
326
+ this.onCancelCallback();
327
+ }
328
+ // Pass everything else to search input
329
+ else {
330
+ this.searchInput.handleInput(keyData);
331
+ this.filterModels(this.searchInput.getValue());
332
+ }
333
+ }
334
+ handleSelect(model) {
335
+ this.onSelectCallback(model);
336
+ }
337
+ getSearchInput() {
338
+ return this.searchInput;
339
+ }
340
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"paste-command.d.ts","sourceRoot":"","sources":["../../source/commands/paste-command.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAsH9D,eAAO,MAAM,YAAY,GAAI,0DAK1B,cAAc,KAAG,WAqHnB,CAAC"}
1
+ {"version":3,"file":"paste-command.d.ts","sourceRoot":"","sources":["../../source/commands/paste-command.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAsH9D,eAAO,MAAM,YAAY,GAAI,0DAK1B,cAAc,KAAG,WAqRnB,CAAC"}
@@ -1,6 +1,8 @@
1
1
  import Clipboard from "@crosscopy/clipboard";
2
2
  import { formatBlock } from "../formatting.js";
3
3
  import { logger } from "../logger.js";
4
+ import style from "../terminal/style.js";
5
+ import { Text } from "../tui/index.js";
4
6
  function extractBase64Content(dataUrl) {
5
7
  return dataUrl.replace(/^data:.*?;base64,/, "");
6
8
  }
@@ -181,5 +183,95 @@ export const pasteCommand = ({ terminal, modelManager, promptManager, tokenCount
181
183
  }
182
184
  return "continue";
183
185
  },
186
+ async handle(_args, { tui, container, editor, }) {
187
+ try {
188
+ if (Clipboard.hasImage()) {
189
+ const base64DataUrl = await Clipboard.getImageBase64();
190
+ // Validate the base64 data
191
+ if (!isValidBase64(base64DataUrl)) {
192
+ container.addChild(new Text(style.red("Invalid base64 data in clipboard. The image data may be corrupted."), 1, 0));
193
+ tui.requestRender();
194
+ editor.setText("");
195
+ return "continue";
196
+ }
197
+ // Extract MIME type with better error handling and actual image format detection
198
+ let mimeType;
199
+ try {
200
+ // First, try to get MIME type from data URL
201
+ const dataUrlMimeType = extractMimeTypeFromDataUrl(base64DataUrl);
202
+ // Then, detect actual image format from base64 content
203
+ const base64Content = extractBase64Content(base64DataUrl);
204
+ const detectedFormat = detectImageFormatFromBase64(base64Content);
205
+ // Use detected format if available, otherwise fall back to data URL MIME type
206
+ if (detectedFormat !== "unknown") {
207
+ mimeType = detectedFormat;
208
+ // Log if there's a mismatch between data URL and actual format
209
+ if (dataUrlMimeType !== detectedFormat) {
210
+ logger.warn(`Clipboard library reported ${dataUrlMimeType} but actual image format is ${detectedFormat}. Using detected format.`);
211
+ }
212
+ }
213
+ else {
214
+ mimeType = dataUrlMimeType;
215
+ logger.warn(`Could not detect image format, using data URL MIME type: ${mimeType}`);
216
+ }
217
+ }
218
+ catch (error) {
219
+ logger.warn(`Failed to extract MIME type from clipboard image: ${error}`);
220
+ mimeType = "image/png";
221
+ }
222
+ // Ensure the data URL format is correct
223
+ if (!base64DataUrl.startsWith(`data:${mimeType};base64,`)) {
224
+ // Fix malformed data URLs
225
+ const base64Content = base64DataUrl.replace(/^data:.*?;base64,/, "");
226
+ const correctedDataUrl = `data:${mimeType};base64,${base64Content}`;
227
+ // Final validation
228
+ if (!isValidBase64(correctedDataUrl)) {
229
+ container.addChild(new Text(style.red("Failed to correct base64 data format. The image data may be corrupted."), 1, 0));
230
+ tui.requestRender();
231
+ editor.setText("");
232
+ return "continue";
233
+ }
234
+ promptManager.addContext({
235
+ type: "image",
236
+ image: correctedDataUrl,
237
+ mediaType: mimeType,
238
+ });
239
+ }
240
+ else {
241
+ promptManager.addContext({
242
+ type: "image",
243
+ image: base64DataUrl,
244
+ mediaType: mimeType,
245
+ });
246
+ }
247
+ container.addChild(new Text(style.green("Image from clipboard will be added to your next prompt."), 1, 0));
248
+ tui.requestRender();
249
+ editor.setText("");
250
+ return "continue";
251
+ }
252
+ const clipboardContent = await Clipboard.getText();
253
+ if (!clipboardContent || clipboardContent.trim() === "") {
254
+ container.addChild(new Text(style.yellow("Clipboard is empty."), 0, 1));
255
+ tui.requestRender();
256
+ editor.setText("");
257
+ return "continue";
258
+ }
259
+ const content = formatBlock(clipboardContent, "clipboard", modelManager.getModelMetadata("repl").promptFormat);
260
+ promptManager.addContext(content);
261
+ const tokenCount = tokenCounter.count(content);
262
+ container.addChild(new Text(style.green(`Clipboard content will be added to your next prompt. (${tokenCount} tokens)`), 1, 0));
263
+ tui.requestRender();
264
+ editor.setText("");
265
+ return "continue";
266
+ }
267
+ catch (error) {
268
+ const message = error instanceof Error ? error.message : String(error);
269
+ container.addChild(new Text(style.red(`Error processing clipboard content: ${message}`), 1, 0));
270
+ logger.error(error, "Paste command error:");
271
+ tui.requestRender();
272
+ editor.setText("");
273
+ return "continue";
274
+ }
275
+ },
184
276
  };
185
277
  };
@@ -0,0 +1,3 @@
1
+ import type { CommandOptions, ReplCommand } from "./types.ts";
2
+ export declare const pickupCommand: (options: CommandOptions) => ReplCommand;
3
+ //# sourceMappingURL=pickup-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pickup-command.d.ts","sourceRoot":"","sources":["../../source/commands/pickup-command.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9D,eAAO,MAAM,aAAa,GAAI,SAAS,cAAc,KAAG,WAoMvD,CAAC"}
@@ -0,0 +1,161 @@
1
+ import { readdir, readFile } from "node:fs/promises";
2
+ import { basename } from "node:path";
3
+ import { createUserMessage } from "../messages.js";
4
+ import style from "../terminal/style.js";
5
+ import { Text } from "../tui/index.js";
6
+ export const pickupCommand = (options) => {
7
+ return {
8
+ command: "/pickup",
9
+ description: "Loads a handoff file into a new session to continue previous work. Usage: /pickup <filename>",
10
+ getSubCommands: async () => {
11
+ const getHandoffFileNames = async () => {
12
+ try {
13
+ const dirents = await readdir(options.workspace.primaryDir, {
14
+ withFileTypes: true,
15
+ });
16
+ return dirents
17
+ .filter((dirent) => dirent.isFile() && dirent.name.match(/^handoff-.*\.md$/))
18
+ .map((dirent) => basename(dirent.name, ".md"));
19
+ }
20
+ catch (error) {
21
+ if (error.code === "ENOENT") {
22
+ return []; // Directory doesn't exist, return empty array
23
+ }
24
+ options.terminal.error(`Error reading handoff files: ${error}`);
25
+ return []; // Return empty on other errors too, but log them
26
+ }
27
+ };
28
+ return await getHandoffFileNames();
29
+ },
30
+ execute: async (args) => {
31
+ const { terminal, messageHistory, modelManager } = options;
32
+ // Validate that filename is provided
33
+ const filename = args.join(" ").trim();
34
+ if (!filename) {
35
+ const availableFiles = await getAvailableHandoffFiles(options);
36
+ if (availableFiles.length === 0) {
37
+ terminal.error("No handoff files found. Create a handoff file first using /handoff <purpose>");
38
+ }
39
+ else {
40
+ terminal.error("Please specify a handoff file to load.");
41
+ terminal.info("Available handoff files:");
42
+ availableFiles.forEach((file) => {
43
+ terminal.info(` • ${file}.md`);
44
+ });
45
+ }
46
+ return "continue";
47
+ }
48
+ // Ensure filename has .md extension for file operations
49
+ const filenameWithExt = filename.endsWith(".md")
50
+ ? filename
51
+ : `${filename}.md`;
52
+ const filepath = `${options.workspace.primaryDir}/${filenameWithExt}`;
53
+ try {
54
+ // Read the handoff file
55
+ const handoffContent = await readFile(filepath, "utf8");
56
+ terminal.info(`Loading handoff file: ${filenameWithExt}`);
57
+ // Create new session (like compact-command does)
58
+ messageHistory.create(modelManager.getModel("repl").modelId);
59
+ // Append handoff content as user message
60
+ messageHistory.appendUserMessage(createUserMessage([], handoffContent));
61
+ terminal.info("Handoff file loaded successfully into new session.");
62
+ terminal.info("You can now continue with your previous work.");
63
+ return "use";
64
+ }
65
+ catch (error) {
66
+ if (error.code === "ENOENT") {
67
+ terminal.error(`Handoff file not found: ${filenameWithExt}`);
68
+ // Show available files as helpful suggestion
69
+ const availableFiles = await getAvailableHandoffFiles(options);
70
+ if (availableFiles.length > 0) {
71
+ terminal.info("Available handoff files:");
72
+ availableFiles.forEach((file) => {
73
+ terminal.info(` • ${file}.md`);
74
+ });
75
+ }
76
+ }
77
+ else {
78
+ terminal.error(`Error reading handoff file: ${error}`);
79
+ }
80
+ return "continue";
81
+ }
82
+ },
83
+ async handle(args, { tui, container, editor, }) {
84
+ const { messageHistory, modelManager } = options;
85
+ // Validate that filename is provided
86
+ const filename = args.join(" ").trim();
87
+ if (!filename) {
88
+ const availableFiles = await getAvailableHandoffFiles(options);
89
+ if (availableFiles.length === 0) {
90
+ container.addChild(new Text(style.red("No handoff files found. Create a handoff file first using /handoff <purpose>"), 1, 0));
91
+ }
92
+ else {
93
+ container.addChild(new Text(style.red("Please specify a handoff file to load."), 0, 1));
94
+ container.addChild(new Text("Available handoff files:", 2, 0));
95
+ availableFiles.forEach((file, index) => {
96
+ container.addChild(new Text(` • ${file}.md`, 3 + index, 0));
97
+ });
98
+ }
99
+ tui.requestRender();
100
+ editor.setText("");
101
+ return "continue";
102
+ }
103
+ // Ensure filename has .md extension for file operations
104
+ const filenameWithExt = filename.endsWith(".md")
105
+ ? filename
106
+ : `${filename}.md`;
107
+ const filepath = `${options.workspace.primaryDir}/${filenameWithExt}`;
108
+ try {
109
+ // Read the handoff file
110
+ const handoffContent = await readFile(filepath, "utf8");
111
+ container.addChild(new Text(`Loading handoff file: ${style.blue(filenameWithExt)}`, 1, 0));
112
+ // Create new session (like compact-command does)
113
+ messageHistory.create(modelManager.getModel("repl").modelId);
114
+ // Append handoff content as user message
115
+ messageHistory.appendUserMessage(createUserMessage([], handoffContent));
116
+ container.addChild(new Text(style.green("Handoff file loaded successfully into new session."), 2, 0));
117
+ container.addChild(new Text("You can now continue with your previous work.", 3, 0));
118
+ tui.requestRender();
119
+ editor.setText("");
120
+ return "use";
121
+ }
122
+ catch (error) {
123
+ if (error.code === "ENOENT") {
124
+ container.addChild(new Text(style.red(`Handoff file not found: ${filenameWithExt}`), 1, 0));
125
+ // Show available files as helpful suggestion
126
+ const availableFiles = await getAvailableHandoffFiles(options);
127
+ if (availableFiles.length > 0) {
128
+ container.addChild(new Text("Available handoff files:", 2, 0));
129
+ availableFiles.forEach((file, index) => {
130
+ container.addChild(new Text(` • ${file}.md`, 3 + index, 0));
131
+ });
132
+ }
133
+ }
134
+ else {
135
+ container.addChild(new Text(style.red(`Error reading handoff file: ${error}`), 0, 1));
136
+ }
137
+ tui.requestRender();
138
+ editor.setText("");
139
+ return "continue";
140
+ }
141
+ },
142
+ };
143
+ };
144
+ async function getAvailableHandoffFiles(options) {
145
+ try {
146
+ const dirents = await readdir(options.workspace.primaryDir, {
147
+ withFileTypes: true,
148
+ });
149
+ return dirents
150
+ .filter((dirent) => dirent.isFile() && dirent.name.match(/^handoff-.*\.md$/))
151
+ .map((dirent) => basename(dirent.name, ".md"))
152
+ .sort();
153
+ }
154
+ catch (error) {
155
+ if (error.code === "ENOENT") {
156
+ return [];
157
+ }
158
+ options.terminal.error(`Error reading handoff files: ${error}`);
159
+ return [];
160
+ }
161
+ }
@@ -1,3 +1,3 @@
1
1
  import type { CommandOptions, ReplCommand } from "./types.ts";
2
- export declare const promptCommand: ({ terminal, modelManager, promptManager, config, promptHistory, }: CommandOptions) => ReplCommand;
2
+ export declare const promptCommand: ({ terminal, modelManager, promptManager, config, promptHistory, workspace, }: CommandOptions) => ReplCommand;
3
3
  //# sourceMappingURL=prompt-command.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prompt-command.d.ts","sourceRoot":"","sources":["../../source/commands/prompt-command.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9D,eAAO,MAAM,aAAa,GAAI,mEAM3B,cAAc,KAAG,WAuHnB,CAAC"}
1
+ {"version":3,"file":"prompt-command.d.ts","sourceRoot":"","sources":["../../source/commands/prompt-command.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9D,eAAO,MAAM,aAAa,GAAI,8EAO3B,cAAc,KAAG,WAgPnB,CAAC"}