stigmergy 1.2.13 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +39 -3
  2. package/STIGMERGY.md +3 -0
  3. package/config/builtin-skills.json +43 -0
  4. package/config/enhanced-cli-config.json +438 -0
  5. package/docs/CLI_TOOLS_AGENT_SKILL_ANALYSIS.md +463 -0
  6. package/docs/DESIGN_CLI_HELP_ANALYZER_REFACTOR.md +726 -0
  7. package/docs/ENHANCED_CLI_AGENT_SKILL_CONFIG.md +285 -0
  8. package/docs/IMPLEMENTATION_CHECKLIST_CLI_HELP_ANALYZER_REFACTOR.md +1268 -0
  9. package/docs/INSTALLER_ARCHITECTURE.md +257 -0
  10. package/docs/LESSONS_LEARNED.md +252 -0
  11. package/docs/SPECS_CLI_HELP_ANALYZER_REFACTOR.md +287 -0
  12. package/docs/SUDO_PROBLEM_AND_SOLUTION.md +529 -0
  13. package/docs/correct-skillsio-implementation.md +368 -0
  14. package/docs/development_guidelines.md +276 -0
  15. package/docs/independent-resume-implementation.md +198 -0
  16. package/docs/resumesession-final-implementation.md +195 -0
  17. package/docs/resumesession-usage.md +87 -0
  18. package/package.json +146 -136
  19. package/scripts/analyze-router.js +168 -0
  20. package/scripts/run-comprehensive-tests.js +230 -0
  21. package/scripts/run-quick-tests.js +90 -0
  22. package/scripts/test-runner.js +344 -0
  23. package/skills/resumesession/INDEPENDENT_SKILL.md +403 -0
  24. package/skills/resumesession/README.md +381 -0
  25. package/skills/resumesession/SKILL.md +211 -0
  26. package/skills/resumesession/__init__.py +33 -0
  27. package/skills/resumesession/implementations/simple-resume.js +13 -0
  28. package/skills/resumesession/independent-resume.js +750 -0
  29. package/skills/resumesession/package.json +1 -0
  30. package/skills/resumesession/skill.json +1 -0
  31. package/src/adapters/claude/install_claude_integration.js +9 -1
  32. package/src/adapters/codebuddy/install_codebuddy_integration.js +3 -1
  33. package/src/adapters/codex/install_codex_integration.js +15 -5
  34. package/src/adapters/gemini/install_gemini_integration.js +3 -1
  35. package/src/adapters/qwen/install_qwen_integration.js +3 -1
  36. package/src/cli/commands/autoinstall.js +65 -0
  37. package/src/cli/commands/errors.js +190 -0
  38. package/src/cli/commands/independent-resume.js +395 -0
  39. package/src/cli/commands/install.js +179 -0
  40. package/src/cli/commands/permissions.js +108 -0
  41. package/src/cli/commands/project.js +485 -0
  42. package/src/cli/commands/scan.js +97 -0
  43. package/src/cli/commands/simple-resume.js +377 -0
  44. package/src/cli/commands/skills.js +158 -0
  45. package/src/cli/commands/status.js +113 -0
  46. package/src/cli/commands/stigmergy-resume.js +775 -0
  47. package/src/cli/commands/system.js +301 -0
  48. package/src/cli/commands/universal-resume.js +394 -0
  49. package/src/cli/router-beta.js +471 -0
  50. package/src/cli/utils/environment.js +75 -0
  51. package/src/cli/utils/formatters.js +47 -0
  52. package/src/cli/utils/skills_cache.js +92 -0
  53. package/src/core/cache_cleaner.js +1 -0
  54. package/src/core/cli_adapters.js +345 -0
  55. package/src/core/cli_help_analyzer.js +1236 -680
  56. package/src/core/cli_path_detector.js +702 -709
  57. package/src/core/cli_tools.js +515 -160
  58. package/src/core/coordination/nodejs/CLIIntegrationManager.js +18 -0
  59. package/src/core/coordination/nodejs/HookDeploymentManager.js +242 -412
  60. package/src/core/coordination/nodejs/HookDeploymentManager.refactored.js +323 -0
  61. package/src/core/coordination/nodejs/generators/CLIAdapterGenerator.js +363 -0
  62. package/src/core/coordination/nodejs/generators/ResumeSessionGenerator.js +932 -0
  63. package/src/core/coordination/nodejs/generators/SkillsIntegrationGenerator.js +1395 -0
  64. package/src/core/coordination/nodejs/generators/index.js +12 -0
  65. package/src/core/enhanced_cli_installer.js +1208 -608
  66. package/src/core/enhanced_cli_parameter_handler.js +402 -0
  67. package/src/core/execution_mode_detector.js +222 -0
  68. package/src/core/installer.js +151 -106
  69. package/src/core/local_skill_scanner.js +732 -0
  70. package/src/core/multilingual/language-pattern-manager.js +1 -1
  71. package/src/core/skills/BuiltinSkillsDeployer.js +188 -0
  72. package/src/core/skills/StigmergySkillManager.js +123 -16
  73. package/src/core/skills/embedded-openskills/SkillParser.js +7 -3
  74. package/src/core/smart_router.js +550 -261
  75. package/src/index.js +10 -4
  76. package/src/utils.js +66 -7
  77. package/test/cli-integration.test.js +304 -0
  78. package/test/direct_smart_router_test.js +88 -0
  79. package/test/enhanced-cli-agent-skill-test.js +485 -0
  80. package/test/simple_test.js +82 -0
  81. package/test/smart_router_test_runner.js +123 -0
  82. package/test/smart_routing_edge_cases.test.js +284 -0
  83. package/test/smart_routing_simple_verification.js +139 -0
  84. package/test/smart_routing_verification.test.js +346 -0
  85. package/test/specific-cli-agent-skill-analysis.js +385 -0
  86. package/test/unit/smart_router.test.js +295 -0
  87. package/test/very_simple_test.js +54 -0
  88. package/src/cli/router.js +0 -1783
