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.
package/cli/logger.js ADDED
@@ -0,0 +1,181 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const chalkModule = require('chalk');
6
+ const chalk = chalkModule.default || chalkModule;
7
+
8
+ /**
9
+ * Production Logger for Recoder Code
10
+ * Handles different log levels and environments
11
+ */
12
+ class Logger {
13
+ constructor(options = {}) {
14
+ this.logLevel = options.logLevel || process.env.LOG_LEVEL || 'info';
15
+ this.silent = options.silent || process.env.RECODER_SILENT === 'true';
16
+ this.logFile = options.logFile || path.join(process.cwd(), 'logs', 'recoder.log');
17
+ this.enableFileLogging = options.enableFileLogging !== false;
18
+
19
+ // Create logs directory
20
+ if (this.enableFileLogging) {
21
+ const logDir = path.dirname(this.logFile);
22
+ if (!fs.existsSync(logDir)) {
23
+ fs.mkdirSync(logDir, { recursive: true });
24
+ }
25
+ }
26
+
27
+ this.levels = {
28
+ error: 0,
29
+ warn: 1,
30
+ info: 2,
31
+ debug: 3
32
+ };
33
+ }
34
+
35
+ _shouldLog(level) {
36
+ return this.levels[level] <= this.levels[this.logLevel];
37
+ }
38
+
39
+ _formatMessage(level, message, ...args) {
40
+ const timestamp = new Date().toISOString();
41
+ const formattedArgs = args.map(arg =>
42
+ typeof arg === 'object' ? JSON.stringify(arg) : String(arg)
43
+ ).join(' ');
44
+
45
+ return {
46
+ console: this._colorizeMessage(level, message, formattedArgs),
47
+ file: `[${timestamp}] ${level.toUpperCase()}: ${message} ${formattedArgs}`.trim()
48
+ };
49
+ }
50
+
51
+ _colorizeMessage(level, message, args) {
52
+ const fullMessage = `${message} ${args}`.trim();
53
+
54
+ switch (level) {
55
+ case 'error':
56
+ return chalk.red(`❌ ${fullMessage}`);
57
+ case 'warn':
58
+ return chalk.yellow(`⚠️ ${fullMessage}`);
59
+ case 'info':
60
+ return chalk.blue(`ℹ️ ${fullMessage}`);
61
+ case 'debug':
62
+ return chalk.gray(`🔍 ${fullMessage}`);
63
+ default:
64
+ return fullMessage;
65
+ }
66
+ }
67
+
68
+ _writeToFile(message) {
69
+ if (this.enableFileLogging) {
70
+ try {
71
+ fs.appendFileSync(this.logFile, message + '\n');
72
+ } catch (error) {
73
+ // Fail silently for file logging errors
74
+ }
75
+ }
76
+ }
77
+
78
+ error(message, ...args) {
79
+ if (this._shouldLog('error')) {
80
+ const formatted = this._formatMessage('error', message, ...args);
81
+ if (!this.silent) {
82
+ console.error(formatted.console);
83
+ }
84
+ this._writeToFile(formatted.file);
85
+ }
86
+ }
87
+
88
+ warn(message, ...args) {
89
+ if (this._shouldLog('warn')) {
90
+ const formatted = this._formatMessage('warn', message, ...args);
91
+ if (!this.silent) {
92
+ console.warn(formatted.console);
93
+ }
94
+ this._writeToFile(formatted.file);
95
+ }
96
+ }
97
+
98
+ info(message, ...args) {
99
+ if (this._shouldLog('info')) {
100
+ const formatted = this._formatMessage('info', message, ...args);
101
+ if (!this.silent) {
102
+ console.log(formatted.console);
103
+ }
104
+ this._writeToFile(formatted.file);
105
+ }
106
+ }
107
+
108
+ debug(message, ...args) {
109
+ if (this._shouldLog('debug')) {
110
+ const formatted = this._formatMessage('debug', message, ...args);
111
+ if (!this.silent) {
112
+ console.log(formatted.console);
113
+ }
114
+ this._writeToFile(formatted.file);
115
+ }
116
+ }
117
+
118
+ // Production-safe console.log replacement
119
+ log(message, ...args) {
120
+ // In production, treat as info level
121
+ if (process.env.NODE_ENV === 'production') {
122
+ this.info(message, ...args);
123
+ } else {
124
+ // In development, allow direct logging
125
+ console.log(message, ...args);
126
+ }
127
+ }
128
+
129
+ // Success messages for user feedback
130
+ success(message, ...args) {
131
+ const formatted = `✅ ${message} ${args.join(' ')}`.trim();
132
+ if (!this.silent) {
133
+ console.log(chalk.green(formatted));
134
+ }
135
+ this._writeToFile(`[${new Date().toISOString()}] SUCCESS: ${message} ${args.join(' ')}`.trim());
136
+ }
137
+
138
+ // Progress indicators
139
+ progress(message, ...args) {
140
+ const formatted = `⏳ ${message} ${args.join(' ')}`.trim();
141
+ if (!this.silent) {
142
+ console.log(chalk.cyan(formatted));
143
+ }
144
+ }
145
+
146
+ // User prompts and interactive messages
147
+ user(message, ...args) {
148
+ const formatted = `${message} ${args.join(' ')}`.trim();
149
+ if (!this.silent) {
150
+ console.log(formatted);
151
+ }
152
+ }
153
+ }
154
+
155
+ // Global logger instance
156
+ const logger = new Logger();
157
+
158
+ // Replace console methods in production
159
+ if (process.env.NODE_ENV === 'production') {
160
+ // Store original methods
161
+ const originalConsole = {
162
+ log: console.log,
163
+ error: console.error,
164
+ warn: console.warn,
165
+ info: console.info
166
+ };
167
+
168
+ // Override console methods
169
+ console.log = (...args) => logger.log(...args);
170
+ console.error = (...args) => logger.error(...args);
171
+ console.warn = (...args) => logger.warn(...args);
172
+ console.info = (...args) => logger.info(...args);
173
+
174
+ // Provide way to restore original console
175
+ console._restore = () => {
176
+ Object.assign(console, originalConsole);
177
+ };
178
+ }
179
+
180
+ module.exports = Logger;
181
+ module.exports.logger = logger;
package/cli/mcp-client.js CHANGED
@@ -2,7 +2,9 @@
2
2
 
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
- const chalk = require('chalk');
5
+ // Handle chalk properly
6
+ const chalkModule = require('chalk');
7
+ const chalk = chalkModule.default || chalkModule;
6
8
  const EventEmitter = require('events');
