stigmergy 1.2.0 → 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 (125) 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 +36 -15
  14. package/scripts/build.js +74 -74
  15. package/scripts/post-deployment-config.js +296 -296
  16. package/scripts/preinstall-check.js +173 -173
  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 +417 -38
  33. package/src/core/cache_cleaner.js +767 -744
  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 -456
  47. package/src/core/enhanced_uninstaller.js +638 -618
  48. package/src/core/error_handler.js +406 -406
  49. package/src/core/installer.js +815 -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 +76 -59
  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/cache-cleaner-implemented.test.js +0 -328
  64. package/test/cache-cleaner.test.js +0 -390
  65. package/test/calculator.test.js +0 -215
  66. package/test/collision-test.js +0 -26
  67. package/test/comprehensive-enhanced-features.test.js +0 -252
  68. package/test/comprehensive-execution-test.js +0 -428
  69. package/test/conflict-prevention-test.js +0 -95
  70. package/test/cross-cli-detection-test.js +0 -33
  71. package/test/csv-processing-test.js +0 -36
  72. package/test/deploy-hooks-test.js +0 -250
  73. package/test/e2e/claude-cli-test.js +0 -128
  74. package/test/e2e/collaboration-test.js +0 -75
  75. package/test/e2e/comprehensive-test.js +0 -431
  76. package/test/e2e/error-handling-test.js +0 -90
  77. package/test/e2e/individual-tool-test.js +0 -143
  78. package/test/e2e/other-cli-test.js +0 -130
  79. package/test/e2e/qoder-cli-test.js +0 -128
  80. package/test/e2e/run-e2e-tests.js +0 -73
  81. package/test/e2e/test-data.js +0 -88
  82. package/test/e2e/test-utils.js +0 -222
  83. package/test/encryption-simple-test.js +0 -110
  84. package/test/encryption.test.js +0 -129
  85. package/test/enhanced-main-alignment.test.js +0 -298
  86. package/test/enhanced-uninstaller-implemented.test.js +0 -271
  87. package/test/enhanced-uninstaller.test.js +0 -284
  88. package/test/error-handling-test.js +0 -341
  89. package/test/fibonacci.test.js +0 -178
  90. package/test/final-deploy-test.js +0 -221
  91. package/test/final-install-test.js +0 -226
  92. package/test/hash-table-demo.js +0 -33
  93. package/test/hash-table-test.js +0 -26
  94. package/test/hash_table_test.js +0 -114
  95. package/test/hook-system-integration-test.js +0 -307
  96. package/test/iflow-integration-test.js +0 -292
  97. package/test/improved-install-test.js +0 -362
  98. package/test/install-command-test.js +0 -370
  99. package/test/json-parser-test.js +0 -161
  100. package/test/json-validation-test.js +0 -164
  101. package/test/natural-language-skills-test.js +0 -320
  102. package/test/nl-integration-test.js +0 -179
  103. package/test/parameter-parsing-test.js +0 -143
  104. package/test/plugin-deployment-test.js +0 -316
  105. package/test/postinstall-test.js +0 -269
  106. package/test/python-plugins-test.js +0 -259
  107. package/test/real-test.js +0 -435
  108. package/test/remaining-adapters-test.js +0 -256
  109. package/test/rest-client-test.js +0 -56
  110. package/test/rest_client.test.js +0 -85
  111. package/test/safe-installation-cleaner.test.js +0 -343
  112. package/test/simple-iflow-hook-test.js +0 -137
  113. package/test/stigmergy-upgrade-test.js +0 -243
  114. package/test/system-compatibility-test.js +0 -467
  115. package/test/tdd-deploy-fix-test.js +0 -324
  116. package/test/tdd-fixes-test.js +0 -211
  117. package/test/third-party-skills-test.js +0 -321
  118. package/test/tool-selection-integration-test.js +0 -158
  119. package/test/unit/calculator-full.test.js +0 -191
  120. package/test/unit/calculator-simple.test.js +0 -96
  121. package/test/unit/calculator.test.js +0 -97
  122. package/test/unit/cli-scanner.test.js +0 -291
  123. package/test/unit/cli_parameter_handler.test.js +0 -116
  124. package/test/unit/cross-cli-executor.test.js +0 -399
  125. package/test/weather-processor.test.js +0 -104
