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.
- 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 +14 -5
- package/scripts/analyze-router.js +168 -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 +185 -422
- 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 +701 -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 +220 -30
- package/src/core/enhanced_cli_parameter_handler.js +395 -0
- package/src/core/execution_mode_detector.js +222 -0
- package/src/core/installer.js +51 -70
- 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 -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
|
-
//
|
|
70
|
-
|
|
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
|
-
|
|
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');
|
|
82
|
+
// Deploy Skills integration
|
|
83
|
+
await this.deploySkillsIntegration(cliName, hookDir);
|
|
107
84
|
|
|
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
|
-
};
|
|
85
|
+
// Deploy CLI adapter
|
|
86
|
+
await this.deployCLIAdapter(cliName, hookDir);
|
|
125
87
|
|
|
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
|
-
};
|
|
88
|
+
// Create basic configuration
|
|
89
|
+
await this.createBasicConfiguration(cliName, hookDir);
|
|
204
90
|
}
|
|
205
91
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
92
|
+
async deployResumeSessionExtension(cliName, hookDir) {
|
|
93
|
+
console.log(`[HOOK_DEPLOYMENT] Deploying ResumeSession extension for ${cliName}...`);
|
|
209
94
|
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
|
|
215
|
-
|
|
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
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
246
|
-
|
|
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
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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
|
-
|
|
260
|
-
|
|
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
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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
|
-
|
|
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
|
-
|
|
277
|
-
|
|
278
|
-
|
|
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
|
-
|
|
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
|
-
|
|
307
|
-
|
|
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
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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
|
-
|
|
374
|
-
|
|
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
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
];
|
|
390
|
-
|
|
391
|
-
|
|
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
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
return
|
|
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
|
-
|
|
413
|
-
|
|
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
|
-
//
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
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
|
|
435
|
-
|
|
436
|
-
|
|
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
|
-
|
|
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
|
-
|
|
448
|
-
return
|
|
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
|
-
|
|
456
|
-
|
|
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
|
-
|
|
467
|
-
|
|
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
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
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
|
-
|
|
477
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
226
|
+
return results;
|
|
478
227
|
}
|
|
479
228
|
|
|
480
|
-
async
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
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
|
-
|
|
503
|
-
|
|
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
|
-
|
|
554
|
-
|
|
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
|
|
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
|
|