7
9
  const WebSocket = require('ws');
8
10
 
@@ -47,6 +49,13 @@ class MCPClient extends EventEmitter {
47
49
  /**
48
50
  * Initialize MCP client
49
51
  */
52
+ async initialize() {
53
+ return this.initializeMCP();
54
+ }
55
+
56
+ /**
57
+ * Initialize MCP client implementation
58
+ */
50
59
  async initializeMCP() {
51
60
  console.log(chalk.cyan('🔌 Initializing MCP Client...'));
52
61
 
@@ -213,24 +222,38 @@ class MCPClient extends EventEmitter {
213
222
  });
214
223
 
215
224
  ws.on('error', (error) => {
216
- console.log(chalk.red(`❌ Connection error for ${serverName}: ${error.message}`));
225
+ // Gracefully handle connection errors - these are expected for external services
217
226
  this.connectionStates.set(serverName, 'error');
218
- reject(error);
227
+
228
+ // Only log in debug mode
229
+ if (process.env.RECODER_DEBUG === 'true') {
230
+ console.log(chalk.gray(`🔌 MCP service ${serverName} unavailable: ${error.message}`));
231
+ }
232
+
233
+ // Resolve with null instead of rejecting to allow graceful degradation
234
+ resolve(null);
219
235
  });
220
236
 
221
237
  ws.on('close', () => {
222
- console.log(chalk.yellow(`⚠️ Connection closed for ${serverName}`));
238
+ // Gracefully handle connection close
223
239
  this.connectionStates.set(serverName, 'disconnected');
224
240
  this.connections.delete(serverName);
225
241
 
226
- // Attempt reconnection
227
- setTimeout(() => {
228
- this.reconnectToServer(serverName);
229
- }, 5000);
242
+ // Only log in debug mode
243
+ if (process.env.RECODER_DEBUG === 'true') {
244
+ console.log(chalk.gray(`🔌 MCP connection to ${serverName} closed`));
245
+ }
246
+
247
+ // Don't attempt automatic reconnection to avoid spam
230
248
  });
231
249
 
232
250
  } catch (error) {
233
- reject(error);
251
+ // Gracefully handle any connection setup errors
252
+ this.connectionStates.set(serverName, 'error');
253
+ if (process.env.RECODER_DEBUG === 'true') {
254
+ console.log(chalk.gray(`🔌 MCP setup failed for ${serverName}: ${error.message}`));
255
+ }
256
+ resolve(null);
234
257
  }
235
258
  });
236
259
  }
@@ -7,7 +7,9 @@
7
7
 
8
8
  const fs = require('fs').promises;
9
9
  const path = require('path');
10
- const chalk = require('chalk');
10
+ // Handle chalk properly
11
+ const chalkModule = require('chalk');
12
+ const chalk = chalkModule.default || chalkModule;;
11
13
  const crypto = require('crypto');
