stigmergy 1.2.6 → 1.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -20
- package/STIGMERGY.md +26 -7
- package/docs/MULTI_USER_WIKI_COLLABORATION_SYSTEM.md +523 -0
- package/docs/PROMPT_BASED_SKILLS_SYSTEM_DESIGN.md +458 -0
- package/docs/SKILL_IMPLEMENTATION_CONSTRAINTS_AND_ALIGNMENT.md +423 -0
- package/docs/TECHNICAL_FEASIBILITY_ANALYSIS.md +308 -0
- package/examples/multilingual-hook-demo.js +125 -0
- package/package.json +30 -19
- package/scripts/dependency-analyzer.js +101 -0
- package/scripts/generate-cli-docs.js +64 -0
- package/scripts/postuninstall.js +46 -0
- package/scripts/preuninstall.js +85 -0
- package/scripts/run-layered-tests.js +3 -3
- package/src/adapters/claude/install_claude_integration.js +37 -37
- package/src/adapters/codebuddy/install_codebuddy_integration.js +66 -63
- package/src/adapters/codex/install_codex_integration.js +54 -55
- package/src/adapters/copilot/install_copilot_integration.js +46 -46
- package/src/adapters/gemini/install_gemini_integration.js +68 -68
- package/src/adapters/iflow/install_iflow_integration.js +77 -77
- package/src/adapters/qoder/install_qoder_integration.js +76 -76
- package/src/adapters/qwen/install_qwen_integration.js +23 -23
- package/src/cli/router.js +713 -163
- package/src/commands/skill-bridge.js +39 -0
- package/src/commands/skill-handler.js +150 -0
- package/src/commands/skill.js +127 -0
- package/src/core/cache_cleaner.js +767 -767
- package/src/core/cli_help_analyzer.js +680 -680
- package/src/core/cli_parameter_handler.js +132 -132
- package/src/core/cli_path_detector.js +573 -0
- package/src/core/cli_tools.js +160 -89
- package/src/core/coordination/index.js +16 -16
- package/src/core/coordination/nodejs/AdapterManager.js +130 -102
- package/src/core/coordination/nodejs/CLCommunication.js +132 -132
- package/src/core/coordination/nodejs/CLIIntegrationManager.js +272 -272
- package/src/core/coordination/nodejs/HealthChecker.js +76 -76
- package/src/core/coordination/nodejs/HookDeploymentManager.js +463 -274
- package/src/core/coordination/nodejs/StatisticsCollector.js +71 -71
- package/src/core/coordination/nodejs/index.js +90 -90
- package/src/core/coordination/nodejs/utils/Logger.js +29 -29
- package/src/core/directory_permission_manager.js +568 -0
- package/src/core/enhanced_cli_installer.js +609 -0
- package/src/core/error_handler.js +406 -406
- package/src/core/installer.js +263 -119
- package/src/core/memory_manager.js +83 -83
- package/src/core/multilingual/language-pattern-manager.js +200 -0
- package/src/core/persistent_shell_configurator.js +468 -0
- package/src/core/rest_client.js +160 -160
- package/src/core/skills/StigmergySkillManager.js +357 -0
- package/src/core/skills/__tests__/SkillInstaller.test.js +275 -0
- package/src/core/skills/__tests__/SkillParser.test.js +202 -0
- package/src/core/skills/__tests__/SkillReader.test.js +189 -0
- package/src/core/skills/cli-command-test.js +201 -0
- package/src/core/skills/comprehensive-e2e-test.js +473 -0
- package/src/core/skills/e2e-test.js +267 -0
- package/src/core/skills/embedded-openskills/SkillInstaller.js +438 -0
- package/src/core/skills/embedded-openskills/SkillParser.js +123 -0
- package/src/core/skills/embedded-openskills/SkillReader.js +143 -0
- package/src/core/skills/integration-test.js +248 -0
- package/src/core/skills/package.json +6 -0
- package/src/core/skills/regression-test.js +285 -0
- package/src/core/skills/run-all-tests.js +129 -0
- package/src/core/skills/sync-test.js +210 -0
- package/src/core/skills/test-runner.js +242 -0
- package/src/core/smart_router.js +261 -249
- package/src/core/upgrade_manager.js +48 -20
- package/src/index.js +30 -30
- package/src/test/cli-availability-checker.js +194 -194
- package/src/test/test-environment.js +289 -289
- package/src/utils/helpers.js +18 -35
- package/src/utils.js +921 -921
- package/src/weatherProcessor.js +228 -228
- package/test/multilingual/hook-deployment.test.js +91 -0
- package/test/multilingual/language-pattern-manager.test.js +140 -0
- package/test/multilingual/system-test.js +85 -0
- package/src/auth.js +0 -173
- package/src/auth_command.js +0 -208
- package/src/calculator.js +0 -313
- package/src/core/enhanced_installer.js +0 -479
- package/src/core/enhanced_uninstaller.js +0 -638
- package/src/data_encryption.js +0 -143
- package/src/data_structures.js +0 -440
- package/src/deploy.js +0 -55
|
@@ -1,289 +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 +=
|
|
135
|
-
script += ` echo "${config.version}"\n`;
|
|
136
|
-
script +=
|
|
137
|
-
script +=
|
|
138
|
-
|
|
139
|
-
// Handle help command
|
|
140
|
-
script +=
|
|
141
|
-
script += ` echo "Mock ${toolName} help"\n`;
|
|
142
|
-
script += ` echo "Usage: ${toolName} [options]"\n`;
|
|
143
|
-
script +=
|
|
144
|
-
script +=
|
|
145
|
-
|
|
146
|
-
// Handle prompt flag
|
|
147
|
-
script +=
|
|
148
|
-
script +=
|
|
149
|
-
script +=
|
|
150
|
-
script +=
|
|
151
|
-
script +=
|
|
152
|
-
script +=
|
|
153
|
-
script +=
|
|
154
|
-
script +=
|
|
155
|
-
|
|
156
|
-
// Generate response
|
|
157
|
-
script +=
|
|
158
|
-
script +=
|
|
159
|
-
script += ` echo "${config.output}"\n`;
|
|
160
|
-
script +=
|
|
161
|
-
script += ` echo "${config.output}"\n`;
|
|
162
|
-
script +=
|
|
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;
|
|
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;
|
package/src/utils/helpers.js
CHANGED
|
@@ -1,35 +1,18 @@
|
|
|
1
|
-
const fs = require('fs/promises');
|
|
2
|
-
const fsSync = require('fs');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (!fsSync.existsSync(authFile)) {
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const authData = JSON.parse(fsSync.readFileSync(authFile, 'utf8'));
|
|
25
|
-
return authenticator.validateToken(authData.token);
|
|
26
|
-
} catch (error) {
|
|
27
|
-
console.log(`[AUTH] Authentication check failed: ${error.message}`);
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
module.exports = {
|
|
33
|
-
maxOfTwo,
|
|
34
|
-
isAuthenticated,
|
|
35
|
-
};
|
|
1
|
+
const fs = require('fs/promises');
|
|
2
|
+
const fsSync = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
function maxOfTwo(a, b) {
|
|
6
|
+
return a > b ? a : b;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Placeholder function - always returns true since no user authentication is needed
|
|
10
|
+
// for a multi-AI CLI collaboration system
|
|
11
|
+
function isAuthenticated() {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = {
|
|
16
|
+
maxOfTwo,
|
|
17
|
+
isAuthenticated,
|
|
18
|
+
};
|