stigmergy 1.2.13 → 1.3.1-beta

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 (84) 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 +19 -9
  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 +171 -0
  24. package/skills/resumesession/SKILL.md +127 -0
  25. package/skills/resumesession/__init__.py +33 -0
  26. package/skills/resumesession/implementations/simple-resume.js +13 -0
  27. package/src/adapters/claude/install_claude_integration.js +9 -1
  28. package/src/adapters/codebuddy/install_codebuddy_integration.js +3 -1
  29. package/src/adapters/codex/install_codex_integration.js +15 -5
  30. package/src/adapters/gemini/install_gemini_integration.js +3 -1
  31. package/src/adapters/qwen/install_qwen_integration.js +3 -1
  32. package/src/cli/commands/autoinstall.js +65 -0
  33. package/src/cli/commands/errors.js +190 -0
  34. package/src/cli/commands/independent-resume.js +395 -0
  35. package/src/cli/commands/install.js +179 -0
  36. package/src/cli/commands/permissions.js +108 -0
  37. package/src/cli/commands/project.js +485 -0
  38. package/src/cli/commands/scan.js +97 -0
  39. package/src/cli/commands/simple-resume.js +377 -0
  40. package/src/cli/commands/skills.js +158 -0
  41. package/src/cli/commands/status.js +113 -0
  42. package/src/cli/commands/stigmergy-resume.js +775 -0
  43. package/src/cli/commands/system.js +301 -0
  44. package/src/cli/commands/universal-resume.js +394 -0
  45. package/src/cli/router-beta.js +471 -0
  46. package/src/cli/utils/environment.js +75 -0
  47. package/src/cli/utils/formatters.js +47 -0
  48. package/src/cli/utils/skills_cache.js +92 -0
  49. package/src/core/cache_cleaner.js +1 -0
  50. package/src/core/cli_adapters.js +345 -0
  51. package/src/core/cli_help_analyzer.js +582 -26
  52. package/src/core/cli_path_detector.js +702 -709
  53. package/src/core/cli_tools.js +515 -160
  54. package/src/core/coordination/nodejs/CLIIntegrationManager.js +18 -0
  55. package/src/core/coordination/nodejs/HookDeploymentManager.js +242 -412
  56. package/src/core/coordination/nodejs/HookDeploymentManager.refactored.js +323 -0
  57. package/src/core/coordination/nodejs/generators/CLIAdapterGenerator.js +363 -0
  58. package/src/core/coordination/nodejs/generators/ResumeSessionGenerator.js +932 -0
  59. package/src/core/coordination/nodejs/generators/SkillsIntegrationGenerator.js +1395 -0
  60. package/src/core/coordination/nodejs/generators/index.js +12 -0
  61. package/src/core/enhanced_cli_installer.js +1208 -608
  62. package/src/core/enhanced_cli_parameter_handler.js +402 -0
  63. package/src/core/execution_mode_detector.js +222 -0
  64. package/src/core/installer.js +151 -106
  65. package/src/core/local_skill_scanner.js +732 -0
  66. package/src/core/multilingual/language-pattern-manager.js +1 -1
  67. package/src/core/skills/BuiltinSkillsDeployer.js +188 -0
  68. package/src/core/skills/StigmergySkillManager.js +123 -16
  69. package/src/core/skills/embedded-openskills/SkillParser.js +7 -3
  70. package/src/core/smart_router.js +291 -2
  71. package/src/index.js +10 -4
  72. package/src/utils.js +66 -7
  73. package/test/cli-integration.test.js +304 -0
  74. package/test/direct_smart_router_test.js +88 -0
  75. package/test/enhanced-cli-agent-skill-test.js +485 -0
  76. package/test/simple_test.js +82 -0
  77. package/test/smart_router_test_runner.js +123 -0
  78. package/test/smart_routing_edge_cases.test.js +284 -0
  79. package/test/smart_routing_simple_verification.js +139 -0
  80. package/test/smart_routing_verification.test.js +346 -0
  81. package/test/specific-cli-agent-skill-analysis.js +385 -0
  82. package/test/unit/smart_router.test.js +295 -0
  83. package/test/very_simple_test.js +54 -0
  84. package/src/cli/router.js +0 -1783
