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.
- package/README.md +39 -3
- package/STIGMERGY.md +3 -0
- package/config/builtin-skills.json +43 -0
- package/config/enhanced-cli-config.json +438 -0
- package/docs/CLI_TOOLS_AGENT_SKILL_ANALYSIS.md +463 -0
- package/docs/DESIGN_CLI_HELP_ANALYZER_REFACTOR.md +726 -0
- package/docs/ENHANCED_CLI_AGENT_SKILL_CONFIG.md +285 -0
- package/docs/IMPLEMENTATION_CHECKLIST_CLI_HELP_ANALYZER_REFACTOR.md +1268 -0
- package/docs/INSTALLER_ARCHITECTURE.md +257 -0
- package/docs/LESSONS_LEARNED.md +252 -0
- package/docs/SPECS_CLI_HELP_ANALYZER_REFACTOR.md +287 -0
- package/docs/SUDO_PROBLEM_AND_SOLUTION.md +529 -0
- package/docs/correct-skillsio-implementation.md +368 -0
- package/docs/development_guidelines.md +276 -0
- package/docs/independent-resume-implementation.md +198 -0
- package/docs/resumesession-final-implementation.md +195 -0
- package/docs/resumesession-usage.md +87 -0
- package/package.json +146 -136
- package/scripts/analyze-router.js +168 -0
- package/scripts/run-comprehensive-tests.js +230 -0
- package/scripts/run-quick-tests.js +90 -0
- package/scripts/test-runner.js +344 -0
- package/skills/resumesession/INDEPENDENT_SKILL.md +403 -0
- package/skills/resumesession/README.md +381 -0
- package/skills/resumesession/SKILL.md +211 -0
- package/skills/resumesession/__init__.py +33 -0
- package/skills/resumesession/implementations/simple-resume.js +13 -0
- package/skills/resumesession/independent-resume.js +750 -0
- package/skills/resumesession/package.json +1 -0
- package/skills/resumesession/skill.json +1 -0
- package/src/adapters/claude/install_claude_integration.js +9 -1
- package/src/adapters/codebuddy/install_codebuddy_integration.js +3 -1
- package/src/adapters/codex/install_codex_integration.js +15 -5
- package/src/adapters/gemini/install_gemini_integration.js +3 -1
- package/src/adapters/qwen/install_qwen_integration.js +3 -1
- package/src/cli/commands/autoinstall.js +65 -0
- package/src/cli/commands/errors.js +190 -0
- package/src/cli/commands/independent-resume.js +395 -0
- package/src/cli/commands/install.js +179 -0
- package/src/cli/commands/permissions.js +108 -0
- package/src/cli/commands/project.js +485 -0
- package/src/cli/commands/scan.js +97 -0
- package/src/cli/commands/simple-resume.js +377 -0
- package/src/cli/commands/skills.js +158 -0
- package/src/cli/commands/status.js +113 -0
- package/src/cli/commands/stigmergy-resume.js +775 -0
- package/src/cli/commands/system.js +301 -0
- package/src/cli/commands/universal-resume.js +394 -0
- package/src/cli/router-beta.js +471 -0
- package/src/cli/utils/environment.js +75 -0
- package/src/cli/utils/formatters.js +47 -0
- package/src/cli/utils/skills_cache.js +92 -0
- package/src/core/cache_cleaner.js +1 -0
- package/src/core/cli_adapters.js +345 -0
- package/src/core/cli_help_analyzer.js +1236 -680
- package/src/core/cli_path_detector.js +702 -709
- package/src/core/cli_tools.js +515 -160
- package/src/core/coordination/nodejs/CLIIntegrationManager.js +18 -0
- package/src/core/coordination/nodejs/HookDeploymentManager.js +242 -412
- package/src/core/coordination/nodejs/HookDeploymentManager.refactored.js +323 -0
- package/src/core/coordination/nodejs/generators/CLIAdapterGenerator.js +363 -0
- package/src/core/coordination/nodejs/generators/ResumeSessionGenerator.js +932 -0
- package/src/core/coordination/nodejs/generators/SkillsIntegrationGenerator.js +1395 -0
- package/src/core/coordination/nodejs/generators/index.js +12 -0
- package/src/core/enhanced_cli_installer.js +1208 -608
- package/src/core/enhanced_cli_parameter_handler.js +402 -0
- package/src/core/execution_mode_detector.js +222 -0
- package/src/core/installer.js +151 -106
- package/src/core/local_skill_scanner.js +732 -0
- package/src/core/multilingual/language-pattern-manager.js +1 -1
- package/src/core/skills/BuiltinSkillsDeployer.js +188 -0
- package/src/core/skills/StigmergySkillManager.js +123 -16
- package/src/core/skills/embedded-openskills/SkillParser.js +7 -3
- package/src/core/smart_router.js +550 -261
- package/src/index.js +10 -4
- package/src/utils.js +66 -7
- package/test/cli-integration.test.js +304 -0
- package/test/direct_smart_router_test.js +88 -0
- package/test/enhanced-cli-agent-skill-test.js +485 -0
- package/test/simple_test.js +82 -0
- package/test/smart_router_test_runner.js +123 -0
- package/test/smart_routing_edge_cases.test.js +284 -0
- package/test/smart_routing_simple_verification.js +139 -0
- package/test/smart_routing_verification.test.js +346 -0
- package/test/specific-cli-agent-skill-analysis.js +385 -0
- package/test/unit/smart_router.test.js +295 -0
- package/test/very_simple_test.js +54 -0
- 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
|
+
});
|