@_xtribe/cli 2.2.7 → 2.2.9

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
@@ -217,8 +217,25 @@ async function main() {
217
217
 
218
218
  spinner.succeed('Ready!');
219
219
 
220
- // Show completion
221
- console.log('Type ' + chalk.cyan.bold('tribe') + ' to get started');
220
+ // Test if tribe command works immediately
221
+ let commandWorks = false;
222
+ try {
223
+ execSync('which tribe', { stdio: 'ignore', timeout: 2000 });
224
+ commandWorks = true;
225
+ } catch (error) {
226
+ commandWorks = false;
227
+ }
228
+
229
+ // Show appropriate completion message
230
+ if (commandWorks) {
231
+ console.log(chalk.green('\nāœ… TRIBE CLI is ready to use!'));
232
+ console.log('Type ' + chalk.cyan.bold('tribe') + ' to get started');
233
+ } else {
234
+ console.log(chalk.yellow('\nāš ļø TRIBE CLI installed successfully!'));
235
+ console.log(chalk.blue('To use the tribe command, please restart your terminal'));
236
+ console.log(chalk.gray('or run: ') + chalk.cyan.bold('source ~/.tribe/tribe-env.sh'));
237
+ console.log(chalk.gray('\nThen type: ') + chalk.cyan.bold('tribe') + chalk.gray(' to get started'));
238
+ }
222
239
 
223
240
  } catch (error) {
224
241
  spinner.fail('Installation failed');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@_xtribe/cli",
3
- "version": "2.2.7",
3
+ "version": "2.2.9",
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",