stigmergy 1.0.93 → 1.0.95

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 (51) hide show
  1. package/bin/stigmergy +26 -12
  2. package/bin/stigmergy.cmd +1 -1
  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/requirements_specification.md +148 -0
  8. package/docs/system_design.md +314 -0
  9. package/docs/tdd_implementation_plan.md +384 -0
  10. package/docs/test_report.md +49 -0
  11. package/examples/calculator-example.js +72 -0
  12. package/examples/json-validation-example.js +64 -0
  13. package/examples/rest-client-example.js +52 -0
  14. package/package.json +21 -17
  15. package/scripts/post-deployment-config.js +9 -2
  16. package/src/auth.js +171 -0
  17. package/src/auth_command.js +195 -0
  18. package/src/calculator.js +220 -0
  19. package/src/core/cli_help_analyzer.js +625 -562
  20. package/src/core/cli_parameter_handler.js +121 -0
  21. package/src/core/cli_tools.js +89 -0
  22. package/src/core/error_handler.js +307 -0
  23. package/src/core/memory_manager.js +76 -0
  24. package/src/core/smart_router.js +133 -0
  25. package/src/deploy.js +50 -0
  26. package/src/main_english.js +643 -720
  27. package/src/main_fixed.js +1035 -0
  28. package/src/utils.js +529 -0
  29. package/src/weatherProcessor.js +205 -0
  30. package/test/calculator.test.js +215 -0
  31. package/test/collision-test.js +26 -0
  32. package/test/csv-processing-test.js +36 -0
  33. package/test/e2e/claude-cli-test.js +128 -0
  34. package/test/e2e/collaboration-test.js +75 -0
  35. package/test/e2e/comprehensive-test.js +431 -0
  36. package/test/e2e/error-handling-test.js +90 -0
  37. package/test/e2e/individual-tool-test.js +143 -0
  38. package/test/e2e/other-cli-test.js +130 -0
  39. package/test/e2e/qoder-cli-test.js +128 -0
  40. package/test/e2e/run-e2e-tests.js +73 -0
  41. package/test/e2e/test-data.js +88 -0
  42. package/test/e2e/test-utils.js +222 -0
  43. package/test/hash-table-demo.js +33 -0
  44. package/test/hash-table-test.js +26 -0
  45. package/test/json-validation-test.js +164 -0
  46. package/test/rest-client-test.js +56 -0
  47. package/test/unit/calculator-full.test.js +191 -0
  48. package/test/unit/calculator-simple.test.js +96 -0
  49. package/test/unit/calculator.test.js +97 -0
  50. package/test/unit/cli_parameter_handler.test.js +116 -0
  51. package/test/weather-processor.test.js +104 -0
