stigmergy 1.2.13 → 1.3.1

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 (88) hide show
  1. package/README.md +39 -3
  2. package/STIGMERGY.md +3 -0
  3. package/config/builtin-skills.json +43 -0
  4. package/config/enhanced-cli-config.json +438 -0
  5. package/docs/CLI_TOOLS_AGENT_SKILL_ANALYSIS.md +463 -0
  6. package/docs/DESIGN_CLI_HELP_ANALYZER_REFACTOR.md +726 -0
  7. package/docs/ENHANCED_CLI_AGENT_SKILL_CONFIG.md +285 -0
  8. package/docs/IMPLEMENTATION_CHECKLIST_CLI_HELP_ANALYZER_REFACTOR.md +1268 -0
  9. package/docs/INSTALLER_ARCHITECTURE.md +257 -0
  10. package/docs/LESSONS_LEARNED.md +252 -0
  11. package/docs/SPECS_CLI_HELP_ANALYZER_REFACTOR.md +287 -0
  12. package/docs/SUDO_PROBLEM_AND_SOLUTION.md +529 -0
  13. package/docs/correct-skillsio-implementation.md +368 -0
  14. package/docs/development_guidelines.md +276 -0
  15. package/docs/independent-resume-implementation.md +198 -0
  16. package/docs/resumesession-final-implementation.md +195 -0
  17. package/docs/resumesession-usage.md +87 -0
  18. package/package.json +146 -136
  19. package/scripts/analyze-router.js +168 -0
  20. package/scripts/run-comprehensive-tests.js +230 -0
  21. package/scripts/run-quick-tests.js +90 -0
  22. package/scripts/test-runner.js +344 -0
  23. package/skills/resumesession/INDEPENDENT_SKILL.md +403 -0
  24. package/skills/resumesession/README.md +381 -0
  25. package/skills/resumesession/SKILL.md +211 -0
  26. package/skills/resumesession/__init__.py +33 -0
  27. package/skills/resumesession/implementations/simple-resume.js +13 -0
  28. package/skills/resumesession/independent-resume.js +750 -0
  29. package/skills/resumesession/package.json +1 -0
  30. package/skills/resumesession/skill.json +1 -0
  31. package/src/adapters/claude/install_claude_integration.js +9 -1
  32. package/src/adapters/codebuddy/install_codebuddy_integration.js +3 -1
  33. package/src/adapters/codex/install_codex_integration.js +15 -5
  34. package/src/adapters/gemini/install_gemini_integration.js +3 -1
  35. package/src/adapters/qwen/install_qwen_integration.js +3 -1
  36. package/src/cli/commands/autoinstall.js +65 -0
  37. package/src/cli/commands/errors.js +190 -0
  38. package/src/cli/commands/independent-resume.js +395 -0
  39. package/src/cli/commands/install.js +179 -0
  40. package/src/cli/commands/permissions.js +108 -0
  41. package/src/cli/commands/project.js +485 -0
  42. package/src/cli/commands/scan.js +97 -0
  43. package/src/cli/commands/simple-resume.js +377 -0
  44. package/src/cli/commands/skills.js +158 -0
  45. package/src/cli/commands/status.js +113 -0
  46. package/src/cli/commands/stigmergy-resume.js +775 -0
  47. package/src/cli/commands/system.js +301 -0
  48. package/src/cli/commands/universal-resume.js +394 -0
  49. package/src/cli/router-beta.js +471 -0
  50. package/src/cli/utils/environment.js +75 -0
  51. package/src/cli/utils/formatters.js +47 -0
  52. package/src/cli/utils/skills_cache.js +92 -0
  53. package/src/core/cache_cleaner.js +1 -0
  54. package/src/core/cli_adapters.js +345 -0
  55. package/src/core/cli_help_analyzer.js +1236 -680
  56. package/src/core/cli_path_detector.js +702 -709
  57. package/src/core/cli_tools.js +515 -160
  58. package/src/core/coordination/nodejs/CLIIntegrationManager.js +18 -0
  59. package/src/core/coordination/nodejs/HookDeploymentManager.js +242 -412
  60. package/src/core/coordination/nodejs/HookDeploymentManager.refactored.js +323 -0
  61. package/src/core/coordination/nodejs/generators/CLIAdapterGenerator.js +363 -0
  62. package/src/core/coordination/nodejs/generators/ResumeSessionGenerator.js +932 -0
  63. package/src/core/coordination/nodejs/generators/SkillsIntegrationGenerator.js +1395 -0
  64. package/src/core/coordination/nodejs/generators/index.js +12 -0
  65. package/src/core/enhanced_cli_installer.js +1208 -608
  66. package/src/core/enhanced_cli_parameter_handler.js +402 -0
  67. package/src/core/execution_mode_detector.js +222 -0
  68. package/src/core/installer.js +151 -106
  69. package/src/core/local_skill_scanner.js +732 -0
  70. package/src/core/multilingual/language-pattern-manager.js +1 -1
  71. package/src/core/skills/BuiltinSkillsDeployer.js +188 -0
  72. package/src/core/skills/StigmergySkillManager.js +123 -16
  73. package/src/core/skills/embedded-openskills/SkillParser.js +7 -3
  74. package/src/core/smart_router.js +550 -261
  75. package/src/index.js +10 -4
  76. package/src/utils.js +66 -7
  77. package/test/cli-integration.test.js +304 -0
  78. package/test/direct_smart_router_test.js +88 -0
  79. package/test/enhanced-cli-agent-skill-test.js +485 -0
  80. package/test/simple_test.js +82 -0
  81. package/test/smart_router_test_runner.js +123 -0
  82. package/test/smart_routing_edge_cases.test.js +284 -0
  83. package/test/smart_routing_simple_verification.js +139 -0
  84. package/test/smart_routing_verification.test.js +346 -0
  85. package/test/specific-cli-agent-skill-analysis.js +385 -0
  86. package/test/unit/smart_router.test.js +295 -0
  87. package/test/very_simple_test.js +54 -0
  88. package/src/cli/router.js +0 -1783
