UNPKG

mini-claude-code

Version:

Advanced AI-powered coding assistant with streaming responses, context memory, intelligent auto-completion, error handling, test generation, and task planning

1,506 lines (1,276 loc) 59.1 kB
#!/usr/bin/env node const { program } = require('commander'); const readline = require('readline'); const chalk = require('chalk'); const ora = require('ora'); const path = require('path'); const ToolManager = require('../lib/tool-manager'); const CodeGenerator = require('../lib/code-generator'); const AutoCompletion = require('../lib/auto-completion'); class MiniClaudeCLI { constructor() { this.toolManager = new ToolManager(); this.codeGenerator = new CodeGenerator(); this.autoCompletion = null; this.isInteractive = false; this.rl = null; } async init() { console.log(chalk.blue.bold('🤖 Mini Claude Code')); console.log(chalk.gray('A simplified AI coding assistant\n')); // 初始化工具管理器 const spinner = ora('Initializing...').start(); try { await this.toolManager.initialize(); // 初始化自动补全系统 this.autoCompletion = new AutoCompletion(this.toolManager); spinner.succeed('Ready to assist!'); } catch (error) { spinner.fail(`Initialization failed: ${error.message}`); process.exit(1); } } async startInteractiveMode() { this.isInteractive = true; console.log(chalk.green('🚀 Interactive mode started. Type "help" for commands or "exit" to quit.')); console.log(chalk.gray('💡 Use Tab key for auto-completion\n')); // 创建 readline 接口 this.rl = readline.createInterface({ input: process.stdin, output: process.stdout, prompt: chalk.cyan('mini-claude> '), completer: this.completer.bind(this), history: this.getCommandHistory() }); // 设置 readline 事件监听器 this.setupReadlineEvents(); // 显示提示符 this.rl.prompt(); } /** * 设置 readline 事件监听器 */ setupReadlineEvents() { this.rl.on('line', async (input) => { const command = input.trim(); if (!command) { this.rl.prompt(); return; } try { await this.handleCommand(command); } catch (error) { console.error(chalk.red(`Error: ${error.message}`)); } if (this.isInteractive) { console.log(); // 添加空行 this.rl.prompt(); } }); this.rl.on('close', () => { this.exit(); }); this.rl.on('SIGINT', () => { console.log(chalk.yellow('\n👋 Use "exit" to quit or Ctrl+C again to force quit')); this.rl.prompt(); }); // 处理 Tab 键补全的特殊情况 this.rl.on('completer', (line) => { this.showIntelligentSuggestions(line); }); } /** * Tab 补全函数 */ completer(line) { try { const completions = this.autoCompletion.getCommandCompletions(line); // 提取补全文本 const hits = completions.map(comp => comp.text); // 如果只有一个匹配,直接返回 if (hits.length === 1) { return [hits, line]; } // 多个匹配时,显示所有选项 if (hits.length > 1) { console.log(); // 换行 console.log(chalk.blue('📋 Available completions:')); completions.forEach((comp, index) => { const icon = this.getCompletionIcon(comp.type); const description = comp.description ? chalk.gray(` - ${comp.description}`) : ''; console.log(` ${icon} ${chalk.green(comp.display)}${description}`); }); console.log(); // 添加空行 this.rl.prompt(); } return [hits, line]; } catch (error) { console.warn('Completion error:', error.message); return [[], line]; } } /** * 获取补全项图标 */ getCompletionIcon(type) { const icons = { 'command': '⚡', 'file': '📄', 'directory': '📁', 'file-type': '🏗️', 'filename': '📝' }; return icons[type] || '•'; } /** * 显示智能建议 */ showIntelligentSuggestions(line) { if (!line.trim()) { const suggestions = this.autoCompletion.getSmartSuggestions(); if (suggestions.length > 0) { console.log(); console.log(chalk.blue('💡 Smart suggestions:')); suggestions.forEach((suggestion, index) => { console.log(` ${index + 1}. ${chalk.green(suggestion.command)} - ${chalk.gray(suggestion.description)}`); }); console.log(); } } } /** * 获取命令历史 */ getCommandHistory() { return this.toolManager.context.session.commands .filter(cmd => cmd.input && typeof cmd.input === 'string') .map(cmd => cmd.input) .slice(-50); // 最近50个命令 } async handleCommand(input) { const args = input.split(' '); const command = args[0].toLowerCase(); const params = args.slice(1); switch (command) { case 'help': this.showHelp(); break; case 'exit': case 'quit': this.exit(); break; case 'analyze': await this.analyzeProject(params[0]); break; case 'read': await this.readFile(params[0]); break; case 'write': await this.writeFile(params[0], params.slice(1).join(' ')); break; case 'edit': await this.editFile(params[0], params[1], params.slice(2).join(' ')); break; case 'search': await this.searchInFiles(params[0], params[1]); break; case 'run': await this.runCommand(params.join(' ')); break; case 'create': await this.createFile(params[0], params[1]); break; case 'list': await this.listFiles(params[0] || '.'); break; case 'status': this.showStatus(); break; case 'suggest': this.showSuggestions(); break; case 'report': this.showReport(); break; case 'completion': this.showCompletionStatus(); break; case 'fix': await this.handleErrorFix(params); break; case 'check': await this.checkErrors(params[0]); break; case 'test': await this.handleTestCommand(params); break; case 'plan': await this.handlePlanCommand(params); break; case 'chat': await this.chatWithAI(params.join(' ')); break; case 'ai': await this.handleAICommand(params); break; case 'config': await this.handleConfig(params); break; case 'generate': await this.generateWithAI(params.join(' ')); break; default: if (input.length > 0) { await this.processNaturalLanguage(input); } break; } } showHelp() { console.log(chalk.yellow('\n📖 Available Commands:\n')); const commands = [ ['help', 'Show this help message'], ['analyze [path]', 'Analyze project structure'], ['read <file>', 'Read file contents'], ['write <file> <content>', 'Write content to file'], ['edit <file> <old> <new>', 'Edit file by replacing text'], ['search <term> [path]', 'Search for text in files'], ['run <command>', 'Execute shell command'], ['create <type> <name>', 'Create new file/component'], ['list [path]', 'List files in directory'], ['status', 'Show current project status'], ['completion', 'Show auto-completion statistics'], ['suggest', 'Get suggestions for next actions'], ['report', 'Generate project report'], ['', ''], [chalk.red('🔧 Error Handling:'), ''], ['fix <file> [error]', 'Analyze and fix errors in file'], ['check [path]', 'Check project for errors'], ['', ''], [chalk.blue('🧪 Test Generation:'), ''], ['test generate <file> [type]', 'Generate tests for a file'], ['test run [file]', 'Run tests'], ['test analyze <file>', 'Analyze code for testing'], ['test framework', 'Detect test framework'], ['test coverage <test> <source>', 'Generate coverage report'], ['', ''], [chalk.blue('🎯 Task Planning:'), ''], ['plan create "<description>"', 'Generate task plan from description'], ['plan execute [plan]', 'Execute current or specified plan'], ['plan status', 'Show execution status'], ['plan pause', 'Pause current execution'], ['plan resume', 'Resume paused execution'], ['plan stop', 'Stop current execution'], ['', ''], [chalk.blue('🤖 AI Commands:'), ''], ['chat <message>', 'Chat with AI assistant'], ['generate <description>', 'Generate code with AI'], ['ai status', 'Show AI service status'], ['ai review <file>', 'AI code review'], ['config set-api-key <key>', 'Set DeepSeek API key'], ['config show', 'Show current configuration'], ['', ''], [chalk.blue('🔧 Auto-Completion Features:'), ''], ['Tab', 'Auto-complete commands and file paths'], ['Tab Tab', 'Show all available completions'], ['Up/Down arrows', 'Browse command history'], ['', ''], ['exit', 'Exit the program'] ]; commands.forEach(([cmd, desc]) => { if (cmd === '') { console.log(''); } else if (cmd.includes('🤖')) { console.log(`${cmd}`); } else { console.log(` ${chalk.cyan(cmd.padEnd(25))} ${chalk.gray(desc)}`); } }); console.log(chalk.yellow('\n💡 You can also describe what you want in natural language!\n')); console.log(chalk.green('Examples:')); console.log(chalk.gray(' "创建一个React组件叫做Header"')); console.log(chalk.gray(' "搜索所有包含TODO的文件"')); console.log(chalk.gray(' "运行npm install"')); console.log(chalk.gray(' "Show me the package.json file"')); console.log(chalk.gray(' "Generate a login form component"\n')); } async analyzeProject(projectPath) { const spinner = ora('Analyzing project...').start(); try { const result = await this.toolManager.initialize(projectPath); if (result.success) { spinner.succeed('Project analysis complete'); this.displayProjectInfo(result.analysis); } else { spinner.fail(`Analysis failed: ${result.error}`); } } catch (error) { spinner.fail(`Analysis error: ${error.message}`); } } async readFile(filePath) { if (!filePath) { console.log(chalk.red('❌ Please specify a file path')); return; } const spinner = ora(`Reading ${filePath}...`).start(); try { const result = await this.toolManager.execute('fs', 'readFile', filePath); if (result.success) { spinner.succeed(`File read successfully (${result.lines} lines, ${result.size} bytes)`); console.log(chalk.gray('\\n--- File Content ---')); console.log(result.content); console.log(chalk.gray('--- End of File ---\\n')); } else { spinner.fail(`Failed to read file: ${result.error}`); } } catch (error) { spinner.fail(`Read error: ${error.message}`); } } async writeFile(filePath, content) { if (!filePath || !content) { console.log(chalk.red('❌ Please specify both file path and content')); return; } const spinner = ora(`Writing to ${filePath}...`).start(); try { const result = await this.toolManager.execute('fs', 'writeFile', filePath, content); if (result.success) { spinner.succeed(`File written successfully: ${filePath}`); } else { spinner.fail(`Failed to write file: ${result.error}`); } } catch (error) { spinner.fail(`Write error: ${error.message}`); } } async editFile(filePath, oldText, newText) { if (!filePath || !oldText || !newText) { console.log(chalk.red('❌ Please specify file path, old text, and new text')); return; } const spinner = ora(`Editing ${filePath}...`).start(); try { const result = await this.toolManager.execute('fs', 'editFile', filePath, oldText, newText); if (result.success) { spinner.succeed(`File edited successfully: ${filePath}`); } else { spinner.fail(`Failed to edit file: ${result.error}`); } } catch (error) { spinner.fail(`Edit error: ${error.message}`); } } async searchInFiles(searchTerm, searchPath = '.') { if (!searchTerm) { console.log(chalk.red('❌ Please specify a search term')); return; } const spinner = ora(`Searching for "${searchTerm}"...`).start(); try { const result = await this.toolManager.execute('fs', 'searchInFiles', searchPath, searchTerm); if (result.success) { const totalMatches = result.results.reduce((sum, file) => sum + file.matches.length, 0); spinner.succeed(`Found ${totalMatches} matches in ${result.results.length} files`); result.results.forEach(file => { console.log(chalk.blue(`\\n📄 ${file.file}:`)); file.matches.slice(0, 3).forEach(match => { // Show max 3 matches per file console.log(` ${chalk.yellow(`Line ${match.line}:`)} ${match.content}`); }); if (file.matches.length > 3) { console.log(chalk.gray(` ... and ${file.matches.length - 3} more matches`)); } }); } else { spinner.fail(`Search failed: ${result.error}`); } } catch (error) { spinner.fail(`Search error: ${error.message}`); } } async runCommand(command) { if (!command) { console.log(chalk.red('❌ Please specify a command to run')); return; } const spinner = ora(`Running: ${command}`).start(); try { const result = await this.toolManager.execute('cmd', 'run', command); if (result.success) { spinner.succeed(`Command completed`); if (result.stdout) { console.log(chalk.green('📤 Output:')); console.log(result.stdout); } if (result.stderr) { console.log(chalk.yellow('⚠️ Warnings:')); console.log(result.stderr); } } else { spinner.fail(`Command failed: ${result.error}`); if (result.stderr) { console.log(chalk.red('📥 Error output:')); console.log(result.stderr); } } } catch (error) { spinner.fail(`Command error: ${error.message}`); } } async createFile(fileType, fileName) { if (!fileType || !fileName) { console.log(chalk.red('❌ Please specify file type and name')); console.log(chalk.gray('Example: create component MyComponent')); return; } const spinner = ora(`Creating ${fileType}: ${fileName}...`).start(); try { const result = await this.codeGenerator.generateFile(fileType, fileName, this.toolManager.context.projectInfo); if (result.success) { // Write the generated file const writeResult = await this.toolManager.execute('fs', 'writeFile', result.filePath, result.content); if (writeResult.success) { spinner.succeed(`Created ${fileType}: ${result.filePath}`); } else { spinner.fail(`Failed to write file: ${writeResult.error}`); } } else { spinner.fail(`Failed to generate ${fileType}: ${result.error}`); } } catch (error) { spinner.fail(`Creation error: ${error.message}`); } } async listFiles(dirPath) { const spinner = ora(`Listing files in ${dirPath}...`).start(); try { const result = await this.toolManager.execute('fs', 'listFiles', dirPath); if (result.success) { spinner.succeed(`Found ${result.files.length} items`); result.files.forEach(file => { const icon = file.isDirectory ? '📁' : '📄'; const size = file.isDirectory ? '' : ` (${file.size} bytes)`; console.log(` ${icon} ${file.name}${size}`); }); } else { spinner.fail(`Failed to list files: ${result.error}`); } } catch (error) { spinner.fail(`List error: ${error.message}`); } } showStatus() { const context = this.toolManager.getContext(); console.log(chalk.blue('\\n📊 Current Status:\\n')); console.log(`🗂️ Working Directory: ${context.currentDirectory}`); console.log(`⏰ Session Started: ${context.session.startTime.toLocaleString()}`); console.log(`🔧 Commands Executed: ${context.session.commands.length}`); if (context.projectInfo) { console.log(`\\n📦 Project Info:`); console.log(` Languages: ${Object.keys(context.projectInfo.languages).join(', ') || 'None'}`); console.log(` Frameworks: ${context.projectInfo.frameworks.map(f => f.name).join(', ') || 'None'}`); console.log(` Package Manager: ${context.projectInfo.packageManager || 'None'}`); console.log(` Total Files: ${context.projectInfo.totalFiles}`); console.log(` Code Files: ${context.projectInfo.codeFiles}`); } console.log(''); } showSuggestions() { const suggestions = this.toolManager.suggestNextActions(); if (suggestions.length === 0) { console.log(chalk.yellow('💡 No specific suggestions at the moment. Try analyzing the project first!')); return; } console.log(chalk.blue('\\n💡 Suggested Actions:\\n')); suggestions.forEach((suggestion, index) => { console.log(`${index + 1}. ${chalk.green(suggestion.action)}: ${suggestion.description}`); console.log(` ${chalk.gray('Command:')} ${chalk.cyan(suggestion.command)}`); console.log(''); }); } showReport() { const report = this.toolManager.generateReport(); console.log(chalk.blue('\\n📋 Project Report\\n')); console.log(chalk.yellow('Summary:')); Object.entries(report.summary).forEach(([key, value]) => { console.log(` ${key}: ${value}`); }); if (report.project) { console.log(chalk.yellow('\\nProject Details:')); console.log(` Languages: ${Object.entries(report.project.languages).map(([lang, count]) => `${lang} (${count})`).join(', ')}`); console.log(` Frameworks: ${report.project.frameworks.map(f => `${f.name} (${(f.confidence * 100).toFixed(0)}%)`).join(', ')}`); } if (report.recentCommands.length > 0) { console.log(chalk.yellow('\\nRecent Commands:')); report.recentCommands.slice(-5).forEach(cmd => { const status = cmd.result ? '✅' : '❌'; console.log(` ${status} ${cmd.tool}.${cmd.method} (${cmd.duration}ms)`); }); } console.log(''); } async processNaturalLanguage(input) { console.log(chalk.cyan(`🤔 Processing: "${input}"`)); try { const result = await this.toolManager.processNaturalLanguage(input); if (result.success) { if (result.response) { // 如果是 AI 响应,使用流式显示已经在处理过程中完成了 if (!result.isStreaming) { console.log(chalk.blue('\n🤖 AI Response:')); console.log(result.response); } } else if (result.message) { console.log(chalk.green(`✅ ${result.message}`)); } else { console.log(chalk.green('✅ Command executed successfully')); } if (result.filePath) { console.log(chalk.gray(`📄 File: ${result.filePath}`)); } } else { console.log(chalk.red(`❌ ${result.error}`)); if (result.suggestion) { console.log(chalk.yellow(`💡 Suggestion: ${result.suggestion}`)); } } } catch (error) { console.log(chalk.red(`❌ Error: ${error.message}`)); } } async chatWithAI(message) { if (!message.trim()) { console.log(chalk.red('❌ Please provide a message to chat')); return; } // 检查是否使用流式响应(默认启用) const useStreaming = true; if (useStreaming) { await this.chatWithAIStreaming(message); } else { await this.chatWithAIClassic(message); } } async chatWithAIStreaming(message) { console.log(chalk.blue('\n🤖 AI Assistant:')); let responseStarted = false; let fullResponse = ''; try { // 生成包含历史记忆的上下文提示 const contextPrompt = this.toolManager.memory.generateContextPrompt(message, this.toolManager.context.projectInfo); const result = await this.toolManager.ai.chatStream(message, { systemPrompt: `你是一个专业的编程助手。 ${contextPrompt} 请基于以上上下文信息提供有帮助的、专业的回答。如果用户询问过类似问题,请参考历史对话。如果有相关代码片段,可以引用它们。`, temperature: 0.7, timeout: 45000 }, async (data) => { if (!responseStarted) { process.stdout.write(chalk.green('')); responseStarted = true; } if (data.content) { process.stdout.write(data.content); fullResponse = data.fullResponse; } if (data.isComplete) { process.stdout.write('\n'); } }); if (result.success) { console.log(chalk.gray(`\n💬 Completed in ${result.totalChunks || 0} chunks`)); // 保存对话到记忆系统 await this.toolManager.memory.addConversation(message, result.response, { projectPath: this.toolManager.context.currentDirectory, language: this.toolManager.context.projectInfo?.languages ? Object.keys(this.toolManager.context.projectInfo.languages)[0] : null, framework: this.toolManager.context.projectInfo?.frameworks?.length > 0 ? this.toolManager.context.projectInfo.frameworks[0].name : null, commandType: 'ai_chat_stream' }); // 记录聊天历史 this.toolManager.context.session.commands.push({ input: message, type: 'ai_chat_stream', result: true, response: result.response, timestamp: new Date() }); } else { if (!responseStarted) { console.log(chalk.red(`❌ ${result.error}`)); } else { console.log(chalk.red(`\n❌ Stream interrupted: ${result.error}`)); if (result.partialResponse) { console.log(chalk.yellow('📝 Partial response received.')); } } } } catch (error) { console.log(chalk.red(`\n❌ Streaming error: ${error.message}`)); } } async chatWithAIClassic(message) { const spinner = ora('🤖 AI is thinking...').start(); try { const result = await this.toolManager.chat(message); if (result.success) { spinner.succeed('AI response received'); console.log(chalk.blue('\n🤖 AI Assistant:')); console.log(result.response); if (result.usage) { console.log(chalk.gray(`\n📊 Tokens used: ${result.usage.total_tokens}`)); } } else { spinner.fail('AI chat failed'); console.log(chalk.red(`❌ ${result.error}`)); if (result.suggestion) { console.log(chalk.yellow(`💡 ${result.suggestion}`)); } } } catch (error) { spinner.fail('Chat error'); console.log(chalk.red(`❌ Error: ${error.message}`)); } } async handleAICommand(params) { const subCommand = params[0]; switch (subCommand) { case 'status': this.showAIStatus(); break; case 'review': await this.reviewCodeWithAI(params[1]); break; default: console.log(chalk.red('❌ Unknown AI command. Available: status, review')); console.log(chalk.gray('Usage: ai status | ai review <file>')); } } async handleConfig(params) { const subCommand = params[0]; switch (subCommand) { case 'set-api-key': await this.setApiKey(params[1]); break; case 'show': this.showConfig(); break; default: console.log(chalk.red('❌ Unknown config command. Available: set-api-key, show')); console.log(chalk.gray('Usage: config set-api-key <key> | config show')); } } async generateWithAI(description) { if (!description.trim()) { console.log(chalk.red('❌ Please provide a description for code generation')); return; } console.log(chalk.blue('\n🎨 AI Code Generator:')); console.log(chalk.gray(`Generating code for: "${description}"`)); let responseStarted = false; let fullCode = ''; try { const result = await this.toolManager.ai.chatStream( `请根据以下描述生成高质量的代码。只返回代码,不要包含解释文字: ${description} 项目上下文: ${this.toolManager.context.projectInfo ? JSON.stringify(this.toolManager.context.projectInfo, null, 2) : '通用项目'}`, { systemPrompt: `你是一个专业的代码生成专家。根据用户需求生成干净、高效、符合最佳实践的代码。 - 包含必要的注释和错误处理 - 遵循代码规范和风格指南 - 确保代码可以直接使用 - 只返回代码,不要包含markdown格式或解释`, temperature: 0.3, maxTokens: 3000 }, async (data) => { if (!responseStarted) { console.log(chalk.green('\n--- Generated Code ---')); responseStarted = true; } if (data.content) { process.stdout.write(data.content); fullCode = data.fullResponse; } if (data.isComplete) { process.stdout.write('\n'); console.log(chalk.gray('--- End of Code ---')); } } ); if (result.success && fullCode.trim()) { // 自动保存生成的代码 const timestamp = Date.now(); const filePath = `generated_${timestamp}.js`; const saveResult = await this.toolManager.execute('fs', 'writeFile', filePath, fullCode); if (saveResult.success) { console.log(chalk.blue(`\n📄 Code saved to: ${filePath}`)); console.log(chalk.gray(`💾 ${fullCode.split('\n').length} lines generated`)); } else { console.log(chalk.yellow(`⚠️ Could not save file: ${saveResult.error}`)); } // 保存生成的代码到记忆系统 const language = this.toolManager.detectLanguageFromCode(fullCode); await this.toolManager.memory.addCodeSnippet( fullCode, description, language, { projectPath: this.toolManager.context.currentDirectory, framework: this.toolManager.context.projectInfo?.frameworks?.length > 0 ? this.toolManager.context.projectInfo.frameworks[0].name : null, fileType: 'generated' } ); // 记录到历史 this.toolManager.context.session.commands.push({ input: description, type: 'ai_code_generation', result: true, filePath: filePath, timestamp: new Date() }); } else { console.log(chalk.red(`❌ ${result.error || 'No code generated'}`)); } } catch (error) { console.log(chalk.red(`\n❌ Generation error: ${error.message}`)); } } async reviewCodeWithAI(filePath) { if (!filePath) { console.log(chalk.red('❌ Please specify a file to review')); return; } const spinner = ora(`🔍 AI is reviewing ${filePath}...`).start(); try { // 先读取文件 const fileResult = await this.toolManager.execute('fs', 'readFile', filePath); if (!fileResult.success) { spinner.fail('Failed to read file'); console.log(chalk.red(`❌ ${fileResult.error}`)); return; } // AI 审查代码 const result = await this.toolManager.ai.reviewCode(fileResult.content, filePath); if (result.success) { spinner.succeed('Code review completed'); console.log(chalk.blue('\n📋 AI Code Review:')); console.log(result.response); } else { spinner.fail('Code review failed'); console.log(chalk.red(`❌ ${result.error}`)); } } catch (error) { spinner.fail('Review error'); console.log(chalk.red(`❌ Error: ${error.message}`)); } } async setApiKey(apiKey) { if (!apiKey) { console.log(chalk.red('❌ Please provide an API key')); return; } const spinner = ora('💾 Saving API key...').start(); try { const result = await this.toolManager.configureAPI(apiKey); if (result.success) { spinner.succeed('API key configured successfully'); console.log(chalk.green('✅ DeepSeek AI is now available!')); } else { spinner.fail('Failed to save API key'); console.log(chalk.red(`❌ ${result.error}`)); } } catch (error) { spinner.fail('Configuration error'); console.log(chalk.red(`❌ Error: ${error.message}`)); } } showAIStatus() { const status = this.toolManager.getAIStatus(); console.log(chalk.blue('\n🤖 AI Service Status:\n')); console.log(`🔌 Available: ${status.available ? chalk.green('Yes') : chalk.red('No')}`); if (status.stats.isConfigured) { console.log(`🔑 API Key: ${chalk.gray(status.stats.apiKey)}`); console.log(`📊 Models: ${Object.values(status.stats.availableModels).join(', ')}`); } else { console.log(chalk.yellow('⚠️ API key not configured')); console.log(chalk.gray(' Use: config set-api-key <your-deepseek-api-key>')); } console.log(`\n🧠 NLP Status:`); console.log(` Supported Actions: ${status.nlpStats.supportedActions.length}`); console.log(` File Types: ${status.nlpStats.supportedFileTypes.length}`); // 显示记忆统计 console.log(`\n💾 Memory System:`); console.log(` Conversations: ${status.memoryStats.conversationCount}`); console.log(` Code Snippets: ${status.memoryStats.codeSnippetCount}`); console.log(` User Preferences: ${status.memoryStats.userPreferences}`); console.log(` Projects Remembered: ${status.memoryStats.projectsRemembered}`); console.log(` Current Session: ${status.memoryStats.currentSessionId}`); console.log(''); } showConfig() { const status = this.toolManager.getAIStatus(); console.log(chalk.blue('\n⚙️ Configuration:\n')); console.log(`AI Service: ${status.available ? chalk.green('Enabled') : chalk.red('Disabled')}`); console.log(`API Key: ${status.stats.apiKey || chalk.gray('Not set')}`); console.log(`Base URL: https://api.deepseek.com`); console.log(`Models: ${Object.values(status.stats.availableModels).join(', ')}`); console.log(''); } showCompletionStatus() { if (!this.autoCompletion) { console.log(chalk.red('❌ Auto-completion system not initialized')); return; } const stats = this.autoCompletion.getStats(); console.log(chalk.blue('\n🔧 Auto-Completion System Status:\n')); console.log(`⚡ Base Commands: ${stats.baseCommands}`); console.log(`🗃️ Cache Size: ${stats.cacheSize} entries`); console.log(`🏗️ File Types: ${stats.supportedFileTypes}`); console.log(`📄 Extensions: ${stats.commonExtensions}`); console.log(chalk.blue('\n📋 Available Completion Types:')); console.log(' • Commands and sub-commands'); console.log(' • File paths and directories'); console.log(' • File types and extensions'); console.log(' • Project-specific suggestions'); console.log(' • Command history'); console.log(chalk.blue('\n💡 Usage Tips:')); console.log(' • Press Tab to auto-complete'); console.log(' • Press Tab twice to see all options'); console.log(' • Use Up/Down arrows for history'); console.log(' • Type partial commands for smart suggestions'); console.log(''); } async handleErrorFix(params) { const filePath = params[0]; const errorMessage = params.slice(1).join(' '); if (!filePath) { console.log(chalk.red('❌ Please specify a file to analyze/fix')); console.log(chalk.gray('Usage: fix <file> [error_message]')); return; } const spinner = ora(`🔍 Analyzing errors in ${filePath}...`).start(); try { let analysis; if (errorMessage) { // 分析特定错误 analysis = await this.toolManager.analyzeError(errorMessage, filePath); } else { // 验证文件并检查错误 const validation = await this.toolManager.validateFix(filePath); if (validation.success) { spinner.succeed('No errors found in file'); console.log(chalk.green(`✅ ${filePath} appears to be error-free`)); return; } else if (validation.stillHasErrors) { analysis = await this.toolManager.analyzeError(validation.error, filePath); } else { spinner.fail('Could not validate file'); console.log(chalk.red(`❌ ${validation.error}`)); return; } } if (!analysis.success) { spinner.fail('Error analysis failed'); console.log(chalk.red(`❌ ${analysis.error}`)); return; } spinner.succeed('Error analysis complete'); const errorAnalysis = analysis.analysis; console.log(chalk.blue('\n📋 Error Analysis:')); console.log(`🔍 Error Type: ${errorAnalysis.detectedType || 'Unknown'}`); console.log(`⚠️ Severity: ${errorAnalysis.severity}`); console.log(`🔧 Auto-fixable: ${errorAnalysis.autoFixable ? 'Yes' : 'No'}`); if (errorAnalysis.suggestions.length > 0) { console.log(chalk.blue('\n💡 Suggestions:')); errorAnalysis.suggestions.forEach((suggestion, index) => { console.log(` ${index + 1}. ${suggestion}`); }); } if (errorAnalysis.aiAnalysis) { console.log(chalk.blue('\n🤖 AI Analysis:')); console.log(` Root Cause: ${errorAnalysis.aiAnalysis.rootCause}`); if (errorAnalysis.aiAnalysis.codeExample) { console.log(chalk.blue('\n📝 Code Example:')); console.log(chalk.gray(errorAnalysis.aiAnalysis.codeExample)); } } // 询问是否自动修复 if (errorAnalysis.autoFixable) { console.log(chalk.yellow('\n🤔 This error appears to be auto-fixable.')); // 在实际应用中,这里可以使用 inquirer 询问用户 // 为了演示,我们直接尝试修复 const fixSpinner = ora('🔧 Attempting auto-fix...').start(); const fixResult = await this.toolManager.autoFixError(filePath, errorAnalysis); if (fixResult.success) { fixSpinner.succeed('Auto-fix completed'); console.log(chalk.green(`✅ ${fixResult.message}`)); if (fixResult.changes) { console.log(chalk.blue('\n📝 Changes made:')); fixResult.changes.forEach(change => { console.log(` • ${change}`); }); } if (fixResult.backup) { console.log(chalk.gray(`💾 Backup created: ${fixResult.backup}`)); } // 验证修复结果 const validation = await this.toolManager.validateFix(filePath); if (validation.success) { console.log(chalk.green('✅ Fix verified successfully')); } else { console.log(chalk.yellow('⚠️ Fix applied, but validation failed:')); console.log(chalk.gray(validation.error)); } } else { fixSpinner.fail('Auto-fix failed'); console.log(chalk.red(`❌ ${fixResult.error}`)); if (fixResult.manual) { console.log(chalk.yellow('💡 Manual fix required')); if (fixResult.suggestion) { console.log(` ${fixResult.suggestion}`); } } } } else { console.log(chalk.yellow('\n⚠️ This error requires manual fixing')); } } catch (error) { spinner.fail('Error handling failed'); console.log(chalk.red(`❌ ${error.message}`)); } } async checkErrors(projectPath) { const checkPath = projectPath || '.'; const spinner = ora(`🔍 Checking for errors in ${checkPath}...`).start(); try { const result = await this.toolManager.checkProjectErrors(checkPath); if (!result.success) { spinner.fail('Error check failed'); console.log(chalk.red(`❌ ${result.error}`)); return; } spinner.succeed('Error check complete'); console.log(chalk.blue('\n📊 Error Check Results:')); console.log(`📁 Files scanned: ${result.totalFiles}`); console.log(`❌ Files with errors: ${result.errorFiles}`); if (result.errorFiles === 0) { console.log(chalk.green('\n🎉 No errors found in the project!')); return; } console.log(chalk.blue('\n📋 Errors found:')); result.results.forEach((fileResult, index) => { console.log(`\n${index + 1}. ${chalk.yellow(fileResult.file)}`); console.log(` Error: ${fileResult.error.substring(0, 100)}...`); if (fileResult.analysis) { console.log(` Type: ${fileResult.analysis.detectedType || 'Unknown'}`); console.log(` Severity: ${fileResult.analysis.severity}`); console.log(` Auto-fixable: ${fileResult.analysis.autoFixable ? 'Yes' : 'No'}`); } }); // 统计可自动修复的错误 const autoFixableCount = result.results.filter(r => r.analysis && r.analysis.autoFixable ).length; if (autoFixableCount > 0) { console.log(chalk.green(`\n🔧 ${autoFixableCount} errors can be auto-fixed`)); console.log(chalk.gray('Use "fix <file>" to fix individual files')); } // 显示错误处理统计 const errorStats = this.toolManager.getErrorHandlerStats(); console.log(chalk.blue('\n📊 Error Handler Capabilities:')); console.log(` Supported Error Types: ${errorStats.supportedErrorTypes}`); console.log(` Auto-fixable Types: ${errorStats.autoFixableTypes}`); console.log(` Fix Strategies: ${errorStats.fixStrategies}`); } catch (error) { spinner.fail('Error check failed'); console.log(chalk.red(`❌ ${error.message}`)); } } async handleTestCommand(params) { const subCommand = params[0]; switch (subCommand) { case 'generate': await this.generateTest(params[1], params[2]); break; case 'run': await this.runTest(params[1]); break; case 'analyze': await this.analyzeForTests(params[1]); break; case 'framework': await this.detectTestFramework(); break; case 'coverage': await this.generateCoverage(params[1], params[2]); break; default: console.log(chalk.red('❌ Unknown test command. Available: generate, run, analyze, framework, coverage')); console.log(chalk.gray('Usage: test generate <file> [type] | test run [file] | test analyze <file> | test framework | test coverage <test> <source>')); } } async generateTest(filePath, testType = 'unit') { if (!filePath) { console.log(chalk.red('❌ Please specify a file to generate tests for')); console.log(chalk.gray('Usage: test generate <file> [type]')); return; } const spinner = ora(`🧪 Generating ${testType} tests for ${filePath}...`).start(); try { const result = await this.toolManager.generateTests(filePath, testType); if (!result.success) { spinner.fail('Test generation failed'); console.log(chalk.red(`❌ ${result.error}`)); return; } spinner.succeed('Test generation completed'); console.log(chalk.blue('\n🧪 Test Generation Results:')); console.log(`📄 Test file: ${result.testFilePath}`); console.log(`🔧 Framework: ${result.framework}`); console.log(`📊 Test type: ${testType}`); if (result.analysis) { console.log(chalk.blue('\n📋 Code Analysis:')); console.log(` Functions: ${result.analysis.functions.length}`); console.log(` Classes: ${result.analysis.classes.length}`); console.log(` File type: ${result.analysis.fileType}`); console.log(` Complexity: ${result.analysis.complexity}`); } if (result.suggestions && result.suggestions.length > 0) { console.log(chalk.blue('\n💡 Optimization Suggestions:')); result.suggestions.forEach((suggestion, index) => { console.log(` ${index + 1}. ${suggestion}`); }); } // 询问是否保存测试文件 console.log(chalk.yellow('\n💾 Saving test file...')); const writeResult = await this.toolManager.execute('fs', 'writeFile', result.testFilePath, result.testCode); if (writeResult.success) { console.log(chalk.green(`✅ Test file saved: ${result.testFilePath}`)); console.log(chalk.gray(`📝 ${result.testCode.split('\n').length} lines generated`)); } else { console.log(chalk.red(`❌ Failed to save test file: ${writeResult.error}`)); } } catch (error) { spinner.fail('Test generation error'); console.log(chalk.red(`❌ ${error.message}`)); } } async runTest(testFilePath) { const spinner = ora(`🏃 Running tests${testFilePath ? ` for ${testFilePath}` : ''}...`).start(); try { const result = await this.toolManager.runTests(testFilePath); if (result.success) { spinner.succeed('Tests completed'); console.log(chalk.green('\n✅ Test Results:')); if (result.stdout) { console.log(result.stdout); } } else { spinner.fail('Tests failed'); console.log(chalk.red('\n❌ Test Failures:')); if (result.stderr) { console.log(result.stderr); } console.log(chalk.yellow(`Exit code: ${result.exitCode}`)); } } catch (error) { spinner.fail('Test execution error'); console.log(chalk.red(`❌ ${error.message}`)); } } async analyzeForTests(filePath) { if (!filePath) { console.log(chalk.red('❌ Please specify a file to analyze')); return; } const spinner = ora(`🔍 Analyzing ${filePath} for test generation...`).start(); try { const result = await this.toolManager.analyzeCodeForTests(filePath); if (!result.success) { spinner.fail('Code analysis failed'); console.log(chalk.red(`❌ ${result.error}`)); return; } spinner.succeed('Code analysis completed'); const analysis = result.analysis; console.log(chalk.blue('\n📋 Code Analysis Results:')); console.log(`📄 File: ${analysis.filePath}`); console.log(`🏗️ Type: ${analysis.fileType}`); console.log(`📊 Complexity: ${analysis.complexity}`); if (analysis.functions.length > 0) { console.log(chalk.blue('\n⚡ Functions:')); analysis.functions.forEach((func, index) => { console.log(` ${index + 1}. ${func.name}${func.async ? ' (async)' : ''} - ${func.params.length} params`); }); } if (analysis.classes.length > 0) { console.log(chalk.blue('\n🏗️ Classes:')); analysis.classes.forEach((cls, index) => { console.log(` ${index + 1}. ${cls.name}`); }); } if (analysis.exports.length > 0) { console.log(chalk.blue('\n📤 Exports:')); analysis.exports.forEach((exp, index) => { console.log(` ${index + 1}. ${exp.name} (${exp.type})`); }); } if (analysis.testSuggestions.length > 0) { console.log(chalk.blue('\n💡 Test Suggestions:')); analysis.testSuggestions.forEach((suggestion, index) => { console.log(` ${index + 1}. ${suggestion.description} (${suggestion.type})`); if (suggestion.testCases && suggestion.testCases.length > 0) { console.log(` Test cases: ${suggestion.testCases.slice(0, 2).join(', ')}${suggestion.testCases.length > 2 ? '...' : ''}`); } }); } } catch (error) { spinner.fail('Analysis error'); console.log(chalk.red(`❌ ${error.message}`)); } } async detectTestFramework() { const spinner = ora('🔍 Detecting test framework...').start(); try { const result = await this.toolManager.detectTestFramework(); if (!result.success) { spinner.fail('Framework detection failed'); console.log(chalk.red(`❌ ${result.error}`)); return; } spinner.succeed('Framework detection completed'); console.log(chalk.blue('\n🧪 Test Framework Detection:')); if (result.frameworks.length === 0) { console.log(chalk.yellow('No test frameworks detected in this project')); console.log(chalk.gray('Consider installing a test framework like Jest, Mocha, or Vitest')); return; } console.log(`📊 Detected ${result.frameworks.length} framework(s):`); result.frameworks.forEach((framework, index) => { const confidence = (framework.confidence * 100).toFixed(0); const isPrimary = result.primary && result.primary.name === framework.name; const marker = isPrimary ? '🎯' : ' '; console.log(`${marker} ${index + 1}. ${framework.name} (${confidence}% confidence) - ${framework.source}`); }); if (result.primary) { console.log(chalk.green(`\n🎯 Primary framework: ${result.primary.name}`)); } // 显示测试生成系统统计 const testStats = this.toolManager.getTestGeneratorStats(); console.log(chalk.blue('\n📊 Test Generator Capabilities:')); console.log(` Supported Frameworks: ${testStats.supportedFrameworks}`); console.log(` Test Types: ${testStats.testTypes}`); console.log(` Test Patterns: ${testStats.testPatterns}`); } catch (error) { spinner.fail('Framework detection error'); console.log(chalk.red(`❌ ${error.message}`)); } } async generateCoverage(testFilePath, sourceFilePath) { if (!testFilePath || !sourceFilePath) { console.log(chalk.red('❌ Please specify both test file and source file')); console.log(chalk.gray('Usage: test coverage <test_file> <source_file>')); return; } const spinner = ora(`📊 Generating coverage report...`).start(); try { const result = await this.toolManager.generateCoverageReport(testFilePath, sourceFilePath); if (!result.success) { spinner.fail('Coverage generation failed'); console.log(chalk.red(`❌ ${result.error}`)); return; } spinner.succeed('Coverage report generated'); console.log(chalk.blue('\n📊 Coverage Report:')); if (result.coverage) { console.log(`📏 Statements: ${result.coverage.statements}%`); console.log(`🌿 Branches: ${result.coverage.branches}%`); console.log(`⚡ Functions: ${result.coverage.functions}%`); console.log(`📝 Lines: ${result.coverage.lines}%`); const avgCoverage = ( result.coverage.statements + result.coverage.branches + result.coverage.functions + result.coverage.lines ) / 4; const coverageColor = avgCoverage >= 80 ? chalk.green : avgCoverage >= 60 ? chalk.yellow : chalk.red; console.log(coverageColor(`\n📈 Average Coverage: ${avgCoverage.toFixed(1)}%`)); } } catch (error) { spinner.fail('Coverage generation error'); console.log(chalk.red(`❌ ${error.message}`)); } } async handlePlanCommand(params) { const subCommand = params[0]; switch (subCommand) { case 'create': await this.createTaskPlan(params.slice(1).join(' ')); break; case 'execute': await this.executeTaskPlan(params[1]); break; case 'status': await this.showPlanStatus(); break; case 'pause': await this.pausePlan(); break; case 'resume': await this.resumePlan(); break; case 'stop': await this.stopPlan(); break; default: console.log(chalk.red('❌ Unknown plan command. Available: create, execute, status, pause, resume, stop')); console.log(chalk.gray('Usage: plan create "<description>" | plan execute | plan status | plan pause | plan resume | plan stop')); } } async createTaskPlan(description) { if (!description.trim()) { console.log(chalk.red('❌ Please provide a description for the task plan')); console.log(chalk.gray('Usage: plan create "Create a full-stack e-commerce app with React and Node.js"')); return; } const spinner = ora('🤖 AI is analyzing requirements and generating task plan...').start(); try { const result = await this.toolManager.generateTaskPlan(description); if (!result.success) { spinner.fail('Task plan generation failed'); console.log(chalk.red(`❌ ${result.error}`)); return; } spinner.succeed('Task plan generated successfully'); const { plan, summary } = result; console.log(chalk.blue(`\n🎯 Task Plan: ${plan.title}`)); console.log(chalk.gray(`📝 ${plan.description}`)); if (plan.techStack && plan.techStack.length > 0) { console.log(chalk.blue(`\n🔧 Technology Stack:`)); plan.techStack.forEach(tech => console.log(` • ${tech}`)); } console.log(chalk.blue(`\n📊 Plan Summary:`)); console.log(` 📋 Total Tasks: ${summary.totalTasks}`); console.log(` 🔥 High Priority: ${summary.highPriorityTasks}`); console.log(` ⏱️ Estimated Time: ${summary.estimatedTime} minutes`); console.log(` 📊 Complexity: ${plan.complexity}`); console.log(chalk.blue('\n📋 Task Breakdown:')); plan.tasks.forEach((task, index) => { const priorityIcon = task.priority === 'high' ? '🔥' : task.priority === 'medium' ? '🟡' : '🟢'; console.log(` ${index + 1}. ${priorityIcon} ${task.title} (${task.estimatedTime}min)`); console.log(` ${chalk.gray(task.description)}`); }); if (plan.metadata.recommendations && plan.metadata.recommendations.length > 0) { console.log(chalk.blue('\n💡 Recommendations:')); plan.metadata.recommendations.forEach((rec, index) => { console.log(` ${index + 1}. ${rec}`); }); } console.log(chalk.yellow('\n🚀 Ready to execute! Use "plan execute" to start implementation.')); } catch (error) { spinner.fail('Task plan generation error'); console.log(chalk.red(`❌ ${error.message}`)); } }