package/src/deploy.js CHANGED
@@ -1,55 +1,55 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Stigmergy Deployment Script
5
- * This script deploys hooks and integrations for all available CLI tools
6
- */
7
-
8
- const fs = require('fs').promises;
9
- const path = require('path');
10
-
11
- // Import the main Stigmergy installer
12
- const { StigmergyInstaller } = require('./main_english.js');
13
-
14
- // Set up global error handlers using our error handler module
15
- const { setupGlobalErrorHandlers } = require('./core/error_handler');
16
- setupGlobalErrorHandlers();
17
-
18
- async function deploy() {
19
- console.log('Stigmergy Deployment Script');
20
- console.log('==========================');
21
-
22
- try {
23
- // Create installer instance
24
- const installer = new StigmergyInstaller();
25
-
26
- // Scan for available tools
27
- console.log('[SCAN] Scanning for available CLI tools...');
28
- const scanResult = await installer.scanCLI();
29
- const available = scanResult.available;
30
-
31
- // Deploy hooks for all available tools
32
- console.log('[DEPLOY] Deploying hooks for all available tools...');
33
- await installer.deployHooks(available);
34
-
35
- console.log('\n[SUCCESS] Deployment completed successfully!');
36
- return true;
37
- } catch (error) {
38
- console.error('[ERROR] Deployment failed:', error.message);
39
- return false;
40
- }
41
- }
42
-
43
- // Run deployment if called directly
44
- if (require.main === module) {
45
- deploy()
46
- .then((success) => {
47
- process.exit(success ? 0 : 1);
48
- })
49
- .catch((error) => {
50
- console.error('[FATAL ERROR]:', error.message);
51
- process.exit(1);
52
- });
53
- }
54
-
55
- module.exports = { deploy };
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Stigmergy Deployment Script
5
+ * This script deploys hooks and integrations for all available CLI tools
6
+ */
7
+
8
+ const fs = require('fs').promises;
9
+ const path = require('path');
10
+
11
+ // Import the main Stigmergy installer
12
+ const StigmergyInstaller = require('./core/installer');
13
+
14
+ // Set up global error handlers using our error handler module
15
+ const { setupGlobalErrorHandlers } = require('./core/error_handler');
16
+ setupGlobalErrorHandlers();
17
+
18
+ async function deploy() {
19
+ console.log('Stigmergy Deployment Script');
20
+ console.log('==========================');
21
+
22
+ try {
23
+ // Create installer instance
24
+ const installer = new StigmergyInstaller();
25
+
26
+ // Scan for available tools
27
+ console.log('[SCAN] Scanning for available CLI tools...');
28
+ const scanResult = await installer.scanCLI();
29
+ const available = scanResult.available;
30
+
31
+ // Deploy hooks for all available tools
32
+ console.log('[DEPLOY] Deploying hooks for all available tools...');
33
+ await installer.deployHooks(available);
34
+
35
+ console.log('\n[SUCCESS] Deployment completed successfully!');
36
+ return true;
37
+ } catch (error) {
38
+ console.error('[ERROR] Deployment failed:', error.message);
39
+ return false;
40
+ }
41
+ }
42
+
43
+ // Run deployment if called directly
44
+ if (require.main === module) {
45
+ deploy()
46
+ .then((success) => {
47
+ process.exit(success ? 0 : 1);
48
+ })
49
+ .catch((error) => {
50
+ console.error('[FATAL ERROR]:', error.message);
51
+ process.exit(1);
52
+ });
53
+ }
54
+
55
+ module.exports = { deploy };
package/src/index.js CHANGED
@@ -1,30 +1,30 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Stigmergy CLI - Multi-Agents Cross-AI CLI Tools Collaboration System
5
- * Unified Entry Point
6
- * International Version - Pure English & ANSI Only
7
- * Version: 1.0.94
8
- */
9
-
10
- // Import all components
11
- const MemoryManager = require('./core/memory_manager');
12
- const StigmergyInstaller = require('./core/installer');
13
- const { maxOfTwo, isAuthenticated } = require('./utils/helpers');
14
- const main = require('./cli/router');
15
-
16
- // Run the main application
17
- if (require.main === module) {
18
- main().catch(error => {
19
- console.error('[FATAL] Unhandled error in Stigmergy CLI:', error);
20
- process.exit(1);
21
- });
22
- }
23
-
24
- module.exports = {
25
- MemoryManager,
26
- StigmergyInstaller,
27
- maxOfTwo,
28
- isAuthenticated,
29
- main
30
- };
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Stigmergy CLI - Multi-Agents Cross-AI CLI Tools Collaboration System
5
+ * Unified Entry Point
6
+ * International Version - Pure English & ANSI Only
7
+ * Version: 1.0.94
8
+ */
9
+
10
+ // Import all components
11
+ const MemoryManager = require('./core/memory_manager');
12
+ const StigmergyInstaller = require('./core/installer');
13
+ const { maxOfTwo, isAuthenticated } = require('./utils/helpers');
14
+ const main = require('./cli/router');
15
+
16
+ // Run the main application
17
+ if (require.main === module) {
18
+ main().catch((error) => {
19
+ console.error('[FATAL] Unhandled error in Stigmergy CLI:', error);
20
+ process.exit(1);
21
+ });
22
+ }
23
+
24
+ module.exports = {
25
+ MemoryManager,
26
+ StigmergyInstaller,
27
+ maxOfTwo,
28
+ isAuthenticated,
29
+ main,
30
+ };
@@ -0,0 +1,194 @@
1
+ /**
2
+ * CLI Availability Checker
3
+ * Detects which CLI tools are installed and available for testing
4
+ */
5
+
6
+ const { spawn } = require('child_process');
7
+ const { CLI_TOOLS } = require('../../src/core/cli_tools');
8
+
9
+ class CLIAvailabilityChecker {
10
+ constructor() {
11
+ this.checkedTools = new Map();
12
+ this.timeout = 5000; // 5 seconds timeout for each check
13
+ }
14
+
15
+ /**
16
+ * Check if a CLI tool is installed and available
17
+ * @param {string} toolName - Name of the CLI tool
18
+ * @returns {Promise<boolean>} True if tool is available
19
+ */
20
+ async isToolAvailable(toolName) {
21
+ if (this.checkedTools.has(toolName)) {
22
+ return this.checkedTools.get(toolName);
23
+ }
24
+
25
+ const tool = CLI_TOOLS[toolName];
26
+ if (!tool) {
27
+ this.checkedTools.set(toolName, false);
28
+ return false;
29
+ }
30
+
31
+ try {
32
+ const isAvailable = await this.checkToolExists(toolName, tool.version);
33
+ this.checkedTools.set(toolName, isAvailable);
34
+ return isAvailable;
35
+ } catch (error) {
36
+ this.checkedTools.set(toolName, false);
37
+ return false;
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Check if a specific command exists
43
+ * @param {string} toolName - Tool name
44
+ * @param {string} versionCommand - Command to check version
45
+ * @returns {Promise<boolean>} True if command exists
46
+ */
47
+ async checkToolExists(toolName, versionCommand) {
48
+ return new Promise((resolve) => {
49
+ const parts = versionCommand.split(' ');
50
+ const command = parts[0];
51
+ const args = parts.slice(1);
52
+
53
+ const child = spawn(command, args, {
54
+ stdio: 'pipe',
55
+ shell: true,
56
+ windowsHide: true
57
+ });
58
+
59
+ let resolved = false;
60
+ let stdout = '';
61
+ let stderr = '';
62
+
63
+ child.stdout?.on('data', (data) => {
64
+ stdout += data.toString();
65
+ });
66
+
67
+ child.stderr?.on('data', (data) => {
68
+ stderr += data.toString();
69
+ });
70
+
71
+ // Set timeout
72
+ const timeoutId = setTimeout(() => {
73
+ if (!resolved) {
74
+ resolved = true;
75
+ child.kill('SIGTERM');
76
+ resolve(false);
77
+ }
78
+ }, this.timeout);
79
+
80
+ child.on('close', (code) => {
81
+ if (!resolved) {
82
+ resolved = true;
83
+ clearTimeout(timeoutId);
84
+
85
+ // Check for serious errors
86
+ if (stderr.includes('ERR_MODULE_NOT_FOUND') ||
87
+ stderr.includes('Cannot find module') ||
88
+ stderr.includes('command not found') ||
89
+ stderr.includes('is not recognized')) {
90
+ resolve(false);
91
+ return;
92
+ }
93
+
94
+ // Consider exit code 0, 1, or 255 as success
95
+ // Some CLIs return 255 for version commands
96
+ // But also check that we got some output (not just empty error)
97
+ const hasOutput = stdout.length > 0 || (stderr.length > 0 && !stderr.includes('Error'));
98
+ resolve((code === 0 || code === 1 || code === 255) && hasOutput);
99
+ }
100
+ });
101
+
102
+ child.on('error', () => {
103
+ if (!resolved) {
104
+ resolved = true;
105
+ clearTimeout(timeoutId);
106
+ resolve(false);
107
+ }
108
+ });
109
+ });
110
+ }
111
+
112
+ /**
113
+ * Get availability of all CLI tools
114
+ * @returns {Promise<Object>} Map of tool availability
115
+ */
116
+ async checkAllTools() {
117
+ const results = {};
118
+ const toolNames = Object.keys(CLI_TOOLS);
119
+
120
+ // Check tools sequentially to avoid conflicts
121
+ for (const toolName of toolNames) {
122
+ results[toolName] = await this.isToolAvailable(toolName);
123
+ }
124
+
125
+ return results;
126
+ }
127
+
128
+ /**
129
+ * Get only available tools
130
+ * @returns {Promise<Array>} List of available tool names
131
+ */
132
+ async getAvailableTools() {
133
+ const allTools = await this.checkAllTools();
134
+ return Object.entries(allTools)
135
+ .filter(([_, available]) => available)
136
+ .map(([name, _]) => name);
137
+ }
138
+
139
+ /**
140
+ * Get installation commands for unavailable tools
141
+ * @returns {Promise<Object>} Map of tool to installation command
142
+ */
143
+ async getInstallationCommands() {
144
+ const allTools = await this.checkAllTools();
145
+ const commands = {};
146
+
147
+ Object.entries(allTools).forEach(([toolName, available]) => {
148
+ if (!available && CLI_TOOLS[toolName]) {
149
+ commands[toolName] = CLI_TOOLS[toolName].install;
150
+ }
151
+ });
152
+
153
+ return commands;
154
+ }
155
+
156
+ /**
157
+ * Print availability report
158
+ */
159
+ async printAvailabilityReport() {
160
+ const allTools = await this.checkAllTools();
161
+ const available = Object.entries(allTools).filter(([_, a]) => a).length;
162
+ const total = Object.keys(allTools).length;
163
+
164
+ console.log('\n=== CLI Tool Availability Report ===');
165
+ console.log(`Available: ${available}/${total}\n`);
166
+
167
+ console.log('�?Available Tools:');
168
+ Object.entries(allTools)
169
+ .filter(([_, available]) => available)
170
+ .forEach(([toolName, _]) => {
171
+ console.log(` - ${toolName}`);
172
+ });
173
+
174
+ const unavailable = Object.entries(allTools).filter(([_, a]) => !a);
175
+ if (unavailable.length > 0) {
176
+ console.log('\n�?Unavailable Tools:');
177
+ unavailable.forEach(([toolName, _]) => {
178
+ console.log(` - ${toolName}`);
179
+ console.log(` Install: ${CLI_TOOLS[toolName].install}`);
180
+ });
181
+ }
182
+
183
+ console.log('=====================================\n');
184
+ }
185
+
186
+ /**
187
+ * Clear cache of checked tools
188
+ */
189
+ clearCache() {
190
+ this.checkedTools.clear();
191
+ }
192
+ }
193
+
194
+ module.exports = CLIAvailabilityChecker;
@@ -0,0 +1,289 @@
1
+ /**
2
+ * Test Environment Isolation
3
+ * Creates isolated environment for testing without affecting user setup
4
+ */
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const os = require('os');
9
+ const { spawn } = require('child_process');
10
+
11
+ class TestEnvironment {
12
+ constructor(name = 'stigmergy-test') {
13
+ this.name = name;
14
+ this.baseDir = path.join(os.tmpdir(), name);
15
+ this.originalEnv = { ...process.env };
16
+ this.isSetup = false;
17
+ }
18
+
19
+ /**
20
+ * Setup isolated test environment
21
+ * @param {Object} options - Setup options
22
+ */
23
+ async setup(options = {}) {
24
+ const { clean = true } = options;
25
+
26
+ // Create base directory
27
+ if (clean && fs.existsSync(this.baseDir)) {
28
+ await this.cleanup();
29
+ }
30
+ fs.mkdirSync(this.baseDir, { recursive: true });
31
+
32
+ // Setup home directory
33
+ this.testHome = path.join(this.baseDir, 'home');
34
+ fs.mkdirSync(this.testHome, { recursive: true });
35
+
36
+ // Setup test configuration directories
37
+ this.configDirs = {
38
+ stigmergy: path.join(this.testHome, '.stigmergy'),
39
+ claude: path.join(this.testHome, '.claude'),
40
+ gemini: path.join(this.testHome, '.gemini'),
41
+ qwen: path.join(this.testHome, '.qwen'),
42
+ // Add more as needed
43
+ };
44
+
45
+ Object.values(this.configDirs).forEach(dir => {
46
+ fs.mkdirSync(dir, { recursive: true });
47
+ });
48
+
49
+ // Modify environment variables
50
+ process.env.HOME = this.testHome;
51
+ process.env.USERPROFILE = this.testHome; // Windows
52
+ process.env.PATH = this.createSafePath();
53
+
54
+ this.isSetup = true;
55
+ }
56
+
57
+ /**
58
+ * Create safe PATH with essential tools only
59
+ * @returns {string} Safe PATH
60
+ */
61
+ createSafePath() {
62
+ const essentialPaths = [
63
+ '/usr/bin',
64
+ '/usr/local/bin',
65
+ path.join(process.env.ProgramFiles || '', 'Git', 'bin'),
66
+ path.join(process.env.ProgramFiles || '', 'nodejs'),
67
+ ].filter(p => p && fs.existsSync(p));
68
+
69
+ return essentialPaths.join(path.delimiter);
70
+ }
71
+
72
+ /**
73
+ * Setup mock CLI tools for testing
74
+ * @param {Object} mockTools - Map of tool name to mock implementation
75
+ */
76
+ async setupMockCLIs(mockTools = {}) {
77
+ const mockBinDir = path.join(this.testHome, 'bin');
78
+ fs.mkdirSync(mockBinDir, { recursive: true });
79
+
80
+ // Add mock bin directory to PATH
81
+ process.env.PATH = `${mockBinDir}${path.delimiter}${process.env.PATH}`;
82
+
83
+ for (const [toolName, behavior] of Object.entries(mockTools)) {
84
+ await this.createMockCLI(toolName, behavior, mockBinDir);
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Create a mock CLI tool
90
+ * @param {string} name - Tool name
91
+ * @param {Object} behavior - Mock behavior
92
+ * @param {string} binDir - Binary directory
93
+ */
94
+ async createMockCLI(name, behavior, binDir) {
95
+ const script = this.generateMockScript(name, behavior);
96
+ const scriptPath = path.join(binDir, name);
97
+
98
+ if (process.platform === 'win32') {
99
+ // Windows batch file
100
+ const batPath = scriptPath + '.cmd';
101
+ fs.writeFileSync(batPath, `@echo off\n${script}\n`, 'utf8');
102
+ // Make executable
103
+ fs.chmodSync(batPath, '755');
104
+ } else {
105
+ // Unix shell script
106
+ fs.writeFileSync(scriptPath, `#!/bin/bash\n${script}\n`, 'utf8');
107
+ // Make executable
108
+ fs.chmodSync(scriptPath, '755');
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Generate mock script content
114
+ * @param {string} toolName - Tool name
115
+ * @param {Object} behavior - Mock behavior definition
116
+ * @returns {string} Script content
117
+ */
118
+ generateMockScript(toolName, behavior) {
119
+ const defaultBehavior = {
120
+ version: `${toolName} version 1.0.0-mock`,
121
+ success: true,
122
+ delay: 0,
123
+ output: `Mock ${toolName} executed successfully`
124
+ };
125
+
126
+ const config = { ...defaultBehavior, ...behavior };
127
+
128
+ let script = '';
129
+ if (config.delay > 0) {
130
+ script += `sleep ${config.delay}\n`;
131
+ }
132
+
133
+ // Handle version command
134
+ script += `if echo "$*" | grep -q "\\-\\-version\\|-v"; then\n`;
135
+ script += ` echo "${config.version}"\n`;
136
+ script += ` exit 0\n`;
137
+ script += `fi\n`;
138
+
139
+ // Handle help command
140
+ script += `if echo "$*" | grep -q "\\-\\-help\\|-h"; then\n`;
141
+ script += ` echo "Mock ${toolName} help"\n`;
142
+ script += ` echo "Usage: ${toolName} [options]"\n`;
143
+ script += ` exit 0\n`;
144
+ script += `fi\n`;
145
+
146
+ // Handle prompt flag
147
+ script += `PROMPT=""\n`;
148
+ script += `for arg in "$@"; do\n`;
149
+ script += ` if [[ "$arg" == "-p" ]]; then\n`;
150
+ script += ` PROMPT="$1"\n`;
151
+ script += ` shift\n`;
152
+ script += ` fi\n`;
153
+ script += ` shift\n`;
154
+ script += `done\n`;
155
+
156
+ // Generate response
157
+ script += `if [ -n "$PROMPT" ]; then\n`;
158
+ script += ` echo "Mock response for: $PROMPT"\n`;
159
+ script += ` echo "${config.output}"\n`;
160
+ script += `else\n`;
161
+ script += ` echo "${config.output}"\n`;
162
+ script += `fi\n`;
163
+
164
+ // Handle success/failure
165
+ script += config.success ? 'exit 0\n' : 'exit 1\n';
166
+
167
+ return script;
168
+ }
169
+
170
+ /**
171
+ * Execute command in isolated environment
172
+ * @param {string} command - Command to execute
173
+ * @param {Array} args - Command arguments
174
+ * @param {Object} options - Execution options
175
+ * @returns {Promise<Object>} Execution result
176
+ */
177
+ async execute(command, args = [], options = {}) {
178
+ if (!this.isSetup) {
179
+ throw new Error('Test environment not setup. Call setup() first.');
180
+ }
181
+
182
+ return new Promise((resolve) => {
183
+ let stdout = '';
184
+ let stderr = '';
185
+
186
+ const child = spawn(command, args, {
187
+ stdio: ['ignore', 'pipe', 'pipe'],
188
+ shell: true,
189
+ env: process.env, // Use modified environment
190
+ ...options
191
+ });
192
+
193
+ child.stdout?.on('data', (data) => {
194
+ stdout += data.toString();
195
+ });
196
+
197
+ child.stderr?.on('data', (data) => {
198
+ stderr += data.toString();
199
+ });
200
+
201
+ child.on('close', (code) => {
202
+ resolve({
203
+ success: code === 0,
204
+ stdout,
205
+ stderr,
206
+ exitCode: code
207
+ });
208
+ });
209
+
210
+ child.on('error', (error) => {
211
+ resolve({
212
+ success: false,
213
+ stdout,
214
+ stderr,
215
+ error: error.message
216
+ });
217
+ });
218
+ });
219
+ }
220
+
221
+ /**
222
+ * Create test data in environment
223
+ * @param {Object} testData - Test data structure
224
+ */
225
+ createTestData(testData = {}) {
226
+ for (const [filePath, content] of Object.entries(testData)) {
227
+ const fullPath = path.join(this.testHome, filePath);
228
+ const dir = path.dirname(fullPath);
229
+
230
+ // Create directory if it doesn't exist
231
+ if (!fs.existsSync(dir)) {
232
+ fs.mkdirSync(dir, { recursive: true });
233
+ }
234
+
235
+ // Write file
236
+ if (typeof content === 'object') {
237
+ fs.writeFileSync(fullPath, JSON.stringify(content, null, 2), 'utf8');
238
+ } else {
239
+ fs.writeFileSync(fullPath, content, 'utf8');
240
+ }
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Restore original environment
246
+ */
247
+ restore() {
248
+ if (!this.isSetup) return;
249
+
250
+ // Restore environment variables
251
+ Object.keys(this.originalEnv).forEach(key => {
252
+ process.env[key] = this.originalEnv[key];
253
+ });
254
+
255
+ // Remove any additions
256
+ const addedKeys = Object.keys(process.env).filter(key => !this.originalEnv.hasOwnProperty(key));
257
+ addedKeys.forEach(key => {
258
+ delete process.env[key];
259
+ });
260
+
261
+ this.isSetup = false;
262
+ }
263
+
264
+ /**
265
+ * Cleanup test environment
266
+ */
267
+ async cleanup() {
268
+ this.restore();
269
+
270
+ try {
271
+ if (fs.existsSync(this.baseDir)) {
272
+ fs.rmSync(this.baseDir, { recursive: true, force: true });
273
+ }
274
+ } catch (error) {
275
+ console.warn(`Warning: Could not cleanup test directory: ${error.message}`);
276
+ }
277
+ }
278
+
279
+ /**
280
+ * Get path in test environment
281
+ * @param {...string} parts - Path parts
282
+ * @returns {string} Full path in test environment
283
+ */
284
+ getPath(...parts) {
285
+ return path.join(this.testHome, ...parts);
286
+ }
287
+ }
288
+
289
+ module.exports = TestEnvironment;