stigmergy 1.2.0 → 1.2.8
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/LICENSE +18 -18
- package/README.md +31 -211
- package/STIGMERGY.md +70 -61
- package/docs/MULTI_USER_WIKI_COLLABORATION_SYSTEM.md +523 -0
- package/docs/PROJECT_CONSTITUTION.md +433 -433
- package/docs/PROJECT_STRUCTURE_CURRENT.md +80 -80
- package/docs/PROMPT_BASED_SKILLS_SYSTEM_DESIGN.md +458 -0
- package/docs/SKILL_IMPLEMENTATION_CONSTRAINTS_AND_ALIGNMENT.md +423 -0
- package/docs/TECHNICAL_FEASIBILITY_ANALYSIS.md +308 -0
- package/examples/calculator-example.js +72 -72
- package/examples/cline_usage_examples.md +364 -364
- package/examples/encryption-example.js +67 -67
- package/examples/json-parser-example.js +120 -120
- package/examples/json-validation-example.js +64 -64
- package/examples/multilingual-hook-demo.js +125 -0
- package/examples/rest-client-example.js +52 -52
- package/examples/rest_client_example.js +54 -54
- package/package.json +38 -20
- package/scripts/build.js +74 -74
- package/scripts/dependency-analyzer.js +101 -0
- package/scripts/generate-cli-docs.js +64 -0
- package/scripts/post-deployment-config.js +296 -296
- package/scripts/postuninstall.js +46 -0
- package/scripts/preinstall-check.js +173 -173
- package/scripts/preuninstall.js +75 -0
- package/scripts/publish.js +58 -268
- package/scripts/run-layered-tests.js +247 -0
- package/scripts/safe-install.js +139 -139
- package/scripts/simple-publish.js +57 -59
- package/src/adapters/claude/install_claude_integration.js +292 -0
- package/src/adapters/codebuddy/install_codebuddy_integration.js +349 -0
- package/src/adapters/codex/install_codex_integration.js +395 -0
- package/src/adapters/copilot/install_copilot_integration.js +716 -0
- package/src/adapters/gemini/install_gemini_integration.js +304 -0
- package/src/adapters/iflow/install_iflow_integration.js +304 -0
- package/src/adapters/qoder/install_qoder_integration.js +1090 -0
- package/src/adapters/qwen/install_qwen_integration.js +285 -0
- package/src/cli/router.js +562 -39
- package/src/core/cache_cleaner.js +82 -59
- package/src/core/cli_help_analyzer.js +297 -291
- package/src/core/cli_parameter_handler.js +5 -0
- package/src/core/cli_tools.js +6 -6
- package/src/core/coordination/index.js +2 -2
- package/src/core/coordination/nodejs/AdapterManager.js +30 -17
- package/src/core/coordination/nodejs/CLCommunication.js +28 -20
- package/src/core/coordination/nodejs/CLIIntegrationManager.js +72 -36
- package/src/core/coordination/nodejs/HealthChecker.js +13 -14
- package/src/core/coordination/nodejs/HookDeploymentManager.js +325 -63
- package/src/core/coordination/nodejs/StatisticsCollector.js +6 -6
- package/src/core/coordination/nodejs/index.js +29 -11
- package/src/core/coordination/nodejs/utils/Logger.js +1 -1
- package/src/core/enhanced_installer.js +92 -69
- package/src/core/enhanced_uninstaller.js +73 -53
- package/src/core/installer.js +815 -294
- package/src/core/multilingual/language-pattern-manager.js +172 -0
- package/src/core/smart_router.js +141 -26
- package/src/core/upgrade_manager.js +91 -46
- package/src/data_structures.js +1 -1
- package/src/deploy.js +2 -2
- package/src/index.js +3 -3
- package/src/test/cli-availability-checker.js +194 -0
- package/src/test/test-environment.js +289 -0
- package/src/utils/helpers.js +2 -2
- package/src/utils.js +7 -1
- package/test/multilingual/hook-deployment.test.js +91 -0
- package/test/multilingual/language-pattern-manager.test.js +140 -0
- package/test/multilingual/system-test.js +85 -0
- package/test/cache-cleaner-implemented.test.js +0 -328
- package/test/cache-cleaner.test.js +0 -390
- package/test/calculator.test.js +0 -215
- package/test/collision-test.js +0 -26
- package/test/comprehensive-enhanced-features.test.js +0 -252
- package/test/comprehensive-execution-test.js +0 -428
- package/test/conflict-prevention-test.js +0 -95
- package/test/cross-cli-detection-test.js +0 -33
- package/test/csv-processing-test.js +0 -36
- package/test/deploy-hooks-test.js +0 -250
- package/test/e2e/claude-cli-test.js +0 -128
- package/test/e2e/collaboration-test.js +0 -75
- package/test/e2e/comprehensive-test.js +0 -431
- package/test/e2e/error-handling-test.js +0 -90
- package/test/e2e/individual-tool-test.js +0 -143
- package/test/e2e/other-cli-test.js +0 -130
- package/test/e2e/qoder-cli-test.js +0 -128
- package/test/e2e/run-e2e-tests.js +0 -73
- package/test/e2e/test-data.js +0 -88
- package/test/e2e/test-utils.js +0 -222
- package/test/encryption-simple-test.js +0 -110
- package/test/encryption.test.js +0 -129
- package/test/enhanced-main-alignment.test.js +0 -298
- package/test/enhanced-uninstaller-implemented.test.js +0 -271
- package/test/enhanced-uninstaller.test.js +0 -284
- package/test/error-handling-test.js +0 -341
- package/test/fibonacci.test.js +0 -178
- package/test/final-deploy-test.js +0 -221
- package/test/final-install-test.js +0 -226
- package/test/hash-table-demo.js +0 -33
- package/test/hash-table-test.js +0 -26
- package/test/hash_table_test.js +0 -114
- package/test/hook-system-integration-test.js +0 -307
- package/test/iflow-integration-test.js +0 -292
- package/test/improved-install-test.js +0 -362
- package/test/install-command-test.js +0 -370
- package/test/json-parser-test.js +0 -161
- package/test/json-validation-test.js +0 -164
- package/test/natural-language-skills-test.js +0 -320
- package/test/nl-integration-test.js +0 -179
- package/test/parameter-parsing-test.js +0 -143
- package/test/plugin-deployment-test.js +0 -316
- package/test/postinstall-test.js +0 -269
- package/test/python-plugins-test.js +0 -259
- package/test/real-test.js +0 -435
- package/test/remaining-adapters-test.js +0 -256
- package/test/rest-client-test.js +0 -56
- package/test/rest_client.test.js +0 -85
- package/test/safe-installation-cleaner.test.js +0 -343
- package/test/simple-iflow-hook-test.js +0 -137
- package/test/stigmergy-upgrade-test.js +0 -243
- package/test/system-compatibility-test.js +0 -467
- package/test/tdd-deploy-fix-test.js +0 -324
- package/test/tdd-fixes-test.js +0 -211
- package/test/third-party-skills-test.js +0 -321
- package/test/tool-selection-integration-test.js +0 -158
- package/test/unit/calculator-full.test.js +0 -191
- package/test/unit/calculator-simple.test.js +0 -96
- package/test/unit/calculator.test.js +0 -97
- package/test/unit/cli-scanner.test.js +0 -291
- package/test/unit/cli_parameter_handler.test.js +0 -116
- package/test/unit/cross-cli-executor.test.js +0 -399
- package/test/weather-processor.test.js +0 -104
|
@@ -8,7 +8,14 @@ class HookDeploymentManager {
|
|
|
8
8
|
constructor() {
|
|
9
9
|
this.deploymentDir = path.join(os.homedir(), '.stigmergy', 'hooks');
|
|
10
10
|
this.supportedCLIs = [
|
|
11
|
-
'claude',
|
|
11
|
+
'claude',
|
|
12
|
+
'gemini',
|
|
13
|
+
'qwen',
|
|
14
|
+
'iflow',
|
|
15
|
+
'qodercli',
|
|
16
|
+
'codebuddy',
|
|
17
|
+
'codex',
|
|
18
|
+
'copilot',
|
|
12
19
|
];
|
|
13
20
|
}
|
|
14
21
|
|
|
@@ -20,13 +27,15 @@ class HookDeploymentManager {
|
|
|
20
27
|
async ensureDeploymentDirectory() {
|
|
21
28
|
if (!fs.existsSync(this.deploymentDir)) {
|
|
22
29
|
fs.mkdirSync(this.deploymentDir, { recursive: true });
|
|
23
|
-
console.log(
|
|
30
|
+
console.log(
|
|
31
|
+
`[HOOK_DEPLOYMENT] Created deployment directory: ${this.deploymentDir}`,
|
|
32
|
+
);
|
|
24
33
|
}
|
|
25
34
|
}
|
|
26
35
|
|
|
27
36
|
async deployHooksForCLI(cliName, options = {}) {
|
|
28
37
|
console.log(`[HOOK_DEPLOYMENT] Deploying hooks for ${cliName}...`);
|
|
29
|
-
|
|
38
|
+
|
|
30
39
|
if (!this.supportedCLIs.includes(cliName.toLowerCase())) {
|
|
31
40
|
throw new Error(`Unsupported CLI: ${cliName}`);
|
|
32
41
|
}
|
|
@@ -40,41 +49,48 @@ class HookDeploymentManager {
|
|
|
40
49
|
|
|
41
50
|
// Deploy Node.js specific hooks
|
|
42
51
|
await this.deployNodeJsHooks(cliName, cliHookDir, options);
|
|
43
|
-
|
|
44
|
-
console.log(
|
|
52
|
+
|
|
53
|
+
console.log(
|
|
54
|
+
`[HOOK_DEPLOYMENT] Hooks deployed successfully for ${cliName}`,
|
|
55
|
+
);
|
|
45
56
|
return true;
|
|
46
57
|
} catch (error) {
|
|
47
|
-
console.error(
|
|
58
|
+
console.error(
|
|
59
|
+
`[HOOK_DEPLOYMENT] Failed to deploy hooks for ${cliName}:`,
|
|
60
|
+
error,
|
|
61
|
+
);
|
|
48
62
|
return false;
|
|
49
63
|
}
|
|
50
64
|
}
|
|
51
65
|
|
|
52
66
|
async deployNodeJsHooks(cliName, hookDir, options) {
|
|
53
67
|
console.log(`[HOOK_DEPLOYMENT] Deploying Node.js hooks for ${cliName}...`);
|
|
54
|
-
|
|
68
|
+
|
|
55
69
|
// Create a basic hook template for Node.js
|
|
56
70
|
const hookTemplate = this.generateNodeJsHookTemplate(cliName);
|
|
57
71
|
const hookFilePath = path.join(hookDir, `${cliName}_nodejs_hook.js`);
|
|
58
|
-
|
|
72
|
+
|
|
59
73
|
fs.writeFileSync(hookFilePath, hookTemplate);
|
|
60
74
|
console.log(`[HOOK_DEPLOYMENT] Created Node.js hook: ${hookFilePath}`);
|
|
61
|
-
|
|
75
|
+
|
|
62
76
|
// Make the hook executable
|
|
63
77
|
try {
|
|
64
78
|
fs.chmodSync(hookFilePath, 0o755);
|
|
65
79
|
console.log(`[HOOK_DEPLOYMENT] Made hook executable: ${hookFilePath}`);
|
|
66
80
|
} catch (error) {
|
|
67
|
-
console.warn(
|
|
81
|
+
console.warn(
|
|
82
|
+
`[HOOK_DEPLOYMENT] Failed to make hook executable: ${error.message}`,
|
|
83
|
+
);
|
|
68
84
|
}
|
|
69
|
-
|
|
85
|
+
|
|
70
86
|
// Create configuration file
|
|
71
87
|
const config = {
|
|
72
88
|
cli: cliName,
|
|
73
89
|
hookPath: hookFilePath,
|
|
74
90
|
deploymentTime: new Date().toISOString(),
|
|
75
|
-
version: '1.0.0-nodejs'
|
|
91
|
+
version: '1.0.0-nodejs',
|
|
76
92
|
};
|
|
77
|
-
|
|
93
|
+
|
|
78
94
|
const configPath = path.join(hookDir, 'config.json');
|
|
79
95
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
80
96
|
console.log(`[HOOK_DEPLOYMENT] Created hook configuration: ${configPath}`);
|
|
@@ -82,64 +98,296 @@ class HookDeploymentManager {
|
|
|
82
98
|
|
|
83
99
|
generateNodeJsHookTemplate(cliName) {
|
|
84
100
|
return `#!/usr/bin/env node
|
|
85
|
-
|
|
86
101
|
/**
|
|
87
102
|
* Node.js Hook for ${cliName.toUpperCase()}
|
|
88
103
|
* Auto-generated by Stigmergy CLI Hook Deployment Manager
|
|
89
104
|
*/
|
|
90
|
-
|
|
91
105
|
const fs = require('fs');
|
|
92
106
|
const path = require('path');
|
|
93
107
|
|
|
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
|
+
};
|
|
125
|
+
|
|
126
|
+
// Define language patterns directly in the hook
|
|
127
|
+
this.languagePatterns = {
|
|
128
|
+
// English patterns
|
|
129
|
+
en: [
|
|
130
|
+
{ name: 'use_tool_for_task', regex: /(?:use|call|ask)\\s+(\\w+)\\s+(?:to|for)\\s+(.+)$/i },
|
|
131
|
+
{ name: 'please_use_tool', regex: /(?:please\\s+)?(?:use|call|ask)\\s+(\\w+)\\s+(.+)$/i },
|
|
132
|
+
{ name: 'tool_please_help', regex: /(\\w+)[,\\s]+(?:please\\s+)?(?:help\\s+me\\s+)?(.+)$/i }
|
|
133
|
+
],
|
|
134
|
+
// Chinese patterns
|
|
135
|
+
zh: [
|
|
136
|
+
{ name: 'qing_yong_gongneng_bang_wo', regex: /请用(\\w+)\\s*帮我(.+)$/i },
|
|
137
|
+
{ name: 'diaoyong_lai', regex: /调用(\\w+)\\s*来(.+)$/i },
|
|
138
|
+
{ name: 'yong_gongneng_bang_wo', regex: /用(\\w+)\\s*帮我(.+)$/i },
|
|
139
|
+
{ name: 'tool_comma_task', regex: /(\\w+),(.+)$/i },
|
|
140
|
+
{ name: 'rang_gongneng', regex: /让(\\w+)\\s*(.+)$/i }
|
|
141
|
+
],
|
|
142
|
+
// German patterns
|
|
143
|
+
de: [
|
|
144
|
+
{ name: 'benutze_tool_um', regex: /benutze\\s+(\\w+)\\s+um\\s+(.+)$/i },
|
|
145
|
+
{ name: 'verwende_tool_fur', regex: /verwende\\s+(\\w+)\\s+für\\s+(.+)$/i },
|
|
146
|
+
{ name: 'rufe_tool_fur_an', regex: /rufe\\s+(\\w+)\\s+für\\s+(.+)\\s+an$/i }
|
|
147
|
+
],
|
|
148
|
+
// French patterns
|
|
149
|
+
fr: [
|
|
150
|
+
{ name: 'utilise_tool_pour', regex: /utilise\\s+(\\w+)\\s+pour\\s+(.+)$/i },
|
|
151
|
+
{ name: 'emploie_tool_avec', regex: /emploie\\s+(\\w+)\\s+avec\\s+(.+)$/i },
|
|
152
|
+
{ name: 'appelle_tool_pour', regex: /appelle\\s+(\\w+)\\s+pour\\s+(.+)$/i }
|
|
153
|
+
],
|
|
154
|
+
// Spanish patterns
|
|
155
|
+
es: [
|
|
156
|
+
{ name: 'usa_tool_para', regex: /usa\\s+(\\w+)\\s+para\\s+(.+)$/i },
|
|
157
|
+
{ name: 'utiliza_tool_para', regex: /utiliza\\s+(\\w+)\\s+para\\s+(.+)$/i },
|
|
158
|
+
{ name: 'llama_tool_para', regex: /llama\\s+(\\w+)\\s+para\\s+(.+)$/i }
|
|
159
|
+
],
|
|
160
|
+
// Italian patterns
|
|
161
|
+
it: [
|
|
162
|
+
{ name: 'usa_tool_per', regex: /usa\\s+(\\w+)\\s+per\\s+(.+)$/i },
|
|
163
|
+
{ name: 'utilizza_tool_per', regex: /utilizza\\s+(\\w+)\\s+per\\s+(.+)$/i },
|
|
164
|
+
{ name: 'chiedi_tool_per', regex: /chiedi\\s+(\\w+)\\s+per\\s+(.+)$/i }
|
|
165
|
+
],
|
|
166
|
+
// Portuguese patterns
|
|
167
|
+
pt: [
|
|
168
|
+
{ name: 'usa_tool_para_pt', regex: /usa\\s+(\\w+)\\s+para\\s+(.+)$/i },
|
|
169
|
+
{ name: 'utiliza_tool_para_pt', regex: /utiliza\\s+(\\w+)\\s+para\\s+(.+)$/i },
|
|
170
|
+
{ name: 'chama_tool_para', regex: /chama\\s+(\\w+)\\s+para\\s+(.+)$/i }
|
|
171
|
+
],
|
|
172
|
+
// Russian patterns
|
|
173
|
+
ru: [
|
|
174
|
+
{ name: 'ispolzuy_tool_chtoby', regex: /используй\\s+(\\w+)\\s+чтобы\\s+(.+)$/i },
|
|
175
|
+
{ name: 'primeni_tool_dlya', regex: /примени\\s+(\\w+)\\s+для\\s+(.+)$/i },
|
|
176
|
+
{ name: 'vysovyi_tool_dlya', regex: /вызови\\s+(\\w+)\\s+для\\s+(.+)$/i }
|
|
177
|
+
],
|
|
178
|
+
// Arabic patterns
|
|
179
|
+
ar: [
|
|
180
|
+
{ name: 'ista5dam_tool_liktabat', regex: /استخدم\\s+(\\w+)\\s+ل(?:كتابة|عمل)\\s+(.+)$/i },
|
|
181
|
+
{ name: 'atssil_b_tool', regex: /اتصل\\s+ب\\s+(\\w+)\\s+(.+)$/i },
|
|
182
|
+
{ name: 'ast5raj_tool', regex: /استخرج\\s+(\\w+)\\s+(.+)$/i }
|
|
183
|
+
],
|
|
184
|
+
// Japanese patterns
|
|
185
|
+
ja: [
|
|
186
|
+
{ name: 'tool_o_tsukatte', regex: /(\\w+)\\s*を使って\\s*(.+)$/i },
|
|
187
|
+
{ name: 'tool_wo_yatte', regex: /(\\w+)\\s*を\\s*やって\\s*(.+)$/i },
|
|
188
|
+
{ name: 'tool_ni_onegaishimasu', regex: /(\\w+)、\\s*(.+)$/i }
|
|
189
|
+
],
|
|
190
|
+
// Korean patterns
|
|
191
|
+
ko: [
|
|
192
|
+
{ name: 'tool_sayonghae', regex: /(\\w+)\\s*를\\s*사용해\\s*(.+)$/i },
|
|
193
|
+
{ name: 'tool_sayonghayeyo', regex: /(\\w+)\\s*를\\s*사용하여\\s*(.+)$/i },
|
|
194
|
+
{ name: 'tool_irae', regex: /(\\w+)\\s*을\\s*이용해\\s*(.+)$/i },
|
|
195
|
+
{ name: 'tool_ggumyeon', regex: /(\\w+)\\s*하고\\s*(.+)$/i }
|
|
196
|
+
],
|
|
197
|
+
// Turkish patterns
|
|
198
|
+
tr: [
|
|
199
|
+
{ name: 'tool_icin_kullan', regex: /(\\w+)'?u\\s*(.+)\\s+için\\s+kullan/i },
|
|
200
|
+
{ name: 'tool_kullan_icin', regex: /(\\w+)\\s*kullan\\s+için\\s*(.+)$/i },
|
|
201
|
+
{ name: 'tool_ile_yap', regex: /(\\w+)\\s*ile\\s*(.+)$/i }
|
|
202
|
+
]
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
getPatterns(languageCode) {
|
|
207
|
+
return this.languagePatterns[languageCode] || [];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
getAllPatterns() {
|
|
211
|
+
return this.languagePatterns;
|
|
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
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
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
|
+
}
|
|
237
|
+
} catch (error) {
|
|
238
|
+
// Intl API not available or failed
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Default to English
|
|
242
|
+
return 'en';
|
|
243
|
+
}
|
|
244
|
+
|
|
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
|
+
}
|
|
258
|
+
|
|
259
|
+
// Fall back to English
|
|
260
|
+
if (detectedLanguage !== 'en') {
|
|
261
|
+
const result = this.matchPatterns(input, 'en');
|
|
262
|
+
if (result) return result;
|
|
263
|
+
}
|
|
264
|
+
|
|
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
|
+
}
|
|
272
|
+
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
|
|
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
|
+
}
|
|
301
|
+
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Instantiate the LanguagePatternManager
|
|
307
|
+
const embeddedLanguageManager = new LanguagePatternManager();
|
|
308
|
+
|
|
94
309
|
class ${this.capitalize(cliName)}NodeJsHook {
|
|
95
310
|
constructor() {
|
|
96
311
|
this.cliName = '${cliName}';
|
|
97
312
|
this.hookDir = __dirname;
|
|
98
313
|
this.logFile = path.join(this.hookDir, '${cliName}_hook.log');
|
|
314
|
+
this.languageManager = embeddedLanguageManager;
|
|
99
315
|
}
|
|
100
|
-
|
|
101
316
|
async onUserPrompt(prompt, context) {
|
|
102
317
|
this.log('INFO', \`User prompt received: \${prompt}\`);
|
|
103
|
-
|
|
104
318
|
// Check for cross-CLI requests
|
|
105
319
|
const crossCLIRequest = this.detectCrossCLIRequest(prompt);
|
|
106
320
|
if (crossCLIRequest) {
|
|
107
321
|
return await this.handleCrossCLIRequest(crossCLIRequest, context);
|
|
108
322
|
}
|
|
109
|
-
|
|
110
323
|
// Default processing
|
|
111
324
|
return null;
|
|
112
325
|
}
|
|
113
|
-
|
|
114
326
|
async onToolUse(toolName, toolArgs, context) {
|
|
115
327
|
this.log('INFO', \`Tool use detected: \${toolName}\`);
|
|
116
328
|
return null;
|
|
117
329
|
}
|
|
118
|
-
|
|
119
330
|
async onResponseGenerated(response, context) {
|
|
120
331
|
this.log('INFO', 'Response generated');
|
|
121
332
|
return null;
|
|
122
333
|
}
|
|
123
|
-
|
|
124
334
|
detectCrossCLIRequest(prompt) {
|
|
125
|
-
//
|
|
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
|
+
}
|
|
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
|
|
126
357
|
const patterns = [
|
|
127
|
-
/(?:use|call|ask)
|
|
128
|
-
/(?:please
|
|
129
|
-
/(
|
|
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
|
|
130
371
|
];
|
|
131
|
-
|
|
372
|
+
|
|
132
373
|
for (const pattern of patterns) {
|
|
133
374
|
const match = prompt.match(pattern);
|
|
134
375
|
if (match && match.length >= 3) {
|
|
135
376
|
const targetCLI = match[1].toLowerCase();
|
|
136
|
-
|
|
137
|
-
|
|
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
|
+
|
|
138
386
|
// Validate that the target CLI is supported
|
|
139
387
|
const supportedCLIs = [
|
|
140
388
|
'claude', 'gemini', 'qwen', 'iflow', 'qodercli', 'codebuddy', 'codex', 'copilot'
|
|
141
389
|
];
|
|
142
|
-
|
|
390
|
+
|
|
143
391
|
if (supportedCLIs.includes(targetCLI)) {
|
|
144
392
|
return {
|
|
145
393
|
targetCLI: targetCLI,
|
|
@@ -149,39 +397,47 @@ class ${this.capitalize(cliName)}NodeJsHook {
|
|
|
149
397
|
}
|
|
150
398
|
}
|
|
151
399
|
}
|
|
152
|
-
|
|
400
|
+
|
|
153
401
|
return null;
|
|
154
402
|
}
|
|
155
|
-
|
|
156
403
|
async handleCrossCLIRequest(request, context) {
|
|
157
404
|
this.log('INFO', \`Cross-CLI request detected: \${JSON.stringify(request)}\`);
|
|
158
|
-
|
|
405
|
+
|
|
159
406
|
// Validate the request
|
|
160
407
|
if (!request.targetCLI || !request.task) {
|
|
161
408
|
this.log('ERROR', 'Invalid cross-CLI request: missing targetCLI or task');
|
|
162
409
|
return \`[CROSS-CLI] Invalid request: missing targetCLI or task\`;
|
|
163
410
|
}
|
|
164
|
-
|
|
411
|
+
|
|
165
412
|
// Check if the target CLI is the same as the source
|
|
166
413
|
if (request.targetCLI === this.cliName) {
|
|
167
414
|
this.log('WARN', 'Cross-CLI request to self ignored');
|
|
168
415
|
return \`[CROSS-CLI] Cannot call self (\${request.targetCLI})\`;
|
|
169
416
|
}
|
|
170
|
-
|
|
417
|
+
|
|
171
418
|
// Communicate with the coordination layer to execute the cross-CLI call
|
|
172
419
|
try {
|
|
173
|
-
//
|
|
174
|
-
|
|
175
|
-
|
|
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
|
+
|
|
176
432
|
const communicator = new CLCommunication();
|
|
177
|
-
|
|
433
|
+
|
|
178
434
|
const result = await communicator.executeTask(
|
|
179
|
-
request.source,
|
|
180
|
-
request.targetCLI,
|
|
181
|
-
request.task,
|
|
435
|
+
request.source,
|
|
436
|
+
request.targetCLI,
|
|
437
|
+
request.task,
|
|
182
438
|
context
|
|
183
439
|
);
|
|
184
|
-
|
|
440
|
+
|
|
185
441
|
if (result.success) {
|
|
186
442
|
return \`[CROSS-CLI] Response from \${request.targetCLI}: \${result.output}\`;
|
|
187
443
|
} else {
|
|
@@ -192,18 +448,16 @@ class ${this.capitalize(cliName)}NodeJsHook {
|
|
|
192
448
|
return \`[CROSS-CLI] Failed to execute \${request.targetCLI}: \${error.message}\`;
|
|
193
449
|
}
|
|
194
450
|
}
|
|
195
|
-
|
|
196
451
|
log(level, message) {
|
|
197
452
|
const timestamp = new Date().toISOString();
|
|
198
|
-
const logEntry = \`[\${timestamp}] [\${level}] [\${this.cliName.toUpperCase()}] \${message}
|
|
199
|
-
|
|
453
|
+
const logEntry = \`[\${timestamp}] [\${level}] [\${this.cliName.toUpperCase()}] \${message}\\n\`;
|
|
454
|
+
|
|
200
455
|
try {
|
|
201
456
|
fs.appendFileSync(this.logFile, logEntry);
|
|
202
457
|
} catch (error) {
|
|
203
458
|
console.error('Failed to write to log file:', error);
|
|
204
459
|
}
|
|
205
460
|
}
|
|
206
|
-
|
|
207
461
|
capitalize(str) {
|
|
208
462
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
209
463
|
}
|
|
@@ -216,8 +470,7 @@ module.exports = ${this.capitalize(cliName)}NodeJsHook;
|
|
|
216
470
|
if (require.main === module) {
|
|
217
471
|
const hook = new ${this.capitalize(cliName)}NodeJsHook();
|
|
218
472
|
console.log('${cliName.toUpperCase()} Node.js Hook initialized');
|
|
219
|
-
}
|
|
220
|
-
`;
|
|
473
|
+
}`;
|
|
221
474
|
}
|
|
222
475
|
|
|
223
476
|
capitalize(str) {
|
|
@@ -226,7 +479,7 @@ if (require.main === module) {
|
|
|
226
479
|
|
|
227
480
|
async undeployHooksForCLI(cliName) {
|
|
228
481
|
console.log(`[HOOK_DEPLOYMENT] Undeploying hooks for ${cliName}...`);
|
|
229
|
-
|
|
482
|
+
|
|
230
483
|
const cliHookDir = path.join(this.deploymentDir, cliName);
|
|
231
484
|
if (fs.existsSync(cliHookDir)) {
|
|
232
485
|
try {
|
|
@@ -234,11 +487,14 @@ if (require.main === module) {
|
|
|
234
487
|
console.log(`[HOOK_DEPLOYMENT] Removed hook directory: ${cliHookDir}`);
|
|
235
488
|
return true;
|
|
236
489
|
} catch (error) {
|
|
237
|
-
console.error(
|
|
490
|
+
console.error(
|
|
491
|
+
'[HOOK_DEPLOYMENT] Failed to remove hook directory:',
|
|
492
|
+
error,
|
|
493
|
+
);
|
|
238
494
|
return false;
|
|
239
495
|
}
|
|
240
496
|
}
|
|
241
|
-
|
|
497
|
+
|
|
242
498
|
console.log(`[HOOK_DEPLOYMENT] No hooks found for ${cliName}`);
|
|
243
499
|
return true;
|
|
244
500
|
}
|
|
@@ -247,11 +503,12 @@ if (require.main === module) {
|
|
|
247
503
|
if (!fs.existsSync(this.deploymentDir)) {
|
|
248
504
|
return [];
|
|
249
505
|
}
|
|
250
|
-
|
|
251
|
-
const cliDirs = fs
|
|
252
|
-
.
|
|
253
|
-
.
|
|
254
|
-
|
|
506
|
+
|
|
507
|
+
const cliDirs = fs
|
|
508
|
+
.readdirSync(this.deploymentDir, { withFileTypes: true })
|
|
509
|
+
.filter((dirent) => dirent.isDirectory())
|
|
510
|
+
.map((dirent) => dirent.name);
|
|
511
|
+
|
|
255
512
|
const hooks = [];
|
|
256
513
|
for (const cliName of cliDirs) {
|
|
257
514
|
const configPath = path.join(this.deploymentDir, cliName, 'config.json');
|
|
@@ -260,32 +517,37 @@ if (require.main === module) {
|
|
|
260
517
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
261
518
|
hooks.push(config);
|
|
262
519
|
} catch (error) {
|
|
263
|
-
console.warn(
|
|
520
|
+
console.warn(
|
|
521
|
+
`[HOOK_DEPLOYMENT] Failed to read config for ${cliName}:`,
|
|
522
|
+
error.message,
|
|
523
|
+
);
|
|
264
524
|
}
|
|
265
525
|
}
|
|
266
526
|
}
|
|
267
|
-
|
|
527
|
+
|
|
268
528
|
return hooks;
|
|
269
529
|
}
|
|
270
530
|
|
|
271
531
|
async validateHookDeployment(cliName) {
|
|
272
|
-
console.log(
|
|
273
|
-
|
|
532
|
+
console.log(
|
|
533
|
+
`[HOOK_DEPLOYMENT] Validating hook deployment for ${cliName}...`,
|
|
534
|
+
);
|
|
535
|
+
|
|
274
536
|
const cliHookDir = path.join(this.deploymentDir, cliName);
|
|
275
537
|
if (!fs.existsSync(cliHookDir)) {
|
|
276
538
|
return { valid: false, error: 'Hook directory not found' };
|
|
277
539
|
}
|
|
278
|
-
|
|
540
|
+
|
|
279
541
|
const configPath = path.join(cliHookDir, 'config.json');
|
|
280
542
|
if (!fs.existsSync(configPath)) {
|
|
281
543
|
return { valid: false, error: 'Configuration file not found' };
|
|
282
544
|
}
|
|
283
|
-
|
|
545
|
+
|
|
284
546
|
const hookPath = path.join(cliHookDir, `${cliName}_nodejs_hook.js`);
|
|
285
547
|
if (!fs.existsSync(hookPath)) {
|
|
286
548
|
return { valid: false, error: 'Hook script not found' };
|
|
287
549
|
}
|
|
288
|
-
|
|
550
|
+
|
|
289
551
|
// Try to load the hook
|
|
290
552
|
try {
|
|
291
553
|
require(hookPath);
|
|
@@ -16,11 +16,11 @@ class StatisticsCollector {
|
|
|
16
16
|
cross_cli_calls: 0,
|
|
17
17
|
successful_calls: 0,
|
|
18
18
|
failed_calls: 0,
|
|
19
|
-
adapter_loads: 0
|
|
19
|
+
adapter_loads: 0,
|
|
20
20
|
};
|
|
21
21
|
this.timings = {
|
|
22
22
|
execution_times: [],
|
|
23
|
-
last_reset: Date.now()
|
|
23
|
+
last_reset: Date.now(),
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -41,7 +41,7 @@ class StatisticsCollector {
|
|
|
41
41
|
calls: this.counters.cross_cli_calls,
|
|
42
42
|
successRate: this.calculateSuccessRate(),
|
|
43
43
|
averageExecutionTime: this.calculateAverageExecutionTime(),
|
|
44
|
-
uptime: Date.now() - this.startTime
|
|
44
|
+
uptime: Date.now() - this.startTime,
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -50,9 +50,9 @@ class StatisticsCollector {
|
|
|
50
50
|
counters: this.counters,
|
|
51
51
|
timings: {
|
|
52
52
|
...this.timings,
|
|
53
|
-
averageExecutionTime: this.calculateAverageExecutionTime()
|
|
53
|
+
averageExecutionTime: this.calculateAverageExecutionTime(),
|
|
54
54
|
},
|
|
55
|
-
uptime: Date.now() - this.startTime
|
|
55
|
+
uptime: Date.now() - this.startTime,
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
58
|
|
|
@@ -68,4 +68,4 @@ class StatisticsCollector {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
module.exports = StatisticsCollector;
|
|
71
|
+
module.exports = StatisticsCollector;
|