@_xtribe/cli 2.2.3 → 2.2.8

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.
@@ -0,0 +1,457 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const os = require('os');
6
+ const { execSync } = require('child_process');
7
+ const chalk = require('chalk');
8
+ const ora = require('ora');
9
+
10
+ const homeDir = os.homedir();
11
+ const tribeDir = path.join(homeDir, '.tribe');
12
+ const tribeBinDir = path.join(tribeDir, 'bin');
13
+ const envScriptPath = path.join(tribeDir, 'tribe-env.sh');
14
+
15
+ /**
16
+ * Enhanced PATH setup with comprehensive edge case handling
17
+ */
18
+ class EnhancedPathSetup {
19
+ constructor() {
20
+ this.detectedShell = this.detectShell();
21
+ this.shellConfigs = this.getShellConfigs();
22
+ this.backupDir = path.join(tribeDir, 'backups');
23
+ this.installId = Date.now().toString();
24
+ }
25
+
26
+ /**
27
+ * Comprehensive shell detection
28
+ */
29
+ detectShell() {
30
+ const shell = process.env.SHELL || '';
31
+ const parentProcess = process.env._ || '';
32
+
33
+ // Check various indicators
34
+ if (shell.includes('zsh') || parentProcess.includes('zsh')) {
35
+ return 'zsh';
36
+ } else if (shell.includes('bash') || parentProcess.includes('bash')) {
37
+ return 'bash';
38
+ } else if (shell.includes('fish') || parentProcess.includes('fish')) {
39
+ return 'fish';
40
+ } else if (shell.includes('tcsh') || shell.includes('csh')) {
41
+ return 'csh';
42
+ } else if (process.platform === 'win32') {
43
+ return 'powershell';
44
+ }
45
+
46
+ // Fallback: try to detect from system
47
+ try {
48
+ const userShell = execSync('echo $0', { encoding: 'utf8', timeout: 1000 }).trim();
49
+ if (userShell.includes('zsh')) return 'zsh';
50
+ if (userShell.includes('bash')) return 'bash';
51
+ if (userShell.includes('fish')) return 'fish';
52
+ } catch (e) {
53
+ // Ignore detection failures
54
+ }
55
+
56
+ return 'bash'; // Safe fallback
57
+ }
58
+
59
+ /**
60
+ * Get shell configuration files in priority order
61
+ */
62
+ getShellConfigs() {
63
+ const configs = {
64
+ zsh: [
65
+ '.zshrc',
66
+ '.zprofile',
67
+ '.zshenv',
68
+ '.profile'
69
+ ],
70
+ bash: [
71
+ '.bashrc',
72
+ '.bash_profile',
73
+ '.profile',
74
+ '.bash_login'
75
+ ],
76
+ fish: [
77
+ '.config/fish/config.fish'
78
+ ],
79
+ csh: [
80
+ '.cshrc',
81
+ '.tcshrc'
82
+ ],
83
+ powershell: [
84
+ 'Documents/PowerShell/Microsoft.PowerShell_profile.ps1',
85
+ 'Documents/WindowsPowerShell/Microsoft.PowerShell_profile.ps1'
86
+ ]
87
+ };
88
+
89
+ return configs[this.detectedShell] || configs.bash;
90
+ }
91
+
92
+ /**
93
+ * Create enhanced environment script with better detection
94
+ */
95
+ createEnvScript() {
96
+ let envScript;
97
+
98
+ if (this.detectedShell === 'fish') {
99
+ envScript = `# TRIBE CLI Environment (Fish Shell)
100
+ set -l tribe_bin_dir "$HOME/.tribe/bin"
101
+ if test -d "$tribe_bin_dir"
102
+ if not contains "$tribe_bin_dir" $PATH
103
+ set -gx PATH "$tribe_bin_dir" $PATH
104
+ end
105
+ end`;
106
+ } else if (this.detectedShell === 'csh') {
107
+ envScript = `# TRIBE CLI Environment (C Shell)
108
+ set tribe_bin_dir = "$HOME/.tribe/bin"
109
+ if ( -d "$tribe_bin_dir" ) then
110
+ echo ":$PATH:" | grep -q ":$tribe_bin_dir:" || setenv PATH "$tribe_bin_dir:$PATH"
111
+ endif`;
112
+ } else if (this.detectedShell === 'powershell') {
113
+ envScript = `# TRIBE CLI Environment (PowerShell)
114
+ $TribeBinDir = "$env:USERPROFILE\\.tribe\\bin"
115
+ if (Test-Path $TribeBinDir) {
116
+ if (-not ($env:PATH -split ';' -contains $TribeBinDir)) {
117
+ $env:PATH = "$TribeBinDir;$env:PATH"
118
+ }
119
+ }`;
120
+ } else {
121
+ // Bash/Zsh with enhanced detection
122
+ envScript = `#!/bin/bash
123
+ # TRIBE CLI Environment
124
+ # Generated on $(date) - Install ID: ${this.installId}
125
+
126
+ TRIBE_BIN_DIR="$HOME/.tribe/bin"
127
+
128
+ # Enhanced PATH detection - handles edge cases
129
+ if [[ -d "$TRIBE_BIN_DIR" ]]; then
130
+ # Check if already in PATH using multiple methods
131
+ case ":$PATH:" in
132
+ *":$TRIBE_BIN_DIR:"*)
133
+ # Already in PATH, do nothing
134
+ ;;
135
+ *)
136
+ # Add to PATH
137
+ export PATH="$TRIBE_BIN_DIR:$PATH"
138
+ ;;
139
+ esac
140
+ fi
141
+
142
+ # Validate installation
143
+ if command -v tribe >/dev/null 2>&1; then
144
+ # Optional: Set additional TRIBE environment variables
145
+ export TRIBE_HOME="$HOME/.tribe"
146
+ export TRIBE_VERSION="$(tribe version 2>/dev/null || echo 'unknown')"
147
+ fi`;
148
+ }
149
+
150
+ fs.writeFileSync(envScriptPath, envScript);
151
+ if (this.detectedShell !== 'powershell') {
152
+ fs.chmodSync(envScriptPath, '755');
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Backup existing shell config before modification
158
+ */
159
+ backupShellConfig(configPath) {
160
+ if (!fs.existsSync(this.backupDir)) {
161
+ fs.mkdirSync(this.backupDir, { recursive: true });
162
+ }
163
+
164
+ const configName = path.basename(configPath);
165
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
166
+ const backupPath = path.join(this.backupDir, `${configName}.backup.${timestamp}`);
167
+
168
+ try {
169
+ fs.copyFileSync(configPath, backupPath);
170
+ return backupPath;
171
+ } catch (error) {
172
+ console.warn(`Could not backup ${configPath}:`, error.message);
173
+ return null;
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Smart shell config modification with duplicate detection
179
+ */
180
+ updateShellConfig() {
181
+ const indicators = [
182
+ 'TRIBE CLI',
183
+ 'tribe-env.sh',
184
+ '.tribe/bin',
185
+ 'source ~/.tribe',
186
+ 'TRIBE_BIN_DIR'
187
+ ];
188
+
189
+ for (const configFile of this.shellConfigs) {
190
+ const configPath = path.join(homeDir, configFile);
191
+
192
+ if (!fs.existsSync(configPath)) {
193
+ // Create config file if it doesn't exist (for primary configs only)
194
+ if (configFile === this.shellConfigs[0]) {
195
+ try {
196
+ // Create parent directory if needed
197
+ const configDir = path.dirname(configPath);
198
+ if (!fs.existsSync(configDir)) {
199
+ fs.mkdirSync(configDir, { recursive: true });
200
+ }
201
+ fs.writeFileSync(configPath, '# Created by TRIBE installer\n');
202
+ } catch (error) {
203
+ continue; // Skip if can't create
204
+ }
205
+ } else {
206
+ continue; // Skip optional configs that don't exist
207
+ }
208
+ }
209
+
210
+ try {
211
+ const content = fs.readFileSync(configPath, 'utf8');
212
+
213
+ // Check for existing TRIBE configuration
214
+ const hasExisting = indicators.some(indicator =>
215
+ content.includes(indicator)
216
+ );
217
+
218
+ if (hasExisting) {
219
+ console.log(chalk.yellow(`⚠️ TRIBE already configured in ${configFile}`));
220
+
221
+ // Check if it's working
222
+ if (this.testPathSetup()) {
223
+ console.log(chalk.green(`✅ Existing configuration is working`));
224
+ return true;
225
+ } else {
226
+ console.log(chalk.yellow(`🔧 Existing configuration needs fixing`));
227
+ this.cleanupOldEntries(configPath, content);
228
+ }
229
+ }
230
+
231
+ // Backup before modification
232
+ this.backupShellConfig(configPath);
233
+
234
+ // Add new configuration
235
+ const sourceLine = this.getSourceLine();
236
+ const configBlock = `
237
+ # TRIBE CLI - Added ${new Date().toISOString()}
238
+ ${sourceLine}`;
239
+
240
+ fs.appendFileSync(configPath, configBlock);
241
+ console.log(chalk.green(`✅ Updated ${configFile}`));
242
+ return true;
243
+
244
+ } catch (error) {
245
+ console.warn(chalk.yellow(`⚠️ Could not update ${configFile}: ${error.message}`));
246
+ continue;
247
+ }
248
+ }
249
+
250
+ return false;
251
+ }
252
+
253
+ /**
254
+ * Get appropriate source line for shell type
255
+ */
256
+ getSourceLine() {
257
+ if (this.detectedShell === 'fish') {
258
+ return 'source ~/.tribe/tribe-env.sh';
259
+ } else if (this.detectedShell === 'csh') {
260
+ return 'source ~/.tribe/tribe-env.sh';
261
+ } else if (this.detectedShell === 'powershell') {
262
+ return '. $env:USERPROFILE\\.tribe\\tribe-env.ps1';
263
+ } else {
264
+ return 'source ~/.tribe/tribe-env.sh';
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Clean up duplicate or broken TRIBE entries
270
+ */
271
+ cleanupOldEntries(configPath, content) {
272
+ const lines = content.split('\n');
273
+ const cleanedLines = [];
274
+ let skipNext = false;
275
+ let removedCount = 0;
276
+
277
+ for (let i = 0; i < lines.length; i++) {
278
+ const line = lines[i];
279
+
280
+ if (skipNext) {
281
+ skipNext = false;
282
+ removedCount++;
283
+ continue;
284
+ }
285
+
286
+ // Skip old TRIBE entries
287
+ if (line.includes('TRIBE CLI') ||
288
+ line.includes('tribe-env.sh') ||
289
+ line.includes('export PATH=') && line.includes('.tribe/bin') ||
290
+ line.includes('TRIBE_BIN_DIR')) {
291
+
292
+ removedCount++;
293
+
294
+ // Check if next line is related
295
+ if (i + 1 < lines.length &&
296
+ (lines[i + 1].includes('tribe') || lines[i + 1].trim() === '')) {
297
+ skipNext = true;
298
+ }
299
+ continue;
300
+ }
301
+
302
+ cleanedLines.push(line);
303
+ }
304
+
305
+ if (removedCount > 0) {
306
+ fs.writeFileSync(configPath, cleanedLines.join('\n'));
307
+ console.log(chalk.blue(`🧹 Cleaned up ${removedCount} old TRIBE entries`));
308
+ }
309
+ }
310
+
311
+ /**
312
+ * Test if PATH setup is working
313
+ */
314
+ testPathSetup() {
315
+ try {
316
+ // Test if tribe command is accessible
317
+ const tribePath = path.join(tribeBinDir, 'tribe');
318
+ if (!fs.existsSync(tribePath)) {
319
+ return false;
320
+ }
321
+
322
+ // Test if it's in PATH
323
+ const whichResult = execSync('which tribe 2>/dev/null || echo ""', {
324
+ encoding: 'utf8',
325
+ timeout: 2000
326
+ }).trim();
327
+
328
+ return whichResult.includes('.tribe/bin/tribe');
329
+ } catch (error) {
330
+ return false;
331
+ }
332
+ }
333
+
334
+ /**
335
+ * Provide comprehensive installation summary
336
+ */
337
+ generateInstallSummary() {
338
+ const summary = {
339
+ shell: this.detectedShell,
340
+ pathWorking: this.testPathSetup(),
341
+ envScriptExists: fs.existsSync(envScriptPath),
342
+ tribeBinaryExists: fs.existsSync(path.join(tribeBinDir, 'tribe')),
343
+ configuredFiles: [],
344
+ instructions: []
345
+ };
346
+
347
+ // Check which configs were modified
348
+ for (const configFile of this.shellConfigs) {
349
+ const configPath = path.join(homeDir, configFile);
350
+ if (fs.existsSync(configPath)) {
351
+ const content = fs.readFileSync(configPath, 'utf8');
352
+ if (content.includes('tribe-env.sh')) {
353
+ summary.configuredFiles.push(configFile);
354
+ }
355
+ }
356
+ }
357
+
358
+ // Generate specific instructions
359
+ if (!summary.pathWorking) {
360
+ summary.instructions.push(
361
+ `Restart your terminal or run: source ~/.tribe/tribe-env.sh`
362
+ );
363
+
364
+ if (summary.configuredFiles.length === 0) {
365
+ summary.instructions.push(
366
+ `Manual setup required. Add to your shell config:\n source ~/.tribe/tribe-env.sh`
367
+ );
368
+ }
369
+ }
370
+
371
+ return summary;
372
+ }
373
+
374
+ /**
375
+ * Main setup function
376
+ */
377
+ async setup() {
378
+ const spinner = ora('🔧 Setting up enhanced PATH configuration...').start();
379
+
380
+ try {
381
+ // 1. Create enhanced environment script
382
+ this.createEnvScript();
383
+ spinner.text = 'Environment script created...';
384
+
385
+ // 2. Update shell configuration
386
+ const configSuccess = this.updateShellConfig();
387
+ spinner.text = 'Shell configuration updated...';
388
+
389
+ // 3. Test setup
390
+ await new Promise(resolve => setTimeout(resolve, 500)); // Brief pause for file system
391
+ const pathWorking = this.testPathSetup();
392
+
393
+ // 4. Generate summary
394
+ const summary = this.generateInstallSummary();
395
+
396
+ if (pathWorking && configSuccess) {
397
+ spinner.succeed('PATH setup completed successfully');
398
+
399
+ console.log(chalk.green('\n✅ TRIBE CLI is ready to use!'));
400
+ console.log(chalk.blue(` Shell: ${summary.shell}`));
401
+ console.log(chalk.blue(` Configured: ${summary.configuredFiles.join(', ')}`));
402
+
403
+ if (summary.instructions.length > 0) {
404
+ console.log(chalk.yellow('\n📝 Next steps:'));
405
+ summary.instructions.forEach(instruction => {
406
+ console.log(chalk.cyan(` ${instruction}`));
407
+ });
408
+ }
409
+ } else {
410
+ spinner.warn('PATH setup completed with warnings');
411
+
412
+ console.log(chalk.yellow('\n⚠️ Manual configuration may be needed'));
413
+ console.log(chalk.blue(' Try running: source ~/.tribe/tribe-env.sh'));
414
+ console.log(chalk.blue(' Or restart your terminal'));
415
+
416
+ if (summary.instructions.length > 0) {
417
+ console.log(chalk.yellow('\n📝 Manual steps:'));
418
+ summary.instructions.forEach(instruction => {
419
+ console.log(chalk.cyan(` ${instruction}`));
420
+ });
421
+ }
422
+ }
423
+
424
+ // Always provide troubleshooting info
425
+ console.log(chalk.gray('\n🔍 Troubleshooting:'));
426
+ console.log(chalk.gray(' Config backups: ~/.tribe/backups/'));
427
+ console.log(chalk.gray(' Test command: which tribe'));
428
+ console.log(chalk.gray(' Manual setup: export PATH="$HOME/.tribe/bin:$PATH"'));
429
+
430
+ return { success: pathWorking && configSuccess, summary };
431
+
432
+ } catch (error) {
433
+ spinner.fail('PATH setup failed');
434
+ console.error(chalk.red(`Error: ${error.message}`));
435
+
436
+ // Provide fallback instructions
437
+ console.log(chalk.yellow('\n🚨 Fallback instructions:'));
438
+ console.log(chalk.cyan(' 1. Add to your shell config (~/.zshrc or ~/.bashrc):'));
439
+ console.log(chalk.cyan(' export PATH="$HOME/.tribe/bin:$PATH"'));
440
+ console.log(chalk.cyan(' 2. Restart your terminal'));
441
+ console.log(chalk.cyan(' 3. Test with: tribe --version'));
442
+
443
+ return { success: false, error: error.message };
444
+ }
445
+ }
446
+ }
447
+
448
+ // Export for use in main installer
449
+ module.exports = { EnhancedPathSetup };
450
+
451
+ // Run if executed directly
452
+ if (require.main === module) {
453
+ const setup = new EnhancedPathSetup();
454
+ setup.setup().then(result => {
455
+ process.exit(result.success ? 0 : 1);
456
+ });
457
+ }
package/install-tribe.js CHANGED
@@ -7,6 +7,7 @@ const https = require('https');
7
7
  const { execSync } = require('child_process');
8
8
  const chalk = require('chalk');
9
9
  const ora = require('ora');
10
+ const { EnhancedPathSetup } = require('./enhanced-path-setup');
10
11
 
11
12
  // ASCII art for TRIBE
12
13
  const asciiArt = `
@@ -162,69 +163,18 @@ async function installTribeCLIQuiet() {
162
163
  }
163
164
 
164
165
  async function setupPath() {
165
- const spinner = ora('Setting up PATH...').start();
166
-
167
- try {
168
- // Create PATH script
169
- const envScript = `#!/bin/bash
170
- # TRIBE CLI Environment
171
- TRIBE_BIN_DIR="$HOME/.tribe/bin"
172
- if [[ -d "$TRIBE_BIN_DIR" ]] && [[ ":$PATH:" != *":$TRIBE_BIN_DIR:"* ]]; then
173
- export PATH="$TRIBE_BIN_DIR:$PATH"
174
- fi`;
175
-
176
- const envScriptPath = path.join(tribeDir, 'tribe-env.sh');
177
- fs.writeFileSync(envScriptPath, envScript);
178
- fs.chmodSync(envScriptPath, '755');
179
-
180
- // Try to add to shell config
181
- const shellConfig = process.env.SHELL?.includes('zsh') ? '.zshrc' : '.bashrc';
182
- const rcPath = path.join(homeDir, shellConfig);
183
- const sourceLine = 'source ~/.tribe/tribe-env.sh';
184
-
185
- if (fs.existsSync(rcPath)) {
186
- const content = fs.readFileSync(rcPath, 'utf8');
187
- if (!content.includes(sourceLine)) {
188
- fs.appendFileSync(rcPath, `\n# TRIBE CLI\n${sourceLine}\n`);
189
- }
190
- }
191
-
192
- spinner.succeed('Environment ready');
193
- return true;
194
- } catch (error) {
195
- spinner.warn('PATH setup needs manual configuration');
196
- console.log(chalk.yellow('\nTo use tribe command, run:'));
197
- console.log(chalk.cyan(' source ~/.tribe/tribe-env.sh\n'));
198
- return false;
199
- }
166
+ const pathSetup = new EnhancedPathSetup();
167
+ const result = await pathSetup.setup();
168
+ return result.success;
200
169
  }
201
170
 
202
171
  async function setupPathQuiet() {
203
172
  try {
204
- // Create PATH script
205
- const envScript = `#!/bin/bash
206
- # TRIBE CLI Environment
207
- TRIBE_BIN_DIR="$HOME/.tribe/bin"
208
- if [[ -d "$TRIBE_BIN_DIR" ]] && [[ ":$PATH:" != *":$TRIBE_BIN_DIR:"* ]]; then
209
- export PATH="$TRIBE_BIN_DIR:$PATH"
210
- fi`;
211
-
212
- const envScriptPath = path.join(tribeDir, 'tribe-env.sh');
213
- fs.writeFileSync(envScriptPath, envScript);
214
- fs.chmodSync(envScriptPath, '755');
215
-
216
- // Try to add to shell config
217
- const shellConfig = process.env.SHELL?.includes('zsh') ? '.zshrc' : '.bashrc';
218
- const rcPath = path.join(homeDir, shellConfig);
219
- const sourceLine = 'source ~/.tribe/tribe-env.sh';
220
-
221
- if (fs.existsSync(rcPath)) {
222
- const content = fs.readFileSync(rcPath, 'utf8');
223
- if (!content.includes(sourceLine)) {
224
- fs.appendFileSync(rcPath, `\n# TRIBE CLI\n${sourceLine}\n`);
225
- }
173
+ const pathSetup = new EnhancedPathSetup();
174
+ const result = await pathSetup.setup();
175
+ if (!result.success) {
176
+ throw new Error(result.error || 'PATH setup failed');
226
177
  }
227
-
228
178
  return true;
229
179
  } catch (error) {
230
180
  throw new Error('PATH setup failed: ' + error.message);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@_xtribe/cli",
3
- "version": "2.2.3",
3
+ "version": "2.2.8",
4
4
  "description": "TRIBE - Track, measure and optimize your AI coding agents to become 10x faster",
5
5
  "main": "install-tribe.js",
6
6
  "bin": {
@@ -43,6 +43,7 @@
43
43
  "install-tribe-minimal.js",
44
44
  "install-tribe-autolaunch.js",
45
45
  "setup-path.js",
46
+ "enhanced-path-setup.js",
46
47
  "install.sh",
47
48
  "tribe-deployment.yaml",
48
49
  "README.md",