stigmergy 1.2.13 → 1.3.2-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +39 -3
  2. package/STIGMERGY.md +3 -0
  3. package/config/enhanced-cli-config.json +438 -0
  4. package/docs/CLI_TOOLS_AGENT_SKILL_ANALYSIS.md +463 -0
  5. package/docs/ENHANCED_CLI_AGENT_SKILL_CONFIG.md +285 -0
  6. package/docs/INSTALLER_ARCHITECTURE.md +257 -0
  7. package/docs/SUDO_PROBLEM_AND_SOLUTION.md +529 -0
  8. package/package.json +14 -5
  9. package/scripts/analyze-router.js +168 -0
  10. package/scripts/test-runner.js +344 -0
  11. package/src/cli/commands/autoinstall.js +158 -0
  12. package/src/cli/commands/errors.js +190 -0
  13. package/src/cli/commands/install.js +142 -0
  14. package/src/cli/commands/permissions.js +108 -0
  15. package/src/cli/commands/project.js +449 -0
  16. package/src/cli/commands/resume.js +136 -0
  17. package/src/cli/commands/scan.js +97 -0
  18. package/src/cli/commands/skills.js +158 -0
  19. package/src/cli/commands/status.js +106 -0
  20. package/src/cli/commands/system.js +301 -0
  21. package/src/cli/router-beta.js +477 -0
  22. package/src/cli/utils/environment.js +75 -0
  23. package/src/cli/utils/formatters.js +47 -0
  24. package/src/cli/utils/skills_cache.js +92 -0
  25. package/src/core/cache_cleaner.js +1 -0
  26. package/src/core/cli_adapters.js +345 -0
  27. package/src/core/cli_help_analyzer.js +473 -1
  28. package/src/core/cli_path_detector.js +2 -1
  29. package/src/core/cli_tools.js +107 -0
  30. package/src/core/coordination/nodejs/HookDeploymentManager.js +185 -422
  31. package/src/core/coordination/nodejs/HookDeploymentManager.refactored.js +323 -0
  32. package/src/core/coordination/nodejs/generators/CLIAdapterGenerator.js +363 -0
  33. package/src/core/coordination/nodejs/generators/ResumeSessionGenerator.js +701 -0
  34. package/src/core/coordination/nodejs/generators/SkillsIntegrationGenerator.js +1210 -0
  35. package/src/core/coordination/nodejs/generators/index.js +12 -0
  36. package/src/core/enhanced_cli_installer.js +220 -30
  37. package/src/core/enhanced_cli_parameter_handler.js +395 -0
  38. package/src/core/execution_mode_detector.js +222 -0
  39. package/src/core/installer.js +51 -70
  40. package/src/core/local_skill_scanner.js +732 -0
  41. package/src/core/multilingual/language-pattern-manager.js +1 -1
  42. package/src/core/skills/StigmergySkillManager.js +26 -8
  43. package/src/core/smart_router.js +279 -2
  44. package/src/index.js +10 -4
  45. package/test/cli-integration.test.js +304 -0
  46. package/test/enhanced-cli-agent-skill-test.js +485 -0
  47. package/test/specific-cli-agent-skill-analysis.js +385 -0
  48. package/src/cli/router.js +0 -1783
