stigmergy 1.0.94 → 1.0.97

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 (67) hide show
  1. package/README.md +20 -0
  2. package/bin/stigmergy +37 -12
  3. package/docs/HASH_TABLE.md +83 -0
  4. package/docs/WEATHER_PROCESSOR_API.md +230 -0
  5. package/docs/best_practices.md +135 -0
  6. package/docs/development_guidelines.md +392 -0
  7. package/docs/http-request-handler.md +289 -0
  8. package/docs/json-parser.md +102 -0
  9. package/docs/requirements_specification.md +148 -0
  10. package/docs/rest_client.md +144 -0
  11. package/docs/system_design.md +314 -0
  12. package/docs/tdd_implementation_plan.md +384 -0
  13. package/docs/test_report.md +49 -0
  14. package/examples/calculator-example.js +72 -0
  15. package/examples/encryption-example.js +67 -0
  16. package/examples/json-parser-example.js +120 -0
  17. package/examples/json-validation-example.js +64 -0
  18. package/examples/rest-client-example.js +52 -0
  19. package/examples/rest_client_example.js +54 -0
  20. package/package.json +26 -21
  21. package/scripts/post-deployment-config.js +9 -2
  22. package/src/auth.js +173 -0
  23. package/src/auth_command.js +208 -0
  24. package/src/calculator.js +313 -0
  25. package/src/core/cli_help_analyzer.js +674 -563
  26. package/src/core/cli_parameter_handler.js +127 -0
  27. package/src/core/cli_tools.js +89 -0
  28. package/src/core/error_handler.js +406 -0
  29. package/src/core/memory_manager.js +83 -0
  30. package/src/core/rest_client.js +160 -0
  31. package/src/core/smart_router.js +146 -0
  32. package/src/data_encryption.js +143 -0
  33. package/src/data_structures.js +440 -0
  34. package/src/deploy.js +56 -0
  35. package/src/index.js +9 -9
  36. package/src/main.js +889 -752
  37. package/src/main_english.js +1305 -977
  38. package/src/main_fixed.js +1172 -0
  39. package/src/utils.js +916 -0
  40. package/src/weatherProcessor.js +228 -0
  41. package/test/calculator.test.js +215 -0
  42. package/test/collision-test.js +26 -0
  43. package/test/csv-processing-test.js +36 -0
  44. package/test/e2e/claude-cli-test.js +128 -0
  45. package/test/e2e/collaboration-test.js +75 -0
  46. package/test/e2e/comprehensive-test.js +431 -0
  47. package/test/e2e/error-handling-test.js +90 -0
  48. package/test/e2e/individual-tool-test.js +143 -0
  49. package/test/e2e/other-cli-test.js +130 -0
  50. package/test/e2e/qoder-cli-test.js +128 -0
  51. package/test/e2e/run-e2e-tests.js +73 -0
  52. package/test/e2e/test-data.js +88 -0
  53. package/test/e2e/test-utils.js +222 -0
  54. package/test/encryption-simple-test.js +110 -0
  55. package/test/encryption.test.js +129 -0
  56. package/test/hash-table-demo.js +33 -0
  57. package/test/hash-table-test.js +26 -0
  58. package/test/hash_table_test.js +114 -0
  59. package/test/json-parser-test.js +161 -0
  60. package/test/json-validation-test.js +164 -0
  61. package/test/rest-client-test.js +56 -0
  62. package/test/rest_client.test.js +85 -0
  63. package/test/unit/calculator-full.test.js +191 -0
  64. package/test/unit/calculator-simple.test.js +96 -0
  65. package/test/unit/calculator.test.js +97 -0
  66. package/test/unit/cli_parameter_handler.test.js +116 -0
  67. package/test/weather-processor.test.js +104 -0
