stigmergy 1.0.89 → 1.0.92
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 +202 -189
- package/bin/stigmergy.cmd +5 -0
- package/docs/CONFLICT_PREVENTION.md +150 -0
- package/package.json +11 -5
- package/scripts/post-deployment-config.js +289 -0
- package/scripts/preinstall-check.js +111 -0
- package/scripts/safe-install.js +139 -0
- package/src/main.js +963 -892
- package/src/main_english.js +179 -35
- package/test/comprehensive-execution-test.js +428 -0
- package/test/conflict-prevention-test.js +95 -0
- package/test/deploy-hooks-test.js +250 -0
- package/test/error-handling-test.js +341 -0
- package/test/final-deploy-test.js +221 -0
- package/test/final-install-test.js +226 -0
- package/test/iflow-integration-test.js +292 -0
- package/test/improved-install-test.js +362 -0
- package/test/install-command-test.js +370 -0
- package/test/plugin-deployment-test.js +316 -0
- package/test/postinstall-test.js +269 -0
- package/test/simple-iflow-hook-test.js +137 -0
- package/test/tdd-deploy-fix-test.js +324 -0
package/src/main.js
CHANGED
|
@@ -1,893 +1,964 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Stigmergy CLI - Multi-Agents Cross-AI CLI Tools Collaboration System
|
|
5
|
-
* International Version - Pure ANSI Characters Only
|
|
6
|
-
* Version: 1.0.76
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const { spawn, spawnSync } = require('child_process');
|
|
10
|
-
const path = require('path');
|
|
11
|
-
const fs = require('fs/promises');
|
|
12
|
-
const os = require('os');
|
|
13
|
-
|
|
14
|
-
// AI CLI Tools Configuration
|
|
15
|
-
const CLI_TOOLS = {
|
|
16
|
-
claude: {
|
|
17
|
-
name: 'Claude CLI',
|
|
18
|
-
version: 'claude --version',
|
|
19
|
-
install: 'npm install -g @anthropic-ai/claude-cli',
|
|
20
|
-
hooksDir: path.join(os.homedir(), '.claude', 'hooks'),
|
|
21
|
-
config: path.join(os.homedir(), '.claude', 'config.json')
|
|
22
|
-
},
|
|
23
|
-
gemini: {
|
|
24
|
-
name: 'Gemini CLI',
|
|
25
|
-
version: 'gemini --version',
|
|
26
|
-
install: 'npm install -g @google/generative-ai-cli',
|
|
27
|
-
hooksDir: path.join(os.homedir(), '.gemini', 'extensions'),
|
|
28
|
-
config: path.join(os.homedir(), '.gemini', 'config.json')
|
|
29
|
-
},
|
|
30
|
-
qwen: {
|
|
31
|
-
name: 'Qwen CLI',
|
|
32
|
-
version: 'qwen --version',
|
|
33
|
-
install: 'npm install -g @alibaba/qwen-cli',
|
|
34
|
-
hooksDir: path.join(os.homedir(), '.qwen', 'hooks'),
|
|
35
|
-
config: path.join(os.homedir(), '.qwen', 'config.json')
|
|
36
|
-
},
|
|
37
|
-
iflow: {
|
|
38
|
-
name: 'iFlow CLI',
|
|
39
|
-
version: 'iflow --version',
|
|
40
|
-
install: 'npm install -g iflow-cli',
|
|
41
|
-
hooksDir: path.join(os.homedir(), '.iflow', 'hooks'),
|
|
42
|
-
config: path.join(os.homedir(), '.iflow', 'config.json')
|
|
43
|
-
},
|
|
44
|
-
qoder: {
|
|
45
|
-
name: 'Qoder CLI',
|
|
46
|
-
version: 'qodercli --version',
|
|
47
|
-
install: 'npm install -g @qoder-ai/qodercli',
|
|
48
|
-
hooksDir: path.join(os.homedir(), '.qoder', 'hooks'),
|
|
49
|
-
config: path.join(os.homedir(), '.qoder', 'config.json')
|
|
50
|
-
},
|
|
51
|
-
codebuddy: {
|
|
52
|
-
name: 'CodeBuddy CLI',
|
|
53
|
-
version: 'codebuddy --version',
|
|
54
|
-
install: 'npm install -g codebuddy-cli',
|
|
55
|
-
hooksDir: path.join(os.homedir(), '.codebuddy', 'hooks'),
|
|
56
|
-
config: path.join(os.homedir(), '.codebuddy', 'config.json')
|
|
57
|
-
},
|
|
58
|
-
copilot: {
|
|
59
|
-
name: 'GitHub Copilot CLI',
|
|
60
|
-
version: 'copilot --version',
|
|
61
|
-
install: 'npm install -g @github/copilot-cli',
|
|
62
|
-
hooksDir: path.join(os.homedir(), '.copilot', 'mcp'),
|
|
63
|
-
config: path.join(os.homedir(), '.copilot', 'config.json')
|
|
64
|
-
},
|
|
65
|
-
codex: {
|
|
66
|
-
name: 'OpenAI Codex CLI',
|
|
67
|
-
version: 'codex --version',
|
|
68
|
-
install: 'npm install -g openai-codex-cli',
|
|
69
|
-
hooksDir: path.join(os.homedir(), '.config', 'codex', 'slash_commands'),
|
|
70
|
-
config: path.join(os.homedir(), '.codex', 'config.json')
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
class SmartRouter {
|
|
75
|
-
constructor() {
|
|
76
|
-
this.tools = CLI_TOOLS;
|
|
77
|
-
this.routeKeywords = ['use', 'help', 'please', 'write', 'generate', 'explain', 'analyze', 'translate', 'code', 'article'];
|
|
78
|
-
this.defaultTool = 'claude';
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
shouldRoute(userInput) {
|
|
82
|
-
return this.routeKeywords.some(keyword =>
|
|
83
|
-
userInput.toLowerCase().includes(keyword.toLowerCase())
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
smartRoute(userInput) {
|
|
88
|
-
const input = userInput.trim();
|
|
89
|
-
|
|
90
|
-
// Detect tool-specific keywords
|
|
91
|
-
for (const [toolName, toolInfo] of Object.entries(this.tools)) {
|
|
92
|
-
for (const keyword of this.extractKeywords(toolName)) {
|
|
93
|
-
if (input.toLowerCase().includes(keyword.toLowerCase())) {
|
|
94
|
-
// Extract clean parameters
|
|
95
|
-
const cleanInput = input
|
|
96
|
-
.replace(new RegExp(`.*${keyword}\\s*`, 'gi'), '')
|
|
97
|
-
.replace(/^(用|帮我|请|麻烦|给我|帮我写|帮我生成)\s*/i, '')
|
|
98
|
-
.trim();
|
|
99
|
-
return { tool: toolName, prompt: cleanInput };
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Default routing
|
|
105
|
-
const cleanInput = input
|
|
106
|
-
.replace(/^(用|帮我|请|麻烦|给我|帮我写|帮我生成)\s*/i, '')
|
|
107
|
-
.trim();
|
|
108
|
-
return { tool: this.defaultTool, prompt: cleanInput };
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
extractKeywords(toolName) {
|
|
112
|
-
const keywordMap = {
|
|
113
|
-
claude: ['claude', 'anthropic'],
|
|
114
|
-
gemini: ['gemini', 'google', '谷歌'],
|
|
115
|
-
qwen: ['qwen', '通义', '阿里'],
|
|
116
|
-
iflow: ['iflow', '心流', 'intelligent'],
|
|
117
|
-
qoder: ['qoder', 'qodercli'],
|
|
118
|
-
codebuddy: ['codebuddy', 'buddy'],
|
|
119
|
-
copilot: ['copilot', 'github'],
|
|
120
|
-
codex: ['codex', 'openai']
|
|
121
|
-
};
|
|
122
|
-
return keywordMap[toolName] || [toolName];
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
async executeTool(toolName, prompt) {
|
|
126
|
-
const tool = this.tools[toolName];
|
|
127
|
-
if (!tool) {
|
|
128
|
-
return { success: false, error: `Unknown tool: ${toolName}` };
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (!this.checkCLI(toolName)) {
|
|
132
|
-
return { success: false, error: `${tool.name} is not available` };
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
try {
|
|
136
|
-
const result = spawnSync(toolName, [prompt], {
|
|
137
|
-
encoding: 'utf8',
|
|
138
|
-
timeout: 30000
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
return {
|
|
142
|
-
success: result.status === 0,
|
|
143
|
-
output: result.stdout,
|
|
144
|
-
error: result.stderr
|
|
145
|
-
};
|
|
146
|
-
} catch (error) {
|
|
147
|
-
return { success: false, error: error.message };
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
checkCLI(toolName) {
|
|
152
|
-
try {
|
|
153
|
-
const tool = this.tools[toolName];
|
|
154
|
-
const command = tool.version.split(' ')[0];
|
|
155
|
-
const args = tool.version.split(' ').slice(1);
|
|
156
|
-
|
|
157
|
-
const result = spawnSync(command, args, {
|
|
158
|
-
stdio: 'ignore',
|
|
159
|
-
timeout: 10000,
|
|
160
|
-
env: { ...process.env }
|
|
161
|
-
});
|
|
162
|
-
return result.status === 0;
|
|
163
|
-
} catch (error) {
|
|
164
|
-
return false;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
async analyzeCLIHelp(toolName) {
|
|
169
|
-
const tool = this.tools[toolName];
|
|
170
|
-
if (!tool) return null;
|
|
171
|
-
|
|
172
|
-
try {
|
|
173
|
-
const result = spawnSync(toolName, ['--help'], {
|
|
174
|
-
encoding: 'utf8',
|
|
175
|
-
timeout: 15000
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
if (result.status === 0) {
|
|
179
|
-
return this.parseHelpOutput(result.stdout);
|
|
180
|
-
}
|
|
181
|
-
return null;
|
|
182
|
-
} catch (error) {
|
|
183
|
-
return null;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
parseHelpOutput(helpText) {
|
|
188
|
-
const capabilities = {
|
|
189
|
-
commands: [],
|
|
190
|
-
options: [],
|
|
191
|
-
examples: [],
|
|
192
|
-
features: []
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
// Extract commands
|
|
196
|
-
const commandMatches = helpText.match(/^\s{0,4}([a-z][a-z0-9_-]+)\s+.+$/gm);
|
|
197
|
-
if (commandMatches) {
|
|
198
|
-
capabilities.commands = commandMatches.map(match => match.trim());
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Extract options
|
|
202
|
-
const optionMatches = helpText.match(/--[a-z-]+/g);
|
|
203
|
-
if (optionMatches) {
|
|
204
|
-
capabilities.options = [...new Set(optionMatches)];
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Extract examples
|
|
208
|
-
const exampleMatches = helpText.match(/(example|Usage|用法)[::]\s*\n([\s\S]*?)(?=\n\n|\n[A-Z]|\n$)/gi);
|
|
209
|
-
if (exampleMatches) {
|
|
210
|
-
capabilities.examples = exampleMatches.map(match => match.replace(/^(example|Usage|用法)[::]\s*/i, '').trim());
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
return capabilities;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
class StigmergyInstaller {
|
|
218
|
-
constructor() {
|
|
219
|
-
this.homeDir = os.homedir();
|
|
220
|
-
this.stigmergyDir = path.join(this.homeDir, '.stigmergy');
|
|
221
|
-
this.projectDir = process.cwd();
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
async ensureDirectory(dirPath) {
|
|
225
|
-
try {
|
|
226
|
-
await fs.mkdir(dirPath, { recursive: true });
|
|
227
|
-
return true;
|
|
228
|
-
} catch (error) {
|
|
229
|
-
return false;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
checkCLI(toolName) {
|
|
234
|
-
try {
|
|
235
|
-
const tool = CLI_TOOLS[toolName];
|
|
236
|
-
const command = tool.version.split(' ')[0];
|
|
237
|
-
const args = tool.version.split(' ').slice(1);
|
|
238
|
-
|
|
239
|
-
const result = spawnSync(command, args, {
|
|
240
|
-
stdio: 'ignore',
|
|
241
|
-
timeout: 10000,
|
|
242
|
-
env: { ...process.env }
|
|
243
|
-
});
|
|
244
|
-
return result.status === 0;
|
|
245
|
-
} catch (error) {
|
|
246
|
-
return false;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
async scanAvailableTools() {
|
|
251
|
-
const results = {
|
|
252
|
-
total: Object.keys(CLI_TOOLS).length,
|
|
253
|
-
available: [],
|
|
254
|
-
unavailable: [],
|
|
255
|
-
details: []
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
console.log('[SCAN] Scanning for AI CLI tools on your system...');
|
|
259
|
-
console.log('='.repeat(60));
|
|
260
|
-
|
|
261
|
-
for (const [key, tool] of Object.entries(CLI_TOOLS)) {
|
|
262
|
-
const isAvailable = this.checkCLI(key);
|
|
263
|
-
|
|
264
|
-
if (isAvailable) {
|
|
265
|
-
results.available.push(key);
|
|
266
|
-
console.log(`[OK] ${tool.name}: Available (${tool.version})`);
|
|
267
|
-
results.details.push({
|
|
268
|
-
key,
|
|
269
|
-
name: tool.name,
|
|
270
|
-
status: 'Available',
|
|
271
|
-
install: tool.install,
|
|
272
|
-
hooksDir: tool.hooksDir
|
|
273
|
-
});
|
|
274
|
-
} else {
|
|
275
|
-
results.unavailable.push(key);
|
|
276
|
-
console.log(`[X] ${tool.name}: Not Available`);
|
|
277
|
-
results.details.push({
|
|
278
|
-
key,
|
|
279
|
-
name: tool.name,
|
|
280
|
-
status: 'Not Available',
|
|
281
|
-
install: tool.install,
|
|
282
|
-
hooksDir: tool.hooksDir
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
console.log('='.repeat(60));
|
|
288
|
-
console.log(`[SUMMARY] ${results.available.length}/${results.total} tools available`);
|
|
289
|
-
console.log('');
|
|
290
|
-
|
|
291
|
-
return results;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
async promptForInstallation(scanResults) {
|
|
295
|
-
if (scanResults.unavailable.length === 0) {
|
|
296
|
-
console.log('[SUCCESS] All AI CLI tools are already installed!');
|
|
297
|
-
return [];
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
console.log('[INSTALL] The following AI CLI tools can be automatically installed:');
|
|
301
|
-
console.log('');
|
|
302
|
-
|
|
303
|
-
scanResults.unavailable.forEach((toolKey, index) => {
|
|
304
|
-
const tool = CLI_TOOLS[toolKey];
|
|
305
|
-
console.log(` ${index + 1}. ${tool.name}`);
|
|
306
|
-
console.log(` Install: ${tool.install}`);
|
|
307
|
-
console.log('');
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
console.log('[OPTIONS] Installation Options:');
|
|
311
|
-
console.log(' - Enter numbers separated by spaces (e.g: 1 3 5)');
|
|
312
|
-
console.log(' - Enter "all" to install all missing tools');
|
|
313
|
-
console.log(' - Enter "skip" to skip CLI installation');
|
|
314
|
-
|
|
315
|
-
return new Promise((resolve) => {
|
|
316
|
-
process.stdout.write('\n[SELECT] Select tools to install: ');
|
|
317
|
-
|
|
318
|
-
process.stdin.once('data', (data) => {
|
|
319
|
-
const input = data.toString().trim();
|
|
320
|
-
|
|
321
|
-
if (input === '' || input.toLowerCase() === 'skip') {
|
|
322
|
-
resolve([]);
|
|
323
|
-
} else if (input.toLowerCase() === 'all') {
|
|
324
|
-
resolve(scanResults.unavailable);
|
|
325
|
-
} else {
|
|
326
|
-
const numbers = input.split(/\s+/).map(n => parseInt(n) - 1);
|
|
327
|
-
const selected = numbers
|
|
328
|
-
.filter(n => n >= 0 && n < scanResults.unavailable.length)
|
|
329
|
-
.map(n => scanResults.unavailable[n]);
|
|
330
|
-
resolve(selected);
|
|
331
|
-
}
|
|
332
|
-
});
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
async installTools(toolKeys) {
|
|
337
|
-
if (toolKeys.length === 0) {
|
|
338
|
-
console.log('[SKIP] Skipping CLI tool installation.');
|
|
339
|
-
return;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
console.log(`\n[INSTALL] Installing ${toolKeys.length} AI CLI tools...`);
|
|
343
|
-
console.log('='.repeat(60));
|
|
344
|
-
|
|
345
|
-
for (const toolKey of toolKeys) {
|
|
346
|
-
const tool = CLI_TOOLS[toolKey];
|
|
347
|
-
console.log(`\n[INSTALLING] Installing ${tool.name}...`);
|
|
348
|
-
|
|
349
|
-
try {
|
|
350
|
-
const packageInstall = spawn('npm', ['install', '-g'].concat(tool.install.split(' ').slice(3)), {
|
|
351
|
-
stdio: 'inherit',
|
|
352
|
-
shell: true
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
await new Promise((resolve, reject) => {
|
|
356
|
-
packageInstall.on('close', (code) => {
|
|
357
|
-
if (code === 0) {
|
|
358
|
-
console.log(`[OK] ${tool.name} installed successfully!`);
|
|
359
|
-
resolve();
|
|
360
|
-
} else {
|
|
361
|
-
console.log(`[ERROR] Failed to install ${tool.name}`);
|
|
362
|
-
reject(new Error(`Installation failed with code ${code}`));
|
|
363
|
-
}
|
|
364
|
-
});
|
|
365
|
-
});
|
|
366
|
-
} catch (error) {
|
|
367
|
-
console.log(`[ERROR] Error installing ${tool.name}:`, error.message);
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
console.log('\n[VERIFY] CLI Installation completed! Verifying...');
|
|
372
|
-
await this.verifyInstallation(toolKeys);
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
async verifyInstallation(toolKeys) {
|
|
376
|
-
console.log('='.repeat(60));
|
|
377
|
-
let successCount = 0;
|
|
378
|
-
|
|
379
|
-
for (const toolKey of toolKeys) {
|
|
380
|
-
const tool = CLI_TOOLS[toolKey];
|
|
381
|
-
if (this.checkCLI(toolKey)) {
|
|
382
|
-
console.log(`[OK] ${tool.name}: Successfully installed and functional!`);
|
|
383
|
-
successCount++;
|
|
384
|
-
} else {
|
|
385
|
-
console.log(`[FAIL] ${tool.name}: Installation verification failed`);
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
console.log(`\n[RESULT] Installation Result: ${successCount}/${toolKeys.length} tools successfully installed`);
|
|
390
|
-
|
|
391
|
-
if (successCount === toolKeys.length) {
|
|
392
|
-
console.log('[SUCCESS] All selected CLI tools are now ready to use!');
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
async deployHooks(availableTools) {
|
|
397
|
-
if (availableTools.length === 0) {
|
|
398
|
-
console.log('[SKIP] No CLI tools available for hook deployment.');
|
|
399
|
-
return;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
console.log(`\n[DEPLOY] Deploying Stigmergy hooks to ${availableTools.length} CLI tools...`);
|
|
403
|
-
console.log('='.repeat(60));
|
|
404
|
-
|
|
405
|
-
const hookTemplate = `#!/usr/bin/env node
|
|
406
|
-
|
|
407
|
-
/**
|
|
408
|
-
* Stigmergy Hook - ${new Date().toISOString()}
|
|
409
|
-
* Generated by Stigmergy CLI
|
|
410
|
-
*/
|
|
411
|
-
|
|
412
|
-
const { spawn } = require('child_process');
|
|
413
|
-
|
|
414
|
-
// Stigmergy Hook Implementation
|
|
415
|
-
class StigmergyHook {
|
|
416
|
-
constructor() {
|
|
417
|
-
this.processArgs();
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
processArgs() {
|
|
421
|
-
const args = process.argv.slice(2);
|
|
422
|
-
if (args.length === 0) {
|
|
423
|
-
this.showHelp();
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
const command = args[0];
|
|
428
|
-
switch (command) {
|
|
429
|
-
case 'collaborate':
|
|
430
|
-
this.handleCollaborate(args.slice(1));
|
|
431
|
-
break;
|
|
432
|
-
case 'status':
|
|
433
|
-
this.showStatus();
|
|
434
|
-
break;
|
|
435
|
-
case 'init':
|
|
436
|
-
this.initProject(args.slice(1));
|
|
437
|
-
break;
|
|
438
|
-
default:
|
|
439
|
-
this.showHelp();
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
handleCollaborate(args) {
|
|
444
|
-
console.log('[COLLAB] Stigmergy Collaboration System');
|
|
445
|
-
console.log('Available AI CLI tools:', Object.keys(CLI_TOOLS).join(', '));
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
showStatus() {
|
|
449
|
-
console.log('[STATUS] Stigmergy Hook Status: Active');
|
|
450
|
-
console.log('Configuration: Loaded successfully');
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
initProject(projectName) {
|
|
454
|
-
console.log('[INIT] Initializing Stigmergy project:', projectName || 'current');
|
|
455
|
-
console.log('[OK] Project configuration created');
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
showHelp() {
|
|
459
|
-
console.log('Stigmergy Hook - Multi-AI CLI Collaboration');
|
|
460
|
-
console.log('');
|
|
461
|
-
console.log('Commands:');
|
|
462
|
-
console.log(' collaborate [options] Start collaboration with other AI CLI tools');
|
|
463
|
-
console.log(' status Show hook status');
|
|
464
|
-
console.log(' init [project] Initialize Stigmergy project');
|
|
465
|
-
console.log(' help Show this help message');
|
|
466
|
-
console.log('');
|
|
467
|
-
console.log('Examples:');
|
|
468
|
-
console.log(' stigmergy-hook collaborate claude gemini');
|
|
469
|
-
console.log(' stigmergy-hook status');
|
|
470
|
-
console.log(' stigmergy-hook init my-project');
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
// Initialize hook
|
|
475
|
-
const hook = new StigmergyHook();
|
|
476
|
-
`;
|
|
477
|
-
|
|
478
|
-
let deployedCount = 0;
|
|
479
|
-
|
|
480
|
-
for (const toolKey of availableTools) {
|
|
481
|
-
const tool = CLI_TOOLS[toolKey];
|
|
482
|
-
const hooksDir = tool.hooksDir;
|
|
483
|
-
|
|
484
|
-
await this.ensureDirectory(hooksDir);
|
|
485
|
-
|
|
486
|
-
const hookFile = path.join(hooksDir, 'stigmergy-hook.cjs');
|
|
487
|
-
try {
|
|
488
|
-
await fs.writeFile(hookFile, hookTemplate, 'utf8');
|
|
489
|
-
|
|
490
|
-
if (process.platform !== 'win32') {
|
|
491
|
-
const { spawn } = require('child_process');
|
|
492
|
-
spawn('chmod', ['+x', hookFile], { stdio: 'ignore' });
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
console.log(`[OK] ${tool.name}: Hook deployed to ${hooksDir}`);
|
|
496
|
-
deployedCount++;
|
|
497
|
-
} catch (error) {
|
|
498
|
-
console.log(`[FAIL] ${tool.name}: Failed to deploy hook - ${error.message}`);
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
console.log('\n[RESULT] Hook Deployment Result: ' + deployedCount + '/' + availableTools.length + ' hooks deployed');
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
async
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
console.log('
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
console.log('
|
|
664
|
-
console.log('
|
|
665
|
-
console.log('
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
console.log('
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
console.log('
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
console.log('
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
const
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
console.log('
|
|
700
|
-
console.log('');
|
|
701
|
-
console.log('
|
|
702
|
-
console.log('');
|
|
703
|
-
console.log('
|
|
704
|
-
console.log('
|
|
705
|
-
console.log('
|
|
706
|
-
console.log('
|
|
707
|
-
console.log('
|
|
708
|
-
console.log('
|
|
709
|
-
console.log('
|
|
710
|
-
console.log('
|
|
711
|
-
console.log('
|
|
712
|
-
console.log('
|
|
713
|
-
console.log('');
|
|
714
|
-
console.log('
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
if (
|
|
727
|
-
console.log('
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
await
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
tool
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Stigmergy CLI - Multi-Agents Cross-AI CLI Tools Collaboration System
|
|
5
|
+
* International Version - Pure ANSI Characters Only
|
|
6
|
+
* Version: 1.0.76
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { spawn, spawnSync } = require('child_process');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const fs = require('fs/promises');
|
|
12
|
+
const os = require('os');
|
|
13
|
+
|
|
14
|
+
// AI CLI Tools Configuration
|
|
15
|
+
const CLI_TOOLS = {
|
|
16
|
+
claude: {
|
|
17
|
+
name: 'Claude CLI',
|
|
18
|
+
version: 'claude --version',
|
|
19
|
+
install: 'npm install -g @anthropic-ai/claude-cli',
|
|
20
|
+
hooksDir: path.join(os.homedir(), '.claude', 'hooks'),
|
|
21
|
+
config: path.join(os.homedir(), '.claude', 'config.json')
|
|
22
|
+
},
|
|
23
|
+
gemini: {
|
|
24
|
+
name: 'Gemini CLI',
|
|
25
|
+
version: 'gemini --version',
|
|
26
|
+
install: 'npm install -g @google/generative-ai-cli',
|
|
27
|
+
hooksDir: path.join(os.homedir(), '.gemini', 'extensions'),
|
|
28
|
+
config: path.join(os.homedir(), '.gemini', 'config.json')
|
|
29
|
+
},
|
|
30
|
+
qwen: {
|
|
31
|
+
name: 'Qwen CLI',
|
|
32
|
+
version: 'qwen --version',
|
|
33
|
+
install: 'npm install -g @alibaba/qwen-cli',
|
|
34
|
+
hooksDir: path.join(os.homedir(), '.qwen', 'hooks'),
|
|
35
|
+
config: path.join(os.homedir(), '.qwen', 'config.json')
|
|
36
|
+
},
|
|
37
|
+
iflow: {
|
|
38
|
+
name: 'iFlow CLI',
|
|
39
|
+
version: 'iflow --version',
|
|
40
|
+
install: 'npm install -g iflow-cli',
|
|
41
|
+
hooksDir: path.join(os.homedir(), '.iflow', 'hooks'),
|
|
42
|
+
config: path.join(os.homedir(), '.iflow', 'config.json')
|
|
43
|
+
},
|
|
44
|
+
qoder: {
|
|
45
|
+
name: 'Qoder CLI',
|
|
46
|
+
version: 'qodercli --version',
|
|
47
|
+
install: 'npm install -g @qoder-ai/qodercli',
|
|
48
|
+
hooksDir: path.join(os.homedir(), '.qoder', 'hooks'),
|
|
49
|
+
config: path.join(os.homedir(), '.qoder', 'config.json')
|
|
50
|
+
},
|
|
51
|
+
codebuddy: {
|
|
52
|
+
name: 'CodeBuddy CLI',
|
|
53
|
+
version: 'codebuddy --version',
|
|
54
|
+
install: 'npm install -g codebuddy-cli',
|
|
55
|
+
hooksDir: path.join(os.homedir(), '.codebuddy', 'hooks'),
|
|
56
|
+
config: path.join(os.homedir(), '.codebuddy', 'config.json')
|
|
57
|
+
},
|
|
58
|
+
copilot: {
|
|
59
|
+
name: 'GitHub Copilot CLI',
|
|
60
|
+
version: 'copilot --version',
|
|
61
|
+
install: 'npm install -g @github/copilot-cli',
|
|
62
|
+
hooksDir: path.join(os.homedir(), '.copilot', 'mcp'),
|
|
63
|
+
config: path.join(os.homedir(), '.copilot', 'config.json')
|
|
64
|
+
},
|
|
65
|
+
codex: {
|
|
66
|
+
name: 'OpenAI Codex CLI',
|
|
67
|
+
version: 'codex --version',
|
|
68
|
+
install: 'npm install -g openai-codex-cli',
|
|
69
|
+
hooksDir: path.join(os.homedir(), '.config', 'codex', 'slash_commands'),
|
|
70
|
+
config: path.join(os.homedir(), '.codex', 'config.json')
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
class SmartRouter {
|
|
75
|
+
constructor() {
|
|
76
|
+
this.tools = CLI_TOOLS;
|
|
77
|
+
this.routeKeywords = ['use', 'help', 'please', 'write', 'generate', 'explain', 'analyze', 'translate', 'code', 'article'];
|
|
78
|
+
this.defaultTool = 'claude';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
shouldRoute(userInput) {
|
|
82
|
+
return this.routeKeywords.some(keyword =>
|
|
83
|
+
userInput.toLowerCase().includes(keyword.toLowerCase())
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
smartRoute(userInput) {
|
|
88
|
+
const input = userInput.trim();
|
|
89
|
+
|
|
90
|
+
// Detect tool-specific keywords
|
|
91
|
+
for (const [toolName, toolInfo] of Object.entries(this.tools)) {
|
|
92
|
+
for (const keyword of this.extractKeywords(toolName)) {
|
|
93
|
+
if (input.toLowerCase().includes(keyword.toLowerCase())) {
|
|
94
|
+
// Extract clean parameters
|
|
95
|
+
const cleanInput = input
|
|
96
|
+
.replace(new RegExp(`.*${keyword}\\s*`, 'gi'), '')
|
|
97
|
+
.replace(/^(用|帮我|请|麻烦|给我|帮我写|帮我生成)\s*/i, '')
|
|
98
|
+
.trim();
|
|
99
|
+
return { tool: toolName, prompt: cleanInput };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Default routing
|
|
105
|
+
const cleanInput = input
|
|
106
|
+
.replace(/^(用|帮我|请|麻烦|给我|帮我写|帮我生成)\s*/i, '')
|
|
107
|
+
.trim();
|
|
108
|
+
return { tool: this.defaultTool, prompt: cleanInput };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
extractKeywords(toolName) {
|
|
112
|
+
const keywordMap = {
|
|
113
|
+
claude: ['claude', 'anthropic'],
|
|
114
|
+
gemini: ['gemini', 'google', '谷歌'],
|
|
115
|
+
qwen: ['qwen', '通义', '阿里'],
|
|
116
|
+
iflow: ['iflow', '心流', 'intelligent'],
|
|
117
|
+
qoder: ['qoder', 'qodercli'],
|
|
118
|
+
codebuddy: ['codebuddy', 'buddy'],
|
|
119
|
+
copilot: ['copilot', 'github'],
|
|
120
|
+
codex: ['codex', 'openai']
|
|
121
|
+
};
|
|
122
|
+
return keywordMap[toolName] || [toolName];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async executeTool(toolName, prompt) {
|
|
126
|
+
const tool = this.tools[toolName];
|
|
127
|
+
if (!tool) {
|
|
128
|
+
return { success: false, error: `Unknown tool: ${toolName}` };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (!this.checkCLI(toolName)) {
|
|
132
|
+
return { success: false, error: `${tool.name} is not available` };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
const result = spawnSync(toolName, [prompt], {
|
|
137
|
+
encoding: 'utf8',
|
|
138
|
+
timeout: 30000
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
success: result.status === 0,
|
|
143
|
+
output: result.stdout,
|
|
144
|
+
error: result.stderr
|
|
145
|
+
};
|
|
146
|
+
} catch (error) {
|
|
147
|
+
return { success: false, error: error.message };
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
checkCLI(toolName) {
|
|
152
|
+
try {
|
|
153
|
+
const tool = this.tools[toolName];
|
|
154
|
+
const command = tool.version.split(' ')[0];
|
|
155
|
+
const args = tool.version.split(' ').slice(1);
|
|
156
|
+
|
|
157
|
+
const result = spawnSync(command, args, {
|
|
158
|
+
stdio: 'ignore',
|
|
159
|
+
timeout: 10000,
|
|
160
|
+
env: { ...process.env }
|
|
161
|
+
});
|
|
162
|
+
return result.status === 0;
|
|
163
|
+
} catch (error) {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async analyzeCLIHelp(toolName) {
|
|
169
|
+
const tool = this.tools[toolName];
|
|
170
|
+
if (!tool) return null;
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
const result = spawnSync(toolName, ['--help'], {
|
|
174
|
+
encoding: 'utf8',
|
|
175
|
+
timeout: 15000
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
if (result.status === 0) {
|
|
179
|
+
return this.parseHelpOutput(result.stdout);
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
} catch (error) {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
parseHelpOutput(helpText) {
|
|
188
|
+
const capabilities = {
|
|
189
|
+
commands: [],
|
|
190
|
+
options: [],
|
|
191
|
+
examples: [],
|
|
192
|
+
features: []
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// Extract commands
|
|
196
|
+
const commandMatches = helpText.match(/^\s{0,4}([a-z][a-z0-9_-]+)\s+.+$/gm);
|
|
197
|
+
if (commandMatches) {
|
|
198
|
+
capabilities.commands = commandMatches.map(match => match.trim());
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Extract options
|
|
202
|
+
const optionMatches = helpText.match(/--[a-z-]+/g);
|
|
203
|
+
if (optionMatches) {
|
|
204
|
+
capabilities.options = [...new Set(optionMatches)];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Extract examples
|
|
208
|
+
const exampleMatches = helpText.match(/(example|Usage|用法)[::]\s*\n([\s\S]*?)(?=\n\n|\n[A-Z]|\n$)/gi);
|
|
209
|
+
if (exampleMatches) {
|
|
210
|
+
capabilities.examples = exampleMatches.map(match => match.replace(/^(example|Usage|用法)[::]\s*/i, '').trim());
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return capabilities;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
class StigmergyInstaller {
|
|
218
|
+
constructor() {
|
|
219
|
+
this.homeDir = os.homedir();
|
|
220
|
+
this.stigmergyDir = path.join(this.homeDir, '.stigmergy');
|
|
221
|
+
this.projectDir = process.cwd();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async ensureDirectory(dirPath) {
|
|
225
|
+
try {
|
|
226
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
227
|
+
return true;
|
|
228
|
+
} catch (error) {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
checkCLI(toolName) {
|
|
234
|
+
try {
|
|
235
|
+
const tool = CLI_TOOLS[toolName];
|
|
236
|
+
const command = tool.version.split(' ')[0];
|
|
237
|
+
const args = tool.version.split(' ').slice(1);
|
|
238
|
+
|
|
239
|
+
const result = spawnSync(command, args, {
|
|
240
|
+
stdio: 'ignore',
|
|
241
|
+
timeout: 10000,
|
|
242
|
+
env: { ...process.env }
|
|
243
|
+
});
|
|
244
|
+
return result.status === 0;
|
|
245
|
+
} catch (error) {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async scanAvailableTools() {
|
|
251
|
+
const results = {
|
|
252
|
+
total: Object.keys(CLI_TOOLS).length,
|
|
253
|
+
available: [],
|
|
254
|
+
unavailable: [],
|
|
255
|
+
details: []
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
console.log('[SCAN] Scanning for AI CLI tools on your system...');
|
|
259
|
+
console.log('='.repeat(60));
|
|
260
|
+
|
|
261
|
+
for (const [key, tool] of Object.entries(CLI_TOOLS)) {
|
|
262
|
+
const isAvailable = this.checkCLI(key);
|
|
263
|
+
|
|
264
|
+
if (isAvailable) {
|
|
265
|
+
results.available.push(key);
|
|
266
|
+
console.log(`[OK] ${tool.name}: Available (${tool.version})`);
|
|
267
|
+
results.details.push({
|
|
268
|
+
key,
|
|
269
|
+
name: tool.name,
|
|
270
|
+
status: 'Available',
|
|
271
|
+
install: tool.install,
|
|
272
|
+
hooksDir: tool.hooksDir
|
|
273
|
+
});
|
|
274
|
+
} else {
|
|
275
|
+
results.unavailable.push(key);
|
|
276
|
+
console.log(`[X] ${tool.name}: Not Available`);
|
|
277
|
+
results.details.push({
|
|
278
|
+
key,
|
|
279
|
+
name: tool.name,
|
|
280
|
+
status: 'Not Available',
|
|
281
|
+
install: tool.install,
|
|
282
|
+
hooksDir: tool.hooksDir
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
console.log('='.repeat(60));
|
|
288
|
+
console.log(`[SUMMARY] ${results.available.length}/${results.total} tools available`);
|
|
289
|
+
console.log('');
|
|
290
|
+
|
|
291
|
+
return results;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
async promptForInstallation(scanResults) {
|
|
295
|
+
if (scanResults.unavailable.length === 0) {
|
|
296
|
+
console.log('[SUCCESS] All AI CLI tools are already installed!');
|
|
297
|
+
return [];
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
console.log('[INSTALL] The following AI CLI tools can be automatically installed:');
|
|
301
|
+
console.log('');
|
|
302
|
+
|
|
303
|
+
scanResults.unavailable.forEach((toolKey, index) => {
|
|
304
|
+
const tool = CLI_TOOLS[toolKey];
|
|
305
|
+
console.log(` ${index + 1}. ${tool.name}`);
|
|
306
|
+
console.log(` Install: ${tool.install}`);
|
|
307
|
+
console.log('');
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
console.log('[OPTIONS] Installation Options:');
|
|
311
|
+
console.log(' - Enter numbers separated by spaces (e.g: 1 3 5)');
|
|
312
|
+
console.log(' - Enter "all" to install all missing tools');
|
|
313
|
+
console.log(' - Enter "skip" to skip CLI installation');
|
|
314
|
+
|
|
315
|
+
return new Promise((resolve) => {
|
|
316
|
+
process.stdout.write('\n[SELECT] Select tools to install: ');
|
|
317
|
+
|
|
318
|
+
process.stdin.once('data', (data) => {
|
|
319
|
+
const input = data.toString().trim();
|
|
320
|
+
|
|
321
|
+
if (input === '' || input.toLowerCase() === 'skip') {
|
|
322
|
+
resolve([]);
|
|
323
|
+
} else if (input.toLowerCase() === 'all') {
|
|
324
|
+
resolve(scanResults.unavailable);
|
|
325
|
+
} else {
|
|
326
|
+
const numbers = input.split(/\s+/).map(n => parseInt(n) - 1);
|
|
327
|
+
const selected = numbers
|
|
328
|
+
.filter(n => n >= 0 && n < scanResults.unavailable.length)
|
|
329
|
+
.map(n => scanResults.unavailable[n]);
|
|
330
|
+
resolve(selected);
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
async installTools(toolKeys) {
|
|
337
|
+
if (toolKeys.length === 0) {
|
|
338
|
+
console.log('[SKIP] Skipping CLI tool installation.');
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
console.log(`\n[INSTALL] Installing ${toolKeys.length} AI CLI tools...`);
|
|
343
|
+
console.log('='.repeat(60));
|
|
344
|
+
|
|
345
|
+
for (const toolKey of toolKeys) {
|
|
346
|
+
const tool = CLI_TOOLS[toolKey];
|
|
347
|
+
console.log(`\n[INSTALLING] Installing ${tool.name}...`);
|
|
348
|
+
|
|
349
|
+
try {
|
|
350
|
+
const packageInstall = spawn('npm', ['install', '-g'].concat(tool.install.split(' ').slice(3)), {
|
|
351
|
+
stdio: 'inherit',
|
|
352
|
+
shell: true
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
await new Promise((resolve, reject) => {
|
|
356
|
+
packageInstall.on('close', (code) => {
|
|
357
|
+
if (code === 0) {
|
|
358
|
+
console.log(`[OK] ${tool.name} installed successfully!`);
|
|
359
|
+
resolve();
|
|
360
|
+
} else {
|
|
361
|
+
console.log(`[ERROR] Failed to install ${tool.name}`);
|
|
362
|
+
reject(new Error(`Installation failed with code ${code}`));
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
} catch (error) {
|
|
367
|
+
console.log(`[ERROR] Error installing ${tool.name}:`, error.message);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
console.log('\n[VERIFY] CLI Installation completed! Verifying...');
|
|
372
|
+
await this.verifyInstallation(toolKeys);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
async verifyInstallation(toolKeys) {
|
|
376
|
+
console.log('='.repeat(60));
|
|
377
|
+
let successCount = 0;
|
|
378
|
+
|
|
379
|
+
for (const toolKey of toolKeys) {
|
|
380
|
+
const tool = CLI_TOOLS[toolKey];
|
|
381
|
+
if (this.checkCLI(toolKey)) {
|
|
382
|
+
console.log(`[OK] ${tool.name}: Successfully installed and functional!`);
|
|
383
|
+
successCount++;
|
|
384
|
+
} else {
|
|
385
|
+
console.log(`[FAIL] ${tool.name}: Installation verification failed`);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
console.log(`\n[RESULT] Installation Result: ${successCount}/${toolKeys.length} tools successfully installed`);
|
|
390
|
+
|
|
391
|
+
if (successCount === toolKeys.length) {
|
|
392
|
+
console.log('[SUCCESS] All selected CLI tools are now ready to use!');
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
async deployHooks(availableTools) {
|
|
397
|
+
if (availableTools.length === 0) {
|
|
398
|
+
console.log('[SKIP] No CLI tools available for hook deployment.');
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
console.log(`\n[DEPLOY] Deploying Stigmergy hooks to ${availableTools.length} CLI tools...`);
|
|
403
|
+
console.log('='.repeat(60));
|
|
404
|
+
|
|
405
|
+
const hookTemplate = `#!/usr/bin/env node
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Stigmergy Hook - ${new Date().toISOString()}
|
|
409
|
+
* Generated by Stigmergy CLI
|
|
410
|
+
*/
|
|
411
|
+
|
|
412
|
+
const { spawn } = require('child_process');
|
|
413
|
+
|
|
414
|
+
// Stigmergy Hook Implementation
|
|
415
|
+
class StigmergyHook {
|
|
416
|
+
constructor() {
|
|
417
|
+
this.processArgs();
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
processArgs() {
|
|
421
|
+
const args = process.argv.slice(2);
|
|
422
|
+
if (args.length === 0) {
|
|
423
|
+
this.showHelp();
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const command = args[0];
|
|
428
|
+
switch (command) {
|
|
429
|
+
case 'collaborate':
|
|
430
|
+
this.handleCollaborate(args.slice(1));
|
|
431
|
+
break;
|
|
432
|
+
case 'status':
|
|
433
|
+
this.showStatus();
|
|
434
|
+
break;
|
|
435
|
+
case 'init':
|
|
436
|
+
this.initProject(args.slice(1));
|
|
437
|
+
break;
|
|
438
|
+
default:
|
|
439
|
+
this.showHelp();
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
handleCollaborate(args) {
|
|
444
|
+
console.log('[COLLAB] Stigmergy Collaboration System');
|
|
445
|
+
console.log('Available AI CLI tools:', Object.keys(CLI_TOOLS).join(', '));
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
showStatus() {
|
|
449
|
+
console.log('[STATUS] Stigmergy Hook Status: Active');
|
|
450
|
+
console.log('Configuration: Loaded successfully');
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
initProject(projectName) {
|
|
454
|
+
console.log('[INIT] Initializing Stigmergy project:', projectName || 'current');
|
|
455
|
+
console.log('[OK] Project configuration created');
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
showHelp() {
|
|
459
|
+
console.log('Stigmergy Hook - Multi-AI CLI Collaboration');
|
|
460
|
+
console.log('');
|
|
461
|
+
console.log('Commands:');
|
|
462
|
+
console.log(' collaborate [options] Start collaboration with other AI CLI tools');
|
|
463
|
+
console.log(' status Show hook status');
|
|
464
|
+
console.log(' init [project] Initialize Stigmergy project');
|
|
465
|
+
console.log(' help Show this help message');
|
|
466
|
+
console.log('');
|
|
467
|
+
console.log('Examples:');
|
|
468
|
+
console.log(' stigmergy-hook collaborate claude gemini');
|
|
469
|
+
console.log(' stigmergy-hook status');
|
|
470
|
+
console.log(' stigmergy-hook init my-project');
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Initialize hook
|
|
475
|
+
const hook = new StigmergyHook();
|
|
476
|
+
`;
|
|
477
|
+
|
|
478
|
+
let deployedCount = 0;
|
|
479
|
+
|
|
480
|
+
for (const toolKey of availableTools) {
|
|
481
|
+
const tool = CLI_TOOLS[toolKey];
|
|
482
|
+
const hooksDir = tool.hooksDir;
|
|
483
|
+
|
|
484
|
+
await this.ensureDirectory(hooksDir);
|
|
485
|
+
|
|
486
|
+
const hookFile = path.join(hooksDir, 'stigmergy-hook.cjs');
|
|
487
|
+
try {
|
|
488
|
+
await fs.writeFile(hookFile, hookTemplate, 'utf8');
|
|
489
|
+
|
|
490
|
+
if (process.platform !== 'win32') {
|
|
491
|
+
const { spawn } = require('child_process');
|
|
492
|
+
spawn('chmod', ['+x', hookFile], { stdio: 'ignore' });
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
console.log(`[OK] ${tool.name}: Hook deployed to ${hooksDir}`);
|
|
496
|
+
deployedCount++;
|
|
497
|
+
} catch (error) {
|
|
498
|
+
console.log(`[FAIL] ${tool.name}: Failed to deploy hook - ${error.message}`);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
console.log('\n[RESULT] Hook Deployment Result: ' + deployedCount + '/' + availableTools.length + ' hooks deployed');
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
async removeHooks() {
|
|
506
|
+
console.log('\n[REMOVE] Removing Stigmergy hooks from CLI tools...');
|
|
507
|
+
console.log('='.repeat(60));
|
|
508
|
+
|
|
509
|
+
let removedCount = 0;
|
|
510
|
+
let errorCount = 0;
|
|
511
|
+
|
|
512
|
+
// Iterate through all CLI tools and remove hooks
|
|
513
|
+
for (const [toolKey, toolInfo] of Object.entries(CLI_TOOLS)) {
|
|
514
|
+
console.log(`\n[REMOVE] Removing hooks for ${toolInfo.name}...`);
|
|
515
|
+
|
|
516
|
+
try {
|
|
517
|
+
// Remove the stigmergy hook file
|
|
518
|
+
const hookFile = path.join(toolInfo.hooksDir, 'stigmergy-hook.cjs');
|
|
519
|
+
|
|
520
|
+
// Check if the hook file exists
|
|
521
|
+
try {
|
|
522
|
+
await fs.access(hookFile);
|
|
523
|
+
// Remove the hook file
|
|
524
|
+
await fs.unlink(hookFile);
|
|
525
|
+
console.log(`[OK] Removed hook file from ${toolInfo.name}`);
|
|
526
|
+
removedCount++;
|
|
527
|
+
} catch (accessError) {
|
|
528
|
+
// Hook file doesn't exist, that's fine
|
|
529
|
+
console.log(`[INFO] No hook file found for ${toolInfo.name}`);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Try to remove the hooks directory if it's empty
|
|
533
|
+
try {
|
|
534
|
+
const files = await fs.readdir(toolInfo.hooksDir);
|
|
535
|
+
if (files.length === 0) {
|
|
536
|
+
await fs.rmdir(toolInfo.hooksDir);
|
|
537
|
+
console.log(`[OK] Removed empty hooks directory for ${toolInfo.name}`);
|
|
538
|
+
} else {
|
|
539
|
+
console.log(`[INFO] Hooks directory for ${toolInfo.name} not empty, keeping it`);
|
|
540
|
+
}
|
|
541
|
+
} catch (dirError) {
|
|
542
|
+
// Directory might not exist or have other files, that's fine
|
|
543
|
+
console.log(`[INFO] Hooks directory for ${toolInfo.name} not removed: ${dirError.message}`);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
} catch (error) {
|
|
547
|
+
console.log(`[ERROR] Failed to remove hooks for ${toolInfo.name}: ${error.message}`);
|
|
548
|
+
errorCount++;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
console.log('\n[RESULT] Hook Removal Result: ' + removedCount + ' hooks removed, ' + errorCount + ' errors');
|
|
553
|
+
console.log('[SUCCESS] Stigmergy hooks removal completed!');
|
|
554
|
+
|
|
555
|
+
// Also remove global configuration
|
|
556
|
+
try {
|
|
557
|
+
const configPath = path.join(this.stigmergyDir, 'config.json');
|
|
558
|
+
await fs.unlink(configPath);
|
|
559
|
+
console.log('[OK] Removed global configuration file');
|
|
560
|
+
} catch (error) {
|
|
561
|
+
console.log('[INFO] Global configuration file not found or already removed');
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
console.log('\n[NEXT] To completely uninstall Stigmergy:');
|
|
565
|
+
console.log(' 1. Run: npm uninstall -g stigmergy');
|
|
566
|
+
console.log(' 2. Manually check and clean up any remaining files if needed');
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
async setupGlobalConfiguration(availableTools = []) {
|
|
570
|
+
await this.ensureDirectory(this.stigmergyDir);
|
|
571
|
+
|
|
572
|
+
const globalConfig = {
|
|
573
|
+
version: '1.0.77',
|
|
574
|
+
installed: new Date().toISOString(),
|
|
575
|
+
projectPath: this.projectDir,
|
|
576
|
+
availableTools: availableTools,
|
|
577
|
+
deployedHooks: availableTools,
|
|
578
|
+
collaboration: {
|
|
579
|
+
enabled: true,
|
|
580
|
+
protocols: [
|
|
581
|
+
'Use {cli} to {task}',
|
|
582
|
+
'Call {cli} to {task}',
|
|
583
|
+
'Ask {cli} for {task}',
|
|
584
|
+
'Get {cli} to {task}',
|
|
585
|
+
'Have {cli} {task}'
|
|
586
|
+
],
|
|
587
|
+
examples: [
|
|
588
|
+
'Use claude to help debug this code',
|
|
589
|
+
'Call gemini to analyze the file',
|
|
590
|
+
'Ask qwen to translate this text'
|
|
591
|
+
]
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
const configPath = path.join(this.stigmergyDir, 'config.json');
|
|
596
|
+
try {
|
|
597
|
+
await fs.writeFile(configPath, JSON.stringify(globalConfig, null, 2));
|
|
598
|
+
console.log('[CONFIG] Global configuration saved to:', configPath);
|
|
599
|
+
} catch (error) {
|
|
600
|
+
console.log('[WARN] Warning: Could not save global configuration');
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
const projectDocs = path.join(this.projectDir, 'STIGMERGY.md');
|
|
604
|
+
const docsTemplate = `# Stigmergy Multi-AI CLI Collaboration
|
|
605
|
+
|
|
606
|
+
This project is configured for Stigmergy-based multi-AI CLI collaboration.
|
|
607
|
+
|
|
608
|
+
## Available AI CLI Tools
|
|
609
|
+
|
|
610
|
+
${availableTools.map(tool => `- **${CLI_TOOLS[tool].name}**: \`stigmergy call ${tool}\``).join('\n')}
|
|
611
|
+
|
|
612
|
+
## Usage Examples
|
|
613
|
+
|
|
614
|
+
### Cross-CLI Collaboration
|
|
615
|
+
\`\`\`bash
|
|
616
|
+
# Use Claude to analyze code
|
|
617
|
+
stigmergy call claude "analyze this function"
|
|
618
|
+
|
|
619
|
+
# Use Gemini for documentation
|
|
620
|
+
stigmergy call gemini "generate docs for this file"
|
|
621
|
+
|
|
622
|
+
# Use Qwen for translation
|
|
623
|
+
stigmergy call qwen "translate to English"
|
|
624
|
+
\`\`\`
|
|
625
|
+
|
|
626
|
+
### Project Initialization
|
|
627
|
+
\`\`\`bash
|
|
628
|
+
# Initialize with Claude as primary AI
|
|
629
|
+
stigmergy init --primary claude
|
|
630
|
+
|
|
631
|
+
# Initialize with multiple AI tools
|
|
632
|
+
stigmergy init --all-tools
|
|
633
|
+
\`\`\`
|
|
634
|
+
|
|
635
|
+
## Configuration
|
|
636
|
+
|
|
637
|
+
Global configuration: \`~/.stigmergy/config.json\`
|
|
638
|
+
|
|
639
|
+
## Getting Started
|
|
640
|
+
|
|
641
|
+
1. Run \`stigmergy status\` to verify setup
|
|
642
|
+
2. Use \`stigmergy call <ai-tool> "<prompt>"\` to collaborate with AI CLI tools
|
|
643
|
+
3. Check project-specific configurations in individual CLI tool directories
|
|
644
|
+
|
|
645
|
+
For more information: https://github.com/ptreezh/stigmergy-CLI-Multi-Agents
|
|
646
|
+
`;
|
|
647
|
+
|
|
648
|
+
try {
|
|
649
|
+
await fs.writeFile(projectDocs, docsTemplate, 'utf8');
|
|
650
|
+
console.log('[DOCS] Project documentation created: STIGMERGY.md');
|
|
651
|
+
} catch (error) {
|
|
652
|
+
console.log('[WARN] Warning: Could not create project documentation');
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
async showUsageInstructions() {
|
|
657
|
+
console.log('\n' + '='.repeat(60));
|
|
658
|
+
console.log('[SUCCESS] Stigmergy Installation and Deployment Complete!');
|
|
659
|
+
console.log('='.repeat(60));
|
|
660
|
+
console.log('');
|
|
661
|
+
console.log('[NEXT] Next Steps:');
|
|
662
|
+
console.log('');
|
|
663
|
+
console.log('1. Verify Installation:');
|
|
664
|
+
console.log(' stigmergy status');
|
|
665
|
+
console.log('');
|
|
666
|
+
console.log('2. Check Available Tools:');
|
|
667
|
+
console.log(' stigmergy scan');
|
|
668
|
+
console.log('');
|
|
669
|
+
console.log('3. Start Using AI CLI Collaboration:');
|
|
670
|
+
console.log(' stigmergy call claude "help me debug this code"');
|
|
671
|
+
console.log(' stigmergy call gemini "generate documentation"');
|
|
672
|
+
console.log(' stigmergy call qwen "translate to English"');
|
|
673
|
+
console.log('');
|
|
674
|
+
console.log('4. Initialize New Projects:');
|
|
675
|
+
console.log(' stigmergy init --primary claude');
|
|
676
|
+
console.log('');
|
|
677
|
+
console.log('[INFO] Documentation:');
|
|
678
|
+
console.log(' - Global Config: ~/.stigmergy/config.json');
|
|
679
|
+
console.log(' - Project Docs: ./STIGMERGY.md');
|
|
680
|
+
console.log(' - GitHub: https://github.com/ptreezh/stigmergy-CLI-Multi-Agents');
|
|
681
|
+
console.log('');
|
|
682
|
+
console.log('[END] Happy collaborating with multiple AI CLI tools!');
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// Main CLI functionality
|
|
687
|
+
async function main() {
|
|
688
|
+
const args = process.argv.slice(2);
|
|
689
|
+
const installer = new StigmergyInstaller();
|
|
690
|
+
|
|
691
|
+
if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
|
|
692
|
+
console.log('Stigmergy CLI - Multi-Agents Cross-AI CLI Tools Collaboration System');
|
|
693
|
+
console.log('Version: 1.0.78');
|
|
694
|
+
console.log('');
|
|
695
|
+
console.log('[SYSTEM] Automated Installation and Deployment System');
|
|
696
|
+
console.log('');
|
|
697
|
+
console.log('Usage: stigmergy [command] [options]');
|
|
698
|
+
console.log('');
|
|
699
|
+
console.log('Commands:');
|
|
700
|
+
console.log(' help, --help Show this help message');
|
|
701
|
+
console.log(' version, --version Show version information');
|
|
702
|
+
console.log(' status Check CLI tools status');
|
|
703
|
+
console.log(' scan Scan for available AI CLI tools');
|
|
704
|
+
console.log(' install Auto-install missing CLI tools');
|
|
705
|
+
console.log(' deploy Deploy hooks to installed tools');
|
|
706
|
+
console.log(' remove Remove all Stigmergy hooks from installed tools');
|
|
707
|
+
console.log(' setup Complete setup and configuration');
|
|
708
|
+
console.log(' call <tool> Execute prompt with specified or auto-routed AI CLI');
|
|
709
|
+
console.log('');
|
|
710
|
+
console.log('[WORKFLOW] Automated Workflow:');
|
|
711
|
+
console.log(' 1. npm install -g stigmergy # Install Stigmergy');
|
|
712
|
+
console.log(' 2. stigmergy install # Auto-scan & install CLI tools');
|
|
713
|
+
console.log(' 3. stigmergy setup # Deploy hooks & config');
|
|
714
|
+
console.log(' 4. stigmergy call <ai> <prompt> # Start collaborating');
|
|
715
|
+
console.log('');
|
|
716
|
+
console.log('For more information, visit: https://github.com/ptreezh/stigmergy-CLI-Multi-Agents');
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
if (args.includes('--version') || args.includes('version')) {
|
|
721
|
+
console.log('1.0.77');
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// Auto-install mode for postinstall script
|
|
726
|
+
if (args.includes('auto-install')) {
|
|
727
|
+
console.log('[AUTO-INSTALL] Stigmergy CLI - Automated Installation and Deployment');
|
|
728
|
+
console.log('Multi-AI CLI Tools Collaboration System v1.0.77');
|
|
729
|
+
console.log('='.repeat(60));
|
|
730
|
+
|
|
731
|
+
const originalPrompt = installer.promptForInstallation;
|
|
732
|
+
installer.promptForInstallation = async () => {
|
|
733
|
+
console.log('[AUTO-INSTALL] Skipping interactive CLI installation in postinstall mode');
|
|
734
|
+
console.log('[AUTO-INSTALL] You can run "stigmergy" manually to install CLI tools interactively');
|
|
735
|
+
return [];
|
|
736
|
+
};
|
|
737
|
+
|
|
738
|
+
console.log('\n[STEP 1] Scanning for AI CLI tools...');
|
|
739
|
+
const scanResults = await installer.scanAvailableTools();
|
|
740
|
+
|
|
741
|
+
console.log('\n[STEP 2] Deploying Stigmergy hooks...');
|
|
742
|
+
await installer.deployHooks(scanResults.available);
|
|
743
|
+
|
|
744
|
+
console.log('\n[STEP 3] Setting up configuration...');
|
|
745
|
+
await installer.setupGlobalConfiguration(scanResults.available);
|
|
746
|
+
|
|
747
|
+
console.log('\n[AUTO-INSTALL] Stigmergy automated setup completed!');
|
|
748
|
+
console.log('[AUTO-INSTALL] Run "stigmergy" to start interactive CLI tool installation');
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// Define valid commands
|
|
753
|
+
const validCommands = ['help', '--help', '-h', 'version', '--version', 'status', 'scan', 'install', 'deploy', 'remove', 'setup', 'auto-install', 'call'];
|
|
754
|
+
|
|
755
|
+
// Handle remove command
|
|
756
|
+
if (args[0] === 'remove') {
|
|
757
|
+
console.log('[REMOVE] Removing Stigmergy hooks from all installed CLI tools...');
|
|
758
|
+
await installer.removeHooks();
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// Handle call command
|
|
763
|
+
if (args[0] === 'call') {
|
|
764
|
+
return await handleCallCommand(args.slice(1));
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// Check for invalid commands
|
|
768
|
+
const hasValidCommand = args.some(arg => validCommands.includes(arg));
|
|
769
|
+
if (!hasValidCommand && args.length > 0) {
|
|
770
|
+
console.log('[ERROR] Invalid command:', args[0]);
|
|
771
|
+
console.log('');
|
|
772
|
+
console.log('Usage: stigmergy [command] [options]');
|
|
773
|
+
console.log('');
|
|
774
|
+
console.log('Available commands:');
|
|
775
|
+
console.log(' help, --help Show this help message');
|
|
776
|
+
console.log(' version, --version Show version information');
|
|
777
|
+
console.log(' status Check CLI tools status');
|
|
778
|
+
console.log(' scan Scan for available AI CLI tools');
|
|
779
|
+
console.log(' install Auto-install missing CLI tools');
|
|
780
|
+
console.log(' deploy Deploy hooks to installed tools');
|
|
781
|
+
console.log(' setup Complete setup and configuration');
|
|
782
|
+
console.log(' call <tool> Execute prompt with specified or auto-routed AI CLI');
|
|
783
|
+
console.log(' call <tool> Execute prompt with specified or auto-routed AI CLI');
|
|
784
|
+
console.log('');
|
|
785
|
+
console.log('Run "stigmergy --help" for more information.');
|
|
786
|
+
process.exit(1);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
// Start automated installation and deployment
|
|
790
|
+
console.log('[START] Stigmergy CLI - Automated Installation and Deployment');
|
|
791
|
+
console.log('Multi-AI CLI Tools Collaboration System v1.0.77');
|
|
792
|
+
console.log('='.repeat(60));
|
|
793
|
+
|
|
794
|
+
console.log('\n[STEP 1] Scanning for AI CLI tools...');
|
|
795
|
+
const scanResults = await installer.scanAvailableTools();
|
|
796
|
+
|
|
797
|
+
if (scanResults.unavailable.length > 0) {
|
|
798
|
+
console.log('\n[STEP 2] CLI Tool Installation');
|
|
799
|
+
const selectedTools = await installer.promptForInstallation(scanResults);
|
|
800
|
+
await installer.installTools(selectedTools);
|
|
801
|
+
|
|
802
|
+
if (selectedTools.length > 0) {
|
|
803
|
+
console.log('\n[RESCAN] Re-scanning after installation...');
|
|
804
|
+
scanResults.available = scanResults.available.concat(selectedTools.filter(tool => installer.checkCLI(tool)));
|
|
805
|
+
scanResults.unavailable = scanResults.unavailable.filter(tool => !selectedTools.includes(tool));
|
|
806
|
+
}
|
|
807
|
+
} else {
|
|
808
|
+
console.log('\n[STEP 2] All CLI tools already available!');
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
console.log('\n[STEP 3] Deploying Stigmergy hooks...');
|
|
812
|
+
await installer.deployHooks(scanResults.available);
|
|
813
|
+
|
|
814
|
+
console.log('\n[STEP 4] Setting up configuration...');
|
|
815
|
+
await installer.setupGlobalConfiguration(scanResults.available);
|
|
816
|
+
|
|
817
|
+
await installer.showUsageInstructions();
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// Memory Management System
|
|
821
|
+
class MemoryManager {
|
|
822
|
+
constructor(stigmergyDir) {
|
|
823
|
+
this.memoryDir = path.join(stigmergyDir, 'memory');
|
|
824
|
+
this.globalMemoryFile = path.join(this.memoryDir, 'global.json');
|
|
825
|
+
this.projectMemoryFile = path.join(process.cwd(), '.stigmergy-memory.json');
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
async ensureDirectory() {
|
|
829
|
+
await fs.mkdir(this.memoryDir, { recursive: true });
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
async saveGlobalMemory(data) {
|
|
833
|
+
await this.ensureDirectory();
|
|
834
|
+
await fs.writeFile(this.globalMemoryFile, JSON.stringify({
|
|
835
|
+
lastUpdated: new Date().toISOString(),
|
|
836
|
+
...data
|
|
837
|
+
}, null, 2));
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
async loadGlobalMemory() {
|
|
841
|
+
try {
|
|
842
|
+
await this.ensureDirectory();
|
|
843
|
+
const data = await fs.readFile(this.globalMemoryFile, 'utf8');
|
|
844
|
+
return JSON.parse(data);
|
|
845
|
+
} catch (error) {
|
|
846
|
+
return {};
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
async saveProjectMemory(data) {
|
|
851
|
+
const memory = await this.loadProjectMemory();
|
|
852
|
+
await fs.writeFile(this.projectMemoryFile, JSON.stringify({
|
|
853
|
+
...memory,
|
|
854
|
+
lastUpdated: new Date().toISOString(),
|
|
855
|
+
...data
|
|
856
|
+
}, null, 2));
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
async loadProjectMemory() {
|
|
860
|
+
try {
|
|
861
|
+
const data = await fs.readFile(this.projectMemoryFile, 'utf8');
|
|
862
|
+
return JSON.parse(data);
|
|
863
|
+
} catch (error) {
|
|
864
|
+
return {
|
|
865
|
+
projectName: path.basename(process.cwd()),
|
|
866
|
+
createdAt: new Date().toISOString(),
|
|
867
|
+
interactions: []
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
async addInteraction(tool, prompt, response) {
|
|
873
|
+
const interaction = {
|
|
874
|
+
timestamp: new Date().toISOString(),
|
|
875
|
+
tool,
|
|
876
|
+
prompt,
|
|
877
|
+
response,
|
|
878
|
+
duration: Date.now() - new Date().getTime()
|
|
879
|
+
};
|
|
880
|
+
|
|
881
|
+
await this.saveProjectMemory({
|
|
882
|
+
lastInteraction: interaction,
|
|
883
|
+
interactions: (await this.loadProjectMemory()).interactions.concat(interaction).slice(-100) // Keep last 100
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// Call Command Handler
|
|
889
|
+
async function handleCallCommand(args) {
|
|
890
|
+
const router = new SmartRouter();
|
|
891
|
+
const memoryManager = new MemoryManager(path.join(os.homedir(), '.stigmergy'));
|
|
892
|
+
|
|
893
|
+
if (args.length === 0) {
|
|
894
|
+
console.log('[ERROR] Call command requires a tool name and/or prompt');
|
|
895
|
+
console.log('');
|
|
896
|
+
console.log('Usage: stigmergy call <tool> <prompt>');
|
|
897
|
+
console.log(' stigmergy call <prompt> (auto-route to best tool)');
|
|
898
|
+
console.log('');
|
|
899
|
+
console.log('Available tools:', Object.keys(CLI_TOOLS).join(', '));
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
let toolName, prompt;
|
|
904
|
+
|
|
905
|
+
// Check if first argument is a valid tool
|
|
906
|
+
if (CLI_TOOLS[args[0]]) {
|
|
907
|
+
toolName = args[0];
|
|
908
|
+
prompt = args.slice(1).join(' ');
|
|
909
|
+
} else {
|
|
910
|
+
// Auto-route based on keywords
|
|
911
|
+
const routeResult = router.smartRoute(args.join(' '));
|
|
912
|
+
toolName = routeResult.tool;
|
|
913
|
+
prompt = routeResult.prompt || args.join(' ');
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
if (!prompt) {
|
|
917
|
+
console.log('[ERROR] Prompt is required');
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
console.log(`[CALL] Routing to: ${CLI_TOOLS[toolName].name}`);
|
|
922
|
+
console.log(`[PROMPT] ${prompt}`);
|
|
923
|
+
console.log('='.repeat(60));
|
|
924
|
+
|
|
925
|
+
const result = await router.executeTool(toolName, prompt);
|
|
926
|
+
|
|
927
|
+
if (result.success) {
|
|
928
|
+
console.log('[SUCCESS] Execution completed');
|
|
929
|
+
if (result.output) {
|
|
930
|
+
console.log('[OUTPUT]');
|
|
931
|
+
console.log(result.output);
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
// Save to memory
|
|
935
|
+
await memoryManager.addInteraction(toolName, prompt, result.output || 'Success');
|
|
936
|
+
console.log(`[MEMORY] Interaction saved to project memory`);
|
|
937
|
+
} else {
|
|
938
|
+
console.log('[ERROR] Execution failed');
|
|
939
|
+
if (result.error) {
|
|
940
|
+
console.log('[ERROR]', result.error);
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// Save failed interaction to memory
|
|
944
|
+
await memoryManager.addInteraction(toolName, prompt, result.error || 'Failed');
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
console.log('');
|
|
948
|
+
console.log('[INFO] For help with CLI usage: stigmergy help');
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
// Setup stdin for interactive prompts
|
|
952
|
+
if (require.main === module) {
|
|
953
|
+
if (process.stdin.isTTY) {
|
|
954
|
+
process.stdin.resume();
|
|
955
|
+
process.stdin.setEncoding('utf8');
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
main().catch(error => {
|
|
959
|
+
console.error('[ERROR] Error:', error.message);
|
|
960
|
+
process.exit(1);
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
|
|
893
964
|
module.exports = { main, StigmergyInstaller, CLI_TOOLS, SmartRouter, MemoryManager };
|