@mcp-use/cli 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/InputPrompt.d.ts +13 -0
  2. package/dist/InputPrompt.js +188 -0
  3. package/dist/MultilineInput.d.ts +13 -0
  4. package/dist/MultilineInput.js +154 -0
  5. package/dist/MultilineTextInput.d.ts +11 -0
  6. package/dist/MultilineTextInput.js +97 -0
  7. package/dist/PasteAwareInput.d.ts +13 -0
  8. package/dist/PasteAwareInput.js +183 -0
  9. package/dist/SimpleMultilineInput.d.ts +11 -0
  10. package/dist/SimpleMultilineInput.js +125 -0
  11. package/dist/app.d.ts +1 -5
  12. package/dist/app.js +291 -186
  13. package/dist/cli.js +2 -5
  14. package/dist/commands.d.ts +15 -30
  15. package/dist/commands.js +308 -568
  16. package/dist/components/AsciiLogo.d.ts +2 -0
  17. package/dist/components/AsciiLogo.js +7 -0
  18. package/dist/components/Footer.d.ts +5 -0
  19. package/dist/components/Footer.js +19 -0
  20. package/dist/components/InputPrompt.d.ts +13 -0
  21. package/dist/components/InputPrompt.js +188 -0
  22. package/dist/components/Messages.d.ts +21 -0
  23. package/dist/components/Messages.js +80 -0
  24. package/dist/components/ServerStatus.d.ts +7 -0
  25. package/dist/components/ServerStatus.js +36 -0
  26. package/dist/components/Spinner.d.ts +16 -0
  27. package/dist/components/Spinner.js +63 -0
  28. package/dist/components/ToolStatus.d.ts +8 -0
  29. package/dist/components/ToolStatus.js +33 -0
  30. package/dist/components/textInput.d.ts +1 -0
  31. package/dist/components/textInput.js +1 -0
  32. package/dist/logger.d.ts +10 -0
  33. package/dist/logger.js +48 -0
  34. package/dist/mcp-service.d.ts +5 -4
  35. package/dist/mcp-service.js +98 -207
  36. package/dist/services/agent-service.d.ts +56 -0
  37. package/dist/services/agent-service.js +203 -0
  38. package/dist/services/cli-service.d.ts +132 -0
  39. package/dist/services/cli-service.js +591 -0
  40. package/dist/services/index.d.ts +4 -0
  41. package/dist/services/index.js +4 -0
  42. package/dist/services/llm-service.d.ts +174 -0
  43. package/dist/services/llm-service.js +567 -0
  44. package/dist/services/mcp-config-service.d.ts +69 -0
  45. package/dist/services/mcp-config-service.js +426 -0
  46. package/dist/services/mcp-service.d.ts +1 -0
  47. package/dist/services/mcp-service.js +1 -0
  48. package/dist/services/utility-service.d.ts +47 -0
  49. package/dist/services/utility-service.js +208 -0
  50. package/dist/storage.js +4 -4
  51. package/dist/types.d.ts +30 -0
  52. package/dist/types.js +1 -0
  53. package/package.json +22 -8
  54. package/readme.md +68 -39
