@_xtribe/cli 2.0.7 → 2.1.1
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 +104 -439
- package/package.json +2 -2
package/install-tribe.js
CHANGED
|
@@ -4,39 +4,27 @@ const os = require('os');
|
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const https = require('https');
|
|
7
|
-
const { execSync
|
|
8
|
-
const readline = require('readline');
|
|
7
|
+
const { execSync } = require('child_process');
|
|
9
8
|
const chalk = require('chalk');
|
|
10
9
|
const ora = require('ora');
|
|
11
|
-
const which = require('which');
|
|
12
|
-
const fetch = require('node-fetch');
|
|
13
|
-
const crypto = require('crypto');
|
|
14
10
|
|
|
15
11
|
// ASCII art for TRIBE
|
|
16
12
|
const asciiArt = `
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
║ ╚═╝ ╚═╝ ╚═╝╚═╝╚═════╝ ╚══════╝ ║
|
|
25
|
-
║ ║
|
|
26
|
-
║ AI Development Teams That Make You 10x Faster ║
|
|
27
|
-
║ ║
|
|
28
|
-
╚══════════════════════════════════════════════════════════════════╝`;
|
|
13
|
+
_______ _______ ___ _______ _______
|
|
14
|
+
| || || | | || |
|
|
15
|
+
| ___|| _ || | | ___|| _ |
|
|
16
|
+
| |___ | | | || | | |___ | | | |
|
|
17
|
+
| ___|| |_| || | | ___|| |_| |
|
|
18
|
+
| | | || | | |___ | |
|
|
19
|
+
|___| |_______||___| |_______||_______|
|
|
29
20
|
|
|
30
|
-
|
|
31
|
-
const shownWarnings = new Set();
|
|
21
|
+
Learn AI Engineering • Maximum Impact • It Doesn't Have to Be Hard`;
|
|
32
22
|
|
|
33
23
|
const platform = os.platform();
|
|
34
|
-
const
|
|
35
|
-
const arch = nodeArch === 'x64' ? 'amd64' : (nodeArch === 'arm64' || nodeArch === 'aarch64' ? 'arm64' : nodeArch);
|
|
24
|
+
const arch = os.arch() === 'x64' ? 'amd64' : (os.arch() === 'arm64' ? 'arm64' : os.arch());
|
|
36
25
|
const homeDir = os.homedir();
|
|
37
26
|
const tribeDir = path.join(homeDir, '.tribe');
|
|
38
27
|
const tribeBinDir = path.join(tribeDir, 'bin');
|
|
39
|
-
const binDir = tribeBinDir;
|
|
40
28
|
|
|
41
29
|
// Ensure directories exist
|
|
42
30
|
if (!fs.existsSync(tribeDir)) {
|
|
@@ -46,110 +34,55 @@ if (!fs.existsSync(tribeBinDir)) {
|
|
|
46
34
|
fs.mkdirSync(tribeBinDir, { recursive: true });
|
|
47
35
|
}
|
|
48
36
|
|
|
49
|
-
const log = {
|
|
50
|
-
success: (msg) => console.log(chalk.green('✓'), msg),
|
|
51
|
-
error: (msg) => console.log(chalk.red('✗'), msg),
|
|
52
|
-
warning: (msg) => {
|
|
53
|
-
// Check if we've already shown this warning
|
|
54
|
-
if (!shownWarnings.has(msg)) {
|
|
55
|
-
console.log(chalk.yellow('⚠'), msg);
|
|
56
|
-
shownWarnings.add(msg);
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
info: (msg) => console.log(chalk.blue('ℹ'), msg),
|
|
60
|
-
step: (msg) => console.log(chalk.cyan('→'), msg)
|
|
61
|
-
};
|
|
62
|
-
|
|
63
37
|
function showWelcome() {
|
|
64
38
|
console.clear();
|
|
65
39
|
console.log(chalk.cyan(asciiArt));
|
|
66
|
-
console.log(chalk.bold('\n
|
|
67
|
-
console.log('
|
|
68
|
-
|
|
69
|
-
console.log('
|
|
70
|
-
console.log('
|
|
71
|
-
console.log('
|
|
72
|
-
console.log('
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
console.log(chalk.
|
|
40
|
+
console.log(chalk.bold('\n🎓 Welcome to TRIBE Tutor\n'));
|
|
41
|
+
console.log(chalk.bold('Your Personal AI Engineering Coach • Build Skills That Matter\n'));
|
|
42
|
+
|
|
43
|
+
console.log('TRIBE Tutor teaches you to master AI tools through hands-on learning:');
|
|
44
|
+
console.log(' 📚 ' + chalk.cyan('Context engineering mastery') + ' - Learn what context gets better results');
|
|
45
|
+
console.log(' 🎯 ' + chalk.cyan('Personalized coaching') + ' - Tailored lessons from your real usage');
|
|
46
|
+
console.log(' 🧠 ' + chalk.cyan('Skill development') + ' - From basic prompts to expert context engineering');
|
|
47
|
+
console.log(' ⚡ ' + chalk.cyan('Maximum impact') + ' - Focus on techniques that actually matter\n');
|
|
48
|
+
|
|
49
|
+
console.log(chalk.yellow('🚀 Everyone can learn AI engineering - we make it simple and practical'));
|
|
50
|
+
console.log(chalk.gray(`\nPlatform: ${platform} (${arch}) • Node: ${process.version}\n`));
|
|
76
51
|
}
|
|
77
52
|
|
|
78
53
|
function showInstallationSummary() {
|
|
79
54
|
console.log('\n' + '═'.repeat(70));
|
|
80
|
-
console.log(chalk.green.bold('\n
|
|
81
|
-
|
|
82
|
-
console.log('
|
|
83
|
-
console.log('
|
|
84
|
-
console.log('
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
console.log(chalk.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
console.log('
|
|
104
|
-
|
|
105
|
-
console.log(chalk.
|
|
106
|
-
console.log(chalk.
|
|
107
|
-
|
|
108
|
-
console.log(chalk.gray('Documentation: https://tribecode.ai/docs'));
|
|
109
|
-
console.log(chalk.gray('Support: https://tribecode.ai/support'));
|
|
55
|
+
console.log(chalk.green.bold('\n🎓 Your AI Engineering Coach is Ready!\n'));
|
|
56
|
+
|
|
57
|
+
console.log(chalk.cyan.bold('📚 Personalized Learning System Activated'));
|
|
58
|
+
console.log(chalk.gray(' Watching your AI workflow to create custom lessons just for you'));
|
|
59
|
+
console.log(chalk.gray(' 🔒 Privacy-first: We analyze patterns, not your code content\n'));
|
|
60
|
+
|
|
61
|
+
console.log(chalk.bold('⚡ How Your Skills Will Grow:'));
|
|
62
|
+
console.log(' 📖 ' + chalk.green('Learn from real examples') + ' - Your own coding sessions become lessons');
|
|
63
|
+
console.log(' 🎯 ' + chalk.green('Practice with purpose') + ' - Focus on what actually improves your work');
|
|
64
|
+
console.log(' 🧠 ' + chalk.green('Build expert intuition') + ' - Understand why some approaches work better\n');
|
|
65
|
+
|
|
66
|
+
console.log(chalk.bold.magenta('🛠️ AI Engineering Skills You\'ll Master:'));
|
|
67
|
+
console.log(' 🤖 Claude context engineering • ⚡ Cursor workflow design • 🚀 Multi-tool context switching • 💻 Advanced prompt architecture\n');
|
|
68
|
+
|
|
69
|
+
console.log(chalk.bold('🎯 Your Learning Path:'));
|
|
70
|
+
console.log(chalk.cyan.bold(' 1. tribe login') + chalk.gray(' # Start your personalized curriculum'));
|
|
71
|
+
console.log(chalk.cyan.bold(' 2. code normally') + chalk.gray(' # Every session teaches us about your style'));
|
|
72
|
+
console.log(chalk.cyan.bold(' 3. tribe insights') + chalk.gray(' # Get custom lessons based on your progress\n'));
|
|
73
|
+
|
|
74
|
+
console.log(chalk.bold('🚀 Skills You\'ll Develop:'));
|
|
75
|
+
console.log(' 🎨 ' + chalk.yellow('Context engineering that gets better results every time'));
|
|
76
|
+
console.log(' ⚡ ' + chalk.yellow('Workflow patterns that maximize your speed and context quality'));
|
|
77
|
+
console.log(' 🎯 ' + chalk.yellow('Context switching - when to use what tools and why'));
|
|
78
|
+
console.log(' 🧠 ' + chalk.yellow('AI engineering intuition that compounds over time\n'));
|
|
79
|
+
|
|
80
|
+
console.log(chalk.gray('Learning Dashboard: ') + chalk.cyan('https://tribecode.ai/tribe/learn'));
|
|
81
|
+
console.log(chalk.gray('Skill Progression: ') + chalk.cyan('https://tribecode.ai/tribe/progress'));
|
|
82
|
+
console.log(chalk.gray('Privacy & Security: ') + chalk.cyan('https://tribecode.ai/docs/privacy-security'));
|
|
110
83
|
console.log('\n' + '═'.repeat(70) + '\n');
|
|
111
84
|
}
|
|
112
85
|
|
|
113
|
-
async function checkCommand(cmd) {
|
|
114
|
-
try {
|
|
115
|
-
await which(cmd);
|
|
116
|
-
return true;
|
|
117
|
-
} catch {
|
|
118
|
-
const cmdPath = path.join(tribeBinDir, cmd);
|
|
119
|
-
try {
|
|
120
|
-
await fs.promises.access(cmdPath, fs.constants.X_OK);
|
|
121
|
-
return true;
|
|
122
|
-
} catch {
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
async function findCommand(cmd) {
|
|
129
|
-
const possiblePaths = [
|
|
130
|
-
path.join(binDir, cmd),
|
|
131
|
-
path.join('/opt/homebrew/bin', cmd),
|
|
132
|
-
path.join('/usr/local/bin', cmd),
|
|
133
|
-
cmd
|
|
134
|
-
];
|
|
135
|
-
|
|
136
|
-
try {
|
|
137
|
-
const cmdPath = await which(cmd);
|
|
138
|
-
return cmdPath;
|
|
139
|
-
} catch {
|
|
140
|
-
for (const cmdPath of possiblePaths) {
|
|
141
|
-
try {
|
|
142
|
-
await fs.promises.access(cmdPath, fs.constants.X_OK);
|
|
143
|
-
return cmdPath;
|
|
144
|
-
} catch {
|
|
145
|
-
// Continue searching
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return null;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
86
|
async function downloadFile(url, dest) {
|
|
154
87
|
return new Promise((resolve, reject) => {
|
|
155
88
|
const file = fs.createWriteStream(dest);
|
|
@@ -158,173 +91,39 @@ async function downloadFile(url, dest) {
|
|
|
158
91
|
return downloadFile(response.headers.location, dest).then(resolve, reject);
|
|
159
92
|
}
|
|
160
93
|
if (response.statusCode !== 200) {
|
|
161
|
-
reject(new Error(`HTTP ${response.statusCode}
|
|
94
|
+
reject(new Error(`HTTP ${response.statusCode}`));
|
|
162
95
|
return;
|
|
163
96
|
}
|
|
164
97
|
response.pipe(file);
|
|
165
|
-
file.on('finish',
|
|
98
|
+
file.on('finish', () => {
|
|
166
99
|
file.close();
|
|
167
|
-
|
|
168
|
-
// Check for pointer files
|
|
169
|
-
const stats = fs.statSync(dest);
|
|
170
|
-
if (stats.size < 500) {
|
|
171
|
-
const content = fs.readFileSync(dest, 'utf8').trim();
|
|
172
|
-
if (content.match(/^[\w\-\/]+$/) && !content.includes('<!DOCTYPE') && !content.includes('<html')) {
|
|
173
|
-
let actualBinaryUrl;
|
|
174
|
-
|
|
175
|
-
if (content.startsWith('http')) {
|
|
176
|
-
actualBinaryUrl = content;
|
|
177
|
-
} else if (content.includes('/')) {
|
|
178
|
-
const baseUrl = url.substring(0, url.lastIndexOf('/'));
|
|
179
|
-
actualBinaryUrl = `${baseUrl}/${content}`;
|
|
180
|
-
} else {
|
|
181
|
-
const baseUrl = url.substring(0, url.lastIndexOf('/'));
|
|
182
|
-
actualBinaryUrl = `${baseUrl}/${content}`;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
fs.unlinkSync(dest);
|
|
186
|
-
await downloadFile(actualBinaryUrl, dest);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
100
|
resolve();
|
|
191
101
|
});
|
|
192
102
|
}).on('error', reject);
|
|
193
103
|
});
|
|
194
104
|
}
|
|
195
105
|
|
|
196
|
-
async function installColima() {
|
|
197
|
-
if (platform !== 'darwin') {
|
|
198
|
-
return true;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const existingColima = await findCommand('colima');
|
|
202
|
-
if (existingColima) {
|
|
203
|
-
console.log(`🐳 Colima ${chalk.gray('(already installed)')}`);
|
|
204
|
-
return true;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const spinner = ora('🐳 Installing Colima...').start();
|
|
208
|
-
|
|
209
|
-
try {
|
|
210
|
-
// Try Homebrew first
|
|
211
|
-
try {
|
|
212
|
-
execSync('brew --version', { stdio: 'ignore' });
|
|
213
|
-
execSync('brew install colima', { stdio: 'ignore' });
|
|
214
|
-
spinner.succeed('Colima installed');
|
|
215
|
-
return true;
|
|
216
|
-
} catch {
|
|
217
|
-
// Continue with direct download
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Direct download
|
|
221
|
-
const colimaUrl = `https://github.com/abiosoft/colima/releases/latest/download/colima-${platform}-${arch}`;
|
|
222
|
-
const colimaDest = path.join(binDir, 'colima');
|
|
223
|
-
|
|
224
|
-
await downloadFile(colimaUrl, colimaDest);
|
|
225
|
-
fs.chmodSync(colimaDest, '755');
|
|
226
|
-
|
|
227
|
-
try {
|
|
228
|
-
execSync(`xattr -d com.apple.quarantine ${colimaDest}`, { stdio: 'ignore' });
|
|
229
|
-
} catch {
|
|
230
|
-
// Quarantine attribute may not exist
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
spinner.succeed('Colima installed');
|
|
234
|
-
return true;
|
|
235
|
-
} catch (error) {
|
|
236
|
-
spinner.fail(`Colima installation failed: ${error.message}`);
|
|
237
|
-
return false;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
async function installKubectl() {
|
|
242
|
-
if (await checkCommand('kubectl')) {
|
|
243
|
-
console.log(`☸️ kubectl ${chalk.gray('(already installed)')}`);
|
|
244
|
-
return true;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const spinner = ora('☸️ Installing kubectl...').start();
|
|
248
|
-
|
|
249
|
-
try {
|
|
250
|
-
const versionResponse = await fetch('https://dl.k8s.io/release/stable.txt');
|
|
251
|
-
const version = await versionResponse.text();
|
|
252
|
-
const kubectlUrl = `https://dl.k8s.io/release/${version.trim()}/bin/${platform}/${arch}/kubectl`;
|
|
253
|
-
const kubectlDest = path.join(binDir, 'kubectl');
|
|
254
|
-
|
|
255
|
-
await downloadFile(kubectlUrl, kubectlDest);
|
|
256
|
-
fs.chmodSync(kubectlDest, '755');
|
|
257
|
-
|
|
258
|
-
spinner.succeed('kubectl installed');
|
|
259
|
-
return true;
|
|
260
|
-
} catch (error) {
|
|
261
|
-
spinner.fail(`kubectl installation failed: ${error.message}`);
|
|
262
|
-
return false;
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
106
|
async function installTribeCLI() {
|
|
267
107
|
const tribeDest = path.join(tribeBinDir, 'tribe');
|
|
268
108
|
|
|
269
|
-
// Check if
|
|
270
|
-
const
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
const spinner = ora(isUpdate ? '🚀 Updating TRIBE CLI...' : '🚀 Installing TRIBE CLI...').start();
|
|
109
|
+
// Check if update needed
|
|
110
|
+
const exists = fs.existsSync(tribeDest);
|
|
111
|
+
const spinner = ora(exists ? '🔄 Updating TRIBE CLI...' : '📥 Installing TRIBE CLI...').start();
|
|
274
112
|
|
|
275
113
|
try {
|
|
276
114
|
// Remove existing if present
|
|
277
|
-
if (
|
|
115
|
+
if (exists) {
|
|
278
116
|
fs.unlinkSync(tribeDest);
|
|
279
117
|
}
|
|
280
118
|
|
|
281
|
-
// Check for local binary first
|
|
282
|
-
const localBinary = path.join(__dirname, 'tribe');
|
|
283
|
-
if (fs.existsSync(localBinary)) {
|
|
284
|
-
fs.copyFileSync(localBinary, tribeDest);
|
|
285
|
-
fs.chmodSync(tribeDest, '755');
|
|
286
|
-
spinner.succeed(isUpdate ? 'TRIBE CLI updated' : 'TRIBE CLI installed');
|
|
287
|
-
return true;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
119
|
// Download from GitHub
|
|
291
|
-
const githubRepo =
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
let downloadUrl = '';
|
|
295
|
-
|
|
296
|
-
try {
|
|
297
|
-
const response = await fetch(`https://api.github.com/repos/${githubRepo}/releases`, {
|
|
298
|
-
headers: {
|
|
299
|
-
'User-Agent': 'TRIBE-Installer',
|
|
300
|
-
...(process.env.GITHUB_TOKEN ? { 'Authorization': `token ${process.env.GITHUB_TOKEN}` } : {})
|
|
301
|
-
}
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
if (response.ok) {
|
|
305
|
-
const releases = await response.json();
|
|
306
|
-
if (releases && releases.length > 0) {
|
|
307
|
-
const asset = releases[0].assets?.find(a =>
|
|
308
|
-
a.name === `tribe-${platform}-${arch}` ||
|
|
309
|
-
a.name === `tribe-${platform}-${arch}.exe`
|
|
310
|
-
);
|
|
311
|
-
if (asset) {
|
|
312
|
-
downloadUrl = asset.browser_download_url;
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
} catch {
|
|
317
|
-
// Fallback to direct URL
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
if (!downloadUrl) {
|
|
321
|
-
downloadUrl = `https://github.com/${githubRepo}/releases/latest/download/tribe-${platform}-${arch}`;
|
|
322
|
-
}
|
|
120
|
+
const githubRepo = 'TRIBE-INC/releases';
|
|
121
|
+
const downloadUrl = `https://github.com/${githubRepo}/releases/latest/download/tribe-${platform}-${arch}`;
|
|
323
122
|
|
|
324
123
|
await downloadFile(downloadUrl, tribeDest);
|
|
325
124
|
fs.chmodSync(tribeDest, '755');
|
|
326
125
|
|
|
327
|
-
// Remove macOS quarantine
|
|
126
|
+
// Remove macOS quarantine if needed
|
|
328
127
|
if (platform === 'darwin') {
|
|
329
128
|
try {
|
|
330
129
|
execSync(`xattr -d com.apple.quarantine "${tribeDest}"`, { stdio: 'ignore' });
|
|
@@ -333,160 +132,48 @@ async function installTribeCLI() {
|
|
|
333
132
|
}
|
|
334
133
|
}
|
|
335
134
|
|
|
336
|
-
spinner.succeed(
|
|
135
|
+
spinner.succeed(exists ? 'TRIBE CLI updated' : 'TRIBE CLI installed');
|
|
337
136
|
return true;
|
|
338
137
|
} catch (error) {
|
|
339
|
-
spinner.fail(`
|
|
138
|
+
spinner.fail(`Installation failed: ${error.message}`);
|
|
340
139
|
return false;
|
|
341
140
|
}
|
|
342
141
|
}
|
|
343
142
|
|
|
344
|
-
async function
|
|
345
|
-
const
|
|
143
|
+
async function setupPath() {
|
|
144
|
+
const spinner = ora('🔧 Setting up PATH...').start();
|
|
346
145
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
146
|
+
try {
|
|
147
|
+
// Create PATH script
|
|
148
|
+
const envScript = `#!/bin/bash
|
|
149
|
+
# TRIBE CLI Environment
|
|
350
150
|
TRIBE_BIN_DIR="$HOME/.tribe/bin"
|
|
351
|
-
|
|
352
151
|
if [[ -d "$TRIBE_BIN_DIR" ]] && [[ ":$PATH:" != *":$TRIBE_BIN_DIR:"* ]]; then
|
|
353
152
|
export PATH="$TRIBE_BIN_DIR:$PATH"
|
|
354
|
-
fi
|
|
355
|
-
|
|
356
|
-
command -v tribe &> /dev/null || true
|
|
357
|
-
`;
|
|
358
|
-
|
|
359
|
-
fs.writeFileSync(envScriptDest, envScriptContent);
|
|
360
|
-
fs.chmodSync(envScriptDest, '755');
|
|
361
|
-
|
|
362
|
-
return true;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
async function setupGlobalNpmCommand() {
|
|
366
|
-
try {
|
|
367
|
-
const tempDir = path.join(os.tmpdir(), 'tribe-global-install-' + Date.now());
|
|
368
|
-
fs.mkdirSync(tempDir, { recursive: true });
|
|
369
|
-
|
|
370
|
-
const packageJson = {
|
|
371
|
-
name: 'tribe-cli-global',
|
|
372
|
-
version: '1.0.0',
|
|
373
|
-
description: 'TRIBE CLI global command',
|
|
374
|
-
bin: {
|
|
375
|
-
tribe: './tribe-wrapper.js'
|
|
376
|
-
},
|
|
377
|
-
files: ['tribe-wrapper.js'],
|
|
378
|
-
private: true
|
|
379
|
-
};
|
|
380
|
-
|
|
381
|
-
fs.writeFileSync(
|
|
382
|
-
path.join(tempDir, 'package.json'),
|
|
383
|
-
JSON.stringify(packageJson, null, 2)
|
|
384
|
-
);
|
|
385
|
-
|
|
386
|
-
const wrapperScript = `#!/usr/bin/env node
|
|
387
|
-
|
|
388
|
-
const { spawn } = require('child_process');
|
|
389
|
-
const fs = require('fs');
|
|
390
|
-
const path = require('path');
|
|
391
|
-
const os = require('os');
|
|
392
|
-
|
|
393
|
-
const tribeBinaryPath = path.join(os.homedir(), '.tribe', 'bin', 'tribe');
|
|
394
|
-
|
|
395
|
-
if (!fs.existsSync(tribeBinaryPath)) {
|
|
396
|
-
console.error('Error: TRIBE CLI binary not found.');
|
|
397
|
-
console.error('Please reinstall using: npx @_xtribe/cli@latest');
|
|
398
|
-
process.exit(1);
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
try {
|
|
402
|
-
fs.accessSync(tribeBinaryPath, fs.constants.X_OK);
|
|
403
|
-
} catch (err) {
|
|
404
|
-
console.error('Error: TRIBE CLI binary is not executable.');
|
|
405
|
-
console.error('Please reinstall using: npx @_xtribe/cli@latest');
|
|
406
|
-
process.exit(1);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
const child = spawn(tribeBinaryPath, process.argv.slice(2), {
|
|
410
|
-
stdio: 'inherit',
|
|
411
|
-
env: process.env
|
|
412
|
-
});
|
|
413
|
-
|
|
414
|
-
child.on('exit', (code) => {
|
|
415
|
-
process.exit(code || 0);
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
child.on('error', (err) => {
|
|
419
|
-
if (err.code === 'ENOENT') {
|
|
420
|
-
console.error('Error: TRIBE CLI binary not found at expected location.');
|
|
421
|
-
console.error('Please reinstall using: npx @_xtribe/cli@latest');
|
|
422
|
-
} else {
|
|
423
|
-
console.error('Failed to execute tribe:', err.message);
|
|
424
|
-
}
|
|
425
|
-
process.exit(1);
|
|
426
|
-
});`;
|
|
153
|
+
fi`;
|
|
427
154
|
|
|
428
|
-
const
|
|
429
|
-
fs.writeFileSync(
|
|
430
|
-
fs.chmodSync(
|
|
155
|
+
const envScriptPath = path.join(tribeDir, 'tribe-env.sh');
|
|
156
|
+
fs.writeFileSync(envScriptPath, envScript);
|
|
157
|
+
fs.chmodSync(envScriptPath, '755');
|
|
431
158
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
});
|
|
437
|
-
} catch (error) {
|
|
438
|
-
if (error.message.includes('EACCES') || error.message.includes('permission')) {
|
|
439
|
-
try {
|
|
440
|
-
execSync('sudo npm install -g . --force', {
|
|
441
|
-
cwd: tempDir,
|
|
442
|
-
stdio: 'inherit'
|
|
443
|
-
});
|
|
444
|
-
} catch {
|
|
445
|
-
// Ignore errors
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
try {
|
|
451
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
452
|
-
} catch {
|
|
453
|
-
// Ignore cleanup errors
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
return true;
|
|
457
|
-
} catch {
|
|
458
|
-
return false;
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
async function startColimaWithKubernetes() {
|
|
463
|
-
const spinner = ora('🌟 Starting container runtime...').start();
|
|
464
|
-
|
|
465
|
-
try {
|
|
466
|
-
const colimaPath = await findCommand('colima') || path.join(binDir, 'colima');
|
|
159
|
+
// Try to add to shell config
|
|
160
|
+
const shellConfig = process.env.SHELL?.includes('zsh') ? '.zshrc' : '.bashrc';
|
|
161
|
+
const rcPath = path.join(homeDir, shellConfig);
|
|
162
|
+
const sourceLine = 'source ~/.tribe/tribe-env.sh';
|
|
467
163
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
spinner.succeed('Container runtime ready');
|
|
473
|
-
return true;
|
|
164
|
+
if (fs.existsSync(rcPath)) {
|
|
165
|
+
const content = fs.readFileSync(rcPath, 'utf8');
|
|
166
|
+
if (!content.includes(sourceLine)) {
|
|
167
|
+
fs.appendFileSync(rcPath, `\n# TRIBE CLI\n${sourceLine}\n`);
|
|
474
168
|
}
|
|
475
|
-
} catch {
|
|
476
|
-
// Not running, start it
|
|
477
169
|
}
|
|
478
170
|
|
|
479
|
-
spinner.
|
|
480
|
-
execSync(`${colimaPath} start --kubernetes --cpu 4 --memory 8 --disk 20`, {
|
|
481
|
-
stdio: 'pipe',
|
|
482
|
-
env: { ...process.env, PATH: `${binDir}:${process.env.PATH}` },
|
|
483
|
-
timeout: 300000
|
|
484
|
-
});
|
|
485
|
-
|
|
486
|
-
spinner.succeed('Container runtime started');
|
|
171
|
+
spinner.succeed('PATH configured');
|
|
487
172
|
return true;
|
|
488
173
|
} catch (error) {
|
|
489
|
-
spinner.
|
|
174
|
+
spinner.warn('PATH setup needs manual configuration');
|
|
175
|
+
console.log(chalk.yellow('\nTo use tribe command, run:'));
|
|
176
|
+
console.log(chalk.cyan(' source ~/.tribe/tribe-env.sh\n'));
|
|
490
177
|
return false;
|
|
491
178
|
}
|
|
492
179
|
}
|
|
@@ -496,64 +183,42 @@ async function main() {
|
|
|
496
183
|
|
|
497
184
|
// Handle help
|
|
498
185
|
if (args.includes('--help') || args.includes('-h')) {
|
|
499
|
-
console.log(chalk.bold
|
|
186
|
+
console.log(chalk.bold('TRIBE Telemetry Installer\n'));
|
|
187
|
+
console.log('Empower your AI coding agents and become 10x faster\n');
|
|
500
188
|
console.log('Usage: npx @_xtribe/cli [options]\n');
|
|
501
189
|
console.log('Options:');
|
|
502
|
-
console.log(' --help, -h
|
|
503
|
-
console.log(' --
|
|
504
|
-
console.log(' --dry-run Show what would be installed');
|
|
190
|
+
console.log(' --help, -h Show this help message');
|
|
191
|
+
console.log(' --version Show version');
|
|
505
192
|
process.exit(0);
|
|
506
193
|
}
|
|
507
194
|
|
|
508
195
|
// Show welcome
|
|
509
196
|
showWelcome();
|
|
510
197
|
|
|
511
|
-
//
|
|
512
|
-
console.log(chalk.bold('
|
|
513
|
-
|
|
514
|
-
const existingTools = {
|
|
515
|
-
kubectl: await checkCommand('kubectl'),
|
|
516
|
-
colima: platform === 'darwin' ? await checkCommand('colima') : true,
|
|
517
|
-
tribe: await checkCommand('tribe')
|
|
518
|
-
};
|
|
519
|
-
|
|
520
|
-
const toolsFound = Object.values(existingTools).filter(v => v).length;
|
|
521
|
-
if (toolsFound > 0) {
|
|
522
|
-
console.log(chalk.green(`📦 Found ${toolsFound} existing tool${toolsFound > 1 ? 's' : ''}:`));
|
|
523
|
-
if (existingTools.kubectl) console.log(' ✓ kubectl');
|
|
524
|
-
if (existingTools.colima && platform === 'darwin') console.log(' ✓ colima');
|
|
525
|
-
if (existingTools.tribe) console.log(' ✓ tribe');
|
|
526
|
-
console.log('');
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
// Installation
|
|
530
|
-
console.log(chalk.bold('📥 Installing components...\n'));
|
|
198
|
+
// Simple installation flow
|
|
199
|
+
console.log(chalk.bold('📦 Setting up TRIBE Telemetry...\n'));
|
|
531
200
|
|
|
532
|
-
// Install
|
|
533
|
-
|
|
534
|
-
|
|
201
|
+
// Install TRIBE CLI
|
|
202
|
+
const success = await installTribeCLI();
|
|
203
|
+
if (!success) {
|
|
204
|
+
console.error(chalk.red('\n❌ Installation failed'));
|
|
205
|
+
console.log(chalk.yellow('Please check your internet connection and try again'));
|
|
206
|
+
process.exit(1);
|
|
535
207
|
}
|
|
536
|
-
await installKubectl();
|
|
537
|
-
await installTribeCLI();
|
|
538
|
-
|
|
539
|
-
console.log();
|
|
540
208
|
|
|
541
|
-
//
|
|
542
|
-
|
|
543
|
-
const configSpinner = ora('Setting up PATH...').start();
|
|
544
|
-
await setupPathEnvironment();
|
|
545
|
-
await setupGlobalNpmCommand();
|
|
546
|
-
configSpinner.succeed('Environment configured');
|
|
547
|
-
|
|
548
|
-
// Start container runtime if needed
|
|
549
|
-
if (platform === 'darwin' && !args.includes('--skip-cluster')) {
|
|
550
|
-
console.log();
|
|
551
|
-
await startColimaWithKubernetes();
|
|
552
|
-
}
|
|
209
|
+
// Setup PATH
|
|
210
|
+
await setupPath();
|
|
553
211
|
|
|
554
212
|
// Show summary
|
|
555
213
|
showInstallationSummary();
|
|
556
214
|
|
|
215
|
+
// Final message
|
|
216
|
+
console.log(chalk.green.bold('🎓 Your AI Engineering Journey Starts Now!\n'));
|
|
217
|
+
console.log(chalk.magenta.bold('📚 Run ') + chalk.cyan.bold('tribe login') + chalk.magenta.bold(' to begin your personalized learning path'));
|
|
218
|
+
console.log(chalk.gray(' Every coding session becomes a lesson. Every challenge becomes growth.\n'));
|
|
219
|
+
console.log(chalk.yellow('🚀 Master AI engineering step by step - because it really doesn\'t have to be hard'));
|
|
220
|
+
console.log();
|
|
221
|
+
|
|
557
222
|
process.exit(0);
|
|
558
223
|
}
|
|
559
224
|
|
|
@@ -563,7 +228,7 @@ module.exports = { main };
|
|
|
563
228
|
// Run if executed directly
|
|
564
229
|
if (require.main === module) {
|
|
565
230
|
main().catch(error => {
|
|
566
|
-
console.error(chalk.red('
|
|
231
|
+
console.error(chalk.red('Error:'), error.message);
|
|
567
232
|
process.exit(1);
|
|
568
233
|
});
|
|
569
234
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@_xtribe/cli",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "TRIBE
|
|
3
|
+
"version": "2.1.1",
|
|
4
|
+
"description": "TRIBE - Track, measure and optimize your AI coding agents to become 10x faster",
|
|
5
5
|
"main": "install-tribe.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"cli": "./index.js",
|