stigmergy 1.2.13 → 1.3.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/builtin-skills.json +43 -0
- package/config/enhanced-cli-config.json +438 -0
- package/docs/CLI_TOOLS_AGENT_SKILL_ANALYSIS.md +463 -0
- package/docs/DESIGN_CLI_HELP_ANALYZER_REFACTOR.md +726 -0
- package/docs/ENHANCED_CLI_AGENT_SKILL_CONFIG.md +285 -0
- package/docs/IMPLEMENTATION_CHECKLIST_CLI_HELP_ANALYZER_REFACTOR.md +1268 -0
- package/docs/INSTALLER_ARCHITECTURE.md +257 -0
- package/docs/LESSONS_LEARNED.md +252 -0
- package/docs/SPECS_CLI_HELP_ANALYZER_REFACTOR.md +287 -0
- package/docs/SUDO_PROBLEM_AND_SOLUTION.md +529 -0
- package/docs/correct-skillsio-implementation.md +368 -0
- package/docs/development_guidelines.md +276 -0
- package/docs/independent-resume-implementation.md +198 -0
- package/docs/resumesession-final-implementation.md +195 -0
- package/docs/resumesession-usage.md +87 -0
- package/package.json +146 -136
- 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/skills/resumesession/INDEPENDENT_SKILL.md +403 -0
- package/skills/resumesession/README.md +381 -0
- package/skills/resumesession/SKILL.md +211 -0
- package/skills/resumesession/__init__.py +33 -0
- package/skills/resumesession/implementations/simple-resume.js +13 -0
- package/skills/resumesession/independent-resume.js +750 -0
- package/skills/resumesession/package.json +1 -0
- package/skills/resumesession/skill.json +1 -0
- package/src/adapters/claude/install_claude_integration.js +9 -1
- package/src/adapters/codebuddy/install_codebuddy_integration.js +3 -1
- package/src/adapters/codex/install_codex_integration.js +15 -5
- package/src/adapters/gemini/install_gemini_integration.js +3 -1
- package/src/adapters/qwen/install_qwen_integration.js +3 -1
- package/src/cli/commands/autoinstall.js +65 -0
- package/src/cli/commands/errors.js +190 -0
- package/src/cli/commands/independent-resume.js +395 -0
- package/src/cli/commands/install.js +179 -0
- package/src/cli/commands/permissions.js +108 -0
- package/src/cli/commands/project.js +485 -0
- package/src/cli/commands/scan.js +97 -0
- package/src/cli/commands/simple-resume.js +377 -0
- package/src/cli/commands/skills.js +158 -0
- package/src/cli/commands/status.js +113 -0
- package/src/cli/commands/stigmergy-resume.js +775 -0
- package/src/cli/commands/system.js +301 -0
- package/src/cli/commands/universal-resume.js +394 -0
- package/src/cli/router-beta.js +471 -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 +1236 -680
- package/src/core/cli_path_detector.js +702 -709
- package/src/core/cli_tools.js +515 -160
- package/src/core/coordination/nodejs/CLIIntegrationManager.js +18 -0
- package/src/core/coordination/nodejs/HookDeploymentManager.js +242 -412
- 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 +932 -0
- package/src/core/coordination/nodejs/generators/SkillsIntegrationGenerator.js +1395 -0
- package/src/core/coordination/nodejs/generators/index.js +12 -0
- package/src/core/enhanced_cli_installer.js +1208 -608
- package/src/core/enhanced_cli_parameter_handler.js +402 -0
- package/src/core/execution_mode_detector.js +222 -0
- package/src/core/installer.js +151 -106
- package/src/core/local_skill_scanner.js +732 -0
- package/src/core/multilingual/language-pattern-manager.js +1 -1
- package/src/core/skills/BuiltinSkillsDeployer.js +188 -0
- package/src/core/skills/StigmergySkillManager.js +123 -16
- package/src/core/skills/embedded-openskills/SkillParser.js +7 -3
- package/src/core/smart_router.js +550 -261
- package/src/index.js +10 -4
- package/src/utils.js +66 -7
- package/test/cli-integration.test.js +304 -0
- package/test/direct_smart_router_test.js +88 -0
- package/test/enhanced-cli-agent-skill-test.js +485 -0
- package/test/simple_test.js +82 -0
- package/test/smart_router_test_runner.js +123 -0
- package/test/smart_routing_edge_cases.test.js +284 -0
- package/test/smart_routing_simple_verification.js +139 -0
- package/test/smart_routing_verification.test.js +346 -0
- package/test/specific-cli-agent-skill-analysis.js +385 -0
- package/test/unit/smart_router.test.js +295 -0
- package/test/very_simple_test.js +54 -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,16 @@ class HookDeploymentManager {
|
|
|
16
20
|
'codebuddy',
|
|
17
21
|
'codex',
|
|
18
22
|
'copilot',
|
|
23
|
+
'kode',
|
|
24
|
+
'opencode',
|
|
25
|
+
'oh-my-opencode'
|
|
26
|
+
// Note: 'resumesession' is handled separately as a session recovery tool, not a regular CLI
|
|
19
27
|
];
|
|
28
|
+
|
|
29
|
+
// Initialize generators
|
|
30
|
+
this.resumeSessionGenerator = new ResumeSessionGenerator();
|
|
31
|
+
this.skillsIntegrationGenerator = new SkillsIntegrationGenerator();
|
|
32
|
+
this.cliAdapterGenerator = new CLIAdapterGenerator();
|
|
20
33
|
}
|
|
21
34
|
|
|
22
35
|
async initialize() {
|
|
@@ -36,6 +49,12 @@ class HookDeploymentManager {
|
|
|
36
49
|
async deployHooksForCLI(cliName, options = {}) {
|
|
37
50
|
console.log(`[HOOK_DEPLOYMENT] Deploying hooks for ${cliName}...`);
|
|
38
51
|
|
|
52
|
+
// Skip resumesession as it's a session recovery tool, not a regular CLI tool
|
|
53
|
+
if (cliName.toLowerCase() === 'resumesession') {
|
|
54
|
+
console.log(`[HOOK_DEPLOYMENT] Skipping hooks deployment for ${cliName} (session recovery tool)`);
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
|
|
39
58
|
if (!this.supportedCLIs.includes(cliName.toLowerCase())) {
|
|
40
59
|
throw new Error(`Unsupported CLI: ${cliName}`);
|
|
41
60
|
}
|
|
@@ -47,7 +66,7 @@ class HookDeploymentManager {
|
|
|
47
66
|
fs.mkdirSync(cliHookDir, { recursive: true });
|
|
48
67
|
}
|
|
49
68
|
|
|
50
|
-
// Deploy Node.js specific hooks
|
|
69
|
+
// Deploy Node.js specific hooks and extensions
|
|
51
70
|
await this.deployNodeJsHooks(cliName, cliHookDir, options);
|
|
52
71
|
|
|
53
72
|
console.log(
|
|
@@ -66,466 +85,241 @@ class HookDeploymentManager {
|
|
|
66
85
|
async deployNodeJsHooks(cliName, hookDir, options) {
|
|
67
86
|
console.log(`[HOOK_DEPLOYMENT] Deploying Node.js hooks for ${cliName}...`);
|
|
68
87
|
|
|
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
|
-
);
|
|
88
|
+
// Ensure hook directory exists
|
|
89
|
+
if (!fs.existsSync(hookDir)) {
|
|
90
|
+
fs.mkdirSync(hookDir, { recursive: true });
|
|
84
91
|
}
|
|
85
92
|
|
|
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
|
-
}
|
|
93
|
+
// Deploy ResumeSession extension
|
|
94
|
+
await this.deployResumeSessionExtension(cliName, hookDir);
|
|
98
95
|
|
|
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');
|
|
96
|
+
// Deploy Skills integration
|
|
97
|
+
await this.deploySkillsIntegration(cliName, hookDir);
|
|
107
98
|
|
|
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
|
-
};
|
|
99
|
+
// Deploy CLI adapter
|
|
100
|
+
await this.deployCLIAdapter(cliName, hookDir);
|
|
125
101
|
|
|
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
|
-
};
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
getPatterns(languageCode) {
|
|
207
|
-
return this.languagePatterns[languageCode] || [];
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
getAllPatterns() {
|
|
211
|
-
return this.languagePatterns;
|
|
102
|
+
// Create basic configuration
|
|
103
|
+
await this.createBasicConfiguration(cliName, hookDir);
|
|
212
104
|
}
|
|
213
105
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
const envLang = process.env.LANG || process.env.LANGUAGE || process.env.LC_ALL || process.env.LC_MESSAGES;
|
|
106
|
+
async deployResumeSessionExtension(cliName, hookDir) {
|
|
107
|
+
console.log(`[HOOK_DEPLOYMENT] Deploying ResumeSession extension for ${cliName}...`);
|
|
217
108
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if (this.supportedLanguages[langCode]) {
|
|
222
|
-
return langCode;
|
|
223
|
-
}
|
|
109
|
+
// Ensure hook directory exists
|
|
110
|
+
if (!fs.existsSync(hookDir)) {
|
|
111
|
+
fs.mkdirSync(hookDir, { recursive: true });
|
|
224
112
|
}
|
|
225
113
|
|
|
226
|
-
// Try to detect language using Intl API
|
|
227
114
|
try {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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
|
-
}
|
|
115
|
+
const extensionContent = this.resumeSessionGenerator.generateForCLI(cliName);
|
|
116
|
+
const fileName = this.resumeSessionGenerator.getFileName(cliName);
|
|
117
|
+
const extensionPath = path.join(hookDir, fileName);
|
|
244
118
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
if (preferredLanguage && this.languagePatterns[preferredLanguage]) {
|
|
248
|
-
const result = this.matchPatterns(input, preferredLanguage);
|
|
249
|
-
if (result) return result;
|
|
250
|
-
}
|
|
119
|
+
fs.writeFileSync(extensionPath, extensionContent);
|
|
120
|
+
console.log(`[HOOK_DEPLOYMENT] Created ResumeSession extension: ${extensionPath}`);
|
|
251
121
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
122
|
+
// Make the extension executable
|
|
123
|
+
try {
|
|
124
|
+
fs.chmodSync(extensionPath, 0o755);
|
|
125
|
+
console.log(`[HOOK_DEPLOYMENT] Made extension executable: ${extensionPath}`);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
console.warn(
|
|
128
|
+
`[HOOK_DEPLOYMENT] Failed to make extension executable: ${error.message}`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
258
131
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
const result = this.matchPatterns(input, 'en');
|
|
262
|
-
if (result) return result;
|
|
263
|
-
}
|
|
132
|
+
// Create or update hooks.json to register the stigmergy-resume command
|
|
133
|
+
await this.updateHooksJson(cliName, hookDir, fileName);
|
|
264
134
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
if (result) return result;
|
|
270
|
-
}
|
|
135
|
+
return true;
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error(`[HOOK_DEPLOYMENT] Failed to deploy ResumeSession extension for ${cliName}:`, error);
|
|
138
|
+
return false;
|
|
271
139
|
}
|
|
272
|
-
|
|
273
|
-
return null;
|
|
274
140
|
}
|
|
275
141
|
|
|
276
|
-
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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
|
-
}
|
|
142
|
+
async updateHooksJson(cliName, hookDir, resumeSessionFileName) {
|
|
143
|
+
const hooksJsonPath = path.join(hookDir, 'hooks.json');
|
|
144
|
+
let hooksConfig = {};
|
|
145
|
+
|
|
146
|
+
// Load existing hooks.json if it exists
|
|
147
|
+
if (fs.existsSync(hooksJsonPath)) {
|
|
148
|
+
try {
|
|
149
|
+
const existingContent = fs.readFileSync(hooksJsonPath, 'utf8');
|
|
150
|
+
hooksConfig = JSON.parse(existingContent);
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.warn(`[HOOK_DEPLOYMENT] Failed to parse existing hooks.json, creating new one: ${error.message}`);
|
|
153
|
+
hooksConfig = {};
|
|
299
154
|
}
|
|
300
155
|
}
|
|
301
156
|
|
|
302
|
-
|
|
157
|
+
// Register the stigmergy-resume command
|
|
158
|
+
hooksConfig.resumesession = {
|
|
159
|
+
enabled: true,
|
|
160
|
+
command: '/stigmergy-resume',
|
|
161
|
+
handler: resumeSessionFileName,
|
|
162
|
+
description: 'Resume or query session history across CLIs',
|
|
163
|
+
version: '1.0.4',
|
|
164
|
+
deploymentTime: new Date().toISOString()
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// Write the updated hooks.json
|
|
168
|
+
fs.writeFileSync(hooksJsonPath, JSON.stringify(hooksConfig, null, 2));
|
|
169
|
+
console.log(`[HOOK_DEPLOYMENT] Updated hooks.json with resumesession command: ${hooksJsonPath}`);
|
|
303
170
|
}
|
|
304
|
-
}
|
|
305
171
|
|
|
306
|
-
|
|
307
|
-
|
|
172
|
+
async deploySkillsIntegration(cliName, hookDir) {
|
|
173
|
+
console.log(`[HOOK_DEPLOYMENT] Deploying skills integration for ${cliName}...`);
|
|
308
174
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
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);
|
|
175
|
+
// Ensure hook directory exists
|
|
176
|
+
if (!fs.existsSync(hookDir)) {
|
|
177
|
+
fs.mkdirSync(hookDir, { recursive: true });
|
|
322
178
|
}
|
|
323
|
-
|
|
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
|
|
179
|
+
|
|
336
180
|
try {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
}
|
|
181
|
+
const skillsResult = this.skillsIntegrationGenerator.generateForCLI(cliName);
|
|
182
|
+
const skillsPath = path.join(hookDir, skillsResult.fileName);
|
|
183
|
+
|
|
184
|
+
fs.writeFileSync(skillsPath, skillsResult.content);
|
|
185
|
+
console.log(`[HOOK_DEPLOYMENT] Created skills integration: ${skillsPath}`);
|
|
186
|
+
|
|
187
|
+
// Make the skills file executable
|
|
188
|
+
try {
|
|
189
|
+
fs.chmodSync(skillsPath, 0o755);
|
|
190
|
+
console.log(`[HOOK_DEPLOYMENT] Made skills integration executable: ${skillsPath}`);
|
|
191
|
+
} catch (error) {
|
|
192
|
+
console.warn(`[HOOK_DEPLOYMENT] Failed to make skills integration executable: ${error.message}`);
|
|
350
193
|
}
|
|
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
194
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
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
|
-
];
|
|
195
|
+
// Create skills configuration
|
|
196
|
+
const skillsConfig = {
|
|
197
|
+
cli: cliName,
|
|
198
|
+
skillsPath: skillsPath,
|
|
199
|
+
skillsDirectory: path.join(os.homedir(), '.stigmergy', 'skills'),
|
|
200
|
+
deploymentTime: new Date().toISOString(),
|
|
201
|
+
version: '1.0.0-skills'
|
|
202
|
+
};
|
|
372
203
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
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
|
-
}
|
|
204
|
+
const configPath = path.join(hookDir, 'skills-config.json');
|
|
205
|
+
fs.writeFileSync(configPath, JSON.stringify(skillsConfig, null, 2));
|
|
206
|
+
console.log(`[HOOK_DEPLOYMENT] Created skills configuration: ${configPath}`);
|
|
385
207
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
if (supportedCLIs.includes(targetCLI)) {
|
|
392
|
-
return {
|
|
393
|
-
targetCLI: targetCLI,
|
|
394
|
-
task: task,
|
|
395
|
-
source: this.cliName
|
|
396
|
-
};
|
|
397
|
-
}
|
|
398
|
-
}
|
|
208
|
+
return true;
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.error(`[HOOK_DEPLOYMENT] Failed to deploy skills integration for ${cliName}:`, error);
|
|
211
|
+
return false;
|
|
399
212
|
}
|
|
400
|
-
|
|
401
|
-
return null;
|
|
402
213
|
}
|
|
403
|
-
async handleCrossCLIRequest(request, context) {
|
|
404
|
-
this.log('INFO', \`Cross-CLI request detected: \${JSON.stringify(request)}\`);
|
|
405
214
|
|
|
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
|
-
}
|
|
215
|
+
async deployCLIAdapter(cliName, hookDir) {
|
|
216
|
+
console.log(`[HOOK_DEPLOYMENT] Deploying CLI adapter for ${cliName}...`);
|
|
411
217
|
|
|
412
|
-
//
|
|
413
|
-
if (
|
|
414
|
-
|
|
415
|
-
return \`[CROSS-CLI] Cannot call self (\${request.targetCLI})\`;
|
|
218
|
+
// Ensure hook directory exists
|
|
219
|
+
if (!fs.existsSync(hookDir)) {
|
|
220
|
+
fs.mkdirSync(hookDir, { recursive: true });
|
|
416
221
|
}
|
|
417
222
|
|
|
418
|
-
// Communicate with the coordination layer to execute the cross-CLI call
|
|
419
223
|
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
|
-
}
|
|
224
|
+
const adapterContent = this.cliAdapterGenerator.generateForCLI(cliName);
|
|
225
|
+
const fileName = this.cliAdapterGenerator.getFileName(cliName);
|
|
226
|
+
const adapterPath = path.join(hookDir, fileName);
|
|
431
227
|
|
|
432
|
-
|
|
228
|
+
fs.writeFileSync(adapterPath, adapterContent);
|
|
229
|
+
console.log(`[HOOK_DEPLOYMENT] Created CLI adapter: ${adapterPath}`);
|
|
433
230
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
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}\`;
|
|
231
|
+
// Make the adapter executable
|
|
232
|
+
try {
|
|
233
|
+
fs.chmodSync(adapterPath, 0o755);
|
|
234
|
+
console.log(`[HOOK_DEPLOYMENT] Made adapter executable: ${adapterPath}`);
|
|
235
|
+
} catch (error) {
|
|
236
|
+
console.warn(`[HOOK_DEPLOYMENT] Failed to make adapter executable: ${error.message}`);
|
|
445
237
|
}
|
|
238
|
+
|
|
239
|
+
return true;
|
|
446
240
|
} catch (error) {
|
|
447
|
-
|
|
448
|
-
return
|
|
241
|
+
console.error(`[HOOK_DEPLOYMENT] Failed to deploy CLI adapter for ${cliName}:`, error);
|
|
242
|
+
return false;
|
|
449
243
|
}
|
|
450
244
|
}
|
|
451
|
-
log(level, message) {
|
|
452
|
-
const timestamp = new Date().toISOString();
|
|
453
|
-
const logEntry = \`[\${timestamp}] [\${level}] [\${this.cliName.toUpperCase()}] \${message}\\n\`;
|
|
454
245
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
246
|
+
async createBasicConfiguration(cliName, hookDir) {
|
|
247
|
+
console.log(`[HOOK_DEPLOYMENT] Creating basic configuration for ${cliName}...`);
|
|
248
|
+
|
|
249
|
+
// Ensure hook directory exists
|
|
250
|
+
if (!fs.existsSync(hookDir)) {
|
|
251
|
+
fs.mkdirSync(hookDir, { recursive: true });
|
|
459
252
|
}
|
|
460
|
-
}
|
|
461
|
-
capitalize(str) {
|
|
462
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
253
|
|
|
466
|
-
|
|
467
|
-
|
|
254
|
+
try {
|
|
255
|
+
// Create main configuration file
|
|
256
|
+
const config = {
|
|
257
|
+
cli: cliName,
|
|
258
|
+
hookPath: hookDir,
|
|
259
|
+
deploymentTime: new Date().toISOString(),
|
|
260
|
+
version: '1.0.0-nodejs',
|
|
261
|
+
modules: {
|
|
262
|
+
resumeSession: true,
|
|
263
|
+
skillsIntegration: true,
|
|
264
|
+
cliAdapter: true
|
|
265
|
+
}
|
|
266
|
+
};
|
|
468
267
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
console.log('${cliName.toUpperCase()} Node.js Hook initialized');
|
|
473
|
-
}`;
|
|
474
|
-
}
|
|
268
|
+
const configPath = path.join(hookDir, 'config.json');
|
|
269
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
270
|
+
console.log(`[HOOK_DEPLOYMENT] Created configuration: ${configPath}`);
|
|
475
271
|
|
|
476
|
-
|
|
477
|
-
|
|
272
|
+
return true;
|
|
273
|
+
} catch (error) {
|
|
274
|
+
console.error(`[HOOK_DEPLOYMENT] Failed to create configuration for ${cliName}:`, error);
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
478
277
|
}
|
|
479
278
|
|
|
480
|
-
async
|
|
481
|
-
console.log(
|
|
279
|
+
async deployHooksForAllCLIs(options = {}) {
|
|
280
|
+
console.log('[HOOK_DEPLOYMENT] Deploying hooks for all supported CLIs...');
|
|
482
281
|
|
|
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
|
-
}
|
|
282
|
+
const results = [];
|
|
283
|
+
for (const cliName of this.supportedCLIs) {
|
|
284
|
+
const success = await this.deployHooksForCLI(cliName, options);
|
|
285
|
+
results.push({ cli: cliName, success });
|
|
496
286
|
}
|
|
497
287
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
288
|
+
const successful = results.filter(r => r.success).length;
|
|
289
|
+
console.log(
|
|
290
|
+
`[HOOK_DEPLOYMENT] Deployment complete: ${successful}/${this.supportedCLIs.length} CLIs configured`,
|
|
291
|
+
);
|
|
501
292
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
return [];
|
|
505
|
-
}
|
|
293
|
+
return results;
|
|
294
|
+
}
|
|
506
295
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
.
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
296
|
+
async getDeployedHooks() {
|
|
297
|
+
try {
|
|
298
|
+
const cliDirs = fs.readdirSync(this.deploymentDir, { withFileTypes: true })
|
|
299
|
+
.filter(dirent => dirent.isDirectory())
|
|
300
|
+
.map(dirent => dirent.name);
|
|
301
|
+
|
|
302
|
+
const hooks = [];
|
|
303
|
+
for (const cliName of cliDirs) {
|
|
304
|
+
const configPath = path.join(this.deploymentDir, cliName, 'config.json');
|
|
305
|
+
if (fs.existsSync(configPath)) {
|
|
306
|
+
try {
|
|
307
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
308
|
+
hooks.push(config);
|
|
309
|
+
} catch (error) {
|
|
310
|
+
console.warn(
|
|
311
|
+
`[HOOK_DEPLOYMENT] Failed to read config for ${cliName}:`,
|
|
312
|
+
error.message,
|
|
313
|
+
);
|
|
314
|
+
}
|
|
524
315
|
}
|
|
525
316
|
}
|
|
526
|
-
}
|
|
527
317
|
|
|
528
|
-
|
|
318
|
+
return hooks;
|
|
319
|
+
} catch (error) {
|
|
320
|
+
console.error('[HOOK_DEPLOYMENT] Failed to get deployed hooks:', error);
|
|
321
|
+
return [];
|
|
322
|
+
}
|
|
529
323
|
}
|
|
530
324
|
|
|
531
325
|
async validateHookDeployment(cliName) {
|
|
@@ -543,18 +337,54 @@ if (require.main === module) {
|
|
|
543
337
|
return { valid: false, error: 'Configuration file not found' };
|
|
544
338
|
}
|
|
545
339
|
|
|
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
340
|
try {
|
|
553
|
-
|
|
554
|
-
|
|
341
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
342
|
+
|
|
343
|
+
// Validate that all expected modules are present
|
|
344
|
+
const expectedModules = ['resumeSession', 'skillsIntegration', 'cliAdapter'];
|
|
345
|
+
if (!config.modules) {
|
|
346
|
+
return { valid: false, error: 'Module configuration not found' };
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
for (const module of expectedModules) {
|
|
350
|
+
if (!config.modules[module]) {
|
|
351
|
+
return { valid: false, error: `Module ${module} not configured` };
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return {
|
|
356
|
+
valid: true,
|
|
357
|
+
message: 'Hook deployment is valid',
|
|
358
|
+
modules: config.modules
|
|
359
|
+
};
|
|
555
360
|
} catch (error) {
|
|
556
|
-
return { valid: false, error: `Failed to
|
|
361
|
+
return { valid: false, error: `Failed to validate deployment: ${error.message}` };
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
async validateAllDeployments() {
|
|
366
|
+
console.log('[HOOK_DEPLOYMENT] Validating all hook deployments...');
|
|
367
|
+
|
|
368
|
+
const results = [];
|
|
369
|
+
for (const cliName of this.supportedCLIs) {
|
|
370
|
+
const validation = await this.validateHookDeployment(cliName);
|
|
371
|
+
results.push({ cli: cliName, ...validation });
|
|
557
372
|
}
|
|
373
|
+
|
|
374
|
+
const valid = results.filter(r => r.valid).length;
|
|
375
|
+
console.log(
|
|
376
|
+
`[HOOK_DEPLOYMENT] Validation complete: ${valid}/${this.supportedCLIs.length} deployments valid`,
|
|
377
|
+
);
|
|
378
|
+
|
|
379
|
+
return results;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
getDeploymentStatus() {
|
|
383
|
+
return {
|
|
384
|
+
deploymentDir: this.deploymentDir,
|
|
385
|
+
supportedCLIs: this.supportedCLIs,
|
|
386
|
+
initialized: fs.existsSync(this.deploymentDir)
|
|
387
|
+
};
|
|
558
388
|
}
|
|
559
389
|
}
|
|
560
390
|
|