stigmergy 1.0.68 → 1.0.70

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 (102) hide show
  1. package/README.en.md +306 -300
  2. package/README.md +469 -301
  3. package/package.json +97 -81
  4. package/scripts/publish.js +268 -0
  5. package/scripts/simple-publish.js +59 -0
  6. package/src/index.js +12 -0
  7. package/test/enhanced-main-alignment.test.js +298 -0
  8. package/test/hook-system-integration-test.js +307 -0
  9. package/test/natural-language-skills-test.js +320 -0
  10. package/test/nl-integration-test.js +179 -0
  11. package/test/parameter-parsing-test.js +143 -0
  12. package/test/real-test.js +435 -0
  13. package/test/system-compatibility-test.js +447 -0
  14. package/test/tdd-fixes-test.js +211 -0
  15. package/test/third-party-skills-test.js +321 -0
  16. package/test/tool-selection-integration-test.js +157 -0
  17. package/test/unit/cli-scanner.test.js +291 -0
  18. package/test/unit/cross-cli-executor.test.js +399 -0
  19. package/src/adapters/claude/__init__.py +0 -13
  20. package/src/adapters/claude/claude_skills_integration.py +0 -609
  21. package/src/adapters/claude/hook_adapter.py +0 -663
  22. package/src/adapters/claude/install_claude_integration.py +0 -265
  23. package/src/adapters/claude/skills_hook_adapter.py +0 -841
  24. package/src/adapters/claude/standalone_claude_adapter.py +0 -384
  25. package/src/adapters/cline/__init__.py +0 -20
  26. package/src/adapters/cline/config.py +0 -108
  27. package/src/adapters/cline/install_cline_integration.py +0 -617
  28. package/src/adapters/cline/mcp_server.py +0 -713
  29. package/src/adapters/cline/standalone_cline_adapter.py +0 -459
  30. package/src/adapters/codebuddy/__init__.py +0 -13
  31. package/src/adapters/codebuddy/buddy_adapter.py +0 -1125
  32. package/src/adapters/codebuddy/install_codebuddy_integration.py +0 -279
  33. package/src/adapters/codebuddy/skills_hook_adapter.py +0 -672
  34. package/src/adapters/codebuddy/skills_integration.py +0 -395
  35. package/src/adapters/codebuddy/standalone_codebuddy_adapter.py +0 -403
  36. package/src/adapters/codex/__init__.py +0 -11
  37. package/src/adapters/codex/base.py +0 -46
  38. package/src/adapters/codex/install_codex_integration.py +0 -311
  39. package/src/adapters/codex/mcp_server.py +0 -493
  40. package/src/adapters/codex/natural_language_parser.py +0 -82
  41. package/src/adapters/codex/slash_command_adapter.py +0 -326
  42. package/src/adapters/codex/standalone_codex_adapter.py +0 -362
  43. package/src/adapters/copilot/__init__.py +0 -13
  44. package/src/adapters/copilot/install_copilot_integration.py +0 -564
  45. package/src/adapters/copilot/mcp_adapter.py +0 -772
  46. package/src/adapters/copilot/mcp_server.py +0 -168
  47. package/src/adapters/copilot/standalone_copilot_adapter.py +0 -114
  48. package/src/adapters/gemini/__init__.py +0 -13
  49. package/src/adapters/gemini/extension_adapter.py +0 -690
  50. package/src/adapters/gemini/install_gemini_integration.py +0 -257
  51. package/src/adapters/gemini/standalone_gemini_adapter.py +0 -366
  52. package/src/adapters/iflow/__init__.py +0 -7
  53. package/src/adapters/iflow/hook_adapter.py +0 -1038
  54. package/src/adapters/iflow/hook_installer.py +0 -536
  55. package/src/adapters/iflow/install_iflow_integration.py +0 -271
  56. package/src/adapters/iflow/official_hook_adapter.py +0 -1272
  57. package/src/adapters/iflow/standalone_iflow_adapter.py +0 -48
  58. package/src/adapters/iflow/workflow_adapter.py +0 -793
  59. package/src/adapters/qoder/hook_installer.py +0 -732
  60. package/src/adapters/qoder/install_qoder_integration.py +0 -265
  61. package/src/adapters/qoder/notification_hook_adapter.py +0 -863
  62. package/src/adapters/qoder/standalone_qoder_adapter.py +0 -48
  63. package/src/adapters/qwen/__init__.py +0 -17
  64. package/src/adapters/qwencode/__init__.py +0 -13
  65. package/src/adapters/qwencode/inheritance_adapter.py +0 -818
  66. package/src/adapters/qwencode/install_qwencode_integration.py +0 -276
  67. package/src/adapters/qwencode/standalone_qwencode_adapter.py +0 -399
  68. package/src/atomic_collaboration_handler.py +0 -461
  69. package/src/cli_collaboration_agent.py +0 -697
  70. package/src/collaboration/hooks.py +0 -315
  71. package/src/core/__init__.py +0 -21
  72. package/src/core/ai_environment_scanner.py +0 -331
  73. package/src/core/base_adapter.py +0 -220
  74. package/src/core/cli_hook_integration.py +0 -406
  75. package/src/core/cross_cli_executor.py +0 -713
  76. package/src/core/cross_cli_mapping.py +0 -1165
  77. package/src/core/cross_platform_encoding.py +0 -365
  78. package/src/core/cross_platform_safe_cli.py +0 -894
  79. package/src/core/direct_cli_executor.py +0 -805
  80. package/src/core/direct_cli_hook_system.py +0 -958
  81. package/src/core/enhanced_init_processor.py +0 -467
  82. package/src/core/graceful_cli_executor.py +0 -912
  83. package/src/core/md_enhancer.py +0 -342
  84. package/src/core/md_generator.py +0 -619
  85. package/src/core/models.py +0 -218
  86. package/src/core/parser.py +0 -108
  87. package/src/core/real_cli_hook_system.py +0 -852
  88. package/src/core/real_cross_cli_system.py +0 -925
  89. package/src/core/verified_cross_cli_system.py +0 -961
  90. package/src/deploy.js +0 -737
  91. package/src/enhanced-main.js +0 -626
  92. package/src/enhanced_deploy.js +0 -303
  93. package/src/enhanced_universal_cli_setup.py +0 -930
  94. package/src/kimi_wrapper.py +0 -104
  95. package/src/main.js +0 -1309
  96. package/src/shell_integration.py +0 -398
  97. package/src/simple-main.js +0 -315
  98. package/src/smart_router_creator.py +0 -323
  99. package/src/universal_cli_setup.py +0 -1289
  100. package/src/utils/__init__.py +0 -12
  101. package/src/utils/cli_detector.py +0 -445
  102. package/src/utils/file_utils.py +0 -246
