stigmergy 1.1.6 → 1.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/LICENSE +18 -18
  2. package/README.md +28 -223
  3. package/STIGMERGY.md +61 -61
  4. package/docs/PROJECT_CONSTITUTION.md +433 -433
  5. package/docs/PROJECT_STRUCTURE_CURRENT.md +80 -80
  6. package/examples/calculator-example.js +72 -72
  7. package/examples/cline_usage_examples.md +364 -364
  8. package/examples/encryption-example.js +67 -67
  9. package/examples/json-parser-example.js +120 -120
  10. package/examples/json-validation-example.js +64 -64
  11. package/examples/rest-client-example.js +52 -52
  12. package/examples/rest_client_example.js +54 -54
  13. package/package.json +15 -7
  14. package/scripts/build.js +74 -74
  15. package/scripts/post-deployment-config.js +296 -296
  16. package/scripts/preinstall-check.js +173 -111
  17. package/scripts/publish.js +58 -268
  18. package/scripts/run-layered-tests.js +247 -0
  19. package/scripts/safe-install.js +139 -139
  20. package/scripts/simple-publish.js +57 -59
  21. package/src/adapters/claude/install_claude_integration.js +292 -0
  22. package/src/adapters/codebuddy/install_codebuddy_integration.js +349 -0
  23. package/src/adapters/codex/install_codex_integration.js +395 -0
  24. package/src/adapters/copilot/install_copilot_integration.js +716 -0
  25. package/src/adapters/gemini/install_gemini_integration.js +304 -0
  26. package/src/adapters/iflow/install_iflow_integration.js +304 -0
  27. package/src/adapters/qoder/install_qoder_integration.js +1090 -0
  28. package/src/adapters/qwen/install_qwen_integration.js +285 -0
  29. package/src/auth.js +173 -173
  30. package/src/auth_command.js +208 -208
  31. package/src/calculator.js +313 -313
  32. package/src/cli/router.js +792 -67
  33. package/src/core/cache_cleaner.js +767 -0
  34. package/src/core/cli_help_analyzer.js +680 -674
  35. package/src/core/cli_parameter_handler.js +132 -127
  36. package/src/core/cli_tools.js +89 -89
  37. package/src/core/coordination/index.js +16 -16
  38. package/src/core/coordination/nodejs/AdapterManager.js +102 -89
  39. package/src/core/coordination/nodejs/CLCommunication.js +132 -124
  40. package/src/core/coordination/nodejs/CLIIntegrationManager.js +272 -236
  41. package/src/core/coordination/nodejs/HealthChecker.js +76 -77
  42. package/src/core/coordination/nodejs/HookDeploymentManager.js +263 -190
  43. package/src/core/coordination/nodejs/StatisticsCollector.js +71 -71
  44. package/src/core/coordination/nodejs/index.js +90 -72
  45. package/src/core/coordination/nodejs/utils/Logger.js +29 -29
  46. package/src/core/enhanced_installer.js +479 -0
  47. package/src/core/enhanced_uninstaller.js +638 -0
  48. package/src/core/error_handler.js +406 -406
  49. package/src/core/installer.js +816 -294
  50. package/src/core/memory_manager.js +83 -83
  51. package/src/core/rest_client.js +160 -160
  52. package/src/core/smart_router.js +249 -146
  53. package/src/core/upgrade_manager.js +420 -0
  54. package/src/data_encryption.js +143 -143
  55. package/src/data_structures.js +440 -440
  56. package/src/deploy.js +55 -55
  57. package/src/index.js +30 -30
  58. package/src/test/cli-availability-checker.js +194 -0
  59. package/src/test/test-environment.js +289 -0
  60. package/src/utils/helpers.js +35 -35
  61. package/src/utils.js +921 -915
  62. package/src/weatherProcessor.js +228 -228
  63. package/test/calculator.test.js +0 -215
  64. package/test/collision-test.js +0 -26
  65. package/test/comprehensive-execution-test.js +0 -428
  66. package/test/conflict-prevention-test.js +0 -95
  67. package/test/cross-cli-detection-test.js +0 -33
  68. package/test/csv-processing-test.js +0 -36
  69. package/test/deploy-hooks-test.js +0 -250
  70. package/test/e2e/claude-cli-test.js +0 -128
  71. package/test/e2e/collaboration-test.js +0 -75
  72. package/test/e2e/comprehensive-test.js +0 -431
  73. package/test/e2e/error-handling-test.js +0 -90
  74. package/test/e2e/individual-tool-test.js +0 -143
  75. package/test/e2e/other-cli-test.js +0 -130
  76. package/test/e2e/qoder-cli-test.js +0 -128
  77. package/test/e2e/run-e2e-tests.js +0 -73
  78. package/test/e2e/test-data.js +0 -88
  79. package/test/e2e/test-utils.js +0 -222
  80. package/test/encryption-simple-test.js +0 -110
  81. package/test/encryption.test.js +0 -129
  82. package/test/enhanced-main-alignment.test.js +0 -298
  83. package/test/error-handling-test.js +0 -341
  84. package/test/fibonacci.test.js +0 -178
  85. package/test/final-deploy-test.js +0 -221
  86. package/test/final-install-test.js +0 -226
  87. package/test/hash-table-demo.js +0 -33
  88. package/test/hash-table-test.js +0 -26
  89. package/test/hash_table_test.js +0 -114
  90. package/test/hook-system-integration-test.js +0 -307
  91. package/test/iflow-integration-test.js +0 -292
  92. package/test/improved-install-test.js +0 -362
  93. package/test/install-command-test.js +0 -370
  94. package/test/json-parser-test.js +0 -161
  95. package/test/json-validation-test.js +0 -164
  96. package/test/natural-language-skills-test.js +0 -320
  97. package/test/nl-integration-test.js +0 -179
  98. package/test/parameter-parsing-test.js +0 -143
  99. package/test/plugin-deployment-test.js +0 -316
  100. package/test/postinstall-test.js +0 -269
  101. package/test/python-plugins-test.js +0 -259
  102. package/test/real-test.js +0 -435
  103. package/test/remaining-adapters-test.js +0 -256
  104. package/test/rest-client-test.js +0 -56
  105. package/test/rest_client.test.js +0 -85
  106. package/test/simple-iflow-hook-test.js +0 -137
  107. package/test/system-compatibility-test.js +0 -467
  108. package/test/tdd-deploy-fix-test.js +0 -324
  109. package/test/tdd-fixes-test.js +0 -211
  110. package/test/third-party-skills-test.js +0 -321
  111. package/test/tool-selection-integration-test.js +0 -158
  112. package/test/unit/calculator-full.test.js +0 -191
  113. package/test/unit/calculator-simple.test.js +0 -96
  114. package/test/unit/calculator.test.js +0 -97
  115. package/test/unit/cli-scanner.test.js +0 -291
  116. package/test/unit/cli_parameter_handler.test.js +0 -116
  117. package/test/unit/cross-cli-executor.test.js +0 -399
  118. package/test/weather-processor.test.js +0 -104