@@ -0,0 +1,121 @@
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 = ['claude', 'qodercli', 'iflow', 'codebuddy', 'copilot'];
91
+ if (toolsWithPFlag.includes(toolName)) {
92
+ return ['-p', `"${prompt}"`];
93
+ }
94
+
95
+ // Default to argument-based approach
96
+ return [`"${prompt}"`];
97
+ }
98
+
99
+ /**
100
+ * Execute CLI tool with appropriate arguments
101
+ * @param {string} toolPath - Path to the CLI tool
102
+ * @param {string} toolName - Name of the CLI tool
103
+ * @param {string} prompt - User prompt
104
+ * @param {Object} cliPattern - CLI pattern information from analyzer
105
+ * @returns {Object} Child process object
106
+ */
107
+ static executeCLI(toolPath, toolName, prompt, cliPattern) {
108
+ // Generate appropriate arguments
109
+ const toolArgs = this.generateArguments(toolName, prompt, cliPattern);
110
+
111
+ // Execute the tool
112
+ const child = spawn(toolPath, toolArgs, {
113
+ stdio: 'inherit',
114
+ shell: true
115
+ });
116
+
117
+ return child;
118
+ }
119
+ }
120
+
121
+ 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,307 @@
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
+
10
+ // Error types enumeration
11
+ const ERROR_TYPES = {
12
+ VALIDATION: 'VALIDATION_ERROR',
13
+ NETWORK: 'NETWORK_ERROR',
14
+ FILE_SYSTEM: 'FILE_SYSTEM_ERROR',
15
+ CLI_TOOL: 'CLI_TOOL_ERROR',
16
+ CONFIGURATION: 'CONFIGURATION_ERROR',
17
+ PERMISSION: 'PERMISSION_ERROR',
18
+ UNKNOWN: 'UNKNOWN_ERROR'
19
+ };
20
+
21
+ // Log levels enumeration
22
+ const LOG_LEVELS = {
23
+ ERROR: 'ERROR',
24
+ WARN: 'WARN',
25
+ INFO: 'INFO',
26
+ DEBUG: 'DEBUG'
27
+ };
28
+
29
+ class StigmergyError extends Error {
30
+ constructor(message, type = ERROR_TYPES.UNKNOWN, code = null, details = null) {
31
+ super(message);
32
+ this.name = 'StigmergyError';
33
+ this.type = type;
34
+ this.code = code;
35
+ this.details = details;
36
+ this.timestamp = new Date().toISOString();
37
+
38
+ // Ensure proper stack trace
39
+ if (Error.captureStackTrace) {
40
+ Error.captureStackTrace(this, StigmergyError);
41
+ }
42
+ }
43
+ }
44
+
45
+ class ErrorHandler {
46
+ constructor() {
47
+ this.logFile = path.join(os.homedir(), '.stigmergy', 'error.log');
48
+ this.maxLogSize = 10 * 1024 * 1024; // 10MB
49
+ }
50
+
51
+ /**
52
+ * Create a standardized Stigmergy error
53
+ * @param {string} message - Error message
54
+ * @param {string} type - Error type from ERROR_TYPES
55
+ * @param {string|null} code - Error code
56
+ * @param {Object|null} details - Additional error details
57
+ * @returns {StigmergyError}
58
+ */
59
+ createError(message, type = ERROR_TYPES.UNKNOWN, code = null, details = null) {
60
+ return new StigmergyError(message, type, code, details);
61
+ }
62
+
63
+ /**
64
+ * Log an error to console and file
65
+ * @param {Error|StigmergyError} error - The error to log
66
+ * @param {string} level - Log level (ERROR, WARN, INFO, DEBUG)
67
+ * @param {string|null} context - Context where error occurred
68
+ */
69
+ async logError(error, level = LOG_LEVELS.ERROR, context = null) {
70
+ try {
71
+ const timestamp = new Date().toISOString();
72
+ const errorType = error.type || ERROR_TYPES.UNKNOWN;
73
+ const errorCode = error.code || 'NO_CODE';
74
+ const errorMessage = error.message || 'Unknown error';
75
+
76
+ // Format log entry
77
+ const logEntry = {
78
+ timestamp,
79
+ level,
80
+ type: errorType,
81
+ code: errorCode,
82
+ message: errorMessage,
83
+ context,
84
+ stack: error.stack || 'No stack trace available'
85
+ };
86
+
87
+ // Console output
88
+ const consoleMessage = `[${level}] [${errorType}] ${errorMessage}`;
89
+ switch (level) {
90
+ case LOG_LEVELS.ERROR:
91
+ console.error(consoleMessage);
92
+ break;
93
+ case LOG_LEVELS.WARN:
94
+ console.warn(consoleMessage);
95
+ break;
96
+ case LOG_LEVELS.INFO:
97
+ console.info(consoleMessage);
98
+ break;
99
+ case LOG_LEVELS.DEBUG:
100
+ console.debug(consoleMessage);
101
+ break;
102
+ default:
103
+ console.log(consoleMessage);
104
+ }
105
+
106
+ // File logging
107
+ await this.writeToFile(logEntry);
108
+ } catch (logError) {
109
+ console.error('[ERROR_HANDLER] Failed to log error:', logError.message);
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Write error to log file
115
+ * @param {Object} logEntry - Formatted log entry
116
+ */
117
+ async writeToFile(logEntry) {
118
+ try {
119
+ // Ensure log directory exists
120
+ const logDir = path.dirname(this.logFile);
121
+ await fs.mkdir(logDir, { recursive: true });
122
+
123
+ // Check file size and rotate if necessary
124
+ await this.rotateLogFile();
125
+
126
+ // Append to log file
127
+ const logLine = JSON.stringify(logEntry) + '\n';
128
+ await fs.appendFile(this.logFile, logLine, { encoding: 'utf8' });
129
+ } catch (error) {
130
+ // Silent fail to prevent infinite loop
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Rotate log file if it exceeds max size
136
+ */
137
+ async rotateLogFile() {
138
+ try {
139
+ const stats = await fs.stat(this.logFile);
140
+ if (stats.size > this.maxLogSize) {
141
+ const backupFile = this.logFile + '.old';
142
+ await fs.rename(this.logFile, backupFile);
143
+ }
144
+ } catch (error) {
145
+ // File doesn't exist or other issue, continue
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Handle CLI tool execution errors
151
+ * @param {string} toolName - Name of the CLI tool
152
+ * @param {Error} error - Error that occurred
153
+ * @param {string|null} command - Command that failed
154
+ */
155
+ async handleCLIError(toolName, error, command = null) {
156
+ const cliError = new StigmergyError(
157
+ `Failed to execute ${toolName}${command ? ` command: ${command}` : ''}`,
158
+ ERROR_TYPES.CLI_TOOL,
159
+ null,
160
+ {
161
+ tool: toolName,
162
+ command,
163
+ originalError: error.message,
164
+ stack: error.stack
165
+ }
166
+ );
167
+
168
+ await this.logError(cliError, LOG_LEVELS.ERROR, 'CLI_EXECUTION');
169
+ return cliError;
170
+ }
171
+
172
+ /**
173
+ * Handle file system errors
174
+ * @param {string} operation - File operation that failed
175
+ * @param {string} filePath - Path of file involved
176
+ * @param {Error} error - Original error
177
+ */
178
+ async handleFileError(operation, filePath, error) {
179
+ const fileError = new StigmergyError(
180
+ `Failed to ${operation} file: ${filePath}`,
181
+ ERROR_TYPES.FILE_SYSTEM,
182
+ error.code,
183
+ {
184
+ operation,
185
+ filePath,
186
+ originalError: error.message,
187
+ stack: error.stack
188
+ }
189
+ );
190
+
191
+ await this.logError(fileError, LOG_LEVELS.ERROR, 'FILE_SYSTEM');
192
+ return fileError;
193
+ }
194
+
195
+ /**
196
+ * Handle network errors
197
+ * @param {string} operation - Network operation that failed
198
+ * @param {string} url - URL involved
199
+ * @param {Error} error - Original error
200
+ */
201
+ async handleNetworkError(operation, url, error) {
202
+ const networkError = new StigmergyError(
203
+ `Network error during ${operation}: ${url}`,
204
+ ERROR_TYPES.NETWORK,
205
+ error.code,
206
+ {
207
+ operation,
208
+ url,
209
+ originalError: error.message,
210
+ stack: error.stack
211
+ }
212
+ );
213
+
214
+ await this.logError(networkError, LOG_LEVELS.ERROR, 'NETWORK');
215
+ return networkError;
216
+ }
217
+
218
+ /**
219
+ * Handle validation errors
220
+ * @param {string} field - Field that failed validation
221
+ * @param {string} value - Value that failed validation
222
+ * @param {string} reason - Reason for validation failure
223
+ */
224
+ createValidationError(field, value, reason) {
225
+ const validationError = new StigmergyError(
226
+ `Validation failed for ${field}: ${reason}`,
227
+ ERROR_TYPES.VALIDATION,
228
+ 'VALIDATION_FAILED',
229
+ {
230
+ field,
231
+ value,
232
+ reason
233
+ }
234
+ );
235
+
236
+ return validationError;
237
+ }
238
+
239
+ /**
240
+ * Wrap an async function with error handling
241
+ * @param {Function} fn - Async function to wrap
242
+ * @param {string} context - Context for error logging
243
+ * @returns {Function} Wrapped function
244
+ */
245
+ wrapAsync(fn, context) {
246
+ return async (...args) => {
247
+ try {
248
+ return await fn(...args);
249
+ } catch (error) {
250
+ if (error instanceof StigmergyError) {
251
+ await this.logError(error, LOG_LEVELS.ERROR, context);
252
+ throw error;
253
+ } else {
254
+ const wrappedError = new StigmergyError(
255
+ error.message || 'Unknown error occurred',
256
+ ERROR_TYPES.UNKNOWN,
257
+ null,
258
+ { originalError: error.message, stack: error.stack }
259
+ );
260
+ await this.logError(wrappedError, LOG_LEVELS.ERROR, context);
261
+ throw wrappedError;
262
+ }
263
+ }
264
+ };
265
+ }
266
+
267
+ /**
268
+ * Get error statistics from log file
269
+ */
270
+ async getErrorStats() {
271
+ try {
272
+ const data = await fs.readFile(this.logFile, 'utf8');
273
+ const lines = data.split('\n').filter(line => line.trim());
274
+
275
+ const stats = {
276
+ totalErrors: lines.length,
277
+ byType: {},
278
+ byLevel: {}
279
+ };
280
+
281
+ for (const line of lines) {
282
+ try {
283
+ const entry = JSON.parse(line);
284
+ stats.byType[entry.type] = (stats.byType[entry.type] || 0) + 1;
285
+ stats.byLevel[entry.level] = (stats.byLevel[entry.level] || 0) + 1;
286
+ } catch (parseError) {
287
+ // Skip malformed entries
288
+ }
289
+ }
290
+
291
+ return stats;
292
+ } catch (error) {
293
+ return { totalErrors: 0, byType: {}, byLevel: {} };
294
+ }
295
+ }
296
+ }
297
+
298
+ // Export singleton instance
299
+ const errorHandler = new ErrorHandler();
300
+
301
+ module.exports = {
302
+ ErrorHandler,
303
+ errorHandler,
304
+ StigmergyError,
305
+ ERROR_TYPES,
306
+ LOG_LEVELS
307
+ };
@@ -0,0 +1,76 @@
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(os.homedir(), '.stigmergy', 'memory.json');
13
+ this.projectMemoryFile = path.join(process.cwd(), 'STIGMERGY.md');
14
+ }
15
+
16
+ /**
17
+ * Get global memory data
18
+ * @returns {Promise<Object>} Memory data
19
+ */
20
+ async getGlobalMemory() {
21
+ try {
22
+ const data = await fs.readFile(this.globalMemoryFile, 'utf8');
23
+ return JSON.parse(data);
24
+ } catch (error) {
25
+ // Return default memory structure if file doesn't exist or is invalid
26
+ return {
27
+ version: '1.0.0',
28
+ lastUpdated: new Date().toISOString(),
29
+ interactions: [],
30
+ collaborations: []
31
+ };
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Update global memory data
37
+ * @param {Function} updateFn - Function that takes current memory and returns updated memory
38
+ * @returns {Promise<Object>} Updated memory data
39
+ */
40
+ async updateGlobalMemory(updateFn) {
41
+ const memory = await this.getGlobalMemory();
42
+ const updatedMemory = updateFn(memory);
43
+ updatedMemory.lastUpdated = new Date().toISOString();
44
+
45
+ // Ensure directory exists
46
+ const dir = path.dirname(this.globalMemoryFile);
47
+ await fs.mkdir(dir, { recursive: true });
48
+
49
+ // Write updated memory
50
+ await fs.writeFile(this.globalMemoryFile, JSON.stringify(updatedMemory, null, 2));
51
+ return updatedMemory;
52
+ }
53
+
54
+ /**
55
+ * Add an interaction record to memory
56
+ * @param {string} tool - CLI tool name
57
+ * @param {string} prompt - User prompt
58
+ * @param {string} response - Tool response
59
+ * @param {number} duration - Execution duration in milliseconds
60
+ * @returns {Promise<void>}
61
+ */
62
+ async addInteraction(tool, prompt, response, duration = 0) {
63
+ await this.updateGlobalMemory(memory => {
64
+ memory.interactions.push({
65
+ timestamp: new Date().toISOString(),
66
+ tool,
67
+ prompt,
68
+ response,
69
+ duration
70
+ });
71
+ return memory;
72
+ });
73
+ }
74
+ }
75
+
76
+ module.exports = MemoryManager;