@@ -0,0 +1,127 @@
1
+ // Unified CLI Parameter Handler
2
+ const { spawn } = require("child_process");
3
+
4
+ class CLIParameterHandler {
5
+ /**
6
+ * Generate appropriate arguments for CLI execution based on tool patterns
7
+ * @param {string} toolName - Name of the CLI tool
8
+ * @param {string} prompt - User prompt
9
+ * @param {Object} cliPattern - CLI pattern information from analyzer
10
+ * @returns {Array} Arguments array for spawn
11
+ */
12
+ static generateArguments(toolName, prompt, cliPattern) {
13
+ // Default arguments
14
+ let toolArgs = [];
15
+
16
+ // Special handling for Codex CLI which always needs 'exec' subcommand
17
+ if (toolName === "codex") {
18
+ return ["exec", "-p", `"${prompt}"`];
19
+ }
20
+
21
+ try {
22
+ // Check if we have pattern information from CLI help analyzer
23
+ if (cliPattern && cliPattern.commandStructure) {
24
+ const commandStructure = cliPattern.commandStructure;
25
+
26
+ // Handle based on command structure and patterns from help analyzer
27
+ if (commandStructure.nonInteractiveSupport) {
28
+ // Use flag-based approach if available
29
+ if (commandStructure.promptFlag) {
30
+ toolArgs = [commandStructure.promptFlag, `"${prompt}"`];
31
+ } else if (commandStructure.nonInteractiveFlag) {
32
+ toolArgs = [commandStructure.nonInteractiveFlag, `"${prompt}"`];
33
+ } else {
34
+ // Fallback to standard -p flag
35
+ toolArgs = ["-p", `"${prompt}"`];
36
+ }
37
+ } else if (commandStructure.executionPattern === "flag-based") {
38
+ // Explicitly flag-based tools
39
+ if (commandStructure.promptFlag) {
40
+ toolArgs = [commandStructure.promptFlag, `"${prompt}"`];
41
+ } else {
42
+ toolArgs = ["-p", `"${prompt}"`];
43
+ }
44
+ } else if (commandStructure.executionPattern === "argument-based") {
45
+ // Argument-based tools
46
+ toolArgs = [`"${prompt}"`];
47
+ } else if (commandStructure.executionPattern === "subcommand-based") {
48
+ // Subcommand-based tools
49
+ toolArgs = ["-p", `"${prompt}"`];
50
+ } else {
51
+ // Fallback to tool-specific handling
52
+ toolArgs = this.getToolSpecificArguments(toolName, prompt);
53
+ }
54
+ } else {
55
+ // Fallback to tool-specific handling if no pattern information
56
+ toolArgs = this.getToolSpecificArguments(toolName, prompt);
57
+ }
58
+ } catch (error) {
59
+ // Final fallback to tool-specific handling
60
+ toolArgs = this.getToolSpecificArguments(toolName, prompt);
61
+ }
62
+
63
+ return toolArgs;
64
+ }
65
+
66
+ /**
67
+ * Get tool-specific arguments based on known patterns
68
+ * @param {string} toolName - Name of the CLI tool
69
+ * @param {string} prompt - User prompt
70
+ * @returns {Array} Arguments array for spawn
71
+ */
72
+ static getToolSpecificArguments(toolName, prompt) {
73
+ // Tool-specific argument handling
74
+ const toolSpecificArgs = {
75
+ claude: ["-p", `"${prompt}"`],
76
+ qodercli: ["-p", `"${prompt}"`],
77
+ iflow: ["-p", `"${prompt}"`],
78
+ codebuddy: ["-p", `"${prompt}"`],
79
+ copilot: ["-p", `"${prompt}"`],
80
+ codex: ["exec", "-p", `"${prompt}"`], // Codex needs 'exec' subcommand
81
+ };
82
+
83
+ // Return tool-specific arguments if available
84
+ if (toolSpecificArgs[toolName]) {
85
+ return toolSpecificArgs[toolName];
86
+ }
87
+
88
+ // Default handling for other tools
89
+ // Check if the tool commonly uses -p flag
90
+ const toolsWithPFlag = [
91
+ "claude",
92
+ "qodercli",
93
+ "iflow",
94
+ "codebuddy",
95
+ "copilot",
96
+ ];
97
+ if (toolsWithPFlag.includes(toolName)) {
98
+ return ["-p", `"${prompt}"`];
99
+ }
100
+
101
+ // Default to argument-based approach
102
+ return [`"${prompt}"`];
103
+ }
104
+
105
+ /**
106
+ * Execute CLI tool with appropriate arguments
107
+ * @param {string} toolPath - Path to the CLI tool
108
+ * @param {string} toolName - Name of the CLI tool
109
+ * @param {string} prompt - User prompt
110
+ * @param {Object} cliPattern - CLI pattern information from analyzer
111
+ * @returns {Object} Child process object
112
+ */
113
+ static executeCLI(toolPath, toolName, prompt, cliPattern) {
114
+ // Generate appropriate arguments
115
+ const toolArgs = this.generateArguments(toolName, prompt, cliPattern);
116
+
117
+ // Execute the tool
118
+ const child = spawn(toolPath, toolArgs, {
119
+ stdio: "inherit",
120
+ shell: true,
121
+ });
122
+
123
+ return child;
124
+ }
125
+ }
126
+
127
+ module.exports = CLIParameterHandler;
@@ -0,0 +1,89 @@
1
+ const path = require("path");
2
+ const os = require("os");
3
+ const { errorHandler, ERROR_TYPES } = require("./error_handler");
4
+
5
+ // AI CLI Tools Configuration
6
+ const CLI_TOOLS = {
7
+ claude: {
8
+ name: "Claude CLI",
9
+ version: "claude --version",
10
+ install: "npm install -g @anthropic-ai/claude-cli",
11
+ hooksDir: path.join(os.homedir(), ".claude", "hooks"),
12
+ config: path.join(os.homedir(), ".claude", "config.json"),
13
+ },
14
+ gemini: {
15
+ name: "Gemini CLI",
16
+ version: "gemini --version",
17
+ install: "npm install -g @google/generative-ai-cli",
18
+ hooksDir: path.join(os.homedir(), ".gemini", "extensions"),
19
+ config: path.join(os.homedir(), ".gemini", "config.json"),
20
+ },
21
+ qwen: {
22
+ name: "Qwen CLI",
23
+ version: "qwen --version",
24
+ install: "npm install -g @alibaba/qwen-cli",
25
+ hooksDir: path.join(os.homedir(), ".qwen", "hooks"),
26
+ config: path.join(os.homedir(), ".qwen", "config.json"),
27
+ },
28
+ iflow: {
29
+ name: "iFlow CLI",
30
+ version: "iflow --version",
31
+ install: "npm install -g iflow-cli",
32
+ hooksDir: path.join(os.homedir(), ".iflow", "hooks"),
33
+ config: path.join(os.homedir(), ".iflow", "config.json"),
34
+ },
35
+ qodercli: {
36
+ name: "Qoder CLI",
37
+ version: "qodercli --version",
38
+ install: "npm install -g @qoder-ai/qodercli",
39
+ hooksDir: path.join(os.homedir(), ".qoder", "hooks"),
40
+ config: path.join(os.homedir(), ".qoder", "config.json"),
41
+ },
42
+ codebuddy: {
43
+ name: "CodeBuddy CLI",
44
+ version: "codebuddy --version",
45
+ install: "npm install -g codebuddy-cli",
46
+ hooksDir: path.join(os.homedir(), ".codebuddy", "hooks"),
47
+ config: path.join(os.homedir(), ".codebuddy", "config.json"),
48
+ },
49
+ copilot: {
50
+ name: "GitHub Copilot CLI",
51
+ version: "copilot --version",
52
+ install: "npm install -g @github/copilot-cli",
53
+ hooksDir: path.join(os.homedir(), ".copilot", "mcp"),
54
+ config: path.join(os.homedir(), ".copilot", "config.json"),
55
+ },
56
+ codex: {
57
+ name: "OpenAI Codex CLI",
58
+ version: "codex --version",
59
+ install: "npm install -g openai-codex-cli",
60
+ hooksDir: path.join(os.homedir(), ".config", "codex", "slash_commands"),
61
+ config: path.join(os.homedir(), ".codex", "config.json"),
62
+ },
63
+ };
64
+
65
+ /**
66
+ * Validate CLI tool configuration
67
+ * @param {string} toolName - Name of the tool to validate
68
+ * @throws {StigmergyError} If validation fails
69
+ */
70
+ function validateCLITool(toolName) {
71
+ if (!CLI_TOOLS[toolName]) {
72
+ throw errorHandler.createError(
73
+ `CLI tool '${toolName}' is not configured`,
74
+ ERROR_TYPES.CONFIGURATION,
75
+ "INVALID_CLI_TOOL",
76
+ );
77
+ }
78
+
79
+ const tool = CLI_TOOLS[toolName];
80
+ if (!tool.name || !tool.version || !tool.install) {
81
+ throw errorHandler.createError(
82
+ `CLI tool '${toolName}' has invalid configuration`,
83
+ ERROR_TYPES.CONFIGURATION,
84
+ "INCOMPLETE_CLI_CONFIG",
85
+ );
86
+ }
87
+ }
88
+
89
+ module.exports = { CLI_TOOLS, validateCLITool };
@@ -0,0 +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
+ };
@@ -0,0 +1,83 @@
1
+ // src/core/memory_manager.js
2
+
3
+ const fs = require("fs/promises");
4
+ const path = require("path");
5
+ const os = require("os");
6
+
7
+ /**
8
+ * Memory Manager - Handles global memory and context sharing between CLI tools
9
+ */
10
+ class MemoryManager {
11
+ constructor() {
12
+ this.globalMemoryFile = path.join(
13
+ os.homedir(),
14
+ ".stigmergy",
15
+ "memory.json",
16
+ );
17
+ this.projectMemoryFile = path.join(process.cwd(), "STIGMERGY.md");
18
+ }
19
+
20
+ /**
21
+ * Get global memory data
22
+ * @returns {Promise<Object>} Memory data
23
+ */
24
+ async getGlobalMemory() {
25
+ try {
26
+ const data = await fs.readFile(this.globalMemoryFile, "utf8");
27
+ return JSON.parse(data);
28
+ } catch (error) {
29
+ // Return default memory structure if file doesn't exist or is invalid
30
+ return {
31
+ version: "1.0.0",
32
+ lastUpdated: new Date().toISOString(),
33
+ interactions: [],
34
+ collaborations: [],
35
+ };
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Update global memory data
41
+ * @param {Function} updateFn - Function that takes current memory and returns updated memory
42
+ * @returns {Promise<Object>} Updated memory data
43
+ */
44
+ async updateGlobalMemory(updateFn) {
45
+ const memory = await this.getGlobalMemory();
46
+ const updatedMemory = updateFn(memory);
47
+ updatedMemory.lastUpdated = new Date().toISOString();
48
+
49
+ // Ensure directory exists
50
+ const dir = path.dirname(this.globalMemoryFile);
51
+ await fs.mkdir(dir, { recursive: true });
52
+
53
+ // Write updated memory
54
+ await fs.writeFile(
55
+ this.globalMemoryFile,
56
+ JSON.stringify(updatedMemory, null, 2),
57
+ );
58
+ return updatedMemory;
59
+ }
60
+
61
+ /**
62
+ * Add an interaction record to memory
63
+ * @param {string} tool - CLI tool name
64
+ * @param {string} prompt - User prompt
65
+ * @param {string} response - Tool response
66
+ * @param {number} duration - Execution duration in milliseconds
67
+ * @returns {Promise<void>}
68
+ */
69
+ async addInteraction(tool, prompt, response, duration = 0) {
70
+ await this.updateGlobalMemory((memory) => {
71
+ memory.interactions.push({
72
+ timestamp: new Date().toISOString(),
73
+ tool,
74
+ prompt,
75
+ response,
76
+ duration,
77
+ });
78
+ return memory;
79
+ });
80
+ }
81
+ }
82
+
83
+ module.exports = MemoryManager;