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.
- package/README.md +39 -3
- package/STIGMERGY.md +3 -0
- package/config/enhanced-cli-config.json +438 -0
- package/docs/CLI_TOOLS_AGENT_SKILL_ANALYSIS.md +463 -0
- package/docs/ENHANCED_CLI_AGENT_SKILL_CONFIG.md +285 -0
- package/docs/INSTALLER_ARCHITECTURE.md +257 -0
- package/docs/SUDO_PROBLEM_AND_SOLUTION.md +529 -0
- package/package.json +16 -5
- package/scripts/analyze-router.js +168 -0
- package/scripts/run-comprehensive-tests.js +230 -0
- package/scripts/run-quick-tests.js +90 -0
- package/scripts/test-runner.js +344 -0
- package/src/cli/commands/autoinstall.js +158 -0
- package/src/cli/commands/errors.js +190 -0
- package/src/cli/commands/install.js +142 -0
- package/src/cli/commands/permissions.js +108 -0
- package/src/cli/commands/project.js +449 -0
- package/src/cli/commands/resume.js +136 -0
- package/src/cli/commands/scan.js +97 -0
- package/src/cli/commands/skills.js +158 -0
- package/src/cli/commands/status.js +106 -0
- package/src/cli/commands/system.js +301 -0
- package/src/cli/router-beta.js +477 -0
- package/src/cli/utils/environment.js +75 -0
- package/src/cli/utils/formatters.js +47 -0
- package/src/cli/utils/skills_cache.js +92 -0
- package/src/core/cache_cleaner.js +1 -0
- package/src/core/cli_adapters.js +345 -0
- package/src/core/cli_help_analyzer.js +473 -1
- package/src/core/cli_path_detector.js +2 -1
- package/src/core/cli_tools.js +107 -0
- package/src/core/coordination/nodejs/HookDeploymentManager.js +204 -416
- package/src/core/coordination/nodejs/HookDeploymentManager.refactored.js +323 -0
- package/src/core/coordination/nodejs/generators/CLIAdapterGenerator.js +363 -0
- package/src/core/coordination/nodejs/generators/ResumeSessionGenerator.js +703 -0
- package/src/core/coordination/nodejs/generators/SkillsIntegrationGenerator.js +1210 -0
- package/src/core/coordination/nodejs/generators/index.js +12 -0
- package/src/core/enhanced_cli_installer.js +375 -31
- package/src/core/enhanced_cli_parameter_handler.js +395 -0
- package/src/core/execution_mode_detector.js +222 -0
- package/src/core/installer.js +83 -67
- package/src/core/local_skill_scanner.js +732 -0
- package/src/core/multilingual/language-pattern-manager.js +1 -1
- package/src/core/skills/StigmergySkillManager.js +26 -8
- package/src/core/smart_router.js +279 -2
- package/src/index.js +10 -4
- package/test/cli-integration.test.js +304 -0
- package/test/enhanced-cli-agent-skill-test.js +485 -0
- package/test/specific-cli-agent-skill-analysis.js +385 -0
- 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
|
-
//
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
//
|
|
87
|
-
|
|
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
|
-
|
|
100
|
-
|
|
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
|
-
//
|
|
109
|
-
|
|
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
|
-
//
|
|
127
|
-
this.
|
|
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
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
97
|
+
async deployResumeSessionExtension(cliName, hookDir) {
|
|
98
|
+
console.log(`[HOOK_DEPLOYMENT] Deploying ResumeSession extension for ${cliName}...`);
|
|
209
99
|
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
|
|
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
|
-
|
|
246
|
-
|
|
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
|
-
//
|
|
260
|
-
if (
|
|
261
|
-
|
|
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
|
-
|
|
266
|
-
|
|
267
|
-
|
|
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
|
-
|
|
274
|
-
|
|
142
|
+
fs.writeFileSync(skillsPath, skillsResult.content);
|
|
143
|
+
console.log(`[HOOK_DEPLOYMENT] Created skills integration: ${skillsPath}`);
|
|
275
144
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|
-
|
|
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
|
-
|
|
307
|
-
|
|
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
|
-
|
|
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
|
-
|
|
353
|
-
|
|
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
|
-
|
|
407
|
-
|
|
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
|
-
//
|
|
413
|
-
if (
|
|
414
|
-
|
|
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
|
-
|
|
421
|
-
|
|
422
|
-
|
|
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
|
-
|
|
435
|
-
|
|
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
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
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
|
-
|
|
448
|
-
return
|
|
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
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
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
|
-
|
|
467
|
-
|
|
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
|
-
|
|
470
|
-
|
|
471
|
-
|
|
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
|
-
|
|
477
|
-
|
|
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
|
|
481
|
-
console.log(
|
|
237
|
+
async deployHooksForAllCLIs(options = {}) {
|
|
238
|
+
console.log('[HOOK_DEPLOYMENT] Deploying hooks for all supported CLIs...');
|
|
482
239
|
|
|
483
|
-
const
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
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
|
-
|
|
499
|
-
|
|
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
|
-
|
|
503
|
-
|
|
504
|
-
return [];
|
|
505
|
-
}
|
|
251
|
+
return results;
|
|
252
|
+
}
|
|
506
253
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
.
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
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
|
-
|
|
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
|
-
|
|
554
|
-
|
|
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
|
|
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
|
|