recoder-code 2.2.0 → 2.2.2

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.
@@ -25,6 +25,7 @@ const TaskManager = require('./task-manager.js');
25
25
  const DevToolsIntegration = require('./dev-tools-integration.js');
26
26
  const VisionAnalyzer = require('./vision-analyzer.js');
27
27
  const NaturalLanguageProcessor = require('./natural-language-processor.js');
28
+ const TodoManager = require('./todo-manager.js');
28
29
 
29
30
  // Create logs and sessions directories
30
31
  const logsDir = path.join(process.cwd(), 'logs');
@@ -35,10 +36,17 @@ const sessionsDir = path.join(process.cwd(), 'sessions');
35
36
  }
36
37
  });
37
38
 
39
+ // Initialize unified error handling for interactive mode
40
+ const UnifiedErrorHandler = require('./unified-error-handler.js');
41
+ const errorHandler = UnifiedErrorHandler.instance;
42
+ errorHandler.initialize();
43
+
38
44
  class InteractiveRecoderChat {
39
45
  constructor() {
40
- this.apiKey = config.apiKey;
41
- this.model = config.model;
46
+ // Load user configuration
47
+ this.userConfig = this.loadUserConfig();
48
+ this.apiKey = this.userConfig.apiKey || config.apiKey;
49
+ this.model = this.userConfig.model || config.model;
42
50
  this.maxTokens = config.maxTokens;
43
51
  this.temperature = config.temperature;
44
52
 
@@ -87,8 +95,10 @@ class InteractiveRecoderChat {
87
95
  // Initialize plugin manager
88
96
  this.pluginManager = new PluginManager();
89
97
 
90
- // Initialize voice manager
98
+ // Initialize voice manager (silently disabled)
91
99
  this.voiceManager = new VoiceManager();
100
+ // Silently disable TTS without console output
101
+ this.voiceManager.ttsEnabled = false;
92
102
 
93
103
  // Initialize collaboration manager
94
104
  this.collaborationManager = new CollaborationManager();
@@ -104,6 +114,12 @@ class InteractiveRecoderChat {
104
114
  sessionId: this.sessionId
105
115
  });
106
116
 
117
+ // Initialize todo manager for production-ready task tracking
118
+ this.todoManager = new TodoManager({ user: process.env.USER || 'developer' });
119
+
120
+ // Add comprehensive promise rejection handling
121
+ this.setupGlobalErrorHandlers();
122
+
107
123
  // Initialize advanced systems
108
124
  this.contextEngine = null;
109
125
  this.slashCommands = null;
@@ -115,6 +131,14 @@ class InteractiveRecoderChat {
115
131
  this.visionAnalyzer = null;
116
132
  this.nlProcessor = null;
117
133
 
134
+ // Enhanced conversation tracking
135
+ this.conversationHistory = [];
136
+ this.currentSession = {
137
+ startTime: Date.now(),
138
+ messageCount: 0,
139
+ lastActivity: Date.now()
140
+ };
141
+
118
142
  this.initializeAdvancedSystems().catch(error => {
119
143
  console.log(chalk.yellow(`⚠️ Advanced features initialization failed: ${error.message}`));
120
144
  });
@@ -122,46 +146,15 @@ class InteractiveRecoderChat {
122
146
  // Initialize project manager for persistence
123
147
  this.projectManager = new ProjectManager();
124
148
 
149
+ // Always initialize readline interface first to prevent errors
150
+ this.initializeReadlineInterface();
151
+
125
152
  if (!this.apiKey) {
126
- console.log(chalk.red('❌ Error: OPENROUTER_API_KEY environment variable is required'));
127
- console.log(chalk.yellow('Please set your OpenRouter API key:'));
128
- console.log(chalk.gray('export OPENROUTER_API_KEY=your_api_key_here'));
129
- process.exit(1);
153
+ this.promptForApiKey();
154
+ return;
130
155
  }
131
156
 
132
- // Initialize OpenAI client with OpenRouter configuration
133
- this.openai = new OpenAI({
134
- baseURL: config.baseURL,
135
- apiKey: this.apiKey,
136
- defaultHeaders: {
137
- 'HTTP-Referer': config.siteUrl,
138
- 'X-Title': config.siteName,
139
- },
140
- });
141
-
142
- // Initialize readline interface with enhanced options
143
- this.rl = readline.createInterface({
144
- input: process.stdin,
145
- output: process.stdout,
146
- prompt: chalk.magenta('▶ ') + chalk.white.bold('You: '),
147
- historySize: 100,
148
- removeHistoryDuplicates: true,
149
- completer: this.autoComplete.bind(this)
150
- });
151
-
152
- // Set up custom prompt and keyboard shortcuts
153
- this.updatePrompt();
154
- this.setupKeyboardShortcuts();
155
-
156
- this.setupEventHandlers();
157
- this.initializeFileWatcher();
158
- this.analyzeProjectContext().catch(error => {
159
- console.log(chalk.yellow(`⚠️ Project context analysis failed: ${error.message}`));
160
- });
161
- this.loadPlugins().catch(error => {
162
- console.log(chalk.yellow(`⚠️ Plugin loading failed: ${error.message}`));
163
- });
164
- this.displayWelcome();
157
+ this.initializeWithApiKey();
165
158
  }
166
159
 
167
160
  /**
@@ -169,54 +162,69 @@ class InteractiveRecoderChat {
169
162
  */
170
163
  async initializeAdvancedSystems() {
171
164
  try {
172
- console.log(chalk.gray('🚀 Initializing advanced features...'));
165
+ // Silently initialize advanced features (no console.log for clean startup)
173
166
 
174
167
  // Initialize context engine for codebase understanding
175
168
  this.contextEngine = new ContextEngine();
176
- await this.contextEngine.initialize();
169
+ try {
170
+ await this.contextEngine.initialize();
171
+ } catch (contextError) {
172
+ console.log(chalk.yellow(`⚠️ Context engine initialization failed: ${contextError.message}`));
173
+ }
177
174
 
178
175
  // Initialize MCP client for model communication
179
176
  this.mcpClient = new MCPClient();
180
- await this.mcpClient.initialize();
181
177
 
182
178
  // Initialize model manager with MCP and context engine
183
179
  this.modelManager = new ModelManager(this.mcpClient, this.contextEngine);
184
180
 
185
181
  // Initialize rules engine with project context
186
182
  this.rulesEngine = new RulesEngine(this.projectManager);
187
- await this.rulesEngine.autoDiscoverProjectRules();
183
+ try {
184
+ await this.rulesEngine.autoDiscoverProjectRules();
185
+ } catch (rulesError) {
186
+ console.log(chalk.yellow(`⚠️ Rules engine initialization failed: ${rulesError.message}`));
187
+ }
188
188
 
189
189
  // Initialize task manager with full integration
190
190
  this.taskManager = new TaskManager(this.projectManager, this.contextEngine, this.rulesEngine);
191
- this.taskManager.setupSmartAutomationTriggers();
191
+ try {
192
+ this.taskManager.setupSmartAutomationTriggers();
193
+ } catch (automationError) {
194
+ console.log(chalk.yellow(`⚠️ Automation setup failed: ${automationError.message}`));
195
+ }
192
196
 
193
197
  // Initialize development tools integration
194
198
  this.devTools = new DevToolsIntegration(this.contextEngine, this.projectManager, this.taskManager);
195
- await this.devTools.setupIDEIntegration();
196
- await this.devTools.setupGitIntegration();
197
- await this.devTools.setupDatabaseIntegration();
198
- await this.devTools.setupPerformanceMonitoring();
199
+ try {
200
+ await this.devTools.setupIDEIntegration();
201
+ await this.devTools.setupGitIntegration();
202
+ await this.devTools.setupDatabaseIntegration();
203
+ await this.devTools.setupPerformanceMonitoring();
204
+ } catch (devToolsError) {
205
+ console.log(chalk.yellow(`⚠️ Dev tools setup failed: ${devToolsError.message}`));
206
+ }
199
207
 
200
208
  // Initialize vision analyzer with model manager and context engine
201
209
  this.visionAnalyzer = new VisionAnalyzer(this.modelManager, this.contextEngine);
202
210
 
203
- // Initialize slash commands system with all dependencies including vision analyzer
204
- this.slashCommands = new SlashCommandsEngine(this.contextEngine, this.projectManager, this.visionAnalyzer);
211
+ // Initialize slash commands system with all dependencies including vision analyzer and todo manager
212
+ this.slashCommands = new SlashCommandsEngine(this.contextEngine, this.projectManager, this.visionAnalyzer, this.todoManager);
205
213
 
206
214
  // Initialize natural language processor with access to all systems
207
215
  this.nlProcessor = new NaturalLanguageProcessor(this);
208
216
 
209
217
  // Set up system integrations
210
- await this.setupSystemIntegrations();
218
+ try {
219
+ await this.setupSystemIntegrations();
220
+ } catch (integrationError) {
221
+ console.log(chalk.yellow(`⚠️ System integrations setup failed: ${integrationError.message}`));
222
+ }
211
223
 
212
224
  // Enable auto model switching based on context
213
225
  this.enableIntelligentModelSwitching();
214
226
 
215
- console.log(chalk.green('✅ Advanced features initialized and integrated'));
216
- console.log(chalk.gray(` 🤖 Model Manager: ${this.modelManager.models.size} models available`));
217
- console.log(chalk.gray(` 📋 Rules Engine: Auto-discovered project rules`));
218
- console.log(chalk.gray(` 🎯 Task Manager: Automation triggers active`));
219
- console.log(chalk.gray(` 🔧 Dev Tools: Native integrations enabled`));
227
+ // Advanced features initialized silently
220
228
 
221
229
  } catch (error) {
222
230
  console.log(chalk.yellow(`⚠️ Some advanced features may be limited: ${error.message}`));
@@ -403,9 +411,9 @@ class InteractiveRecoderChat {
403
411
  const convIndicator = this.currentConversationId === 'main' ? '' : chalk.gray(`[${currentConv.name}] `);
404
412
 
405
413
  if (this.multiLineMode) {
406
- this.rl.setPrompt(chalk.magenta('┃ ') + chalk.gray('... '));
414
+ this.rl.setPrompt(chalk.gray('... '));
407
415
  } else {
408
- this.rl.setPrompt(convIndicator + chalk.magenta(' ') + chalk.white.bold('You: '));
416
+ this.rl.setPrompt(convIndicator + chalk.gray('> '));
409
417
  }
410
418
  }
411
419
 
@@ -793,6 +801,11 @@ class InteractiveRecoderChat {
793
801
  case 14: // Ctrl+N (New/Clear conversation)
794
802
  this.handleCtrlN();
795
803
  break;
804
+
805
+ case 27: // ESC (Exit)
806
+ console.log(chalk.yellow('\n👋 Goodbye!'));
807
+ process.exit(0);
808
+ break;
796
809
  }
797
810
  }
798
811
  }
@@ -978,111 +991,723 @@ class InteractiveRecoderChat {
978
991
  });
979
992
  }
980
993
 
981
- displayWelcome() {
994
+ promptForApiKey() {
982
995
  console.clear();
983
-
984
- // Stunning purple/gray/black UI with recoder.xyz branding
985
- console.log('');
986
- console.log(chalk.magenta.bold('╭─────────────────────────────────────────────────────────────────╮'));
987
- console.log(chalk.magenta.bold('│') + chalk.black.bgMagenta.bold(' RECODER.XYZ ') + chalk.magenta.bold('│'));
988
- console.log(chalk.magenta.bold('│') + chalk.gray(' AI-Powered Development Terminal ') + chalk.magenta.bold('│'));
989
- console.log(chalk.magenta.bold('╰─────────────────────────────────────────────────────────────────╯'));
990
- console.log('');
991
-
992
- // ASCII Art Logo
993
- console.log(chalk.magenta.bold('██████╗ ███████╗ ██████╗ ██████╗ ██████╗ ███████╗██████╗'));
994
- console.log(chalk.magenta.bold('██╔══██╗██╔════╝██╔════╝██╔═══██╗██╔══██╗██╔════╝██╔══██╗'));
995
- console.log(chalk.magenta.bold('██████╔╝█████╗ ██║ ██║ ██║██║ ██║█████╗ ██████╔╝'));
996
- console.log(chalk.gray('██╔══██╗██╔══╝ ██║ ██║ ██║██║ ██║██╔══╝ ██╔══██╗'));
997
- console.log(chalk.gray('██║ ██║███████╗╚██████╗╚██████╔╝██████╔╝███████╗██║ ██║'));
998
- console.log(chalk.gray('╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝'));
999
996
  console.log('');
1000
-
1001
- // Status Bar
1002
- console.log(chalk.magenta('') + chalk.white.bold('Ready') + chalk.gray(' │ ') + chalk.gray(`${process.cwd()}`));
1003
- console.log(chalk.gray('─'.repeat(65)));
997
+ console.log(chalk.magenta('╭─────────────────────────────────────────────────────────────╮'));
998
+ console.log(chalk.magenta('│') + chalk.white.bold(' ✨ Welcome to Recoder Code ') + chalk.magenta('│'));
999
+ console.log(chalk.magenta('╰─────────────────────────────────────────────────────────────╯'));
1004
1000
  console.log('');
1005
-
1006
- // Show session suggestions if available
1007
- this.showSessionSuggestions();
1008
-
1009
- // Quick actions
1010
- console.log(chalk.magenta('⚡ ') + chalk.white.bold('Quick Actions:'));
1011
- console.log(chalk.gray(' • ') + chalk.cyan('/help') + chalk.gray(' - Show available commands'));
1012
- console.log(chalk.gray(' • ') + chalk.cyan('/status') + chalk.gray(' - System status & configuration'));
1013
- console.log(chalk.gray(' • ') + chalk.cyan('/models') + chalk.gray(' - Switch AI models'));
1001
+ console.log(chalk.white('🔑 API key required to continue'));
1002
+ console.log(chalk.gray('Get your free API key at: ') + chalk.magenta('https://openrouter.ai'));
1014
1003
  console.log('');
1015
1004
 
1016
- console.log(chalk.magenta('💬 ') + chalk.white.bold('Start coding with AI assistance!'));
1017
- console.log(chalk.gray('✨ Try: ') + chalk.cyan('"build a React app"') + chalk.gray(' or ') + chalk.cyan('"debug my Python code"'));
1018
- console.log('');
1005
+ const keyPrompt = readline.createInterface({
1006
+ input: process.stdin,
1007
+ output: process.stdout
1008
+ });
1019
1009
 
1010
+ keyPrompt.question(chalk.magenta('Enter your OpenRouter API key: '), (apiKey) => {
1011
+ keyPrompt.close();
1012
+
1013
+ if (!apiKey || apiKey.trim() === '') {
1014
+ console.log(chalk.red('No API key provided. Exiting...'));
1015
+ process.exit(1);
1016
+ }
1017
+
1018
+ // Save the API key to environment
1019
+ process.env.OPENROUTER_API_KEY = apiKey.trim();
1020
+ this.apiKey = apiKey.trim();
1021
+
1022
+ // Continue initialization
1023
+ this.initializeWithApiKey();
1024
+ });
1025
+ }
1026
+
1027
+ initializeReadlineInterface() {
1028
+ // Initialize readline interface with enhanced options
1029
+ this.rl = readline.createInterface({
1030
+ input: process.stdin,
1031
+ output: process.stdout,
1032
+ prompt: chalk.gray('> '),
1033
+ historySize: 100,
1034
+ removeHistoryDuplicates: true,
1035
+ completer: (line) => this.autoComplete(line)
1036
+ });
1037
+
1038
+ // Set up readline event handlers
1039
+ this.setupReadlineHandlers();
1040
+ }
1041
+
1042
+ setupReadlineHandlers() {
1043
+ if (!this.rl) return;
1044
+
1045
+ this.rl.on('line', async (input) => {
1046
+ await this.handleUserInput(input.trim());
1047
+ });
1048
+
1049
+ this.rl.on('close', () => {
1050
+ console.log('\nGoodbye!');
1051
+ process.exit(0);
1052
+ });
1053
+
1054
+ // Handle advanced keyboard shortcuts
1055
+ process.stdin.on('keypress', (str, key) => {
1056
+ if (key && key.ctrl) {
1057
+ this.handleCtrlKeys(key);
1058
+ }
1059
+ });
1060
+ }
1061
+
1062
+ initializeWithApiKey() {
1063
+ // Initialize OpenAI client with OpenRouter configuration
1064
+ this.openai = new OpenAI({
1065
+ baseURL: config.baseURL,
1066
+ apiKey: this.apiKey,
1067
+ defaultHeaders: {
1068
+ 'HTTP-Referer': config.siteUrl,
1069
+ 'X-Title': config.siteName,
1070
+ },
1071
+ });
1072
+
1073
+ // If readline interface wasn't already initialized, do it now
1074
+ if (!this.rl) {
1075
+ this.initializeReadlineInterface();
1076
+ }
1077
+
1078
+ // Set up custom prompt and keyboard shortcuts
1079
+ this.updatePrompt();
1080
+ this.setupKeyboardShortcuts();
1081
+
1082
+ this.setupEventHandlers();
1083
+ this.initializeFileWatcher();
1084
+ this.analyzeProjectContext().catch(error => {
1085
+ console.log(chalk.yellow(`⚠️ Project context analysis failed: ${error.message}`));
1086
+ });
1087
+ this.loadPlugins().catch(error => {
1088
+ console.log(chalk.yellow(`⚠️ Plugin loading failed: ${error.message}`));
1089
+ });
1090
+ this.displayWelcome();
1091
+ }
1092
+
1093
+ updatePrompt() {
1094
+ // Neon purple prompt with white text
1095
+ if (this.rl) {
1096
+ this.rl.setPrompt(chalk.magenta('> '));
1097
+ }
1098
+ }
1099
+
1100
+ setupEventHandlers() {
1101
+ // Set up readline event handlers for user input
1102
+ this.rl.on('line', async (input) => {
1103
+ await this.handleUserInput(input.trim());
1104
+ });
1105
+
1106
+ this.rl.on('close', () => {
1107
+ console.log('\nGoodbye!');
1108
+ process.exit(0);
1109
+ });
1110
+
1111
+ // Handle advanced keyboard shortcuts
1112
+ process.stdin.on('keypress', (str, key) => {
1113
+ if (key) {
1114
+ this.handleKeyboardShortcuts(key);
1115
+ }
1116
+ });
1117
+
1118
+ // Start the prompt
1020
1119
  this.rl.prompt();
1021
1120
  }
1022
1121
 
1023
- showSessionSuggestions() {
1122
+ async handleUserInput(input) {
1123
+ if (!input) {
1124
+ this.rl.prompt();
1125
+ return;
1126
+ }
1127
+
1128
+ // Handle slash commands like Claude Code
1129
+ if (input.startsWith('/')) {
1130
+ this.handleSlashCommand(input);
1131
+ return;
1132
+ }
1133
+
1024
1134
  try {
1025
- // Check if project exists
1026
- const projectStatus = this.projectManager.getProjectStatus();
1135
+ // Track performance metrics
1136
+ const startTime = Date.now();
1137
+ let firstChunkTime = null;
1138
+ let totalChunks = 0;
1027
1139
 
1028
- if (projectStatus.exists) {
1029
- console.log(chalk.magenta('📂 ') + chalk.white.bold(`Project: ${projectStatus.context.name}`));
1030
- console.log(chalk.gray(` • Todos: ${projectStatus.todos.pending} pending, ${projectStatus.todos.completed} completed`));
1031
- }
1140
+ // Start progressive loading with enhanced animations
1141
+ this.startProgressiveLoading();
1032
1142
 
1033
- // Check for intelligent auto-resume candidate
1034
- const resumeCandidate = this.projectManager.shouldOfferAutoResume();
1143
+ // Create a RecoderCode instance to process the conversation
1144
+ const { RecoderCode } = require('./run.js');
1145
+ const recoder = new RecoderCode();
1035
1146
 
1036
- if (resumeCandidate) {
1037
- console.log(chalk.magenta('🔄 ') + chalk.white.bold('Resume Previous Session?'));
1038
- console.log(chalk.gray(` • Session: ${resumeCandidate.id} (${resumeCandidate.activityDescription})`));
1039
- console.log(chalk.gray(` • Messages: ${resumeCandidate.messageCount} | Model: ${resumeCandidate.model}`));
1040
-
1041
- if (resumeCandidate.lastUserMessage) {
1042
- console.log(chalk.gray(` • Last: "${resumeCandidate.lastUserMessage}"`));
1147
+ // Initialize streaming response system
1148
+ const streamBuffer = {
1149
+ content: '',
1150
+ chunks: [],
1151
+ isStreaming: false,
1152
+ streamingStarted: false
1153
+ };
1154
+
1155
+ // Enhanced streaming response handler
1156
+ const response = await this.processWithAdvancedStreaming({
1157
+ recoder,
1158
+ input,
1159
+ startTime,
1160
+ streamBuffer,
1161
+ onFirstChunk: (time) => { firstChunkTime = time; },
1162
+ onChunk: (chunk) => {
1163
+ totalChunks++;
1164
+ this.displayStreamingChunk(chunk, totalChunks);
1043
1165
  }
1044
-
1045
- console.log(chalk.gray(' 💡 Type ') + chalk.cyan('/resume') + chalk.gray(' to continue or start fresh'));
1046
- console.log('');
1166
+ });
1167
+
1168
+ // Calculate comprehensive metrics
1169
+ const totalDuration = Math.round((Date.now() - startTime) / 1000);
1170
+ const timeToFirstChunk = firstChunkTime ? Math.round((firstChunkTime - startTime) / 1000) : null;
1171
+
1172
+ // Track conversation history with enhanced metadata
1173
+ this.conversationHistory.push({
1174
+ timestamp: new Date(),
1175
+ input: input,
1176
+ response: response,
1177
+ duration: totalDuration,
1178
+ timeToFirstChunk,
1179
+ totalChunks,
1180
+ streamingUsed: streamBuffer.isStreaming
1181
+ });
1182
+ this.currentSession.messageCount++;
1183
+ this.currentSession.lastActivity = Date.now();
1184
+
1185
+ console.log('');
1186
+
1187
+ // Show performance metrics and tool usage
1188
+ this.showAdvancedMetrics({
1189
+ duration: totalDuration,
1190
+ timeToFirstChunk,
1191
+ totalChunks,
1192
+ streamingUsed: streamBuffer.isStreaming
1193
+ });
1194
+
1195
+ // Enhanced response display if not already streamed
1196
+ if (!streamBuffer.streamingStarted) {
1197
+ await this.displayAdvancedTypingResponse(response, startTime);
1047
1198
  } else {
1048
- // Get recent sessions if no auto-resume candidate
1049
- const recentSessions = this.projectManager.listSessions({ limit: 3 });
1050
-
1051
- if (recentSessions.length > 0) {
1052
- console.log(chalk.magenta('🕒 ') + chalk.white.bold('Recent Sessions:'));
1053
- recentSessions.forEach((session, index) => {
1054
- const timeAgo = this.getTimeAgo(new Date(session.modified));
1055
- console.log(chalk.gray(` • ${session.id} - ${timeAgo} (${session.messageCount} messages)`));
1056
- });
1057
- console.log(chalk.gray(' 💡 Use ') + chalk.cyan('/resume <id>') + chalk.gray(' to continue a session'));
1058
- console.log('');
1059
- }
1199
+ // Finalize streaming display
1200
+ this.finalizeStreamingResponse(response, totalDuration);
1060
1201
  }
1061
1202
 
1203
+ console.log('');
1204
+
1062
1205
  } catch (error) {
1063
- // Silently ignore errors in session suggestions
1206
+ this.stopProgressiveLoading();
1207
+ this.stopStreamingDisplay();
1208
+ console.log('');
1209
+ this.showEnhancedError(error);
1210
+ console.log('');
1064
1211
  }
1212
+
1213
+ this.rl.prompt();
1065
1214
  }
1066
1215
 
1067
- getTimeAgo(date) {
1068
- const now = new Date();
1069
- const seconds = Math.floor((now - date) / 1000);
1216
+ displayWelcome() {
1217
+ console.clear();
1070
1218
 
1071
- if (seconds < 60) return 'just now';
1072
- if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
1073
- if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`;
1074
- return `${Math.floor(seconds / 86400)}d ago`;
1219
+ // Enhanced welcome screen with gradient effects and animations
1220
+ console.log('');
1221
+
1222
+ // Animated gradient border
1223
+ this.displayAnimatedBorder();
1224
+
1225
+ // Beautiful gradient ASCII art
1226
+ console.log('');
1227
+ this.displayGradientLogo();
1228
+ console.log('');
1229
+
1230
+ // Dynamic status information
1231
+ this.displayDynamicStatus();
1232
+ console.log('');
1233
+
1234
+ // Enhanced status hints with themes
1235
+ this.showEnhancedStatusHints();
1236
+
1237
+ this.rl.prompt();
1075
1238
  }
1076
1239
 
1077
- async handleCommand(command) {
1078
- const [cmd, ...args] = command.slice(1).split(' ');
1079
- const startTime = Date.now();
1240
+ displayAnimatedBorder() {
1241
+ const colors = ['magenta', 'cyan', 'blue', 'magenta'];
1242
+ const border = '╭─────────────────────────────────────────────────────────────╮';
1243
+ const title = '│ ✨ Welcome to Recoder Code - AI Terminal │';
1244
+ const bottom = '╰─────────────────────────────────────────────────────────────╯';
1080
1245
 
1081
- // Log command execution
1082
- this.logger.debug(`Executing command: ${cmd}`, { args });
1246
+ console.log(chalk[colors[0]](border));
1247
+ console.log(chalk[colors[1]](title));
1248
+ console.log(chalk[colors[2]](bottom));
1249
+ }
1250
+
1251
+ displayGradientLogo() {
1252
+ // Beautiful gradient ASCII art with purple to cyan transition
1253
+ const lines = [
1254
+ '██████╗ ███████╗ ██████╗ ██████╗ ██████╗ ███████╗██████╗',
1255
+ '██╔══██╗██╔════╝██╔════╝██╔═══██╗██╔══██╗██╔════╝██╔══██╗',
1256
+ '██████╔╝█████╗ ██║ ██║ ██║██║ ██║█████╗ ██████╔╝',
1257
+ '██╔══██╗██╔══╝ ██║ ██║ ██║██║ ██║██╔══╝ ██╔══██╗',
1258
+ '██║ ██║███████╗╚██████╗╚██████╔╝██████╔╝███████╗██║ ██║',
1259
+ '╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝'
1260
+ ];
1083
1261
 
1262
+ const gradientColors = ['magenta', 'magenta', 'cyan', 'blue', 'blue', 'gray'];
1263
+
1264
+ lines.forEach((line, index) => {
1265
+ console.log(chalk[gradientColors[index]].bold(line));
1266
+ });
1267
+ }
1268
+
1269
+ displayDynamicStatus() {
1270
+ const currentTime = new Date().toLocaleTimeString();
1271
+ const projectContext = this.getCurrentProjectContext();
1272
+
1273
+ console.log(chalk.gray('┌─ Session Info ─────────────────────────────────────────────┐'));
1274
+ console.log(chalk.gray('│') + chalk.white(` 🕐 Started: ${currentTime}`) +
1275
+ chalk.gray(' │'));
1276
+ console.log(chalk.gray('│') + chalk.cyan(` 📁 Project: ${projectContext.name || 'recoder-code'}`) +
1277
+ chalk.gray(' │'));
1278
+ console.log(chalk.gray('│') + chalk.green(` 🤖 Model: ${this.model || 'claude-sonnet-4'}`) +
1279
+ chalk.gray(' │'));
1280
+ console.log(chalk.gray('└────────────────────────────────────────────────────────────┘'));
1281
+ }
1282
+
1283
+ getCurrentProjectContext() {
1084
1284
  try {
1085
- switch (cmd.toLowerCase()) {
1285
+ const packageJson = require(process.cwd() + '/package.json');
1286
+ return {
1287
+ name: packageJson.name || 'Unknown Project',
1288
+ version: packageJson.version || '1.0.0'
1289
+ };
1290
+ } catch (error) {
1291
+ return { name: 'recoder-code', version: '2.0.0' };
1292
+ }
1293
+ }
1294
+
1295
+ showEnhancedStatusHints() {
1296
+ console.log(chalk.gray('┌─ Quick Commands ───────────────────────────────────────────┐'));
1297
+ console.log(chalk.gray('│') + chalk.yellow(' ! ') + chalk.white('bash mode') +
1298
+ chalk.gray(' • ') + chalk.yellow('/ ') + chalk.white('commands') +
1299
+ chalk.gray(' • ') + chalk.yellow('tab ') + chalk.white('autocomplete') +
1300
+ chalk.gray(' ') + chalk.red('ESC ') + chalk.white('exit') +
1301
+ chalk.gray(' │'));
1302
+ console.log(chalk.gray('└────────────────────────────────────────────────────────────┘'));
1303
+
1304
+ console.log(chalk.gray('┌─ Features ─────────────────────────────────────────────────┐'));
1305
+ console.log(chalk.gray('│') + chalk.magenta(' 📁 ') + chalk.white(this.getCurrentProjectContext().name) +
1306
+ chalk.gray(' • ') + chalk.green('✨ ') + chalk.white('AI Ready') +
1307
+ chalk.gray(' • ') + chalk.cyan('Ctrl+S ') + chalk.white('save') +
1308
+ chalk.gray(' • ') + chalk.blue('Ctrl+H ') + chalk.white('help') +
1309
+ chalk.gray(' │'));
1310
+ console.log(chalk.gray('└────────────────────────────────────────────────────────────┘'));
1311
+ }
1312
+
1313
+ showError(error) {
1314
+ // Beautiful error display like Claude Code
1315
+ console.log(chalk.red('╭─────────────────────────────────────────╮'));
1316
+ console.log(chalk.red('│') + chalk.white.bold(' ❌ Error ') + chalk.red('│'));
1317
+ console.log(chalk.red('╰─────────────────────────────────────────╯'));
1318
+ console.log('');
1319
+ console.log(chalk.white(error.message));
1320
+ console.log('');
1321
+
1322
+ // Show helpful suggestions based on error type
1323
+ if (error.message.includes('429')) {
1324
+ console.log(chalk.gray('💡 Rate limit reached. Try again in a few seconds.'));
1325
+ } else if (error.message.includes('API key')) {
1326
+ console.log(chalk.gray('💡 Check your API key with /status command.'));
1327
+ } else if (error.message.includes('timeout')) {
1328
+ console.log(chalk.gray('💡 Request timed out. Try a simpler query.'));
1329
+ } else {
1330
+ console.log(chalk.gray('💡 Type /help for available commands.'));
1331
+ }
1332
+ }
1333
+
1334
+ showStatusHints() {
1335
+ // Show helpful hints at bottom like Claude Code with project context
1336
+ const projectName = path.basename(process.cwd());
1337
+ console.log(chalk.gray(`! for bash mode • / for commands • tab to autocomplete ESC to exit`));
1338
+ console.log(chalk.gray(`📁 ${projectName} • ✨ Recoder Code ready • Ctrl+S save • Ctrl+H help`));
1339
+ }
1340
+
1341
+ startProcessingAnimation() {
1342
+ const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
1343
+ let frameIndex = 0;
1344
+
1345
+ this.processingInterval = setInterval(() => {
1346
+ const frame = frames[frameIndex % frames.length];
1347
+ process.stdout.write(`\r${chalk.magenta(frame)} ${chalk.gray('Processing...')}`);
1348
+ frameIndex++;
1349
+ }, 80);
1350
+ }
1351
+
1352
+ stopProcessingAnimation() {
1353
+ if (this.processingInterval) {
1354
+ clearInterval(this.processingInterval);
1355
+ this.processingInterval = null;
1356
+ process.stdout.write('\r'); // Clear the line
1357
+ }
1358
+ }
1359
+
1360
+ showToolUsage() {
1361
+ // Show tool usage indicators like Claude Code
1362
+ const tools = ['Read', 'Analyze', 'Generate'];
1363
+ tools.forEach(tool => {
1364
+ console.log(chalk.gray('● ') + chalk.white(`${tool}(project_structure)...`));
1365
+ });
1366
+ }
1367
+
1368
+ formatResponse(response) {
1369
+ // Format response with rich styling like Claude Code
1370
+ if (!response) return '';
1371
+
1372
+ // Add bullet points and styling
1373
+ const formatted = response
1374
+ .split('\n')
1375
+ .map(line => {
1376
+ if (line.trim().startsWith('-') || line.trim().startsWith('*')) {
1377
+ return chalk.gray('● ') + chalk.white(line.replace(/^[\s\-\*]+/, ''));
1378
+ }
1379
+ return chalk.white(line);
1380
+ })
1381
+ .join('\n');
1382
+
1383
+ return formatted;
1384
+ }
1385
+
1386
+ async typeResponse(text) {
1387
+ // Type out response character by character like Claude Code
1388
+ const lines = text.split('\n');
1389
+ for (const line of lines) {
1390
+ for (let i = 0; i < line.length; i++) {
1391
+ process.stdout.write(line[i]);
1392
+ await new Promise(resolve => setTimeout(resolve, 15)); // 15ms per character
1393
+ }
1394
+ console.log(''); // New line after each line
1395
+ }
1396
+ }
1397
+
1398
+ handleSlashCommand(input) {
1399
+ const command = input.slice(1).toLowerCase();
1400
+
1401
+ switch (command) {
1402
+ case 'help':
1403
+ this.showHelp();
1404
+ break;
1405
+ case 'clear':
1406
+ console.clear();
1407
+ this.displayWelcome();
1408
+ return; // Don't show prompt again
1409
+ case 'exit':
1410
+ case 'quit':
1411
+ console.log(chalk.magenta('\nGoodbye! ✨'));
1412
+ process.exit(0);
1413
+ break;
1414
+ case 'status':
1415
+ this.showStatus();
1416
+ break;
1417
+ case 'history':
1418
+ this.showHistory();
1419
+ break;
1420
+ case 'session':
1421
+ this.showSession();
1422
+ break;
1423
+ case 'watch':
1424
+ this.showWatchedFiles();
1425
+ break;
1426
+ case 'save':
1427
+ this.saveCurrentSession();
1428
+ break;
1429
+ case 'analyze':
1430
+ this.performCodeAnalysis();
1431
+ break;
1432
+ case 'git':
1433
+ this.showGitStatus();
1434
+ break;
1435
+ case 'review':
1436
+ this.performCodeReview();
1437
+ break;
1438
+ case 'optimize':
1439
+ this.performOptimizationAnalysis();
1440
+ break;
1441
+ case 'security':
1442
+ this.performSecurityAnalysis();
1443
+ break;
1444
+ case 'theme':
1445
+ this.cycleTheme();
1446
+ break;
1447
+ default:
1448
+ console.log(chalk.red(`Unknown command: ${input}`));
1449
+ console.log(chalk.gray('Type /help for available commands'));
1450
+ }
1451
+
1452
+ this.rl.prompt();
1453
+ }
1454
+
1455
+ showHelp() {
1456
+ console.log('');
1457
+ console.log(chalk.magenta('╭─────────────────────────────────────────╮'));
1458
+ console.log(chalk.magenta('│') + chalk.white.bold(' 🚀 Available Commands ') + chalk.magenta('│'));
1459
+ console.log(chalk.magenta('╰─────────────────────────────────────────╯'));
1460
+ console.log('');
1461
+
1462
+ console.log(chalk.cyan('📋 Basic Commands:'));
1463
+ console.log(chalk.gray(' /help ') + chalk.white('Show this help message'));
1464
+ console.log(chalk.gray(' /clear ') + chalk.white('Clear the screen'));
1465
+ console.log(chalk.gray(' /status ') + chalk.white('Show system status'));
1466
+ console.log(chalk.gray(' /exit ') + chalk.white('Exit the terminal'));
1467
+ console.log('');
1468
+
1469
+ console.log(chalk.green('🔍 Analysis Commands:'));
1470
+ console.log(chalk.gray(' /analyze ') + chalk.white('Perform intelligent code analysis'));
1471
+ console.log(chalk.gray(' /review ') + chalk.white('AI-powered code review'));
1472
+ console.log(chalk.gray(' /security') + chalk.white('Security vulnerability analysis'));
1473
+ console.log(chalk.gray(' /optimize') + chalk.white('Performance optimization suggestions'));
1474
+ console.log('');
1475
+
1476
+ console.log(chalk.blue('🌿 Git Commands:'));
1477
+ console.log(chalk.gray(' /git ') + chalk.white('Show git repository status'));
1478
+ console.log('');
1479
+
1480
+ console.log(chalk.magenta('🎨 Customization:'));
1481
+ console.log(chalk.gray(' /theme ') + chalk.white('Cycle through visual themes'));
1482
+ console.log('');
1483
+
1484
+ console.log(chalk.yellow('💾 Session Management:'));
1485
+ console.log(chalk.gray(' /history ') + chalk.white('Show conversation history'));
1486
+ console.log(chalk.gray(' /session ') + chalk.white('Show current session info'));
1487
+ console.log(chalk.gray(' /save ') + chalk.white('Save current session'));
1488
+ console.log(chalk.gray(' /watch ') + chalk.white('Show watched files'));
1489
+ console.log('');
1490
+ }
1491
+
1492
+ showStatus() {
1493
+ console.log('');
1494
+ console.log(chalk.magenta('╭─────────────────────────────────────────╮'));
1495
+ console.log(chalk.magenta('│') + chalk.white.bold(' System Status ') + chalk.magenta('│'));
1496
+ console.log(chalk.magenta('╰─────────────────────────────────────────╯'));
1497
+ console.log('');
1498
+ console.log(chalk.gray('Model: ') + chalk.white(this.model || 'Default'));
1499
+ console.log(chalk.gray('Session: ') + chalk.white(this.sessionId.slice(0, 8) + '...'));
1500
+ console.log(chalk.gray('Directory:') + chalk.white(process.cwd().replace(process.env.HOME, '~')));
1501
+ console.log('');
1502
+ }
1503
+
1504
+ showHistory() {
1505
+ console.log('');
1506
+ console.log(chalk.magenta('╭─────────────────────────────────────────╮'));
1507
+ console.log(chalk.magenta('│') + chalk.white.bold(' Conversation History ') + chalk.magenta('│'));
1508
+ console.log(chalk.magenta('╰─────────────────────────────────────────╯'));
1509
+ console.log('');
1510
+
1511
+ if (this.conversationHistory.length === 0) {
1512
+ console.log(chalk.gray('No conversation history yet.'));
1513
+ } else {
1514
+ const recent = this.conversationHistory.slice(-5); // Show last 5 exchanges
1515
+ recent.forEach((entry, index) => {
1516
+ const time = entry.timestamp.toLocaleTimeString();
1517
+ console.log(chalk.gray(`[${time}] `) + chalk.white(entry.input.slice(0, 50) + '...'));
1518
+ console.log(chalk.gray(` → Response in ${entry.duration}s`));
1519
+ if (index < recent.length - 1) console.log('');
1520
+ });
1521
+ }
1522
+ console.log('');
1523
+ }
1524
+
1525
+ showSession() {
1526
+ console.log('');
1527
+ console.log(chalk.magenta('╭─────────────────────────────────────────╮'));
1528
+ console.log(chalk.magenta('│') + chalk.white.bold(' Current Session ') + chalk.magenta('│'));
1529
+ console.log(chalk.magenta('╰─────────────────────────────────────────╯'));
1530
+ console.log('');
1531
+
1532
+ const sessionDuration = Math.round((Date.now() - this.currentSession.startTime) / 1000 / 60);
1533
+ const lastActivity = Math.round((Date.now() - this.currentSession.lastActivity) / 1000);
1534
+
1535
+ console.log(chalk.gray('Session ID: ') + chalk.white(this.sessionId.slice(0, 8) + '...'));
1536
+ console.log(chalk.gray('Duration: ') + chalk.white(`${sessionDuration} minutes`));
1537
+ console.log(chalk.gray('Messages: ') + chalk.white(this.currentSession.messageCount));
1538
+ console.log(chalk.gray('Last Activity: ') + chalk.white(`${lastActivity}s ago`));
1539
+ console.log(chalk.gray('Model: ') + chalk.white(this.model || 'Default'));
1540
+ console.log('');
1541
+ }
1542
+
1543
+ smartAutoComplete(line) {
1544
+ // Smart autocomplete for commands and context-aware suggestions
1545
+ const completions = [];
1546
+
1547
+ if (line.startsWith('/')) {
1548
+ // Slash command completions
1549
+ const commands = ['help', 'clear', 'status', 'history', 'session', 'exit'];
1550
+ const matches = commands.filter(cmd => cmd.startsWith(line.slice(1)));
1551
+ completions.push(...matches.map(cmd => '/' + cmd));
1552
+ } else if (line.startsWith('!')) {
1553
+ // Bash command completions
1554
+ const bashCommands = ['ls', 'cd', 'pwd', 'cat', 'grep', 'find', 'git', 'npm', 'node'];
1555
+ const matches = bashCommands.filter(cmd => cmd.startsWith(line.slice(1)));
1556
+ completions.push(...matches.map(cmd => '!' + cmd));
1557
+ } else {
1558
+ // Context-aware suggestions based on project
1559
+ const suggestions = [
1560
+ 'explain this project structure',
1561
+ 'help me debug this code',
1562
+ 'create a README file',
1563
+ 'optimize this function',
1564
+ 'add error handling',
1565
+ 'write unit tests',
1566
+ 'refactor this code'
1567
+ ];
1568
+ const matches = suggestions.filter(s => s.toLowerCase().includes(line.toLowerCase()));
1569
+ completions.push(...matches);
1570
+ }
1571
+
1572
+ return [completions, line];
1573
+ }
1574
+
1575
+ showWatchedFiles() {
1576
+ console.log('');
1577
+ console.log(chalk.magenta('╭─────────────────────────────────────────╮'));
1578
+ console.log(chalk.magenta('│') + chalk.white.bold(' Watched Files ') + chalk.magenta('│'));
1579
+ console.log(chalk.magenta('╰─────────────────────────────────────────╯'));
1580
+ console.log('');
1581
+
1582
+ if (this.watchedFiles && this.watchedFiles.size > 0) {
1583
+ this.watchedFiles.forEach(file => {
1584
+ console.log(chalk.gray('📁 ') + chalk.white(file));
1585
+ });
1586
+ } else {
1587
+ console.log(chalk.gray('No files being watched.'));
1588
+ console.log(chalk.gray('Files will be automatically watched during conversations.'));
1589
+ }
1590
+ console.log('');
1591
+ }
1592
+
1593
+ saveCurrentSession() {
1594
+ console.log('');
1595
+ console.log(chalk.magenta('╭─────────────────────────────────────────╮'));
1596
+ console.log(chalk.magenta('│') + chalk.white.bold(' Session Saved ') + chalk.magenta('│'));
1597
+ console.log(chalk.magenta('╰─────────────────────────────────────────╯'));
1598
+ console.log('');
1599
+
1600
+ const sessionData = {
1601
+ id: this.sessionId,
1602
+ startTime: this.currentSession.startTime,
1603
+ history: this.conversationHistory,
1604
+ messageCount: this.currentSession.messageCount,
1605
+ lastActivity: this.currentSession.lastActivity,
1606
+ savedAt: Date.now()
1607
+ };
1608
+
1609
+ try {
1610
+ const sessionsDir = path.join(process.cwd(), 'sessions');
1611
+ if (!fs.existsSync(sessionsDir)) {
1612
+ fs.mkdirSync(sessionsDir, { recursive: true });
1613
+ }
1614
+
1615
+ const filename = `session-${this.sessionId}.json`;
1616
+ const filepath = path.join(sessionsDir, filename);
1617
+ fs.writeFileSync(filepath, JSON.stringify(sessionData, null, 2));
1618
+
1619
+ console.log(chalk.white(`Session saved to: ${filename}`));
1620
+ console.log(chalk.gray(`${this.conversationHistory.length} messages preserved`));
1621
+ } catch (error) {
1622
+ console.log(chalk.red(`Failed to save session: ${error.message}`));
1623
+ }
1624
+ console.log('');
1625
+ }
1626
+
1627
+ handleKeyboardShortcuts(key) {
1628
+ // Advanced keyboard shortcuts like in modern IDEs
1629
+ if (key.name === 'escape') {
1630
+ console.log(chalk.magenta('\n✨ Goodbye!'));
1631
+ process.exit(0);
1632
+ } else if (key.ctrl && key.name === 'l') {
1633
+ // Ctrl+L: Clear and restart
1634
+ console.clear();
1635
+ this.displayWelcome();
1636
+ } else if (key.ctrl && key.name === 's') {
1637
+ // Ctrl+S: Quick save session
1638
+ this.rl.write('\r/save\r');
1639
+ } else if (key.ctrl && key.name === 'h') {
1640
+ // Ctrl+H: Show help
1641
+ this.rl.write('\r/help\r');
1642
+ } else if (key.ctrl && key.name === 'u') {
1643
+ // Ctrl+U: Show status
1644
+ this.rl.write('\r/status\r');
1645
+ }
1646
+ }
1647
+
1648
+ showSessionSuggestions() {
1649
+ try {
1650
+ // Check if project exists
1651
+ const projectStatus = this.projectManager.getProjectStatus();
1652
+
1653
+ if (projectStatus.exists) {
1654
+ console.log(chalk.magenta('📂 ') + chalk.white.bold(`Project: ${projectStatus.context.name}`));
1655
+ console.log(chalk.gray(` • Todos: ${projectStatus.todos.pending} pending, ${projectStatus.todos.completed} completed`));
1656
+ }
1657
+
1658
+ // Check for intelligent auto-resume candidate
1659
+ const resumeCandidate = this.projectManager.shouldOfferAutoResume();
1660
+
1661
+ if (resumeCandidate) {
1662
+ console.log(chalk.magenta('🔄 ') + chalk.white.bold('Resume Previous Session?'));
1663
+ console.log(chalk.gray(` • Session: ${resumeCandidate.id} (${resumeCandidate.activityDescription})`));
1664
+ console.log(chalk.gray(` • Messages: ${resumeCandidate.messageCount} | Model: ${resumeCandidate.model}`));
1665
+
1666
+ if (resumeCandidate.lastUserMessage) {
1667
+ console.log(chalk.gray(` • Last: "${resumeCandidate.lastUserMessage}"`));
1668
+ }
1669
+
1670
+ console.log(chalk.gray(' 💡 Type ') + chalk.cyan('/resume') + chalk.gray(' to continue or start fresh'));
1671
+ console.log('');
1672
+ } else {
1673
+ // Get recent sessions if no auto-resume candidate
1674
+ const recentSessions = this.projectManager.listSessions({ limit: 3 });
1675
+
1676
+ if (recentSessions.length > 0) {
1677
+ console.log(chalk.magenta('🕒 ') + chalk.white.bold('Recent Sessions:'));
1678
+ recentSessions.forEach((session, index) => {
1679
+ const timeAgo = this.getTimeAgo(new Date(session.modified));
1680
+ console.log(chalk.gray(` • ${session.id} - ${timeAgo} (${session.messageCount} messages)`));
1681
+ });
1682
+ console.log(chalk.gray(' 💡 Use ') + chalk.cyan('/resume <id>') + chalk.gray(' to continue a session'));
1683
+ console.log('');
1684
+ }
1685
+ }
1686
+
1687
+ } catch (error) {
1688
+ // Silently ignore errors in session suggestions
1689
+ }
1690
+ }
1691
+
1692
+ getTimeAgo(date) {
1693
+ const now = new Date();
1694
+ const seconds = Math.floor((now - date) / 1000);
1695
+
1696
+ if (seconds < 60) return 'just now';
1697
+ if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
1698
+ if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`;
1699
+ return `${Math.floor(seconds / 86400)}d ago`;
1700
+ }
1701
+
1702
+ async handleCommand(command) {
1703
+ const [cmd, ...args] = command.slice(1).split(' ');
1704
+ const startTime = Date.now();
1705
+
1706
+ // Log command execution
1707
+ this.logger.debug(`Executing command: ${cmd}`, { args });
1708
+
1709
+ try {
1710
+ switch (cmd.toLowerCase()) {
1086
1711
  case 'help':
1087
1712
  this.showHelp();
1088
1713
  break;
@@ -2945,12 +3570,15 @@ class InteractiveRecoderChat {
2945
3570
 
2946
3571
  console.log(chalk.white(assistantMessage));
2947
3572
 
2948
- // Optional voice output for AI responses
2949
- if (this.voiceManager.textToSpeechEnabled) {
2950
- // Speak the response (in background, don't wait)
2951
- this.voiceManager.speakText(assistantMessage).catch(err => {
2952
- // Ignore TTS errors to not interrupt the conversation
2953
- });
3573
+ // Only speak if user specifically requested it
3574
+ if (this.shouldSpeak || userInput.toLowerCase().includes('speak') || userInput.toLowerCase().includes('read loud')) {
3575
+ if (this.voiceManager.textToSpeechEnabled) {
3576
+ console.log(chalk.gray('🔊 Speaking response...'));
3577
+ this.voiceManager.speakText(assistantMessage).catch(err => {
3578
+ // Ignore TTS errors to not interrupt the conversation
3579
+ });
3580
+ }
3581
+ this.shouldSpeak = false; // Reset flag
2954
3582
  }
2955
3583
 
2956
3584
  // Update conversation-specific stats
@@ -3417,40 +4045,1505 @@ ${projectInfo}`
3417
4045
  }
3418
4046
  }
3419
4047
 
3420
- async cleanup() {
3421
- this.logger.info('Starting cleanup process');
4048
+ // Advanced streaming and progressive loading methods
4049
+ startProgressiveLoading() {
4050
+ if (this.progressiveLoader) {
4051
+ clearInterval(this.progressiveLoader.interval);
4052
+ }
3422
4053
 
3423
- if (this.fileWatcher) {
3424
- this.fileWatcher.close();
3425
- this.fileWatcher = null;
3426
- this.logger.debug('File watcher closed');
4054
+ const stages = [
4055
+ { text: 'Initializing...', color: 'cyan', icon: '⚡' },
4056
+ { text: 'Connecting to AI...', color: 'blue', icon: '🔗' },
4057
+ { text: 'Processing request...', color: 'magenta', icon: '🧠' },
4058
+ { text: 'Generating response...', color: 'yellow', icon: '✨' }
4059
+ ];
4060
+
4061
+ let currentStage = 0;
4062
+ let dots = 0;
4063
+
4064
+ this.progressiveLoader = {
4065
+ interval: setInterval(() => {
4066
+ const stage = stages[currentStage % stages.length];
4067
+ const dotString = '.'.repeat((dots % 3) + 1).padEnd(3);
4068
+
4069
+ process.stdout.write('\r' + ' '.repeat(50) + '\r');
4070
+ process.stdout.write(
4071
+ chalk[stage.color](stage.icon + ' ' + stage.text + dotString)
4072
+ );
4073
+
4074
+ dots++;
4075
+ if (dots % 6 === 0) {
4076
+ currentStage++;
4077
+ }
4078
+ }, 200),
4079
+ stage: 0
4080
+ };
4081
+ }
4082
+
4083
+ stopProgressiveLoading(loader = this.progressiveLoader) {
4084
+ if (loader && loader.interval) {
4085
+ clearInterval(loader.interval);
4086
+ process.stdout.write('\r' + ' '.repeat(50) + '\r');
4087
+ }
4088
+ this.progressiveLoader = null;
4089
+ }
4090
+
4091
+ async processWithAdvancedStreaming({ recoder, input, startTime, streamBuffer, onFirstChunk, onChunk }) {
4092
+ try {
4093
+ // Attempt streaming first
4094
+ const response = await recoder.processConversation(input, false, true, {
4095
+ onChunk: (chunk) => {
4096
+ if (!streamBuffer.streamingStarted) {
4097
+ this.stopProgressiveLoading();
4098
+ this.startStreamingDisplay();
4099
+ streamBuffer.streamingStarted = true;
4100
+ onFirstChunk(Date.now());
4101
+ }
4102
+
4103
+ streamBuffer.content += chunk;
4104
+ streamBuffer.chunks.push(chunk);
4105
+ streamBuffer.isStreaming = true;
4106
+ onChunk(chunk);
4107
+ }
4108
+ });
4109
+
4110
+ return response || streamBuffer.content;
4111
+
4112
+ } catch (error) {
4113
+ // Fallback to non-streaming
4114
+ this.stopProgressiveLoading();
4115
+ return await recoder.processConversation(input, false, false, null);
4116
+ }
4117
+ }
4118
+
4119
+ startStreamingDisplay() {
4120
+ if (!this.streamingActive) {
4121
+ console.log('\n' + chalk.cyan('🔥 Streaming response...'));
4122
+ this.streamingActive = true;
4123
+ this.streamCursor = 0;
3427
4124
  }
4125
+ }
4126
+
4127
+ displayStreamingChunk(chunk, chunkNumber) {
4128
+ if (!this.streamingActive) return;
3428
4129
 
3429
- // Cleanup voice manager
3430
- if (this.voiceManager) {
3431
- this.voiceManager.cleanup();
3432
- this.logger.debug('Voice manager cleaned up');
4130
+ // Add subtle chunk indicators for debugging
4131
+ if (process.env.RECODER_DEBUG === 'true') {
4132
+ process.stdout.write(chalk.gray(`[${chunkNumber}]`));
3433
4133
  }
3434
4134
 
3435
- // Cleanup collaboration manager
3436
- if (this.collaborationManager) {
3437
- this.collaborationManager.cleanup();
3438
- this.logger.debug('Collaboration manager cleaned up');
4135
+ // Display chunk with subtle animation
4136
+ const chars = chunk.split('');
4137
+ chars.forEach((char, index) => {
4138
+ setTimeout(() => {
4139
+ process.stdout.write(chalk.white(char));
4140
+ }, index * 2); // Very fast typing effect
4141
+ });
4142
+ }
4143
+
4144
+ stopStreamingDisplay() {
4145
+ this.streamingActive = false;
4146
+ this.streamCursor = 0;
4147
+ }
4148
+
4149
+ finalizeStreamingResponse(response, duration) {
4150
+ this.stopStreamingDisplay();
4151
+ console.log('\n');
4152
+ console.log(chalk.gray(`✅ Stream complete (${duration}s)`));
4153
+ }
4154
+
4155
+ async displayAdvancedTypingResponse(response, startTime) {
4156
+ if (!response) return;
4157
+
4158
+ const formattedResponse = this.formatResponse(response);
4159
+ const duration = Math.round((Date.now() - startTime) / 1000);
4160
+
4161
+ // Enhanced typing animation with word-by-word display
4162
+ const words = formattedResponse.split(' ');
4163
+ let currentLine = '';
4164
+
4165
+ for (const word of words) {
4166
+ currentLine += word + ' ';
4167
+
4168
+ // Display word
4169
+ process.stdout.write(chalk.white(word + ' '));
4170
+
4171
+ // Variable delay based on word length and content
4172
+ let delay = 30; // Base delay
4173
+ if (word.includes('\n')) delay += 100; // Pause at line breaks
4174
+ if (word.length > 10) delay += 20; // Slower for long words
4175
+ if (word.includes('.') || word.includes('!') || word.includes('?')) delay += 150; // Pause at sentences
4176
+
4177
+ await new Promise(resolve => setTimeout(resolve, delay));
3439
4178
  }
3440
4179
 
3441
- // Cleanup plugins
3442
- if (this.pluginManager) {
3443
- await this.pluginManager.cleanup();
3444
- this.logger.debug('Plugins cleaned up');
4180
+ console.log('\n' + chalk.gray(`(${duration}s • advanced typing)`));
4181
+ }
4182
+
4183
+ showAdvancedMetrics({ duration, timeToFirstChunk, totalChunks, streamingUsed }) {
4184
+ const metrics = [];
4185
+
4186
+ if (streamingUsed) {
4187
+ metrics.push(chalk.green(`🚀 Streamed ${totalChunks} chunks`));
4188
+ if (timeToFirstChunk) {
4189
+ metrics.push(chalk.blue(`⚡ First response: ${timeToFirstChunk}s`));
4190
+ }
4191
+ } else {
4192
+ metrics.push(chalk.yellow('📄 Standard response'));
3445
4193
  }
3446
4194
 
3447
- // End session logging
3448
- if (this.logger) {
3449
- this.logger.endSession();
3450
- this.logger.info('Session cleanup completed');
4195
+ metrics.push(chalk.gray(`⏱️ Total: ${duration}s`));
4196
+
4197
+ // Show tool usage indicators
4198
+ this.showToolUsage();
4199
+
4200
+ console.log(chalk.gray('Performance: ') + metrics.join(' • '));
4201
+ }
4202
+
4203
+ showEnhancedError(error) {
4204
+ // Enhanced error display with better formatting
4205
+ console.log(chalk.red('╭─────────────────────────────────────────╮'));
4206
+ console.log(chalk.red('│') + chalk.white.bold(' ❌ Error ') + chalk.red('│'));
4207
+ console.log(chalk.red('╰─────────────────────────────────────────╯'));
4208
+ console.log('');
4209
+
4210
+ // Error details
4211
+ console.log(chalk.white(error.message));
4212
+ console.log('');
4213
+
4214
+ // Enhanced contextual suggestions
4215
+ if (error.message.includes('429')) {
4216
+ console.log(chalk.yellow('💡 Rate limit: Wait 10-30 seconds before retrying'));
4217
+ } else if (error.message.includes('API key')) {
4218
+ console.log(chalk.yellow('💡 Set your API key: export OPENROUTER_API_KEY=your_key'));
4219
+ } else if (error.message.includes('network') || error.message.includes('timeout')) {
4220
+ console.log(chalk.yellow('💡 Network issue: Check connection and try again'));
4221
+ } else if (error.message.includes('model')) {
4222
+ console.log(chalk.yellow('💡 Model issue: Try switching models with /model command'));
4223
+ } else {
4224
+ console.log(chalk.yellow('💡 Try: /help for available commands or /reset to start fresh'));
4225
+ }
4226
+
4227
+ // Debug information if enabled
4228
+ if (process.env.RECODER_DEBUG === 'true') {
4229
+ console.log('');
4230
+ console.log(chalk.gray('Debug details:'));
4231
+ console.log(chalk.gray(error.stack?.split('\n').slice(0, 3).join('\n')));
3451
4232
  }
3452
4233
  }
3453
4234
 
4235
+ setupGlobalErrorHandlers() {
4236
+ // Register domain-specific handlers with unified error handler
4237
+ errorHandler.registerHandler('interactive', (error) => {
4238
+ if (this.logger) {
4239
+ this.logger.error('Interactive mode error', {
4240
+ error: error?.toString(),
4241
+ stack: error?.stack
4242
+ });
4243
+ }
4244
+
4245
+ // In debug mode, show detailed error for interactive context
4246
+ if (process.env.RECODER_DEBUG === 'true') {
4247
+ console.log(chalk.blue('\n🔍 Interactive Context Error:'));
4248
+ console.log(chalk.gray(error?.toString()));
4249
+ if (error?.stack) {
4250
+ console.log(chalk.gray(error.stack.split('\n').slice(0, 5).join('\n')));
4251
+ }
4252
+ }
4253
+ });
4254
+
4255
+ // Register MCP error handler for graceful degradation
4256
+ errorHandler.registerHandler('mcp', (error) => {
4257
+ // MCP errors are non-critical, just log for debugging
4258
+ if (process.env.RECODER_DEBUG === 'true') {
4259
+ console.log(chalk.gray('🔌 MCP connection issue (non-critical):'), error?.message);
4260
+ }
4261
+ });
4262
+
4263
+ // Handle warnings specifically
4264
+ process.on('warning', (warning) => {
4265
+ if (warning.name === 'PromiseRejectionHandledWarning' ||
4266
+ warning.name === 'UnhandledPromiseRejectionWarning') {
4267
+ if (this.logger) {
4268
+ this.logger.warn('Promise warning', { name: warning.name, message: warning.message });
4269
+ }
4270
+ }
4271
+ });
4272
+ }
4273
+
4274
+ // ========== INTELLIGENT FEATURES ==========
4275
+
4276
+ performCodeAnalysis() {
4277
+ console.log(chalk.cyan('🔍 Analyzing Codebase...'));
4278
+ console.log('');
4279
+
4280
+ const analysis = this.analyzeCurrentProject();
4281
+
4282
+ console.log(chalk.magenta('╭─────────────────────────────────────────╮'));
4283
+ console.log(chalk.magenta('│') + chalk.white.bold(' 📊 Code Analysis Results ') + chalk.magenta('│'));
4284
+ console.log(chalk.magenta('╰─────────────────────────────────────────╯'));
4285
+ console.log('');
4286
+
4287
+ console.log(chalk.cyan('📁 Project Structure:'));
4288
+ console.log(chalk.white(` • Files: ${analysis.fileCount}`));
4289
+ console.log(chalk.white(` • Languages: ${analysis.languages.join(', ')}`));
4290
+ console.log(chalk.white(` • Main Language: ${analysis.mainLanguage}`));
4291
+ console.log('');
4292
+
4293
+ console.log(chalk.green('✅ Strengths:'));
4294
+ analysis.strengths.forEach(strength => {
4295
+ console.log(chalk.white(` • ${strength}`));
4296
+ });
4297
+ console.log('');
4298
+
4299
+ console.log(chalk.yellow('💡 Suggestions:'));
4300
+ analysis.suggestions.forEach(suggestion => {
4301
+ console.log(chalk.white(` • ${suggestion}`));
4302
+ });
4303
+ console.log('');
4304
+ }
4305
+
4306
+ analyzeCurrentProject() {
4307
+ const fs = require('fs');
4308
+ const path = require('path');
4309
+
4310
+ try {
4311
+ const analysis = {
4312
+ fileCount: 0,
4313
+ languages: new Set(),
4314
+ fileTypes: {},
4315
+ dependencies: [],
4316
+ devDependencies: [],
4317
+ strengths: [],
4318
+ issues: [],
4319
+ suggestions: []
4320
+ };
4321
+
4322
+ // Scan actual files in project
4323
+ const scanDirectory = (dir, depth = 0) => {
4324
+ if (depth > 3) return; // Limit depth
4325
+
4326
+ try {
4327
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
4328
+
4329
+ for (const entry of entries) {
4330
+ const fullPath = path.join(dir, entry.name);
4331
+
4332
+ // Skip node_modules, .git, etc.
4333
+ if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;
4334
+
4335
+ if (entry.isFile()) {
4336
+ analysis.fileCount++;
4337
+ const ext = path.extname(entry.name).toLowerCase();
4338
+
4339
+ // Count file types
4340
+ analysis.fileTypes[ext] = (analysis.fileTypes[ext] || 0) + 1;
4341
+
4342
+ // Detect languages
4343
+ const languageMap = {
4344
+ '.js': 'JavaScript',
4345
+ '.ts': 'TypeScript',
4346
+ '.py': 'Python',
4347
+ '.java': 'Java',
4348
+ '.cpp': 'C++',
4349
+ '.c': 'C',
4350
+ '.cs': 'C#',
4351
+ '.go': 'Go',
4352
+ '.rs': 'Rust',
4353
+ '.php': 'PHP',
4354
+ '.rb': 'Ruby',
4355
+ '.json': 'JSON',
4356
+ '.md': 'Markdown',
4357
+ '.html': 'HTML',
4358
+ '.css': 'CSS',
4359
+ '.scss': 'SCSS',
4360
+ '.yaml': 'YAML',
4361
+ '.yml': 'YAML'
4362
+ };
4363
+
4364
+ if (languageMap[ext]) {
4365
+ analysis.languages.add(languageMap[ext]);
4366
+ }
4367
+ } else if (entry.isDirectory()) {
4368
+ scanDirectory(fullPath, depth + 1);
4369
+ }
4370
+ }
4371
+ } catch (error) {
4372
+ // Skip directories we can't read
4373
+ }
4374
+ };
4375
+
4376
+ // Scan from current directory
4377
+ scanDirectory(process.cwd());
4378
+
4379
+ // Analyze package.json if it exists
4380
+ try {
4381
+ const packagePath = path.join(process.cwd(), 'package.json');
4382
+ if (fs.existsSync(packagePath)) {
4383
+ const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
4384
+ analysis.dependencies = Object.keys(packageJson.dependencies || {});
4385
+ analysis.devDependencies = Object.keys(packageJson.devDependencies || {});
4386
+
4387
+ // Analyze dependencies for insights
4388
+ if (analysis.dependencies.includes('express')) analysis.strengths.push('Express.js web framework detected');
4389
+ if (analysis.dependencies.includes('react')) analysis.strengths.push('React frontend framework detected');
4390
+ if (analysis.dependencies.includes('chalk')) analysis.strengths.push('Terminal styling with chalk');
4391
+ if (analysis.devDependencies.includes('jest')) analysis.strengths.push('Jest testing framework configured');
4392
+ if (analysis.devDependencies.includes('eslint')) analysis.strengths.push('ESLint code linting configured');
4393
+ }
4394
+ } catch (error) {
4395
+ analysis.issues.push('Could not read package.json');
4396
+ }
4397
+
4398
+ // Generate insights based on actual analysis
4399
+ const languageArray = Array.from(analysis.languages);
4400
+ const mainLanguage = this.determineMainLanguage(analysis.fileTypes);
4401
+
4402
+ if (analysis.fileCount === 0) {
4403
+ analysis.issues.push('No files found in project');
4404
+ } else if (analysis.fileCount > 1000) {
4405
+ analysis.issues.push('Large project detected - consider optimization');
4406
+ }
4407
+
4408
+ if (!languageArray.includes('TypeScript') && languageArray.includes('JavaScript')) {
4409
+ analysis.suggestions.push('Consider migrating to TypeScript for better type safety');
4410
+ }
4411
+
4412
+ if (!analysis.fileTypes['.md']) {
4413
+ analysis.suggestions.push('Add README.md and documentation');
4414
+ }
4415
+
4416
+ if (analysis.dependencies.length === 0) {
4417
+ analysis.suggestions.push('Consider using npm/yarn for dependency management');
4418
+ }
4419
+
4420
+ if (!analysis.devDependencies.includes('jest') && !analysis.devDependencies.includes('mocha')) {
4421
+ analysis.suggestions.push('Add testing framework (Jest/Mocha)');
4422
+ }
4423
+
4424
+ return {
4425
+ fileCount: analysis.fileCount,
4426
+ languages: languageArray,
4427
+ mainLanguage: mainLanguage,
4428
+ fileTypes: analysis.fileTypes,
4429
+ dependencies: analysis.dependencies,
4430
+ devDependencies: analysis.devDependencies,
4431
+ strengths: analysis.strengths.length > 0 ? analysis.strengths : ['Project structure analyzed'],
4432
+ issues: analysis.issues,
4433
+ suggestions: analysis.suggestions.length > 0 ? analysis.suggestions : ['Project appears well-structured']
4434
+ };
4435
+
4436
+ } catch (error) {
4437
+ return {
4438
+ fileCount: 0,
4439
+ languages: ['Unknown'],
4440
+ mainLanguage: 'Unknown',
4441
+ fileTypes: {},
4442
+ dependencies: [],
4443
+ devDependencies: [],
4444
+ strengths: ['Basic analysis attempted'],
4445
+ issues: [`Analysis error: ${error.message}`],
4446
+ suggestions: ['Ensure proper file permissions']
4447
+ };
4448
+ }
4449
+ }
4450
+
4451
+ determineMainLanguage(fileTypes) {
4452
+ let maxCount = 0;
4453
+ let mainExt = '';
4454
+
4455
+ for (const [ext, count] of Object.entries(fileTypes)) {
4456
+ if (count > maxCount && ext !== '.md' && ext !== '.json') {
4457
+ maxCount = count;
4458
+ mainExt = ext;
4459
+ }
4460
+ }
4461
+
4462
+ const languageMap = {
4463
+ '.js': 'JavaScript',
4464
+ '.ts': 'TypeScript',
4465
+ '.py': 'Python',
4466
+ '.java': 'Java',
4467
+ '.cpp': 'C++',
4468
+ '.go': 'Go',
4469
+ '.rs': 'Rust'
4470
+ };
4471
+
4472
+ return languageMap[mainExt] || 'Mixed';
4473
+ }
4474
+
4475
+ showGitStatus() {
4476
+ console.log(chalk.cyan('📊 Git Repository Status'));
4477
+ console.log('');
4478
+
4479
+ const { execSync } = require('child_process');
4480
+
4481
+ try {
4482
+ // Check if git is available
4483
+ execSync('git --version', { stdio: 'ignore' });
4484
+
4485
+ // Check if we're in a git repository
4486
+ execSync('git rev-parse --git-dir', { stdio: 'ignore' });
4487
+
4488
+ console.log(chalk.magenta('╭─────────────────────────────────────────╮'));
4489
+ console.log(chalk.magenta('│') + chalk.white.bold(' 🌿 Git Repository Analysis ') + chalk.magenta('│'));
4490
+ console.log(chalk.magenta('╰─────────────────────────────────────────╯'));
4491
+ console.log('');
4492
+
4493
+ // Get comprehensive git information
4494
+ const gitInfo = this.getGitInformation();
4495
+
4496
+ // Display branch information
4497
+ console.log(chalk.green(`🌿 Current Branch: ${gitInfo.currentBranch}`));
4498
+ if (gitInfo.upstreamBranch) {
4499
+ console.log(chalk.blue(`🔗 Upstream: ${gitInfo.upstreamBranch}`));
4500
+ }
4501
+
4502
+ // Show commits ahead/behind
4503
+ if (gitInfo.aheadBehind.ahead > 0 || gitInfo.aheadBehind.behind > 0) {
4504
+ console.log(chalk.yellow(`📊 Sync Status: ${gitInfo.aheadBehind.ahead} ahead, ${gitInfo.aheadBehind.behind} behind`));
4505
+ }
4506
+ console.log('');
4507
+
4508
+ // Show working directory status
4509
+ if (gitInfo.statusFiles.length > 0) {
4510
+ console.log(chalk.yellow('📝 Working Directory Changes:'));
4511
+
4512
+ const statusCounts = { modified: 0, added: 0, deleted: 0, untracked: 0 };
4513
+
4514
+ gitInfo.statusFiles.forEach(file => {
4515
+ let statusIcon = '❓';
4516
+ let statusColor = 'white';
4517
+
4518
+ if (file.status.includes('M')) {
4519
+ statusIcon = '📝';
4520
+ statusColor = 'yellow';
4521
+ statusCounts.modified++;
4522
+ } else if (file.status.includes('A')) {
4523
+ statusIcon = '➕';
4524
+ statusColor = 'green';
4525
+ statusCounts.added++;
4526
+ } else if (file.status.includes('D')) {
4527
+ statusIcon = '🗑️';
4528
+ statusColor = 'red';
4529
+ statusCounts.deleted++;
4530
+ } else if (file.status.includes('??')) {
4531
+ statusIcon = '❔';
4532
+ statusColor = 'gray';
4533
+ statusCounts.untracked++;
4534
+ }
4535
+
4536
+ console.log(chalk[statusColor](` ${statusIcon} ${file.file}`));
4537
+ });
4538
+
4539
+ console.log('');
4540
+ console.log(chalk.cyan('📊 Summary:'));
4541
+ if (statusCounts.modified > 0) console.log(chalk.yellow(` 📝 ${statusCounts.modified} modified`));
4542
+ if (statusCounts.added > 0) console.log(chalk.green(` ➕ ${statusCounts.added} added`));
4543
+ if (statusCounts.deleted > 0) console.log(chalk.red(` 🗑️ ${statusCounts.deleted} deleted`));
4544
+ if (statusCounts.untracked > 0) console.log(chalk.gray(` ❔ ${statusCounts.untracked} untracked`));
4545
+
4546
+ } else {
4547
+ console.log(chalk.green('✅ Working directory clean'));
4548
+ }
4549
+
4550
+ console.log('');
4551
+
4552
+ // Show recent commits with more detail
4553
+ console.log(chalk.blue('📚 Recent Commits:'));
4554
+ gitInfo.recentCommits.forEach((commit, index) => {
4555
+ const timeAgo = this.getTimeAgo(commit.date);
4556
+ console.log(chalk.gray(` ${index + 1}. ${commit.hash} ${commit.message}`));
4557
+ console.log(chalk.gray(` by ${commit.author} ${timeAgo}`));
4558
+ });
4559
+
4560
+ console.log('');
4561
+
4562
+ // Show repository statistics
4563
+ console.log(chalk.cyan('📈 Repository Stats:'));
4564
+ console.log(chalk.white(` 🏷️ Total Tags: ${gitInfo.stats.totalTags}`));
4565
+ console.log(chalk.white(` 🌿 Total Branches: ${gitInfo.stats.totalBranches}`));
4566
+ console.log(chalk.white(` 📝 Total Commits: ${gitInfo.stats.totalCommits}`));
4567
+ console.log(chalk.white(` 👥 Contributors: ${gitInfo.stats.contributors}`));
4568
+
4569
+ } catch (error) {
4570
+ if (error.message.includes('not a git repository')) {
4571
+ console.log(chalk.red('❌ Not a git repository'));
4572
+ console.log(chalk.gray('Initialize with: git init'));
4573
+ } else if (error.message.includes('git --version')) {
4574
+ console.log(chalk.red('❌ Git not installed or not in PATH'));
4575
+ } else {
4576
+ console.log(chalk.red(`❌ Git error: ${error.message}`));
4577
+ }
4578
+ }
4579
+ console.log('');
4580
+ }
4581
+
4582
+ getGitInformation() {
4583
+ const { execSync } = require('child_process');
4584
+
4585
+ try {
4586
+ // Get current branch
4587
+ const currentBranch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();
4588
+
4589
+ // Get upstream branch
4590
+ let upstreamBranch = null;
4591
+ try {
4592
+ upstreamBranch = execSync('git rev-parse --abbrev-ref @{upstream}', { encoding: 'utf8' }).trim();
4593
+ } catch (e) {
4594
+ // No upstream branch
4595
+ }
4596
+
4597
+ // Get ahead/behind count
4598
+ let aheadBehind = { ahead: 0, behind: 0 };
4599
+ if (upstreamBranch) {
4600
+ try {
4601
+ const aheadBehindOutput = execSync(`git rev-list --count --left-right ${upstreamBranch}...HEAD`, { encoding: 'utf8' }).trim();
4602
+ const [behind, ahead] = aheadBehindOutput.split('\t').map(Number);
4603
+ aheadBehind = { ahead, behind };
4604
+ } catch (e) {
4605
+ // Can't determine ahead/behind
4606
+ }
4607
+ }
4608
+
4609
+ // Get status files
4610
+ const statusOutput = execSync('git status --porcelain', { encoding: 'utf8' });
4611
+ const statusFiles = statusOutput.split('\n')
4612
+ .filter(line => line.trim())
4613
+ .map(line => ({
4614
+ status: line.substring(0, 2),
4615
+ file: line.substring(3)
4616
+ }));
4617
+
4618
+ // Get recent commits with detailed info
4619
+ const commitOutput = execSync('git log --oneline --format="%H|%s|%an|%ad" --date=iso -10', { encoding: 'utf8' });
4620
+ const recentCommits = commitOutput.split('\n')
4621
+ .filter(line => line.trim())
4622
+ .map(line => {
4623
+ const [hash, message, author, date] = line.split('|');
4624
+ return {
4625
+ hash: hash.substring(0, 8),
4626
+ message,
4627
+ author,
4628
+ date: new Date(date)
4629
+ };
4630
+ });
4631
+
4632
+ // Get repository statistics
4633
+ const totalTags = execSync('git tag --list | wc -l', { encoding: 'utf8' }).trim();
4634
+ const totalBranches = execSync('git branch -a | wc -l', { encoding: 'utf8' }).trim();
4635
+ const totalCommits = execSync('git rev-list --count HEAD', { encoding: 'utf8' }).trim();
4636
+ const contributors = execSync('git shortlog -sn | wc -l', { encoding: 'utf8' }).trim();
4637
+
4638
+ return {
4639
+ currentBranch,
4640
+ upstreamBranch,
4641
+ aheadBehind,
4642
+ statusFiles,
4643
+ recentCommits,
4644
+ stats: {
4645
+ totalTags: parseInt(totalTags) || 0,
4646
+ totalBranches: parseInt(totalBranches) || 0,
4647
+ totalCommits: parseInt(totalCommits) || 0,
4648
+ contributors: parseInt(contributors) || 0
4649
+ }
4650
+ };
4651
+
4652
+ } catch (error) {
4653
+ throw error;
4654
+ }
4655
+ }
4656
+
4657
+ getTimeAgo(date) {
4658
+ const now = new Date();
4659
+ const diffMs = now - date;
4660
+ const diffMins = Math.floor(diffMs / 60000);
4661
+ const diffHours = Math.floor(diffMs / 3600000);
4662
+ const diffDays = Math.floor(diffMs / 86400000);
4663
+
4664
+ if (diffMins < 60) return `${diffMins} minutes ago`;
4665
+ if (diffHours < 24) return `${diffHours} hours ago`;
4666
+ if (diffDays < 30) return `${diffDays} days ago`;
4667
+ return date.toLocaleDateString();
4668
+ }
4669
+
4670
+ performCodeReview() {
4671
+ console.log(chalk.cyan('🔍 Performing AI Code Review...'));
4672
+ console.log('');
4673
+
4674
+ console.log(chalk.magenta('╭─────────────────────────────────────────╮'));
4675
+ console.log(chalk.magenta('│') + chalk.white.bold(' 🤖 AI Code Review ') + chalk.magenta('│'));
4676
+ console.log(chalk.magenta('╰─────────────────────────────────────────╯'));
4677
+ console.log('');
4678
+
4679
+ const review = this.generateRealCodeReview();
4680
+
4681
+ // Display scores with color coding
4682
+ const getScoreColor = (score) => {
4683
+ if (score >= 90) return 'green';
4684
+ if (score >= 75) return 'yellow';
4685
+ return 'red';
4686
+ };
4687
+
4688
+ console.log(chalk[getScoreColor(review.scores.quality)](`✅ Code Quality: ${review.scores.quality}/100`));
4689
+ console.log(chalk[getScoreColor(review.scores.maintainability)](`📊 Maintainability: ${review.scores.maintainability}/100`));
4690
+ console.log(chalk[getScoreColor(review.scores.security)](`🛡️ Security: ${review.scores.security}/100`));
4691
+ console.log(chalk[getScoreColor(review.scores.testCoverage)](`🧪 Test Coverage: ${review.scores.testCoverage}/100`));
4692
+ console.log('');
4693
+
4694
+ // Display architecture analysis
4695
+ if (review.architecture.length > 0) {
4696
+ console.log(chalk.blue('🏗️ Architecture Analysis:'));
4697
+ review.architecture.forEach(item => {
4698
+ console.log(chalk.white(` • ${item.type}: ${item.description}`));
4699
+ if (item.file) {
4700
+ console.log(chalk.gray(` File: ${item.file}`));
4701
+ }
4702
+ });
4703
+ console.log('');
4704
+ }
4705
+
4706
+ // Display code quality issues
4707
+ if (review.qualityIssues.length > 0) {
4708
+ console.log(chalk.yellow('⚠️ Code Quality Issues:'));
4709
+ review.qualityIssues.forEach(issue => {
4710
+ console.log(chalk.white(` • ${issue.type}: ${issue.description}`));
4711
+ console.log(chalk.gray(` File: ${issue.file}:${issue.line}`));
4712
+ });
4713
+ console.log('');
4714
+ }
4715
+
4716
+ // Display maintainability metrics
4717
+ if (review.maintainabilityMetrics.length > 0) {
4718
+ console.log(chalk.cyan('📈 Maintainability Metrics:'));
4719
+ review.maintainabilityMetrics.forEach(metric => {
4720
+ console.log(chalk.white(` • ${metric.metric}: ${metric.value} (${metric.assessment})`));
4721
+ if (metric.suggestion) {
4722
+ console.log(chalk.gray(` Suggestion: ${metric.suggestion}`));
4723
+ }
4724
+ });
4725
+ console.log('');
4726
+ }
4727
+
4728
+ // Display AI recommendations
4729
+ if (review.recommendations.length > 0) {
4730
+ console.log(chalk.cyan('💡 AI-Powered Recommendations:'));
4731
+ review.recommendations.forEach(rec => {
4732
+ console.log(chalk.white(` • ${rec.category}: ${rec.suggestion}`));
4733
+ if (rec.priority) {
4734
+ console.log(chalk.gray(` Priority: ${rec.priority}`));
4735
+ }
4736
+ });
4737
+ console.log('');
4738
+ }
4739
+
4740
+ // Display overall assessment
4741
+ const overallScore = Math.round((review.scores.quality + review.scores.maintainability + review.scores.security + review.scores.testCoverage) / 4);
4742
+ console.log(chalk[getScoreColor(overallScore)](`🎯 Overall Code Health: ${overallScore}/100`));
4743
+ }
4744
+
4745
+ generateRealCodeReview() {
4746
+ const fs = require('fs');
4747
+ const path = require('path');
4748
+
4749
+ const review = {
4750
+ scores: {
4751
+ quality: 100,
4752
+ maintainability: 100,
4753
+ security: 100,
4754
+ testCoverage: 50
4755
+ },
4756
+ architecture: [],
4757
+ qualityIssues: [],
4758
+ maintainabilityMetrics: [],
4759
+ recommendations: []
4760
+ };
4761
+
4762
+ // Code quality patterns to analyze
4763
+ const qualityPatterns = [
4764
+ { pattern: /function\s+\w+\s*\([^)]*\)\s*\{[\s\S]{500,}?\}/, type: 'Long Function', description: 'Function exceeds recommended length' },
4765
+ { pattern: /if\s*\([^)]+\)\s*\{[\s\S]*?if\s*\([^)]+\)\s*\{[\s\S]*?if\s*\([^)]+\)/, type: 'Deep Nesting', description: 'Excessive nesting depth detected' },
4766
+ { pattern: /var\s+\w+/, type: 'Var Usage', description: 'Use const/let instead of var' },
4767
+ { pattern: /==\s*(?!null)/, type: 'Loose Equality', description: 'Use strict equality (===) instead' },
4768
+ { pattern: /console\.log/, type: 'Debug Code', description: 'Remove console.log from production code' },
4769
+ { pattern: /\.catch\s*\(\s*\(\s*\)\s*=>\s*\{\s*\}\s*\)/, type: 'Empty Catch', description: 'Empty catch block - handle errors properly' }
4770
+ ];
4771
+
4772
+ // Maintainability patterns
4773
+ const maintainabilityPatterns = [
4774
+ { pattern: /\/\*[\s\S]*?\*\/|\/\/.*$/gm, type: 'Comments', description: 'Code documentation' },
4775
+ { pattern: /class\s+\w+/, type: 'Classes', description: 'Object-oriented structure' },
4776
+ { pattern: /export\s+/, type: 'Modules', description: 'Modular architecture' },
4777
+ { pattern: /import\s+.*from/, type: 'Dependencies', description: 'Dependency management' }
4778
+ ];
4779
+
4780
+ // Architecture patterns
4781
+ const architecturePatterns = [
4782
+ { pattern: /class\s+\w+Manager/, type: 'Manager Pattern', description: 'Centralized management logic' },
4783
+ { pattern: /\.on\(|\.emit\(/, type: 'Event-Driven', description: 'Event-driven architecture' },
4784
+ { pattern: /async\s+function|await\s+/, type: 'Async Pattern', description: 'Asynchronous programming patterns' },
4785
+ { pattern: /module\.exports\s*=/, type: 'Module System', description: 'CommonJS module pattern' }
4786
+ ];
4787
+
4788
+ let totalLines = 0;
4789
+ let totalFunctions = 0;
4790
+ let totalClasses = 0;
4791
+ let commentLines = 0;
4792
+ let testFiles = 0;
4793
+
4794
+ // Analyze codebase
4795
+ const analyzeDirectory = (dir, depth = 0) => {
4796
+ if (depth > 3) return;
4797
+
4798
+ try {
4799
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
4800
+
4801
+ for (const entry of entries) {
4802
+ const fullPath = path.join(dir, entry.name);
4803
+
4804
+ if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;
4805
+
4806
+ if (entry.isFile() && this.isCodeFile(entry.name)) {
4807
+ this.analyzeCodeFile(fullPath, qualityPatterns, maintainabilityPatterns, architecturePatterns, review);
4808
+
4809
+ // Count metrics
4810
+ const content = fs.readFileSync(fullPath, 'utf8');
4811
+ const lines = content.split('\n');
4812
+ totalLines += lines.length;
4813
+
4814
+ // Count functions and classes
4815
+ totalFunctions += (content.match(/function\s+\w+|=>\s*\{|\w+\s*:\s*function/g) || []).length;
4816
+ totalClasses += (content.match(/class\s+\w+/g) || []).length;
4817
+
4818
+ // Count comments
4819
+ commentLines += (content.match(/\/\*[\s\S]*?\*\/|\/\/.*$/gm) || []).length;
4820
+
4821
+ // Check if it's a test file
4822
+ if (entry.name.includes('test') || entry.name.includes('spec')) {
4823
+ testFiles++;
4824
+ }
4825
+
4826
+ } else if (entry.isDirectory()) {
4827
+ analyzeDirectory(fullPath, depth + 1);
4828
+ }
4829
+ }
4830
+ } catch (error) {
4831
+ // Skip directories we can't read
4832
+ }
4833
+ };
4834
+
4835
+ analyzeDirectory(process.cwd());
4836
+
4837
+ // Calculate scores based on findings
4838
+ const qualityIssueCount = review.qualityIssues.length;
4839
+ review.scores.quality = Math.max(0, 100 - (qualityIssueCount * 5));
4840
+
4841
+ // Maintainability score based on structure and documentation
4842
+ const commentRatio = totalLines > 0 ? (commentLines / totalLines) * 100 : 0;
4843
+ const avgFunctionsPerFile = totalFunctions > 0 ? totalFunctions / (totalLines / 50) : 0;
4844
+
4845
+ review.scores.maintainability = Math.max(0, 100 -
4846
+ (avgFunctionsPerFile > 10 ? 20 : 0) -
4847
+ (commentRatio < 10 ? 15 : 0) -
4848
+ (totalClasses === 0 ? 10 : 0));
4849
+
4850
+ // Test coverage estimation
4851
+ review.scores.testCoverage = Math.min(100, testFiles * 20);
4852
+
4853
+ // Add maintainability metrics
4854
+ review.maintainabilityMetrics.push({
4855
+ metric: 'Comment Ratio',
4856
+ value: `${commentRatio.toFixed(1)}%`,
4857
+ assessment: commentRatio > 15 ? 'Good' : commentRatio > 5 ? 'Fair' : 'Poor',
4858
+ suggestion: commentRatio < 10 ? 'Add more documentation' : null
4859
+ });
4860
+
4861
+ review.maintainabilityMetrics.push({
4862
+ metric: 'Total Lines of Code',
4863
+ value: totalLines.toString(),
4864
+ assessment: totalLines < 5000 ? 'Manageable' : totalLines < 15000 ? 'Large' : 'Very Large'
4865
+ });
4866
+
4867
+ review.maintainabilityMetrics.push({
4868
+ metric: 'Functions Count',
4869
+ value: totalFunctions.toString(),
4870
+ assessment: 'Active'
4871
+ });
4872
+
4873
+ review.maintainabilityMetrics.push({
4874
+ metric: 'Classes Count',
4875
+ value: totalClasses.toString(),
4876
+ assessment: totalClasses > 0 ? 'Object-oriented' : 'Functional'
4877
+ });
4878
+
4879
+ // Generate AI-powered recommendations
4880
+ if (qualityIssueCount > 0) {
4881
+ review.recommendations.push({
4882
+ category: 'Code Quality',
4883
+ suggestion: 'Address code quality issues to improve maintainability',
4884
+ priority: 'High'
4885
+ });
4886
+ }
4887
+
4888
+ if (review.scores.testCoverage < 70) {
4889
+ review.recommendations.push({
4890
+ category: 'Testing',
4891
+ suggestion: 'Increase test coverage with unit and integration tests',
4892
+ priority: 'High'
4893
+ });
4894
+ }
4895
+
4896
+ if (commentRatio < 10) {
4897
+ review.recommendations.push({
4898
+ category: 'Documentation',
4899
+ suggestion: 'Add more inline documentation and comments',
4900
+ priority: 'Medium'
4901
+ });
4902
+ }
4903
+
4904
+ review.recommendations.push({
4905
+ category: 'Architecture',
4906
+ suggestion: 'Consider implementing design patterns for better code organization',
4907
+ priority: 'Medium'
4908
+ });
4909
+
4910
+ review.recommendations.push({
4911
+ category: 'Performance',
4912
+ suggestion: 'Profile critical code paths and optimize bottlenecks',
4913
+ priority: 'Low'
4914
+ });
4915
+
4916
+ review.recommendations.push({
4917
+ category: 'Security',
4918
+ suggestion: 'Implement input validation and security best practices',
4919
+ priority: 'High'
4920
+ });
4921
+
4922
+ return review;
4923
+ }
4924
+
4925
+ analyzeCodeFile(filePath, qualityPatterns, maintainabilityPatterns, architecturePatterns, review) {
4926
+ const fs = require('fs');
4927
+
4928
+ try {
4929
+ const content = fs.readFileSync(filePath, 'utf8');
4930
+ const lines = content.split('\n');
4931
+
4932
+ // Check for quality issues
4933
+ for (const patternInfo of qualityPatterns) {
4934
+ if (patternInfo.pattern.test(content)) {
4935
+ review.qualityIssues.push({
4936
+ type: patternInfo.type,
4937
+ description: patternInfo.description,
4938
+ file: filePath.replace(process.cwd(), '.'),
4939
+ line: this.findPatternLine(lines, patternInfo.pattern)
4940
+ });
4941
+ }
4942
+ }
4943
+
4944
+ // Check architecture patterns
4945
+ for (const patternInfo of architecturePatterns) {
4946
+ if (patternInfo.pattern.test(content)) {
4947
+ review.architecture.push({
4948
+ type: patternInfo.type,
4949
+ description: patternInfo.description,
4950
+ file: filePath.replace(process.cwd(), '.')
4951
+ });
4952
+ }
4953
+ }
4954
+
4955
+ } catch (error) {
4956
+ // Skip files we can't read
4957
+ }
4958
+ }
4959
+
4960
+ findPatternLine(lines, pattern) {
4961
+ for (let i = 0; i < lines.length; i++) {
4962
+ if (pattern.test(lines[i])) {
4963
+ return i + 1;
4964
+ }
4965
+ }
4966
+ return 1;
4967
+ }
4968
+
4969
+ performSecurityAnalysis() {
4970
+ console.log(chalk.cyan('🛡️ Performing Security Analysis...'));
4971
+ console.log('');
4972
+
4973
+ console.log(chalk.magenta('╭─────────────────────────────────────────╮'));
4974
+ console.log(chalk.magenta('│') + chalk.white.bold(' 🛡️ Security Vulnerability Scan ') + chalk.magenta('│'));
4975
+ console.log(chalk.magenta('╰─────────────────────────────────────────╯'));
4976
+ console.log('');
4977
+
4978
+ const securityReport = this.performRealSecurityScan();
4979
+
4980
+ // Display security score
4981
+ let scoreColor = 'green';
4982
+ if (securityReport.score < 70) scoreColor = 'red';
4983
+ else if (securityReport.score < 85) scoreColor = 'yellow';
4984
+
4985
+ console.log(chalk[scoreColor](`🛡️ Security Score: ${securityReport.score}/100`));
4986
+ console.log('');
4987
+
4988
+ // Display vulnerabilities by severity
4989
+ if (securityReport.vulnerabilities.critical.length > 0) {
4990
+ console.log(chalk.red('🚨 Critical Vulnerabilities:'));
4991
+ securityReport.vulnerabilities.critical.forEach(vuln => {
4992
+ console.log(chalk.red(` • ${vuln.type}: ${vuln.description}`));
4993
+ console.log(chalk.gray(` File: ${vuln.file}:${vuln.line}`));
4994
+ });
4995
+ console.log('');
4996
+ }
4997
+
4998
+ if (securityReport.vulnerabilities.high.length > 0) {
4999
+ console.log(chalk.red('⚠️ High Risk Issues:'));
5000
+ securityReport.vulnerabilities.high.forEach(vuln => {
5001
+ console.log(chalk.yellow(` • ${vuln.type}: ${vuln.description}`));
5002
+ console.log(chalk.gray(` File: ${vuln.file}:${vuln.line}`));
5003
+ });
5004
+ console.log('');
5005
+ }
5006
+
5007
+ if (securityReport.vulnerabilities.medium.length > 0) {
5008
+ console.log(chalk.yellow('⚠️ Medium Risk Issues:'));
5009
+ securityReport.vulnerabilities.medium.forEach(vuln => {
5010
+ console.log(chalk.yellow(` • ${vuln.type}: ${vuln.description}`));
5011
+ console.log(chalk.gray(` File: ${vuln.file}:${vuln.line}`));
5012
+ });
5013
+ console.log('');
5014
+ }
5015
+
5016
+ if (securityReport.vulnerabilities.low.length > 0) {
5017
+ console.log(chalk.cyan('ℹ️ Low Risk Issues:'));
5018
+ securityReport.vulnerabilities.low.forEach(vuln => {
5019
+ console.log(chalk.cyan(` • ${vuln.type}: ${vuln.description}`));
5020
+ console.log(chalk.gray(` File: ${vuln.file}:${vuln.line}`));
5021
+ });
5022
+ console.log('');
5023
+ }
5024
+
5025
+ // Show dependency vulnerabilities
5026
+ if (securityReport.dependencyIssues.length > 0) {
5027
+ console.log(chalk.yellow('📦 Dependency Vulnerabilities:'));
5028
+ securityReport.dependencyIssues.forEach(dep => {
5029
+ console.log(chalk.yellow(` • ${dep.name}@${dep.version}: ${dep.issue}`));
5030
+ console.log(chalk.gray(` Severity: ${dep.severity}, Fix: ${dep.fix}`));
5031
+ });
5032
+ console.log('');
5033
+ }
5034
+
5035
+ // Show recommendations
5036
+ if (securityReport.recommendations.length > 0) {
5037
+ console.log(chalk.cyan('💡 Security Recommendations:'));
5038
+ securityReport.recommendations.forEach(rec => {
5039
+ console.log(chalk.white(` • ${rec}`));
5040
+ });
5041
+ console.log('');
5042
+ }
5043
+
5044
+ // Summary
5045
+ const totalIssues = securityReport.vulnerabilities.critical.length +
5046
+ securityReport.vulnerabilities.high.length +
5047
+ securityReport.vulnerabilities.medium.length +
5048
+ securityReport.vulnerabilities.low.length;
5049
+
5050
+ if (totalIssues === 0) {
5051
+ console.log(chalk.green('✅ No security vulnerabilities detected in code scan'));
5052
+ } else {
5053
+ console.log(chalk.yellow(`📊 Total Issues Found: ${totalIssues}`));
5054
+ }
5055
+ }
5056
+
5057
+ performRealSecurityScan() {
5058
+ const fs = require('fs');
5059
+ const path = require('path');
5060
+
5061
+ const securityReport = {
5062
+ score: 100,
5063
+ vulnerabilities: {
5064
+ critical: [],
5065
+ high: [],
5066
+ medium: [],
5067
+ low: []
5068
+ },
5069
+ dependencyIssues: [],
5070
+ recommendations: []
5071
+ };
5072
+
5073
+ // Security patterns to detect
5074
+ const securityPatterns = {
5075
+ critical: [
5076
+ { pattern: /password\s*=\s*['"][^'"]+['"]/, type: 'Hardcoded Password', description: 'Hardcoded password detected' },
5077
+ { pattern: /api[_-]?key\s*=\s*['"][^'"]+['"]/, type: 'Hardcoded API Key', description: 'Hardcoded API key detected' },
5078
+ { pattern: /secret\s*=\s*['"][^'"]+['"]/, type: 'Hardcoded Secret', description: 'Hardcoded secret detected' },
5079
+ { pattern: /eval\s*\(/, type: 'Code Injection', description: 'Use of eval() detected - potential code injection risk' }
5080
+ ],
5081
+ high: [
5082
+ { pattern: /exec\s*\(.*\$/, type: 'Command Injection', description: 'Potential command injection vulnerability' },
5083
+ { pattern: /innerHTML\s*=/, type: 'XSS Risk', description: 'Use of innerHTML - potential XSS vulnerability' },
5084
+ { pattern: /document\.write\s*\(/, type: 'XSS Risk', description: 'Use of document.write - potential XSS vulnerability' },
5085
+ { pattern: /sql\s*=.*\+/, type: 'SQL Injection', description: 'Potential SQL injection vulnerability' }
5086
+ ],
5087
+ medium: [
5088
+ { pattern: /console\.log\s*\(.*password/, type: 'Information Disclosure', description: 'Password logged to console' },
5089
+ { pattern: /console\.log\s*\(.*token/, type: 'Information Disclosure', description: 'Token logged to console' },
5090
+ { pattern: /http:\/\//, type: 'Insecure Protocol', description: 'Use of insecure HTTP protocol' },
5091
+ { pattern: /Math\.random\s*\(\)/, type: 'Weak Cryptography', description: 'Use of Math.random() for security purposes' }
5092
+ ],
5093
+ low: [
5094
+ { pattern: /TODO.*security/, type: 'Security TODO', description: 'Security-related TODO comment' },
5095
+ { pattern: /FIXME.*security/, type: 'Security FIXME', description: 'Security-related FIXME comment' },
5096
+ { pattern: /\.trim\s*\(\)\.length/, type: 'Input Validation', description: 'Consider more robust input validation' }
5097
+ ]
5098
+ };
5099
+
5100
+ // Scan files for security issues
5101
+ const scanDirectory = (dir, depth = 0) => {
5102
+ if (depth > 3) return;
5103
+
5104
+ try {
5105
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
5106
+
5107
+ for (const entry of entries) {
5108
+ const fullPath = path.join(dir, entry.name);
5109
+
5110
+ if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;
5111
+
5112
+ if (entry.isFile() && this.isCodeFile(entry.name)) {
5113
+ this.scanFileForSecurity(fullPath, securityPatterns, securityReport);
5114
+ } else if (entry.isDirectory()) {
5115
+ scanDirectory(fullPath, depth + 1);
5116
+ }
5117
+ }
5118
+ } catch (error) {
5119
+ // Skip directories we can't read
5120
+ }
5121
+ };
5122
+
5123
+ scanDirectory(process.cwd());
5124
+
5125
+ // Check dependencies for known vulnerabilities
5126
+ this.checkDependencyVulnerabilities(securityReport);
5127
+
5128
+ // Calculate final score
5129
+ const criticalCount = securityReport.vulnerabilities.critical.length;
5130
+ const highCount = securityReport.vulnerabilities.high.length;
5131
+ const mediumCount = securityReport.vulnerabilities.medium.length;
5132
+ const lowCount = securityReport.vulnerabilities.low.length;
5133
+
5134
+ securityReport.score = Math.max(0, 100 - (criticalCount * 25) - (highCount * 15) - (mediumCount * 10) - (lowCount * 5));
5135
+
5136
+ // Generate recommendations based on findings
5137
+ if (criticalCount > 0) {
5138
+ securityReport.recommendations.push('🚨 Address critical vulnerabilities immediately');
5139
+ }
5140
+ if (highCount > 0) {
5141
+ securityReport.recommendations.push('⚠️ Review and fix high-risk security issues');
5142
+ }
5143
+
5144
+ securityReport.recommendations.push('🔒 Use environment variables for sensitive data');
5145
+ securityReport.recommendations.push('🛡️ Implement input validation and sanitization');
5146
+ securityReport.recommendations.push('📦 Keep dependencies updated regularly');
5147
+ securityReport.recommendations.push('🔍 Consider using a security linter (ESLint security plugin)');
5148
+
5149
+ return securityReport;
5150
+ }
5151
+
5152
+ isCodeFile(filename) {
5153
+ const codeExtensions = ['.js', '.ts', '.py', '.java', '.cpp', '.c', '.cs', '.go', '.rs', '.php', '.rb'];
5154
+ return codeExtensions.some(ext => filename.toLowerCase().endsWith(ext));
5155
+ }
5156
+
5157
+ scanFileForSecurity(filePath, patterns, report) {
5158
+ const fs = require('fs');
5159
+
5160
+ try {
5161
+ const content = fs.readFileSync(filePath, 'utf8');
5162
+ const lines = content.split('\n');
5163
+
5164
+ for (const [severity, patternList] of Object.entries(patterns)) {
5165
+ for (const patternInfo of patternList) {
5166
+ for (let i = 0; i < lines.length; i++) {
5167
+ if (patternInfo.pattern.test(lines[i])) {
5168
+ report.vulnerabilities[severity].push({
5169
+ type: patternInfo.type,
5170
+ description: patternInfo.description,
5171
+ file: filePath.replace(process.cwd(), '.'),
5172
+ line: i + 1,
5173
+ code: lines[i].trim()
5174
+ });
5175
+ }
5176
+ }
5177
+ }
5178
+ }
5179
+ } catch (error) {
5180
+ // Skip files we can't read
5181
+ }
5182
+ }
5183
+
5184
+ checkDependencyVulnerabilities(report) {
5185
+ const fs = require('fs');
5186
+ const path = require('path');
5187
+
5188
+ try {
5189
+ const packagePath = path.join(process.cwd(), 'package.json');
5190
+ if (!fs.existsSync(packagePath)) return;
5191
+
5192
+ const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
5193
+ const dependencies = { ...packageJson.dependencies, ...packageJson.devDependencies };
5194
+
5195
+ // Check for known vulnerable packages (simplified check)
5196
+ const knownVulnerablePackages = {
5197
+ 'lodash': { versions: ['< 4.17.21'], issue: 'Prototype pollution vulnerability', severity: 'High', fix: 'Update to latest version' },
5198
+ 'node-forge': { versions: ['< 1.0.0'], issue: 'RSA PKCS#1 signature verification issue', severity: 'High', fix: 'Update to 1.0.0+' },
5199
+ 'axios': { versions: ['< 0.21.2'], issue: 'Server-side request forgery', severity: 'Medium', fix: 'Update to 0.21.2+' }
5200
+ };
5201
+
5202
+ for (const [depName, version] of Object.entries(dependencies)) {
5203
+ if (knownVulnerablePackages[depName]) {
5204
+ const vuln = knownVulnerablePackages[depName];
5205
+ report.dependencyIssues.push({
5206
+ name: depName,
5207
+ version: version,
5208
+ issue: vuln.issue,
5209
+ severity: vuln.severity,
5210
+ fix: vuln.fix
5211
+ });
5212
+ }
5213
+ }
5214
+ } catch (error) {
5215
+ // Could not check dependencies
5216
+ }
5217
+ }
5218
+
5219
+ performOptimizationAnalysis() {
5220
+ console.log(chalk.cyan('⚡ Analyzing Performance...'));
5221
+ console.log('');
5222
+
5223
+ console.log(chalk.magenta('╭─────────────────────────────────────────╮'));
5224
+ console.log(chalk.magenta('│') + chalk.white.bold(' ⚡ Performance Analysis ') + chalk.magenta('│'));
5225
+ console.log(chalk.magenta('╰─────────────────────────────────────────╯'));
5226
+ console.log('');
5227
+
5228
+ const perfReport = this.performRealPerformanceAnalysis();
5229
+
5230
+ // Display performance score
5231
+ let scoreColor = 'green';
5232
+ if (perfReport.score < 60) scoreColor = 'red';
5233
+ else if (perfReport.score < 80) scoreColor = 'yellow';
5234
+
5235
+ console.log(chalk[scoreColor](`⚡ Performance Score: ${perfReport.score}/100`));
5236
+ console.log('');
5237
+
5238
+ // Display bottlenecks
5239
+ if (perfReport.bottlenecks.length > 0) {
5240
+ console.log(chalk.yellow('🐌 Performance Bottlenecks Detected:'));
5241
+ perfReport.bottlenecks.forEach(bottleneck => {
5242
+ console.log(chalk.white(` • ${bottleneck.type}: ${bottleneck.description}`));
5243
+ console.log(chalk.gray(` File: ${bottleneck.file}:${bottleneck.line}`));
5244
+ });
5245
+ console.log('');
5246
+ }
5247
+
5248
+ // Display file size issues
5249
+ if (perfReport.fileSizeIssues.length > 0) {
5250
+ console.log(chalk.yellow('📁 Large File Issues:'));
5251
+ perfReport.fileSizeIssues.forEach(issue => {
5252
+ console.log(chalk.white(` • ${issue.file}: ${issue.sizeKB}KB (${issue.description})`));
5253
+ });
5254
+ console.log('');
5255
+ }
5256
+
5257
+ // Display dependency analysis
5258
+ if (perfReport.dependencyImpact.heavyDependencies.length > 0) {
5259
+ console.log(chalk.yellow('📦 Heavy Dependencies:'));
5260
+ perfReport.dependencyImpact.heavyDependencies.forEach(dep => {
5261
+ console.log(chalk.white(` • ${dep}: Large bundle size impact`));
5262
+ });
5263
+ console.log('');
5264
+ }
5265
+
5266
+ // Display optimization suggestions
5267
+ if (perfReport.optimizations.length > 0) {
5268
+ console.log(chalk.cyan('🚀 Optimization Suggestions:'));
5269
+ perfReport.optimizations.forEach(opt => {
5270
+ console.log(chalk.white(` • ${opt}`));
5271
+ });
5272
+ console.log('');
5273
+ }
5274
+
5275
+ // Show memory usage patterns
5276
+ if (perfReport.memoryPatterns.length > 0) {
5277
+ console.log(chalk.blue('🧠 Memory Usage Patterns:'));
5278
+ perfReport.memoryPatterns.forEach(pattern => {
5279
+ console.log(chalk.white(` • ${pattern.type}: ${pattern.description}`));
5280
+ if (pattern.file) {
5281
+ console.log(chalk.gray(` File: ${pattern.file}:${pattern.line}`));
5282
+ }
5283
+ });
5284
+ console.log('');
5285
+ }
5286
+
5287
+ // Summary
5288
+ const totalIssues = perfReport.bottlenecks.length + perfReport.fileSizeIssues.length;
5289
+ if (totalIssues === 0) {
5290
+ console.log(chalk.green('✅ No major performance issues detected'));
5291
+ } else {
5292
+ console.log(chalk.yellow(`📊 Total Performance Issues: ${totalIssues}`));
5293
+ }
5294
+ }
5295
+
5296
+ performRealPerformanceAnalysis() {
5297
+ const fs = require('fs');
5298
+ const path = require('path');
5299
+
5300
+ const perfReport = {
5301
+ score: 100,
5302
+ bottlenecks: [],
5303
+ fileSizeIssues: [],
5304
+ dependencyImpact: {
5305
+ heavyDependencies: [],
5306
+ totalDependencies: 0
5307
+ },
5308
+ memoryPatterns: [],
5309
+ optimizations: []
5310
+ };
5311
+
5312
+ // Performance anti-patterns to detect
5313
+ const performancePatterns = [
5314
+ { pattern: /for\s*\(.*\.length.*\)/, type: 'Loop Optimization', description: 'Loop with .length in condition - cache length outside loop' },
5315
+ { pattern: /document\.getElementById.*\(/, type: 'DOM Query', description: 'Repeated DOM queries - consider caching elements' },
5316
+ { pattern: /\$\(.*\)\..*\$\(.*\)/, type: 'jQuery Chaining', description: 'Multiple jQuery selectors - consider chaining or caching' },
5317
+ { pattern: /setInterval\s*\(.*1\)/, type: 'High Frequency Timer', description: 'Very high frequency interval detected' },
5318
+ { pattern: /JSON\.parse\s*\(.*JSON\.stringify/, type: 'Deep Clone', description: 'Inefficient deep cloning - consider structured clone or lodash' },
5319
+ { pattern: /new\s+Array\s*\(/, type: 'Array Constructor', description: 'Use array literal [] instead of new Array()' },
5320
+ { pattern: /\.innerHTML\s*\+=/, type: 'DOM Manipulation', description: 'Inefficient DOM updates - consider batch operations' },
5321
+ { pattern: /require\s*\(.*\).*require\s*\(.*\)/, type: 'Module Loading', description: 'Multiple requires in same scope - consider combining imports' }
5322
+ ];
5323
+
5324
+ // Memory leak patterns
5325
+ const memoryPatterns = [
5326
+ { pattern: /addEventListener.*(?!removeEventListener)/, type: 'Event Listener Leak', description: 'Event listener without cleanup' },
5327
+ { pattern: /setInterval.*(?!clearInterval)/, type: 'Timer Leak', description: 'setInterval without clearInterval' },
5328
+ { pattern: /setTimeout.*(?!clearTimeout)/, type: 'Timer Leak', description: 'setTimeout pattern that might not clear' },
5329
+ { pattern: /closure.*function.*closure/, type: 'Closure Chain', description: 'Potential closure memory retention' }
5330
+ ];
5331
+
5332
+ // Scan files for performance issues
5333
+ const scanDirectory = (dir, depth = 0) => {
5334
+ if (depth > 3) return;
5335
+
5336
+ try {
5337
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
5338
+
5339
+ for (const entry of entries) {
5340
+ const fullPath = path.join(dir, entry.name);
5341
+
5342
+ if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;
5343
+
5344
+ if (entry.isFile()) {
5345
+ // Check file size
5346
+ const stats = fs.statSync(fullPath);
5347
+ const sizeKB = Math.round(stats.size / 1024);
5348
+
5349
+ if (sizeKB > 500 && this.isCodeFile(entry.name)) {
5350
+ perfReport.fileSizeIssues.push({
5351
+ file: fullPath.replace(process.cwd(), '.'),
5352
+ sizeKB: sizeKB,
5353
+ description: sizeKB > 1000 ? 'Very large file - consider splitting' : 'Large file - review for optimization'
5354
+ });
5355
+ }
5356
+
5357
+ // Scan code files for performance patterns
5358
+ if (this.isCodeFile(entry.name)) {
5359
+ this.scanFileForPerformance(fullPath, performancePatterns, memoryPatterns, perfReport);
5360
+ }
5361
+ } else if (entry.isDirectory()) {
5362
+ scanDirectory(fullPath, depth + 1);
5363
+ }
5364
+ }
5365
+ } catch (error) {
5366
+ // Skip directories we can't read
5367
+ }
5368
+ };
5369
+
5370
+ scanDirectory(process.cwd());
5371
+
5372
+ // Analyze dependencies
5373
+ this.analyzeDependencyPerformance(perfReport);
5374
+
5375
+ // Calculate score based on findings
5376
+ const bottleneckCount = perfReport.bottlenecks.length;
5377
+ const fileSizeIssueCount = perfReport.fileSizeIssues.length;
5378
+ const memoryIssueCount = perfReport.memoryPatterns.length;
5379
+
5380
+ perfReport.score = Math.max(0, 100 - (bottleneckCount * 10) - (fileSizeIssueCount * 5) - (memoryIssueCount * 8));
5381
+
5382
+ // Generate optimization recommendations
5383
+ if (bottleneckCount > 0) {
5384
+ perfReport.optimizations.push('🔧 Address performance bottlenecks in critical code paths');
5385
+ }
5386
+ if (fileSizeIssueCount > 0) {
5387
+ perfReport.optimizations.push('📦 Consider code splitting for large files');
5388
+ }
5389
+ if (memoryIssueCount > 0) {
5390
+ perfReport.optimizations.push('🧠 Implement proper memory management and cleanup');
5391
+ }
5392
+
5393
+ perfReport.optimizations.push('⚡ Use Web Workers for CPU-intensive tasks');
5394
+ perfReport.optimizations.push('🗂️ Implement lazy loading for non-critical resources');
5395
+ perfReport.optimizations.push('📊 Add performance monitoring and profiling');
5396
+ perfReport.optimizations.push('🔄 Consider using virtual scrolling for large lists');
5397
+
5398
+ return perfReport;
5399
+ }
5400
+
5401
+ scanFileForPerformance(filePath, performancePatterns, memoryPatterns, report) {
5402
+ const fs = require('fs');
5403
+
5404
+ try {
5405
+ const content = fs.readFileSync(filePath, 'utf8');
5406
+ const lines = content.split('\n');
5407
+
5408
+ // Check for performance bottlenecks
5409
+ for (const patternInfo of performancePatterns) {
5410
+ for (let i = 0; i < lines.length; i++) {
5411
+ if (patternInfo.pattern.test(lines[i])) {
5412
+ report.bottlenecks.push({
5413
+ type: patternInfo.type,
5414
+ description: patternInfo.description,
5415
+ file: filePath.replace(process.cwd(), '.'),
5416
+ line: i + 1,
5417
+ code: lines[i].trim()
5418
+ });
5419
+ }
5420
+ }
5421
+ }
5422
+
5423
+ // Check for memory patterns
5424
+ for (const patternInfo of memoryPatterns) {
5425
+ for (let i = 0; i < lines.length; i++) {
5426
+ if (patternInfo.pattern.test(lines[i])) {
5427
+ report.memoryPatterns.push({
5428
+ type: patternInfo.type,
5429
+ description: patternInfo.description,
5430
+ file: filePath.replace(process.cwd(), '.'),
5431
+ line: i + 1,
5432
+ code: lines[i].trim()
5433
+ });
5434
+ }
5435
+ }
5436
+ }
5437
+ } catch (error) {
5438
+ // Skip files we can't read
5439
+ }
5440
+ }
5441
+
5442
+ analyzeDependencyPerformance(report) {
5443
+ const fs = require('fs');
5444
+ const path = require('path');
5445
+
5446
+ try {
5447
+ const packagePath = path.join(process.cwd(), 'package.json');
5448
+ if (!fs.existsSync(packagePath)) return;
5449
+
5450
+ const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
5451
+ const dependencies = { ...packageJson.dependencies, ...packageJson.devDependencies };
5452
+
5453
+ report.dependencyImpact.totalDependencies = Object.keys(dependencies).length;
5454
+
5455
+ // Known heavy dependencies that impact performance
5456
+ const heavyDependencies = ['lodash', 'moment', 'jquery', 'axios', 'react', 'vue', 'angular'];
5457
+
5458
+ for (const depName of Object.keys(dependencies)) {
5459
+ if (heavyDependencies.includes(depName)) {
5460
+ report.dependencyImpact.heavyDependencies.push(depName);
5461
+ }
5462
+ }
5463
+
5464
+ // Add recommendations based on dependency analysis
5465
+ if (dependencies['lodash']) {
5466
+ report.optimizations.push('Consider using native ES6 methods instead of lodash where possible');
5467
+ }
5468
+ if (dependencies['moment']) {
5469
+ report.optimizations.push('Consider using date-fns or dayjs instead of moment.js for smaller bundle size');
5470
+ }
5471
+
5472
+ } catch (error) {
5473
+ // Skip dependency analysis if package.json is not readable
5474
+ }
5475
+ }
5476
+
5477
+ cycleTheme() {
5478
+ if (!this.currentTheme) this.currentTheme = 'neon-purple';
5479
+
5480
+ const themes = ['neon-purple', 'matrix-green', 'ocean-blue', 'sunset-orange'];
5481
+ const currentIndex = themes.indexOf(this.currentTheme);
5482
+ const nextIndex = (currentIndex + 1) % themes.length;
5483
+ this.currentTheme = themes[nextIndex];
5484
+
5485
+ console.log(chalk.cyan(`🎨 Theme switched to: ${this.currentTheme}`));
5486
+ console.log(chalk.gray('New color scheme will apply to future messages'));
5487
+ console.log('');
5488
+ }
5489
+
5490
+ async cleanup() {
5491
+ this.logger.info('Starting cleanup process');
5492
+
5493
+ // Clean up streaming and loading animations
5494
+ this.stopProgressiveLoading();
5495
+ this.stopStreamingDisplay();
5496
+
5497
+ if (this.fileWatcher) {
5498
+ this.fileWatcher.close();
5499
+ this.fileWatcher = null;
5500
+ this.logger.debug('File watcher closed');
5501
+ }
5502
+
5503
+ // Cleanup voice manager
5504
+ if (this.voiceManager) {
5505
+ this.voiceManager.cleanup();
5506
+ this.logger.debug('Voice manager cleaned up');
5507
+ }
5508
+
5509
+ // Cleanup collaboration manager
5510
+ if (this.collaborationManager) {
5511
+ this.collaborationManager.cleanup();
5512
+ this.logger.debug('Collaboration manager cleaned up');
5513
+ }
5514
+
5515
+ // Cleanup plugins
5516
+ if (this.pluginManager) {
5517
+ await this.pluginManager.cleanup();
5518
+ this.logger.debug('Plugins cleaned up');
5519
+ }
5520
+
5521
+ // End session logging
5522
+ if (this.logger) {
5523
+ this.logger.endSession();
5524
+ this.logger.info('Session cleanup completed');
5525
+ }
5526
+ }
5527
+
5528
+ /**
5529
+ * Load user configuration from onboarding
5530
+ */
5531
+ loadUserConfig() {
5532
+ const path = require('path');
5533
+ const fs = require('fs');
5534
+ const configFile = path.join(require('os').homedir(), '.recoder-code', 'config.json');
5535
+
5536
+ try {
5537
+ if (fs.existsSync(configFile)) {
5538
+ return JSON.parse(fs.readFileSync(configFile, 'utf8'));
5539
+ }
5540
+ } catch (error) {
5541
+ // Return empty config if can't load
5542
+ }
5543
+
5544
+ return {};
5545
+ }
5546
+
3454
5547
  start() {
3455
5548
  this.rl.prompt();
3456
5549
  }