stigmergy 1.2.13 → 1.3.2-beta.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 (50) hide show
  1. package/README.md +39 -3
  2. package/STIGMERGY.md +3 -0
  3. package/config/enhanced-cli-config.json +438 -0
  4. package/docs/CLI_TOOLS_AGENT_SKILL_ANALYSIS.md +463 -0
  5. package/docs/ENHANCED_CLI_AGENT_SKILL_CONFIG.md +285 -0
  6. package/docs/INSTALLER_ARCHITECTURE.md +257 -0
  7. package/docs/SUDO_PROBLEM_AND_SOLUTION.md +529 -0
  8. package/package.json +16 -5
  9. package/scripts/analyze-router.js +168 -0
  10. package/scripts/run-comprehensive-tests.js +230 -0
  11. package/scripts/run-quick-tests.js +90 -0
  12. package/scripts/test-runner.js +344 -0
  13. package/src/cli/commands/autoinstall.js +158 -0
  14. package/src/cli/commands/errors.js +190 -0
  15. package/src/cli/commands/install.js +142 -0
  16. package/src/cli/commands/permissions.js +108 -0
  17. package/src/cli/commands/project.js +449 -0
  18. package/src/cli/commands/resume.js +136 -0
  19. package/src/cli/commands/scan.js +97 -0
  20. package/src/cli/commands/skills.js +158 -0
  21. package/src/cli/commands/status.js +106 -0
  22. package/src/cli/commands/system.js +301 -0
  23. package/src/cli/router-beta.js +477 -0
  24. package/src/cli/utils/environment.js +75 -0
  25. package/src/cli/utils/formatters.js +47 -0
  26. package/src/cli/utils/skills_cache.js +92 -0
  27. package/src/core/cache_cleaner.js +1 -0
  28. package/src/core/cli_adapters.js +345 -0
  29. package/src/core/cli_help_analyzer.js +473 -1
  30. package/src/core/cli_path_detector.js +2 -1
  31. package/src/core/cli_tools.js +107 -0
  32. package/src/core/coordination/nodejs/HookDeploymentManager.js +204 -416
  33. package/src/core/coordination/nodejs/HookDeploymentManager.refactored.js +323 -0
  34. package/src/core/coordination/nodejs/generators/CLIAdapterGenerator.js +363 -0
  35. package/src/core/coordination/nodejs/generators/ResumeSessionGenerator.js +703 -0
  36. package/src/core/coordination/nodejs/generators/SkillsIntegrationGenerator.js +1210 -0
  37. package/src/core/coordination/nodejs/generators/index.js +12 -0
  38. package/src/core/enhanced_cli_installer.js +375 -31
  39. package/src/core/enhanced_cli_parameter_handler.js +395 -0
  40. package/src/core/execution_mode_detector.js +222 -0
  41. package/src/core/installer.js +83 -67
  42. package/src/core/local_skill_scanner.js +732 -0
  43. package/src/core/multilingual/language-pattern-manager.js +1 -1
  44. package/src/core/skills/StigmergySkillManager.js +26 -8
  45. package/src/core/smart_router.js +279 -2
  46. package/src/index.js +10 -4
  47. package/test/cli-integration.test.js +304 -0
  48. package/test/enhanced-cli-agent-skill-test.js +485 -0
  49. package/test/specific-cli-agent-skill-analysis.js +385 -0
  50. 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,13 @@ class HookDeploymentManager {
16
20
  'codebuddy',
17
21
  'codex',
18
22
  'copilot',
23
+ 'kode',
19
24
  ];
25
+
26
+ // Initialize generators
27
+ this.resumeSessionGenerator = new ResumeSessionGenerator();
28
+ this.skillsIntegrationGenerator = new SkillsIntegrationGenerator();
29
+ this.cliAdapterGenerator = new CLIAdapterGenerator();
20
30
  }
21
31
 
