@_xtribe/cli 1.0.10 → 1.0.12

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/install-tribe.js CHANGED
@@ -5,6 +5,7 @@ const fs = require('fs');
5
5
  const path = require('path');
6
6
  const https = require('https');
7
7
  const { execSync, spawn } = require('child_process');
8
+ const readline = require('readline');
8
9
  const chalk = require('chalk');
9
10
  const ora = require('ora');
10
11
  const which = require('which');
@@ -46,6 +47,32 @@ async function checkCommand(cmd) {
46
47
  await which(cmd);
47
48
  return true;
48
49
  } catch {
50
+ // If not in PATH, check our install locations
51
+ if (cmd === 'tribe') {
52
+ const tribePath = path.join(tribeBinDir, 'tribe');
53
+ try {
54
+ await fs.promises.access(tribePath, fs.constants.X_OK);
55
+ return true;
56
+ } catch {
57
+ return false;
58
+ }
59
+ } else if (cmd === 'kubectl') {
60
+ const kubectlPath = path.join(binDir, 'kubectl');
61
+ try {
62
+ await fs.promises.access(kubectlPath, fs.constants.X_OK);
63
+ return true;
64
+ } catch {
65
+ return false;
66
+ }
67
+ } else if (cmd === 'colima') {
68
+ const colimaPath = path.join(binDir, 'colima');
69
+ try {
70
+ await fs.promises.access(colimaPath, fs.constants.X_OK);
71
+ return true;
72
+ } catch {
73
+ return false;
74
+ }
75
+ }
49
76
  return false;
50
77
  }
51
78
  }
@@ -318,6 +345,53 @@ async function installKubectl() {
318
345
  }
319
346
  }
320
347
 
