@abacus-ai/cli 2.0.0-canary.1 → 2.0.0-canary.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/dist/index.mjs +450 -422
  2. package/package.json +4 -1
  3. package/.oxlintrc.json +0 -8
  4. package/resources/abacus.ico +0 -0
  5. package/resources/entitlements.plist +0 -9
  6. package/src/__e2e__/README.md +0 -196
  7. package/src/__e2e__/agent-interactions.e2e.test.tsx +0 -61
  8. package/src/__e2e__/cli-commands.e2e.test.tsx +0 -77
  9. package/src/__e2e__/conversation-throttle.e2e.test.ts +0 -453
  10. package/src/__e2e__/conversation.e2e.test.tsx +0 -56
  11. package/src/__e2e__/diff-preview.e2e.test.tsx +0 -3399
  12. package/src/__e2e__/file-creation.e2e.test.tsx +0 -149
  13. package/src/__e2e__/helpers/test-helpers.ts +0 -449
  14. package/src/__e2e__/keyboard-navigation.e2e.test.tsx +0 -34
  15. package/src/__e2e__/llm-models.e2e.test.ts +0 -402
  16. package/src/__e2e__/mcp/mcp-callback-flow.e2e.test.tsx +0 -71
  17. package/src/__e2e__/mcp/mcp-full-app-ui.e2e.test.tsx +0 -167
  18. package/src/__e2e__/mcp/mcp-ui-rendering.e2e.test.tsx +0 -185
  19. package/src/__e2e__/repl.e2e.test.tsx +0 -78
  20. package/src/__e2e__/shell-compatibility.e2e.test.tsx +0 -76
  21. package/src/__e2e__/theme-mcp.e2e.test.tsx +0 -98
  22. package/src/__e2e__/tool-permissions.e2e.test.tsx +0 -66
  23. package/src/args.ts +0 -22
  24. package/src/components/__tests__/react-compiler.test.tsx +0 -78
  25. package/src/components/__tests__/status-indicator.test.tsx +0 -403
  26. package/src/components/composer/__tests__/bash-runner.test.tsx +0 -263
  27. package/src/components/composer/agent-mode-indicator.tsx +0 -63
  28. package/src/components/composer/bash-runner.tsx +0 -54
  29. package/src/components/composer/commands/default-commands.tsx +0 -615
  30. package/src/components/composer/commands/handler.tsx +0 -59
  31. package/src/components/composer/commands/picker.tsx +0 -273
  32. package/src/components/composer/commands/registry.ts +0 -233
  33. package/src/components/composer/commands/types.ts +0 -33
  34. package/src/components/composer/context.tsx +0 -88
  35. package/src/components/composer/file-mention-picker.tsx +0 -83
  36. package/src/components/composer/help.tsx +0 -44
  37. package/src/components/composer/index.tsx +0 -1007
  38. package/src/components/composer/mentions.ts +0 -57
  39. package/src/components/composer/message-queue.tsx +0 -70
  40. package/src/components/composer/mode-panel.tsx +0 -35
  41. package/src/components/composer/modes/__tests__/bash-handler.test.tsx +0 -755
  42. package/src/components/composer/modes/__tests__/bash-renderer.test.tsx +0 -1108
  43. package/src/components/composer/modes/bash-handler.tsx +0 -132
  44. package/src/components/composer/modes/bash-renderer.tsx +0 -175
  45. package/src/components/composer/modes/default-handlers.tsx +0 -33
  46. package/src/components/composer/modes/index.ts +0 -41
  47. package/src/components/composer/modes/types.ts +0 -21
  48. package/src/components/composer/persistent-shell.ts +0 -283
  49. package/src/components/composer/process.ts +0 -65
  50. package/src/components/composer/types.ts +0 -9
  51. package/src/components/composer/use-mention-search.ts +0 -68
  52. package/src/components/error-boundry.tsx +0 -60
  53. package/src/components/exit-message.tsx +0 -29
  54. package/src/components/expanded-view.tsx +0 -74
  55. package/src/components/file-completion.tsx +0 -127
  56. package/src/components/header.tsx +0 -47
  57. package/src/components/logo.tsx +0 -37
  58. package/src/components/segments.tsx +0 -356
  59. package/src/components/status-indicator.tsx +0 -306
  60. package/src/components/tool-group-summary.tsx +0 -263
  61. package/src/components/tool-permissions/ask-user-question-permission-ui.tsx +0 -319
  62. package/src/components/tool-permissions/diff-preview.tsx +0 -359
  63. package/src/components/tool-permissions/index.ts +0 -5
  64. package/src/components/tool-permissions/permission-options.tsx +0 -401
  65. package/src/components/tool-permissions/permission-preview-header.tsx +0 -57
  66. package/src/components/tool-permissions/tool-permission-ui.tsx +0 -420
  67. package/src/components/tools/agent/ask-user-question.tsx +0 -107
  68. package/src/components/tools/agent/enter-plan-mode.tsx +0 -55
  69. package/src/components/tools/agent/exit-plan-mode.tsx +0 -83
  70. package/src/components/tools/agent/handoff-to-main.tsx +0 -27
  71. package/src/components/tools/agent/subagent.tsx +0 -37
  72. package/src/components/tools/agent/todo-write.tsx +0 -104
  73. package/src/components/tools/browser/close-tab.tsx +0 -58
  74. package/src/components/tools/browser/computer.tsx +0 -70
  75. package/src/components/tools/browser/get-interactive-elements.tsx +0 -54
  76. package/src/components/tools/browser/get-tab-content.tsx +0 -51
  77. package/src/components/tools/browser/navigate-to.tsx +0 -59
  78. package/src/components/tools/browser/new-tab.tsx +0 -60
  79. package/src/components/tools/browser/perform-action.tsx +0 -63
  80. package/src/components/tools/browser/refresh-tab.tsx +0 -43
  81. package/src/components/tools/browser/switch-tab.tsx +0 -58
  82. package/src/components/tools/filesystem/delete-file.tsx +0 -104
  83. package/src/components/tools/filesystem/edit.tsx +0 -220
  84. package/src/components/tools/filesystem/list-dir.tsx +0 -78
  85. package/src/components/tools/filesystem/read-file.tsx +0 -180
  86. package/src/components/tools/filesystem/upload-image.tsx +0 -76
  87. package/src/components/tools/ide/ide-diagnostics.tsx +0 -62
  88. package/src/components/tools/index.ts +0 -91
  89. package/src/components/tools/mcp/mcp-tool.tsx +0 -158
  90. package/src/components/tools/search/fetch-url.tsx +0 -73
  91. package/src/components/tools/search/file-search.tsx +0 -78
  92. package/src/components/tools/search/grep.tsx +0 -90
  93. package/src/components/tools/search/semantic-search.tsx +0 -66
  94. package/src/components/tools/search/web-search.tsx +0 -71
  95. package/src/components/tools/shared/index.tsx +0 -48
  96. package/src/components/tools/shared/zod-coercion.ts +0 -35
  97. package/src/components/tools/terminal/bash-tool-output.tsx +0 -188
  98. package/src/components/tools/terminal/get-terminal-output.tsx +0 -91
  99. package/src/components/tools/terminal/run-in-terminal.tsx +0 -131
  100. package/src/components/tools/types.ts +0 -16
  101. package/src/components/tools.tsx +0 -68
  102. package/src/components/ui/__tests__/divider.test.tsx +0 -61
  103. package/src/components/ui/__tests__/gradient.test.tsx +0 -125
  104. package/src/components/ui/__tests__/input.test.tsx +0 -166
  105. package/src/components/ui/__tests__/select.test.tsx +0 -273
  106. package/src/components/ui/__tests__/shimmer.test.tsx +0 -99
  107. package/src/components/ui/blinking-indicator.tsx +0 -27
  108. package/src/components/ui/divider.tsx +0 -162
  109. package/src/components/ui/gradient.tsx +0 -56
  110. package/src/components/ui/input.tsx +0 -228
  111. package/src/components/ui/select.tsx +0 -151
  112. package/src/components/ui/shimmer.tsx +0 -76
  113. package/src/context/agent-mode.tsx +0 -95
  114. package/src/context/extension-file.tsx +0 -136
  115. package/src/context/network-activity.tsx +0 -45
  116. package/src/context/notification.tsx +0 -62
  117. package/src/context/shell-size.tsx +0 -49
  118. package/src/context/shell-title.tsx +0 -38
  119. package/src/entrypoints/print-mode.ts +0 -312
  120. package/src/entrypoints/repl.tsx +0 -389
  121. package/src/hooks/use-agent.ts +0 -15
  122. package/src/hooks/use-api-client.ts +0 -1
  123. package/src/hooks/use-available-height.ts +0 -8
  124. package/src/hooks/use-cleanup.ts +0 -29
  125. package/src/hooks/use-interrupt-manager.ts +0 -242
  126. package/src/hooks/use-models.ts +0 -22
  127. package/src/index.ts +0 -217
  128. package/src/lib/__tests__/ansi.test.ts +0 -255
  129. package/src/lib/__tests__/cli.test.ts +0 -122
  130. package/src/lib/__tests__/commands.test.ts +0 -325
  131. package/src/lib/__tests__/constants.test.ts +0 -15
  132. package/src/lib/__tests__/focusables.test.ts +0 -25
  133. package/src/lib/__tests__/fs.test.ts +0 -231
  134. package/src/lib/__tests__/markdown.test.tsx +0 -348
  135. package/src/lib/__tests__/mcpCommandHandler.test.ts +0 -173
  136. package/src/lib/__tests__/mcpManagement.test.ts +0 -38
  137. package/src/lib/__tests__/path-paste.test.ts +0 -144
  138. package/src/lib/__tests__/path.test.ts +0 -300
  139. package/src/lib/__tests__/queries.test.ts +0 -39
  140. package/src/lib/__tests__/standaloneMcpService.test.ts +0 -71
  141. package/src/lib/__tests__/text-buffer.test.ts +0 -328
  142. package/src/lib/__tests__/text-utils.test.ts +0 -32
  143. package/src/lib/__tests__/timing.test.ts +0 -78
  144. package/src/lib/__tests__/utils.test.ts +0 -238
  145. package/src/lib/__tests__/vim-buffer-actions.test.ts +0 -154
  146. package/src/lib/ansi.ts +0 -150
  147. package/src/lib/cli-push-server.ts +0 -112
  148. package/src/lib/cli.ts +0 -44
  149. package/src/lib/clipboard.ts +0 -226
  150. package/src/lib/command-utils.ts +0 -93
  151. package/src/lib/commands.ts +0 -270
  152. package/src/lib/constants.ts +0 -3
  153. package/src/lib/extension-connection.ts +0 -181
  154. package/src/lib/focusables.ts +0 -7
  155. package/src/lib/fs.ts +0 -533
  156. package/src/lib/markdown/code-block.tsx +0 -63
  157. package/src/lib/markdown/index.ts +0 -4
  158. package/src/lib/markdown/link.tsx +0 -19
  159. package/src/lib/markdown/markdown.tsx +0 -372
  160. package/src/lib/markdown/types.ts +0 -15
  161. package/src/lib/mcpCommandHandler.ts +0 -121
  162. package/src/lib/mcpManagement.ts +0 -44
  163. package/src/lib/path-paste.ts +0 -185
  164. package/src/lib/path.ts +0 -179
  165. package/src/lib/queries.ts +0 -15
  166. package/src/lib/standaloneMcpService.ts +0 -688
  167. package/src/lib/status-utils.ts +0 -237
  168. package/src/lib/test-utils.tsx +0 -72
  169. package/src/lib/text-buffer.ts +0 -2415
  170. package/src/lib/text-utils.ts +0 -272
  171. package/src/lib/timing.ts +0 -63
  172. package/src/lib/types.ts +0 -295
  173. package/src/lib/utils.ts +0 -182
  174. package/src/lib/vim-buffer-actions.ts +0 -732
  175. package/src/providers/agent.tsx +0 -1063
  176. package/src/providers/api-client.tsx +0 -43
  177. package/src/services/logger.ts +0 -85
  178. package/src/terminal/detection.ts +0 -187
  179. package/src/terminal/exit.ts +0 -279
  180. package/src/terminal/notification.ts +0 -83
  181. package/src/terminal/progress.ts +0 -201
  182. package/src/terminal/setup.ts +0 -797
  183. package/src/terminal/types.ts +0 -51
  184. package/src/theme/context.tsx +0 -57
  185. package/src/theme/index.ts +0 -4
  186. package/src/theme/themed.tsx +0 -35
  187. package/src/theme/themes.json +0 -546
  188. package/src/theme/types.ts +0 -110
  189. package/src/tools/types.ts +0 -59
  190. package/src/tools/utils/__tests__/zod-coercion.test.ts +0 -33
  191. package/src/tools/utils/tool-ui-components.tsx +0 -649
  192. package/src/tools/utils/zod-coercion.ts +0 -35
  193. package/tsconfig.json +0 -16
  194. package/tsconfig.node.json +0 -29
  195. package/tsconfig.test.json +0 -27
  196. package/tsdown.config.ts +0 -17
  197. package/vitest.config.ts +0 -76