@@ -0,0 +1,1395 @@
1
+ // Skills Integration Generator
2
+ // 专门负责生成各CLI的Skills集成文件
3
+
4
+ const path = require('path');
5
+ const os = require('os');
6
+
7
+ class SkillsIntegrationGenerator {
8
+ constructor() {
9
+ this.supportedCLIs = [
10
+ 'claude', 'gemini', 'qwen', 'codebuddy', 'codex',
11
+ 'iflow', 'qodercli', 'copilot', 'kode'
12
+ ];
13
+ }
14
+
15
+ generateForCLI(cliName) {
16
+ const currentProjectPath = process.cwd();
17
+ const skillsDir = path.join(os.homedir(), '.stigmergy', 'skills');
18
+
19
+ switch (cliName.toLowerCase()) {
20
+ case 'claude':
21
+ return {
22
+ fileName: 'claude_skills_hook.py',
23
+ content: this.generateClaudeSkillsIntegration(currentProjectPath, skillsDir)
24
+ };
25
+ case 'gemini':
26
+ return {
27
+ fileName: 'skills-extension.js',
28
+ content: this.generateGeminiSkillsIntegration(currentProjectPath, skillsDir)
29
+ };
30
+ case 'qwen':
31
+ return {
32
+ fileName: 'skills-hook.js',
33
+ content: this.generateQwenSkillsIntegration(currentProjectPath, skillsDir)
34
+ };
35
+ case 'codebuddy':
36
+ return {
37
+ fileName: 'skills-integration.js',
38
+ content: this.generateCodeBuddySkillsIntegration(currentProjectPath, skillsDir)
39
+ };
40
+ case 'codex':
41
+ return {
42
+ fileName: 'skills-handler.js',
43
+ content: this.generateCodexSkillsIntegration(currentProjectPath, skillsDir)
44
+ };
45
+ case 'qodercli':
46
+ return {
47
+ fileName: 'skills-extension.js',
48
+ content: this.generateQoderCliSkillsIntegration(currentProjectPath, skillsDir)
49
+ };
50
+ default:
51
+ return {
52
+ fileName: 'skills-hook.js',
53
+ content: this.generateGenericSkillsIntegration(cliName, currentProjectPath, skillsDir)
54
+ };
55
+ }
56
+ }
57
+
58
+ generateClaudeSkillsIntegration(projectPath, skillsDir) {
59
+ return `"""
60
+ Claude CLI Skills Integration Hook
61
+ Auto-generated by Stigmergy CLI v1.2.1
62
+ Project: ${projectPath.replace(/\\/g, '/')}
63
+ Skills Directory: ${skillsDir.replace(/\\/g, '/')}
64
+
65
+ This hook provides skills integration for Claude CLI with dynamic loading
66
+ and execution of skills from the centralized skills directory.
67
+ """
68
+
69
+ import sys
70
+ import os
71
+ import json
72
+ import importlib.util
73
+ from pathlib import Path
74
+ from typing import Dict, List, Optional, Any
75
+
76
+ class ClaudeSkillsHook:
77
+ def __init__(self):
78
+ self.skills_dir = Path("${skillsDir.replace(/\\/g, '/')}")
79
+ self.loaded_skills = {}
80
+ self.skill_metadata = {}
81
+
82
+ def initialize(self):
83
+ """Initialize the skills hook"""
84
+ print("[CLAUDE_SKILLS] Initializing skills integration...")
85
+ self.load_skills()
86
+ print(f"[CLAUDE_SKILLS] Loaded {len(self.loaded_skills)} skills")
87
+
88
+ def load_skills(self):
89
+ """Dynamically load all available skills"""
90
+ if not self.skills_dir.exists():
91
+ print(f"[CLAUDE_SKILLS] Skills directory not found: {self.skills_dir}")
92
+ return
93
+
94
+ try:
95
+ for skill_dir in self.skills_dir.iterdir():
96
+ if skill_dir.is_dir() and (skill_dir / 'SKILL.md').exists():
97
+ skill = self.load_skill(skill_dir)
98
+ if skill:
99
+ self.loaded_skills[skill_dir.name] = skill
100
+ self.skill_metadata[skill_dir.name] = self.parse_skill_metadata(skill_dir)
101
+ print(f"[CLAUDE_SKILLS] Loaded skill: {skill_dir.name}")
102
+ except Exception as e:
103
+ print(f"[CLAUDE_SKILLS] Failed to load skills: {e}")
104
+
105
+ def load_skill(self, skill_dir):
106
+ """Load a single skill from directory"""
107
+ try:
108
+ # Check for skill implementation files in scripts subdirectory
109
+ scripts_dir = skill_dir / 'scripts'
110
+ if scripts_dir.exists() and scripts_dir.is_dir():
111
+ for py_file in scripts_dir.glob("*.py"):
112
+ if py_file.name != "test_skill.py": # Skip test files
113
+ spec = importlib.util.spec_from_file_location(
114
+ f"{skill_dir.name}.{py_file.stem}", py_file
115
+ )
116
+ if spec and spec.loader:
117
+ module = importlib.util.module_from_spec(spec)
118
+ spec.loader.exec_module(module)
119
+ print(f"[CLAUDE_SKILLS] Loaded module: {py_file.name}")
120
+ return module
121
+
122
+ # Fallback: check for Python files in root directory
123
+ for py_file in skill_dir.glob("*.py"):
124
+ if py_file.name != "test_skill.py": # Skip test files
125
+ spec = importlib.util.spec_from_file_location(
126
+ skill_dir.name, py_file
127
+ )
128
+ if spec and spec.loader:
129
+ module = importlib.util.module_from_spec(spec)
130
+ spec.loader.exec_module(module)
131
+ print(f"[CLAUDE_SKILLS] Loaded module: {py_file.name}")
132
+ return module
133
+
134
+ # If no Python files found, return the skill directory info
135
+ # This allows skills to be registered even without Python implementation
136
+ print(f"[CLAUDE_SKILLS] No Python files found, registering skill directory")
137
+ return {'type': 'directory', 'path': skill_dir}
138
+
139
+ except Exception as e:
140
+ print(f"[CLAUDE_SKILLS] Failed to load skill {skill_dir.name}: {e}")
141
+ return None
142
+
143
+ def parse_skill_metadata(self, skill_dir):
144
+ """Parse skill metadata from SKILL.md"""
145
+ try:
146
+ skill_md = skill_dir / 'SKILL.md'
147
+ if skill_md.exists():
148
+ content = skill_md.read_text(encoding='utf-8')
149
+ metadata = {}
150
+
151
+ # Parse YAML-style frontmatter
152
+ lines = content.split('\\n')
153
+ in_frontmatter = False
154
+ for line in lines:
155
+ if line.strip() == '---':
156
+ in_frontmatter = not in_frontmatter
157
+ continue
158
+ if in_frontmatter:
159
+ if ':' in line:
160
+ key, value = line.split(':', 1)
161
+ metadata[key.strip()] = value.strip()
162
+ else:
163
+ # Fallback to old format
164
+ if line.startswith('## Description:'):
165
+ metadata['description'] = line.replace('## Description:', '').strip()
166
+ elif line.startswith('## Usage:'):
167
+ metadata['usage'] = line.replace('## Usage:', '').strip()
168
+ elif line.startswith('## Location:'):
169
+ metadata['location'] = line.replace('## Location:', '').strip()
170
+
171
+ return metadata
172
+ except Exception as e:
173
+ print(f"[CLAUDE_SKILLS] Failed to parse metadata for {skill_dir.name}: {e}")
174
+
175
+ return {}
176
+
177
+ def find_relevant_skills(self, prompt: str) -> List[str]:
178
+ """Find skills relevant to the given prompt"""
179
+ relevant_skills = []
180
+ prompt_lower = prompt.lower()
181
+
182
+ for skill_name, metadata in self.skill_metadata.items():
183
+ if self.is_skill_relevant(metadata, prompt_lower):
184
+ relevant_skills.append(skill_name)
185
+
186
+ return relevant_skills
187
+
188
+ def is_skill_relevant(self, metadata: Dict, prompt: str) -> bool:
189
+ """Check if a skill is relevant to the prompt"""
190
+ description = metadata.get('description', '').lower()
191
+ usage = metadata.get('usage', '').lower()
192
+
193
+ # Simple keyword matching - can be enhanced with more sophisticated NLP
194
+ keywords = ['create', 'design', 'build', 'generate', 'analyze', 'process', 'document', 'code']
195
+
196
+ # Check if any keywords from the skill match the prompt
197
+ skill_text = f"{description} {usage}"
198
+
199
+ # Look for matches between skill description and prompt
200
+ for word in skill_text.split():
201
+ if len(word) > 3 and word in prompt:
202
+ return True
203
+
204
+ return False
205
+
206
+ def execute_skill(self, skill_name: str, prompt: str, context: Dict = None) -> Optional[str]:
207
+ """Execute a specific skill"""
208
+ if skill_name not in self.loaded_skills:
209
+ print(f"[CLAUDE_SKILLS] Skill not found: {skill_name}")
210
+ return None
211
+
212
+ try:
213
+ skill_module = self.loaded_skills[skill_name]
214
+ metadata = self.skill_metadata[skill_name]
215
+
216
+ # Call the skill if it has a main function
217
+ if hasattr(skill_module, 'main') or hasattr(skill_module, 'execute'):
218
+ execute_func = getattr(skill_module, 'main') or getattr(skill_module, 'execute')
219
+ result = execute_func(prompt, context or {})
220
+ return result
221
+ else:
222
+ return f"[SKILL: {skill_name}] Available but no execution function found"
223
+
224
+ except Exception as e:
225
+ print(f"[CLAUDE_SKILLS] Error executing skill {skill_name}: {e}")
226
+ return f"[SKILL: {skill_name}] Error: {str(e)}"
227
+
228
+ def on_prompt(self, prompt: str, context: Dict = None) -> List[str]:
229
+ """Handle prompt and execute relevant skills"""
230
+ if not self.loaded_skills:
231
+ self.initialize()
232
+
233
+ relevant_skills = self.find_relevant_skills(prompt)
234
+ results = []
235
+
236
+ for skill_name in relevant_skills:
237
+ result = self.execute_skill(skill_name, prompt, context)
238
+ if result:
239
+ results.append(result)
240
+
241
+ return results
242
+
243
+ # Global hook instance
244
+ hook = ClaudeSkillsHook()
245
+
246
+ def on_prompt(prompt, context=None):
247
+ """Main hook function called by Claude CLI"""
248
+ return hook.on_prompt(prompt, context)
249
+
250
+ def initialize():
251
+ """Initialize the hook"""
252
+ return hook.initialize()
253
+
254
+ # Export functions for Claude's hook system
255
+ __all__ = ['on_prompt', 'initialize']
256
+ `;
257
+ }
258
+
259
+ generateGeminiSkillsIntegration(projectPath, skillsDir) {
260
+ return `// Gemini CLI Skills Integration Extension
261
+ // Auto-generated by Stigmergy CLI v1.2.1
262
+ // Project: ${projectPath.replace(/\\/g, '/')}
263
+ // Skills Directory: ${skillsDir.replace(/\\/g, '/')}
264
+
265
+ const fs = require('fs');
266
+ const path = require('path');
267
+ const os = require('os');
268
+
269
+ class GeminiSkillsExtension {
270
+ constructor() {
271
+ this.skillsDir = path.join('${skillsDir.replace(/\\/g, '/')}');
272
+ this.loadedSkills = new Map();
273
+ this.skillMetadata = new Map();
274
+ }
275
+
276
+ async initialize() {
277
+ console.log('[GEMINI_SKILLS] Initializing skills extension...');
278
+ await this.loadSkills();
279
+ console.log(\`[GEMINI_SKILLS] Loaded \${this.loadedSkills.size} skills\`);
280
+ }
281
+
282
+ async loadSkills() {
283
+ try {
284
+ if (!fs.existsSync(this.skillsDir)) {
285
+ console.log(\`[GEMINI_SKILLS] Skills directory not found: \${this.skillsDir}\`);
286
+ return;
287
+ }
288
+
289
+ const skillDirs = fs.readdirSync(this.skillsDir, { withFileTypes: true })
290
+ .filter(dirent => dirent.isDirectory())
291
+ .map(dirent => dirent.name);
292
+
293
+ for (const skillName of skillDirs) {
294
+ const skillPath = path.join(this.skillsDir, skillName);
295
+ const skillMdPath = path.join(skillPath, 'SKILL.md');
296
+
297
+ if (fs.existsSync(skillMdPath)) {
298
+ const metadata = this.parseSkillMetadata(skillMdPath);
299
+ this.skillMetadata.set(skillName, metadata);
300
+
301
+ // Load skill implementation if available
302
+ const skill = await this.loadSkill(skillPath);
303
+ if (skill) {
304
+ this.loadedSkills.set(skillName, skill);
305
+ console.log(\`[GEMINI_SKILLS] Loaded skill: \${skillName}\`);
306
+ }
307
+ }
308
+ }
309
+ } catch (error) {
310
+ console.error('[GEMINI_SKILLS] Failed to load skills:', error);
311
+ }
312
+ }
313
+
314
+ async loadSkill(skillPath) {
315
+ try {
316
+ // Check for skill implementation files in scripts subdirectory
317
+ const scriptsDir = path.join(skillPath, 'scripts');
318
+ if (fs.existsSync(scriptsDir) && fs.statSync(scriptsDir).isDirectory()) {
319
+ const jsFiles = fs.readdirSync(scriptsDir).filter(file => file.endsWith('.js'));
320
+
321
+ for (const jsFile of jsFiles) {
322
+ if (!jsFile.includes('test')) {
323
+ const skillModulePath = path.join(scriptsDir, jsFile);
324
+ try {
325
+ delete require.cache[require.resolve(skillModulePath)];
326
+ const skillModule = require(skillModulePath);
327
+ console.log(\`[GEMINI_SKILLS] Loaded module: \${jsFile}\`);
328
+ return skillModule;
329
+ } catch (moduleError) {
330
+ console.warn(\`[GEMINI_SKILLS] Could not load module \${jsFile}: \${moduleError.message}\`);
331
+ }
332
+ }
333
+ }
334
+ }
335
+
336
+ // Fallback: check for JavaScript files in root directory
337
+ const jsFiles = fs.readdirSync(skillPath).filter(file => file.endsWith('.js'));
338
+
339
+ for (const jsFile of jsFiles) {
340
+ if (!jsFile.includes('test')) {
341
+ const skillModulePath = path.join(skillPath, jsFile);
342
+ try {
343
+ delete require.cache[require.resolve(skillModulePath)];
344
+ const skillModule = require(skillModulePath);
345
+ console.log(\`[GEMINI_SKILLS] Loaded module: \${jsFile}\`);
346
+ return skillModule;
347
+ } catch (moduleError) {
348
+ console.warn(\`[GEMINI_SKILLS] Could not load module \${jsFile}: \${moduleError.message}\`);
349
+ }
350
+ }
351
+ }
352
+
353
+ // If no JavaScript files found, return skill directory info
354
+ console.log(\`[GEMINI_SKILLS] No JavaScript files found, registering skill directory\`);
355
+ return { type: 'directory', path: skillPath };
356
+
357
+ } catch (error) {
358
+ console.error(\`[GEMINI_SKILLS] Failed to load skill from \${skillPath}:\`, error);
359
+ }
360
+ return null;
361
+ }
362
+
363
+ parseSkillMetadata(skillMdPath) {
364
+ try {
365
+ const content = fs.readFileSync(skillMdPath, 'utf8');
366
+ const metadata = {};
367
+
368
+ for (const line of content.split('\\n')) {
369
+ if (line.startsWith('## Description:')) {
370
+ metadata.description = line.replace('## Description:', '').trim();
371
+ } else if (line.startsWith('## Usage:')) {
372
+ metadata.usage = line.replace('## Usage:', '').trim();
373
+ } else if (line.startsWith('## Location:')) {
374
+ metadata.location = line.replace('## Location:', '').trim();
375
+ }
376
+ }
377
+
378
+ return metadata;
379
+ } catch (error) {
380
+ console.error(\`[GEMINI_SKILLS] Failed to parse metadata from \${skillMdPath}:\`, error);
381
+ return {};
382
+ }
383
+ }
384
+
385
+ async findRelevantSkills(prompt) {
386
+ const relevant = new Map();
387
+ const promptLower = prompt.toLowerCase();
388
+
389
+ for (const [skillName, metadata] of this.skillMetadata) {
390
+ if (this.isSkillRelevant(metadata, promptLower)) {
391
+ relevant.set(skillName, metadata);
392
+ }
393
+ }
394
+ return relevant;
395
+ }
396
+
397
+ isSkillRelevant(metadata, prompt) {
398
+ const description = (metadata.description || '').toLowerCase();
399
+ const usage = (metadata.usage || '').toLowerCase();
400
+ const skillText = \`\${description} \${usage}\`;
401
+
402
+ // Enhanced relevance checking
403
+ const promptWords = prompt.split(' ').filter(word => word.length > 3);
404
+ const skillWords = skillText.split(' ').filter(word => word.length > 3);
405
+
406
+ return promptWords.some(promptWord =>
407
+ skillWords.some(skillWord =>
408
+ promptWord.includes(skillWord) || skillWord.includes(promptWord)
409
+ )
410
+ );
411
+ }
412
+
413
+ async executeSkill(skillName, input, session) {
414
+ if (!this.loadedSkills.has(skillName)) {
415
+ return \`[SKILL: \${skillName}] Skill not available\`;
416
+ }
417
+
418
+ try {
419
+ const skill = this.loadedSkills.get(skillName);
420
+ const metadata = this.skillMetadata.get(skillName);
421
+
422
+ // Try different execution patterns
423
+ if (typeof skill === 'function') {
424
+ const result = await skill(input, { session, metadata });
425
+ return result;
426
+ } else if (skill && typeof skill === 'object') {
427
+ if (skill.execute && typeof skill.execute === 'function') {
428
+ const result = await skill.execute(input, { session, metadata });
429
+ return result;
430
+ } else if (skill.main && typeof skill.main === 'function') {
431
+ const result = await skill.main(input, { session, metadata });
432
+ return result;
433
+ }
434
+ }
435
+
436
+ return \`[SKILL: \${skillName}] Available but no execution function found\`;
437
+ } catch (error) {
438
+ console.error(\`[GEMINI_SKILLS] Error executing skill \${skillName}:\`, error);
439
+ return \`[SKILL: \${skillName}] Error: \${error.message}\`;
440
+ }
441
+ }
442
+
443
+ async handleCommand(input, session) {
444
+ if (!this.shouldHandleSkillCommand(input)) {
445
+ return null;
446
+ }
447
+
448
+ try {
449
+ if (this.loadedSkills.size === 0) {
450
+ await this.initialize();
451
+ }
452
+
453
+ const relevantSkills = await this.findRelevantSkills(input);
454
+ const results = [];
455
+
456
+ for (const [skillName, metadata] of relevantSkills) {
457
+ const result = await this.executeSkill(skillName, input, session);
458
+ if (result) {
459
+ results.push(result);
460
+ }
461
+ }
462
+
463
+ if (results.length === 0) {
464
+ return {
465
+ text: 'No relevant skills found for your request.',
466
+ continue: true,
467
+ suggestions: ['Try "skills list" to see available skills']
468
+ };
469
+ }
470
+
471
+ return {
472
+ text: results.join('\\n\\n'),
473
+ continue: true,
474
+ suggestions: this.generateSkillSuggestions(Array.from(relevantSkills.keys()))
475
+ };
476
+ } catch (error) {
477
+ console.error('[GEMINI_SKILLS] Error handling skill command:', error);
478
+ return {
479
+ text: \`Error executing skills: \${error.message}\`,
480
+ continue: true
481
+ };
482
+ }
483
+ }
484
+
485
+ shouldHandleSkillCommand(input) {
486
+ // Handle skill-related commands
487
+ const skillKeywords = ['create', 'design', 'build', 'generate', 'analyze', 'skill use'];
488
+ const inputLower = input.toLowerCase();
489
+
490
+ return skillKeywords.some(keyword => inputLower.includes(keyword)) ||
491
+ inputLower.startsWith('skill') ||
492
+ this.loadedSkills.size > 0; // If skills are loaded, try to handle any input
493
+ }
494
+
495
+ generateSkillSuggestions(skillNames) {
496
+ return skillNames.map(name => \`Try skill: \${name}\`);
497
+ }
498
+ }
499
+
500
+ // Gemini CLI integration
501
+ const extension = new GeminiSkillsExtension();
502
+
503
+ if (typeof module !== 'undefined' && module.exports) {
504
+ module.exports = { GeminiSkillsExtension, extension };
505
+ }
506
+
507
+ if (typeof geminiCLI !== 'undefined') {
508
+ extension.initialize();
509
+ geminiCLI.addCommandHandler('skills', extension.handleCommand.bind(extension));
510
+ }
511
+ `;
512
+ }
513
+
514
+ generateQwenSkillsIntegration(projectPath, skillsDir) {
515
+ return `// Qwen CLI Skills Integration Hook
516
+ // Auto-generated by Stigmergy CLI v1.2.1
517
+ // Project: ${projectPath.replace(/\\/g, '/')}
518
+ // Skills Directory: ${skillsDir.replace(/\\/g, '/')}
519
+
520
+ const fs = require('fs');
521
+ const path = require('path');
522
+
523
+ class QwenSkillsHook {
524
+ constructor() {
525
+ this.toolName = 'qwen';
526
+ this.skillsDir = path.join('${skillsDir.replace(/\\/g, '/')}');
527
+ this.loadedSkills = new Map();
528
+ }
529
+
530
+ async initialize() {
531
+ console.log('[QWEN_SKILLS] Initializing skills hook...');
532
+ await this.loadSkills();
533
+ console.log(\`[QWEN_SKILLS] Loaded \${this.loadedSkills.size} skills\`);
534
+ }
535
+
536
+ async loadSkills() {
537
+ try {
538
+ if (!fs.existsSync(this.skillsDir)) {
539
+ console.log(\`[QWEN_SKILLS] Skills directory not found: \${this.skillsDir}\`);
540
+ return;
541
+ }
542
+
543
+ const skillDirs = fs.readdirSync(this.skillsDir, { withFileTypes: true })
544
+ .filter(dirent => dirent.isDirectory())
545
+ .map(dirent => dirent.name);
546
+
547
+ for (const skillName of skillDirs) {
548
+ const skillPath = path.join(this.skillsDir, skillName);
549
+ const skillMdPath = path.join(skillPath, 'SKILL.md');
550
+
551
+ if (fs.existsSync(skillMdPath)) {
552
+ const skill = await this.loadSkill(skillPath);
553
+ if (skill) {
554
+ this.loadedSkills.set(skillName, skill);
555
+ console.log(\`[QWEN_SKILLS] Loaded skill: \${skillName}\`);
556
+ }
557
+ }
558
+ }
559
+ } catch (error) {
560
+ console.error('[QWEN_SKILLS] Failed to load skills:', error);
561
+ }
562
+ }
563
+
564
+ async loadSkill(skillPath) {
565
+ try {
566
+ // Check for skill implementation files in scripts subdirectory
567
+ const scriptsDir = path.join(skillPath, 'scripts');
568
+ if (fs.existsSync(scriptsDir) && fs.statSync(scriptsDir).isDirectory()) {
569
+ const files = fs.readdirSync(scriptsDir);
570
+ const jsFiles = files.filter(file => file.endsWith('.js') && !file.includes('test'));
571
+
572
+ for (const jsFile of jsFiles) {
573
+ const skillModulePath = path.join(scriptsDir, jsFile);
574
+ try {
575
+ const skillModule = require(skillModulePath);
576
+ console.log(\`[QWEN_SKILLS] Loaded module: \${jsFile}\`);
577
+ return skillModule;
578
+ } catch (moduleError) {
579
+ console.warn(\`[QWEN_SKILLS] Could not load \${jsFile}: \${moduleError.message}\`);
580
+ }
581
+ }
582
+ }
583
+
584
+ // Fallback: check for JavaScript files in root directory
585
+ const files = fs.readdirSync(skillPath);
586
+ const jsFiles = files.filter(file => file.endsWith('.js') && !file.includes('test'));
587
+
588
+ for (const jsFile of jsFiles) {
589
+ const skillModulePath = path.join(skillPath, jsFile);
590
+ try {
591
+ const skillModule = require(skillModulePath);
592
+ console.log(\`[QWEN_SKILLS] Loaded module: \${jsFile}\`);
593
+ return skillModule;
594
+ } catch (moduleError) {
595
+ console.warn(\`[QWEN_SKILLS] Could not load \${jsFile}: \${moduleError.message}\`);
596
+ }
597
+ }
598
+
599
+ // If no JavaScript files found, return skill directory info
600
+ console.log(\`[QWEN_SKILLS] No JavaScript files found, registering skill directory\`);
601
+ return { type: 'directory', path: skillPath };
602
+
603
+ } catch (error) {
604
+ console.error(\`[QWEN_SKILLS] Failed to load skill from \${skillPath}:\`, error);
605
+ }
606
+ return null;
607
+ }
608
+
609
+ async findRelevantSkills(prompt) {
610
+ const relevant = new Map();
611
+ const promptLower = prompt.toLowerCase();
612
+
613
+ // Chinese keywords for better Qwen integration
614
+ const chineseKeywords = ['创建', '设计', '生成', '分析', '处理', '构建', '制作'];
615
+
616
+ for (const [skillName, skill] of this.loadedSkills) {
617
+ const skillDesc = (skill.description || '').toLowerCase();
618
+
619
+ if (this.isSkillRelevant(skillDesc, promptLower, chineseKeywords)) {
620
+ relevant.set(skillName, skill);
621
+ }
622
+ }
623
+ return relevant;
624
+ }
625
+
626
+ isSkillRelevant(skillDesc, prompt, chineseKeywords) {
627
+ const promptWords = prompt.split(' ');
628
+ const skillWords = skillDesc.split(' ');
629
+
630
+ return promptWords.some(promptWord =>
631
+ skillWords.some(skillWord =>
632
+ promptWord.includes(skillWord) || skillWord.includes(promptWord) ||
633
+ chineseKeywords.some(keyword =>
634
+ prompt.includes(keyword) && skillDesc.includes(keyword)
635
+ )
636
+ )
637
+ );
638
+ }
639
+
640
+ async executeSkill(skillName, prompt) {
641
+ if (!this.loadedSkills.has(skillName)) {
642
+ return \`[技能: \${skillName}] 未找到该技能\`;
643
+ }
644
+
645
+ try {
646
+ const skill = this.loadedSkills.get(skillName);
647
+
648
+ if (typeof skill === 'function') {
649
+ const result = await skill(prompt);
650
+ return result;
651
+ } else if (skill && typeof skill === 'object') {
652
+ if (skill.execute && typeof skill.execute === 'function') {
653
+ const result = await skill.execute(prompt);
654
+ return result;
655
+ }
656
+ }
657
+
658
+ return \`[技能: \${skillName}] 可用但无执行函数\`;
659
+ } catch (error) {
660
+ console.error(\`[QWEN_SKILLS] 执行技能 \${skillName} 出错:\`, error);
661
+ return \`[技能: \${skillName}] 错误: \${error.message}\`;
662
+ }
663
+ }
664
+
665
+ async onPrompt(prompt) {
666
+ try {
667
+ if (this.loadedSkills.size === 0) {
668
+ await this.initialize();
669
+ }
670
+
671
+ const relevantSkills = await this.findRelevantSkills(prompt);
672
+ const results = [];
673
+
674
+ for (const [skillName, skill] of relevantSkills) {
675
+ const result = await this.executeSkill(skillName, prompt);
676
+ if (result) {
677
+ results.push(result);
678
+ }
679
+ }
680
+
681
+ if (results.length > 0) {
682
+ return {
683
+ handled: true,
684
+ response: results.join('\\n\\n')
685
+ };
686
+ }
687
+
688
+ return { handled: false };
689
+ } catch (error) {
690
+ console.error('[QWEN_SKILLS] Error processing prompt:', error);
691
+ return {
692
+ handled: true,
693
+ response: \`技能处理错误: \${error.message}\`
694
+ };
695
+ }
696
+ }
697
+ }
698
+
699
+ module.exports = QwenSkillsHook;
700
+ `;
701
+ }
702
+
703
+ generateCodeBuddySkillsIntegration(projectPath, skillsDir) {
704
+ return `// CodeBuddy CLI Skills Integration
705
+ // Auto-generated by Stigmergy CLI v1.2.1
706
+ // Project: ${projectPath.replace(/\\/g, '/')}
707
+ // Skills Directory: ${skillsDir.replace(/\\/g, '/')}
708
+
709
+ const fs = require('fs');
710
+ const path = require('path');
711
+
712
+ class CodeBuddySkillsIntegration {
713
+ constructor() {
714
+ this.toolName = 'codebuddy';
715
+ this.skillsDir = path.join('${skillsDir.replace(/\\/g, '/')}');
716
+ this.loadedSkills = new Map();
717
+ }
718
+
719
+ async initialize() {
720
+ console.log('[CODEBUDDY_SKILLS] Initializing skills integration...');
721
+ await this.loadSkills();
722
+ console.log(\`[CODEBUDDY_SKILLS] Loaded \${this.loadedSkills.size} skills\`);
723
+ }
724
+
725
+ async loadSkills() {
726
+ try {
727
+ if (!fs.existsSync(this.skillsDir)) {
728
+ console.log(\`[CODEBUDDY_SKILLS] Skills directory not found: \${this.skillsDir}\`);
729
+ return;
730
+ }
731
+
732
+ const skillDirs = fs.readdirSync(this.skillsDir, { withFileTypes: true })
733
+ .filter(dirent => dirent.isDirectory())
734
+ .map(dirent => dirent.name);
735
+
736
+ for (const skillName of skillDirs) {
737
+ const skillPath = path.join(this.skillsDir, skillName);
738
+ const skillMdPath = path.join(skillPath, 'SKILL.md');
739
+
740
+ if (fs.existsSync(skillMdPath)) {
741
+ const skill = await this.loadSkill(skillPath);
742
+ if (skill) {
743
+ this.loadedSkills.set(skillName, skill);
744
+ console.log(\`[CODEBUDDY_SKILLS] Loaded skill: \${skillName}\`);
745
+ }
746
+ }
747
+ }
748
+ } catch (error) {
749
+ console.error('[CODEBUDDY_SKILLS] Failed to load skills:', error);
750
+ }
751
+ }
752
+
753
+ async loadSkill(skillPath) {
754
+ try {
755
+ // Check for skill implementation files in scripts subdirectory
756
+ const scriptsDir = path.join(skillPath, 'scripts');
757
+ if (fs.existsSync(scriptsDir) && fs.statSync(scriptsDir).isDirectory()) {
758
+ const files = fs.readdirSync(scriptsDir);
759
+ const jsFiles = files.filter(file => file.endsWith('.js') && !file.includes('test'));
760
+
761
+ for (const jsFile of jsFiles) {
762
+ const skillModulePath = path.join(scriptsDir, jsFile);
763
+ try {
764
+ const skillModule = require(skillModulePath);
765
+ console.log(\`[CODEBUDDY_SKILLS] Loaded module: \${jsFile}\`);
766
+ return skillModule;
767
+ } catch (moduleError) {
768
+ console.warn(\`[CODEBUDDY_SKILLS] Could not load \${jsFile}: \${moduleError.message}\`);
769
+ }
770
+ }
771
+ }
772
+
773
+ // Fallback: check for JavaScript files in root directory
774
+ const files = fs.readdirSync(skillPath);
775
+ const jsFiles = files.filter(file => file.endsWith('.js') && !file.includes('test'));
776
+
777
+ for (const jsFile of jsFiles) {
778
+ const skillModulePath = path.join(skillPath, jsFile);
779
+ try {
780
+ const skillModule = require(skillModulePath);
781
+ console.log(\`[CODEBUDDY_SKILLS] Loaded module: \${jsFile}\`);
782
+ return skillModule;
783
+ } catch (moduleError) {
784
+ console.warn(\`[CODEBUDDY_SKILLS] Could not load \${jsFile}: \${moduleError.message}\`);
785
+ }
786
+ }
787
+
788
+ // If no JavaScript files found, return skill directory info
789
+ console.log(\`[CODEBUDDY_SKILLS] No JavaScript files found, registering skill directory\`);
790
+ return { type: 'directory', path: skillPath };
791
+
792
+ } catch (error) {
793
+ console.error(\`[CODEBUDDY_SKILLS] Failed to load skill from \${skillPath}:\`, error);
794
+ }
795
+ return null;
796
+ }
797
+
798
+ async findRelevantSkills(prompt) {
799
+ const relevant = new Map();
800
+ const promptLower = prompt.toLowerCase();
801
+
802
+ // CodeBuddy-specific keywords
803
+ const codeKeywords = ['code', 'function', 'class', 'algorithm', 'debug', 'refactor', 'test'];
804
+
805
+ for (const [skillName, skill] of this.loadedSkills) {
806
+ if (this.isSkillRelevant(skill, promptLower, codeKeywords)) {
807
+ relevant.set(skillName, skill);
808
+ }
809
+ }
810
+ return relevant;
811
+ }
812
+
813
+ isSkillRelevant(skill, prompt, codeKeywords) {
814
+ const description = (skill.description || '').toLowerCase();
815
+
816
+ return codeKeywords.some(keyword =>
817
+ prompt.includes(keyword) && description.includes(keyword)
818
+ );
819
+ }
820
+
821
+ async executeSkill(skillName, prompt) {
822
+ if (!this.loadedSkills.has(skillName)) {
823
+ return \`[SKILL: \${skillName}] Skill not found\`;
824
+ }
825
+
826
+ try {
827
+ const skill = this.loadedSkills.get(skillName);
828
+
829
+ if (typeof skill === 'function') {
830
+ const result = await skill(prompt);
831
+ return result;
832
+ } else if (skill && typeof skill === 'object') {
833
+ if (skill.execute && typeof skill.execute === 'function') {
834
+ const result = await skill.execute(prompt);
835
+ return result;
836
+ }
837
+ }
838
+
839
+ return \`[SKILL: \${skillName}] Available but no execution function\`;
840
+ } catch (error) {
841
+ console.error(\`[CODEBUDDY_SKILLS] Error executing skill \${skillName}:\`, error);
842
+ return \`[SKILL: \${skillName}] Error: \${error.message}\`;
843
+ }
844
+ }
845
+
846
+ async onPrompt(prompt) {
847
+ try {
848
+ if (this.loadedSkills.size === 0) {
849
+ await this.initialize();
850
+ }
851
+
852
+ const relevantSkills = await this.findRelevantSkills(prompt);
853
+ const results = [];
854
+
855
+ for (const [skillName, skill] of relevantSkills) {
856
+ const result = await this.executeSkill(skillName, prompt);
857
+ if (result) {
858
+ results.push(result);
859
+ }
860
+ }
861
+
862
+ if (results.length > 0) {
863
+ return {
864
+ handled: true,
865
+ response: results.join('\\n\\n')
866
+ };
867
+ }
868
+
869
+ return { handled: false };
870
+ } catch (error) {
871
+ console.error('[CODEBUDDY_SKILLS] Error processing prompt:', error);
872
+ return {
873
+ handled: true,
874
+ response: \`Skill processing error: \${error.message}\`
875
+ };
876
+ }
877
+ }
878
+ }
879
+
880
+ // CodeBuddy CLI integration
881
+ const skillsIntegration = new CodeBuddySkillsIntegration();
882
+
883
+ if (typeof codebuddy !== 'undefined' && codebuddy.addIntegration) {
884
+ codebuddy.addIntegration('skills', skillsIntegration.onPrompt.bind(skillsIntegration));
885
+ }
886
+
887
+ module.exports = CodeBuddySkillsIntegration;
888
+ `;
889
+ }
890
+
891
+ generateCodexSkillsIntegration(projectPath, skillsDir) {
892
+ return `// Codex CLI Skills Integration Handler
893
+ // Auto-generated by Stigmergy CLI v1.2.1
894
+ // Project: ${projectPath.replace(/\\/g, '/')}
895
+ // Skills Directory: ${skillsDir.replace(/\\/g, '/')}
896
+
897
+ const fs = require('fs');
898
+ const path = require('path');
899
+
900
+ class CodexSkillsHandler {
901
+ constructor() {
902
+ this.toolName = 'codex';
903
+ this.skillsDir = path.join('${skillsDir.replace(/\\/g, '/')}');
904
+ this.loadedSkills = new Map();
905
+ }
906
+
907
+ async initialize() {
908
+ console.log('[CODEX_SKILLS] Initializing skills handler...');
909
+ await this.loadSkills();
910
+ console.log(\`[CODEX_SKILLS] Loaded \${this.loadedSkills.size} skills\`);
911
+ }
912
+
913
+ async loadSkills() {
914
+ try {
915
+ if (!fs.existsSync(this.skillsDir)) {
916
+ console.log(\`[CODEX_SKILLS] Skills directory not found: \${this.skillsDir}\`);
917
+ return;
918
+ }
919
+
920
+ const skillDirs = fs.readdirSync(this.skillsDir, { withFileTypes: true })
921
+ .filter(dirent => dirent.isDirectory())
922
+ .map(dirent => dirent.name);
923
+
924
+ for (const skillName of skillDirs) {
925
+ const skillPath = path.join(this.skillsDir, skillName);
926
+ const skillMdPath = path.join(skillPath, 'SKILL.md');
927
+
928
+ if (fs.existsSync(skillMdPath)) {
929
+ const skill = await this.loadSkill(skillPath);
930
+ if (skill) {
931
+ this.loadedSkills.set(skillName, skill);
932
+ console.log(\`[CODEX_SKILLS] Loaded skill: \${skillName}\`);
933
+ }
934
+ }
935
+ }
936
+ } catch (error) {
937
+ console.error('[CODEX_SKILLS] Failed to load skills:', error);
938
+ }
939
+ }
940
+
941
+ async loadSkill(skillPath) {
942
+ try {
943
+ // Check for skill implementation files in scripts subdirectory
944
+ const scriptsDir = path.join(skillPath, 'scripts');
945
+ if (fs.existsSync(scriptsDir) && fs.statSync(scriptsDir).isDirectory()) {
946
+ const files = fs.readdirSync(scriptsDir);
947
+ const jsFiles = files.filter(file => file.endsWith('.js') && !file.includes('test'));
948
+
949
+ for (const jsFile of jsFiles) {
950
+ const skillModulePath = path.join(scriptsDir, jsFile);
951
+ try {
952
+ const skillModule = require(skillModulePath);
953
+ console.log(\`[CODEX_SKILLS] Loaded module: \${jsFile}\`);
954
+ return skillModule;
955
+ } catch (moduleError) {
956
+ console.warn(\`[CODEX_SKILLS] Could not load \${jsFile}: \${moduleError.message}\`);
957
+ }
958
+ }
959
+ }
960
+
961
+ // Fallback: check for JavaScript files in root directory
962
+ const files = fs.readdirSync(skillPath);
963
+ const jsFiles = files.filter(file => file.endsWith('.js') && !file.includes('test'));
964
+
965
+ for (const jsFile of jsFiles) {
966
+ const skillModulePath = path.join(skillPath, jsFile);
967
+ try {
968
+ const skillModule = require(skillModulePath);
969
+ console.log(\`[CODEX_SKILLS] Loaded module: \${jsFile}\`);
970
+ return skillModule;
971
+ } catch (moduleError) {
972
+ console.warn(\`[CODEX_SKILLS] Could not load \${jsFile}: \${moduleError.message}\`);
973
+ }
974
+ }
975
+
976
+ // If no JavaScript files found, return skill directory info
977
+ console.log(\`[CODEX_SKILLS] No JavaScript files found, registering skill directory\`);
978
+ return { type: 'directory', path: skillPath };
979
+
980
+ } catch (error) {
981
+ console.error(\`[CODEX_SKILLS] Failed to load skill from \${skillPath}:\`, error);
982
+ }
983
+ return null;
984
+ }
985
+
986
+ async handleSkillRequest(input) {
987
+ try {
988
+ if (this.loadedSkills.size === 0) {
989
+ await this.initialize();
990
+ }
991
+
992
+ // Find relevant skills for the input
993
+ const relevantSkill = await this.findMostRelevantSkill(input);
994
+
995
+ if (relevantSkill) {
996
+ const result = await this.executeSkill(relevantSkill, input);
997
+ return {
998
+ success: true,
999
+ result: result,
1000
+ skillName: relevantSkill
1001
+ };
1002
+ }
1003
+
1004
+ return {
1005
+ success: false,
1006
+ message: 'No relevant skills found for the request'
1007
+ };
1008
+ } catch (error) {
1009
+ console.error('[CODEX_SKILLS] Error handling skill request:', error);
1010
+ return {
1011
+ success: false,
1012
+ error: error.message
1013
+ };
1014
+ }
1015
+ }
1016
+
1017
+ async findMostRelevantSkill(prompt) {
1018
+ const promptLower = prompt.toLowerCase();
1019
+ let bestMatch = null;
1020
+ let bestScore = 0;
1021
+
1022
+ for (const [skillName, skill] of this.loadedSkills) {
1023
+ const score = this.calculateRelevanceScore(skill, promptLower);
1024
+ if (score > bestScore) {
1025
+ bestScore = score;
1026
+ bestMatch = skillName;
1027
+ }
1028
+ }
1029
+
1030
+ return bestScore > 0.3 ? bestMatch : null;
1031
+ }
1032
+
1033
+ calculateRelevanceScore(skill, prompt) {
1034
+ const description = (skill.description || '').toLowerCase();
1035
+ const keywords = ['code', 'function', 'api', 'data', 'process', 'generate'];
1036
+
1037
+ let score = 0;
1038
+ for (const keyword of keywords) {
1039
+ if (prompt.includes(keyword) && description.includes(keyword)) {
1040
+ score += 0.2;
1041
+ }
1042
+ }
1043
+
1044
+ return Math.min(score, 1.0);
1045
+ }
1046
+
1047
+ async executeSkill(skillName, input) {
1048
+ if (!this.loadedSkills.has(skillName)) {
1049
+ return \`[SKILL: \${skillName}] Skill not found\`;
1050
+ }
1051
+
1052
+ try {
1053
+ const skill = this.loadedSkills.get(skillName);
1054
+
1055
+ if (typeof skill === 'function') {
1056
+ const result = await skill(input);
1057
+ return result;
1058
+ } else if (skill && typeof skill === 'object') {
1059
+ if (skill.execute && typeof skill.execute === 'function') {
1060
+ const result = await skill.execute(input);
1061
+ return result;
1062
+ }
1063
+ }
1064
+
1065
+ return \`[SKILL: \${skillName}] Available but no execution function\`;
1066
+ } catch (error) {
1067
+ console.error(\`[CODEX_SKILLS] Error executing skill \${skillName}:\`, error);
1068
+ return \`[SKILL: \${skillName}] Error: \${error.message}\`;
1069
+ }
1070
+ }
1071
+ }
1072
+
1073
+ // Codex CLI integration
1074
+ const skillsHandler = new CodexSkillsHandler();
1075
+
1076
+ module.exports = CodexSkillsHandler;
1077
+ `;
1078
+ }
1079
+
1080
+ generateQoderCliSkillsIntegration(projectPath, skillsDir) {
1081
+ return `// QoderCLI Skills Integration Extension
1082
+ // Auto-generated by Stigmergy CLI v1.2.1
1083
+ // Project: ${projectPath.replace(/\\/g, '/')}
1084
+ // Skills Directory: ${skillsDir.replace(/\\/g, '/')}
1085
+
1086
+ const fs = require('fs');
1087
+ const path = require('path');
1088
+
1089
+ class QoderCliSkillsExtension {
1090
+ constructor() {
1091
+ this.toolName = 'qodercli';
1092
+ this.skillsDir = path.join('${skillsDir.replace(/\\/g, '/')}');
1093
+ this.loadedSkills = new Map();
1094
+ }
1095
+
1096
+ async initialize() {
1097
+ console.log('[QODERCLI_SKILLS] Initializing skills extension...');
1098
+ await this.loadSkills();
1099
+ console.log(\`[QODERCLI_SKILLS] Loaded \${this.loadedSkills.size} skills\`);
1100
+ }
1101
+
1102
+ async loadSkills() {
1103
+ try {
1104
+ if (!fs.existsSync(this.skillsDir)) {
1105
+ console.log(\`[QODERCLI_SKILLS] Skills directory not found: \${this.skillsDir}\`);
1106
+ return;
1107
+ }
1108
+
1109
+ const skillDirs = fs.readdirSync(this.skillsDir, { withFileTypes: true })
1110
+ .filter(dirent => dirent.isDirectory())
1111
+ .map(dirent => dirent.name);
1112
+
1113
+ for (const skillName of skillDirs) {
1114
+ const skillPath = path.join(this.skillsDir, skillName);
1115
+ const skillMdPath = path.join(skillPath, 'SKILL.md');
1116
+
1117
+ if (fs.existsSync(skillMdPath)) {
1118
+ const skill = await this.loadSkill(skillPath);
1119
+ if (skill) {
1120
+ this.loadedSkills.set(skillName, skill);
1121
+ console.log(\`[QODERCLI_SKILLS] Loaded skill: \${skillName}\`);
1122
+ }
1123
+ }
1124
+ }
1125
+ } catch (error) {
1126
+ console.error('[QODERCLI_SKILLS] Failed to load skills:', error);
1127
+ }
1128
+ }
1129
+
1130
+ async loadSkill(skillPath) {
1131
+ try {
1132
+ // Check for skill implementation files in scripts subdirectory
1133
+ const scriptsDir = path.join(skillPath, 'scripts');
1134
+ if (fs.existsSync(scriptsDir) && fs.statSync(scriptsDir).isDirectory()) {
1135
+ const files = fs.readdirSync(scriptsDir);
1136
+ const jsFiles = files.filter(file => file.endsWith('.js') && !file.includes('test'));
1137
+
1138
+ for (const jsFile of jsFiles) {
1139
+ const skillModulePath = path.join(scriptsDir, jsFile);
1140
+ try {
1141
+ const skillModule = require(skillModulePath);
1142
+ console.log(\`[QODERCLI_SKILLS] Loaded module: \${jsFile}\`);
1143
+ return skillModule;
1144
+ } catch (moduleError) {
1145
+ console.warn(\`[QODERCLI_SKILLS] Could not load \${jsFile}: \${moduleError.message}\`);
1146
+ }
1147
+ }
1148
+ }
1149
+
1150
+ // Fallback: check for JavaScript files in root directory
1151
+ const files = fs.readdirSync(skillPath);
1152
+ const jsFiles = files.filter(file => file.endsWith('.js') && !file.includes('test'));
1153
+
1154
+ for (const jsFile of jsFiles) {
1155
+ const skillModulePath = path.join(skillPath, jsFile);
1156
+ try {
1157
+ const skillModule = require(skillModulePath);
1158
+ console.log(\`[QODERCLI_SKILLS] Loaded module: \${jsFile}\`);
1159
+ return skillModule;
1160
+ } catch (moduleError) {
1161
+ console.warn(\`[QODERCLI_SKILLS] Could not load \${jsFile}: \${moduleError.message}\`);
1162
+ }
1163
+ }
1164
+
1165
+ // If no JavaScript files found, return skill directory info
1166
+ console.log(\`[QODERCLI_SKILLS] No JavaScript files found, registering skill directory\`);
1167
+ return { type: 'directory', path: skillPath };
1168
+
1169
+ } catch (error) {
1170
+ console.error(\`[QODERCLI_SKILLS] Failed to load skill from \${skillPath}:\`, error);
1171
+ }
1172
+ return null;
1173
+ }
1174
+
1175
+ async handleSkillCommand(command, args) {
1176
+ try {
1177
+ if (this.loadedSkills.size === 0) {
1178
+ await this.initialize();
1179
+ }
1180
+
1181
+ if (command === 'list') {
1182
+ return this.listSkills();
1183
+ }
1184
+
1185
+ if (command === 'use' && args.length > 0) {
1186
+ return this.useSkill(args[0], args.slice(1));
1187
+ }
1188
+
1189
+ return {
1190
+ success: false,
1191
+ message: 'Usage: skill list | skill use <skill-name> [args...]'
1192
+ };
1193
+ } catch (error) {
1194
+ console.error('[QODERCLI_SKILLS] Error handling skill command:', error);
1195
+ return {
1196
+ success: false,
1197
+ error: error.message
1198
+ };
1199
+ }
1200
+ }
1201
+
1202
+ async listSkills() {
1203
+ const skillList = Array.from(this.loadedSkills.keys()).map(name => {
1204
+ const skill = this.loadedSkills.get(name);
1205
+ return {
1206
+ name,
1207
+ description: skill.description || 'No description available'
1208
+ };
1209
+ });
1210
+
1211
+ return {
1212
+ success: true,
1213
+ skills: skillList,
1214
+ message: \`Found \${skillList.length} available skills\`
1215
+ };
1216
+ }
1217
+
1218
+ async useSkill(skillName, args) {
1219
+ if (!this.loadedSkills.has(skillName)) {
1220
+ return {
1221
+ success: false,
1222
+ message: \`Skill '\${skillName}' not found\`
1223
+ };
1224
+ }
1225
+
1226
+ try {
1227
+ const skill = this.loadedSkills.get(skillName);
1228
+ const input = args.join(' ');
1229
+
1230
+ let result;
1231
+ if (typeof skill === 'function') {
1232
+ result = await skill(input);
1233
+ } else if (skill && typeof skill === 'object') {
1234
+ if (skill.execute && typeof skill.execute === 'function') {
1235
+ result = await skill.execute(input);
1236
+ } else if (skill.main && typeof skill.main === 'function') {
1237
+ result = await skill.main(input);
1238
+ }
1239
+ }
1240
+
1241
+ return {
1242
+ success: true,
1243
+ result: result || \`Skill '\${skillName}' executed\`,
1244
+ skillName
1245
+ };
1246
+ } catch (error) {
1247
+ console.error(\`[QODERCLI_SKILLS] Error using skill \${skillName}:\`, error);
1248
+ return {
1249
+ success: false,
1250
+ error: error.message
1251
+ };
1252
+ }
1253
+ }
1254
+ }
1255
+
1256
+ // QoderCLI integration
1257
+ const skillsExtension = new QoderCliSkillsExtension();
1258
+
1259
+ if (typeof qodercli !== 'undefined' && qodercli.addExtension) {
1260
+ qodercli.addExtension('skills', skillsExtension.handleSkillCommand.bind(skillsExtension));
1261
+ }
1262
+
1263
+ module.exports = QoderCliSkillsExtension;
1264
+ `;
1265
+ }
1266
+
1267
+ generateGenericSkillsIntegration(cliName, projectPath, skillsDir) {
1268
+ const className = `${cliName.charAt(0).toUpperCase() + cliName.slice(1)}SkillsHook`;
1269
+
1270
+ return `// ${className}
1271
+ // Auto-generated by Stigmergy CLI v1.2.1
1272
+ // Project: ${projectPath.replace(/\\/g, '/')}
1273
+ // Skills Directory: ${skillsDir.replace(/\\/g, '/')}
1274
+
1275
+ const fs = require('fs');
1276
+ const path = require('path');
1277
+
1278
+ class ${className} {
1279
+ constructor() {
1280
+ this.toolName = '${cliName}';
1281
+ this.skillsDir = path.join('${skillsDir.replace(/\\/g, '/')}');
1282
+ this.loadedSkills = new Map();
1283
+ }
1284
+
1285
+ async initialize() {
1286
+ console.log(\`[\${this.toolName.toUpperCase()}_SKILLS] Initializing skills hook...\`);
1287
+ await this.loadSkills();
1288
+ console.log(\`[\${this.toolName.toUpperCase()}_SKILLS] Loaded \${this.loadedSkills.size} skills\`);
1289
+ }
1290
+
1291
+ async loadSkills() {
1292
+ try {
1293
+ if (!fs.existsSync(this.skillsDir)) {
1294
+ console.log(\`[\${this.toolName.toUpperCase()}_SKILLS] Skills directory not found: \${this.skillsDir}\`);
1295
+ return;
1296
+ }
1297
+
1298
+ const skillDirs = fs.readdirSync(this.skillsDir, { withFileTypes: true })
1299
+ .filter(dirent => dirent.isDirectory())
1300
+ .map(dirent => dirent.name);
1301
+
1302
+ for (const skillName of skillDirs) {
1303
+ const skillPath = path.join(this.skillsDir, skillName);
1304
+ const skillMdPath = path.join(skillPath, 'SKILL.md');
1305
+
1306
+ if (fs.existsSync(skillMdPath)) {
1307
+ const skill = await this.loadSkill(skillPath);
1308
+ if (skill) {
1309
+ this.loadedSkills.set(skillName, skill);
1310
+ console.log(\`[\${this.toolName.toUpperCase()}_SKILLS] Loaded skill: \${skillName}\`);
1311
+ }
1312
+ }
1313
+ }
1314
+ } catch (error) {
1315
+ console.error(\`[\${this.toolName.toUpperCase()}_SKILLS] Failed to load skills:\`, error);
1316
+ }
1317
+ }
1318
+
1319
+ async loadSkill(skillPath) {
1320
+ try {
1321
+ // Check for skill implementation files in scripts subdirectory
1322
+ const scriptsDir = path.join(skillPath, 'scripts');
1323
+ if (fs.existsSync(scriptsDir) && fs.statSync(scriptsDir).isDirectory()) {
1324
+ const files = fs.readdirSync(scriptsDir);
1325
+ const jsFiles = files.filter(file => file.endsWith('.js') && !file.includes('test'));
1326
+
1327
+ for (const jsFile of jsFiles) {
1328
+ const skillModulePath = path.join(scriptsDir, jsFile);
1329
+ try {
1330
+ const skillModule = require(skillModulePath);
1331
+ console.log(\`[\${this.toolName.toUpperCase()}_SKILLS] Loaded module: \${jsFile}\`);
1332
+ return skillModule;
1333
+ } catch (moduleError) {
1334
+ console.warn(\`[\${this.toolName.toUpperCase()}_SKILLS] Could not load \${jsFile}: \${moduleError.message}\`);
1335
+ }
1336
+ }
1337
+ }
1338
+
1339
+ // Fallback: check for JavaScript files in root directory
1340
+ const files = fs.readdirSync(skillPath);
1341
+ const jsFiles = files.filter(file => file.endsWith('.js') && !file.includes('test'));
1342
+
1343
+ for (const jsFile of jsFiles) {
1344
+ const skillModulePath = path.join(skillPath, jsFile);
1345
+ try {
1346
+ const skillModule = require(skillModulePath);
1347
+ console.log(\`[\${this.toolName.toUpperCase()}_SKILLS] Loaded module: \${jsFile}\`);
1348
+ return skillModule;
1349
+ } catch (moduleError) {
1350
+ console.warn(\`[\${this.toolName.toUpperCase()}_SKILLS] Could not load \${jsFile}: \${moduleError.message}\`);
1351
+ }
1352
+ }
1353
+
1354
+ // If no JavaScript files found, return skill directory info
1355
+ console.log(\`[\${this.toolName.toUpperCase()}_SKILLS] No JavaScript files found, registering skill directory\`);
1356
+ return { type: 'directory', path: skillPath };
1357
+
1358
+ } catch (error) {
1359
+ console.error(\`[\${this.toolName.toUpperCase()}_SKILLS] Failed to load skill from \${skillPath}:\`, error);
1360
+ }
1361
+ return null;
1362
+ }
1363
+
1364
+ async executeSkill(skillName, prompt) {
1365
+ if (!this.loadedSkills.has(skillName)) {
1366
+ return \`[SKILL: \${skillName}] Skill not found\`;
1367
+ }
1368
+
1369
+ try {
1370
+ const skill = this.loadedSkills.get(skillName);
1371
+
1372
+ if (typeof skill === 'function') {
1373
+ const result = await skill(prompt);
1374
+ return result;
1375
+ } else if (skill && typeof skill === 'object') {
1376
+ if (skill.execute && typeof skill.execute === 'function') {
1377
+ const result = await skill.execute(prompt);
1378
+ return result;
1379
+ }
1380
+ }
1381
+
1382
+ return \`[SKILL: \${skillName}] Available but no execution function\`;
1383
+ } catch (error) {
1384
+ console.error(\`[\${this.toolName.toUpperCase()}_SKILLS] Error executing skill \${skillName}:\`, error);
1385
+ return \`[SKILL: \${skillName}] Error: \${error.message}\`;
1386
+ }
1387
+ }
1388
+ }
1389
+
1390
+ module.exports = ${className};
1391
+ `;
1392
+ }
1393
+ }
1394
+
1395
+ module.exports = SkillsIntegrationGenerator;