@@ -0,0 +1,291 @@
1
+ /**
2
+ * TDD: CLI Scanner Unit Tests
3
+ * 测试驱动开发 - 先写测试,再写实现
4
+ * 使用ANSI编码,无Unicode字符
5
+ */
6
+
7
+ const assert = require('assert');
8
+ const path = require('path');
9
+
10
+ // 测试目标类 - 尚未实现
11
+ class CLIScanner {
12
+ constructor() {
13
+ this.scanResults = new Map();
14
+ }
15
+
16
+ async scanForCLI(cliName) {
17
+ throw new Error('Not implemented yet - TDD approach');
18
+ }
19
+
20
+ async detectInstalledCLIs() {
21
+ throw new Error('Not implemented yet - TDD approach');
22
+ }
23
+
24
+ validateCLIExecutable(command) {
25
+ throw new Error('Not implemented yet - TDD approach');
26
+ }
27
+ }
28
+
29
+ describe('CLI Scanner Unit Tests - ANSI Encoding, Node.js First', () => {
30
+ let scanner;
31
+
32
+ beforeEach(() => {
33
+ scanner = new CLIScanner();
34
+ });
35
+
36
+ describe('Basic CLI Detection', () => {
37
+ it('should detect node command is available', async () => {
38
+ // 测试最基础的情况 - node命令应该可用
39
+ const result = await scanner.scanForCLI('node');
40
+
41
+ assert.strictEqual(result.cliName, 'node');
42
+ assert.strictEqual(result.available, true);
43
+ assert.ok(result.version);
44
+ assert.ok(result.path);
45
+ });
46
+
47
+ it('should detect npm command is available', async () => {
48
+ const result = await scanner.scanForCLI('npm');
49
+
50
+ assert.strictEqual(result.cliName, 'npm');
51
+ assert.strictEqual(result.available, true);
52
+ assert.ok(result.version);
53
+ });
54
+
55
+ it('should handle non-existent CLI gracefully', async () => {
56
+ const result = await scanner.scanForCLI('non-existent-cli-12345');
57
+
58
+ assert.strictEqual(result.cliName, 'non-existent-cli-12345');
59
+ assert.strictEqual(result.available, false);
60
+ assert.ok(result.error);
61
+ });
62
+ });
63
+
64
+ describe('AI CLI Detection', () => {
65
+ it('should detect Claude CLI if installed', async () => {
66
+ const result = await scanner.scanForCLI('claude');
67
+
68
+ assert.strictEqual(result.cliName, 'claude');
69
+ assert(typeof result.available === 'boolean');
70
+
71
+ if (result.available) {
72
+ assert.ok(result.version);
73
+ assert.ok(result.path);
74
+ }
75
+ });
76
+
77
+ it('should detect Gemini CLI if installed', async () => {
78
+ const result = await scanner.scanForCLI('gemini');
79
+
80
+ assert.strictEqual(result.cliName, 'gemini');
81
+ assert(typeof result.available === 'boolean');
82
+ });
83
+
84
+ it('should detect Qwen CLI if installed', async () => {
85
+ const result = await scanner.scanForCLI('qwen');
86
+
87
+ assert.strictEqual(result.cliName, 'qwen');
88
+ assert(typeof result.available === 'boolean');
89
+ });
90
+ });
91
+
92
+ describe('CLI Validation', () => {
93
+ it('should validate valid Windows commands', () => {
94
+ if (process.platform === 'win32') {
95
+ assert.ok(scanner.validateCLIExecutable('cmd'));
96
+ assert.ok(scanner.validateCLIExecutable('powershell'));
97
+ assert.ok(scanner.validateCLIExecutable('node'));
98
+ }
99
+ });
100
+
101
+ it('should validate valid Unix commands', () => {
102
+ if (process.platform !== 'win32') {
103
+ assert.ok(scanner.validateCLIExecutable('sh'));
104
+ assert.ok(scanner.validateCLIExecutable('ls'));
105
+ assert.ok(scanner.validateCLIExecutable('node'));
106
+ }
107
+ });
108
+
109
+ it('should reject invalid commands', () => {
110
+ assert.strictEqual(scanner.validateCLIExecutable(''), false);
111
+ assert.strictEqual(scanner.validateCLIExecutable('non-existent-command-12345'), false);
112
+ });
113
+ });
114
+
115
+ describe('Batch Detection', () => {
116
+ it('should detect all basic development tools', async () => {
117
+ const tools = ['node', 'npm', 'git'];
118
+ const results = await scanner.detectInstalledCLIs(tools);
119
+
120
+ assert.ok(results.size >= 2); // 至少node和npm应该可用
121
+ assert.ok(results.has('node'));
122
+ assert.ok(results.has('npm'));
123
+
124
+ // node和npm都应该可用
125
+ assert.strictEqual(results.get('node').available, true);
126
+ assert.strictEqual(results.get('npm').available, true);
127
+ });
128
+
129
+ it('should handle mixed available/unavailable tools', async () => {
130
+ const tools = ['node', 'non-existent-cli', 'npm'];
131
+ const results = await scanner.detectInstalledCLIs(tools);
132
+
133
+ assert.strictEqual(results.size, 3);
134
+ assert.ok(results.has('node'));
135
+ assert.ok(results.has('npm'));
136
+ assert.ok(results.has('non-existent-cli'));
137
+
138
+ assert.strictEqual(results.get('node').available, true);
139
+ assert.strictEqual(results.get('npm').available, true);
140
+ assert.strictEqual(results.get('non-existent-cli').available, false);
141
+ });
142
+ });
143
+
144
+ describe('Error Handling', () => {
145
+ it('should handle null input gracefully', async () => {
146
+ try {
147
+ await scanner.scanForCLI(null);
148
+ assert.fail('Should have thrown an error');
149
+ } catch (error) {
150
+ assert.ok(error.message.includes('Invalid CLI name'));
151
+ }
152
+ });
153
+
154
+ it('should handle empty string gracefully', async () => {
155
+ try {
156
+ await scanner.scanForCLI('');
157
+ assert.fail('Should have thrown an error');
158
+ } catch (error) {
159
+ assert.ok(error.message.includes('Invalid CLI name'));
160
+ }
161
+ });
162
+
163
+ it('should handle timeout during scanning', async () => {
164
+ // 测试超时处理
165
+ const timeoutMs = 1000;
166
+ const startTime = Date.now();
167
+
168
+ try {
169
+ await scanner.scanForCLI('sleep-command-that-does-not-exist', { timeout: timeoutMs });
170
+ } catch (error) {
171
+ const elapsed = Date.now() - startTime;
172
+ assert.ok(elapsed < timeoutMs + 1000); // 允许一些误差
173
+ assert.ok(error.message.includes('timeout') || error.message.includes('not found'));
174
+ }
175
+ });
176
+ });
177
+
178
+ describe('Platform Specific Tests', () => {
179
+ it('should work on Windows', async () => {
180
+ if (process.platform === 'win32') {
181
+ const result = await scanner.scanForCLI('node');
182
+ assert.strictEqual(result.available, true);
183
+ assert.ok(result.path.includes('.exe') || result.path.includes('node'));
184
+ }
185
+ });
186
+
187
+ it('should work on Unix-like systems', async () => {
188
+ if (process.platform !== 'win32') {
189
+ const result = await scanner.scanForCLI('node');
190
+ assert.strictEqual(result.available, true);
191
+ }
192
+ });
193
+ });
194
+
195
+ describe('Performance Tests', () => {
196
+ it('should scan multiple CLIs efficiently', async () => {
197
+ const tools = ['node', 'npm', 'git', 'code'];
198
+ const startTime = Date.now();
199
+
200
+ const results = await scanner.detectInstalledCLIs(tools);
201
+
202
+ const elapsed = Date.now() - startTime;
203
+ assert.ok(elapsed < 5000); // 应该在5秒内完成
204
+ assert.strictEqual(results.size, tools.length);
205
+ });
206
+
207
+ it('should cache scan results', async () => {
208
+ const cliName = 'node';
209
+
210
+ // 第一次扫描
211
+ const startTime1 = Date.now();
212
+ const result1 = await scanner.scanForCLI(cliName);
213
+ const time1 = Date.now() - startTime1;
214
+
215
+ // 第二次扫描(应该使用缓存)
216
+ const startTime2 = Date.now();
217
+ const result2 = await scanner.scanForCLI(cliName);
218
+ const time2 = Date.now() - startTime2;
219
+
220
+ assert.deepStrictEqual(result1, result2);
221
+ assert.ok(time2 <= time1); // 缓存应该更快或相等
222
+ });
223
+ });
224
+ });
225
+
226
+ // 运行测试
227
+ if (require.main === module) {
228
+ console.log('Running CLI Scanner Unit Tests...');
229
+
230
+ // 简单的测试运行器
231
+ const testMethods = [
232
+ 'should detect node command is available',
233
+ 'should detect npm command is available',
234
+ 'should handle non-existent CLI gracefully',
235
+ 'should validate valid Windows commands',
236
+ 'should reject invalid commands',
237
+ 'should handle null input gracefully'
238
+ ];
239
+
240
+ (async () => {
241
+ const scanner = new CLIScanner();
242
+ let passed = 0;
243
+ let failed = 0;
244
+
245
+ for (const testName of testMethods) {
246
+ try {
247
+ console.log(`Testing: ${testName}`);
248
+
249
+ if (testName.includes('node')) {
250
+ const result = await scanner.scanForCLI('node');
251
+ if (result.available) {
252
+ console.log(`[PASS] ${testName}`);
253
+ passed++;
254
+ } else {
255
+ console.log(`[FAIL] ${testName} - Node not available`);
256
+ failed++;
257
+ }
258
+ } else if (testName.includes('npm')) {
259
+ const result = await scanner.scanForCLI('npm');
260
+ if (result.available) {
261
+ console.log(`[PASS] ${testName}`);
262
+ passed++;
263
+ } else {
264
+ console.log(`[FAIL] ${testName} - NPM not available`);
265
+ failed++;
266
+ }
267
+ } else if (testName.includes('non-existent')) {
268
+ try {
269
+ await scanner.scanForCLI('non-existent-cli-12345');
270
+ console.log(`[FAIL] ${testName} - Should have thrown error`);
271
+ failed++;
272
+ } catch (error) {
273
+ console.log(`[PASS] ${testName}`);
274
+ passed++;
275
+ }
276
+ } else {
277
+ console.log(`[SKIP] ${testName} - Implementation needed`);
278
+ }
279
+
280
+ } catch (error) {
281
+ console.log(`[ERROR] ${testName} - ${error.message}`);
282
+ failed++;
283
+ }
284
+ }
285
+
286
+ console.log(`\nTest Results: ${passed} passed, ${failed} failed`);
287
+ console.log('Implementation needed for full TDD approach.');
288
+ })();
289
+ }
290
+
291
+ module.exports = CLIScanner;
@@ -0,0 +1,399 @@
1
+ /**
2
+ * TDD: Cross-CLI Executor Unit Tests
3
+ * 测试驱动开发 - 先写测试,再写实现
4
+ * 使用ANSI编码,无Unicode字符,Node.js优先
5
+ */
6
+
7
+ const assert = require('assert');
8
+ const EventEmitter = require('events');
9
+
10
+ // 测试目标类 - 尚未实现
11
+ class CrossCLIExecutor extends EventEmitter {
12
+ constructor() {
13
+ super();
14
+ this.executionHistory = [];
15
+ this.activeExecutions = new Map();
16
+ }
17
+
18
+ async executeCrossCLI(sourceCLI, targetCLI, task, options = {}) {
19
+ throw new Error('Not implemented yet - TDD approach');
20
+ }
21
+
22
+ async executeCommand(command, args, options = {}) {
23
+ throw new Error('Not implemented yet - TDD approach');
24
+ }
25
+
26
+ validateExecutionParams(sourceCLI, targetCLI, task) {
27
+ throw new Error('Not implemented yet - TDD approach');
28
+ }
29
+
30
+ getExecutionHistory() {
31
+ return this.executionHistory;
32
+ }
33
+
34
+ getActiveExecutions() {
35
+ return Array.from(this.activeExecutions.values());
36
+ }
37
+ }
38
+
39
+ describe('Cross-CLI Executor Unit Tests - ANSI Encoding', () => {
40
+ let executor;
41
+
42
+ beforeEach(() => {
43
+ executor = new CrossCLIExecutor();
44
+ });
45
+
46
+ describe('Parameter Validation', () => {
47
+ it('should validate correct execution parameters', () => {
48
+ const result = executor.validateExecutionParams('claude', 'gemini', 'translate this text');
49
+
50
+ assert.strictEqual(result.valid, true);
51
+ assert.strictEqual(result.sourceCLI, 'claude');
52
+ assert.strictEqual(result.targetCLI, 'gemini');
53
+ assert.strictEqual(result.task, 'translate this text');
54
+ });
55
+
56
+ it('should reject empty source CLI', () => {
57
+ const result = executor.validateExecutionParams('', 'gemini', 'test task');
58
+
59
+ assert.strictEqual(result.valid, false);
60
+ assert.ok(result.error.includes('source CLI'));
61
+ });
62
+
63
+ it('should reject empty target CLI', () => {
64
+ const result = executor.validateExecutionParams('claude', '', 'test task');
65
+
66
+ assert.strictEqual(result.valid, false);
67
+ assert.ok(result.error.includes('target CLI'));
68
+ });
69
+
70
+ it('should reject empty task', () => {
71
+ const result = executor.validateExecutionParams('claude', 'gemini', '');
72
+
73
+ assert.strictEqual(result.valid, false);
74
+ assert.ok(result.error.includes('task'));
75
+ });
76
+
77
+ it('should reject same source and target CLI', () => {
78
+ const result = executor.validateExecutionParams('claude', 'claude', 'test task');
79
+
80
+ assert.strictEqual(result.valid, false);
81
+ assert.ok(result.error.includes('same'));
82
+ });
83
+ });
84
+
85
+ describe('Command Execution', () => {
86
+ it('should execute node --version successfully', async () => {
87
+ const result = await executor.executeCommand('node', ['--version'], {
88
+ timeout: 5000
89
+ });
90
+
91
+ assert.strictEqual(result.success, true);
92
+ assert.ok(result.stdout);
93
+ assert.strictEqual(result.exitCode, 0);
94
+ });
95
+
96
+ it('should execute npm --version successfully', async () => {
97
+ const result = await executor.executeCommand('npm', ['--version'], {
98
+ timeout: 5000
99
+ });
100
+
101
+ assert.strictEqual(result.success, true);
102
+ assert.ok(result.stdout);
103
+ assert.strictEqual(result.exitCode, 0);
104
+ });
105
+
106
+ it('should handle non-existent command gracefully', async () => {
107
+ const result = await executor.executeCommand('non-existent-command-12345', [], {
108
+ timeout: 3000
109
+ });
110
+
111
+ assert.strictEqual(result.success, false);
112
+ assert.ok(result.error);
113
+ assert.ok(result.error.includes('not found') || result.error.includes('ENOENT'));
114
+ });
115
+
116
+ it('should handle command timeout', async () => {
117
+ const startTime = Date.now();
118
+ const result = await executor.executeCommand('node', ['-e', 'setTimeout(() => {}, 10000)'], {
119
+ timeout: 2000
120
+ });
121
+ const elapsed = Date.now() - startTime;
122
+
123
+ assert.strictEqual(result.success, false);
124
+ assert.ok(elapsed < 5000); // 应该在超时时间内返回
125
+ assert.ok(result.error.includes('timeout') || result.error.includes('killed'));
126
+ });
127
+
128
+ it('should capture stderr output', async () => {
129
+ const result = await executor.executeCommand('node', ['-e', 'console.error("error message")'], {
130
+ timeout: 5000
131
+ });
132
+
133
+ assert.strictEqual(result.success, true);
134
+ assert.ok(result.stderr.includes('error message'));
135
+ });
136
+ });
137
+
138
+ describe('Cross-CLI Execution', () => {
139
+ it('should execute simple claude to gemini translation', async () => {
140
+ // Mock实际调用,因为真实CLI可能未安装
141
+ const result = await executor.executeCrossCLI('claude', 'gemini', 'translate "hello world" to chinese', {
142
+ dryRun: true, // 干运行模式
143
+ timeout: 5000
144
+ });
145
+
146
+ assert.strictEqual(result.success, true);
147
+ assert.strictEqual(result.sourceCLI, 'claude');
148
+ assert.strictEqual(result.targetCLI, 'gemini');
149
+ assert.ok(result.executionId);
150
+ assert.ok(typeof result.executionTime === 'number');
151
+ });
152
+
153
+ it('should handle execution errors gracefully', async () => {
154
+ const result = await executor.executeCrossCLI('non-existent', 'gemini', 'test task', {
155
+ timeout: 3000
156
+ });
157
+
158
+ assert.strictEqual(result.success, false);
159
+ assert.ok(result.error);
160
+ });
161
+
162
+ it('should prevent duplicate executions', async () => {
163
+ const task = 'test duplicate task';
164
+ const options = {
165
+ preventDuplicate: true,
166
+ timeout: 5000
167
+ };
168
+
169
+ const result1 = await executor.executeCrossCLI('claude', 'gemini', task, options);
170
+ const result2 = await executor.executeCrossCLI('claude', 'gemini', task, options);
171
+
172
+ assert.strictEqual(result1.success, true);
173
+ assert.strictEqual(result2.duplicate, true);
174
+ assert.strictEqual(result2.executionId, result1.executionId);
175
+ });
176
+ });
177
+
178
+ describe('Execution History Management', () => {
179
+ it('should track execution history', async () => {
180
+ await executor.executeCommand('node', ['--version']);
181
+
182
+ const history = executor.getExecutionHistory();
183
+ assert.ok(history.length > 0);
184
+
185
+ const lastExecution = history[history.length - 1];
186
+ assert.strictEqual(lastExecution.command, 'node');
187
+ assert.strictEqual(lastExecution.success, true);
188
+ assert.ok(lastExecution.timestamp);
189
+ });
190
+
191
+ it('should track both successful and failed executions', async () => {
192
+ await executor.executeCommand('node', ['--version']);
193
+ await executor.executeCommand('non-existent-command', []);
194
+
195
+ const history = executor.getExecutionHistory();
196
+ assert.ok(history.length >= 2);
197
+
198
+ const results = history.map(h => h.success);
199
+ assert.ok(results.includes(true));
200
+ assert.ok(results.includes(false));
201
+ });
202
+
203
+ it('should limit history size', async () => {
204
+ // 执行多个命令
205
+ for (let i = 0; i < 150; i++) {
206
+ await executor.executeCommand('node', ['--version']);
207
+ }
208
+
209
+ const history = executor.getExecutionHistory();
210
+ assert.ok(history.length <= 100); // 应该有历史大小限制
211
+ });
212
+ });
213
+
214
+ describe('Active Execution Tracking', () => {
215
+ it('should track active executions', async () => {
216
+ const executionPromise = executor.executeCommand('node', ['-e', 'setTimeout(() => {}, 2000)'], {
217
+ timeout: 5000
218
+ });
219
+
220
+ // 立即检查活跃执行
221
+ const activeExecutions = executor.getActiveExecutions();
222
+ assert.ok(activeExecutions.length > 0);
223
+
224
+ const execution = activeExecutions[0];
225
+ assert.strictEqual(execution.command, 'node');
226
+ assert.strictEqual(execution.status, 'running');
227
+
228
+ await executionPromise;
229
+ });
230
+
231
+ it('should clean up completed executions', async () => {
232
+ await executor.executeCommand('node', ['--version']);
233
+
234
+ const activeExecutions = executor.getActiveExecutions();
235
+ const completedExecutions = activeExecutions.filter(e => e.status === 'completed');
236
+ assert.strictEqual(completedExecutions.length, 0);
237
+ });
238
+ });
239
+
240
+ describe('Event Emission', () => {
241
+ it('should emit execution start event', (done) => {
242
+ executor.on('execution:start', (data) => {
243
+ assert.strictEqual(data.command, 'node');
244
+ assert.ok(data.executionId);
245
+ done();
246
+ });
247
+
248
+ executor.executeCommand('node', ['--version']);
249
+ });
250
+
251
+ it('should emit execution complete event', (done) => {
252
+ executor.on('execution:complete', (data) => {
253
+ assert.strictEqual(data.command, 'node');
254
+ assert.strictEqual(data.success, true);
255
+ done();
256
+ });
257
+
258
+ executor.executeCommand('node', ['--version']);
259
+ });
260
+
261
+ it('should emit execution error event', (done) => {
262
+ executor.on('execution:error', (data) => {
263
+ assert.strictEqual(data.command, 'non-existent-command');
264
+ assert.strictEqual(data.success, false);
265
+ done();
266
+ });
267
+
268
+ executor.executeCommand('non-existent-command', []);
269
+ });
270
+ });
271
+
272
+ describe('Performance Tests', () => {
273
+ it('should execute multiple commands concurrently', async () => {
274
+ const commands = [
275
+ ['node', ['--version']],
276
+ ['npm', ['--version']],
277
+ ['node', ['-e', 'console.log("test")']]
278
+ ];
279
+
280
+ const startTime = Date.now();
281
+ const results = await Promise.all(
282
+ commands.map(([cmd, args]) => executor.executeCommand(cmd, args))
283
+ );
284
+ const elapsed = Date.now() - startTime;
285
+
286
+ assert.strictEqual(results.length, 3);
287
+ assert.ok(results.every(r => r.success));
288
+ assert.ok(elapsed < 10000); // 应该在10秒内完成并发执行
289
+ });
290
+
291
+ it('should handle memory usage efficiently', async () => {
292
+ const initialMemory = process.memoryUsage();
293
+
294
+ // 执行多个命令
295
+ for (let i = 0; i < 50; i++) {
296
+ await executor.executeCommand('node', ['--version']);
297
+ }
298
+
299
+ const finalMemory = process.memoryUsage();
300
+ const memoryIncrease = finalMemory.heapUsed - initialMemory.heapUsed;
301
+
302
+ // 内存增长应该控制在合理范围内
303
+ assert.ok(memoryIncrease < 50 * 1024 * 1024); // 小于50MB
304
+ });
305
+ });
306
+
307
+ describe('Platform Specific Tests', () => {
308
+ it('should work on Windows platform', async () => {
309
+ if (process.platform === 'win32') {
310
+ const result = await executor.executeCommand('cmd', ['/c', 'echo test'], {
311
+ timeout: 5000
312
+ });
313
+
314
+ assert.strictEqual(result.success, true);
315
+ assert.ok(result.stdout.includes('test'));
316
+ }
317
+ });
318
+
319
+ it('should work on Unix-like platforms', async () => {
320
+ if (process.platform !== 'win32') {
321
+ const result = await executor.executeCommand('echo', ['test'], {
322
+ timeout: 5000
323
+ });
324
+
325
+ assert.strictEqual(result.success, true);
326
+ assert.ok(result.stdout.includes('test'));
327
+ }
328
+ });
329
+ });
330
+ });
331
+
332
+ // 运行测试
333
+ if (require.main === module) {
334
+ console.log('Running Cross-CLI Executor Unit Tests...');
335
+
336
+ (async () => {
337
+ const executor = new CrossCLIExecutor();
338
+ let passed = 0;
339
+ let failed = 0;
340
+
341
+ const tests = [
342
+ async () => {
343
+ console.log('Testing parameter validation...');
344
+ const result = executor.validateExecutionParams('claude', 'gemini', 'test task');
345
+ if (result.valid) {
346
+ console.log('[PASS] Parameter validation');
347
+ return true;
348
+ }
349
+ return false;
350
+ },
351
+ async () => {
352
+ console.log('Testing command execution...');
353
+ try {
354
+ const result = await executor.executeCommand('node', ['--version'], { timeout: 3000 });
355
+ if (result.success) {
356
+ console.log('[PASS] Command execution');
357
+ return true;
358
+ }
359
+ } catch (error) {
360
+ console.log(`[FAIL] Command execution: ${error.message}`);
361
+ }
362
+ return false;
363
+ },
364
+ async () => {
365
+ console.log('Testing error handling...');
366
+ try {
367
+ const result = await executor.executeCommand('non-existent-command-12345', [], { timeout: 2000 });
368
+ if (!result.success) {
369
+ console.log('[PASS] Error handling');
370
+ return true;
371
+ }
372
+ } catch (error) {
373
+ console.log('[PASS] Error handling (exception thrown)');
374
+ return true;
375
+ }
376
+ return false;
377
+ }
378
+ ];
379
+
380
+ for (const test of tests) {
381
+ try {
382
+ const result = await test();
383
+ if (result) {
384
+ passed++;
385
+ } else {
386
+ failed++;
387
+ }
388
+ } catch (error) {
389
+ console.log(`[ERROR] Test failed: ${error.message}`);
390
+ failed++;
391
+ }
392
+ }
393
+
394
+ console.log(`\nTest Results: ${passed} passed, ${failed} failed`);
395
+ console.log('Implementation needed for full TDD approach.');
396
+ })();
397
+ }
398
+
399
+ module.exports = CrossCLIExecutor;
@@ -1,13 +0,0 @@
1
- """
2
- Claude CLI 适配器包
3
-
4
- 基于 Claude CLI 官方 Hook 系统的原生集成
5
- 支持多种实现方式,全部无抽象层
6
- """
7
-
8
- from .standalone_claude_adapter import get_standalone_claude_adapter, StandaloneClaudeAdapter
9
-
10
- # 向后兼容的别名
11
- ClaudeHookAdapter = StandaloneClaudeAdapter
12
-
13
- __all__ = ['StandaloneClaudeAdapter', 'get_standalone_claude_adapter', 'ClaudeHookAdapter']