348
+ async function setupPathEnvironment() {
349
+ const spinner = ora('Setting up PATH environment...').start();
350
+
351
+ try {
352
+ // Copy the tribe-env.sh script to ~/.tribe/
353
+ const envScriptSource = path.join(__dirname, 'tribe-env.sh');
354
+ const envScriptDest = path.join(tribeBinDir, '..', 'tribe-env.sh');
355
+
356
+ // Create the environment script content
357
+ const envScriptContent = `#!/bin/bash
358
+ # TRIBE CLI Environment Setup
359
+ #
360
+ # To use this, add the following line to your shell config:
361
+ # source ~/.tribe/tribe-env.sh
362
+ #
363
+ # Or run it manually in your current session:
364
+ # source ~/.tribe/tribe-env.sh
365
+
366
+ # Add TRIBE bin directory to PATH if not already present
367
+ TRIBE_BIN_DIR="$HOME/.tribe/bin"
368
+
369
+ if [[ -d "$TRIBE_BIN_DIR" ]] && [[ ":$PATH:" != *":$TRIBE_BIN_DIR:"* ]]; then
370
+ export PATH="$TRIBE_BIN_DIR:$PATH"
371
+ fi
372
+
373
+ # Optional: Add helpful aliases
374
+ alias tribe-logs="$TRIBE_BIN_DIR/tribe-logs" 2>/dev/null || true
375
+
376
+ # Verify tribe is available (silent check)
377
+ command -v tribe &> /dev/null || true
378
+ `;
379
+
380
+ fs.writeFileSync(envScriptDest, envScriptContent);
381
+ fs.chmodSync(envScriptDest, '755');
382
+
383
+ spinner.succeed('PATH environment script created');
384
+
385
+ // Store that we created the env script
386
+ global.envScriptCreated = true;
387
+
388
+ return true;
389
+ } catch (error) {
390
+ spinner.fail(`Failed to create PATH environment script: ${error.message}`);
391
+ return false;
392
+ }
393
+ }
394
+
321
395
  async function setupGlobalNpmCommand() {
322
396
  const spinner = ora('Setting up global tribe command...').start();
323
397
 
@@ -582,6 +656,10 @@ async function installTribeCLI() {
582
656
  downloadUrl,
583
657
  // GitHub releases direct download (fallback if API fails)
584
658
  `https://github.com/${githubRepo}/releases/latest/download/tribe-${platform}-${arch}`,
659
+ // GitHub raw content as emergency fallback
660
+ `https://raw.githubusercontent.com/${githubRepo}/main/releases/tribe-${platform}-${arch}`,
661
+ // jsdelivr CDN (mirrors GitHub)
662
+ `https://cdn.jsdelivr.net/gh/${githubRepo}@latest/releases/tribe-${platform}-${arch}`,
585
663
  // Local build if available
586
664
  ...(hasLocalBuild ? [`file://${localBuildPath}`] : [])
587
665
  ].filter(Boolean); // Filter out empty downloadUrl if API failed
@@ -641,6 +719,16 @@ async function installTribeCLI() {
641
719
 
642
720
  fs.chmodSync(tribeDest, '755');
643
721
 
722
+ // Remove macOS quarantine attribute
723
+ if (platform === 'darwin') {
724
+ try {
725
+ execSync(`xattr -d com.apple.quarantine "${tribeDest}" 2>/dev/null`, { stdio: 'ignore' });
726
+ log.info('Removed macOS quarantine attribute');
727
+ } catch {
728
+ // Not critical if this fails
729
+ }
730
+ }
731
+
644
732
  // Verify the binary works
645
733
  try {
646
734
  // Try version command first
@@ -723,58 +811,22 @@ async function startContainerRuntime() {
723
811
  // Colima not running, need to start it
724
812
  }
725
813
 
726
- // Try to start Colima
814
+ // Don't try to start Colima automatically - it takes too long
727
815
  if (await checkCommand('colima')) {
728
- const spinner = ora('Starting Colima container runtime...').start();
729
-
730
- try {
731
- // Strategy 1: Quick start with minimal resources and Kubernetes
732
- spinner.text = 'Starting Colima with Kubernetes...';
733
- const colimaPath = await findCommand('colima') || path.join(binDir, 'colima');
734
- execSync(`${colimaPath} start --cpu 2 --memory 4 --disk 10 --kubernetes --vm-type=vz`, {
735
- stdio: 'pipe',
736
- timeout: 60000 // 60 second timeout for K8s
737
- });
738
-
739
- spinner.succeed('Colima started successfully');
740
- return true;
741
-
742
- } catch (error) {
743
- // Strategy 2: Start in background with Kubernetes
744
- try {
745
- spinner.text = 'Starting Colima with Kubernetes in background...';
746
- const colimaPath = await findCommand('colima') || path.join(binDir, 'colima');
747
- const child = spawn(colimaPath, ['start', '--cpu', '2', '--memory', '4', '--disk', '10', '--kubernetes'], {
748
- detached: true,
749
- stdio: 'ignore',
750
- env: { ...process.env, PATH: `${binDir}:${process.env.PATH}` }
751
- });
752
- child.unref(); // Don't wait for completion
753
-
754
- spinner.succeed('Colima startup initiated (background)');
755
- log.info('Colima is starting in the background');
756
- log.info('Run "colima status" to check progress');
757
- return true;
758
-
759
- } catch (bgError) {
760
- spinner.fail('Failed to start Colima');
761
- log.warning('Container runtime startup failed (likely due to macOS system restrictions)');
762
- log.info('Options to fix:');
763
- log.info('');
764
- log.info('Option 1 - Manual Colima start:');
765
- log.info(' colima start --cpu 2 --memory 4 --kubernetes');
766
- log.info(' # This downloads a 344MB disk image (may take time)');
767
- log.info('');
768
- log.info('Option 2 - Use Homebrew (recommended):');
769
- log.info(' brew install colima');
770
- log.info(' colima start --kubernetes');
771
- log.info('');
772
- return false;
773
- }
774
- }
816
+ spinner.stop(); // Stop the spinner before showing info
817
+ console.log('');
818
+ console.log(chalk.yellow(' ⚠️ Colima needs to be started before using TRIBE'));
819
+ console.log(chalk.gray(' To start Colima with Kubernetes:'));
820
+ console.log(chalk.cyan(' colima start --kubernetes'));
821
+ console.log(chalk.gray(' This may take 2-5 minutes on first run'));
822
+ console.log('');
823
+ return true; // Return true since it's installed
824
+ } else {
825
+ spinner.warn('Colima not found');
826
+ return false;
775
827
  }
776
828
  } else if (platform === 'linux') {
777
- // Linux - K3s should already be running
829
+ // Linux - Check K3s status but don't try to start it
778
830
  spinner.text = 'Checking K3s status...';
779
831
 
780
832
  // Check if we're in a container (no systemd)
@@ -787,22 +839,18 @@ async function startContainerRuntime() {
787
839
  }
788
840
 
789
841
  try {
790
- execSync('sudo systemctl is-active --quiet k3s', { stdio: 'ignore' });
842
+ execSync('sudo systemctl is-active --quiet k3s', { stdio: 'ignore', timeout: 5000 });
791
843
  spinner.succeed('K3s is running');
792
844
  return true;
793
845
  } catch {
794
- // Try to start K3s
795
- spinner.text = 'Starting K3s...';
796
- try {
797
- execSync('sudo systemctl start k3s', { stdio: 'ignore' });
798
- await new Promise(resolve => setTimeout(resolve, 5000));
799
- spinner.succeed('K3s started');
800
- return true;
801
- } catch (error) {
802
- spinner.warn('K3s not available - requires systemd');
803
- log.info('K3s requires systemd. Install K3s manually or connect to external cluster');
804
- return true; // Don't fail
805
- }
846
+ // Don't try to start K3s - just inform the user
847
+ spinner.info('K3s is not running');
848
+ console.log('');
849
+ console.log(chalk.yellow(' ⚠️ K3s needs to be started'));
850
+ console.log(chalk.gray(' To start K3s:'));
851
+ console.log(chalk.cyan(' sudo systemctl start k3s'));
852
+ console.log('');
853
+ return true; // Don't fail
806
854
  }
807
855
  } else if (platform === 'win32') {
808
856
  spinner.fail('Windows support coming soon!');
@@ -862,10 +910,26 @@ async function verifyInstallation() {
862
910
  spinner.stop();
863
911
 
864
912
  console.log('\n' + chalk.bold('Installation Summary:'));
865
- tools.forEach(tool => {
913
+ for (const tool of tools) {
866
914
  const status = results[tool] ? chalk.green('✓') : chalk.red('✗');
867
- console.log(`${status} ${tool}`);
868
- });
915
+ let message = `${status} ${tool}`;
916
+
917
+ // Add additional info for tribe if it's installed but not in PATH
918
+ if (tool === 'tribe' && results[tool]) {
919
+ try {
920
+ await which('tribe');
921
+ // It's in PATH, no extra message needed
922
+ } catch {
923
+ // It's installed but not in PATH
924
+ message += chalk.gray(' (installed at ~/.tribe/bin/tribe)');
925
+ if (global.envScriptCreated) {
926
+ message += chalk.yellow('\n Run: source ~/.tribe/tribe-env.sh');
927
+ }
928
+ }
929
+ }
930
+
931
+ console.log(message);
932
+ }
869
933
 
870
934
  const extraStatus = containerWorking ? chalk.green('✓') : chalk.yellow('⚠');
871
935
  console.log(`${extraStatus} Container runtime`);
@@ -1173,6 +1237,21 @@ async function deployTribeCluster() {
1173
1237
  }
1174
1238
  }
1175
1239
 
1240
+ async function promptForPathSetup() {
1241
+ return new Promise((resolve) => {
1242
+ const rl = readline.createInterface({
1243
+ input: process.stdin,
1244
+ output: process.stdout
1245
+ });
1246
+
1247
+ console.log('');
1248
+ rl.question(chalk.yellow('Would you like to add "tribe" to your PATH? (y/n) '), (answer) => {
1249
+ rl.close();
1250
+ resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
1251
+ });
1252
+ });
1253
+ }
1254
+
1176
1255
  async function promptForClusterSetup() {
1177
1256
  // Check for auto-approve in CI environments
1178
1257
  if (process.env.CI === 'true' || process.env.TRIBE_AUTO_APPROVE === 'true') {
@@ -1421,7 +1500,8 @@ async function main() {
1421
1500
  tasks.push(
1422
1501
  { name: 'kubectl', fn: installKubectl },
1423
1502
  { name: 'TRIBE CLI', fn: installTribeCLI },
1424
- { name: 'Global tribe command', fn: setupGlobalNpmCommand }
1503
+ { name: 'PATH environment', fn: setupPathEnvironment }
1504
+ // We'll try npm global as a fallback, but not as primary method
1425
1505
  );
1426
1506
 
1427
1507
  let allSuccess = true;
@@ -1429,15 +1509,20 @@ async function main() {
1429
1509
  for (const task of tasks) {
1430
1510
  log.step(`Installing ${task.name}...`);
1431
1511
  const success = await task.fn();
1432
- if (!success) allSuccess = false;
1512
+ if (!success) {
1513
+ allSuccess = false;
1514
+ if (task.name === 'PATH environment') {
1515
+ global.envScriptCreated = false;
1516
+ }
1517
+ }
1433
1518
  }
1434
1519
 
1435
- // Try to start container runtime
1436
- await startContainerRuntime();
1437
-
1438
- // Verify everything
1520
+ // Verify everything first
1439
1521
  const verified = await verifyInstallation();
1440
1522
 
1523
+ // Try to start container runtime (after showing results)
1524
+ await startContainerRuntime();
1525
+
1441
1526
  if (verified) {
1442
1527
  // Check if cluster already exists
1443
1528
  const clusterExists = await checkClusterExists();
@@ -1467,11 +1552,47 @@ async function main() {
1467
1552
  console.log('\n' + chalk.bold.green('✨ TRIBE is ready!'));
1468
1553
  console.log('');
1469
1554
 
1555
+ // Offer to set up PATH
1556
+ if (global.envScriptCreated && !process.env.CI && !process.env.TRIBE_SKIP_PATH_PROMPT) {
1557
+ const shouldSetupPath = await promptForPathSetup();
1558
+ if (shouldSetupPath) {
1559
+ // Run the setup-path logic inline
1560
+ try {
1561
+ const setupPath = require('./setup-path.js');
1562
+ setupPath();
1563
+ console.log('');
1564
+ // PATH is now set up, update instructions
1565
+ global.pathSetupComplete = true;
1566
+ } catch (error) {
1567
+ log.warning('Could not automatically set up PATH');
1568
+ }
1569
+ }
1570
+ }
1571
+
1470
1572
  // Provide immediate access to tribe command
1471
- log.info('TRIBE command is now available globally!');
1472
- console.log(chalk.green(' tribe # Run from anywhere'));
1473
- console.log(chalk.gray(' tribe --help # View available commands'));
1474
- console.log(chalk.gray(' tribe status # Check cluster status'));
1573
+ if (global.pathSetupComplete) {
1574
+ log.info('TRIBE is now in your PATH!');
1575
+ console.log('');
1576
+ console.log(chalk.bold('To start using tribe:'));
1577
+ console.log(chalk.green(' source ~/.tribe/tribe-env.sh # For current session'));
1578
+ console.log(chalk.green(' tribe # In new terminal'));
1579
+ } else if (global.envScriptCreated) {
1580
+ log.info('TRIBE command is installed!');
1581
+ console.log('');
1582
+ console.log(chalk.bold('To add "tribe" to your PATH:'));
1583
+ console.log(chalk.green(' npx @_xtribe/cli setup-path'));
1584
+ console.log('');
1585
+ console.log(chalk.bold('Or for this session only:'));
1586
+ console.log(chalk.green(' source ~/.tribe/tribe-env.sh'));
1587
+ console.log('');
1588
+ console.log(chalk.bold('Or use the full path:'));
1589
+ console.log(chalk.green(' ~/.tribe/bin/tribe'));
1590
+ } else {
1591
+ log.info('TRIBE command is installed!');
1592
+ console.log(chalk.green(' ~/.tribe/bin/tribe # Run TRIBE'));
1593
+ console.log(chalk.gray(' ~/.tribe/bin/tribe --help # View available commands'));
1594
+ console.log(chalk.gray(' ~/.tribe/bin/tribe status # Check cluster status'));
1595
+ }
1475
1596
  console.log('');
1476
1597
 
1477
1598
  log.info('Quick start:');
@@ -1495,8 +1616,18 @@ async function main() {
1495
1616
  console.log('');
1496
1617
 
1497
1618
  // Provide immediate access to tribe command
1498
- log.info('TRIBE command is now available globally!');
1499
- console.log(chalk.green(' tribe # Run from anywhere'));
1619
+ if (global.envScriptCreated) {
1620
+ log.info('TRIBE command is installed!');
1621
+ console.log('');
1622
+ console.log(chalk.bold('To use "tribe" command from anywhere:'));
1623
+ console.log(chalk.green(' source ~/.tribe/tribe-env.sh'));
1624
+ console.log('');
1625
+ console.log(chalk.bold('Or you can use the full path:'));
1626
+ console.log(chalk.green(' ~/.tribe/bin/tribe # Run TRIBE'));
1627
+ } else {
1628
+ log.info('TRIBE command is installed!');
1629
+ console.log(chalk.green(' ~/.tribe/bin/tribe # Run TRIBE'));
1630
+ }
1500
1631
  console.log('');
1501
1632
 
1502
1633
  log.info('Commands:');
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@_xtribe/cli",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "description": "TRIBE multi-agent development system - Zero to productive with one command",
5
5
  "main": "install-tribe.js",
6
6
  "bin": {
7
- "install-tribe": "./install-tribe.js"
7
+ "install-tribe": "./install-tribe.js",
8
+ "setup-path": "./setup-path.js"
8
9
  },
9
10
  "scripts": {
10
11
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -35,6 +36,7 @@
35
36
  },
36
37
  "files": [
37
38
  "install-tribe.js",
39
+ "setup-path.js",
38
40
  "README.md",
39
41
  "package.json"
40
42
  ],
package/setup-path.js ADDED
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const os = require('os');
5
+ const path = require('path');
6
+ const chalk = require('chalk');
7
+
8
+ const homeDir = os.homedir();
9
+ const tribeEnvPath = path.join(homeDir, '.tribe', 'tribe-env.sh');
10
+
11
+ // Detect user's shell
12
+ function detectShell() {
13
+ const shell = process.env.SHELL || '';
14
+ if (shell.includes('zsh')) return 'zsh';
15
+ if (shell.includes('bash')) return 'bash';
16
+ if (shell.includes('fish')) return 'fish';
17
+ return 'unknown';
18
+ }
19
+
20
+ // Get shell config file
21
+ function getShellConfigFile() {
22
+ const shell = detectShell();
23
+ const configFiles = {
24
+ 'zsh': ['.zshrc', '.zprofile'],
25
+ 'bash': ['.bashrc', '.bash_profile', '.profile'],
26
+ 'fish': ['.config/fish/config.fish']
27
+ };
28
+
29
+ const candidates = configFiles[shell] || [];
30
+
31
+ // Check which config file exists
32
+ for (const file of candidates) {
33
+ const fullPath = path.join(homeDir, file);
34
+ if (fs.existsSync(fullPath)) {
35
+ return fullPath;
36
+ }
37
+ }
38
+
39
+ // If none exist, use the first candidate
40
+ if (candidates.length > 0) {
41
+ return path.join(homeDir, candidates[0]);
42
+ }
43
+
44
+ return null;
45
+ }
46
+
47
+ function setupPath() {
48
+ console.log(chalk.bold('🔧 TRIBE PATH Setup\n'));
49
+
50
+ // Check if tribe-env.sh exists
51
+ if (!fs.existsSync(tribeEnvPath)) {
52
+ console.log(chalk.red('Error: TRIBE environment script not found.'));
53
+ console.log('Please run the installer first: npx @_xtribe/cli');
54
+ process.exit(1);
55
+ }
56
+
57
+ const shellConfig = getShellConfigFile();
58
+ const shell = detectShell();
59
+
60
+ if (!shellConfig) {
61
+ console.log(chalk.yellow('Could not detect shell configuration file.'));
62
+ console.log('\nPlease manually add this line to your shell config:');
63
+ console.log(chalk.green(` source ~/.tribe/tribe-env.sh`));
64
+ return;
65
+ }
66
+
67
+ // Check if already added
68
+ try {
69
+ const content = fs.readFileSync(shellConfig, 'utf8');
70
+ if (content.includes('source ~/.tribe/tribe-env.sh') ||
71
+ content.includes('source $HOME/.tribe/tribe-env.sh') ||
72
+ content.includes('. ~/.tribe/tribe-env.sh')) {
73
+ console.log(chalk.green('✓ PATH already configured!'));
74
+ console.log('\nTo use tribe in your current session:');
75
+ console.log(chalk.green(` source ~/.tribe/tribe-env.sh`));
76
+ return;
77
+ }
78
+ } catch (e) {
79
+ // File doesn't exist yet, that's ok
80
+ }
81
+
82
+ // Add to shell config
83
+ const sourceCommand = '\n# TRIBE CLI\nsource ~/.tribe/tribe-env.sh\n';
84
+
85
+ try {
86
+ fs.appendFileSync(shellConfig, sourceCommand);
87
+ console.log(chalk.green(`✓ Added TRIBE to your ${shell} configuration!`));
88
+ console.log(` Configuration file: ${shellConfig}`);
89
+ console.log('\nTo use tribe in your current session:');
90
+ console.log(chalk.green(` source ~/.tribe/tribe-env.sh`));
91
+ console.log('\nOr open a new terminal window.');
92
+ } catch (error) {
93
+ console.log(chalk.red(`Error updating ${shellConfig}: ${error.message}`));
94
+ console.log('\nPlease manually add this line to your shell config:');
95
+ console.log(chalk.green(` source ~/.tribe/tribe-env.sh`));
96
+ }
97
+ }
98
+
99
+ // Export for use by other modules
100
+ module.exports = setupPath;
101
+
102
+ // Add to package.json bin entry
103
+ if (require.main === module) {
104
+ setupPath();
105
+ }