@@ -0,0 +1,426 @@
1
+ import { SecureStorage } from '../storage.js';
2
+ export class MCPConfigService {
3
+ constructor() {
4
+ Object.defineProperty(this, "persistentConfig", {
5
+ enumerable: true,
6
+ configurable: true,
7
+ writable: true,
8
+ value: void 0
9
+ });
10
+ this.persistentConfig = SecureStorage.loadConfig();
11
+ }
12
+ getConfiguredServers() {
13
+ return this.persistentConfig.mcpServers || {};
14
+ }
15
+ isServerConfigured(serverName) {
16
+ return !!this.persistentConfig.mcpServers?.[serverName];
17
+ }
18
+ addServerFromJSON(jsonConfig) {
19
+ try {
20
+ const parsedConfig = JSON.parse(jsonConfig);
21
+ // Validate JSON structure
22
+ if (!parsedConfig.mcpServers ||
23
+ typeof parsedConfig.mcpServers !== 'object') {
24
+ return {
25
+ success: false,
26
+ message: 'Invalid JSON format. Expected format:\n{\n "mcpServers": {\n "servername": {\n "command": "...",\n "args": [...]\n }\n }\n}',
27
+ };
28
+ }
29
+ const servers = parsedConfig.mcpServers;
30
+ const serverNames = Object.keys(servers);
31
+ if (serverNames.length === 0) {
32
+ return {
33
+ success: false,
34
+ message: 'No servers found in JSON configuration.',
35
+ };
36
+ }
37
+ // Check for conflicts with existing servers
38
+ const existingServers = this.persistentConfig.mcpServers || {};
39
+ const conflicts = serverNames.filter(name => existingServers[name]);
40
+ if (conflicts.length > 0) {
41
+ return {
42
+ success: false,
43
+ message: `Server(s) already exist: ${conflicts.join(', ')}. Please use different names.`,
44
+ };
45
+ }
46
+ // Validate each server config
47
+ for (const [name, serverConfig] of Object.entries(servers)) {
48
+ const server = serverConfig;
49
+ if (!server.command || typeof server.command !== 'string') {
50
+ return {
51
+ success: false,
52
+ message: `Server "${name}" missing required "command" field.`,
53
+ };
54
+ }
55
+ }
56
+ // All validation passed, save the servers
57
+ if (!this.persistentConfig.mcpServers) {
58
+ this.persistentConfig.mcpServers = {};
59
+ }
60
+ // Add all servers from JSON
61
+ Object.assign(this.persistentConfig.mcpServers, servers);
62
+ SecureStorage.saveConfig(this.persistentConfig);
63
+ const serverList = serverNames.map(name => `• ${name}`).join('\n');
64
+ return {
65
+ success: true,
66
+ message: `Configured ${serverNames.length} server(s)!\n\n${serverList}`,
67
+ data: {
68
+ serversAdded: true,
69
+ serverNames,
70
+ },
71
+ };
72
+ }
73
+ catch (error) {
74
+ return {
75
+ success: false,
76
+ message: `Invalid JSON format: ${error instanceof Error ? error.message : 'Parse error'}\n\nPlease check your JSON syntax and try again.`,
77
+ };
78
+ }
79
+ }
80
+ addServer(name, config) {
81
+ // Check if server name already exists
82
+ if (this.persistentConfig.mcpServers?.[name]) {
83
+ return {
84
+ success: false,
85
+ message: `Server "${name}" already exists. Use a different name.`,
86
+ };
87
+ }
88
+ // Add server to persistent configuration
89
+ if (!this.persistentConfig.mcpServers) {
90
+ this.persistentConfig.mcpServers = {};
91
+ }
92
+ this.persistentConfig.mcpServers[name] = config;
93
+ // Save configuration
94
+ SecureStorage.saveConfig(this.persistentConfig);
95
+ return {
96
+ success: true,
97
+ message: `Server "${name}" configured!`,
98
+ data: {
99
+ serverAdded: true,
100
+ serverName: name,
101
+ },
102
+ };
103
+ }
104
+ /**
105
+ * Gets the configuration for a specific server.
106
+ * @param serverName - Name of the server
107
+ * @returns The server configuration or null if not found
108
+ */
109
+ getServerConfig(serverName) {
110
+ return this.persistentConfig.mcpServers?.[serverName] || null;
111
+ }
112
+ /**
113
+ * Gets all configured servers with their configurations.
114
+ * Note: Connection status should be determined by the caller using AgentService.
115
+ * @returns Array of server configurations
116
+ */
117
+ getAllServers() {
118
+ const persistentServers = this.persistentConfig.mcpServers || {};
119
+ const servers = [];
120
+ for (const [name, config] of Object.entries(persistentServers)) {
121
+ servers.push({
122
+ name,
123
+ config,
124
+ });
125
+ }
126
+ return servers;
127
+ }
128
+ getServerTestCommand(serverName) {
129
+ const serverConfig = this.persistentConfig.mcpServers?.[serverName];
130
+ if (!serverConfig) {
131
+ const configuredServers = Object.keys(this.persistentConfig.mcpServers || {});
132
+ return {
133
+ success: false,
134
+ message: `Server "${serverName}" is not configured.\n\nConfigured servers: ${configuredServers.length > 0 ? configuredServers.join(', ') : 'none'}`,
135
+ };
136
+ }
137
+ const command = serverConfig.command;
138
+ const args_str = serverConfig.args ? serverConfig.args.join(' ') : '';
139
+ const full_command = `${command} ${args_str}`.trim();
140
+ return {
141
+ success: true,
142
+ command: full_command,
143
+ };
144
+ }
145
+ validateServerName(name) {
146
+ if (!name || !name.trim()) {
147
+ return { valid: false, message: 'Server name cannot be empty.' };
148
+ }
149
+ if (this.persistentConfig.mcpServers?.[name.trim()]) {
150
+ return {
151
+ valid: false,
152
+ message: `Server "${name.trim()}" already exists. Use a different name.`,
153
+ };
154
+ }
155
+ return { valid: true };
156
+ }
157
+ parseEnvironmentVariables(envString) {
158
+ const env = {};
159
+ if (envString.trim()) {
160
+ const envLines = envString.trim().split('\n');
161
+ for (const line of envLines) {
162
+ const [key, ...valueParts] = line.split('=');
163
+ if (key && valueParts.length > 0) {
164
+ env[key.trim()] = valueParts.join('=').trim();
165
+ }
166
+ }
167
+ }
168
+ return env;
169
+ }
170
+ /**
171
+ * Handles the /server command and its subcommands.
172
+ * @param args - Array of arguments where args[0] is the subcommand
173
+ * @returns A CommandResult with the appropriate response
174
+ */
175
+ handleServerCommand(args) {
176
+ if (args.length === 0) {
177
+ return {
178
+ type: 'info',
179
+ message: 'Server management commands:\n\n/server add - Configure a new server (stored but not connected)\n/server connect <name> - Connect to a configured server by name\n/server disconnect <name> - Disconnect from a connected server\n/servers - List configured servers and connection status\n\nUse /server <command> for specific help.',
180
+ };
181
+ }
182
+ if (args[0] === 'add') {
183
+ return {
184
+ type: 'prompt_server_config',
185
+ message: 'Let\'s configure a new MCP server!\n\nYou can either:\n1. Enter a server name for interactive setup\n2. Paste a complete JSON configuration\n\nExample JSON:\n{\n "mcpServers": {\n "myserver": {\n "command": "npx",\n "args": ["-y", "@example/server"]\n }\n }\n}\n\nEnter server name or paste JSON:',
186
+ data: { step: 'name_or_json' },
187
+ };
188
+ }
189
+ if (args[0] === 'connect') {
190
+ if (args.length < 2) {
191
+ const configuredServers = Object.keys(this.getConfiguredServers());
192
+ if (configuredServers.length === 0) {
193
+ return {
194
+ type: 'error',
195
+ message: 'No servers configured. Use /server add to configure servers first.\n\nUsage: /server connect <server_name>',
196
+ };
197
+ }
198
+ return {
199
+ type: 'error',
200
+ message: `Usage: /server connect <server_name>\n\nConfigured servers: ${configuredServers.join(', ')}`,
201
+ };
202
+ }
203
+ const serverName = args[1];
204
+ if (!serverName) {
205
+ return {
206
+ type: 'error',
207
+ message: 'Server name is required.\n\nUsage: /server connect <server_name>',
208
+ };
209
+ }
210
+ return {
211
+ type: 'info',
212
+ message: `Server connect command received for: ${serverName}`,
213
+ data: { connectServer: true, serverName },
214
+ };
215
+ }
216
+ if (args[0] === 'disconnect') {
217
+ if (args.length < 2) {
218
+ return {
219
+ type: 'error',
220
+ message: 'Usage: /server disconnect <server_name>',
221
+ };
222
+ }
223
+ const serverName = args[1];
224
+ if (!serverName) {
225
+ return {
226
+ type: 'error',
227
+ message: 'Server name is required.\n\nUsage: /server disconnect <server_name>',
228
+ };
229
+ }
230
+ return {
231
+ type: 'info',
232
+ message: `Server disconnect command received for: ${serverName}`,
233
+ data: { disconnectServer: true, serverName },
234
+ };
235
+ }
236
+ return {
237
+ type: 'error',
238
+ message: 'Usage: /server <command>\n\nCommands:\n add - Configure server\n connect <name> - Connect to server\n disconnect <name> - Disconnect server\n\nExample: /server connect airbnb',
239
+ };
240
+ }
241
+ /**
242
+ * Handles the /servers command to list all servers and their connection status.
243
+ * @returns A CommandResult with the server list
244
+ */
245
+ handleListServersCommand() {
246
+ const servers = this.getAllServers();
247
+ if (servers.length === 0) {
248
+ return {
249
+ type: 'info',
250
+ message: 'No custom servers configured.\n\nUse /server add to configure servers, then /server connect <name> to connect.',
251
+ };
252
+ }
253
+ return {
254
+ type: 'list_servers',
255
+ message: 'MCP Server Status:',
256
+ data: { servers },
257
+ };
258
+ }
259
+ /**
260
+ * Handles the /test-server command to test server configuration.
261
+ * @param args - Array where args[0] is the server name
262
+ * @returns A CommandResult with test information
263
+ */
264
+ handleTestServerCommand(args) {
265
+ if (args.length === 0) {
266
+ const configuredServers = Object.keys(this.getConfiguredServers());
267
+ if (configuredServers.length === 0) {
268
+ return {
269
+ type: 'error',
270
+ message: 'No servers configured to test.\n\nUsage: /test-server <server_name>\n\nUse /server add to configure servers first.',
271
+ };
272
+ }
273
+ return {
274
+ type: 'info',
275
+ message: `Usage: /test-server <server_name>\n\nConfigured servers: ${configuredServers.join(', ')}\n\nThis command will test if the server package can be started manually.`,
276
+ };
277
+ }
278
+ const serverName = args[0];
279
+ if (!serverName) {
280
+ return {
281
+ type: 'error',
282
+ message: 'Server name is required.\n\nUsage: /test-server <server_name>',
283
+ };
284
+ }
285
+ const result = this.getServerTestCommand(serverName);
286
+ if (!result.success) {
287
+ return {
288
+ type: 'error',
289
+ message: result.message,
290
+ };
291
+ }
292
+ return {
293
+ type: 'info',
294
+ message: `🧪 Testing server "${serverName}"...\n\nCommand: ${result.command}\n\nāš ļø Note: This will attempt to run the server command manually.\nCheck the console for output and errors.\n\nšŸ’” Try running this command manually in your terminal:\n${result.command}`,
295
+ data: { testServer: true, serverName, command: result.command },
296
+ };
297
+ }
298
+ /**
299
+ * Handles server configuration input during the interactive setup flow.
300
+ * @param input - User input string
301
+ * @param step - Current step in the configuration flow
302
+ * @param serverConfig - Partial server configuration being built
303
+ * @returns A CommandResult to continue or complete the flow
304
+ */
305
+ handleServerConfigInput(input, step, serverConfig) {
306
+ const config = serverConfig || {};
307
+ switch (step) {
308
+ case 'name_or_json':
309
+ // Check if input looks like JSON
310
+ const trimmedInput = input.trim();
311
+ if (trimmedInput.startsWith('{') &&
312
+ trimmedInput.includes('mcpServers')) {
313
+ const result = this.addServerFromJSON(trimmedInput);
314
+ if (!result.success) {
315
+ return {
316
+ type: 'error',
317
+ message: result.message,
318
+ };
319
+ }
320
+ return {
321
+ type: 'success',
322
+ message: `${result.message}.`,
323
+ data: result.data,
324
+ };
325
+ }
326
+ // Not JSON, treat as server name for interactive setup
327
+ const validation = this.validateServerName(trimmedInput);
328
+ if (!validation.valid) {
329
+ return {
330
+ type: 'error',
331
+ message: validation.message,
332
+ };
333
+ }
334
+ config.name = trimmedInput;
335
+ return {
336
+ type: 'prompt_server_config',
337
+ message: `Server name: ${config.name}\n\nEnter the command to run this server (e.g., "npx", "node", "python"):`,
338
+ data: { step: 'command', config },
339
+ };
340
+ case 'name':
341
+ const nameValidation = this.validateServerName(input.trim());
342
+ if (!nameValidation.valid) {
343
+ return {
344
+ type: 'error',
345
+ message: nameValidation.message,
346
+ };
347
+ }
348
+ config.name = input.trim();
349
+ return {
350
+ type: 'prompt_server_config',
351
+ message: `Server name: ${config.name}\n\nEnter the command to run this server (e.g., "npx", "node", "python"):`,
352
+ data: { step: 'command', config },
353
+ };
354
+ case 'command':
355
+ if (!input.trim()) {
356
+ return {
357
+ type: 'error',
358
+ message: 'Command cannot be empty.',
359
+ };
360
+ }
361
+ config.command = input.trim();
362
+ return {
363
+ type: 'prompt_server_config',
364
+ message: `Server name: ${config.name}\nCommand: ${config.command}\n\nEnter arguments (space-separated, or press Enter for none):\nExample: "-y @modelcontextprotocol/server-filesystem /tmp"`,
365
+ data: { step: 'args', config },
366
+ };
367
+ case 'args':
368
+ config.args = input.trim() ? input.trim().split(/\s+/) : [];
369
+ return {
370
+ type: 'prompt_server_config',
371
+ message: `Server name: ${config.name}\nCommand: ${config.command}\nArgs: ${config.args.length > 0 ? config.args.join(' ') : 'none'}\n\nEnter environment variables (KEY=VALUE format, one per line, or press Enter for none):\nExample: "DEBUG=1" or press Enter to skip:`,
372
+ data: { step: 'env', config },
373
+ };
374
+ case 'env':
375
+ config.env = this.parseEnvironmentVariables(input);
376
+ return {
377
+ type: 'prompt_server_config',
378
+ message: `Server Configuration Summary:\n\nName: ${config.name}\nCommand: ${config.command}\nArgs: ${config.args.length > 0 ? config.args.join(' ') : 'none'}\nEnv: ${Object.keys(config.env).length > 0
379
+ ? Object.entries(config.env)
380
+ .map(([k, v]) => `${k}=${v}`)
381
+ .join(', ')
382
+ : 'none'}\n\nConfirm to add this server? (y/n):`,
383
+ data: { step: 'confirm', config },
384
+ };
385
+ case 'confirm':
386
+ if (input.trim().toLowerCase() === 'y' ||
387
+ input.trim().toLowerCase() === 'yes') {
388
+ const serverConfig = {
389
+ command: config.command,
390
+ args: config.args,
391
+ env: config.env,
392
+ };
393
+ const result = this.addServer(config.name, serverConfig);
394
+ if (!result.success) {
395
+ return {
396
+ type: 'error',
397
+ message: result.message,
398
+ };
399
+ }
400
+ return {
401
+ type: 'success',
402
+ message: `${result.message}.`,
403
+ data: result.data,
404
+ };
405
+ }
406
+ else if (input.trim().toLowerCase() === 'n' ||
407
+ input.trim().toLowerCase() === 'no') {
408
+ return {
409
+ type: 'info',
410
+ message: 'Server configuration cancelled.',
411
+ };
412
+ }
413
+ else {
414
+ return {
415
+ type: 'error',
416
+ message: 'Please enter "y" for yes or "n" for no.',
417
+ };
418
+ }
419
+ default:
420
+ return {
421
+ type: 'error',
422
+ message: 'Invalid server configuration step.',
423
+ };
424
+ }
425
+ }
426
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,47 @@
1
+ import type { CommandResult } from '../types.js';
2
+ import type { LLMService } from './llm-service.js';
3
+ import type { MCPConfigService } from './mcp-config-service.js';
4
+ /**
5
+ * Service that handles utility commands like help, logs, history, and status.
6
+ * These commands provide information and debugging capabilities but don't
7
+ * modify the core application state.
8
+ */
9
+ export declare class UtilityService {
10
+ private llmService;
11
+ /**
12
+ * Creates a new UtilityService instance.
13
+ * @param deps - Dependencies required by the service
14
+ * @param deps.llmService - Service for LLM operations (used for status)
15
+ * @param deps.mcpConfigService - Service for MCP config (used for status)
16
+ */
17
+ constructor(deps: {
18
+ llmService: LLMService;
19
+ mcpConfigService: MCPConfigService;
20
+ });
21
+ /**
22
+ * Handles the /help command to show available commands.
23
+ * @returns A CommandResult with the help text
24
+ */
25
+ handleHelpCommand(): CommandResult;
26
+ /**
27
+ * Handles the /status command to show current configuration.
28
+ * @returns A CommandResult with the current status
29
+ */
30
+ handleStatusCommand(): CommandResult;
31
+ /**
32
+ * Handles the /logs command and its subcommands.
33
+ * @param args - Array of arguments where args[0] is the subcommand
34
+ * @returns A CommandResult with log information
35
+ */
36
+ handleLogsCommand(args: string[]): CommandResult;
37
+ /**
38
+ * Handles the /clearlogs command to clear debug logs.
39
+ * @returns A CommandResult indicating success or failure
40
+ */
41
+ handleClearLogsCommand(): CommandResult;
42
+ /**
43
+ * Handles the /history command to show input history navigation info.
44
+ * @returns A CommandResult with history navigation instructions
45
+ */
46
+ handleHistoryCommand(): CommandResult;
47
+ }
@@ -0,0 +1,208 @@
1
+ import { Logger } from '../logger.js';
2
+ /**
3
+ * Service that handles utility commands like help, logs, history, and status.
4
+ * These commands provide information and debugging capabilities but don't
5
+ * modify the core application state.
6
+ */
7
+ export class UtilityService {
8
+ /**
9
+ * Creates a new UtilityService instance.
10
+ * @param deps - Dependencies required by the service
11
+ * @param deps.llmService - Service for LLM operations (used for status)
12
+ * @param deps.mcpConfigService - Service for MCP config (used for status)
13
+ */
14
+ constructor(deps) {
15
+ Object.defineProperty(this, "llmService", {
16
+ enumerable: true,
17
+ configurable: true,
18
+ writable: true,
19
+ value: void 0
20
+ });
21
+ this.llmService = deps.llmService;
22
+ // mcpConfigService is passed but not used currently
23
+ // Kept in constructor signature for potential future use and API consistency
24
+ }
25
+ /**
26
+ * Handles the /help command to show available commands.
27
+ * @returns A CommandResult with the help text
28
+ */
29
+ handleHelpCommand() {
30
+ const helpText = `
31
+ Available slash commands:
32
+
33
+ šŸ¤– Get Started:
34
+ /model <provider> <model> - Choose your LLM (CLI handles API key setup)
35
+ /models [provider] - List available models for a provider
36
+
37
+ šŸ”Œ MCP Servers:
38
+ /server add - Configure a new server (auto-connects)
39
+ /server connect <name> - Connect to a configured server by name
40
+ /server disconnect <name> - Disconnect from a connected server
41
+ /servers - List servers and their connection status
42
+ /tools - Show available tools from connected servers
43
+ /test-server <name> - Test if a server package can be started
44
+
45
+ šŸ”‘ API Keys (automatic):
46
+ /setkey <provider> <key> - Set API key manually (stored securely)
47
+ /clearkeys - Clear all stored API keys
48
+
49
+ āš™ļø Configuration:
50
+ /config temp <value> - Set temperature (0.0-2.0)
51
+ /config tokens <value> - Set max tokens
52
+ /help - Show this help
53
+
54
+ šŸ› ļø Debugging & History:
55
+ /logs [path|tail] - View debug logs (written to ~/.mcp-use-cli/debug.log)
56
+ /clearlogs - Clear debug logs
57
+ /history - Info about input history navigation (↑↓ arrows)
58
+
59
+ šŸ“‹ Quick Start Examples:
60
+ /model openai gpt-4o-mini
61
+ /server add # Interactive server setup
62
+ /servers
63
+ /config temp 0.5
64
+ `.trim();
65
+ return {
66
+ type: 'info',
67
+ message: helpText,
68
+ };
69
+ }
70
+ /**
71
+ * Handles the /status command to show current configuration.
72
+ * @returns A CommandResult with the current status
73
+ */
74
+ handleStatusCommand() {
75
+ const availableProviders = this.llmService.getAvailableProviders();
76
+ const currentConfig = this.llmService.getCurrentConfig();
77
+ const apiKeyStatus = this.llmService.getApiKeyStatus();
78
+ let statusText = 'šŸ¤– Current Configuration:\n\n';
79
+ // API Keys status
80
+ statusText += 'šŸ”‘ API Keys:\n';
81
+ Object.entries(apiKeyStatus).forEach(([provider, status]) => {
82
+ if (status.status === 'set') {
83
+ statusText += ` • ${provider}: ${status.masked} (${status.source})\n`;
84
+ }
85
+ else {
86
+ statusText += ` • ${provider}: āŒ not set\n`;
87
+ }
88
+ });
89
+ statusText += '\n';
90
+ // Current model
91
+ if (!currentConfig) {
92
+ if (availableProviders.length === 0) {
93
+ statusText += 'āš ļø No model selected\n';
94
+ statusText += '\nChoose a model to get started:\n';
95
+ statusText += '• /model openai gpt-4o-mini\n';
96
+ statusText += '• /model anthropic claude-3-5-sonnet-20241022\n';
97
+ statusText += '• /model google gemini-1.5-pro\n';
98
+ statusText += '\nThe CLI will help you set up API keys when needed.';
99
+ }
100
+ else {
101
+ statusText += 'āš ļø No model selected\n';
102
+ statusText += `\nAvailable providers: ${availableProviders.join(', ')}\n`;
103
+ statusText += 'Use /model <provider> <model> to get started';
104
+ }
105
+ }
106
+ else {
107
+ statusText += `šŸŽÆ Active Model:\n`;
108
+ statusText += ` Provider: ${currentConfig.provider}\n`;
109
+ statusText += ` Model: ${currentConfig.model}\n`;
110
+ statusText += ` Temperature: ${currentConfig.temperature || 0.7}\n`;
111
+ statusText += ` Max Tokens: ${currentConfig.maxTokens || 'default'}\n`;
112
+ statusText +=
113
+ '\nUse /model to switch models or /config to adjust settings';
114
+ }
115
+ return {
116
+ type: 'info',
117
+ message: statusText,
118
+ };
119
+ }
120
+ /**
121
+ * Handles the /logs command and its subcommands.
122
+ * @param args - Array of arguments where args[0] is the subcommand
123
+ * @returns A CommandResult with log information
124
+ */
125
+ handleLogsCommand(args) {
126
+ const logPath = Logger.getLogPath();
127
+ if (args.length === 0) {
128
+ return {
129
+ type: 'info',
130
+ message: `šŸ“‹ Debug logs are written to:\n${logPath}\n\nCommands:\n /logs path - Show log file path\n /logs tail - Show recent log entries\n /clearlogs - Clear all logs\n\nTo view logs in real-time:\n tail -f ${logPath}`,
131
+ };
132
+ }
133
+ const subcommand = args[0];
134
+ switch (subcommand) {
135
+ case 'path':
136
+ return {
137
+ type: 'info',
138
+ message: `šŸ“ Log file location:\n${logPath}`,
139
+ };
140
+ case 'tail':
141
+ try {
142
+ const fs = require('fs');
143
+ if (!fs.existsSync(logPath)) {
144
+ return {
145
+ type: 'info',
146
+ message: 'šŸ“‹ No log file found yet. Logs will be created when the app starts logging.',
147
+ };
148
+ }
149
+ const logContent = fs.readFileSync(logPath, 'utf8');
150
+ const lines = logContent
151
+ .split('\n')
152
+ .filter((line) => line.trim());
153
+ const recentLines = lines.slice(-20); // Show last 20 lines
154
+ if (recentLines.length === 0) {
155
+ return {
156
+ type: 'info',
157
+ message: 'šŸ“‹ Log file is empty.',
158
+ };
159
+ }
160
+ return {
161
+ type: 'info',
162
+ message: `šŸ“‹ Recent log entries (last ${recentLines.length} lines):\n\n${recentLines.join('\n')}`,
163
+ };
164
+ }
165
+ catch (error) {
166
+ return {
167
+ type: 'error',
168
+ message: `āŒ Failed to read logs: ${error instanceof Error ? error.message : 'Unknown error'}`,
169
+ };
170
+ }
171
+ default:
172
+ return {
173
+ type: 'error',
174
+ message: `Unknown logs subcommand: ${subcommand}. Use /logs for help.`,
175
+ };
176
+ }
177
+ }
178
+ /**
179
+ * Handles the /clearlogs command to clear debug logs.
180
+ * @returns A CommandResult indicating success or failure
181
+ */
182
+ handleClearLogsCommand() {
183
+ try {
184
+ Logger.clearLogs();
185
+ Logger.info('Logs cleared by user command');
186
+ return {
187
+ type: 'success',
188
+ message: 'āœ… Debug logs cleared successfully.',
189
+ };
190
+ }
191
+ catch (error) {
192
+ return {
193
+ type: 'error',
194
+ message: `āŒ Failed to clear logs: ${error instanceof Error ? error.message : 'Unknown error'}`,
195
+ };
196
+ }
197
+ }
198
+ /**
199
+ * Handles the /history command to show input history navigation info.
200
+ * @returns A CommandResult with history navigation instructions
201
+ */
202
+ handleHistoryCommand() {
203
+ return {
204
+ type: 'info',
205
+ message: `šŸ“œ Input History Navigation:\n\nšŸ”¼ Arrow Up - Navigate to previous inputs\nšŸ”½ Arrow Down - Navigate to newer inputs\n\nšŸ’” Tips:\n• Your input history is automatically saved during the session\n• Use ↑ to recall previous commands and messages\n• Use ↓ to navigate back to newer inputs\n• History is reset when you restart the CLI\n\nšŸŽÆ Try it now: Press the up arrow key in the input box!`,
206
+ };
207
+ }
208
+ }