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.
- 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 +19 -9
- 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 +171 -0
- package/skills/resumesession/SKILL.md +127 -0
- package/skills/resumesession/__init__.py +33 -0
- package/skills/resumesession/implementations/simple-resume.js +13 -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 +582 -26
- 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 +291 -2
- 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,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;
|