12
14
  const EventEmitter = require('events');
13
15
 
@@ -2,7 +2,9 @@
2
2
 
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
- const chalk = require('chalk');
5
+ // Handle chalk properly
6
+ const chalkModule = require('chalk');
7
+ const chalk = chalkModule.default || chalkModule;
6
8
  const inquirer = require('inquirer');
7
9
 
8
10
  /**
@@ -2,7 +2,9 @@
2
2
 
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
- const chalk = require('chalk');
5
+ // Handle chalk properly
6
+ const chalkModule = require('chalk');
7
+ const chalk = chalkModule.default || chalkModule;
6
8
 
7
9
  /**
8
10
  * Natural Language Processor
@@ -3,7 +3,9 @@
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
  const crypto = require('crypto');
6
- const chalk = require('chalk');
6
+ // Handle chalk properly
7
+ const chalkModule = require('chalk');
8
+ const chalk = chalkModule.default || chalkModule;
7
9
 
8
10
  class ProjectManager {
9
11
  constructor() {
@@ -2,7 +2,9 @@
2
2
 
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
- const chalk = require('chalk');
5
+ // Handle chalk properly
6
+ const chalkModule = require('chalk');
7
+ const chalk = chalkModule.default || chalkModule;
6
8
 
7
9
  /**
8
10
  * User & Repository Rules Engine
@@ -411,9 +413,12 @@ class RulesEngine {
411
413
  getProjectLanguages() {
412
414
  // Try to get from project context
413
415
  if (this.projectManager) {
414
- const context = this.projectManager.getProjectContext();
415
- if (context && context.language) {
416
- return [context.language];
416
+ // Fallback for missing getProjectContext method
417
+ if (this.projectManager && typeof this.projectManager.getProjectContext === 'function') {
418
+ const context = this.projectManager.getProjectContext();
419
+ if (context && context.language) {
420
+ return [context.language];
421
+ }
417
422
  }
418
423
  }
419
424
 
package/cli/run.js CHANGED
@@ -28,6 +28,11 @@ const ScriptsManager = require('./scripts-manager.js');
28
28
  const GitHubManager = require('./github-manager.js');
29
29
  const WorkspaceManager = require('./workspace-manager.js');
30
30
 
31
+ // Initialize unified error handling
32
+ const UnifiedErrorHandler = require('./unified-error-handler.js');
33
+ const errorHandler = UnifiedErrorHandler.instance;
34
+ errorHandler.initialize();
35
+
31
36
  const execAsync = promisify(exec);
32
37
 
33
38
  // Create logs directory if it doesn't exist
@@ -92,22 +97,72 @@ class RecoderCode {
92
97
  'X-Title': config.siteName,
93
98
  },
94
99
  });
100
+
101
+ // Register API error handler with unified error system
102
+ errorHandler.registerHandler('openrouter', (error) => {
103
+ this.handleOpenRouterError(error);
104
+ });
105
+
106
+ // Initialize resource cleanup
107
+ this.systemsInitialized = false;
108
+ this.setupResourceCleanup();
109
+ }
110
+
111
+ /**
112
+ * Setup resource cleanup handlers
113
+ */
114
+ setupResourceCleanup() {
115
+ const cleanup = () => {
116
+ try {
117
+ // Stop any running services
118
+ if (this.mcpClient && typeof this.mcpClient.cleanup === 'function') {
119
+ this.mcpClient.cleanup();
120
+ }
121
+
122
+ // Clean up file watchers via manager
123
+ const FileWatcherManager = require('./file-watcher-manager.js');
124
+ FileWatcherManager.instance.cleanup();
125
+
126
+ // Clean up context engine
127
+ if (this.contextEngine && typeof this.contextEngine.cleanup === 'function') {
128
+ this.contextEngine.cleanup();
129
+ }
130
+ } catch (error) {
131
+ // Ignore cleanup errors
132
+ }
133
+ };
134
+
135
+ process.once('exit', cleanup);
136
+ process.once('SIGINT', cleanup);
137
+ process.once('SIGTERM', cleanup);
95
138
  }
96
139
 
97
140
  /**
98
141
  * Initialize advanced systems with full integration
99
142
  */