@@ -0,0 +1,385 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Specific CLI Agent and Skill Analysis
5
+ * Analyzes gemini, copilot, codex, and kode CLI tools
6
+ */
7
+
8
+ const path = require('path');
9
+ const os = require('os');
10
+ const { spawnSync } = require('child_process');
11
+
12
+ // Import enhanced components
13
+ const CLIHelpAnalyzer = require('../src/core/cli_help_analyzer');
14
+ const SmartRouter = require('../src/core/smart_router');
15
+ const { CLI_TOOLS } = require('../src/core/cli_tools');
16
+
17
+ class SpecificCLIAnalyzer {
18
+ constructor() {
19
+ this.analyzer = new CLIHelpAnalyzer();
20
+ this.router = new SmartRouter();
21
+ this.targetCLIs = ['gemini', 'copilot', 'codex', 'kode'];
22
+ this.testResults = {};
23
+ }
24
+
25
+ /**
26
+ * Main analysis function
27
+ */
28
+ async analyze() {
29
+ console.log('🔍 Analyzing Specific CLI Tools for Agent and Skill Support');
30
+ console.log('=' .repeat(70));
31
+
32
+ try {
33
+ // Initialize components
34
+ await this.analyzer.initialize();
35
+ await this.router.initialize();
36
+
37
+ // Analyze each target CLI
38
+ for (const cliName of this.targetCLIs) {
39
+ await this.analyzeCLI(cliName);
40
+ }
41
+
42
+ // Show comprehensive results
43
+ this.showAnalysisResults();
44
+
45
+ } catch (error) {
46
+ console.error('❌ Analysis failed:', error.message);
47
+ process.exit(1);
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Analyze individual CLI tool
53
+ */
54
+ async analyzeCLI(cliName) {
55
+ console.log(`\n🔧 Analyzing ${cliName.toUpperCase()} CLI...`);
56
+ console.log('-'.repeat(50));
57
+
58
+ this.testResults[cliName] = {
59
+ installed: false,
60
+ helpAvailable: false,
61
+ agentSupport: false,
62
+ skillSupport: false,
63
+ commandFormat: null,
64
+ examples: [],
65
+ limitations: [],
66
+ testInputs: []
67
+ };
68
+
69
+ // 1. Check if CLI is installed
70
+ await this.checkCLIInstallation(cliName);
71
+
72
+ // 2. Get help information
73
+ await this.getCLIHelp(cliName);
74
+
75
+ // 3. Test agent/skill detection
76
+ await this.testAgentSkillDetection(cliName);
77
+
78
+ // 4. Test command generation
79
+ await this.testCommandGeneration(cliName);
80
+
81
+ // 5. Test smart routing
82
+ await this.testSmartRouting(cliName);
83
+ }
84
+
85
+ /**
86
+ * Check if CLI tool is installed
87
+ */
88
+ async checkCLIInstallation(cliName) {
89
+ try {
90
+ const result = spawnSync(cliName, ['--version'], {
91
+ encoding: 'utf8',
92
+ timeout: 5000,
93
+ stdio: ['pipe', 'pipe', 'pipe']
94
+ });
95
+
96
+ if (result.status === 0) {
97
+ this.testResults[cliName].installed = true;
98
+ console.log(`✅ ${cliName} is installed`);
99
+ if (result.stdout) {
100
+ console.log(` Version: ${result.stdout.trim()}`);
101
+ }
102
+ } else {
103
+ console.log(`❌ ${cliName} is not installed or not responding`);
104
+ }
105
+ } catch (error) {
106
+ console.log(`❌ ${cliName} not found: ${error.message}`);
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Get CLI help information
112
+ */
113
+ async getCLIHelp(cliName) {
114
+ const helpMethods = [
115
+ ['--help'],
116
+ ['-h'],
117
+ ['help'],
118
+ ['--usage'],
119
+ [''],
120
+ ['version'],
121
+ ['--version']
122
+ ];
123
+
124
+ let helpText = '';
125
+ let helpMethod = '';
126
+
127
+ for (const method of helpMethods) {
128
+ try {
129
+ const result = spawnSync(cliName, method, {
130
+ encoding: 'utf8',
131
+ timeout: 8000,
132
+ stdio: ['pipe', 'pipe', 'pipe']
133
+ });
134
+
135
+ if (result.status === 0 && result.stdout) {
136
+ helpText = result.stdout;
137
+ helpMethod = `${cliName} ${method.join(' ')}`;
138
+ break;
139
+ } else if (result.stderr && result.stderr.includes('help')) {
140
+ helpText = result.stderr;
141
+ helpMethod = `${cliName} ${method.join(' ')} (stderr)`;
142
+ break;
143
+ }
144
+ } catch (error) {
145
+ continue;
146
+ }
147
+ }
148
+
149
+ if (helpText) {
150
+ this.testResults[cliName].helpAvailable = true;
151
+ console.log(`📚 Help available via: ${helpMethod}`);
152
+
153
+ // Analyze help text for agent/skill patterns
154
+ this.analyzeHelpText(cliName, helpText);
155
+ } else {
156
+ console.log(`❌ No help information available`);
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Analyze help text for agent/skill patterns
162
+ */
163
+ analyzeHelpText(cliName, helpText) {
164
+ const lowerHelp = helpText.toLowerCase();
165
+
166
+ // Check for agent-related keywords
167
+ const agentKeywords = ['agent', '智能体', 'expert', 'specialist', 'assistant'];
168
+ const hasAgentKeywords = agentKeywords.some(keyword => lowerHelp.includes(keyword));
169
+
170
+ // Check for skill-related keywords
171
+ const skillKeywords = ['skill', '技能', 'capability', 'tool', 'plugin', 'extension'];
172
+ const hasSkillKeywords = skillKeywords.some(keyword => lowerHelp.includes(keyword));
173
+
174
+ // Check for prompt-related parameters
175
+ const promptParams = ['-p', '--prompt', '--input', '--query', 'positional'];
176
+ const hasPromptParams = promptParams.some(param => lowerHelp.includes(param));
177
+
178
+ // Check for non-interactive modes
179
+ const nonInteractiveModes = ['non-interactive', 'batch', 'script', 'stdin'];
180
+ const hasNonInteractive = nonInteractiveModes.some(mode => lowerHelp.includes(mode));
181
+
182
+ this.testResults[cliName].agentSupport = hasAgentKeywords;
183
+ this.testResults[cliName].skillSupport = hasSkillKeywords;
184
+ this.testResults[cliName].hasPromptParams = hasPromptParams;
185
+ this.testResults[cliName].hasNonInteractive = hasNonInteractive;
186
+
187
+ console.log(`🤖 Agent keywords: ${hasAgentKeywords ? '✅' : '❌'}`);
188
+ console.log(`🔧 Skill keywords: ${hasSkillKeywords ? '✅' : '❌'}`);
189
+ console.log(`📝 Prompt params: ${hasPromptParams ? '✅' : '❌'}`);
190
+ console.log(`🔄 Non-interactive: ${hasNonInteractive ? '✅' : '❌'}`);
191
+ }
192
+
193
+ /**
194
+ * Test agent/skill detection capabilities
195
+ */
196
+ async testAgentSkillDetection(cliName) {
197
+ const testInputs = [
198
+ '请使用异化分析技能分析程序员异化现象',
199
+ '使用数字马克思智能体进行阶级分析',
200
+ '请专家助手分析技术影响',
201
+ '使用分析技能进行深度研究'
202
+ ];
203
+
204
+ console.log('\n🧪 Testing agent/skill detection:');
205
+
206
+ for (const input of testInputs) {
207
+ try {
208
+ const detections = this.analyzer.detectAgentSkillMentions(input, cliName);
209
+ this.testResults[cliName].testInputs.push({
210
+ input,
211
+ detections
212
+ });
213
+
214
+ console.log(` Input: "${input}"`);
215
+ console.log(` Agent: ${detections.hasAgent ? '✅' : '❌'} | Skill: ${detections.hasSkill ? '✅' : '❌'} | Confidence: ${detections.confidence}`);
216
+ } catch (error) {
217
+ console.log(` ❌ Detection failed: ${error.message}`);
218
+ }
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Test command generation
224
+ */
225
+ async testCommandGeneration(cliName) {
226
+ const testPrompt = '请使用异化分析技能分析程序员异化现象';
227
+
228
+ console.log('\n⚙️ Testing command generation:');
229
+ console.log(` Test prompt: "${testPrompt}"`);
230
+
231
+ try {
232
+ const optimizedCall = this.analyzer.generateOptimizedCall(cliName, testPrompt);
233
+ if (optimizedCall) {
234
+ this.testResults[cliName].commandFormat = optimizedCall.fullCommand;
235
+ console.log(` Generated: ${optimizedCall.fullCommand}`);
236
+ } else {
237
+ console.log(` ❌ Command generation failed`);
238
+ }
239
+ } catch (error) {
240
+ console.log(` ❌ Command generation error: ${error.message}`);
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Test smart routing
246
+ */
247
+ async testSmartRouting(cliName) {
248
+ const testInputs = [
249
+ `请使用分析技能分析程序员异化现象`,
250
+ `使用数字马克思智能体进行阶级分析`
251
+ ];
252
+
253
+ console.log('\n🧭 Testing smart routing:');
254
+
255
+ for (const input of testInputs) {
256
+ try {
257
+ const route = await this.router.smartRouteEnhanced(input);
258
+ if (route.tool === cliName) {
259
+ console.log(` ✅ Routed to ${cliName} (confidence: ${route.confidence})`);
260
+ } else {
261
+ console.log(` ⚠️ Routed to ${route.tool} instead of ${cliName}`);
262
+ }
263
+ } catch (error) {
264
+ console.log(` ❌ Routing failed: ${error.message}`);
265
+ }
266
+ }
267
+ }
268
+
269
+ /**
270
+ * Show comprehensive analysis results
271
+ */
272
+ showAnalysisResults() {
273
+ console.log('\n' + '=' .repeat(70));
274
+ console.log('📊 COMPREHENSIVE CLI ANALYSIS RESULTS');
275
+ console.log('=' .repeat(70));
276
+
277
+ for (const [cliName, results] of Object.entries(this.testResults)) {
278
+ console.log(`\n🔧 ${cliName.toUpperCase()} CLI ANALYSIS`);
279
+ console.log('-'.repeat(50));
280
+
281
+ // Installation status
282
+ console.log(`📦 Installation: ${results.installed ? '✅ Installed' : '❌ Not Installed'}`);
283
+
284
+ // Help availability
285
+ console.log(`📚 Help Available: ${results.helpAvailable ? '✅ Yes' : '❌ No'}`);
286
+
287
+ // Agent/Skill support
288
+ console.log(`🤖 Agent Support: ${results.agentSupport ? '✅ Yes' : '❌ No'}`);
289
+ console.log(`🔧 Skill Support: ${results.skillSupport ? '✅ Yes' : '❌ No'}`);
290
+
291
+ // Command format
292
+ if (results.commandFormat) {
293
+ console.log(`⚙️ Command Format: ${results.commandFormat}`);
294
+ }
295
+
296
+ // Test inputs summary
297
+ if (results.testInputs.length > 0) {
298
+ console.log(`🧪 Detection Tests: ${results.testInputs.length} completed`);
299
+ const avgConfidence = results.testInputs.reduce((sum, test) => sum + test.detections.confidence, 0) / results.testInputs.length;
300
+ console.log(` Average Confidence: ${avgConfidence.toFixed(2)}`);
301
+ }
302
+
303
+ // Overall assessment
304
+ const supportScore = (results.agentSupport ? 1 : 0) + (results.skillSupport ? 1 : 0);
305
+ let assessment = '';
306
+
307
+ if (supportScore === 2) {
308
+ assessment = '✅ Full Support';
309
+ } else if (supportScore === 1) {
310
+ assessment = '⚠️ Partial Support';
311
+ } else {
312
+ assessment = '❌ No Support';
313
+ }
314
+
315
+ console.log(`🎯 Overall Assessment: ${assessment}`);
316
+ }
317
+
318
+ // Summary comparison
319
+ console.log('\n' + '=' .repeat(70));
320
+ console.log('📋 SUMMARY COMPARISON');
321
+ console.log('=' .repeat(70));
322
+
323
+ const comparisonTable = [
324
+ ['CLI Tool', 'Installed', 'Agent Support', 'Skill Support', 'Overall'],
325
+ ['-'.repeat(10), '-'.repeat(10), '-'.repeat(12), '-'.repeat(12), '-'.repeat(8)]
326
+ ];
327
+
328
+ for (const [cliName, results] of Object.entries(this.testResults)) {
329
+ const installed = results.installed ? '✅' : '❌';
330
+ const agent = results.agentSupport ? '✅' : '❌';
331
+ const skill = results.skillSupport ? '✅' : '❌';
332
+
333
+ const supportScore = (results.agentSupport ? 1 : 0) + (results.skillSupport ? 1 : 0);
334
+ const overall = supportScore === 2 ? 'Excellent' : supportScore === 1 ? 'Good' : 'Poor';
335
+
336
+ comparisonTable.push([cliName, installed, agent, skill, overall]);
337
+ }
338
+
339
+ // Print table
340
+ for (const row of comparisonTable) {
341
+ console.log(row.join(' | '));
342
+ }
343
+
344
+ // Recommendations
345
+ console.log('\n💡 RECOMMENDATIONS');
346
+ console.log('-'.repeat(30));
347
+
348
+ for (const [cliName, results] of Object.entries(this.testResults)) {
349
+ if (!results.installed) {
350
+ console.log(`📦 ${cliName}: Consider installing for broader CLI tool coverage`);
351
+ } else if (!results.agentSupport || !results.skillSupport) {
352
+ console.log(`🔧 ${cliName}: Limited agent/skill support - may need manual prompt engineering`);
353
+ } else {
354
+ console.log(`✨ ${cliName}: Full support available - ready for enhanced CLI calls`);
355
+ }
356
+ }
357
+ }
358
+ }
359
+
360
+ // Main execution
361
+ async function main() {
362
+ const analyzer = new SpecificCLIAnalyzer();
363
+ await analyzer.analyze();
364
+ }
365
+
366
+ // Handle errors
367
+ process.on('unhandledRejection', (reason, promise) => {
368
+ console.error('[FATAL] Unhandled Rejection at:', promise, 'reason:', reason);
369
+ process.exit(1);
370
+ });
371
+
372
+ process.on('uncaughtException', (error) => {
373
+ console.error('[FATAL] Uncaught Exception:', error);
374
+ process.exit(1);
375
+ });
376
+
377
+ // Run analysis if this file is executed directly
378
+ if (require.main === module) {
379
+ main().catch((error) => {
380
+ console.error('[FATAL] Analysis execution failed:', error);
381
+ process.exit(1);
382
+ });
383
+ }
384
+
385
+ module.exports = SpecificCLIAnalyzer;
@@ -0,0 +1,295 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * 单元测试:智能路由模块
5
+ * 测试SmartRouter类的各种路由功能
6
+ */
7
+
8
+ const SmartRouter = require('../../src/core/smart_router');
9
+ const CLIHelpAnalyzer = require('../../src/core/cli_help_analyzer');
10
+ const { CLI_TOOLS } = require('../../src/core/cli_tools');
11
+
12
+ // Mock CLI_TOOLS to avoid actual CLI detection during testing
13
+ jest.mock('../../src/core/cli_tools', () => ({
14
+ CLI_TOOLS: {
15
+ claude: { name: 'Claude CLI', command: 'claude', version: 'claude --version' },
16
+ qwen: { name: 'Qwen CLI', command: 'qwen', version: 'qwen --version' },
17
+ gemini: { name: 'Gemini CLI', command: 'gemini', version: 'gemini --version' },
18
+ iflow: { name: 'iFlow CLI', command: 'iflow', version: 'iflow --version' },
19
+ codebuddy: { name: 'CodeBuddy CLI', command: 'codebuddy', version: 'codebuddy --version' },
20
+ codex: { name: 'Codex CLI', command: 'codex', version: 'codex --version' },
21
+ qodercli: { name: 'QoderCLI', command: 'qodercli', version: 'qodercli --version' },
22
+ copilot: { name: 'Copilot CLI', command: 'copilot', version: 'copilot --version' },
23
+ kode: { name: 'Kode CLI', command: 'kode', version: 'kode --version' }
24
+ },
25
+ validateCLITool: jest.fn().mockReturnValue(true)
26
+ }));
27
+
28
+ // Mock CLIHelpAnalyzer
29
+ jest.mock('../../src/core/cli_help_analyzer', () => {
30
+ return jest.fn().mockImplementation(() => {
31
+ return {
32
+ setCLITools: jest.fn(),
33
+ initialize: jest.fn().mockResolvedValue(undefined),
34
+ analyzeCLI: jest.fn().mockResolvedValue({
35
+ success: true,
36
+ cliName: 'test-cli',
37
+ patterns: {
38
+ commands: [],
39
+ subcommands: []
40
+ }
41
+ }),
42
+ loadPersistentConfig: jest.fn().mockResolvedValue({
43
+ failedAttempts: {},
44
+ cliPatterns: {}
45
+ }),
46
+ getCachedAnalysis: jest.fn().mockResolvedValue(null),
47
+ isCacheExpired: jest.fn().mockReturnValue(false),
48
+ generateOptimizedCall: jest.fn().mockReturnValue({
49
+ optimizedPrompt: 'test prompt'
50
+ }),
51
+ getAgentSkillCompatibilityScore: jest.fn().mockReturnValue({ score: 0.5, reasons: ['test'] }),
52
+ getEnhancedCLIPattern: jest.fn().mockResolvedValue({})
53
+ };
54
+ });
55
+ });
56
+
57
+ describe('SmartRouter', () => {
58
+ let router;
59
+
60
+ beforeEach(async () => {
61
+ router = new SmartRouter();
62
+ await router.initialize();
63
+ });
64
+
65
+ describe('smartRoute', () => {
66
+ test('should route to claude when claude keyword is detected', async () => {
67
+ const result = await router.smartRoute('用claude分析这段代码');
68
+ expect(result.tool).toBe('claude');
69
+ expect(result.prompt).toBe('分析这段代码');
70
+ });
71
+
72
+ test('should route to qwen when qwen keyword is detected', async () => {
73
+ const result = await router.smartRoute('用qwen写一个hello world程序');
74
+ expect(result.tool).toBe('qwen');
75
+ expect(result.prompt).toBe('写一个hello world程序');
76
+ });
77
+
78
+ test('should route to gemini when gemini keyword is detected', async () => {
79
+ const result = await router.smartRoute('请使用gemini解释这段代码');
80
+ expect(result.tool).toBe('gemini');
81
+ expect(result.prompt).toBe('解释这段代码');
82
+ });
83
+
84
+ test('should route to default tool when no keyword is detected', async () => {
85
+ const result = await router.smartRoute('分析项目架构');
86
+ expect(result.tool).toBe('claude');
87
+ expect(result.prompt).toBe('分析项目架构');
88
+ });
89
+
90
+ test('should route to claude when "use claude" pattern is detected', async () => {
91
+ const result = await router.smartRoute('use claude to explain this code');
92
+ expect(result.tool).toBe('claude');
93
+ expect(result.prompt).toBe('explain this code');
94
+ });
95
+
96
+ test('should route to qwen when "using qwen" pattern is detected', async () => {
97
+ const result = await router.smartRoute('using qwen to generate documentation');
98
+ expect(result.tool).toBe('qwen');
99
+ expect(result.prompt).toBe('generate documentation');
100
+ });
101
+
102
+ test('should handle multiple tool keywords and prioritize exact matches', async () => {
103
+ const result = await router.smartRoute('use claude and qwen to analyze code');
104
+ expect(result.tool).toBe('claude'); // Should prioritize the first matching tool
105
+ expect(result.prompt).toBe('and qwen to analyze code');
106
+ });
107
+
108
+ test('should clean up common prefixes from prompts', async () => {
109
+ const result = await router.smartRoute('please help me with this code using claude');
110
+ expect(result.tool).toBe('claude');
111
+ expect(result.prompt).toBe('with this code');
112
+ });
113
+
114
+ test('should handle "with" keyword for tool selection', async () => {
115
+ const result = await router.smartRoute('write an article with qwen');
116
+ expect(result.tool).toBe('qwen');
117
+ expect(result.prompt).toBe('write an article');
118
+ });
119
+
120
+ test('should handle empty input gracefully', async () => {
121
+ const result = await router.smartRoute('');
122
+ expect(result.tool).toBe('claude');
123
+ expect(result.prompt).toBe('');
124
+ });
125
+
126
+ test('should handle input with only whitespace', async () => {
127
+ const result = await router.smartRoute(' ');
128
+ expect(result.tool).toBe('claude');
129
+ expect(result.prompt).toBe('');
130
+ });
131
+ });
132
+
133
+ describe('shouldRoute', () => {
134
+ test('should return true for inputs containing route keywords', () => {
135
+ expect(router.shouldRoute('use claude to analyze')).toBe(true);
136
+ expect(router.shouldRoute('please help with')).toBe(true);
137
+ expect(router.shouldRoute('write code using qwen')).toBe(true);
138
+ expect(router.shouldRoute('explain this with gemini')).toBe(true);
139
+ });
140
+
141
+ test('should return false for inputs without route keywords', () => {
142
+ expect(router.shouldRoute('hello world')).toBe(false);
143
+ expect(router.shouldRoute('test input')).toBe(false);
144
+ expect(router.shouldRoute('')).toBe(false);
145
+ expect(router.shouldRoute(null)).toBe(false);
146
+ expect(router.shouldRoute(undefined)).toBe(false);
147
+ });
148
+
149
+ test('should handle non-string inputs', () => {
150
+ expect(router.shouldRoute(123)).toBe(false);
151
+ expect(router.shouldRoute({})).toBe(false);
152
+ expect(router.shouldRoute([])).toBe(false);
153
+ });
154
+ });
155
+
156
+ describe('extractKeywords', () => {
157
+ test('should extract correct keywords for claude', () => {
158
+ const keywords = router.extractKeywords('claude', null);
159
+ expect(keywords).toContain('claude');
160
+ expect(keywords).toContain('anthropic');
161
+ });
162
+
163
+ test('should extract correct keywords for qwen', () => {
164
+ const keywords = router.extractKeywords('qwen', null);
165
+ expect(keywords).toContain('qwen');
166
+ expect(keywords).toContain('alibaba');
167
+ expect(keywords).toContain('tongyi');
168
+ });
169
+
170
+ test('should extract correct keywords for gemini', () => {
171
+ const keywords = router.extractKeywords('gemini', null);
172
+ expect(keywords).toContain('gemini');
173
+ expect(keywords).toContain('google');
174
+ });
175
+
176
+ test('should extract correct keywords for iflow', () => {
177
+ const keywords = router.extractKeywords('iflow', null);
178
+ expect(keywords).toContain('iflow');
179
+ expect(keywords).toContain('workflow');
180
+ expect(keywords).toContain('intelligent');
181
+ });
182
+
183
+ test('should extract correct keywords for codebuddy', () => {
184
+ const keywords = router.extractKeywords('codebuddy', null);
185
+ expect(keywords).toContain('codebuddy');
186
+ expect(keywords).toContain('buddy');
187
+ expect(keywords).toContain('assistant');
188
+ });
189
+
190
+ test('should extract correct keywords for copilot', () => {
191
+ const keywords = router.extractKeywords('copilot', null);
192
+ expect(keywords).toContain('copilot');
193
+ expect(keywords).toContain('github');
194
+ expect(keywords).toContain('gh');
195
+ });
196
+
197
+ test('should extract correct keywords for codex', () => {
198
+ const keywords = router.extractKeywords('codex', null);
199
+ expect(keywords).toContain('codex');
200
+ expect(keywords).toContain('openai');
201
+ expect(keywords).toContain('gpt');
202
+ });
203
+
204
+ test('should extract correct keywords for qodercli', () => {
205
+ const keywords = router.extractKeywords('qodercli', null);
206
+ expect(keywords).toContain('qodercli');
207
+ expect(keywords).toContain('qoder');
208
+ expect(keywords).toContain('code');
209
+ });
210
+
211
+ test('should extract correct keywords for kode', () => {
212
+ const keywords = router.extractKeywords('kode', null);
213
+ expect(keywords).toContain('kode');
214
+ expect(keywords).toContain('multi-model');
215
+ expect(keywords).toContain('collaboration');
216
+ expect(keywords).toContain('multi模型');
217
+ });
218
+ });
219
+
220
+ describe('smartRouteEnhanced', () => {
221
+ test('should return enhanced routing results with confidence scores', async () => {
222
+ const result = await router.smartRouteEnhanced('用claude分析这段代码');
223
+ expect(result.tool).toBe('claude');
224
+ expect(result.prompt).toBe('分析这段代码');
225
+ expect(result.confidence).toBeDefined();
226
+ expect(typeof result.confidence).toBe('number');
227
+ expect(result.confidence).toBeGreaterThanOrEqual(0);
228
+ expect(result.confidence).toBeLessThanOrEqual(1);
229
+ });
230
+
231
+ test('should return alternative options when available', async () => {
232
+ const result = await router.smartRouteEnhanced('用qwen写一个hello world程序');
233
+ expect(result.tool).toBe('qwen');
234
+ expect(result.alternativeOptions).toBeDefined();
235
+ expect(Array.isArray(result.alternativeOptions)).toBe(true);
236
+ });
237
+ });
238
+
239
+ describe('isRecentFailure', () => {
240
+ test('should return true for failures within 1 hour', () => {
241
+ const recentTime = new Date(Date.now() - 30 * 60 * 1000); // 30 minutes ago
242
+ expect(router.isRecentFailure(recentTime.toISOString())).toBe(true);
243
+ });
244
+
245
+ test('should return false for failures older than 1 hour', () => {
246
+ const oldTime = new Date(Date.now() - 2 * 60 * 60 * 1000); // 2 hours ago
247
+ expect(router.isRecentFailure(oldTime.toISOString())).toBe(false);
248
+ });
249
+ });
250
+ });
251
+
252
+ // Additional integration tests
253
+ describe('SmartRouter Integration Tests', () => {
254
+ let router;
255
+
256
+ beforeEach(async () => {
257
+ router = new SmartRouter();
258
+ await router.initialize();
259
+ });
260
+
261
+ test('should handle complex routing scenarios with multiple keywords', async () => {
262
+ const scenarios = [
263
+ { input: '请用claude帮我分析这段JavaScript代码的性能问题', expectedTool: 'claude', expectedPrompt: '帮我分析这段JavaScript代码的性能问题' },
264
+ { input: '使用qwen写一份技术文档,关于Node.js', expectedTool: 'qwen', expectedPrompt: '写一份技术文档,关于Node.js' },
265
+ { input: '请gemini解释一下机器学习的基本概念', expectedTool: 'gemini', expectedPrompt: '解释一下机器学习的基本概念' },
266
+ { input: '用codebuddy帮我修复这个bug', expectedTool: 'codebuddy', expectedPrompt: '帮我修复这个bug' },
267
+ { input: '通过copilot生成一个React组件', expectedTool: 'copilot', expectedPrompt: '生成一个React组件' }
268
+ ];
269
+
270
+ for (const scenario of scenarios) {
271
+ const result = await router.smartRoute(scenario.input);
272
+ expect(result.tool).toBe(scenario.expectedTool);
273
+ expect(result.prompt).toBe(scenario.expectedPrompt);
274
+ }
275
+ });
276
+
277
+ test('should handle mixed language inputs (Chinese + English)', async () => {
278
+ const result = await router.smartRoute('Use claude to analyze this code and explain it in Chinese');
279
+ expect(result.tool).toBe('claude');
280
+ expect(result.prompt).toBe('to analyze this code and explain it in Chinese');
281
+ });
282
+
283
+ test('should handle routing with specific tool subcommands', async () => {
284
+ // Mock CLI pattern with subcommands
285
+ router.getOptimizedCLIPattern = jest.fn().mockResolvedValue({
286
+ patterns: {
287
+ commands: [{ name: 'analyze' }, { name: 'generate' }, { name: 'explain' }]
288
+ }
289
+ });
290
+
291
+ const result = await router.smartRoute('claude analyze this file');
292
+ expect(result.tool).toBe('claude');
293
+ expect(result.prompt).toBe('analyze this file');
294
+ });
295
+ });