@@ -1,9 +1,13 @@
1
1
  // src/core/coordination/nodejs/HookDeploymentManager.js
2
+ // 重构后的简洁版本 - 核心协调功能
2
3
  const fs = require('fs');
3
4
  const path = require('path');
4
5
  const os = require('os');
5
6
  const { spawn, spawnSync } = require('child_process');
6
7
 
8
+ // Import specialized generators
9
+ const { ResumeSessionGenerator, SkillsIntegrationGenerator, CLIAdapterGenerator } = require('./generators');
10
+
7
11
  class HookDeploymentManager {
8
12
  constructor() {
9
13
  this.deploymentDir = path.join(os.homedir(), '.stigmergy', 'hooks');
@@ -16,7 +20,16 @@ class HookDeploymentManager {
16
20
  'codebuddy',
17
21
  'codex',
18
22
  'copilot',
23
+ 'kode',
24
+ 'opencode',
25
+ 'oh-my-opencode'
26
+ // Note: 'resumesession' is handled separately as a session recovery tool, not a regular CLI
19
27
  ];
28
+
29
+ // Initialize generators
30
+ this.resumeSessionGenerator = new ResumeSessionGenerator();
31
+ this.skillsIntegrationGenerator = new SkillsIntegrationGenerator();
32
+ this.cliAdapterGenerator = new CLIAdapterGenerator();
20
33
  }
21
34
 
22
35
  async initialize() {
@@ -36,6 +49,12 @@ class HookDeploymentManager {
36
49
  async deployHooksForCLI(cliName, options = {}) {
37
50
  console.log(`[HOOK_DEPLOYMENT] Deploying hooks for ${cliName}...`);
38
51
 
52
+ // Skip resumesession as it's a session recovery tool, not a regular CLI tool
53
+ if (cliName.toLowerCase() === 'resumesession') {
54
+ console.log(`[HOOK_DEPLOYMENT] Skipping hooks deployment for ${cliName} (session recovery tool)`);
55
+ return true;
56
+ }
57
+
39
58
  if (!this.supportedCLIs.includes(cliName.toLowerCase())) {
40
59
  throw new Error(`Unsupported CLI: ${cliName}`);
41
60
  }
@@ -47,7 +66,7 @@ class HookDeploymentManager {
47
66
  fs.mkdirSync(cliHookDir, { recursive: true });
48
67
  }
49
68
 
50
- // Deploy Node.js specific hooks
69
+ // Deploy Node.js specific hooks and extensions
51
70
  await this.deployNodeJsHooks(cliName, cliHookDir, options);
52
71
 
53
72
  console.log(
@@ -66,466 +85,241 @@ class HookDeploymentManager {
66
85
  async deployNodeJsHooks(cliName, hookDir, options) {
67
86
  console.log(`[HOOK_DEPLOYMENT] Deploying Node.js hooks for ${cliName}...`);
68
87
 
69
- // Create a basic hook template for Node.js
70
- const hookTemplate = this.generateNodeJsHookTemplate(cliName);
71
- const hookFilePath = path.join(hookDir, `${cliName}_nodejs_hook.js`);
72
-
73
- fs.writeFileSync(hookFilePath, hookTemplate);
74
- console.log(`[HOOK_DEPLOYMENT] Created Node.js hook: ${hookFilePath}`);
75
-
76
- // Make the hook executable
77
- try {
78
- fs.chmodSync(hookFilePath, 0o755);
79
- console.log(`[HOOK_DEPLOYMENT] Made hook executable: ${hookFilePath}`);
80
- } catch (error) {
81
- console.warn(
82
- `[HOOK_DEPLOYMENT] Failed to make hook executable: ${error.message}`,
83
- );
88
+ // Ensure hook directory exists
89
+ if (!fs.existsSync(hookDir)) {
90
+ fs.mkdirSync(hookDir, { recursive: true });
84
91
  }
85
92
 
86
- // Create configuration file
87
- const config = {
88
- cli: cliName,
89
- hookPath: hookFilePath,
90
- deploymentTime: new Date().toISOString(),
91
- version: '1.0.0-nodejs',
92
- };
93
-
94
- const configPath = path.join(hookDir, 'config.json');
95
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
96
- console.log(`[HOOK_DEPLOYMENT] Created hook configuration: ${configPath}`);
97
- }
93
+ // Deploy ResumeSession extension
94
+ await this.deployResumeSessionExtension(cliName, hookDir);
98
95
 
99
- generateNodeJsHookTemplate(cliName) {
100
- return `#!/usr/bin/env node
101
- /**
102
- * Node.js Hook for ${cliName.toUpperCase()}
103
- * Auto-generated by Stigmergy CLI Hook Deployment Manager
104
- */
105
- const fs = require('fs');
106
- const path = require('path');
96
+ // Deploy Skills integration
97
+ await this.deploySkillsIntegration(cliName, hookDir);
107
98
 
108
- // Embed LanguagePatternManager directly to ensure availability in deployed hooks
109
- class LanguagePatternManager {
110
- constructor() {
111
- this.supportedLanguages = {
112
- en: { name: 'English', direction: 'ltr' },
113
- zh: { name: 'Chinese', direction: 'ltr' },
114
- ja: { name: 'Japanese', direction: 'ltr' },
115
- ko: { name: 'Korean', direction: 'ltr' },
116
- de: { name: 'German', direction: 'ltr' },
117
- fr: { name: 'French', direction: 'ltr' },
118
- es: { name: 'Spanish', direction: 'ltr' },
119
- it: { name: 'Italian', direction: 'ltr' },
120
- pt: { name: 'Portuguese', direction: 'ltr' },
121
- ru: { name: 'Russian', direction: 'ltr' },
122
- ar: { name: 'Arabic', direction: 'rtl' },
123
- tr: { name: 'Turkish', direction: 'ltr' }
124
- };
99
+ // Deploy CLI adapter
100
+ await this.deployCLIAdapter(cliName, hookDir);
125
101
 
126
- // Define language patterns directly in the hook
127
- this.languagePatterns = {
128
- // English patterns
129
- en: [
130
- { name: 'use_tool_for_task', regex: /(?:use|call|ask)\\s+(\\w+)\\s+(?:to|for)\\s+(.+)$/i },
131
- { name: 'please_use_tool', regex: /(?:please\\s+)?(?:use|call|ask)\\s+(\\w+)\\s+(.+)$/i },
132
- { name: 'tool_please_help', regex: /(\\w+)[,\\s]+(?:please\\s+)?(?:help\\s+me\\s+)?(.+)$/i }
133
- ],
134
- // Chinese patterns
135
- zh: [
136
- { name: 'qing_yong_gongneng_bang_wo', regex: /请用(\\w+)\\s*帮我(.+)$/i },
137
- { name: 'diaoyong_lai', regex: /调用(\\w+)\\s*来(.+)$/i },
138
- { name: 'yong_gongneng_bang_wo', regex: /用(\\w+)\\s*帮我(.+)$/i },
139
- { name: 'tool_comma_task', regex: /(\\w+),(.+)$/i },
140
- { name: 'rang_gongneng', regex: /让(\\w+)\\s*(.+)$/i }
141
- ],
142
- // German patterns
143
- de: [
144
- { name: 'benutze_tool_um', regex: /benutze\\s+(\\w+)\\s+um\\s+(.+)$/i },
145
- { name: 'verwende_tool_fur', regex: /verwende\\s+(\\w+)\\s+für\\s+(.+)$/i },
146
- { name: 'rufe_tool_fur_an', regex: /rufe\\s+(\\w+)\\s+für\\s+(.+)\\s+an$/i }
147
- ],
148
- // French patterns
149
- fr: [
150
- { name: 'utilise_tool_pour', regex: /utilise\\s+(\\w+)\\s+pour\\s+(.+)$/i },
151
- { name: 'emploie_tool_avec', regex: /emploie\\s+(\\w+)\\s+avec\\s+(.+)$/i },
152
- { name: 'appelle_tool_pour', regex: /appelle\\s+(\\w+)\\s+pour\\s+(.+)$/i }
153
- ],
154
- // Spanish patterns
155
- es: [
156
- { name: 'usa_tool_para', regex: /usa\\s+(\\w+)\\s+para\\s+(.+)$/i },
157
- { name: 'utiliza_tool_para', regex: /utiliza\\s+(\\w+)\\s+para\\s+(.+)$/i },
158
- { name: 'llama_tool_para', regex: /llama\\s+(\\w+)\\s+para\\s+(.+)$/i }
159
- ],
160
- // Italian patterns
161
- it: [
162
- { name: 'usa_tool_per', regex: /usa\\s+(\\w+)\\s+per\\s+(.+)$/i },
163
- { name: 'utilizza_tool_per', regex: /utilizza\\s+(\\w+)\\s+per\\s+(.+)$/i },
164
- { name: 'chiedi_tool_per', regex: /chiedi\\s+(\\w+)\\s+per\\s+(.+)$/i }
165
- ],
166
- // Portuguese patterns
167
- pt: [
168
- { name: 'usa_tool_para_pt', regex: /usa\\s+(\\w+)\\s+para\\s+(.+)$/i },
169
- { name: 'utiliza_tool_para_pt', regex: /utiliza\\s+(\\w+)\\s+para\\s+(.+)$/i },
170
- { name: 'chama_tool_para', regex: /chama\\s+(\\w+)\\s+para\\s+(.+)$/i }
171
- ],
172
- // Russian patterns
173
- ru: [
174
- { name: 'ispolzuy_tool_chtoby', regex: /используй\\s+(\\w+)\\s+чтобы\\s+(.+)$/i },
175
- { name: 'primeni_tool_dlya', regex: /примени\\s+(\\w+)\\s+для\\s+(.+)$/i },
176
- { name: 'vysovyi_tool_dlya', regex: /вызови\\s+(\\w+)\\s+для\\s+(.+)$/i }
177
- ],
178
- // Arabic patterns
179
- ar: [
180
- { name: 'ista5dam_tool_liktabat', regex: /استخدم\\s+(\\w+)\\s+ل(?:كتابة|عمل)\\s+(.+)$/i },
181
- { name: 'atssil_b_tool', regex: /اتصل\\s+ب\\s+(\\w+)\\s+(.+)$/i },
182
- { name: 'ast5raj_tool', regex: /استخرج\\s+(\\w+)\\s+(.+)$/i }
183
- ],
184
- // Japanese patterns
185
- ja: [
186
- { name: 'tool_o_tsukatte', regex: /(\\w+)\\s*を使って\\s*(.+)$/i },
187
- { name: 'tool_wo_yatte', regex: /(\\w+)\\s*を\\s*やって\\s*(.+)$/i },
188
- { name: 'tool_ni_onegaishimasu', regex: /(\\w+)、\\s*(.+)$/i }
189
- ],
190
- // Korean patterns
191
- ko: [
192
- { name: 'tool_sayonghae', regex: /(\\w+)\\s*를\\s*사용해\\s*(.+)$/i },
193
- { name: 'tool_sayonghayeyo', regex: /(\\w+)\\s*를\\s*사용하여\\s*(.+)$/i },
194
- { name: 'tool_irae', regex: /(\\w+)\\s*을\\s*이용해\\s*(.+)$/i },
195
- { name: 'tool_ggumyeon', regex: /(\\w+)\\s*하고\\s*(.+)$/i }
196
- ],
197
- // Turkish patterns
198
- tr: [
199
- { name: 'tool_icin_kullan', regex: /(\\w+)'?u\\s*(.+)\\s+için\\s+kullan/i },
200
- { name: 'tool_kullan_icin', regex: /(\\w+)\\s*kullan\\s+için\\s*(.+)$/i },
201
- { name: 'tool_ile_yap', regex: /(\\w+)\\s*ile\\s*(.+)$/i }
202
- ]
203
- };
204
- }
205
-
206
- getPatterns(languageCode) {
207
- return this.languagePatterns[languageCode] || [];
208
- }
209
-
210
- getAllPatterns() {
211
- return this.languagePatterns;
102
+ // Create basic configuration
103
+ await this.createBasicConfiguration(cliName, hookDir);
212
104
  }
213
105
 
214
- detectLanguage() {
215
- // Try to detect language from environment variables
216
- const envLang = process.env.LANG || process.env.LANGUAGE || process.env.LC_ALL || process.env.LC_MESSAGES;
106
+ async deployResumeSessionExtension(cliName, hookDir) {
107
+ console.log(`[HOOK_DEPLOYMENT] Deploying ResumeSession extension for ${cliName}...`);
217
108
 
218
- if (envLang) {
219
- // Extract language code (e.g., en_US.UTF-8 -> en)
220
- const langCode = envLang.split('.')[0].split('_')[0].split('-')[0].toLowerCase();
221
- if (this.supportedLanguages[langCode]) {
222
- return langCode;
223
- }
109
+ // Ensure hook directory exists
110
+ if (!fs.existsSync(hookDir)) {
111
+ fs.mkdirSync(hookDir, { recursive: true });
224
112
  }
225
113
 
226
- // Try to detect language using Intl API
227
114
  try {
228
- if (typeof Intl !== 'undefined' && Intl.DateTimeFormat) {
229
- const locale = Intl.DateTimeFormat().resolvedOptions().locale;
230
- if (locale) {
231
- const langCode = locale.split('-')[0].toLowerCase();
232
- if (this.supportedLanguages[langCode]) {
233
- return langCode;
234
- }
235
- }
236
- }
237
- } catch (error) {
238
- // Intl API not available or failed
239
- }
240
-
241
- // Default to English
242
- return 'en';
243
- }
115
+ const extensionContent = this.resumeSessionGenerator.generateForCLI(cliName);
116
+ const fileName = this.resumeSessionGenerator.getFileName(cliName);
117
+ const extensionPath = path.join(hookDir, fileName);
244
118
 
245
- detectCrossCLIRequest(input, preferredLanguage = null) {
246
- // If preferred language is specified, try that first
247
- if (preferredLanguage && this.languagePatterns[preferredLanguage]) {
248
- const result = this.matchPatterns(input, preferredLanguage);
249
- if (result) return result;
250
- }
119
+ fs.writeFileSync(extensionPath, extensionContent);
120
+ console.log(`[HOOK_DEPLOYMENT] Created ResumeSession extension: ${extensionPath}`);
251
121
 
252
- // Try user's detected language
253
- const detectedLanguage = this.detectLanguage();
254
- if (detectedLanguage !== preferredLanguage) {
255
- const result = this.matchPatterns(input, detectedLanguage);
256
- if (result) return result;
257
- }
122
+ // Make the extension executable
123
+ try {
124
+ fs.chmodSync(extensionPath, 0o755);
125
+ console.log(`[HOOK_DEPLOYMENT] Made extension executable: ${extensionPath}`);
126
+ } catch (error) {
127
+ console.warn(
128
+ `[HOOK_DEPLOYMENT] Failed to make extension executable: ${error.message}`,
129
+ );
130
+ }
258
131
 
259
- // Fall back to English
260
- if (detectedLanguage !== 'en') {
261
- const result = this.matchPatterns(input, 'en');
262
- if (result) return result;
263
- }
132
+ // Create or update hooks.json to register the stigmergy-resume command
133
+ await this.updateHooksJson(cliName, hookDir, fileName);
264
134
 
265
- // Try all languages as last resort
266
- for (const languageCode in this.languagePatterns) {
267
- if (languageCode !== detectedLanguage && languageCode !== 'en') {
268
- const result = this.matchPatterns(input, languageCode);
269
- if (result) return result;
270
- }
135
+ return true;
136
+ } catch (error) {
137
+ console.error(`[HOOK_DEPLOYMENT] Failed to deploy ResumeSession extension for ${cliName}:`, error);
138
+ return false;
271
139
  }
272
-
273
- return null;
274
140
  }
275
141
 
276
- matchPatterns(input, languageCode) {
277
- const patterns = this.languagePatterns[languageCode];
278
- if (!patterns) return null;
279
-
280
- for (const pattern of patterns) {
281
- const match = input.match(pattern.regex);
282
- if (match && match.length >= 3) {
283
- const targetCLI = match[1].toLowerCase();
284
- const task = match[2];
285
-
286
- // Validate that the target CLI is supported
287
- const supportedCLIs = [
288
- 'claude', 'gemini', 'qwen', 'iflow', 'qodercli', 'codebuddy', 'codex', 'copilot'
289
- ];
290
-
291
- if (supportedCLIs.includes(targetCLI)) {
292
- return {
293
- targetCLI: targetCLI,
294
- task: task,
295
- language: languageCode,
296
- patternName: pattern.name
297
- };
298
- }
142
+ async updateHooksJson(cliName, hookDir, resumeSessionFileName) {
143
+ const hooksJsonPath = path.join(hookDir, 'hooks.json');
144
+ let hooksConfig = {};
145
+
146
+ // Load existing hooks.json if it exists
147
+ if (fs.existsSync(hooksJsonPath)) {
148
+ try {
149
+ const existingContent = fs.readFileSync(hooksJsonPath, 'utf8');
150
+ hooksConfig = JSON.parse(existingContent);
151
+ } catch (error) {
152
+ console.warn(`[HOOK_DEPLOYMENT] Failed to parse existing hooks.json, creating new one: ${error.message}`);
153
+ hooksConfig = {};
299
154
  }
300
155
  }
301
156
 
302
- return null;
157
+ // Register the stigmergy-resume command
158
+ hooksConfig.resumesession = {
159
+ enabled: true,
160
+ command: '/stigmergy-resume',
161
+ handler: resumeSessionFileName,
162
+ description: 'Resume or query session history across CLIs',
163
+ version: '1.0.4',
164
+ deploymentTime: new Date().toISOString()
165
+ };
166
+
167
+ // Write the updated hooks.json
168
+ fs.writeFileSync(hooksJsonPath, JSON.stringify(hooksConfig, null, 2));
169
+ console.log(`[HOOK_DEPLOYMENT] Updated hooks.json with resumesession command: ${hooksJsonPath}`);
303
170
  }
304
- }
305
171
 
306
- // Instantiate the LanguagePatternManager
307
- const embeddedLanguageManager = new LanguagePatternManager();
172
+ async deploySkillsIntegration(cliName, hookDir) {
173
+ console.log(`[HOOK_DEPLOYMENT] Deploying skills integration for ${cliName}...`);
308
174
 
309
- class ${this.capitalize(cliName)}NodeJsHook {
310
- constructor() {
311
- this.cliName = '${cliName}';
312
- this.hookDir = __dirname;
313
- this.logFile = path.join(this.hookDir, '${cliName}_hook.log');
314
- this.languageManager = embeddedLanguageManager;
315
- }
316
- async onUserPrompt(prompt, context) {
317
- this.log('INFO', \`User prompt received: \${prompt}\`);
318
- // Check for cross-CLI requests
319
- const crossCLIRequest = this.detectCrossCLIRequest(prompt);
320
- if (crossCLIRequest) {
321
- return await this.handleCrossCLIRequest(crossCLIRequest, context);
175
+ // Ensure hook directory exists
176
+ if (!fs.existsSync(hookDir)) {
177
+ fs.mkdirSync(hookDir, { recursive: true });
322
178
  }
323
- // Default processing
324
- return null;
325
- }
326
- async onToolUse(toolName, toolArgs, context) {
327
- this.log('INFO', \`Tool use detected: \${toolName}\`);
328
- return null;
329
- }
330
- async onResponseGenerated(response, context) {
331
- this.log('INFO', 'Response generated');
332
- return null;
333
- }
334
- detectCrossCLIRequest(prompt) {
335
- // Use the embedded LanguagePatternManager for enhanced multilingual pattern matching
179
+
336
180
  try {
337
- // Verify that the languageManager has the required method
338
- if (this.languageManager && this.languageManager.detectCrossCLIRequest) {
339
- const result = this.languageManager.detectCrossCLIRequest(prompt);
340
-
341
- if (result) {
342
- return {
343
- targetCLI: result.targetCLI,
344
- task: result.task,
345
- source: this.cliName,
346
- language: result.language,
347
- patternName: result.patternName
348
- };
349
- }
181
+ const skillsResult = this.skillsIntegrationGenerator.generateForCLI(cliName);
182
+ const skillsPath = path.join(hookDir, skillsResult.fileName);
183
+
184
+ fs.writeFileSync(skillsPath, skillsResult.content);
185
+ console.log(`[HOOK_DEPLOYMENT] Created skills integration: ${skillsPath}`);
186
+
187
+ // Make the skills file executable
188
+ try {
189
+ fs.chmodSync(skillsPath, 0o755);
190
+ console.log(`[HOOK_DEPLOYMENT] Made skills integration executable: ${skillsPath}`);
191
+ } catch (error) {
192
+ console.warn(`[HOOK_DEPLOYMENT] Failed to make skills integration executable: ${error.message}`);
350
193
  }
351
- } catch (error) {
352
- // If LanguagePatternManager fails, fall back to original pattern matching
353
- console.warn('LanguagePatternManager failed, falling back to original patterns:', error.message);
354
- }
355
194
 
356
- // Fallback to original pattern matching for backward compatibility
357
- const patterns = [
358
- /(?:use|call|ask)\\s+(\\w+)\\s+(?:to|for)\\s+(.+)$/i,
359
- /(?:please\\s+)?(?:use|call|ask)\\s+(\\w+)\\s+(.+)$/i,
360
- /(\\w+)[,\\s]+((?:please\\s+)?(?:help\\s+me\\s+)?(?:.+))$/i,
361
- /请用(\\w+)\\s*帮我(.+)$/i,
362
- /调用(\\w+)\\s*来(.+)$/i,
363
- /用(\\w+)\\s*帮我(.+)$/i,
364
- /(\\w+),(.+)$/i,
365
- /让(\\w+)\\s*(.+)$/i,
366
- /(?:utiliser|appeler|demander)\\s+(\\w+)\\s+(?:pour|afin de)\\s+(.+)$/i,
367
- /(?:usar|llamar|pedir)\\s+(\\w+)\\s+(?:para|para que)\\s+(.+)$/i,
368
- /(?:utilizzare|chiedere)\\s+(\\w+)\\s+(?:per|per far)\\s+(.+)$/i,
369
- /(?:verwenden|aufrufen|bitten)\\s+(\\w+)\\s+(?:für|um)\\s+(.+)$/i,
370
- /(?:使う|呼び出す|頼む)\\s+(\\w+)\\s+(?:ために|で)\\s+(.+)$/i
371
- ];
195
+ // Create skills configuration
196
+ const skillsConfig = {
197
+ cli: cliName,
198
+ skillsPath: skillsPath,
199
+ skillsDirectory: path.join(os.homedir(), '.stigmergy', 'skills'),
200
+ deploymentTime: new Date().toISOString(),
201
+ version: '1.0.0-skills'
202
+ };
372
203
 
373
- for (const pattern of patterns) {
374
- const match = prompt.match(pattern);
375
- if (match && match.length >= 3) {
376
- const targetCLI = match[1].toLowerCase();
377
- let task = match[2];
378
-
379
- // For the specific pattern that includes optional prefixes in the task capture,
380
- // no additional processing needed as it's already captured correctly
381
- if (pattern.source.includes('(?:please\\\\s+)?(?:help\\\\s+me\\\\s+)?')) {
382
- // This is the pattern /(\\w+)[,\\\\s]+((?:please\\\\s+)?(?:help\\\\s+me\\\\s+)?(?:.+))$/i
383
- // The task is already captured with the prefixes if they exist
384
- }
204
+ const configPath = path.join(hookDir, 'skills-config.json');
205
+ fs.writeFileSync(configPath, JSON.stringify(skillsConfig, null, 2));
206
+ console.log(`[HOOK_DEPLOYMENT] Created skills configuration: ${configPath}`);
385
207
 
386
- // Validate that the target CLI is supported
387
- const supportedCLIs = [
388
- 'claude', 'gemini', 'qwen', 'iflow', 'qodercli', 'codebuddy', 'codex', 'copilot'
389
- ];
390
-
391
- if (supportedCLIs.includes(targetCLI)) {
392
- return {
393
- targetCLI: targetCLI,
394
- task: task,
395
- source: this.cliName
396
- };
397
- }
398
- }
208
+ return true;
209
+ } catch (error) {
210
+ console.error(`[HOOK_DEPLOYMENT] Failed to deploy skills integration for ${cliName}:`, error);
211
+ return false;
399
212
  }
400
-
401
- return null;
402
213
  }
403
- async handleCrossCLIRequest(request, context) {
404
- this.log('INFO', \`Cross-CLI request detected: \${JSON.stringify(request)}\`);
405
214
 
406
- // Validate the request
407
- if (!request.targetCLI || !request.task) {
408
- this.log('ERROR', 'Invalid cross-CLI request: missing targetCLI or task');
409
- return \`[CROSS-CLI] Invalid request: missing targetCLI or task\`;
410
- }
215
+ async deployCLIAdapter(cliName, hookDir) {
216
+ console.log(`[HOOK_DEPLOYMENT] Deploying CLI adapter for ${cliName}...`);
411
217
 
412
- // Check if the target CLI is the same as the source
413
- if (request.targetCLI === this.cliName) {
414
- this.log('WARN', 'Cross-CLI request to self ignored');
415
- return \`[CROSS-CLI] Cannot call self (\${request.targetCLI})\`;
218
+ // Ensure hook directory exists
219
+ if (!fs.existsSync(hookDir)) {
220
+ fs.mkdirSync(hookDir, { recursive: true });
416
221
  }
417
222
 
418
- // Communicate with the coordination layer to execute the cross-CLI call
419
223
  try {
420
- // Try to load the CLCommunication module
421
- let CLCommunication;
422
- try {
423
- // First, try the standard installation path
424
- const modulePath = path.join(__dirname, '..', '..', '..', 'node_modules', 'stigmergy', 'src', 'core', 'coordination', 'nodejs', 'CLCommunication');
425
- CLCommunication = require(modulePath);
426
- } catch (e) {
427
- // If that fails, try alternative paths or gracefully degrade
428
- console.warn('CLCommunication module not found, cross-CLI functionality may be limited');
429
- return \`[CROSS-CLI] CLCommunication module not found for \${request.targetCLI}\`;
430
- }
224
+ const adapterContent = this.cliAdapterGenerator.generateForCLI(cliName);
225
+ const fileName = this.cliAdapterGenerator.getFileName(cliName);
226
+ const adapterPath = path.join(hookDir, fileName);
431
227
 
432
- const communicator = new CLCommunication();
228
+ fs.writeFileSync(adapterPath, adapterContent);
229
+ console.log(`[HOOK_DEPLOYMENT] Created CLI adapter: ${adapterPath}`);
433
230
 
434
- const result = await communicator.executeTask(
435
- request.source,
436
- request.targetCLI,
437
- request.task,
438
- context
439
- );
440
-
441
- if (result.success) {
442
- return \`[CROSS-CLI] Response from \${request.targetCLI}: \${result.output}\`;
443
- } else {
444
- return \`[CROSS-CLI] Error from \${request.targetCLI}: \${result.error}\`;
231
+ // Make the adapter executable
232
+ try {
233
+ fs.chmodSync(adapterPath, 0o755);
234
+ console.log(`[HOOK_DEPLOYMENT] Made adapter executable: ${adapterPath}`);
235
+ } catch (error) {
236
+ console.warn(`[HOOK_DEPLOYMENT] Failed to make adapter executable: ${error.message}`);
445
237
  }
238
+
239
+ return true;
446
240
  } catch (error) {
447
- this.log('ERROR', \`Failed to handle cross-CLI request: \${error.message}\`);
448
- return \`[CROSS-CLI] Failed to execute \${request.targetCLI}: \${error.message}\`;
241
+ console.error(`[HOOK_DEPLOYMENT] Failed to deploy CLI adapter for ${cliName}:`, error);
242
+ return false;
449
243
  }
450
244
  }
451
- log(level, message) {
452
- const timestamp = new Date().toISOString();
453
- const logEntry = \`[\${timestamp}] [\${level}] [\${this.cliName.toUpperCase()}] \${message}\\n\`;
454
245
 
455
- try {
456
- fs.appendFileSync(this.logFile, logEntry);
457
- } catch (error) {
458
- console.error('Failed to write to log file:', error);
246
+ async createBasicConfiguration(cliName, hookDir) {
247
+ console.log(`[HOOK_DEPLOYMENT] Creating basic configuration for ${cliName}...`);
248
+
249
+ // Ensure hook directory exists
250
+ if (!fs.existsSync(hookDir)) {
251
+ fs.mkdirSync(hookDir, { recursive: true });
459
252
  }
460
- }
461
- capitalize(str) {
462
- return str.charAt(0).toUpperCase() + str.slice(1);
463
- }
464
- }
465
253
 
466
- // Export the hook class
467
- module.exports = ${this.capitalize(cliName)}NodeJsHook;
254
+ try {
255
+ // Create main configuration file
256
+ const config = {
257
+ cli: cliName,
258
+ hookPath: hookDir,
259
+ deploymentTime: new Date().toISOString(),
260
+ version: '1.0.0-nodejs',
261
+ modules: {
262
+ resumeSession: true,
263
+ skillsIntegration: true,
264
+ cliAdapter: true
265
+ }
266
+ };
468
267
 
469
- // If run directly, instantiate and test
470
- if (require.main === module) {
471
- const hook = new ${this.capitalize(cliName)}NodeJsHook();
472
- console.log('${cliName.toUpperCase()} Node.js Hook initialized');
473
- }`;
474
- }
268
+ const configPath = path.join(hookDir, 'config.json');
269
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
270
+ console.log(`[HOOK_DEPLOYMENT] Created configuration: ${configPath}`);
475
271
 
476
- capitalize(str) {
477
- return str.charAt(0).toUpperCase() + str.slice(1);
272
+ return true;
273
+ } catch (error) {
274
+ console.error(`[HOOK_DEPLOYMENT] Failed to create configuration for ${cliName}:`, error);
275
+ return false;
276
+ }
478
277
  }
479
278
 
480
- async undeployHooksForCLI(cliName) {
481
- console.log(`[HOOK_DEPLOYMENT] Undeploying hooks for ${cliName}...`);
279
+ async deployHooksForAllCLIs(options = {}) {
280
+ console.log('[HOOK_DEPLOYMENT] Deploying hooks for all supported CLIs...');
482
281
 
483
- const cliHookDir = path.join(this.deploymentDir, cliName);
484
- if (fs.existsSync(cliHookDir)) {
485
- try {
486
- fs.rmSync(cliHookDir, { recursive: true, force: true });
487
- console.log(`[HOOK_DEPLOYMENT] Removed hook directory: ${cliHookDir}`);
488
- return true;
489
- } catch (error) {
490
- console.error(
491
- '[HOOK_DEPLOYMENT] Failed to remove hook directory:',
492
- error,
493
- );
494
- return false;
495
- }
282
+ const results = [];
283
+ for (const cliName of this.supportedCLIs) {
284
+ const success = await this.deployHooksForCLI(cliName, options);
285
+ results.push({ cli: cliName, success });
496
286
  }
497
287
 
498
- console.log(`[HOOK_DEPLOYMENT] No hooks found for ${cliName}`);
499
- return true;
500
- }
288
+ const successful = results.filter(r => r.success).length;
289
+ console.log(
290
+ `[HOOK_DEPLOYMENT] Deployment complete: ${successful}/${this.supportedCLIs.length} CLIs configured`,
291
+ );
501
292
 
502
- async listDeployedHooks() {
503
- if (!fs.existsSync(this.deploymentDir)) {
504
- return [];
505
- }
293
+ return results;
294
+ }
506
295
 
507
- const cliDirs = fs
508
- .readdirSync(this.deploymentDir, { withFileTypes: true })
509
- .filter((dirent) => dirent.isDirectory())
510
- .map((dirent) => dirent.name);
511
-
512
- const hooks = [];
513
- for (const cliName of cliDirs) {
514
- const configPath = path.join(this.deploymentDir, cliName, 'config.json');
515
- if (fs.existsSync(configPath)) {
516
- try {
517
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
518
- hooks.push(config);
519
- } catch (error) {
520
- console.warn(
521
- `[HOOK_DEPLOYMENT] Failed to read config for ${cliName}:`,
522
- error.message,
523
- );
296
+ async getDeployedHooks() {
297
+ try {
298
+ const cliDirs = fs.readdirSync(this.deploymentDir, { withFileTypes: true })
299
+ .filter(dirent => dirent.isDirectory())
300
+ .map(dirent => dirent.name);
301
+
302
+ const hooks = [];
303
+ for (const cliName of cliDirs) {
304
+ const configPath = path.join(this.deploymentDir, cliName, 'config.json');
305
+ if (fs.existsSync(configPath)) {
306
+ try {
307
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
308
+ hooks.push(config);
309
+ } catch (error) {
310
+ console.warn(
311
+ `[HOOK_DEPLOYMENT] Failed to read config for ${cliName}:`,
312
+ error.message,
313
+ );
314
+ }
524
315
  }
525
316
  }
526
- }
527
317
 
528
- return hooks;
318
+ return hooks;
319
+ } catch (error) {
320
+ console.error('[HOOK_DEPLOYMENT] Failed to get deployed hooks:', error);
321
+ return [];
322
+ }
529
323
  }
530
324
 
531
325
  async validateHookDeployment(cliName) {
@@ -543,18 +337,54 @@ if (require.main === module) {
543
337
  return { valid: false, error: 'Configuration file not found' };
544
338
  }
545
339
 
546
- const hookPath = path.join(cliHookDir, `${cliName}_nodejs_hook.js`);
547
- if (!fs.existsSync(hookPath)) {
548
- return { valid: false, error: 'Hook script not found' };
549
- }
550
-
551
- // Try to load the hook
552
340
  try {
553
- require(hookPath);
554
- return { valid: true, message: 'Hook deployment is valid' };
341
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
342
+
343
+ // Validate that all expected modules are present
344
+ const expectedModules = ['resumeSession', 'skillsIntegration', 'cliAdapter'];
345
+ if (!config.modules) {
346
+ return { valid: false, error: 'Module configuration not found' };
347
+ }
348
+
349
+ for (const module of expectedModules) {
350
+ if (!config.modules[module]) {
351
+ return { valid: false, error: `Module ${module} not configured` };
352
+ }
353
+ }
354
+
355
+ return {
356
+ valid: true,
357
+ message: 'Hook deployment is valid',
358
+ modules: config.modules
359
+ };
555
360
  } catch (error) {
556
- return { valid: false, error: `Failed to load hook: ${error.message}` };
361
+ return { valid: false, error: `Failed to validate deployment: ${error.message}` };
362
+ }
363
+ }
364
+
365
+ async validateAllDeployments() {
366
+ console.log('[HOOK_DEPLOYMENT] Validating all hook deployments...');
367
+
368
+ const results = [];
369
+ for (const cliName of this.supportedCLIs) {
370
+ const validation = await this.validateHookDeployment(cliName);
371
+ results.push({ cli: cliName, ...validation });
557
372
  }
373
+
374
+ const valid = results.filter(r => r.valid).length;
375
+ console.log(
376
+ `[HOOK_DEPLOYMENT] Validation complete: ${valid}/${this.supportedCLIs.length} deployments valid`,
377
+ );
378
+
379
+ return results;
380
+ }
381
+
382
+ getDeploymentStatus() {
383
+ return {
384
+ deploymentDir: this.deploymentDir,
385
+ supportedCLIs: this.supportedCLIs,
386
+ initialized: fs.existsSync(this.deploymentDir)
387
+ };
558
388
  }
559
389
  }
560
390