22
32
  async initialize() {
@@ -47,7 +57,7 @@ class HookDeploymentManager {
47
57
  fs.mkdirSync(cliHookDir, { recursive: true });
48
58
  }
49
59
 
50
- // Deploy Node.js specific hooks
60
+ // Deploy Node.js specific hooks and extensions
51
61
  await this.deployNodeJsHooks(cliName, cliHookDir, options);
52
62
 
53
63
  console.log(
@@ -66,466 +76,208 @@ class HookDeploymentManager {
66
76
  async deployNodeJsHooks(cliName, hookDir, options) {
67
77
  console.log(`[HOOK_DEPLOYMENT] Deploying Node.js hooks for ${cliName}...`);
68
78
 
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
- );
79
+ // Ensure hook directory exists
80
+ if (!fs.existsSync(hookDir)) {
81
+ fs.mkdirSync(hookDir, { recursive: true });
84
82
  }
85
83
 
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
- }
84
+ // Deploy ResumeSession extension
85
+ await this.deployResumeSessionExtension(cliName, hookDir);
98
86
 
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');
87
+ // Deploy Skills integration
88
+ await this.deploySkillsIntegration(cliName, hookDir);
107
89
 
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
- };
90
+ // Deploy CLI adapter
91
+ await this.deployCLIAdapter(cliName, hookDir);
125
92
 
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
- };
93
+ // Create basic configuration
94
+ await this.createBasicConfiguration(cliName, hookDir);
204
95
  }
205
96
 
206
- getPatterns(languageCode) {
207
- return this.languagePatterns[languageCode] || [];
208
- }
97
+ async deployResumeSessionExtension(cliName, hookDir) {
98
+ console.log(`[HOOK_DEPLOYMENT] Deploying ResumeSession extension for ${cliName}...`);
209
99
 
210
- getAllPatterns() {
211
- return this.languagePatterns;
212
- }
213
-
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;
217
-
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
- }
100
+ // Ensure hook directory exists
101
+ if (!fs.existsSync(hookDir)) {
102
+ fs.mkdirSync(hookDir, { recursive: true });
224
103
  }
225
104
 
226
- // Try to detect language using Intl API
227
105
  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
- }
106
+ const extensionContent = this.resumeSessionGenerator.generateForCLI(cliName);
107
+ const fileName = this.resumeSessionGenerator.getFileName(cliName);
108
+ const extensionPath = path.join(hookDir, fileName);
109
+
110
+ fs.writeFileSync(extensionPath, extensionContent);
111
+ console.log(`[HOOK_DEPLOYMENT] Created ResumeSession extension: ${extensionPath}`);
112
+
113
+ // Make the extension executable
114
+ try {
115
+ fs.chmodSync(extensionPath, 0o755);
116
+ console.log(`[HOOK_DEPLOYMENT] Made extension executable: ${extensionPath}`);
117
+ } catch (error) {
118
+ console.warn(
119
+ `[HOOK_DEPLOYMENT] Failed to make extension executable: ${error.message}`,
120
+ );
236
121
  }
122
+
123
+ return true;
237
124
  } catch (error) {
238
- // Intl API not available or failed
125
+ console.error(`[HOOK_DEPLOYMENT] Failed to deploy ResumeSession extension for ${cliName}:`, error);
126
+ return false;
239
127
  }
240
-
241
- // Default to English
242
- return 'en';
243
128
  }
244
129
 
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
- }
251
-
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
- }
130
+ async deploySkillsIntegration(cliName, hookDir) {
131
+ console.log(`[HOOK_DEPLOYMENT] Deploying skills integration for ${cliName}...`);
258
132
 
259
- // Fall back to English
260
- if (detectedLanguage !== 'en') {
261
- const result = this.matchPatterns(input, 'en');
262
- if (result) return result;
133
+ // Ensure hook directory exists
134
+ if (!fs.existsSync(hookDir)) {
135
+ fs.mkdirSync(hookDir, { recursive: true });
263
136
  }
264
137
 
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
- }
271
- }
138
+ try {
139
+ const skillsResult = this.skillsIntegrationGenerator.generateForCLI(cliName);
140
+ const skillsPath = path.join(hookDir, skillsResult.fileName);
272
141
 
273
- return null;
274
- }
142
+ fs.writeFileSync(skillsPath, skillsResult.content);
143
+ console.log(`[HOOK_DEPLOYMENT] Created skills integration: ${skillsPath}`);
275
144
 
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
- }
145
+ // Make the skills file executable
146
+ try {
147
+ fs.chmodSync(skillsPath, 0o755);
148
+ console.log(`[HOOK_DEPLOYMENT] Made skills integration executable: ${skillsPath}`);
149
+ } catch (error) {
150
+ console.warn(`[HOOK_DEPLOYMENT] Failed to make skills integration executable: ${error.message}`);
299
151
  }
300
- }
301
152
 
302
- return null;
303
- }
304
- }
153
+ // Create skills configuration
154
+ const skillsConfig = {
155
+ cli: cliName,
156
+ skillsPath: skillsPath,
157
+ skillsDirectory: path.join(os.homedir(), '.stigmergy', 'skills'),
158
+ deploymentTime: new Date().toISOString(),
159
+ version: '1.0.0-skills'
160
+ };
305
161
 
