stigmergy 1.0.81 → 1.0.84

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.
@@ -1,682 +1,777 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Stigmergy CLI - Multi-Agents Cross-AI CLI Tools Collaboration System
5
- * International Version - Pure English & ANSI Only
6
- * Version: 1.0.78
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
- qodercli: {
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(/^(use|please|help|using|with)\s*/i, '')
98
- .trim();
99
- return { tool: toolName, prompt: cleanInput };
100
- }
101
- }
102
- }
103
-
104
- // Default routing
105
- const cleanInput = input.replace(/^(use|please|help|using|with)\s*/i, '').trim();
106
- return { tool: this.defaultTool, prompt: cleanInput };
107
- }
108
-
109
- extractKeywords(toolName) {
110
- const keywords = {
111
- claude: ['claude', 'anthropic'],
112
- gemini: ['gemini', 'google'],
113
- qwen: ['qwen', 'alibaba', 'tongyi'],
114
- iflow: ['iflow', 'workflow', 'intelligent'],
115
- qodercli: ['qoder', 'code'],
116
- codebuddy: ['codebuddy', 'buddy', 'assistant'],
117
- copilot: ['copilot', 'github', 'gh'],
118
- codex: ['codex', 'openai', 'gpt']
119
- };
120
-
121
- return keywords[toolName] || [toolName];
122
- }
123
- }
124
-
125
- class MemoryManager {
126
- constructor() {
127
- this.globalMemoryFile = path.join(os.homedir(), '.stigmergy', 'memory.json');
128
- this.projectMemoryFile = path.join(process.cwd(), 'STIGMERGY.md');
129
- }
130
-
131
- async addInteraction(tool, prompt, response) {
132
- const interaction = {
133
- timestamp: new Date().toISOString(),
134
- tool,
135
- prompt,
136
- response,
137
- duration: Date.now() - new Date().getTime()
138
- };
139
-
140
- // Add to global memory
141
- await this.saveGlobalMemory(interaction);
142
-
143
- // Add to project memory
144
- await this.saveProjectMemory(interaction);
145
- }
146
-
147
- async saveGlobalMemory(interaction) {
148
- try {
149
- const memory = await this.loadGlobalMemory();
150
- memory.interactions = memory.interactions.concat(interaction).slice(-100); // Keep last 100
151
- memory.lastInteraction = interaction;
152
-
153
- await fs.mkdir(path.dirname(this.globalMemoryFile), { recursive: true });
154
- await fs.writeFile(this.globalMemoryFile, JSON.stringify(memory, null, 2));
155
- } catch (error) {
156
- console.error(`[MEMORY] Failed to save global memory: ${error.message}`);
157
- }
158
- }
159
-
160
- async saveProjectMemory(interaction) {
161
- try {
162
- const memory = await this.loadProjectMemory();
163
- memory.interactions = memory.interactions.concat(interaction).slice(-50); // Keep last 50
164
- memory.lastInteraction = interaction;
165
-
166
- await fs.writeFile(this.projectMemoryFile, this.formatProjectMemory(memory));
167
- } catch (error) {
168
- console.error(`[MEMORY] Failed to save project memory: ${error.message}`);
169
- }
170
- }
171
-
172
- async loadGlobalMemory() {
173
- try {
174
- const data = await fs.readFile(this.globalMemoryFile, 'utf8');
175
- return JSON.parse(data);
176
- } catch {
177
- return {
178
- projectName: 'Global Stigmergy Memory',
179
- interactions: [],
180
- createdAt: new Date().toISOString()
181
- };
182
- }
183
- }
184
-
185
- async loadProjectMemory() {
186
- try {
187
- const data = await fs.readFile(this.projectMemoryFile, 'utf8');
188
- return this.parseProjectMemory(data);
189
- } catch {
190
- return {
191
- projectName: path.basename(process.cwd()),
192
- interactions: [],
193
- createdAt: new Date().toISOString()
194
- };
195
- }
196
- }
197
-
198
- formatProjectMemory(memory) {
199
- let content = `# Stigmergy Project Memory\n\n`;
200
- content += `**Project**: ${memory.projectName}\n`;
201
- content += `**Created**: ${memory.createdAt}\n`;
202
- content += `**Last Updated**: ${new Date().toISOString()}\n\n`;
203
-
204
- if (memory.lastInteraction) {
205
- content += `## Last Interaction\n\n`;
206
- content += `- **Tool**: ${memory.lastInteraction.tool}\n`;
207
- content += `- **Timestamp**: ${memory.lastInteraction.timestamp}\n`;
208
- content += `- **Prompt**: ${memory.lastInteraction.prompt}\n`;
209
- content += `- **Response**: ${memory.lastInteraction.response.substring(0, 200)}...\n\n`;
210
- }
211
-
212
- content += `## Recent Interactions (${memory.interactions.length})\n\n`;
213
- memory.interactions.slice(-10).forEach((interaction, index) => {
214
- content += `### ${index + 1}. ${interaction.tool} - ${interaction.timestamp}\n\n`;
215
- content += `**Prompt**: ${interaction.prompt}\n\n`;
216
- content += `**Response**: ${interaction.response.substring(0, 200)}...\n\n`;
217
- });
218
-
219
- return content;
220
- }
221
-
222
- parseProjectMemory(markdown) {
223
- // Simple parser for project memory
224
- return {
225
- projectName: 'Project',
226
- interactions: [],
227
- createdAt: new Date().toISOString()
228
- };
229
- }
230
- }
231
-
232
- class StigmergyInstaller {
233
- constructor() {
234
- this.router = new SmartRouter();
235
- this.memory = new MemoryManager();
236
- this.configDir = path.join(os.homedir(), '.stigmergy');
237
- }
238
-
239
- async checkCLI(toolName) {
240
- const tool = this.router.tools[toolName];
241
- if (!tool) return false;
242
-
243
- // Try multiple ways to check if CLI is available
244
- const checks = [
245
- // Method 1: Try version command
246
- { args: ['--version'], expected: 0 },
247
- // Method 2: Try help command
248
- { args: ['--help'], expected: 0 },
249
- // Method 3: Try help command with -h
250
- { args: ['-h'], expected: 0 },
251
- // Method 4: Try just the command (help case)
252
- { args: [], expected: 0 },
253
- // Method 5: Try version with alternative format
254
- { args: ['version'], expected: 0 }
255
- ];
256
-
257
- for (const check of checks) {
258
- try {
259
- const result = spawnSync(toolName, check.args, {
260
- encoding: 'utf8',
261
- timeout: 8000,
262
- stdio: 'pipe'
263
- });
264
-
265
- // Check if command exists and runs (exit 0 or shows help)
266
- if (result.status === 0 || result.stdout.includes(toolName) || result.stderr.includes(toolName)) {
267
- return true;
268
- }
269
-
270
- // Also check if command exists but returns non-zero (some CLIs do this)
271
- if (result.error === undefined && (result.stdout.length > 0 || result.stderr.length > 0)) {
272
- const output = (result.stdout + result.stderr).toLowerCase();
273
- if (output.includes(toolName) || output.includes('cli') || output.includes('ai') || output.includes('help')) {
274
- return true;
275
- }
276
- }
277
- } catch (error) {
278
- // Command not found, continue to next check
279
- continue;
280
- }
281
- }
282
-
283
- // Method 6: Check if command exists in PATH using `which`/`where` (platform specific)
284
- try {
285
- const whichCmd = process.platform === 'win32' ? 'where' : 'which';
286
- const result = spawnSync(whichCmd, [toolName], {
287
- encoding: 'utf8',
288
- timeout: 5000
289
- });
290
-
291
- if (result.status === 0 && result.stdout.trim().length > 0) {
292
- // Found in PATH
293
- return true;
294
- }
295
- } catch (error) {
296
- // Continue
297
- }
298
-
299
- // Method 7: Check common installation paths
300
- const commonPaths = this.getCommonCLIPaths(toolName);
301
- for (const cliPath of commonPaths) {
302
- try {
303
- if (await this.fileExists(cliPath)) {
304
- // Found at specific path
305
- return true;
306
- }
307
- } catch (error) {
308
- continue;
309
- }
310
- }
311
-
312
- return false;
313
- }
314
-
315
- getCommonCLIPaths(toolName) {
316
- const homeDir = os.homedir();
317
- const paths = [];
318
-
319
- // Add platform-specific paths
320
- if (process.platform === 'win32') {
321
- // Local and global npm paths
322
- paths.push(
323
- path.join(homeDir, 'AppData', 'Roaming', 'npm', `${toolName}.cmd`),
324
- path.join(homeDir, 'AppData', 'Roaming', 'npm', `${toolName}.ps1`),
325
- path.join(homeDir, 'AppData', 'Local', 'npm', `${toolName}.cmd`),
326
- path.join(homeDir, 'AppData', 'Local', 'npm', `${toolName}.ps1`),
327
- // Program Files
328
- path.join(process.env.ProgramFiles || 'C:\\Program Files', `${toolName}`, `${toolName}.exe`),
329
- path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', `${toolName}`, `${toolName}.exe`),
330
- // Node.js global packages
331
- path.join(homeDir, '.npm', `${toolName}.cmd`),
332
- path.join(homeDir, '.npm', `${toolName}.js`),
333
- // User directories
334
- path.join(homeDir, `${toolName}.cmd`),
335
- path.join(homeDir, `${toolName}.exe`),
336
- // Custom test directories
337
- path.join(homeDir, '.stigmergy-test', `${toolName}.cmd`),
338
- path.join(homeDir, '.stigmergy-test', `${toolName}.js`)
339
- );
340
- } else {
341
- paths.push(
342
- // Global npm paths
343
- path.join(homeDir, '.npm', 'global', 'bin', toolName),
344
- path.join(homeDir, '.npm', 'global', 'bin', `${toolName}.js`),
345
- // Local npm paths
346
- path.join(homeDir, '.npm', 'bin', toolName),
347
- path.join(homeDir, '.local', 'bin', toolName),
348
- path.join(homeDir, '.local', 'bin', `${toolName}.js`),
349
- // System paths
350
- path.join('/usr', 'local', 'bin', toolName),
351
- path.join('/usr', 'local', 'bin', `${toolName}.js`),
352
- path.join('/usr', 'bin', toolName),
353
- path.join('/usr', 'bin', `${toolName}.js`),
354
- // User home
355
- path.join(homeDir, '.local', 'bin', toolName),
356
- path.join(homeDir, '.local', 'bin', `${toolName}.js`),
357
- // Custom test directories
358
- path.join(homeDir, '.stigmergy-test', toolName),
359
- path.join(homeDir, '.stigmergy-test', `${toolName}.js`)
360
- );
361
- }
362
-
363
- // Add NPM global bin directory
364
- try {
365
- const { spawnSync } = require('child_process');
366
- const npmRoot = spawnSync('npm', ['root', '-g'], { encoding: 'utf8' }).stdout.trim();
367
- if (npmRoot) {
368
- paths.push(path.join(npmRoot, 'bin', toolName));
369
- paths.push(path.join(npmRoot, `${toolName}.js`));
370
- paths.push(path.join(npmRoot, `${toolName}.cmd`));
371
- }
372
- } catch (error) {
373
- // Continue
374
- }
375
-
376
- return paths;
377
- }
378
-
379
- async fileExists(filePath) {
380
- try {
381
- await fs.access(filePath);
382
- return true;
383
- } catch {
384
- return false;
385
- }
386
- }
387
-
388
- async scanCLI() {
389
- console.log('[SCAN] Scanning for AI CLI tools on your system...');
390
- console.log('='.repeat(60));
391
-
392
- const available = {};
393
- const missing = {};
394
-
395
- for (const [toolName, toolInfo] of Object.entries(this.router.tools)) {
396
- const isAvailable = await this.checkCLI(toolName);
397
-
398
- if (isAvailable) {
399
- available[toolName] = toolInfo;
400
- console.log(`[OK] ${toolInfo.name}: Available`);
401
- } else {
402
- missing[toolName] = toolInfo;
403
- console.log(`[X] ${toolInfo.name}: Not Available`);
404
- }
405
- }
406
-
407
- console.log('='.repeat(60));
408
- console.log(`[SUMMARY] ${Object.keys(available).length}/${Object.keys(this.router.tools).length} tools available`);
409
-
410
- return { available, missing };
411
- }
412
-
413
- async showInstallOptions(missing) {
414
- if (Object.keys(missing).length === 0) {
415
- console.log('[INFO] All AI CLI tools are already installed!');
416
- return [];
417
- }
418
-
419
- console.log('\n[INSTALL] The following AI CLI tools can be automatically installed:\n');
420
-
421
- const options = [];
422
- let index = 1;
423
-
424
- for (const [toolName, toolInfo] of Object.entries(missing)) {
425
- console.log(` ${index}. ${toolInfo.name}`);
426
- console.log(` Install: ${toolInfo.install}`);
427
- options.push({ index, toolName, toolInfo });
428
- index++;
429
- }
430
-
431
- console.log('\n[OPTIONS] Installation Options:');
432
- console.log('- Enter numbers separated by spaces (e.g: 1 3 5)');
433
- console.log('- Enter "all" to install all missing tools');
434
- console.log('- Enter "skip" to skip CLI installation');
435
-
436
- return options;
437
- }
438
-
439
- async installTools(selectedTools, missing) {
440
- if (!selectedTools || selectedTools.length === 0) {
441
- console.log('[INFO] Skipping CLI tool installation');
442
- return true;
443
- }
444
-
445
- console.log('\n[INSTALL] Installing selected AI CLI tools...');
446
-
447
- for (const selection of selectedTools) {
448
- const { toolName, toolInfo } = selection;
449
- console.log(`\n[INSTALLING] ${toolInfo.name}...`);
450
-
451
- try {
452
- const installCmd = toolInfo.install.split(' ');
453
- const result = spawnSync(installCmd[0], installCmd.slice(1), {
454
- encoding: 'utf8',
455
- timeout: 120000,
456
- stdio: 'inherit'
457
- });
458
-
459
- if (result.status === 0) {
460
- console.log(`[OK] ${toolInfo.name} installed successfully`);
461
- } else {
462
- console.log(`[ERROR] Failed to install ${toolInfo.name}`);
463
- console.log(`[INFO] Please run manually: ${toolInfo.install}`);
464
- }
465
- } catch (error) {
466
- console.log(`[ERROR] Failed to install ${toolInfo.name}: ${error.message}`);
467
- console.log(`[INFO] Please run manually: ${toolInfo.install}`);
468
- }
469
- }
470
-
471
- return true;
472
- }
473
-
474
- async deployHooks(available) {
475
- console.log('\n[DEPLOY] Deploying cross-CLI integration hooks...');
476
-
477
- for (const [toolName, toolInfo] of Object.entries(available)) {
478
- console.log(`\n[DEPLOY] Deploying hooks for ${toolInfo.name}...`);
479
-
480
- try {
481
- await fs.mkdir(toolInfo.hooksDir, { recursive: true });
482
- await fs.mkdir(toolInfo.config, { recursive: true });
483
-
484
- console.log(`[OK] Created directories for ${toolInfo.name}`);
485
- console.log(`[INFO] Hooks directory: ${toolInfo.hooksDir}`);
486
- console.log(`[INFO] Config directory: ${toolInfo.config}`);
487
- } catch (error) {
488
- console.log(`[ERROR] Failed to create directories for ${toolInfo.name}: ${error.message}`);
489
- }
490
- }
491
-
492
- console.log('\n[OK] Hook deployment completed');
493
- return true;
494
- }
495
-
496
- async initializeConfig() {
497
- console.log('\n[CONFIG] Initializing Stigmergy configuration...');
498
-
499
- try {
500
- await fs.mkdir(this.configDir, { recursive: true });
501
-
502
- const configFile = path.join(this.configDir, 'config.json');
503
- const config = {
504
- version: '1.0.78',
505
- initialized: true,
506
- createdAt: new Date().toISOString(),
507
- lastUpdated: new Date().toISOString(),
508
- defaultCLI: 'claude',
509
- enableCrossCLI: true,
510
- enableMemory: true
511
- };
512
-
513
- await fs.writeFile(configFile, JSON.stringify(config, null, 2));
514
- console.log('[OK] Configuration initialized');
515
- console.log(`[INFO] Config file: ${configFile}`);
516
-
517
- return true;
518
- } catch (error) {
519
- console.log(`[ERROR] Failed to initialize configuration: ${error.message}`);
520
- return false;
521
- }
522
- }
523
-
524
- showUsageInstructions() {
525
- console.log('\n[USAGE] Stigmergy CLI Usage Instructions:');
526
- console.log('='.repeat(60));
527
- console.log('');
528
- console.log('1. Check System Status:');
529
- console.log(' stigmergy status');
530
- console.log('');
531
- console.log('2. Check Available Tools:');
532
- console.log(' stigmergy scan');
533
- console.log('');
534
- console.log('3. Start Using AI CLI Collaboration:');
535
- console.log(' stigmergy call claude "help me debug this code"');
536
- console.log(' stigmergy call gemini "generate documentation"');
537
- console.log(' stigmergy call qwen "translate to English"');
538
- console.log('');
539
- console.log('4. Initialize New Projects:');
540
- console.log(' stigmergy init --primary claude');
541
- console.log('');
542
- console.log('[INFO] Documentation:');
543
- console.log(' - Global Config: ~/.stigmergy/config.json');
544
- console.log(' - Project Docs: ./STIGMERGY.md');
545
- console.log(' - GitHub: https://github.com/ptreezh/stigmergy-CLI-Multi-Agents');
546
- console.log('');
547
- console.log('[END] Happy collaborating with multiple AI CLI tools!');
548
- }
549
- }
550
-
551
- // Main CLI functionality
552
- async function main() {
553
- const args = process.argv.slice(2);
554
- const installer = new StigmergyInstaller();
555
-
556
- if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
557
- console.log('Stigmergy CLI - Multi-Agents Cross-AI CLI Tools Collaboration System');
558
- console.log('Version: 1.0.78');
559
- console.log('');
560
- console.log('[SYSTEM] Automated Installation and Deployment System');
561
- console.log('');
562
- console.log('Usage: stigmergy [command] [options]');
563
- console.log('');
564
- console.log('Commands:');
565
- console.log(' help, --help Show this help message');
566
- console.log(' version, --version Show version information');
567
- console.log(' status Check CLI tools status');
568
- console.log(' scan Scan for available AI CLI tools');
569
- console.log(' install Auto-install missing CLI tools');
570
- console.log(' deploy Deploy hooks to installed tools');
571
- console.log(' setup Complete setup and configuration');
572
- console.log(' call <tool> Execute prompt with specified or auto-routed AI CLI');
573
- console.log('');
574
- console.log('[WORKFLOW] Automated Workflow:');
575
- console.log(' 1. npm install -g stigmergy # Install Stigmergy');
576
- console.log(' 2. stigmergy install # Auto-scan & install CLI tools');
577
- console.log(' 3. stigmergy setup # Deploy hooks & config');
578
- console.log(' 4. stigmergy call <ai> <prompt> # Start collaborating');
579
- console.log('');
580
- console.log('For more information, visit: https://github.com/ptreezh/stigmergy-CLI-Multi-Agents');
581
- return;
582
- }
583
-
584
- const command = args[0];
585
-
586
- switch (command) {
587
- case 'version':
588
- case '--version':
589
- console.log('Stigmergy CLI v1.0.78');
590
- break;
591
-
592
- case 'status':
593
- const { available, missing } = await installer.scanCLI();
594
- console.log('\n[STATUS] System Status:');
595
- console.log(`Available: ${Object.keys(available).length} tools`);
596
- console.log(`Missing: ${Object.keys(missing).length} tools`);
597
- break;
598
-
599
- case 'scan':
600
- await installer.scanCLI();
601
- break;
602
-
603
- case 'install':
604
- const { missing: missingTools } = await installer.scanCLI();
605
- const options = await installer.showInstallOptions(missingTools);
606
-
607
- if (options.length > 0) {
608
- // For automation, assume user wants to install all tools
609
- console.log('\n[INFO] Installing all missing tools...');
610
- await installer.installTools(options, missingTools);
611
- }
612
- break;
613
-
614
- case 'deploy':
615
- const { available: deployedTools } = await installer.scanCLI();
616
- await installer.deployHooks(deployedTools);
617
- break;
618
-
619
- case 'setup':
620
- console.log('[SETUP] Starting complete Stigmergy setup...\n');
621
-
622
- const { available: setupAvailable, missing: setupMissing } = await installer.scanCLI();
623
- const setupOptions = await installer.showInstallOptions(setupMissing);
624
-
625
- if (setupOptions.length > 0) {
626
- console.log('\n[INFO] Installing all missing tools...');
627
- await installer.installTools(setupOptions, setupMissing);
628
- }
629
-
630
- await installer.deployHooks(setupAvailable);
631
- await installer.initializeConfig();
632
- installer.showUsageInstructions();
633
- break;
634
-
635
- case 'call':
636
- if (args.length < 2) {
637
- console.log('[ERROR] Usage: stigmergy call <tool> "<prompt>"');
638
- process.exit(1);
639
- }
640
-
641
- const targetTool = args[1];
642
- const prompt = args.slice(2).join(' ');
643
-
644
- // Handle call command logic
645
- console.log(`[CALL] Executing with ${targetTool}: ${prompt}`);
646
- // Implementation would go here
647
- break;
648
-
649
- case 'auto-install':
650
- // Auto-install mode for npm postinstall
651
- console.log('[AUTO-INSTALL] Stigmergy CLI automated setup');
652
- console.log('='.repeat(60));
653
-
654
- const { available: autoAvailable, missing: autoMissing } = await installer.scanCLI();
655
-
656
- if (Object.keys(autoMissing).length > 0) {
657
- const autoOptions = await installer.showInstallOptions(autoMissing);
658
- console.log('\n[INFO] Auto-install mode detected. Skipping manual installation.');
659
- console.log('[INFO] Please run "stigmergy install" to install missing tools.');
660
- }
661
-
662
- await installer.deployHooks(autoAvailable);
663
- await installer.initializeConfig();
664
- break;
665
-
666
- default:
667
- console.log(`[ERROR] Unknown command: ${command}`);
668
- console.log('[INFO] Run "stigmergy --help" for usage information');
669
- process.exit(1);
670
- }
671
- }
672
-
673
- // Export for testing
674
- module.exports = { StigmergyInstaller, SmartRouter, MemoryManager, CLI_TOOLS };
675
-
676
- // Run main function
677
- if (require.main === module) {
678
- main().catch(error => {
679
- console.error('[FATAL] Stigmergy CLI encountered an error:', error);
680
- process.exit(1);
681
- });
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Stigmergy CLI - Multi-Agents Cross-AI CLI Tools Collaboration System
5
+ * International Version - Pure English & ANSI Only
6
+ * Version: 1.0.84
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
+ qodercli: {
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(/^(use|please|help|using|with)\s*/i, '')
98
+ .trim();
99
+ return { tool: toolName, prompt: cleanInput };
100
+ }
101
+ }
102
+ }
103
+
104
+ // Default routing
105
+ const cleanInput = input.replace(/^(use|please|help|using|with)\s*/i, '').trim();
106
+ return { tool: this.defaultTool, prompt: cleanInput };
107
+ }
108
+
109
+ extractKeywords(toolName) {
110
+ const keywords = {
111
+ claude: ['claude', 'anthropic'],
112
+ gemini: ['gemini', 'google'],
113
+ qwen: ['qwen', 'alibaba', 'tongyi'],
114
+ iflow: ['iflow', 'workflow', 'intelligent'],
115
+ qodercli: ['qoder', 'code'],
116
+ codebuddy: ['codebuddy', 'buddy', 'assistant'],
117
+ copilot: ['copilot', 'github', 'gh'],
118
+ codex: ['codex', 'openai', 'gpt']
119
+ };
120
+
121
+ return keywords[toolName] || [toolName];
122
+ }
123
+ }
124
+
125
+ class MemoryManager {
126
+ constructor() {
127
+ this.globalMemoryFile = path.join(os.homedir(), '.stigmergy', 'memory.json');
128
+ this.projectMemoryFile = path.join(process.cwd(), 'STIGMERGY.md');
129
+ }
130
+
131
+ async addInteraction(tool, prompt, response) {
132
+ const interaction = {
133
+ timestamp: new Date().toISOString(),
134
+ tool,
135
+ prompt,
136
+ response,
137
+ duration: Date.now() - new Date().getTime()
138
+ };
139
+
140
+ // Add to global memory
141
+ await this.saveGlobalMemory(interaction);
142
+
143
+ // Add to project memory
144
+ await this.saveProjectMemory(interaction);
145
+ }
146
+
147
+ async saveGlobalMemory(interaction) {
148
+ try {
149
+ const memory = await this.loadGlobalMemory();
150
+ memory.interactions = memory.interactions.concat(interaction).slice(-100); // Keep last 100
151
+ memory.lastInteraction = interaction;
152
+
153
+ await fs.mkdir(path.dirname(this.globalMemoryFile), { recursive: true });
154
+ await fs.writeFile(this.globalMemoryFile, JSON.stringify(memory, null, 2));
155
+ } catch (error) {
156
+ console.error(`[MEMORY] Failed to save global memory: ${error.message}`);
157
+ }
158
+ }
159
+
160
+ async saveProjectMemory(interaction) {
161
+ try {
162
+ const memory = await this.loadProjectMemory();
163
+ memory.interactions = memory.interactions.concat(interaction).slice(-50); // Keep last 50
164
+ memory.lastInteraction = interaction;
165
+
166
+ await fs.writeFile(this.projectMemoryFile, this.formatProjectMemory(memory));
167
+ } catch (error) {
168
+ console.error(`[MEMORY] Failed to save project memory: ${error.message}`);
169
+ }
170
+ }
171
+
172
+ async loadGlobalMemory() {
173
+ try {
174
+ const data = await fs.readFile(this.globalMemoryFile, 'utf8');
175
+ return JSON.parse(data);
176
+ } catch {
177
+ return {
178
+ projectName: 'Global Stigmergy Memory',
179
+ interactions: [],
180
+ createdAt: new Date().toISOString()
181
+ };
182
+ }
183
+ }
184
+
185
+ async loadProjectMemory() {
186
+ try {
187
+ const data = await fs.readFile(this.projectMemoryFile, 'utf8');
188
+ return this.parseProjectMemory(data);
189
+ } catch {
190
+ return {
191
+ projectName: path.basename(process.cwd()),
192
+ interactions: [],
193
+ createdAt: new Date().toISOString()
194
+ };
195
+ }
196
+ }
197
+
198
+ formatProjectMemory(memory) {
199
+ let content = `# Stigmergy Project Memory\n\n`;
200
+ content += `**Project**: ${memory.projectName}\n`;
201
+ content += `**Created**: ${memory.createdAt}\n`;
202
+ content += `**Last Updated**: ${new Date().toISOString()}\n\n`;
203
+
204
+ if (memory.lastInteraction) {
205
+ content += `## Last Interaction\n\n`;
206
+ content += `- **Tool**: ${memory.lastInteraction.tool}\n`;
207
+ content += `- **Timestamp**: ${memory.lastInteraction.timestamp}\n`;
208
+ content += `- **Prompt**: ${memory.lastInteraction.prompt}\n`;
209
+ content += `- **Response**: ${memory.lastInteraction.response.substring(0, 200)}...\n\n`;
210
+ }
211
+
212
+ content += `## Recent Interactions (${memory.interactions.length})\n\n`;
213
+ memory.interactions.slice(-10).forEach((interaction, index) => {
214
+ content += `### ${index + 1}. ${interaction.tool} - ${interaction.timestamp}\n\n`;
215
+ content += `**Prompt**: ${interaction.prompt}\n\n`;
216
+ content += `**Response**: ${interaction.response.substring(0, 200)}...\n\n`;
217
+ });
218
+
219
+ return content;
220
+ }
221
+
222
+ parseProjectMemory(markdown) {
223
+ // Simple parser for project memory
224
+ return {
225
+ projectName: 'Project',
226
+ interactions: [],
227
+ createdAt: new Date().toISOString()
228
+ };
229
+ }
230
+ }
231
+
232
+ class StigmergyInstaller {
233
+ constructor() {
234
+ this.router = new SmartRouter();
235
+ this.memory = new MemoryManager();
236
+ this.configDir = path.join(os.homedir(), '.stigmergy');
237
+ }
238
+
239
+ async checkCLI(toolName) {
240
+ const tool = this.router.tools[toolName];
241
+ if (!tool) return false;
242
+
243
+ // Try multiple ways to check if CLI is available
244
+ const checks = [
245
+ // Method 1: Try version command
246
+ { args: ['--version'], expected: 0 },
247
+ // Method 2: Try help command
248
+ { args: ['--help'], expected: 0 },
249
+ // Method 3: Try help command with -h
250
+ { args: ['-h'], expected: 0 },
251
+ // Method 4: Try just the command (help case)
252
+ { args: [], expected: 0 },
253
+ // Method 5: Try version with alternative format
254
+ { args: ['version'], expected: 0 }
255
+ ];
256
+
257
+ for (const check of checks) {
258
+ try {
259
+ const result = spawnSync(toolName, check.args, {
260
+ encoding: 'utf8',
261
+ timeout: 8000,
262
+ stdio: 'pipe'
263
+ });
264
+
265
+ // Check if command exists and runs (exit 0 or shows help)
266
+ if (result.status === 0 || result.stdout.includes(toolName) || result.stderr.includes(toolName)) {
267
+ return true;
268
+ }
269
+
270
+ // Also check if command exists but returns non-zero (some CLIs do this)
271
+ if (result.error === undefined && (result.stdout.length > 0 || result.stderr.length > 0)) {
272
+ const output = (result.stdout + result.stderr).toLowerCase();
273
+ if (output.includes(toolName) || output.includes('cli') || output.includes('ai') || output.includes('help')) {
274
+ return true;
275
+ }
276
+ }
277
+ } catch (error) {
278
+ // Command not found, continue to next check
279
+ continue;
280
+ }
281
+ }
282
+
283
+ // Method 6: Check if command exists in PATH using `which`/`where` (platform specific)
284
+ try {
285
+ const whichCmd = process.platform === 'win32' ? 'where' : 'which';
286
+ const result = spawnSync(whichCmd, [toolName], {
287
+ encoding: 'utf8',
288
+ timeout: 5000
289
+ });
290
+
291
+ if (result.status === 0 && result.stdout.trim().length > 0) {
292
+ // Found in PATH
293
+ return true;
294
+ }
295
+ } catch (error) {
296
+ // Continue
297
+ }
298
+
299
+ // Method 7: Check common installation paths
300
+ const commonPaths = this.getCommonCLIPaths(toolName);
301
+ for (const cliPath of commonPaths) {
302
+ try {
303
+ if (await this.fileExists(cliPath)) {
304
+ // Found at specific path
305
+ return true;
306
+ }
307
+ } catch (error) {
308
+ continue;
309
+ }
310
+ }
311
+
312
+ return false;
313
+ }
314
+
315
+ getCommonCLIPaths(toolName) {
316
+ const homeDir = os.homedir();
317
+ const paths = [];
318
+
319
+ // Add platform-specific paths
320
+ if (process.platform === 'win32') {
321
+ // Local and global npm paths
322
+ paths.push(
323
+ path.join(homeDir, 'AppData', 'Roaming', 'npm', `${toolName}.cmd`),
324
+ path.join(homeDir, 'AppData', 'Roaming', 'npm', `${toolName}.ps1`),
325
+ path.join(homeDir, 'AppData', 'Local', 'npm', `${toolName}.cmd`),
326
+ path.join(homeDir, 'AppData', 'Local', 'npm', `${toolName}.ps1`),
327
+ // Program Files
328
+ path.join(process.env.ProgramFiles || 'C:\\Program Files', `${toolName}`, `${toolName}.exe`),
329
+ path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', `${toolName}`, `${toolName}.exe`),
330
+ // Node.js global packages
331
+ path.join(homeDir, '.npm', `${toolName}.cmd`),
332
+ path.join(homeDir, '.npm', `${toolName}.js`),
333
+ // User directories
334
+ path.join(homeDir, `${toolName}.cmd`),
335
+ path.join(homeDir, `${toolName}.exe`),
336
+ // Custom test directories
337
+ path.join(homeDir, '.stigmergy-test', `${toolName}.cmd`),
338
+ path.join(homeDir, '.stigmergy-test', `${toolName}.js`)
339
+ );
340
+ } else {
341
+ paths.push(
342
+ // Global npm paths
343
+ path.join(homeDir, '.npm', 'global', 'bin', toolName),
344
+ path.join(homeDir, '.npm', 'global', 'bin', `${toolName}.js`),
345
+ // Local npm paths
346
+ path.join(homeDir, '.npm', 'bin', toolName),
347
+ path.join(homeDir, '.local', 'bin', toolName),
348
+ path.join(homeDir, '.local', 'bin', `${toolName}.js`),
349
+ // System paths
350
+ path.join('/usr', 'local', 'bin', toolName),
351
+ path.join('/usr', 'local', 'bin', `${toolName}.js`),
352
+ path.join('/usr', 'bin', toolName),
353
+ path.join('/usr', 'bin', `${toolName}.js`),
354
+ // User home
355
+ path.join(homeDir, '.local', 'bin', toolName),
356
+ path.join(homeDir, '.local', 'bin', `${toolName}.js`),
357
+ // Custom test directories
358
+ path.join(homeDir, '.stigmergy-test', toolName),
359
+ path.join(homeDir, '.stigmergy-test', `${toolName}.js`)
360
+ );
361
+ }
362
+
363
+ // Add NPM global bin directory
364
+ try {
365
+ const { spawnSync } = require('child_process');
366
+ const npmRoot = spawnSync('npm', ['root', '-g'], { encoding: 'utf8' }).stdout.trim();
367
+ if (npmRoot) {
368
+ paths.push(path.join(npmRoot, 'bin', toolName));
369
+ paths.push(path.join(npmRoot, `${toolName}.js`));
370
+ paths.push(path.join(npmRoot, `${toolName}.cmd`));
371
+ }
372
+ } catch (error) {
373
+ // Continue
374
+ }
375
+
376
+ return paths;
377
+ }
378
+
379
+ async fileExists(filePath) {
380
+ try {
381
+ await fs.access(filePath);
382
+ return true;
383
+ } catch {
384
+ return false;
385
+ }
386
+ }
387
+
388
+ async scanCLI() {
389
+ console.log('[SCAN] Scanning for AI CLI tools on your system...');
390
+ console.log('='.repeat(60));
391
+
392
+ const available = {};
393
+ const missing = {};
394
+
395
+ for (const [toolName, toolInfo] of Object.entries(this.router.tools)) {
396
+ const isAvailable = await this.checkCLI(toolName);
397
+
398
+ if (isAvailable) {
399
+ available[toolName] = toolInfo;
400
+ console.log(`[OK] ${toolInfo.name}: Available`);
401
+ } else {
402
+ missing[toolName] = toolInfo;
403
+ console.log(`[X] ${toolInfo.name}: Not Available`);
404
+ }
405
+ }
406
+
407
+ console.log('='.repeat(60));
408
+ console.log(`[SUMMARY] ${Object.keys(available).length}/${Object.keys(this.router.tools).length} tools available`);
409
+
410
+ return { available, missing };
411
+ }
412
+
413
+ async showInstallOptions(missing) {
414
+ if (Object.keys(missing).length === 0) {
415
+ console.log('[INFO] All AI CLI tools are already installed!');
416
+ return [];
417
+ }
418
+
419
+ console.log('\n[INSTALL] The following AI CLI tools can be automatically installed:\n');
420
+
421
+ const options = [];
422
+ let index = 1;
423
+
424
+ for (const [toolName, toolInfo] of Object.entries(missing)) {
425
+ console.log(` ${index}. ${toolInfo.name}`);
426
+ console.log(` Install: ${toolInfo.install}`);
427
+ options.push({ index, toolName, toolInfo });
428
+ index++;
429
+ }
430
+
431
+ console.log('\n[OPTIONS] Installation Options:');
432
+ console.log('- Enter numbers separated by spaces (e.g: 1 3 5)');
433
+ console.log('- Enter "all" to install all missing tools');
434
+ console.log('- Enter "skip" to skip CLI installation');
435
+
436
+ return options;
437
+ }
438
+
439
+ async installTools(selectedTools, missing) {
440
+ if (!selectedTools || selectedTools.length === 0) {
441
+ console.log('[INFO] Skipping CLI tool installation');
442
+ return true;
443
+ }
444
+
445
+ console.log('\n[INSTALL] Installing selected AI CLI tools...');
446
+
447
+ for (const selection of selectedTools) {
448
+ const { toolName, toolInfo } = selection;
449
+ console.log(`\n[INSTALLING] ${toolInfo.name}...`);
450
+
451
+ try {
452
+ const installCmd = toolInfo.install.split(' ');
453
+ const result = spawnSync(installCmd[0], installCmd.slice(1), {
454
+ encoding: 'utf8',
455
+ timeout: 300000, // Increased to 5 minutes for CLI tools that download binaries
456
+ stdio: 'inherit',
457
+ env: process.env
458
+ });
459
+
460
+ if (result.status === 0) {
461
+ console.log(`[OK] ${toolInfo.name} installed successfully`);
462
+ } else {
463
+ console.log(`[ERROR] Failed to install ${toolInfo.name}`);
464
+ console.log(`[INFO] Please run manually: ${toolInfo.install}`);
465
+ }
466
+ } catch (error) {
467
+ console.log(`[ERROR] Failed to install ${toolInfo.name}: ${error.message}`);
468
+ console.log(`[INFO] Please run manually: ${toolInfo.install}`);
469
+ }
470
+ }
471
+
472
+ return true;
473
+ }
474
+
475
+ async deployHooks(available) {
476
+ console.log('\n[DEPLOY] Deploying cross-CLI integration hooks...');
477
+
478
+ for (const [toolName, toolInfo] of Object.entries(available)) {
479
+ console.log(`\n[DEPLOY] Deploying hooks for ${toolInfo.name}...`);
480
+
481
+ try {
482
+ await fs.mkdir(toolInfo.hooksDir, { recursive: true });
483
+
484
+ // Create config directory (not the config file itself)
485
+ const configDir = path.dirname(toolInfo.config);
486
+ await fs.mkdir(configDir, { recursive: true });
487
+
488
+ console.log(`[OK] Created directories for ${toolInfo.name}`);
489
+ console.log(`[INFO] Hooks directory: ${toolInfo.hooksDir}`);
490
+ console.log(`[INFO] Config directory: ${configDir}`);
491
+ } catch (error) {
492
+ console.log(`[ERROR] Failed to create directories for ${toolInfo.name}: ${error.message}`);
493
+ }
494
+ }
495
+
496
+ console.log('\n[OK] Hook deployment completed');
497
+ return true;
498
+ }
499
+
500
+ async initializeConfig() {
501
+ console.log('\n[CONFIG] Initializing Stigmergy configuration...');
502
+
503
+ try {
504
+ await fs.mkdir(this.configDir, { recursive: true });
505
+
506
+ const configFile = path.join(this.configDir, 'config.json');
507
+ const config = {
508
+ version: '1.0.84',
509
+ initialized: true,
510
+ createdAt: new Date().toISOString(),
511
+ lastUpdated: new Date().toISOString(),
512
+ defaultCLI: 'claude',
513
+ enableCrossCLI: true,
514
+ enableMemory: true
515
+ };
516
+
517
+ await fs.writeFile(configFile, JSON.stringify(config, null, 2));
518
+ console.log('[OK] Configuration initialized');
519
+ console.log(`[INFO] Config file: ${configFile}`);
520
+
521
+ return true;
522
+ } catch (error) {
523
+ console.log(`[ERROR] Failed to initialize configuration: ${error.message}`);
524
+ return false;
525
+ }
526
+ }
527
+
528
+ showUsageInstructions() {
529
+ console.log('\n[USAGE] Stigmergy CLI Usage Instructions:');
530
+ console.log('='.repeat(60));
531
+ console.log('');
532
+ console.log('1. Check System Status:');
533
+ console.log(' stigmergy status');
534
+ console.log('');
535
+ console.log('2. Check Available Tools:');
536
+ console.log(' stigmergy scan');
537
+ console.log('');
538
+ console.log('3. Start Using AI CLI Collaboration:');
539
+ console.log(' stigmergy call claude "help me debug this code"');
540
+ console.log(' stigmergy call gemini "generate documentation"');
541
+ console.log(' stigmergy call qwen "translate to English"');
542
+ console.log('');
543
+ console.log('4. Initialize New Projects:');
544
+ console.log(' stigmergy init --primary claude');
545
+ console.log('');
546
+ console.log('[INFO] Documentation:');
547
+ console.log(' - Global Config: ~/.stigmergy/config.json');
548
+ console.log(' - Project Docs: ./STIGMERGY.md');
549
+ console.log(' - GitHub: https://github.com/ptreezh/stigmergy-CLI-Multi-Agents');
550
+ console.log('');
551
+ console.log('[END] Happy collaborating with multiple AI CLI tools!');
552
+ }
553
+ }
554
+
555
+ // Main CLI functionality
556
+ async function main() {
557
+ const args = process.argv.slice(2);
558
+ const installer = new StigmergyInstaller();
559
+
560
+ if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
561
+ console.log('Stigmergy CLI - Multi-Agents Cross-AI CLI Tools Collaboration System');
562
+ console.log('Version: 1.0.84');
563
+ console.log('');
564
+ console.log('[SYSTEM] Automated Installation and Deployment System');
565
+ console.log('');
566
+ console.log('Usage: stigmergy [command] [options]');
567
+ console.log('');
568
+ console.log('Commands:');
569
+ console.log(' help, --help Show this help message');
570
+ console.log(' version, --version Show version information');
571
+ console.log(' status Check CLI tools status');
572
+ console.log(' scan Scan for available AI CLI tools');
573
+ console.log(' install Auto-install missing CLI tools');
574
+ console.log(' deploy Deploy hooks to installed tools');
575
+ console.log(' setup Complete setup and configuration');
576
+ console.log(' call <tool> Execute prompt with specified or auto-routed AI CLI');
577
+ console.log('');
578
+ console.log('[WORKFLOW] Automated Workflow:');
579
+ console.log(' 1. npm install -g stigmergy # Install Stigmergy');
580
+ console.log(' 2. stigmergy install # Auto-scan & install CLI tools');
581
+ console.log(' 3. stigmergy setup # Deploy hooks & config');
582
+ console.log(' 4. stigmergy call <ai> <prompt> # Start collaborating');
583
+ console.log('');
584
+ console.log('For more information, visit: https://github.com/ptreezh/stigmergy-CLI-Multi-Agents');
585
+ return;
586
+ }
587
+
588
+ const command = args[0];
589
+
590
+ switch (command) {
591
+ case 'version':
592
+ case '--version':
593
+ console.log('Stigmergy CLI v1.0.84');
594
+ break;
595
+
596
+ case 'status':
597
+ const { available, missing } = await installer.scanCLI();
598
+ console.log('\n[STATUS] System Status:');
599
+ console.log(`Available: ${Object.keys(available).length} tools`);
600
+ console.log(`Missing: ${Object.keys(missing).length} tools`);
601
+ break;
602
+
603
+ case 'scan':
604
+ await installer.scanCLI();
605
+ break;
606
+
607
+ case 'install':
608
+ const { missing: missingTools } = await installer.scanCLI();
609
+ const options = await installer.showInstallOptions(missingTools);
610
+
611
+ if (options.length > 0) {
612
+ // Use inquirer for interactive selection
613
+ const inquirer = require('inquirer');
614
+
615
+ const choices = options.map(opt => ({
616
+ name: `${opt.toolInfo.name} - ${opt.toolInfo.install}`,
617
+ value: opt.toolName
618
+ }));
619
+
620
+ choices.push(new inquirer.Separator(' = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ='),
621
+ { name: 'All missing tools', value: 'all' },
622
+ { name: 'Skip installation', value: 'skip' });
623
+
624
+ const answers = await inquirer.prompt([
625
+ {
626
+ type: 'checkbox',
627
+ name: 'selectedTools',
628
+ message: 'Select tools to install (Space to select, Enter to confirm):',
629
+ choices: choices,
630
+ validate: function (answer) {
631
+ if (answer.length < 1) {
632
+ return 'You must choose at least one option.';
633
+ }
634
+ return true;
635
+ }
636
+ }
637
+ ]);
638
+
639
+ if (answers.selectedTools.includes('skip')) {
640
+ console.log('[INFO] Skipping CLI tool installation');
641
+ } else {
642
+ let toolsToInstall;
643
+ if (answers.selectedTools.includes('all')) {
644
+ toolsToInstall = options;
645
+ } else {
646
+ toolsToInstall = options.filter(opt => answers.selectedTools.includes(opt.toolName));
647
+ }
648
+
649
+ console.log('\n[INFO] Installing selected tools (this may take several minutes for tools that download binaries)...');
650
+ await installer.installTools(toolsToInstall, missingTools);
651
+ }
652
+ } else {
653
+ console.log('\n[INFO] All required tools are already installed!');
654
+ }
655
+ break;
656
+
657
+ case 'deploy':
658
+ const { available: deployedTools } = await installer.scanCLI();
659
+ await installer.deployHooks(deployedTools);
660
+ break;
661
+
662
+ case 'setup':
663
+ console.log('[SETUP] Starting complete Stigmergy setup...\n');
664
+
665
+ const { available: setupAvailable, missing: setupMissing } = await installer.scanCLI();
666
+ const setupOptions = await installer.showInstallOptions(setupMissing);
667
+
668
+ if (setupOptions.length > 0) {
669
+ // Use inquirer for interactive selection during setup as well
670
+ const inquirer = require('inquirer');
671
+
672
+ const choices = setupOptions.map(opt => ({
673
+ name: `${opt.toolInfo.name} - ${opt.toolInfo.install}`,
674
+ value: opt.toolName
675
+ }));
676
+
677
+ choices.push(new inquirer.Separator(' = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ='),
678
+ { name: 'All missing tools', value: 'all' },
679
+ { name: 'Skip installation', value: 'skip' });
680
+
681
+ const answers = await inquirer.prompt([
682
+ {
683
+ type: 'checkbox',
684
+ name: 'selectedTools',
685
+ message: 'Select tools to install during setup (Space to select, Enter to confirm):',
686
+ choices: choices,
687
+ validate: function (answer) {
688
+ if (answer.length < 1) {
689
+ return 'You must choose at least one option.';
690
+ }
691
+ return true;
692
+ }
693
+ }
694
+ ]);
695
+
696
+ if (answers.selectedTools.includes('skip')) {
697
+ console.log('[INFO] Skipping CLI tool installation during setup');
698
+ } else {
699
+ let toolsToInstall;
700
+ if (answers.selectedTools.includes('all')) {
701
+ toolsToInstall = setupOptions;
702
+ } else {
703
+ toolsToInstall = setupOptions.filter(opt => answers.selectedTools.includes(opt.toolName));
704
+ }
705
+
706
+ console.log('\n[INFO] Installing selected tools (this may take several minutes for tools that download binaries)...');
707
+ await installer.installTools(toolsToInstall, setupMissing);
708
+ }
709
+ } else {
710
+ console.log('\n[INFO] All required tools are already installed!');
711
+ }
712
+
713
+ await installer.deployHooks(setupAvailable);
714
+ await installer.initializeConfig();
715
+ installer.showUsageInstructions();
716
+ break;
717
+
718
+ case 'call':
719
+ if (args.length < 2) {
720
+ console.log('[ERROR] Usage: stigmergy call <tool> "<prompt>"');
721
+ process.exit(1);
722
+ }
723
+
724
+ const targetTool = args[1];
725
+ const prompt = args.slice(2).join(' ');
726
+
727
+ // Handle call command logic
728
+ console.log(`[CALL] Executing with ${targetTool}: ${prompt}`);
729
+ // Implementation would go here
730
+ break;
731
+
732
+ case 'auto-install':
733
+ // Auto-install mode for npm postinstall
734
+ console.log('[AUTO-INSTALL] Stigmergy CLI automated setup');
735
+ console.log('='.repeat(60));
736
+
737
+ const { available: autoAvailable, missing: autoMissing } = await installer.scanCLI();
738
+
739
+ // Show summary to user after installation
740
+ if (Object.keys(autoMissing).length > 0) {
741
+ console.log('\n[INFO] Found ' + Object.keys(autoMissing).length + ' missing AI CLI tools:');
742
+ for (const [toolName, toolInfo] of Object.entries(autoMissing)) {
743
+ console.log(` - ${toolInfo.name} (${toolName})`);
744
+ }
745
+ console.log('\n[INFO] Auto-install mode detected. Skipping automatic installation of missing tools.');
746
+ console.log('[INFO] For full functionality, please run "stigmergy install" after installation completes.');
747
+ } else {
748
+ console.log('\n[INFO] All AI CLI tools are already installed! No additional tools required.');
749
+ }
750
+
751
+ await installer.deployHooks(autoAvailable);
752
+ await installer.initializeConfig();
753
+
754
+ // Show final message to guide users
755
+ console.log('\n[SUCCESS] Stigmergy CLI installed successfully!');
756
+ console.log('[USAGE] Run "stigmergy setup" to complete full configuration and install missing AI CLI tools.');
757
+ console.log('[USAGE] Run "stigmergy install" to install only missing AI CLI tools.');
758
+ console.log('[USAGE] Run "stigmergy --help" to see all available commands.');
759
+ break;
760
+
761
+ default:
762
+ console.log(`[ERROR] Unknown command: ${command}`);
763
+ console.log('[INFO] Run "stigmergy --help" for usage information');
764
+ process.exit(1);
765
+ }
766
+ }
767
+
768
+ // Export for testing
769
+ module.exports = { StigmergyInstaller, SmartRouter, MemoryManager, CLI_TOOLS };
770
+
771
+ // Run main function
772
+ if (require.main === module) {
773
+ main().catch(error => {
774
+ console.error('[FATAL] Stigmergy CLI encountered an error:', error);
775
+ process.exit(1);
776
+ });
682
777
  }