@@ -0,0 +1,12 @@
1
+ // Generators Module Index
2
+ // 导出所有生成器模块
3
+
4
+ const ResumeSessionGenerator = require('./ResumeSessionGenerator');
5
+ const SkillsIntegrationGenerator = require('./SkillsIntegrationGenerator');
6
+ const CLIAdapterGenerator = require('./CLIAdapterGenerator');
7
+
8
+ module.exports = {
9
+ ResumeSessionGenerator,
10
+ SkillsIntegrationGenerator,
11
+ CLIAdapterGenerator
12
+ };
@@ -219,34 +219,67 @@ class EnhancedCLIInstaller {
219
219
  * Setup Unix elevated context
220
220
  */
221
221
  async setupUnixElevatedContext() {
222
- this.log('info', 'Unix: Checking sudo availability...');
222
+ this.log('info', 'Unix: Checking privilege escalation methods...');
223
+
224
+ // List of privilege escalation tools to check (in order of preference)
225
+ const privilegeEscalationTools = [
226
+ { name: 'sudo', testCmd: 'sudo', testArgs: ['-n', 'true'] },
227
+ { name: 'doas', testCmd: 'doas', testArgs: ['-n', 'true'] },
228
+ { name: 'run0', testCmd: 'run0', testArgs: ['-n', 'true'] },
229
+ { name: 'pkexec', testCmd: 'pkexec', testArgs: ['--help'] },
230
+ ];
223
231
 
224
- try {
225
- const result = spawnSync('sudo', ['-n', 'true'], {
226
- stdio: 'pipe',
227
- timeout: 5000
228
- });
232
+ let availableTool = null;
233
+ let requiresPassword = false;
229
234
 
230
- // If sudo is available and configured for passwordless use
231
- if (result.status === 0) {
232
- return {
233
- success: true,
234
- platform: 'unix',
235
- sudo: 'passwordless'
236
- };
237
- } else {
238
- return {
239
- success: true,
240
- platform: 'unix',
241
- sudo: 'password_required',
242
- note: 'Will prompt for password per installation'
243
- };
235
+ for (const tool of privilegeEscalationTools) {
236
+ try {
237
+ const result = spawnSync(tool.testCmd, tool.testArgs, {
238
+ stdio: 'pipe',
239
+ timeout: 5000
240
+ });
241
+
242
+ // Tool exists
243
+ if (result.status === 0 || (result.status !== null && result.error?.code !== 'ENOENT')) {
244
+ // Check if it's passwordless
245
+ if (result.status === 0) {
246
+ availableTool = tool.name;
247
+ requiresPassword = false;
248
+ this.log('success', `Found ${tool.name} (passwordless)`);
249
+ break;
250
+ } else if (result.error?.code !== 'ENOENT') {
251
+ // Tool exists but requires password
252
+ availableTool = tool.name;
253
+ requiresPassword = true;
254
+ this.log('info', `Found ${tool.name} (requires password)`);
255
+ break;
256
+ }
257
+ }
258
+ } catch (error) {
259
+ // Tool doesn't exist, continue to next
260
+ continue;
244
261
  }
245
- } catch (error) {
262
+ }
263
+
264
+ if (availableTool) {
246
265
  return {
247
- success: false,
266
+ success: true,
267
+ platform: 'unix',
268
+ privilegeTool: availableTool,
269
+ requiresPassword: requiresPassword,
270
+ note: requiresPassword ? 'Will prompt for password per installation' : 'Passwordless access available'
271
+ };
272
+ } else {
273
+ // No privilege escalation tool found
274
+ this.log('warn', 'No privilege escalation tool found (sudo, doas, run0, pkexec)');
275
+ this.log('info', 'Will attempt user-space installation without privileges');
276
+ return {
277
+ success: true,
248
278
  platform: 'unix',
249
- error: error.message
279
+ privilegeTool: null,
280
+ requiresPassword: false,
281
+ userSpaceOnly: true,
282
+ note: 'Installation will be performed in user directory without privileges'
250
283
  };
251
284
  }
252
285
  }
@@ -255,6 +288,12 @@ class EnhancedCLIInstaller {
255
288
  * Install a tool using the pre-configured permission mode
256
289
  */
257
290
  async installTool(toolName, toolInfo, retryCount = 0) {
291
+ // Check if install command exists
292
+ if (!toolInfo.install) {
293
+ this.log('warn', `Tool ${toolName} has no install command, skipping...`);
294
+ return false;
295
+ }
296
+
258
297
  // Ensure permissions are configured first
259
298
  if (!this.permissionConfigured) {
260
299
  await this.setupPermissions();
@@ -338,6 +377,14 @@ class EnhancedCLIInstaller {
338
377
  */
339
378
  async executeStandardInstallation(toolInfo) {
340
379
  try {
380
+ // Check if install command exists
381
+ if (!toolInfo.install) {
382
+ return {
383
+ success: false,
384
+ error: `No install command specified for ${toolInfo.name || 'unknown tool'}`
385
+ };
386
+ }
387
+
341
388
  const [command, ...args] = toolInfo.install.split(' ');
342
389
 
343
390
  this.log('debug', `Executing: ${toolInfo.install}`);
@@ -438,10 +485,19 @@ class EnhancedCLIInstaller {
438
485
  * Execute Unix elevated installation
439
486
  */
440
487
  async executeUnixElevatedInstallation(toolInfo) {
441
- const command = `sudo ${toolInfo.install}`;
488
+ const permissionSetup = await this.setupPermissions();
489
+
490
+ // If no privilege escalation tool is available, use user-space installation
491
+ if (permissionSetup.userSpaceOnly) {
492
+ return await this.executeUserSpaceInstallation(toolInfo);
493
+ }
494
+
495
+ // Use the detected privilege escalation tool
496
+ const privilegeTool = permissionSetup.privilegeTool || 'sudo';
497
+ const command = `${privilegeTool} ${toolInfo.install}`;
442
498
 
443
499
  try {
444
- this.log('info', `Using sudo for elevated installation of: ${toolInfo.name}`);
500
+ this.log('info', `Using ${privilegeTool} for elevated installation of: ${toolInfo.name}`);
445
501
 
446
502
  const result = spawnSync('bash', ['-c', command], {
447
503
  stdio: this.options.verbose ? 'inherit' : 'pipe',
@@ -453,19 +509,137 @@ class EnhancedCLIInstaller {
453
509
  return { success: true, error: null };
454
510
  } else {
455
511
  const errorMessage = result.stderr || result.stdout || `Exit code ${result.status}`;
456
- return { success: false, error: errorMessage };
512
+ // If privilege escalation failed, try user-space installation as fallback
513
+ this.log('warn', `Privilege escalation failed, trying user-space installation...`);
514
+ return await this.executeUserSpaceInstallation(toolInfo);
515
+ }
516
+ } catch (error) {
517
+ this.log('warn', `Privilege escalation error: ${error.message}, trying user-space installation...`);
518
+ return await this.executeUserSpaceInstallation(toolInfo);
519
+ }
520
+ }
521
+
522
+ /**
523
+ * Execute user-space installation (no privileges required)
524
+ */
525
+ async executeUserSpaceInstallation(toolInfo) {
526
+ try {
527
+ // Install to user directory using --prefix flag
528
+ const os = require('os');
529
+ const path = require('path');
530
+
531
+ // Get user's npm global directory
532
+ let userNpmDir = process.env.NPM_CONFIG_PREFIX ||
533
+ path.join(os.homedir(), '.npm-global');
534
+
535
+ // Ensure directory exists
536
+ const fs = require('fs');
537
+ if (!fs.existsSync(userNpmDir)) {
538
+ fs.mkdirSync(userNpmDir, { recursive: true, mode: 0o755 });
539
+ }
540
+
541
+ // Extract package name from install command
542
+ // Format: "npm install -g @package/tool" or "npm install -g tool"
543
+ const installMatch = toolInfo.install.match(/npm\s+(?:install|upgrade)\s+(?:-g\s+)?(.+)/);
544
+ if (!installMatch) {
545
+ throw new Error('Cannot parse install command');
546
+ }
547
+
548
+ const packageName = installMatch[1].trim();
549
+
550
+ // Create user-space install command
551
+ const userCommand = `npm install -g --prefix "${userNpmDir}" ${packageName}`;
552
+
553
+ this.log('info', `Installing ${toolInfo.name} to user directory: ${userNpmDir}`);
554
+ this.log('info', `Command: ${userCommand}`);
555
+
556
+ // Set PATH to include user npm directory
557
+ const env = {
558
+ ...process.env,
559
+ PATH: `${path.join(userNpmDir, 'bin')}:${process.env.PATH}`
560
+ };
561
+
562
+ const result = spawnSync('bash', ['-c', userCommand], {
563
+ stdio: this.options.verbose ? 'inherit' : 'pipe',
564
+ timeout: this.options.timeout * 3, // Longer timeout for user-space install
565
+ encoding: 'utf8',
566
+ env: env
567
+ });
568
+
569
+ if (result.status === 0) {
570
+ this.log('success', `Successfully installed ${toolInfo.name} to user directory`);
571
+
572
+ // Provide PATH setup instructions
573
+ const binDir = path.join(userNpmDir, 'bin');
574
+ this.log('info', '⚠️ Make sure to add the bin directory to your PATH:');
575
+ this.log('info', ` export PATH="${binDir}:$PATH"`);
576
+
577
+ // Add to shell config files automatically
578
+ await this.addPathToShellConfig(binDir);
579
+
580
+ return { success: true, error: null, userSpace: true, binDir };
581
+ } else {
582
+ const errorMessage = result.stderr || result.stdout || `Exit code ${result.status}`;
583
+ return { success: false, error: `User-space installation failed: ${errorMessage}` };
457
584
  }
458
585
  } catch (error) {
459
586
  return { success: false, error: error.message };
460
587
  }
461
588
  }
462
589
 
590
+ /**
591
+ * Add PATH to shell configuration files
592
+ */
593
+ async addPathToShellConfig(binDir) {
594
+ const os = require('os');
595
+ const path = require('path');
596
+ const fs = require('fs/promises');
597
+
598
+ const shellConfigs = [
599
+ { file: path.join(os.homedir(), '.bashrc'), marker: '# Stigmergy CLI PATH' },
600
+ { file: path.join(os.homedir(), '.zshrc'), marker: '# Stigmergy CLI PATH' },
601
+ { file: path.join(os.homedir(), '.profile'), marker: '# Stigmergy CLI PATH' },
602
+ ];
603
+
604
+ const pathLine = `export PATH="${binDir}:$PATH"\n`;
605
+
606
+ for (const config of shellConfigs) {
607
+ try {
608
+ let content = '';
609
+ try {
610
+ content = await fs.readFile(config.file, 'utf8');
611
+ } catch (err) {
612
+ // File doesn't exist, will create it
613
+ }
614
+
615
+ // Check if PATH is already configured
616
+ if (content.includes(config.marker)) {
617
+ continue; // Already configured
618
+ }
619
+
620
+ // Append PATH configuration
621
+ await fs.appendFile(config.file, `\n${config.marker}\n${pathLine}`);
622
+ this.log('info', `Added PATH to ${path.basename(config.file)}`);
623
+ } catch (error) {
624
+ // Ignore errors for config files
625
+ }
626
+ }
627
+ }
628
+
463
629
  /**
464
630
  * Fallback installation method
465
631
  */
466
632
  async executeFallbackInstallation(toolInfo) {
467
633
  this.log('warn', 'Attempting fallback installation method...');
468
634
 
635
+ // Check if install command exists
636
+ if (!toolInfo.install) {
637
+ return {
638
+ success: false,
639
+ error: `No install command specified for ${toolInfo.name || 'unknown tool'}`
640
+ };
641
+ }
642
+
469
643
  // Try without some npm flags that might cause permission issues
470
644
  const [command, ...args] = toolInfo.install.split(' ');
471
645
  const fallbackArgs = args.filter(arg => !arg.startsWith('--'));
@@ -531,6 +705,12 @@ class EnhancedCLIInstaller {
531
705
  const toolInfo = toolInfos[toolName];
532
706
  if (!toolInfo) continue;
533
707
 
708
+ // Skip tools without install command (internal functions)
709
+ if (!toolInfo.install) {
710
+ this.log('debug', `Tool ${toolName} has no install command, skipping...`);
711
+ continue;
712
+ }
713
+
534
714
  this.results.installations[toolName] = {
535
715
  startTime: Date.now(),
536
716
  ...this.results.installations[toolName]
@@ -572,14 +752,24 @@ class EnhancedCLIInstaller {
572
752
  this.log('info', `Upgrading ${totalCount} CLI tools in ${this.permissionMode} mode...`);
573
753
 
574
754
  for (const toolName of toolNames) {
755
+ const originalInfo = toolInfos[toolName];
756
+ if (!originalInfo) {
757
+ this.log('warn', `Tool ${toolName} not found in toolInfos, skipping...`);
758
+ continue;
759
+ }
760
+
761
+ // Skip tools without install command (internal functions)
762
+ if (!originalInfo.install) {
763
+ this.log('debug', `Tool ${toolName} has no install command, skipping upgrade...`);
764
+ continue;
765
+ }
766
+
575
767
  const toolInfo = {
576
- ...toolInfos[toolName],
768
+ ...originalInfo,
577
769
  install: `npm upgrade -g ${toolName}`,
578
- name: `${toolInfo.name} (Upgrade)`
770
+ name: `${originalInfo.name} (Upgrade)`
579
771
  };
580
772
 
581
- if (!toolInfo) continue;
582
-
583
773
  const success = await this.installTool(toolName, toolInfo);
584
774
  if (success) {
585
775
  successCount++;