306
- // Instantiate the LanguagePatternManager
307
- const embeddedLanguageManager = new LanguagePatternManager();
162
+ const configPath = path.join(hookDir, 'skills-config.json');
163
+ fs.writeFileSync(configPath, JSON.stringify(skillsConfig, null, 2));
164
+ console.log(`[HOOK_DEPLOYMENT] Created skills configuration: ${configPath}`);
308
165
 
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);
322
- }
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
336
- 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
- }
350
- }
166
+ return true;
351
167
  } 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
-
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
- ];
372
-
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
- }
385
-
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
- }
168
+ console.error(`[HOOK_DEPLOYMENT] Failed to deploy skills integration for ${cliName}:`, error);
169
+ return false;
399
170
  }
400
-
401
- return null;
402
171
  }
403
- async handleCrossCLIRequest(request, context) {
404
- this.log('INFO', \`Cross-CLI request detected: \${JSON.stringify(request)}\`);
405
172
 
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
- }
173
+ async deployCLIAdapter(cliName, hookDir) {
174
+ console.log(`[HOOK_DEPLOYMENT] Deploying CLI adapter for ${cliName}...`);
411
175
 
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})\`;
176
+ // Ensure hook directory exists
177
+ if (!fs.existsSync(hookDir)) {
178
+ fs.mkdirSync(hookDir, { recursive: true });
416
179
  }
417
180
 
418
- // Communicate with the coordination layer to execute the cross-CLI call
419
181
  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
- }
431
-
432
- const communicator = new CLCommunication();
182
+ const adapterContent = this.cliAdapterGenerator.generateForCLI(cliName);
183
+ const fileName = this.cliAdapterGenerator.getFileName(cliName);
184
+ const adapterPath = path.join(hookDir, fileName);
433
185
 
434
- const result = await communicator.executeTask(
435
- request.source,
436
- request.targetCLI,
437
- request.task,
438
- context
439
- );
186
+ fs.writeFileSync(adapterPath, adapterContent);
187
+ console.log(`[HOOK_DEPLOYMENT] Created CLI adapter: ${adapterPath}`);
440
188
 
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}\`;
189
+ // Make the adapter executable
190
+ try {
191
+ fs.chmodSync(adapterPath, 0o755);
192
+ console.log(`[HOOK_DEPLOYMENT] Made adapter executable: ${adapterPath}`);
193
+ } catch (error) {
194
+ console.warn(`[HOOK_DEPLOYMENT] Failed to make adapter executable: ${error.message}`);
445
195
  }
196
+
197
+ return true;
446
198
  } 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}\`;
199
+ console.error(`[HOOK_DEPLOYMENT] Failed to deploy CLI adapter for ${cliName}:`, error);
200
+ return false;
449
201
  }
450
202
  }
451
- log(level, message) {
452
- const timestamp = new Date().toISOString();
453
- const logEntry = \`[\${timestamp}] [\${level}] [\${this.cliName.toUpperCase()}] \${message}\\n\`;
454
203
 
455
- try {
456
- fs.appendFileSync(this.logFile, logEntry);
457
- } catch (error) {
458
- console.error('Failed to write to log file:', error);
204
+ async createBasicConfiguration(cliName, hookDir) {
205
+ console.log(`[HOOK_DEPLOYMENT] Creating basic configuration for ${cliName}...`);
206
+
207
+ // Ensure hook directory exists
208
+ if (!fs.existsSync(hookDir)) {
209
+ fs.mkdirSync(hookDir, { recursive: true });
459
210
  }
460
- }
461
- capitalize(str) {
462
- return str.charAt(0).toUpperCase() + str.slice(1);
463
- }
464
- }
465
211
 
466
- // Export the hook class
467
- module.exports = ${this.capitalize(cliName)}NodeJsHook;
212
+ try {
213
+ // Create main configuration file
214
+ const config = {
215
+ cli: cliName,
216
+ hookPath: hookDir,
217
+ deploymentTime: new Date().toISOString(),
218
+ version: '1.0.0-nodejs',
219
+ modules: {
220
+ resumeSession: true,
221
+ skillsIntegration: true,
222
+ cliAdapter: true
223
+ }
224
+ };
468
225
 
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
- }
226
+ const configPath = path.join(hookDir, 'config.json');
227
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
228
+ console.log(`[HOOK_DEPLOYMENT] Created configuration: ${configPath}`);
475
229
 
476
- capitalize(str) {
477
- return str.charAt(0).toUpperCase() + str.slice(1);
230
+ return true;
231
+ } catch (error) {
232
+ console.error(`[HOOK_DEPLOYMENT] Failed to create configuration for ${cliName}:`, error);
233
+ return false;
234
+ }
478
235
  }
479
236
 
480
- async undeployHooksForCLI(cliName) {
481
- console.log(`[HOOK_DEPLOYMENT] Undeploying hooks for ${cliName}...`);
237
+ async deployHooksForAllCLIs(options = {}) {
238
+ console.log('[HOOK_DEPLOYMENT] Deploying hooks for all supported CLIs...');
482
239
 
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
- }
240
+ const results = [];
241
+ for (const cliName of this.supportedCLIs) {
242
+ const success = await this.deployHooksForCLI(cliName, options);
243
+ results.push({ cli: cliName, success });
496
244
  }
497
245
 
498
- console.log(`[HOOK_DEPLOYMENT] No hooks found for ${cliName}`);
499
- return true;
500
- }
246
+ const successful = results.filter(r => r.success).length;
247
+ console.log(
248
+ `[HOOK_DEPLOYMENT] Deployment complete: ${successful}/${this.supportedCLIs.length} CLIs configured`,
249
+ );
501
250
 
502
- async listDeployedHooks() {
503
- if (!fs.existsSync(this.deploymentDir)) {
504
- return [];
505
- }
251
+ return results;
252
+ }
506
253
 
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
- );
254
+ async getDeployedHooks() {
255
+ try {
256
+ const cliDirs = fs.readdirSync(this.deploymentDir, { withFileTypes: true })
257
+ .filter(dirent => dirent.isDirectory())
258
+ .map(dirent => dirent.name);
259
+
260
+ const hooks = [];
261
+ for (const cliName of cliDirs) {
262
+ const configPath = path.join(this.deploymentDir, cliName, 'config.json');
263
+ if (fs.existsSync(configPath)) {
264
+ try {
265
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
266
+ hooks.push(config);
267
+ } catch (error) {
268
+ console.warn(
269
+ `[HOOK_DEPLOYMENT] Failed to read config for ${cliName}:`,
270
+ error.message,
271
+ );
272
+ }
524
273
  }
525
274
  }
526
- }
527
275
 
528
- return hooks;
276
+ return hooks;
277
+ } catch (error) {
278
+ console.error('[HOOK_DEPLOYMENT] Failed to get deployed hooks:', error);
279
+ return [];
280
+ }
529
281
  }
530
282
 
531
283
  async validateHookDeployment(cliName) {
@@ -543,18 +295,54 @@ if (require.main === module) {
543
295
  return { valid: false, error: 'Configuration file not found' };
544
296
  }
545
297
 
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
298
  try {
553
- require(hookPath);
554
- return { valid: true, message: 'Hook deployment is valid' };
299
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
300
+
301
+ // Validate that all expected modules are present
302
+ const expectedModules = ['resumeSession', 'skillsIntegration', 'cliAdapter'];
303
+ if (!config.modules) {
304
+ return { valid: false, error: 'Module configuration not found' };
305
+ }
306
+
307
+ for (const module of expectedModules) {
308
+ if (!config.modules[module]) {
309
+ return { valid: false, error: `Module ${module} not configured` };
310
+ }
311
+ }
312
+
313
+ return {
314
+ valid: true,
315
+ message: 'Hook deployment is valid',
316
+ modules: config.modules
317
+ };
555
318
  } catch (error) {
556
- return { valid: false, error: `Failed to load hook: ${error.message}` };
319
+ return { valid: false, error: `Failed to validate deployment: ${error.message}` };
320
+ }
321
+ }
322
+
323
+ async validateAllDeployments() {
324
+ console.log('[HOOK_DEPLOYMENT] Validating all hook deployments...');
325
+
326
+ const results = [];
327
+ for (const cliName of this.supportedCLIs) {
328
+ const validation = await this.validateHookDeployment(cliName);
329
+ results.push({ cli: cliName, ...validation });
557
330
  }
331
+
332
+ const valid = results.filter(r => r.valid).length;
333
+ console.log(
334
+ `[HOOK_DEPLOYMENT] Validation complete: ${valid}/${this.supportedCLIs.length} deployments valid`,
335
+ );
336
+
337
+ return results;
338
+ }
339
+
340
+ getDeploymentStatus() {
341
+ return {
342
+ deploymentDir: this.deploymentDir,
343
+ supportedCLIs: this.supportedCLIs,
344
+ initialized: fs.existsSync(this.deploymentDir)
345
+ };
558
346
  }
559
347
  }
560
348