@@ -1,615 +0,0 @@
1
- import type { McpManager } from "@codellm/agent";
2
-
3
- import { product } from "@codellm/product";
4
- import { homedir } from "node:os";
5
- import { resolve } from "node:path";
6
-
7
- import packageJson from "@/package.json" with { type: "json" };
8
-
9
- import { McpCommandHandler } from "../../../lib/mcpCommandHandler.js";
10
- import { AgentStatus } from "../../../providers/agent.js";
11
- import { setupTerminal } from "../../../terminal/setup.js";
12
- import themesData from "../../../theme/themes.json" with { type: "json" };
13
- import { ComposerMode } from "../types.js";
14
- import { commandRegistry } from "./registry.js";
15
- import { CommandDefinition } from "./types.js";
16
-
17
- interface ThemeDefinition {
18
- name: string;
19
- displayName: string;
20
- colors: Record<string, string>;
21
- }
22
-
23
- interface ThemesRegistry {
24
- version: string;
25
- default: string;
26
- themes: ThemeDefinition[];
27
- }
28
-
29
- const themesRegistry: ThemesRegistry = themesData as ThemesRegistry;
30
-
31
- function formatCommandHelp(): string {
32
- const commands = commandRegistry.getAll();
33
- const lines: string[] = [];
34
- const maxUsageLength = Math.max(
35
- 0,
36
- ...commands.map((cmd) => {
37
- if (cmd.subcommands && cmd.subcommands.length > 0) {
38
- return Math.max(0, ...cmd.subcommands.map((sub) => sub.usage?.length || 0));
39
- }
40
- return cmd.usage?.length || 0;
41
- }),
42
- );
43
-
44
- lines.push(
45
- "Abacus.AI CLI",
46
- "",
47
- "Abacus.AI CLI has read access to files in the current directory and can run commands and edit files with your permission.",
48
- "",
49
- "Use npx -y @abacus-ai/cli@latest to run latest version of abacusai interactive mode directly from your terminal",
50
- "",
51
- "Usage Modes:",
52
- "- REPL: abacusai (interactive session)",
53
- '- Non-interactive: abacusai -p "question"',
54
- "",
55
- "Run abacusai --help for all command line options",
56
- "",
57
- "For first time users, use /login to authenticate to Abacus.AI CLI",
58
- "",
59
- "Common Tasks:",
60
- "- Ask questions about your codebase > How does agent.py work?",
61
- "- Edit files > Update code.ts to...",
62
- "- Fix errors > cargo build",
63
- "- Run commands > /help",
64
- "- Run bash commands > wc",
65
- "",
66
- "Interactive Mode Commands:",
67
- "",
68
- );
69
-
70
- for (const cmd of commands) {
71
- if (cmd.subcommands && cmd.subcommands.length > 0) {
72
- for (const sub of cmd.subcommands) {
73
- lines.push(` ${(sub.usage || "").padEnd(maxUsageLength + 2)} ${sub.description || ""}`);
74
- }
75
- } else {
76
- lines.push(` ${(cmd.usage || "").padEnd(maxUsageLength + 2)} ${cmd.description || ""}`);
77
- }
78
- }
79
-
80
- lines.push("", "Type any command and press Enter to execute.");
81
-
82
- return lines.join("\n");
83
- }
84
-
85
- interface ModelInfo {
86
- label: string;
87
- value: string;
88
- model: any;
89
- }
90
-
91
- export function registerDefaultCommands(handlers: {
92
- onExit?: () => void;
93
- onNewChat?: () => void;
94
- onClearChat?: () => void;
95
- onModelChange?: (model: string) => void;
96
- onResumeChat?: (title: string) => void;
97
- onStop?: () => void;
98
- onHelp?: () => void;
99
- onLogin?: (emailOrApiKey?: string) => void;
100
- onLogout?: () => void;
101
- getModels?: () => Promise<ModelInfo[]> | ModelInfo[];
102
- getChatTitles?: () => Promise<string[]> | string[];
103
- addNotification?: (
104
- message: string,
105
- severity?: "info" | "warning" | "error",
106
- temp?: boolean,
107
- ) => void;
108
- mcp?: McpManager;
109
- addAllowedDirectory?: (dirPath: string) => void;
110
- refreshMcpTools?: (skipReload?: boolean, showNotification?: boolean) => Promise<void>;
111
- getAgentStatus?: () => string;
112
- getUserInfo?: () => Promise<void>;
113
- setTheme?: (themeName: string) => void;
114
- checkEnvAuth?: () => { isEnvAuth: boolean; shouldHide: boolean };
115
- }): void {
116
- const mcpHandler = handlers.mcp ? new McpCommandHandler(handlers.mcp) : undefined;
117
-
118
- const addNotification = (
119
- message: string,
120
- severity: "info" | "error" | "warning" = "info",
121
- temp?: boolean,
122
- ) => {
123
- handlers.addNotification?.(message, severity, temp);
124
- };
125
-
126
- const commands: CommandDefinition[] = [
127
- {
128
- id: "version",
129
- description: "Show CLI version",
130
- usage: "/version",
131
- execute: () => {
132
- addNotification(`Abacus.AI CLI version ${packageJson.version}`, "info");
133
- },
134
- },
135
- {
136
- id: "exit",
137
- description: "Exit the application",
138
- usage: "/exit",
139
- alias: { [ComposerMode.Prompt]: ["exit", "quit"] },
140
- execute: () => {
141
- handlers.onExit?.();
142
- },
143
- },
144
- {
145
- id: "new",
146
- description: "Start a new conversation",
147
- usage: "/new",
148
- execute: () => {
149
- handlers.onNewChat?.();
150
- },
151
- },
152
- {
153
- id: "clear",
154
- description: "Clear the output display while keeping the current conversation",
155
- usage: "/clear",
156
- alias: { [ComposerMode.Prompt]: ["cls", "clear"] },
157
- execute: () => {
158
- handlers.onClearChat?.();
159
- },
160
- },
161
- {
162
- id: "stop",
163
- description: "Stop the current streaming response and any running bash tools",
164
- usage: "/stop",
165
- execute: async () => {
166
- const agentStatus = handlers.getAgentStatus?.();
167
- const isAgentActive =
168
- agentStatus === AgentStatus.Submitted ||
169
- agentStatus === AgentStatus.Streaming ||
170
- agentStatus === AgentStatus.ExecutingTool ||
171
- agentStatus === AgentStatus.WaitingForToolPermission;
172
-
173
- handlers.onStop?.();
174
- if (isAgentActive) {
175
- addNotification("Agent stopped successfully", "info");
176
- } else {
177
- addNotification("No active agent process to stop", "warning");
178
- }
179
- },
180
- },
181
- {
182
- id: "help",
183
- description: "Show commands and functionalities",
184
- usage: "/help",
185
- alias: { [ComposerMode.Prompt]: ["help"] },
186
- execute: () => {
187
- if (handlers.onHelp) {
188
- handlers.onHelp();
189
- } else {
190
- addNotification(formatCommandHelp(), "info");
191
- }
192
- },
193
- },
194
- {
195
- id: "model",
196
- description: "Switch to a different model",
197
- usage: "/model <model_name>",
198
- requiresArgument: true,
199
- completionProvider: async (query: string) => {
200
- if (!handlers.getModels) {
201
- return [];
202
- }
203
-
204
- const models = await handlers.getModels();
205
-
206
- if (!models || models.length === 0) {
207
- return [];
208
- }
209
-
210
- const lowerQuery = query.trim().toLowerCase();
211
-
212
- if (!lowerQuery) {
213
- return models.map((m) => m.label);
214
- }
215
-
216
- return models
217
- .filter(
218
- (m) =>
219
- m.label.toLowerCase().includes(lowerQuery) ||
220
- m.value.toLowerCase().includes(lowerQuery),
221
- )
222
- .map((m) => m.label);
223
- },
224
- execute: async (args: string[]) => {
225
- const modelName = args.join(" ").trim();
226
- if (!modelName) {
227
- addNotification("Please specify a model name", "error");
228
- return;
229
- }
230
-
231
- if (!handlers.getModels) {
232
- addNotification("Models not available", "error");
233
- return;
234
- }
235
-
236
- const models = await handlers.getModels();
237
- const matchedModel = models.find((m) => m.label === modelName);
238
-
239
- if (matchedModel) {
240
- handlers.onModelChange?.(matchedModel.value);
241
- } else {
242
- addNotification(`Model "${modelName}" not found`, "error");
243
- }
244
- },
245
- },
246
- {
247
- id: "resume",
248
- description: "Resume a previous chat conversation",
249
- usage: "/resume <chat_title>",
250
- requiresArgument: true,
251
- completionProvider: async (query: string) => {
252
- if (!handlers.getChatTitles) {
253
- return [];
254
- }
255
- const titles = await handlers.getChatTitles();
256
- const lowerQuery = query.toLowerCase();
257
- return titles.filter((title) => title.toLowerCase().includes(lowerQuery));
258
- },
259
- execute: (args: string[]) => {
260
- const title = args.join(" ").trim();
261
- if (!title) {
262
- return;
263
- }
264
- handlers.onResumeChat?.(title);
265
- },
266
- },
267
- {
268
- id: "login",
269
- description: "Log in to Abacus.AI CLI with email or API key",
270
- usage: "/login <email|api_key>",
271
- execute: async (args: string[]) => {
272
- // Check if user is authenticated via environment variables first
273
- if (handlers.checkEnvAuth) {
274
- const { isEnvAuth } = handlers.checkEnvAuth();
275
- if (isEnvAuth) {
276
- addNotification(
277
- `You are logged in via environment variables. Remove ${product.envPrefix}API_KEY to logout.`,
278
- "warning",
279
- );
280
- return;
281
- }
282
- }
283
-
284
- const argument = args.filter(Boolean).join(" ").trim();
285
-
286
- if (!argument) {
287
- addNotification(
288
- "This command requires an argument. Usage: /login <email> or /login <api_key>\n\n" +
289
- "Get your API key from: https://abacus.ai/app/abacusai-cli",
290
- "warning",
291
- );
292
- // Throw error to prevent clearing the input buffer so user can add the argument
293
- throw new Error("Missing required argument");
294
- }
295
-
296
- handlers.onLogin?.(argument);
297
- },
298
- },
299
- {
300
- id: "logout",
301
- description: "Log out of Abacus.AI CLI",
302
- usage: "/logout",
303
- execute: async () => {
304
- // Check if user is authenticated via environment variables
305
- if (handlers.checkEnvAuth) {
306
- const { isEnvAuth } = handlers.checkEnvAuth();
307
- if (isEnvAuth) {
308
- addNotification(
309
- `You are logged in via environment variables. Remove ${product.envPrefix}API_KEY to logout.`,
310
- "warning",
311
- );
312
- return;
313
- }
314
- }
315
-
316
- try {
317
- handlers.onLogout?.();
318
- addNotification("Logged out successfully", "info");
319
- } catch (error) {
320
- const errorMessage = `Logout failed: ${error instanceof Error ? error.message : "Unknown error occurred"}`;
321
- addNotification(errorMessage, "error");
322
- }
323
- },
324
- },
325
- {
326
- id: "status",
327
- description: "Show current user status",
328
- usage: "/status",
329
- execute: async () => {
330
- try {
331
- await handlers.getUserInfo?.();
332
- } catch (error) {
333
- const errorMessage = `Failed to get status: ${error instanceof Error ? error.message : "Unknown error occurred"}`;
334
- addNotification(errorMessage, "error");
335
- }
336
- },
337
- },
338
- {
339
- id: "usage",
340
- description: "Show total credits consumed in current session",
341
- usage: "/usage",
342
- execute: async () => {
343
- addNotification("Usage information not available in this version", "info");
344
- },
345
- },
346
- {
347
- id: "terminal-setup",
348
- description: "Automatically configure terminal for Shift+Enter newlines",
349
- usage: "/terminal-setup",
350
- execute: async () => {
351
- try {
352
- addNotification("Setting up terminal keybindings...", "info");
353
-
354
- const result = await setupTerminal();
355
-
356
- if (result.success) {
357
- let message = result.message;
358
- if (result.backupPath) {
359
- message += `\n\nBackup created: ${result.backupPath}`;
360
- }
361
- if (result.requiresRestart) {
362
- message += "\n\nPlease restart your terminal for changes to take effect.";
363
- }
364
- addNotification(message, "info");
365
- } else {
366
- addNotification(result.message, "warning");
367
- }
368
- } catch (error) {
369
- addNotification(
370
- `Terminal setup failed: ${error instanceof Error ? error.message : "Unknown error"}`,
371
- "error",
372
- );
373
- }
374
- },
375
- },
376
- {
377
- id: "theme",
378
- description: "Switch between color themes",
379
- usage: "/theme [theme_name]",
380
- completionProvider: async (query: string) => {
381
- try {
382
- const themes = themesRegistry.themes;
383
- const themeNames = themes.map((t) => t.displayName);
384
-
385
- if (!query.trim()) {
386
- return themeNames;
387
- }
388
-
389
- const lowerQuery = query.toLowerCase();
390
- return themeNames.filter((name) => name.toLowerCase().includes(lowerQuery));
391
- } catch {
392
- return [];
393
- }
394
- },
395
- execute: async (args: string[]) => {
396
- const themeName = args.join(" ").trim();
397
-
398
- if (!themeName) {
399
- // Don't show notification - completion provider already shows themes
400
- return;
401
- }
402
-
403
- try {
404
- const themes = themesRegistry.themes;
405
-
406
- const matchedTheme = themes.find(
407
- (t) =>
408
- t.displayName.toLowerCase() === themeName.toLowerCase() ||
409
- t.name.toLowerCase() === themeName.toLowerCase(),
410
- );
411
-
412
- if (matchedTheme) {
413
- // Switch theme via handler
414
- if (handlers.setTheme) {
415
- handlers.setTheme(matchedTheme.name);
416
- addNotification(`${matchedTheme.displayName} applied`, "info");
417
- } else {
418
- addNotification("Theme switching not available", "error");
419
- }
420
- } else {
421
- addNotification(`Theme "${themeName}" not found`, "error");
422
- }
423
- } catch (error) {
424
- addNotification(
425
- `Failed to switch theme: ${error instanceof Error ? error.message : "Unknown error"}`,
426
- "error",
427
- );
428
- }
429
- },
430
- },
431
- {
432
- id: "mcp",
433
- description: "MCP server management",
434
- usage: "/mcp <subcommand>",
435
- subcommands: [
436
- {
437
- id: "list",
438
- description: "List configured MCP servers",
439
- usage: "/mcp list",
440
- execute: async () => {
441
- if (!mcpHandler) {
442
- addNotification("MCP service not available", "error");
443
- return;
444
- }
445
- try {
446
- const result = await mcpHandler.listServers();
447
- addNotification(result, "info");
448
- } catch (error) {
449
- const errorMessage = `Failed to list MCP servers: ${error instanceof Error ? error.message : "Unknown error occurred"}`;
450
- addNotification(errorMessage, "error");
451
- }
452
- },
453
- },
454
- {
455
- id: "add",
456
- description: "Add an MCP server",
457
- usage: "/mcp add <name> <commandOrUrl> [args...]",
458
- requiresArgument: true,
459
- execute: async (args: string[]) => {
460
- if (!mcpHandler) {
461
- addNotification("MCP service not available", "error");
462
- return;
463
- }
464
- if (args.length < 2) {
465
- addNotification("Usage: /mcp add <name> <commandOrUrl> [args...]", "error");
466
- addNotification("[TIP] Use /mcp add-json for JSON configuration", "info");
467
- return;
468
- }
469
- const [name, commandOrUrl, ...extraArgs] = args;
470
- if (commandOrUrl.trim().startsWith("{")) {
471
- addNotification(
472
- "Invalid: JSON detected. Use /mcp add-json for JSON configuration",
473
- "error",
474
- );
475
- return;
476
- }
477
- try {
478
- await mcpHandler.addServer(name, commandOrUrl, extraArgs);
479
- addNotification(`Added MCP server '${name}'`, "info");
480
- } catch (error) {
481
- const errorMessage = `Failed to add MCP server: ${error instanceof Error ? error.message : "Unknown error occurred"}`;
482
- addNotification(errorMessage, "error");
483
- }
484
- },
485
- },
486
- {
487
- id: "add-json",
488
- description: "Add an MCP server with JSON configuration",
489
- usage: "/mcp add-json <name> <json>",
490
- requiresArgument: true,
491
- execute: async (args: string[]) => {
492
- if (!mcpHandler) {
493
- addNotification("MCP service not available", "error");
494
- return;
495
- }
496
- if (args.length !== 2) {
497
- addNotification("Usage: /mcp add-json <name> <json>", "error");
498
- addNotification(
499
- 'Example: /mcp add-json github \'{"command":"npx","args":["-y","@modelcontextprotocol/server-github"],"env":{"GITHUB_PERSONAL_ACCESS_TOKEN":"your_token"}}\'',
500
- "info",
501
- );
502
- return;
503
- }
504
- try {
505
- const [name, jsonConfig] = args;
506
- await mcpHandler.addServerFromJson(name, jsonConfig);
507
- addNotification(`Added MCP server '${name}' from JSON`, "info");
508
- } catch (error) {
509
- const errorMessage = `Failed to add MCP server: ${error instanceof Error ? error.message : "Unknown error occurred"}`;
510
- addNotification(errorMessage, "error");
511
- }
512
- },
513
- },
514
- {
515
- id: "remove",
516
- description: "Remove an MCP server",
517
- usage: "/mcp remove <name>",
518
- requiresArgument: true,
519
- execute: async (args: string[]) => {
520
- if (!mcpHandler) {
521
- addNotification("MCP service not available", "error");
522
- return;
523
- }
524
- if (args.length !== 1) {
525
- addNotification("Usage: /mcp remove <name>", "error");
526
- return;
527
- }
528
- try {
529
- await mcpHandler.removeServer(args[0]);
530
- addNotification(`Removed MCP server '${args[0]}'`, "info");
531
- } catch (error) {
532
- const errorMessage = `Failed to remove MCP server: ${error instanceof Error ? error.message : "Unknown error occurred"}`;
533
- addNotification(errorMessage, "error");
534
- }
535
- },
536
- },
537
- {
538
- id: "get",
539
- description: "Get details about an MCP server",
540
- usage: "/mcp get <name>",
541
- requiresArgument: true,
542
- execute: async (args: string[]) => {
543
- if (!mcpHandler) {
544
- addNotification("MCP service not available", "error");
545
- return;
546
- }
547
- if (args.length !== 1) {
548
- addNotification("Usage: /mcp get <name>", "error");
549
- return;
550
- }
551
- try {
552
- const result = await mcpHandler.getServer(args[0]);
553
- if (result) {
554
- addNotification(result, "info");
555
- } else {
556
- addNotification(`Server '${args[0]}' not found`, "error");
557
- }
558
- } catch (error) {
559
- const errorMessage = `Failed to get MCP server: ${error instanceof Error ? error.message : "Unknown error occurred"}`;
560
- addNotification(errorMessage, "error");
561
- }
562
- },
563
- },
564
- ],
565
- },
566
- {
567
- id: "add-dir",
568
- description: "Add a directory to the agent's allowed access paths for this session",
569
- usage: "/add-dir <path>",
570
- requiresArgument: true,
571
- execute: async (args: string[]) => {
572
- const rawPath = args.join(" ").trim();
573
- if (!rawPath) {
574
- addNotification("Usage: /add-dir <path>", "error");
575
- return;
576
- }
577
- if (!handlers.addAllowedDirectory) {
578
- addNotification("Cannot add directory: session not available", "error");
579
- return;
580
- }
581
- try {
582
- const expanded = rawPath.replace(/^~/, homedir());
583
- const absolute = resolve(expanded);
584
- handlers.addAllowedDirectory(absolute);
585
- addNotification(`Added '${absolute}' to allowed directories`, "info");
586
- } catch (error) {
587
- addNotification(
588
- `Failed to add directory: ${error instanceof Error ? error.message : "Unknown error"}`,
589
- "error",
590
- );
591
- }
592
- },
593
- },
594
- ];
595
-
596
- commandRegistry.clear();
597
-
598
- // Filter commands based on environment auth status
599
- const registerCommands = async () => {
600
- let commandsToRegister = commands;
601
-
602
- if (handlers.checkEnvAuth) {
603
- const { shouldHide } = handlers.checkEnvAuth();
604
- if (shouldHide) {
605
- // Hide login and logout commands when in no-auth mode with valid env auth
606
- commandsToRegister = commands.filter((cmd) => cmd.id !== "login" && cmd.id !== "logout");
607
- }
608
- }
609
-
610
- commandsToRegister.forEach((cmd) => commandRegistry.register(cmd));
611
- };
612
-
613
- // Register commands asynchronously
614
- void registerCommands();
615
- }
@@ -1,59 +0,0 @@
1
- import { useCallback } from "react";
2
-
3
- import { ModeRenderProps } from "../modes/types.js";
4
- import { CommandPicker } from "./picker.js";
5
- import { commandRegistry } from "./registry.js";
6
-
7
- export function useCommandHandler(clearBuffer: () => void) {
8
- const executeCommand = useCallback(
9
- async (value: string) => {
10
- const trimmed = value.trim();
11
-
12
- if (!trimmed.startsWith("/")) {
13
- return;
14
- }
15
-
16
- const match = commandRegistry.match(trimmed);
17
- if (!match || match.needsSubcommand) {
18
- return;
19
- }
20
-
21
- const executor = match.subcommand || match.command;
22
- if (executor.completionProvider && executor.requiresArgument) {
23
- clearBuffer();
24
- return;
25
- }
26
-
27
- try {
28
- if (await commandRegistry.execute(match)) {
29
- clearBuffer();
30
- }
31
- } catch {
32
- // If command throws an error, don't clear buffer
33
- // This allows commands to show validation errors without losing user input
34
- }
35
- },
36
- [clearBuffer],
37
- );
38
-
39
- const CommandRenderer = useCallback(({ prompt, onExit, availableHeight }: ModeRenderProps) => {
40
- return (
41
- <CommandPicker
42
- prompt={prompt}
43
- onAccept={onExit}
44
- onCancel={onExit}
45
- availableHeight={availableHeight}
46
- />
47
- );
48
- }, []);
49
-
50
- const shouldPreventDefault = useCallback((key: string) => {
51
- return ["up", "down", "escape", "tab"].includes(key);
52
- }, []);
53
-
54
- return {
55
- executeCommand,
56
- renderer: CommandRenderer,
57
- shouldPreventDefault,
58
- };
59
- }