stigmergy 1.2.0 → 1.2.8
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/LICENSE +18 -18
- package/README.md +31 -211
- package/STIGMERGY.md +70 -61
- package/docs/MULTI_USER_WIKI_COLLABORATION_SYSTEM.md +523 -0
- package/docs/PROJECT_CONSTITUTION.md +433 -433
- package/docs/PROJECT_STRUCTURE_CURRENT.md +80 -80
- 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/calculator-example.js +72 -72
- package/examples/cline_usage_examples.md +364 -364
- package/examples/encryption-example.js +67 -67
- package/examples/json-parser-example.js +120 -120
- package/examples/json-validation-example.js +64 -64
- package/examples/multilingual-hook-demo.js +125 -0
- package/examples/rest-client-example.js +52 -52
- package/examples/rest_client_example.js +54 -54
- package/package.json +38 -20
- package/scripts/build.js +74 -74
- package/scripts/dependency-analyzer.js +101 -0
- package/scripts/generate-cli-docs.js +64 -0
- package/scripts/post-deployment-config.js +296 -296
- package/scripts/postuninstall.js +46 -0
- package/scripts/preinstall-check.js +173 -173
- package/scripts/preuninstall.js +75 -0
- package/scripts/publish.js +58 -268
- package/scripts/run-layered-tests.js +247 -0
- package/scripts/safe-install.js +139 -139
- package/scripts/simple-publish.js +57 -59
- package/src/adapters/claude/install_claude_integration.js +292 -0
- package/src/adapters/codebuddy/install_codebuddy_integration.js +349 -0
- package/src/adapters/codex/install_codex_integration.js +395 -0
- package/src/adapters/copilot/install_copilot_integration.js +716 -0
- package/src/adapters/gemini/install_gemini_integration.js +304 -0
- package/src/adapters/iflow/install_iflow_integration.js +304 -0
- package/src/adapters/qoder/install_qoder_integration.js +1090 -0
- package/src/adapters/qwen/install_qwen_integration.js +285 -0
- package/src/cli/router.js +562 -39
- package/src/core/cache_cleaner.js +82 -59
- package/src/core/cli_help_analyzer.js +297 -291
- package/src/core/cli_parameter_handler.js +5 -0
- package/src/core/cli_tools.js +6 -6
- package/src/core/coordination/index.js +2 -2
- package/src/core/coordination/nodejs/AdapterManager.js +30 -17
- package/src/core/coordination/nodejs/CLCommunication.js +28 -20
- package/src/core/coordination/nodejs/CLIIntegrationManager.js +72 -36
- package/src/core/coordination/nodejs/HealthChecker.js +13 -14
- package/src/core/coordination/nodejs/HookDeploymentManager.js +325 -63
- package/src/core/coordination/nodejs/StatisticsCollector.js +6 -6
- package/src/core/coordination/nodejs/index.js +29 -11
- package/src/core/coordination/nodejs/utils/Logger.js +1 -1
- package/src/core/enhanced_installer.js +92 -69
- package/src/core/enhanced_uninstaller.js +73 -53
- package/src/core/installer.js +815 -294
- package/src/core/multilingual/language-pattern-manager.js +172 -0
- package/src/core/smart_router.js +141 -26
- package/src/core/upgrade_manager.js +91 -46
- package/src/data_structures.js +1 -1
- package/src/deploy.js +2 -2
- package/src/index.js +3 -3
- package/src/test/cli-availability-checker.js +194 -0
- package/src/test/test-environment.js +289 -0
- package/src/utils/helpers.js +2 -2
- package/src/utils.js +7 -1
- 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/test/cache-cleaner-implemented.test.js +0 -328
- package/test/cache-cleaner.test.js +0 -390
- package/test/calculator.test.js +0 -215
- package/test/collision-test.js +0 -26
- package/test/comprehensive-enhanced-features.test.js +0 -252
- package/test/comprehensive-execution-test.js +0 -428
- package/test/conflict-prevention-test.js +0 -95
- package/test/cross-cli-detection-test.js +0 -33
- package/test/csv-processing-test.js +0 -36
- package/test/deploy-hooks-test.js +0 -250
- package/test/e2e/claude-cli-test.js +0 -128
- package/test/e2e/collaboration-test.js +0 -75
- package/test/e2e/comprehensive-test.js +0 -431
- package/test/e2e/error-handling-test.js +0 -90
- package/test/e2e/individual-tool-test.js +0 -143
- package/test/e2e/other-cli-test.js +0 -130
- package/test/e2e/qoder-cli-test.js +0 -128
- package/test/e2e/run-e2e-tests.js +0 -73
- package/test/e2e/test-data.js +0 -88
- package/test/e2e/test-utils.js +0 -222
- package/test/encryption-simple-test.js +0 -110
- package/test/encryption.test.js +0 -129
- package/test/enhanced-main-alignment.test.js +0 -298
- package/test/enhanced-uninstaller-implemented.test.js +0 -271
- package/test/enhanced-uninstaller.test.js +0 -284
- package/test/error-handling-test.js +0 -341
- package/test/fibonacci.test.js +0 -178
- package/test/final-deploy-test.js +0 -221
- package/test/final-install-test.js +0 -226
- package/test/hash-table-demo.js +0 -33
- package/test/hash-table-test.js +0 -26
- package/test/hash_table_test.js +0 -114
- package/test/hook-system-integration-test.js +0 -307
- package/test/iflow-integration-test.js +0 -292
- package/test/improved-install-test.js +0 -362
- package/test/install-command-test.js +0 -370
- package/test/json-parser-test.js +0 -161
- package/test/json-validation-test.js +0 -164
- package/test/natural-language-skills-test.js +0 -320
- package/test/nl-integration-test.js +0 -179
- package/test/parameter-parsing-test.js +0 -143
- package/test/plugin-deployment-test.js +0 -316
- package/test/postinstall-test.js +0 -269
- package/test/python-plugins-test.js +0 -259
- package/test/real-test.js +0 -435
- package/test/remaining-adapters-test.js +0 -256
- package/test/rest-client-test.js +0 -56
- package/test/rest_client.test.js +0 -85
- package/test/safe-installation-cleaner.test.js +0 -343
- package/test/simple-iflow-hook-test.js +0 -137
- package/test/stigmergy-upgrade-test.js +0 -243
- package/test/system-compatibility-test.js +0 -467
- package/test/tdd-deploy-fix-test.js +0 -324
- package/test/tdd-fixes-test.js +0 -211
- package/test/third-party-skills-test.js +0 -321
- package/test/tool-selection-integration-test.js +0 -158
- package/test/unit/calculator-full.test.js +0 -191
- package/test/unit/calculator-simple.test.js +0 -96
- package/test/unit/calculator.test.js +0 -97
- package/test/unit/cli-scanner.test.js +0 -291
- package/test/unit/cli_parameter_handler.test.js +0 -116
- package/test/unit/cross-cli-executor.test.js +0 -399
- package/test/weather-processor.test.js +0 -104
|
@@ -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;
|
package/src/utils/helpers.js
CHANGED
package/src/utils.js
CHANGED
|
@@ -678,6 +678,12 @@ async function executeCommand(command, args = [], options = {}) {
|
|
|
678
678
|
|
|
679
679
|
const child = spawn(command, args, opts);
|
|
680
680
|
|
|
681
|
+
// Add debug logging for Windows command execution
|
|
682
|
+
if (process.platform === 'win32' && process.env.DEBUG === 'true') {
|
|
683
|
+
console.log(`[DEBUG] Spawned process with command: ${command}`);
|
|
684
|
+
console.log(`[DEBUG] Spawned process with args: ${JSON.stringify(args)}`);
|
|
685
|
+
}
|
|
686
|
+
|
|
681
687
|
let stdout = '';
|
|
682
688
|
let stderr = '';
|
|
683
689
|
|
|
@@ -912,4 +918,4 @@ module.exports = {
|
|
|
912
918
|
encryptData,
|
|
913
919
|
decryptData,
|
|
914
920
|
generateKey,
|
|
915
|
-
};
|
|
921
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// test/multilingual/hook-deployment.test.js
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const HookDeploymentManager = require('../../src/core/coordination/nodejs/HookDeploymentManager');
|
|
7
|
+
|
|
8
|
+
describe('HookDeploymentManager Multilingual Support', () => {
|
|
9
|
+
let deploymentManager;
|
|
10
|
+
let tempDir;
|
|
11
|
+
|
|
12
|
+
beforeAll(() => {
|
|
13
|
+
// Create a temporary directory for testing
|
|
14
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'stigmergy-test-'));
|
|
15
|
+
deploymentManager = new HookDeploymentManager();
|
|
16
|
+
deploymentManager.deploymentDir = tempDir;
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterAll(() => {
|
|
20
|
+
// Clean up temporary directory
|
|
21
|
+
if (fs.existsSync(tempDir)) {
|
|
22
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('should initialize deployment manager', async () => {
|
|
27
|
+
await expect(deploymentManager.initialize()).resolves.toBeUndefined();
|
|
28
|
+
expect(fs.existsSync(tempDir)).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('should deploy hooks with multilingual support', async () => {
|
|
32
|
+
const cliName = 'claude';
|
|
33
|
+
const result = await deploymentManager.deployHooksForCLI(cliName);
|
|
34
|
+
expect(result).toBe(true);
|
|
35
|
+
|
|
36
|
+
// Check that the hook file was created
|
|
37
|
+
const hookDir = path.join(tempDir, cliName);
|
|
38
|
+
const hookFile = path.join(hookDir, `${cliName}_nodejs_hook.js`);
|
|
39
|
+
const configFile = path.join(hookDir, 'config.json');
|
|
40
|
+
|
|
41
|
+
expect(fs.existsSync(hookDir)).toBe(true);
|
|
42
|
+
expect(fs.existsSync(hookFile)).toBe(true);
|
|
43
|
+
expect(fs.existsSync(configFile)).toBe(true);
|
|
44
|
+
|
|
45
|
+
// Check that the hook file contains multilingual support
|
|
46
|
+
const hookContent = fs.readFileSync(hookFile, 'utf8');
|
|
47
|
+
expect(hookContent).toContain('LanguagePatternManager');
|
|
48
|
+
expect(hookContent).toContain('detectCrossCLIRequest');
|
|
49
|
+
|
|
50
|
+
// Check that the hook file contains patterns for multiple languages
|
|
51
|
+
expect(hookContent).toContain('请用');
|
|
52
|
+
expect(hookContent).toContain('使って');
|
|
53
|
+
expect(hookContent).toContain('benutze');
|
|
54
|
+
expect(hookContent).toContain('utilise');
|
|
55
|
+
expect(hookContent).toContain('usa');
|
|
56
|
+
expect(hookContent).toContain('usa'); // Italian
|
|
57
|
+
expect(hookContent).toContain('use'); // Portuguese
|
|
58
|
+
expect(hookContent).toContain('используй'); // Russian
|
|
59
|
+
expect(hookContent).toContain('استخدم'); // Arabic
|
|
60
|
+
expect(hookContent).toContain('kullanarak'); // Turkish
|
|
61
|
+
expect(hookContent).toContain('utiliser'); // French
|
|
62
|
+
expect(hookContent).toContain('usar'); // Spanish
|
|
63
|
+
expect(hookContent).toContain('utilizzare'); // Italian
|
|
64
|
+
expect(hookContent).toContain('verwenden'); // German
|
|
65
|
+
expect(hookContent).toContain('使う'); // Japanese
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('should validate hook deployment', async () => {
|
|
69
|
+
const cliName = 'gemini';
|
|
70
|
+
await deploymentManager.deployHooksForCLI(cliName);
|
|
71
|
+
|
|
72
|
+
const validationResult = await deploymentManager.validateHookDeployment(cliName);
|
|
73
|
+
expect(validationResult.valid).toBe(true);
|
|
74
|
+
expect(validationResult.message).toBe('Hook deployment is valid');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('should list deployed hooks', async () => {
|
|
78
|
+
const hooks = await deploymentManager.listDeployedHooks();
|
|
79
|
+
expect(Array.isArray(hooks)).toBe(true);
|
|
80
|
+
expect(hooks.length).toBeGreaterThanOrEqual(1);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('should undeploy hooks', async () => {
|
|
84
|
+
const cliName = 'claude';
|
|
85
|
+
const result = await deploymentManager.undeployHooksForCLI(cliName);
|
|
86
|
+
expect(result).toBe(true);
|
|
87
|
+
|
|
88
|
+
const hookDir = path.join(tempDir, cliName);
|
|
89
|
+
expect(fs.existsSync(hookDir)).toBe(false);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// test/multilingual/language-pattern-manager.test.js
|
|
2
|
+
|
|
3
|
+
const LanguagePatternManager = require('../../src/core/multilingual/language-pattern-manager');
|
|
4
|
+
|
|
5
|
+
describe('LanguagePatternManager', () => {
|
|
6
|
+
let manager;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
manager = new LanguagePatternManager();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test('should load language patterns correctly', () => {
|
|
13
|
+
expect(manager.languagePatterns).toBeDefined();
|
|
14
|
+
expect(Object.keys(manager.languagePatterns).length).toBeGreaterThan(0);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('should detect English patterns', () => {
|
|
18
|
+
const result = manager.detectCrossCLIRequest('use claude to write code');
|
|
19
|
+
expect(result).toBeDefined();
|
|
20
|
+
expect(result.targetCLI).toBe('claude');
|
|
21
|
+
expect(result.task).toBe('write code');
|
|
22
|
+
expect(result.language).toBe('en');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('should detect Chinese patterns', () => {
|
|
26
|
+
const result = manager.detectCrossCLIRequest('请用copilot帮我创建React组件');
|
|
27
|
+
expect(result).toBeDefined();
|
|
28
|
+
expect(result.targetCLI).toBe('copilot');
|
|
29
|
+
expect(result.task).toBe('创建React组件');
|
|
30
|
+
expect(result.language).toBe('zh');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('should detect Japanese patterns', () => {
|
|
34
|
+
const result = manager.detectCrossCLIRequest('claudeを使ってコードを書いて関数を作成');
|
|
35
|
+
expect(result).toBeDefined();
|
|
36
|
+
expect(result.targetCLI).toBe('claude');
|
|
37
|
+
expect(result.task).toBe('関数を作成');
|
|
38
|
+
expect(result.language).toBe('ja');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('should detect Korean patterns', () => {
|
|
42
|
+
const result = manager.detectCrossCLIRequest('claude로 코드 작성해 줘함수를 만들어');
|
|
43
|
+
expect(result).toBeDefined();
|
|
44
|
+
expect(result.targetCLI).toBe('claude');
|
|
45
|
+
expect(result.task).toBe('함수를 만들어');
|
|
46
|
+
expect(result.language).toBe('ko');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('should detect German patterns', () => {
|
|
50
|
+
const result = manager.detectCrossCLIRequest('benutze claude um code zu schreiben');
|
|
51
|
+
expect(result).toBeDefined();
|
|
52
|
+
expect(result.targetCLI).toBe('claude');
|
|
53
|
+
expect(result.task).toBe('code zu schreiben');
|
|
54
|
+
expect(result.language).toBe('de');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('should detect French patterns', () => {
|
|
58
|
+
const result = manager.detectCrossCLIRequest('utilise claude pour écrire du code');
|
|
59
|
+
expect(result).toBeDefined();
|
|
60
|
+
expect(result.targetCLI).toBe('claude');
|
|
61
|
+
expect(result.task).toBe('écrire du code');
|
|
62
|
+
expect(result.language).toBe('fr');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('should detect Spanish patterns', () => {
|
|
66
|
+
const result = manager.detectCrossCLIRequest('usa claude para escribir código');
|
|
67
|
+
expect(result).toBeDefined();
|
|
68
|
+
expect(result.targetCLI).toBe('claude');
|
|
69
|
+
expect(result.task).toBe('escribir código');
|
|
70
|
+
expect(result.language).toBe('es');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('should detect Italian patterns', () => {
|
|
74
|
+
const result = manager.detectCrossCLIRequest('usa claude per scrivere codice');
|
|
75
|
+
expect(result).toBeDefined();
|
|
76
|
+
expect(result.targetCLI).toBe('claude');
|
|
77
|
+
expect(result.task).toBe('scrivere codice');
|
|
78
|
+
expect(result.language).toBe('it');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('should detect Portuguese patterns', () => {
|
|
82
|
+
const result = manager.detectCrossCLIRequest('use claude para escrever código');
|
|
83
|
+
expect(result).toBeDefined();
|
|
84
|
+
expect(result.targetCLI).toBe('claude');
|
|
85
|
+
expect(result.task).toBe('escrever código');
|
|
86
|
+
expect(result.language).toBe('pt');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('should detect Russian patterns', () => {
|
|
90
|
+
const result = manager.detectCrossCLIRequest('используй claude чтобы написать код');
|
|
91
|
+
expect(result).toBeDefined();
|
|
92
|
+
expect(result.targetCLI).toBe('claude');
|
|
93
|
+
expect(result.task).toBe('написать код');
|
|
94
|
+
expect(result.language).toBe('ru');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('should detect Arabic patterns', () => {
|
|
98
|
+
const result = manager.detectCrossCLIRequest('استخدم claude لكتابة الكود');
|
|
99
|
+
expect(result).toBeDefined();
|
|
100
|
+
expect(result.targetCLI).toBe('claude');
|
|
101
|
+
expect(result.task).toBe('كتابة الكود');
|
|
102
|
+
expect(result.language).toBe('ar');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('should detect Turkish patterns', () => {
|
|
106
|
+
const result = manager.detectCrossCLIRequest('claude kullanarak kod yaz bir fonksiyon');
|
|
107
|
+
expect(result).toBeDefined();
|
|
108
|
+
expect(result.targetCLI).toBe('claude');
|
|
109
|
+
expect(result.task).toBe('kod yaz bir fonksiyon');
|
|
110
|
+
expect(result.language).toBe('tr');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('should return null for unsupported CLI tools', () => {
|
|
114
|
+
const result = manager.detectCrossCLIRequest('use nonexistenttool to do something');
|
|
115
|
+
expect(result).toBeNull();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('should return null for non-cross-CLI requests', () => {
|
|
119
|
+
const result = manager.detectCrossCLIRequest('just a regular prompt');
|
|
120
|
+
expect(result).toBeNull();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test('should get patterns for specific language', () => {
|
|
124
|
+
const patterns = manager.getPatterns('en');
|
|
125
|
+
expect(Array.isArray(patterns)).toBe(true);
|
|
126
|
+
expect(patterns.length).toBeGreaterThan(0);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test('should get all patterns', () => {
|
|
130
|
+
const allPatterns = manager.getAllPatterns();
|
|
131
|
+
expect(allPatterns).toBeDefined();
|
|
132
|
+
expect(Object.keys(allPatterns).length).toBeGreaterThan(0);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test('should detect language from environment', () => {
|
|
136
|
+
const language = manager.detectLanguage();
|
|
137
|
+
expect(typeof language).toBe('string');
|
|
138
|
+
expect(language.length).toBeGreaterThan(0);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Simple test to verify the multilingual hook system works
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
console.log('Testing Multilingual Hook System...\n');
|
|
11
|
+
|
|
12
|
+
// Check if required files exist
|
|
13
|
+
const requiredFiles = [
|
|
14
|
+
'../../src/core/multilingual/language-pattern-manager.js',
|
|
15
|
+
'../../.qoder/specs/multilingual-hook-system/language-patterns/english.js',
|
|
16
|
+
'../../.qoder/specs/multilingual-hook-system/language-patterns/chinese.js',
|
|
17
|
+
'../../.qoder/specs/multilingual-hook-system/language-patterns/japanese.js',
|
|
18
|
+
'../../.qoder/specs/multilingual-hook-system/language-patterns/korean.js',
|
|
19
|
+
'../../.qoder/specs/multilingual-hook-system/language-patterns/german.js',
|
|
20
|
+
'../../.qoder/specs/multilingual-hook-system/language-patterns/french.js',
|
|
21
|
+
'../../.qoder/specs/multilingual-hook-system/language-patterns/spanish.js',
|
|
22
|
+
'../../.qoder/specs/multilingual-hook-system/language-patterns/italian.js',
|
|
23
|
+
'../../.qoder/specs/multilingual-hook-system/language-patterns/portuguese.js',
|
|
24
|
+
'../../.qoder/specs/multilingual-hook-system/language-patterns/russian.js',
|
|
25
|
+
'../../.qoder/specs/multilingual-hook-system/language-patterns/arabic.js',
|
|
26
|
+
'../../.qoder/specs/multilingual-hook-system/language-patterns/turkish.js'
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
let allFilesExist = true;
|
|
30
|
+
|
|
31
|
+
requiredFiles.forEach(file => {
|
|
32
|
+
const fullPath = path.join(__dirname, file);
|
|
33
|
+
if (fs.existsSync(fullPath)) {
|
|
34
|
+
console.log(`✓ ${file} exists`);
|
|
35
|
+
} else {
|
|
36
|
+
console.log(`✗ ${file} missing`);
|
|
37
|
+
allFilesExist = false;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (allFilesExist) {
|
|
42
|
+
console.log('\n✓ All required files are present');
|
|
43
|
+
|
|
44
|
+
// Try to load the LanguagePatternManager
|
|
45
|
+
try {
|
|
46
|
+
const LanguagePatternManager = require('../../src/core/multilingual/language-pattern-manager');
|
|
47
|
+
const manager = new LanguagePatternManager();
|
|
48
|
+
|
|
49
|
+
console.log('✓ LanguagePatternManager loaded successfully');
|
|
50
|
+
|
|
51
|
+
// Test English pattern matching
|
|
52
|
+
const englishResult = manager.detectCrossCLIRequest('use claude to write code');
|
|
53
|
+
if (englishResult && englishResult.targetCLI === 'claude') {
|
|
54
|
+
console.log('✓ English pattern matching works');
|
|
55
|
+
} else {
|
|
56
|
+
console.log('✗ English pattern matching failed');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Test Chinese pattern matching
|
|
60
|
+
const chineseResult = manager.detectCrossCLIRequest('请用copilot帮我创建React组件');
|
|
61
|
+
if (chineseResult && chineseResult.targetCLI === 'copilot') {
|
|
62
|
+
console.log('✓ Chinese pattern matching works');
|
|
63
|
+
} else {
|
|
64
|
+
console.log('✗ Chinese pattern matching failed');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Test Japanese pattern matching
|
|
68
|
+
const japaneseResult = manager.detectCrossCLIRequest('claudeを使ってコードを書いて関数を作成');
|
|
69
|
+
if (japaneseResult && japaneseResult.targetCLI === 'claude') {
|
|
70
|
+
console.log('✓ Japanese pattern matching works');
|
|
71
|
+
} else {
|
|
72
|
+
console.log('✗ Japanese pattern matching failed');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
console.log('\n🎉 Multilingual Hook System is ready!');
|
|
76
|
+
console.log('\nTo deploy multilingual hooks, run:');
|
|
77
|
+
console.log(' stigmergy deploy');
|
|
78
|
+
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.log(`✗ Failed to load LanguagePatternManager: ${error.message}`);
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
console.log('\n✗ Some required files are missing');
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|