stigmergy 1.2.12 → 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 -1737
@@ -179,7 +179,7 @@ class LanguagePatternManager {
179
179
 
180
180
  // Validate that the target CLI is supported
181
181
  const supportedCLIs = [
182
- 'claude', 'gemini', 'qwen', 'iflow', 'qodercli', 'codebuddy', 'codex', 'copilot'
182
+ 'claude', 'gemini', 'qwen', 'iflow', 'qodercli', 'codebuddy', 'codex', 'copilot', 'kode'
183
183
  ];
184
184
 
185
185
  if (supportedCLIs.includes(targetCLI)) {
@@ -0,0 +1,188 @@
1
+ /**
2
+ * BuiltinSkillsDeployer - Deploy built-in skills to CLI tools
3
+ * Automatically deploys stigmergy built-in skills during installation
4
+ */
5
+
6
+ import fs from 'fs';
7
+ import path from 'path';
8
+ import os from 'os';
9
+
10
+ class BuiltinSkillsDeployer {
11
+ constructor() {
12
+ this.configPath = path.join(process.cwd(), 'config', 'builtin-skills.json');
13
+ this.skillsBaseDir = process.cwd();
14
+ }
15
+
16
+ /**
17
+ * Load built-in skills configuration
18
+ */
19
+ loadConfig() {
20
+ try {
21
+ if (!fs.existsSync(this.configPath)) {
22
+ console.warn('[BUILTIN_SKILLS] No built-in skills configuration found');
23
+ return null;
24
+ }
25
+
26
+ const content = fs.readFileSync(this.configPath, 'utf8');
27
+ return JSON.parse(content);
28
+ } catch (error) {
29
+ console.error('[BUILTIN_SKILLS] Failed to load configuration:', error.message);
30
+ return null;
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Deploy built-in skills to all CLI tools
36
+ */
37
+ async deployAll() {
38
+ const config = this.loadConfig();
39
+ if (!config) {
40
+ return { success: false, message: 'No built-in skills configuration found' };
41
+ }
42
+
43
+ console.log(`[BUILTIN_SKILLS] Found ${config.skills.length} built-in skill(s)`);
44
+
45
+ const results = [];
46
+ for (const skill of config.skills) {
47
+ if (skill.deployment && skill.deployment.autoDeploy) {
48
+ const result = await this.deploySkill(skill);
49
+ results.push(result);
50
+ }
51
+ }
52
+
53
+ const successCount = results.filter(r => r.success).length;
54
+ console.log(`[BUILTIN_SKILLS] Deployed ${successCount}/${results.length} skill(s)`);
55
+
56
+ return { success: true, results };
57
+ }
58
+
59
+ /**
60
+ * Deploy a single built-in skill to target CLIs
61
+ */
62
+ async deploySkill(skill) {
63
+ const targetCLIs = skill.deployment.targetCLIs || [];
64
+ const results = [];
65
+
66
+ for (const cliName of targetCLIs) {
67
+ const result = await this.deployToCLI(skill, cliName);
68
+ results.push(result);
69
+ }
70
+
71
+ const successCount = results.filter(r => r.success).length;
72
+ return {
73
+ success: successCount === results.length,
74
+ skillName: skill.name,
75
+ targetCLIs: targetCLIs,
76
+ deployedCount: successCount,
77
+ results
78
+ };
79
+ }
80
+
81
+ /**
82
+ * Deploy skill to a specific CLI
83
+ */
84
+ async deployToCLI(skill, cliName) {
85
+ try {
86
+ const cliHomeDir = path.join(os.homedir(), `.${cliName}`);
87
+
88
+ // Check if CLI exists
89
+ if (!fs.existsSync(cliHomeDir)) {
90
+ console.warn(`[BUILTIN_SKILLS] CLI not found: ${cliName} (${cliHomeDir})`);
91
+ return { success: false, cliName, skillName: skill.name, error: 'CLI not installed' };
92
+ }
93
+
94
+ // Ensure skills directory exists
95
+ const cliSkillsRootDir = path.join(cliHomeDir, 'skills');
96
+ if (!fs.existsSync(cliSkillsRootDir)) {
97
+ try {
98
+ fs.mkdirSync(cliSkillsRootDir, { recursive: true });
99
+ } catch (error) {
100
+ console.error(`[BUILTIN_SKILLS] Failed to create skills root directory for ${cliName}:`, error.message);
101
+ return { success: false, cliName, skillName: skill.name, error: error.message };
102
+ }
103
+ }
104
+
105
+ const cliSkillsDir = path.join(cliSkillsRootDir, skill.name);
106
+
107
+ // Create skills directory
108
+ if (!fs.existsSync(cliSkillsDir)) {
109
+ try {
110
+ fs.mkdirSync(cliSkillsDir, { recursive: true });
111
+ } catch (error) {
112
+ console.error(`[BUILTIN_SKILLS] Failed to create skills directory for ${cliName}:`, error.message);
113
+ return { success: false, cliName, skillName: skill.name, error: error.message };
114
+ }
115
+ }
116
+
117
+ // Get actual Stigmergy installation path for placeholder replacement
118
+ const stigmergyPath = process.cwd();
119
+
120
+ // Copy skill files
121
+ const files = skill.deployment.files || [];
122
+ for (const file of files) {
123
+ const sourcePath = path.join(this.skillsBaseDir, file.source);
124
+ const destPath = path.join(cliSkillsDir, path.basename(file.destination));
125
+
126
+ if (!fs.existsSync(sourcePath)) {
127
+ console.warn(`[BUILTIN_SKILLS] Source file not found: ${sourcePath}`);
128
+ continue;
129
+ }
130
+
131
+ try {
132
+ let content = fs.readFileSync(sourcePath, 'utf8');
133
+ // Replace placeholders with actual paths
134
+ content = content.replace(/\{stigmergy_path\}/g, stigmergyPath);
135
+ fs.writeFileSync(destPath, content);
136
+ console.log(`[BUILTIN_SKILLS] Deployed ${file.source} to ${cliName}`);
137
+ } catch (error) {
138
+ console.error(`[BUILTIN_SKILLS] Failed to copy ${file.source} to ${cliName}:`, error.message);
139
+ return { success: false, cliName, skillName: skill.name, error: error.message };
140
+ }
141
+ }
142
+
143
+ return { success: true, cliName, skillName: skill.name };
144
+ } catch (error) {
145
+ console.error(`[BUILTIN_SKILLS] Failed to deploy ${skill.name} to ${cliName}:`, error.message);
146
+ return { success: false, cliName, skillName: skill.name, error: error.message };
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Check if a skill is deployed to a CLI
152
+ */
153
+ isDeployed(skillName, cliName) {
154
+ const cliSkillsDir = path.join(os.homedir(), `.${cliName}`, 'skills', skillName);
155
+ return fs.existsSync(cliSkillsDir);
156
+ }
157
+
158
+ /**
159
+ * Get deployment status for all built-in skills
160
+ */
161
+ getDeploymentStatus() {
162
+ const config = this.loadConfig();
163
+ if (!config) {
164
+ return null;
165
+ }
166
+
167
+ const status = [];
168
+ for (const skill of config.skills) {
169
+ const skillStatus = {
170
+ name: skill.name,
171
+ displayName: skill.displayName,
172
+ version: skill.version,
173
+ deployment: {}
174
+ };
175
+
176
+ const targetCLIs = skill.deployment.targetCLIs || [];
177
+ for (const cliName of targetCLIs) {
178
+ skillStatus.deployment[cliName] = this.isDeployed(skill.name, cliName);
179
+ }
180
+
181
+ status.push(skillStatus);
182
+ }
183
+
184
+ return status;
185
+ }
186
+ }
187
+
188
+ export default BuiltinSkillsDeployer;
@@ -121,23 +121,45 @@ export class StigmergySkillManager {
121
121
 
122
122
  /**
123
123
  * Sync skills to CLI configuration files
124
+ * Also copies skills to CLI-specific directories and refreshes cache
124
125
  * @returns {Promise<void>}
125
126
  */
126
127
  async sync() {
127
- console.log('[INFO] Syncing skills to CLI configuration files...');
128
-
128
+ console.log('[INFO] Syncing skills to CLI directories...');
129
+
129
130
  try {
131
+ // Refresh the LocalSkillScanner cache
132
+ try {
133
+ // Import LocalSkillScanner (it's a CommonJS module)
134
+ const { createRequire } = await import('module');
135
+ const require = createRequire(import.meta.url);
136
+ const LocalSkillScanner = require('./local_skill_scanner.js');
137
+
138
+ const scanner = new LocalSkillScanner();
139
+ await scanner.initialize(true); // Force refresh
140
+ console.log('[INFO] Refreshed skills/agents cache');
141
+ } catch (err) {
142
+ // LocalSkillScanner might not be available, ignore
143
+ if (process.env.DEBUG === 'true') {
144
+ console.log('[DEBUG] LocalSkillScanner not available:', err.message);
145
+ }
146
+ }
147
+
130
148
  const skills = await this.reader.listSkills();
131
-
149
+
132
150
  if (skills.length === 0) {
133
151
  console.log('[INFO] No skills to sync');
134
152
  return;
135
153
  }
136
-
137
- // Generate <available_skills> XML
154
+
155
+ // Step 1: Copy skills to CLI-specific directories
156
+ console.log('[INFO] Copying skills to CLI directories...');
157
+ const copyResults = await this.syncSkillFiles(skills);
158
+
159
+ // Step 2: Generate <available_skills> XML
138
160
  const skillsXml = this.generateSkillsXml(skills);
139
-
140
- // All CLI configuration files to update
161
+
162
+ // Step 3: All CLI configuration files to update
141
163
  const cliFiles = [
142
164
  'AGENTS.md', // Universal config
143
165
  'claude.md', // Claude CLI
@@ -147,17 +169,19 @@ export class StigmergySkillManager {
147
169
  'qodercli.md', // Qoder CLI
148
170
  'codebuddy.md', // CodeBuddy CLI
149
171
  'copilot.md', // Copilot CLI
150
- 'codex.md' // Codex CLI
172
+ 'codex.md', // Codex CLI
173
+ 'opencode.md', // OpenCode CLI
174
+ 'oh-my-opencode.md' // Oh-My-OpenCode Plugin Manager
151
175
  ];
152
-
176
+
153
177
  let syncedCount = 0;
154
178
  let createdCount = 0;
155
179
  let skippedCount = 0;
156
-
180
+
157
181
  // Iterate through all CLI configuration files
158
182
  for (const fileName of cliFiles) {
159
183
  const filePath = path.join(process.cwd(), fileName);
160
-
184
+
161
185
  try {
162
186
  const result = await this.syncToFile(filePath, fileName, skillsXml);
163
187
  if (result === 'synced') {
@@ -170,22 +194,105 @@ export class StigmergySkillManager {
170
194
  skippedCount++;
171
195
  }
172
196
  }
173
-
197
+
174
198
  // Output sync result summary
175
199
  console.log(`\n[OK] Sync completed:`);
176
- console.log(` - Updated: ${syncedCount} file(s)`);
200
+ console.log(` - Skills copied: ${copyResults.copied} to ${copyResults.targets} CLI directory(ies)`);
201
+ console.log(` - Config updated: ${syncedCount} file(s)`);
177
202
  if (createdCount > 0) {
178
- console.log(` - Created: ${createdCount} file(s)`);
203
+ console.log(` - Config created: ${createdCount} file(s)`);
179
204
  }
180
205
  if (skippedCount > 0) {
181
- console.log(` - Skipped: ${skippedCount} file(s)`);
206
+ console.log(` - Config skipped: ${skippedCount} file(s)`);
182
207
  }
183
- console.log(` - Skills: ${skills.length}`);
208
+ console.log(` - Total skills: ${skills.length}`);
184
209
  } catch (err) {
185
210
  console.error(`[X] Sync failed: ${err.message}`);
186
211
  throw err;
187
212
  }
188
213
  }
214
+
215
+ /**
216
+ * Copy skill files to CLI-specific directories
217
+ * @private
218
+ * @param {Array} skills - List of skills to copy
219
+ * @returns {Promise<Object>} Copy results
220
+ */
221
+ async syncSkillFiles(skills) {
222
+ const fsPromises = await import('fs/promises');
223
+
224
+ // Define CLI skill directories
225
+ const cliSkillDirs = [
226
+ { name: 'Claude', path: path.join(os.homedir(), '.claude', 'skills') },
227
+ { name: 'Universal', path: path.join(process.cwd(), '.agent', 'skills') },
228
+ { name: 'Universal (global)', path: path.join(os.homedir(), '.agent', 'skills') }
229
+ ];
230
+
231
+ let copiedCount = 0;
232
+ let targets = 0;
233
+
234
+ for (const cliDir of cliSkillDirs) {
235
+ try {
236
+ // Ensure CLI directory exists
237
+ await fsPromises.mkdir(cliDir.path, { recursive: true });
238
+
239
+ // Copy each skill
240
+ for (const skill of skills) {
241
+ const skillName = skill.name;
242
+ const sourceDir = path.join(this.skillsDir, skillName);
243
+ const targetDir = path.join(cliDir.path, skillName);
244
+
245
+ try {
246
+ // Check if source exists
247
+ await fsPromises.access(sourceDir);
248
+
249
+ // Remove existing if present
250
+ await fsPromises.rm(targetDir, { recursive: true, force: true });
251
+
252
+ // Copy directory
253
+ await this.copyDirectoryRecursive(sourceDir, targetDir);
254
+ copiedCount++;
255
+ } catch (err) {
256
+ // Skip if source doesn't exist or copy fails
257
+ if (process.env.DEBUG === 'true') {
258
+ console.log(`[DEBUG] Skipped ${skillName} for ${cliDir.name}: ${err.message}`);
259
+ }
260
+ }
261
+ }
262
+
263
+ targets++;
264
+ console.log(` [OK] Synced to ${cliDir.name}: ${cliDir.path}`);
265
+ } catch (err) {
266
+ console.log(` [INFO] Skipped ${cliDir.name}: ${err.message}`);
267
+ }
268
+ }
269
+
270
+ return { copied: copiedCount, targets: targets };
271
+ }
272
+
273
+ /**
274
+ * Recursively copy a directory
275
+ * @private
276
+ * @param {string} src - Source directory
277
+ * @param {string} dest - Destination directory
278
+ */
279
+ async copyDirectoryRecursive(src, dest) {
280
+ const fsPromises = await import('fs/promises');
281
+
282
+ await fsPromises.mkdir(dest, { recursive: true });
283
+ const entries = await fsPromises.readdir(src, { withFileTypes: true });
284
+
285
+ for (const entry of entries) {
286
+ const srcPath = path.join(src, entry.name);
287
+ const destPath = path.join(dest, entry.name);
288
+
289
+ if (entry.isDirectory()) {
290
+ await this.copyDirectoryRecursive(srcPath, destPath);
291
+ } else {
292
+ await fsPromises.copyFile(srcPath, destPath);
293
+ }
294
+ }
295
+ }
189
296
 
190
297
  /**
191
298
  * Sync skills to a single file
@@ -13,7 +13,9 @@ export class SkillParser {
13
13
  * @returns {Object} Parsed metadata
14
14
  */
15
15
  parseMetadata(content) {
16
- const match = content.match(/^---\n(.*?)\n---/s);
16
+ // Normalize line endings to LF
17
+ const normalizedContent = content.replace(/\r\n/g, '\n');
18
+ const match = normalizedContent.match(/^---\n(.*?)\n---/s);
17
19
  if (!match) {
18
20
  return {};
19
21
  }
@@ -78,8 +80,10 @@ export class SkillParser {
78
80
  * @returns {string} Content without frontmatter
79
81
  */
80
82
  extractContent(content) {
81
- const match = content.match(/^---\n.*?\n---\n(.*)$/s);
82
- return match ? match[1].trim() : content;
83
+ // Normalize line endings to LF
84
+ const normalizedContent = content.replace(/\r\n/g, '\n');
85
+ const match = normalizedContent.match(/^---\n.*?\n---\n(.*)$/s);
86
+ return match ? match[1].trim() : normalizedContent;
83
87
  }
84
88
 
85
89
  /**