stigmergy 1.2.12 → 1.3.2-beta.0

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