@_xtribe/cli 1.0.9 → 1.0.11

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
  }
@@ -156,7 +183,7 @@ async function installK3s() {
156
183
  if (isContainer) {
157
184
  spinner.warn('Running in container/CI - K3s installation skipped');
158
185
  log.info('K3s requires systemd and privileged access');
159
- log.info('For containers, consider using KIND or external cluster');
186
+ log.info('For containers, consider using an external cluster');
160
187
  return true; // Don't fail in containers
161
188
  }
162
189
 
@@ -274,7 +301,6 @@ async function installColima() {
274
301
 
275
302
  // Lima installation removed - Colima handles virtualization
276
303
 
277
- // KIND installation removed - using Colima's built-in Kubernetes
278
304
 
279
305
  async function installKubectl() {
280
306
  // Linux with K3s already has kubectl
@@ -319,6 +345,53 @@ async function installKubectl() {
319
345
  }
320
346
  }
321
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
+
322
395
  async function setupGlobalNpmCommand() {
323
396
  const spinner = ora('Setting up global tribe command...').start();
324
397
 
@@ -546,7 +619,7 @@ async function installTribeCLI() {
546
619
  // Multiple sources for reliability
547
620
  // For testing, use a direct URL to our releases folder
548
621
  const isTestEnv = process.env.TRIBE_TEST_BINARY_URL;
549
- const githubRepo = process.env.TRIBE_INSTALLER_REPO || 'TRIBE-INC/releases';
622
+ const githubRepo = process.env.TRIBE_INSTALLER_REPO || 'TRIBE-INC/0zen';
550
623
 
551
624
  let downloadUrl = '';
552
625
 
@@ -583,6 +656,10 @@ async function installTribeCLI() {
583
656
  downloadUrl,
584
657
  // GitHub releases direct download (fallback if API fails)
585
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}`,
586
663
  // Local build if available
587
664
  ...(hasLocalBuild ? [`file://${localBuildPath}`] : [])
588
665
  ].filter(Boolean); // Filter out empty downloadUrl if API failed
@@ -642,6 +719,16 @@ async function installTribeCLI() {
642
719
 
643
720
  fs.chmodSync(tribeDest, '755');
644
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
+
645
732
  // Verify the binary works
646
733
  try {
647
734
  // Try version command first
@@ -724,58 +811,22 @@ async function startContainerRuntime() {
724
811
  // Colima not running, need to start it
725
812
  }
726
813
 
727
- // Try to start Colima
814
+ // Don't try to start Colima automatically - it takes too long
728
815
  if (await checkCommand('colima')) {
729
- const spinner = ora('Starting Colima container runtime...').start();
730
-
731
- try {
732
- // Strategy 1: Quick start with minimal resources and Kubernetes
733
- spinner.text = 'Starting Colima with Kubernetes...';
734
- const colimaPath = await findCommand('colima') || path.join(binDir, 'colima');
735
- execSync(`${colimaPath} start --cpu 2 --memory 4 --disk 10 --kubernetes --vm-type=vz`, {
736
- stdio: 'pipe',
737
- timeout: 60000 // 60 second timeout for K8s
738
- });
739
-
740
- spinner.succeed('Colima started successfully');
741
- return true;
742
-
743
- } catch (error) {
744
- // Strategy 2: Start in background with Kubernetes
745
- try {
746
- spinner.text = 'Starting Colima with Kubernetes in background...';
747
- const colimaPath = await findCommand('colima') || path.join(binDir, 'colima');
748
- const child = spawn(colimaPath, ['start', '--cpu', '2', '--memory', '4', '--disk', '10', '--kubernetes'], {
749
- detached: true,
750
- stdio: 'ignore',
751
- env: { ...process.env, PATH: `${binDir}:${process.env.PATH}` }
752
- });
753
- child.unref(); // Don't wait for completion
754
-
755
- spinner.succeed('Colima startup initiated (background)');
756
- log.info('Colima is starting in the background');
757
- log.info('Run "colima status" to check progress');
758
- return true;
759
-
760
- } catch (bgError) {
761
- spinner.fail('Failed to start Colima');
762
- log.warning('Container runtime startup failed (likely due to macOS system restrictions)');
763
- log.info('Options to fix:');
764
- log.info('');
765
- log.info('Option 1 - Manual Colima start:');
766
- log.info(' colima start --cpu 2 --memory 4 --kubernetes');
767
- log.info(' # This downloads a 344MB disk image (may take time)');
768
- log.info('');
769
- log.info('Option 2 - Use Homebrew (recommended):');
770
- log.info(' brew install colima');
771
- log.info(' colima start --kubernetes');
772
- log.info('');
773
- return false;
774
- }
775
- }
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;
776
827
  }
777
828
  } else if (platform === 'linux') {
778
- // Linux - K3s should already be running
829
+ // Linux - Check K3s status but don't try to start it
779
830
  spinner.text = 'Checking K3s status...';
780
831
 
781
832
  // Check if we're in a container (no systemd)
@@ -783,27 +834,23 @@ async function startContainerRuntime() {
783
834
 
784
835
  if (isContainer) {
785
836
  spinner.info('Running in container - K3s requires systemd');
786
- log.info('For containers, use KIND or connect to external cluster');
837
+ log.info('For containers, connect to an external cluster');
787
838
  return true; // Don't fail
788
839
  }
789
840
 
790
841
  try {
791
- execSync('sudo systemctl is-active --quiet k3s', { stdio: 'ignore' });
842
+ execSync('sudo systemctl is-active --quiet k3s', { stdio: 'ignore', timeout: 5000 });
792
843
  spinner.succeed('K3s is running');
793
844
  return true;
794
845
  } catch {
795
- // Try to start K3s
796
- spinner.text = 'Starting K3s...';
797
- try {
798
- execSync('sudo systemctl start k3s', { stdio: 'ignore' });
799
- await new Promise(resolve => setTimeout(resolve, 5000));
800
- spinner.succeed('K3s started');
801
- return true;
802
- } catch (error) {
803
- spinner.warn('K3s not available - requires systemd');
804
- log.info('K3s requires systemd. Install K3s manually or use KIND/external cluster');
805
- return true; // Don't fail
806
- }
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
807
854
  }
808
855
  } else if (platform === 'win32') {
809
856
  spinner.fail('Windows support coming soon!');
@@ -863,10 +910,26 @@ async function verifyInstallation() {
863
910
  spinner.stop();
864
911
 
865
912
  console.log('\n' + chalk.bold('Installation Summary:'));
866
- tools.forEach(tool => {
913
+ for (const tool of tools) {
867
914
  const status = results[tool] ? chalk.green('✓') : chalk.red('✗');
868
- console.log(`${status} ${tool}`);
869
- });
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
+ }
870
933
 
871
934
  const extraStatus = containerWorking ? chalk.green('✓') : chalk.yellow('⚠');
872
935
  console.log(`${extraStatus} Container runtime`);
@@ -1174,6 +1237,21 @@ async function deployTribeCluster() {
1174
1237
  }
1175
1238
  }
1176
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
+
1177
1255
  async function promptForClusterSetup() {
1178
1256
  // Check for auto-approve in CI environments
1179
1257
  if (process.env.CI === 'true' || process.env.TRIBE_AUTO_APPROVE === 'true') {
@@ -1378,7 +1456,7 @@ async function main() {
1378
1456
 
1379
1457
  // 4. Check for existing Kubernetes installations
1380
1458
  const k8sTools = [];
1381
- const checkTools = ['minikube', 'microk8s', 'kind', 'k3d', 'kubectl'];
1459
+ const checkTools = ['minikube', 'microk8s', 'k3d', 'kubectl'];
1382
1460
  for (const tool of checkTools) {
1383
1461
  try {
1384
1462
  execSync(`which ${tool}`, { stdio: 'ignore' });
@@ -1422,7 +1500,8 @@ async function main() {
1422
1500
  tasks.push(
1423
1501
  { name: 'kubectl', fn: installKubectl },
1424
1502
  { name: 'TRIBE CLI', fn: installTribeCLI },
1425
- { 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
1426
1505
  );
1427
1506
 
1428
1507
  let allSuccess = true;
@@ -1430,15 +1509,20 @@ async function main() {
1430
1509
  for (const task of tasks) {
1431
1510
  log.step(`Installing ${task.name}...`);
1432
1511
  const success = await task.fn();
1433
- if (!success) allSuccess = false;
1512
+ if (!success) {
1513
+ allSuccess = false;
1514
+ if (task.name === 'PATH environment') {
1515
+ global.envScriptCreated = false;
1516
+ }
1517
+ }
1434
1518
  }
1435
1519
 
1436
- // Try to start container runtime
1437
- await startContainerRuntime();
1438
-
1439
- // Verify everything
1520
+ // Verify everything first
1440
1521
  const verified = await verifyInstallation();
1441
1522
 
1523
+ // Try to start container runtime (after showing results)
1524
+ await startContainerRuntime();
1525
+
1442
1526
  if (verified) {
1443
1527
  // Check if cluster already exists
1444
1528
  const clusterExists = await checkClusterExists();
@@ -1468,11 +1552,47 @@ async function main() {
1468
1552
  console.log('\n' + chalk.bold.green('✨ TRIBE is ready!'));
1469
1553
  console.log('');
1470
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
+
1471
1572
  // Provide immediate access to tribe command
1472
- log.info('TRIBE command is now available globally!');
1473
- console.log(chalk.green(' tribe # Run from anywhere'));
1474
- console.log(chalk.gray(' tribe --help # View available commands'));
1475
- 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
+ }
1476
1596
  console.log('');
1477
1597
 
1478
1598
  log.info('Quick start:');
@@ -1496,8 +1616,18 @@ async function main() {
1496
1616
  console.log('');
1497
1617
 
1498
1618
  // Provide immediate access to tribe command
1499
- log.info('TRIBE command is now available globally!');
1500
- 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
+ }
1501
1631
  console.log('');
1502
1632
 
1503
1633
  log.info('Commands:');
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@_xtribe/cli",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
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
+ }