100
143
  async initializeSystems() {
144
+ // Prevent duplicate initialization
145
+ if (this.systemsInitialized) {
146
+ return;
147
+ }
148
+
101
149
  try {
102
- console.log(chalk.gray('🚀 Initializing enhanced AI CLI systems...'));
150
+ // Only show technical messages in debug or non-user mode
151
+ if (process.env.RECODER_DEBUG === 'true' || process.env.RECODER_USER_MODE !== 'true') {
152
+ console.log(chalk.gray('🚀 Initializing enhanced AI CLI systems...'));
153
+ }
103
154
 
104
155
  // Initialize context engine for codebase understanding
105
- this.contextEngine = new ContextEngine();
106
- await this.contextEngine.initialize();
156
+ if (!this.contextEngine) {
157
+ this.contextEngine = new ContextEngine();
158
+ await this.contextEngine.initialize();
159
+ }
107
160
 
108
161
  // Initialize MCP client for model communication
109
- this.mcpClient = new MCPClient();
110
- await this.mcpClient.initialize();
162
+ if (!this.mcpClient) {
163
+ this.mcpClient = new MCPClient();
164
+ await this.mcpClient.initialize();
165
+ }
111
166
 
112
167
  // Initialize model manager with intelligent selection
113
168
  this.modelManager = new ModelManager(this.mcpClient, this.contextEngine);
@@ -168,6 +223,9 @@ class RecoderCode {
168
223
  });
169
224
  }
170
225
 
226
+ // Mark systems as initialized
227
+ this.systemsInitialized = true;
228
+
171
229
  } catch (error) {
172
230
  console.log(chalk.yellow(`⚠️ System integration setup failed: ${error.message}`));
173
231
  }
@@ -316,8 +374,10 @@ class RecoderCode {
316
374
  }
317
375
  } catch (error) {
318
376
  this.logRequest(payload, null, error);
319
- this.handleOpenRouterError(error);
320
- throw error;
377
+
378
+ // Let unified error handler manage the error instead of re-throwing
379
+ // Return null for graceful degradation
380
+ return null;
321
381
  }
322
382
  }
323
383
 
@@ -748,8 +808,8 @@ Project context: ${this.getProjectContextString()}${this.getRulesContextString()
748
808
 
749
809
  // Add safety checks for response structure
750
810
  if (!response || !response.choices || !response.choices[0]) {
751
- console.error('Invalid response structure from OpenRouter');
752
- return 'Error: Invalid response received from API';
811
+ // API error already handled by unified error handler, provide graceful fallback
812
+ return 'Unable to process request - API service unavailable. Please check your API key and credits.';
753
813
  }
754
814
 
755
815
  while (response.choices[0].message && response.choices[0].message.tool_calls) {
@@ -771,8 +831,8 @@ Project context: ${this.getProjectContextString()}${this.getRulesContextString()
771
831
 
772
832
  // Add safety check again
773
833
  if (!response || !response.choices || !response.choices[0]) {
774
- console.error('Invalid response structure from OpenRouter in tool loop');
775
- return 'Error: Invalid response received from API during tool execution';
834
+ // API error already handled by unified error handler, provide graceful fallback
835
+ return 'Unable to complete tool execution - API service unavailable. Please check your API key and credits.';
776
836
  }
777
837
  }
778
838
 
@@ -1193,7 +1253,19 @@ async function main() {
1193
1253
  }
1194
1254
 
1195
1255
  if (args.length === 0) {
1196
- // Launch interactive mode when no arguments provided
1256
+ // Launch interactive mode with proper user onboarding
1257
+ const UserOnboarding = require('./user-onboarding.js');
1258
+ const onboarding = new UserOnboarding();
1259
+
1260
+ if (onboarding.needsOnboarding()) {
1261
+ // First time user - run full onboarding
1262
+ await onboarding.runOnboarding();
1263
+ } else {
1264
+ // Returning user - quick start
1265
+ await onboarding.quickStart();
1266
+ }
1267
+
1268
+ // Now launch interactive mode
1197
1269
  const InteractiveChat = require('./interactive.js');
1198
1270
  const chat = new InteractiveChat();
1199
1271
  chat.start();
@@ -2267,7 +2339,12 @@ async function main() {
2267
2339
  }
2268
2340
 
2269
2341
  if (!prompt) {
2270
- console.error('Error: No prompt provided');
2342
+ console.error(chalk.red('Error: No prompt provided'));
2343
+ console.log(chalk.yellow('💡 Usage examples:'));
2344
+ console.log(chalk.white(' recoder-code "Hello, how can you help me?"'));
2345
+ console.log(chalk.white(' recoder-code --help'));
2346
+ console.log(chalk.white(' recoder-code --setup'));
2347
+ console.log(chalk.gray('\nFor interactive mode, run: recoder-code'));
2271
2348
  process.exit(1);
2272
2349
  }
2273
2350
 
@@ -1,7 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  const readline = require('readline');
4
- const chalk = require('chalk');
4
+ // Handle chalk properly
5
+ const chalkModule = require('chalk');
6
+ const chalk = chalkModule.default || chalkModule;;
5
7
  const fs = require('fs');
6
8
  const path = require('path');
7
9