@@ -1,406 +1,406 @@
1
- /**
2
- * Centralized Error Handling Utility for Stigmergy CLI
3
- * Provides consistent error handling, logging, and reporting across the application
4
- */
5
-
6
- const fs = require('fs/promises');
7
- const path = require('path');
8
- const os = require('os');
9
- const chalk = require('chalk');
10
-
11
- // Error types enumeration
12
- const ERROR_TYPES = {
13
- VALIDATION: 'VALIDATION_ERROR',
14
- NETWORK: 'NETWORK_ERROR',
15
- FILE_SYSTEM: 'FILE_SYSTEM_ERROR',
16
- CLI_TOOL: 'CLI_TOOL_ERROR',
17
- CONFIGURATION: 'CONFIGURATION_ERROR',
18
- PERMISSION: 'PERMISSION_ERROR',
19
- UNKNOWN: 'UNKNOWN_ERROR',
20
- };
21
-
22
- // Log levels enumeration
23
- const LOG_LEVELS = {
24
- ERROR: 'ERROR',
25
- WARN: 'WARN',
26
- INFO: 'INFO',
27
- DEBUG: 'DEBUG',
28
- };
29
-
30
- class StigmergyError extends Error {
31
- constructor(
32
- message,
33
- type = ERROR_TYPES.UNKNOWN,
34
- code = null,
35
- details = null,
36
- ) {
37
- super(message);
38
- this.name = 'StigmergyError';
39
- this.type = type;
40
- this.code = code;
41
- this.details = details;
42
- this.timestamp = new Date().toISOString();
43
-
44
- // Ensure proper stack trace
45
- if (Error.captureStackTrace) {
46
- Error.captureStackTrace(this, StigmergyError);
47
- }
48
- }
49
- }
50
-
51
- class ErrorHandler {
52
- constructor() {
53
- this.logFile = path.join(os.homedir(), '.stigmergy', 'error.log');
54
- this.maxLogSize = 10 * 1024 * 1024; // 10MB
55
- }
56
-
57
- /**
58
- * Create a standardized Stigmergy error
59
- * @param {string} message - Error message
60
- * @param {string} type - Error type from ERROR_TYPES
61
- * @param {string|null} code - Error code
62
- * @param {Object|null} details - Additional error details
63
- * @returns {StigmergyError}
64
- */
65
- createError(
66
- message,
67
- type = ERROR_TYPES.UNKNOWN,
68
- code = null,
69
- details = null,
70
- ) {
71
- return new StigmergyError(message, type, code, details);
72
- }
73
-
74
- /**
75
- * Log an error to console and file
76
- * @param {Error|StigmergyError} error - The error to log
77
- * @param {string} level - Log level (ERROR, WARN, INFO, DEBUG)
78
- * @param {string|null} context - Context where error occurred
79
- */
80
- async logError(error, level = LOG_LEVELS.ERROR, context = null) {
81
- try {
82
- const timestamp = new Date().toISOString();
83
- const errorType = error.type || ERROR_TYPES.UNKNOWN;
84
- const errorCode = error.code || 'NO_CODE';
85
- const errorMessage = error.message || 'Unknown error';
86
-
87
- // Format log entry
88
- const logEntry = {
89
- timestamp,
90
- level,
91
- type: errorType,
92
- code: errorCode,
93
- message: errorMessage,
94
- context,
95
- stack: error.stack || 'No stack trace available',
96
- };
97
-
98
- // Console output with enhanced formatting
99
- const consoleMessage = `[${timestamp}] [${level}] [${errorType}] ${errorMessage}`;
100
- const contextMessage = context ? `[CONTEXT] ${context}` : '';
101
-
102
- switch (level) {
103
- case LOG_LEVELS.ERROR:
104
- console.error(chalk.red.bold(consoleMessage));
105
- if (contextMessage) console.error(chalk.yellow(contextMessage));
106
- if (error.stack) console.error(chalk.gray(error.stack));
107
- break;
108
- case LOG_LEVELS.WARN:
109
- console.warn(chalk.yellow.bold(consoleMessage));
110
- if (contextMessage) console.warn(chalk.yellow(contextMessage));
111
- break;
112
- case LOG_LEVELS.INFO:
113
- console.info(chalk.blue(consoleMessage));
114
- if (contextMessage) console.info(chalk.gray(contextMessage));
115
- break;
116
- case LOG_LEVELS.DEBUG:
117
- console.debug(chalk.gray(consoleMessage));
118
- if (contextMessage) console.debug(chalk.gray(contextMessage));
119
- if (error.stack) console.debug(chalk.gray(error.stack));
120
- break;
121
- default:
122
- console.log(chalk.white(consoleMessage));
123
- if (contextMessage) console.log(chalk.gray(contextMessage));
124
- }
125
-
126
- // File logging
127
- await this.writeToFile(logEntry);
128
- } catch (logError) {
129
- console.error('[ERROR_HANDLER] Failed to log error:', logError.message);
130
- }
131
- }
132
-
133
- /**
134
- * Write error to log file
135
- * @param {Object} logEntry - Formatted log entry
136
- */
137
- async writeToFile(logEntry) {
138
- try {
139
- // Ensure log directory exists
140
- const logDir = path.dirname(this.logFile);
141
- await fs.mkdir(logDir, { recursive: true });
142
-
143
- // Check file size and rotate if necessary
144
- await this.rotateLogFile();
145
-
146
- // Append to log file
147
- const logLine = JSON.stringify(logEntry) + '\n';
148
- await fs.appendFile(this.logFile, logLine, { encoding: 'utf8' });
149
- } catch (error) {
150
- // Silent fail to prevent infinite loop
151
- }
152
- }
153
-
154
- /**
155
- * Rotate log file if it exceeds max size
156
- */
157
- async rotateLogFile() {
158
- try {
159
- const stats = await fs.stat(this.logFile);
160
- if (stats.size > this.maxLogSize) {
161
- const backupFile = this.logFile + '.old';
162
- await fs.rename(this.logFile, backupFile);
163
- }
164
- } catch (error) {
165
- // File doesn't exist or other issue, continue
166
- }
167
- }
168
-
169
- /**
170
- * Handle CLI tool execution errors
171
- * @param {string} toolName - Name of the CLI tool
172
- * @param {Error} error - Error that occurred
173
- * @param {string|null} command - Command that failed
174
- */
175
- async handleCLIError(toolName, error, command = null) {
176
- const cliError = new StigmergyError(
177
- `Failed to execute ${toolName}${command ? ` command: ${command}` : ''}`,
178
- ERROR_TYPES.CLI_TOOL,
179
- null,
180
- {
181
- tool: toolName,
182
- command,
183
- originalError: error.message,
184
- stack: error.stack,
185
- },
186
- );
187
-
188
- await this.logError(cliError, LOG_LEVELS.ERROR, 'CLI_EXECUTION');
189
- return cliError;
190
- }
191
-
192
- /**
193
- * Handle file system errors
194
- * @param {string} operation - File operation that failed
195
- * @param {string} filePath - Path of file involved
196
- * @param {Error} error - Original error
197
- */
198
- async handleFileError(operation, filePath, error) {
199
- const fileError = new StigmergyError(
200
- `Failed to ${operation} file: ${filePath}`,
201
- ERROR_TYPES.FILE_SYSTEM,
202
- error.code,
203
- {
204
- operation,
205
- filePath,
206
- originalError: error.message,
207
- stack: error.stack,
208
- },
209
- );
210
-
211
- await this.logError(fileError, LOG_LEVELS.ERROR, 'FILE_SYSTEM');
212
- return fileError;
213
- }
214
-
215
- /**
216
- * Handle network errors
217
- * @param {string} operation - Network operation that failed
218
- * @param {string} url - URL involved
219
- * @param {Error} error - Original error
220
- */
221
- async handleNetworkError(operation, url, error) {
222
- const networkError = new StigmergyError(
223
- `Network error during ${operation}: ${url}`,
224
- ERROR_TYPES.NETWORK,
225
- error.code,
226
- {
227
- operation,
228
- url,
229
- originalError: error.message,
230
- stack: error.stack,
231
- },
232
- );
233
-
234
- await this.logError(networkError, LOG_LEVELS.ERROR, 'NETWORK');
235
- return networkError;
236
- }
237
-
238
- /**
239
- * Handle validation errors
240
- * @param {string} field - Field that failed validation
241
- * @param {string} value - Value that failed validation
242
- * @param {string} reason - Reason for validation failure
243
- */
244
- createValidationError(field, value, reason) {
245
- const validationError = new StigmergyError(
246
- `Validation failed for ${field}: ${reason}`,
247
- ERROR_TYPES.VALIDATION,
248
- 'VALIDATION_FAILED',
249
- {
250
- field,
251
- value,
252
- reason,
253
- },
254
- );
255
-
256
- return validationError;
257
- }
258
-
259
- /**
260
- * Wrap an async function with error handling
261
- * @param {Function} fn - Async function to wrap
262
- * @param {string} context - Context for error logging
263
- * @returns {Function} Wrapped function
264
- */
265
- wrapAsync(fn, context) {
266
- return async (...args) => {
267
- try {
268
- return await fn(...args);
269
- } catch (error) {
270
- if (error instanceof StigmergyError) {
271
- await this.logError(error, LOG_LEVELS.ERROR, context);
272
- throw error;
273
- } else {
274
- const wrappedError = new StigmergyError(
275
- error.message || 'Unknown error occurred',
276
- ERROR_TYPES.UNKNOWN,
277
- null,
278
- { originalError: error.message, stack: error.stack },
279
- );
280
- await this.logError(wrappedError, LOG_LEVELS.ERROR, context);
281
- throw wrappedError;
282
- }
283
- }
284
- };
285
- }
286
-
287
- /**
288
- * Get error statistics from log file
289
- */
290
- async getErrorStats() {
291
- try {
292
- const data = await fs.readFile(this.logFile, 'utf8');
293
- const lines = data.split('\n').filter((line) => line.trim());
294
-
295
- const stats = {
296
- totalErrors: lines.length,
297
- byType: {},
298
- byLevel: {},
299
- };
300
-
301
- for (const line of lines) {
302
- try {
303
- const entry = JSON.parse(line);
304
- stats.byType[entry.type] = (stats.byType[entry.type] || 0) + 1;
305
- stats.byLevel[entry.level] = (stats.byLevel[entry.level] || 0) + 1;
306
- } catch (parseError) {
307
- // Skip malformed entries
308
- }
309
- }
310
-
311
- return stats;
312
- } catch (error) {
313
- return { totalErrors: 0, byType: {}, byLevel: {} };
314
- }
315
- }
316
-
317
- /**
318
- * Generate a formatted error report
319
- * @param {Object} stats - Error statistics from getErrorStats()
320
- * @returns {string} Formatted error report
321
- */
322
- generateErrorReport(stats) {
323
- let report = '\n=== Stigmergy CLI Error Report ===\n';
324
- report += `Total Errors: ${stats.totalErrors}\n\n`;
325
-
326
- report += 'Errors by Type:\n';
327
- for (const [type, count] of Object.entries(stats.byType)) {
328
- report += ` ${type}: ${count}\n`;
329
- }
330
-
331
- report += '\nErrors by Level:\n';
332
- for (const [level, count] of Object.entries(stats.byLevel)) {
333
- report += ` ${level}: ${count}\n`;
334
- }
335
-
336
- report += '==================================\n';
337
- return report;
338
- }
339
-
340
- /**
341
- * Print error report to console
342
- */
343
- async printErrorReport() {
344
- const stats = await this.getErrorStats();
345
- const report = this.generateErrorReport(stats);
346
- console.log(report);
347
- }
348
- }
349
-
350
- // Set up global error handlers within the error handler module
351
- function setupGlobalErrorHandlers() {
352
- // Only set up handlers if they haven't been set up already
353
- if (!process.listenerCount('unhandledRejection')) {
354
- process.on('unhandledRejection', async (reason, promise) => {
355
- console.error(
356
- '[FATAL] Global Unhandled Rejection at:',
357
- promise,
358
- 'reason:',
359
- reason,
360
- );
361
-
362
- // Log the error using our error handler
363
- const error =
364
- reason instanceof Error ? reason : new Error(String(reason));
365
- await errorHandler.logError(
366
- error,
367
- LOG_LEVELS.ERROR,
368
- 'global_unhandledRejection',
369
- );
370
-
371
- // Exit gracefully after a short delay to allow logging
372
- setTimeout(() => process.exit(1), 100);
373
- });
374
- }
375
-
376
- if (!process.listenerCount('uncaughtException')) {
377
- process.on('uncaughtException', async (error) => {
378
- console.error('[FATAL] Global Uncaught Exception:', error);
379
-
380
- // Log the error using our error handler
381
- await errorHandler.logError(
382
- error,
383
- LOG_LEVELS.ERROR,
384
- 'global_uncaughtException',
385
- );
386
-
387
- // Exit gracefully after a short delay to allow logging
388
- setTimeout(() => process.exit(1), 100);
389
- });
390
- }
391
- }
392
-
393
- // Set up global handlers by default
394
- setupGlobalErrorHandlers();
395
-
396
- // Export singleton instance
397
- const errorHandler = new ErrorHandler();
398
-
399
- module.exports = {
400
- ErrorHandler,
401
- errorHandler,
402
- StigmergyError,
403
- ERROR_TYPES,
404
- LOG_LEVELS,
405
- setupGlobalErrorHandlers,
406
- };
1
+ /**
2
+ * Centralized Error Handling Utility for Stigmergy CLI
3
+ * Provides consistent error handling, logging, and reporting across the application
4
+ */
5
+
6
+ const fs = require('fs/promises');
7
+ const path = require('path');
8
+ const os = require('os');
9
+ const chalk = require('chalk');
10
+
11
+ // Error types enumeration
12
+ const ERROR_TYPES = {
13
+ VALIDATION: 'VALIDATION_ERROR',
14
+ NETWORK: 'NETWORK_ERROR',
15
+ FILE_SYSTEM: 'FILE_SYSTEM_ERROR',
16
+ CLI_TOOL: 'CLI_TOOL_ERROR',
17
+ CONFIGURATION: 'CONFIGURATION_ERROR',
18
+ PERMISSION: 'PERMISSION_ERROR',
19
+ UNKNOWN: 'UNKNOWN_ERROR',
20
+ };
21
+
22
+ // Log levels enumeration
23
+ const LOG_LEVELS = {
24
+ ERROR: 'ERROR',
25
+ WARN: 'WARN',
26
+ INFO: 'INFO',
27
+ DEBUG: 'DEBUG',
28
+ };
29
+
30
+ class StigmergyError extends Error {
31
+ constructor(
32
+ message,
33
+ type = ERROR_TYPES.UNKNOWN,
34
+ code = null,
35
+ details = null,
36
+ ) {
37
+ super(message);
38
+ this.name = 'StigmergyError';
39
+ this.type = type;
40
+ this.code = code;
41
+ this.details = details;
42
+ this.timestamp = new Date().toISOString();
43
+
44
+ // Ensure proper stack trace
45
+ if (Error.captureStackTrace) {
46
+ Error.captureStackTrace(this, StigmergyError);
47
+ }
48
+ }
49
+ }
50
+
51
+ class ErrorHandler {
52
+ constructor() {
53
+ this.logFile = path.join(os.homedir(), '.stigmergy', 'error.log');
54
+ this.maxLogSize = 10 * 1024 * 1024; // 10MB
55
+ }
56
+
57
+ /**
58
+ * Create a standardized Stigmergy error
59
+ * @param {string} message - Error message
60
+ * @param {string} type - Error type from ERROR_TYPES
61
+ * @param {string|null} code - Error code
62
+ * @param {Object|null} details - Additional error details
63
+ * @returns {StigmergyError}
64
+ */
65
+ createError(
66
+ message,
67
+ type = ERROR_TYPES.UNKNOWN,
68
+ code = null,
69
+ details = null,
70
+ ) {
71
+ return new StigmergyError(message, type, code, details);
72
+ }
73
+
74
+ /**
75
+ * Log an error to console and file
76
+ * @param {Error|StigmergyError} error - The error to log
77
+ * @param {string} level - Log level (ERROR, WARN, INFO, DEBUG)
78
+ * @param {string|null} context - Context where error occurred
79
+ */
80
+ async logError(error, level = LOG_LEVELS.ERROR, context = null) {
81
+ try {
82
+ const timestamp = new Date().toISOString();
83
+ const errorType = error.type || ERROR_TYPES.UNKNOWN;
84
+ const errorCode = error.code || 'NO_CODE';
85
+ const errorMessage = error.message || 'Unknown error';
86
+
87
+ // Format log entry
88
+ const logEntry = {
89
+ timestamp,
90
+ level,
91
+ type: errorType,
92
+ code: errorCode,
93
+ message: errorMessage,
94
+ context,
95
+ stack: error.stack || 'No stack trace available',
96
+ };
97
+
98
+ // Console output with enhanced formatting
99
+ const consoleMessage = `[${timestamp}] [${level}] [${errorType}] ${errorMessage}`;
100
+ const contextMessage = context ? `[CONTEXT] ${context}` : '';
101
+
102
+ switch (level) {
103
+ case LOG_LEVELS.ERROR:
104
+ console.error(chalk.red.bold(consoleMessage));
105
+ if (contextMessage) console.error(chalk.yellow(contextMessage));
106
+ if (error.stack) console.error(chalk.gray(error.stack));
107
+ break;
108
+ case LOG_LEVELS.WARN:
109
+ console.warn(chalk.yellow.bold(consoleMessage));
110
+ if (contextMessage) console.warn(chalk.yellow(contextMessage));
111
+ break;
112
+ case LOG_LEVELS.INFO:
113
+ console.info(chalk.blue(consoleMessage));
114
+ if (contextMessage) console.info(chalk.gray(contextMessage));
115
+ break;
116
+ case LOG_LEVELS.DEBUG:
117
+ console.debug(chalk.gray(consoleMessage));
118
+ if (contextMessage) console.debug(chalk.gray(contextMessage));
119
+ if (error.stack) console.debug(chalk.gray(error.stack));
120
+ break;
121
+ default:
122
+ console.log(chalk.white(consoleMessage));
123
+ if (contextMessage) console.log(chalk.gray(contextMessage));
124
+ }
125
+
126
+ // File logging
127
+ await this.writeToFile(logEntry);
128
+ } catch (logError) {
129
+ console.error('[ERROR_HANDLER] Failed to log error:', logError.message);
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Write error to log file
135
+ * @param {Object} logEntry - Formatted log entry
136
+ */
137
+ async writeToFile(logEntry) {
138
+ try {
139
+ // Ensure log directory exists
140
+ const logDir = path.dirname(this.logFile);
141
+ await fs.mkdir(logDir, { recursive: true });
142
+
143
+ // Check file size and rotate if necessary
144
+ await this.rotateLogFile();
145
+
146
+ // Append to log file
147
+ const logLine = JSON.stringify(logEntry) + '\n';
148
+ await fs.appendFile(this.logFile, logLine, { encoding: 'utf8' });
149
+ } catch (error) {
150
+ // Silent fail to prevent infinite loop
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Rotate log file if it exceeds max size
156
+ */
157
+ async rotateLogFile() {
158
+ try {
159
+ const stats = await fs.stat(this.logFile);
160
+ if (stats.size > this.maxLogSize) {
161
+ const backupFile = this.logFile + '.old';
162
+ await fs.rename(this.logFile, backupFile);
163
+ }
164
+ } catch (error) {
165
+ // File doesn't exist or other issue, continue
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Handle CLI tool execution errors
171
+ * @param {string} toolName - Name of the CLI tool
172
+ * @param {Error} error - Error that occurred
173
+ * @param {string|null} command - Command that failed
174
+ */
175
+ async handleCLIError(toolName, error, command = null) {
176
+ const cliError = new StigmergyError(
177
+ `Failed to execute ${toolName}${command ? ` command: ${command}` : ''}`,
178
+ ERROR_TYPES.CLI_TOOL,
179
+ null,
180
+ {
181
+ tool: toolName,
182
+ command,
183
+ originalError: error.message,
184
+ stack: error.stack,
185
+ },
186
+ );
187
+
188
+ await this.logError(cliError, LOG_LEVELS.ERROR, 'CLI_EXECUTION');
189
+ return cliError;
190
+ }
191
+
192
+ /**
193
+ * Handle file system errors
194
+ * @param {string} operation - File operation that failed
195
+ * @param {string} filePath - Path of file involved
196
+ * @param {Error} error - Original error
197
+ */
198
+ async handleFileError(operation, filePath, error) {
199
+ const fileError = new StigmergyError(
200
+ `Failed to ${operation} file: ${filePath}`,
201
+ ERROR_TYPES.FILE_SYSTEM,
202
+ error.code,
203
+ {
204
+ operation,
205
+ filePath,
206
+ originalError: error.message,
207
+ stack: error.stack,
208
+ },
209
+ );
210
+
211
+ await this.logError(fileError, LOG_LEVELS.ERROR, 'FILE_SYSTEM');
212
+ return fileError;
213
+ }
214
+
215
+ /**
216
+ * Handle network errors
217
+ * @param {string} operation - Network operation that failed
218
+ * @param {string} url - URL involved
219
+ * @param {Error} error - Original error
220
+ */
221
+ async handleNetworkError(operation, url, error) {
222
+ const networkError = new StigmergyError(
223
+ `Network error during ${operation}: ${url}`,
224
+ ERROR_TYPES.NETWORK,
225
+ error.code,
226
+ {
227
+ operation,
228
+ url,
229
+ originalError: error.message,
230
+ stack: error.stack,
231
+ },
232
+ );
233
+
234
+ await this.logError(networkError, LOG_LEVELS.ERROR, 'NETWORK');
235
+ return networkError;
236
+ }
237
+
238
+ /**
239
+ * Handle validation errors
240
+ * @param {string} field - Field that failed validation
241
+ * @param {string} value - Value that failed validation
242
+ * @param {string} reason - Reason for validation failure
243
+ */
244
+ createValidationError(field, value, reason) {
245
+ const validationError = new StigmergyError(
246
+ `Validation failed for ${field}: ${reason}`,
247
+ ERROR_TYPES.VALIDATION,
248
+ 'VALIDATION_FAILED',
249
+ {
250
+ field,
251
+ value,
252
+ reason,
253
+ },
254
+ );
255
+
256
+ return validationError;
257
+ }
258
+
259
+ /**
260
+ * Wrap an async function with error handling
261
+ * @param {Function} fn - Async function to wrap
262
+ * @param {string} context - Context for error logging
263
+ * @returns {Function} Wrapped function
264
+ */
265
+ wrapAsync(fn, context) {
266
+ return async (...args) => {
267
+ try {
268
+ return await fn(...args);
269
+ } catch (error) {
270
+ if (error instanceof StigmergyError) {
271
+ await this.logError(error, LOG_LEVELS.ERROR, context);
272
+ throw error;
273
+ } else {
274
+ const wrappedError = new StigmergyError(
275
+ error.message || 'Unknown error occurred',
276
+ ERROR_TYPES.UNKNOWN,
277
+ null,
278
+ { originalError: error.message, stack: error.stack },
279
+ );
280
+ await this.logError(wrappedError, LOG_LEVELS.ERROR, context);
281
+ throw wrappedError;
282
+ }
283
+ }
284
+ };
285
+ }
286
+
287
+ /**
288
+ * Get error statistics from log file
289
+ */
290
+ async getErrorStats() {
291
+ try {
292
+ const data = await fs.readFile(this.logFile, 'utf8');
293
+ const lines = data.split('\n').filter((line) => line.trim());
294
+
295
+ const stats = {
296
+ totalErrors: lines.length,
297
+ byType: {},
298
+ byLevel: {},
299
+ };
300
+
301
+ for (const line of lines) {
302
+ try {
303
+ const entry = JSON.parse(line);
304
+ stats.byType[entry.type] = (stats.byType[entry.type] || 0) + 1;
305
+ stats.byLevel[entry.level] = (stats.byLevel[entry.level] || 0) + 1;
306
+ } catch (parseError) {
307
+ // Skip malformed entries
308
+ }
309
+ }
310
+
311
+ return stats;
312
+ } catch (error) {
313
+ return { totalErrors: 0, byType: {}, byLevel: {} };
314
+ }
315
+ }
316
+
317
+ /**
318
+ * Generate a formatted error report
319
+ * @param {Object} stats - Error statistics from getErrorStats()
320
+ * @returns {string} Formatted error report
321
+ */
322
+ generateErrorReport(stats) {
323
+ let report = '\n=== Stigmergy CLI Error Report ===\n';
324
+ report += `Total Errors: ${stats.totalErrors}\n\n`;
325
+
326
+ report += 'Errors by Type:\n';
327
+ for (const [type, count] of Object.entries(stats.byType)) {
328
+ report += ` ${type}: ${count}\n`;
329
+ }
330
+
331
+ report += '\nErrors by Level:\n';
332
+ for (const [level, count] of Object.entries(stats.byLevel)) {
333
+ report += ` ${level}: ${count}\n`;
334
+ }
335
+
336
+ report += '==================================\n';
337
+ return report;
338
+ }
339
+
340
+ /**
341
+ * Print error report to console
342
+ */
343
+ async printErrorReport() {
344
+ const stats = await this.getErrorStats();
345
+ const report = this.generateErrorReport(stats);
346
+ console.log(report);
347
+ }
348
+ }
349
+
350
+ // Set up global error handlers within the error handler module
351
+ function setupGlobalErrorHandlers() {
352
+ // Only set up handlers if they haven't been set up already
353
+ if (!process.listenerCount('unhandledRejection')) {
354
+ process.on('unhandledRejection', async (reason, promise) => {
355
+ console.error(
356
+ '[FATAL] Global Unhandled Rejection at:',
357
+ promise,
358
+ 'reason:',
359
+ reason,
360
+ );
361
+
362
+ // Log the error using our error handler
363
+ const error =
364
+ reason instanceof Error ? reason : new Error(String(reason));
365
+ await errorHandler.logError(
366
+ error,
367
+ LOG_LEVELS.ERROR,
368
+ 'global_unhandledRejection',
369
+ );
370
+
371
+ // Exit gracefully after a short delay to allow logging
372
+ setTimeout(() => process.exit(1), 100);
373
+ });
374
+ }
375
+
376
+ if (!process.listenerCount('uncaughtException')) {
377
+ process.on('uncaughtException', async (error) => {
378
+ console.error('[FATAL] Global Uncaught Exception:', error);
379
+
380
+ // Log the error using our error handler
381
+ await errorHandler.logError(
382
+ error,
383
+ LOG_LEVELS.ERROR,
384
+ 'global_uncaughtException',
385
+ );
386
+
387
+ // Exit gracefully after a short delay to allow logging
388
+ setTimeout(() => process.exit(1), 100);
389
+ });
390
+ }
391
+ }
392
+
393
+ // Set up global handlers by default
394
+ setupGlobalErrorHandlers();
395
+
396
+ // Export singleton instance
397
+ const errorHandler = new ErrorHandler();
398
+
399
+ module.exports = {
400
+ ErrorHandler,
401
+ errorHandler,
402
+ StigmergyError,
403
+ ERROR_TYPES,
404
+ LOG_LEVELS,
405
+ setupGlobalErrorHandlers,
406
+ };