@_xtribe/cli 2.2.15 ā 2.2.16
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/auto-updater.js +268 -0
- package/index.js +11 -4
- package/install-tribe-with-autoupdate.js +328 -0
- package/install-tribe.js +9 -3
- package/package.json +3 -1
package/auto-updater.js
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const https = require('https');
|
|
7
|
+
const { execSync } = require('child_process');
|
|
8
|
+
const chalk = require('chalk');
|
|
9
|
+
|
|
10
|
+
class TribeAutoUpdater {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.homeDir = os.homedir();
|
|
13
|
+
this.tribeDir = path.join(this.homeDir, '.tribe');
|
|
14
|
+
this.tribeBinDir = path.join(this.tribeDir, 'bin');
|
|
15
|
+
this.versionFile = path.join(this.tribeDir, 'version.json');
|
|
16
|
+
this.platform = os.platform();
|
|
17
|
+
this.arch = os.arch() === 'x64' ? 'amd64' : (os.arch() === 'arm64' ? 'arm64' : os.arch());
|
|
18
|
+
this.githubRepo = 'TRIBE-INC/releases';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async fetchLatestVersion() {
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
const url = `https://api.github.com/repos/${this.githubRepo}/releases/latest`;
|
|
24
|
+
https.get(url, {
|
|
25
|
+
headers: {
|
|
26
|
+
'User-Agent': 'TRIBE-CLI-Updater'
|
|
27
|
+
}
|
|
28
|
+
}, (response) => {
|
|
29
|
+
let data = '';
|
|
30
|
+
response.on('data', chunk => data += chunk);
|
|
31
|
+
response.on('end', () => {
|
|
32
|
+
try {
|
|
33
|
+
const release = JSON.parse(data);
|
|
34
|
+
resolve({
|
|
35
|
+
version: release.tag_name,
|
|
36
|
+
publishedAt: release.published_at,
|
|
37
|
+
downloadUrl: `https://github.com/${this.githubRepo}/releases/latest/download/tribe-${this.platform}-${this.arch}`
|
|
38
|
+
});
|
|
39
|
+
} catch (error) {
|
|
40
|
+
reject(error);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}).on('error', reject);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
getCurrentVersion() {
|
|
48
|
+
try {
|
|
49
|
+
if (fs.existsSync(this.versionFile)) {
|
|
50
|
+
const versionData = JSON.parse(fs.readFileSync(this.versionFile, 'utf8'));
|
|
51
|
+
return versionData;
|
|
52
|
+
}
|
|
53
|
+
} catch (error) {
|
|
54
|
+
// Ignore errors, return null for fresh install
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
saveVersionInfo(versionInfo) {
|
|
60
|
+
try {
|
|
61
|
+
fs.writeFileSync(this.versionFile, JSON.stringify({
|
|
62
|
+
version: versionInfo.version,
|
|
63
|
+
publishedAt: versionInfo.publishedAt,
|
|
64
|
+
lastChecked: new Date().toISOString(),
|
|
65
|
+
installedAt: new Date().toISOString()
|
|
66
|
+
}, null, 2));
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.warn(chalk.yellow(`Warning: Could not save version info: ${error.message}`));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
shouldCheckForUpdates() {
|
|
73
|
+
const current = this.getCurrentVersion();
|
|
74
|
+
if (!current || !current.lastChecked) return true;
|
|
75
|
+
|
|
76
|
+
const lastChecked = new Date(current.lastChecked);
|
|
77
|
+
const now = new Date();
|
|
78
|
+
const hoursSinceLastCheck = (now - lastChecked) / (1000 * 60 * 60);
|
|
79
|
+
|
|
80
|
+
// Check for updates every 24 hours
|
|
81
|
+
return hoursSinceLastCheck >= 24;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
isUpdateAvailable(currentVersion, latestVersion) {
|
|
85
|
+
if (!currentVersion) return true; // No version installed
|
|
86
|
+
|
|
87
|
+
// Simple version comparison (assumes semver-like tags)
|
|
88
|
+
const current = currentVersion.version?.replace('v', '') || '0.0.0';
|
|
89
|
+
const latest = latestVersion.version?.replace('v', '') || '0.0.0';
|
|
90
|
+
|
|
91
|
+
return current !== latest;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async downloadFile(url, dest) {
|
|
95
|
+
return new Promise((resolve, reject) => {
|
|
96
|
+
const file = fs.createWriteStream(dest);
|
|
97
|
+
https.get(url, (response) => {
|
|
98
|
+
if (response.statusCode === 302 || response.statusCode === 301) {
|
|
99
|
+
return this.downloadFile(response.headers.location, dest).then(resolve, reject);
|
|
100
|
+
}
|
|
101
|
+
if (response.statusCode !== 200) {
|
|
102
|
+
reject(new Error(`HTTP ${response.statusCode}`));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
response.pipe(file);
|
|
106
|
+
file.on('finish', () => {
|
|
107
|
+
file.close();
|
|
108
|
+
resolve();
|
|
109
|
+
});
|
|
110
|
+
}).on('error', reject);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async performUpdate(latestVersion, options = {}) {
|
|
115
|
+
const { silent = false, force = false } = options;
|
|
116
|
+
const tribeBinary = path.join(this.tribeBinDir, 'tribe');
|
|
117
|
+
|
|
118
|
+
if (!silent) {
|
|
119
|
+
console.log(chalk.blue(`š Updating TRIBE CLI to ${latestVersion.version}...`));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
// Backup existing binary if it exists
|
|
124
|
+
const backupPath = tribeBinary + '.backup';
|
|
125
|
+
if (fs.existsSync(tribeBinary)) {
|
|
126
|
+
fs.copyFileSync(tribeBinary, backupPath);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Download new version
|
|
130
|
+
await this.downloadFile(latestVersion.downloadUrl, tribeBinary);
|
|
131
|
+
fs.chmodSync(tribeBinary, '755');
|
|
132
|
+
|
|
133
|
+
// Remove macOS quarantine if needed
|
|
134
|
+
if (this.platform === 'darwin') {
|
|
135
|
+
try {
|
|
136
|
+
execSync(`xattr -d com.apple.quarantine "${tribeBinary}"`, { stdio: 'ignore' });
|
|
137
|
+
} catch {
|
|
138
|
+
// Not critical
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Verify the update worked
|
|
143
|
+
try {
|
|
144
|
+
execSync(`"${tribeBinary}" --version`, { stdio: 'ignore', timeout: 5000 });
|
|
145
|
+
|
|
146
|
+
// Remove backup on success
|
|
147
|
+
if (fs.existsSync(backupPath)) {
|
|
148
|
+
fs.unlinkSync(backupPath);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Save version info
|
|
152
|
+
this.saveVersionInfo(latestVersion);
|
|
153
|
+
|
|
154
|
+
if (!silent) {
|
|
155
|
+
console.log(chalk.green(`ā
Updated to TRIBE CLI ${latestVersion.version}`));
|
|
156
|
+
}
|
|
157
|
+
return true;
|
|
158
|
+
} catch (error) {
|
|
159
|
+
// Restore backup on verification failure
|
|
160
|
+
if (fs.existsSync(backupPath)) {
|
|
161
|
+
fs.copyFileSync(backupPath, tribeBinary);
|
|
162
|
+
fs.unlinkSync(backupPath);
|
|
163
|
+
}
|
|
164
|
+
throw new Error(`Update verification failed: ${error.message}`);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
} catch (error) {
|
|
168
|
+
if (!silent) {
|
|
169
|
+
console.error(chalk.red(`ā Update failed: ${error.message}`));
|
|
170
|
+
}
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async checkForUpdates(options = {}) {
|
|
176
|
+
const { silent = false, force = false } = options;
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
if (!force && !this.shouldCheckForUpdates()) {
|
|
180
|
+
return { hasUpdate: false, reason: 'Recently checked' };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const latestVersion = await this.fetchLatestVersion();
|
|
184
|
+
const currentVersion = this.getCurrentVersion();
|
|
185
|
+
|
|
186
|
+
// Update last checked time
|
|
187
|
+
if (currentVersion) {
|
|
188
|
+
currentVersion.lastChecked = new Date().toISOString();
|
|
189
|
+
this.saveVersionInfo(currentVersion);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const hasUpdate = this.isUpdateAvailable(currentVersion, latestVersion);
|
|
193
|
+
|
|
194
|
+
if (hasUpdate) {
|
|
195
|
+
if (!silent) {
|
|
196
|
+
console.log(chalk.yellow(`š¦ New version available: ${latestVersion.version}`));
|
|
197
|
+
if (currentVersion?.version) {
|
|
198
|
+
console.log(chalk.gray(` Current: ${currentVersion.version}`));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
hasUpdate: true,
|
|
203
|
+
currentVersion: currentVersion?.version || 'unknown',
|
|
204
|
+
latestVersion: latestVersion.version,
|
|
205
|
+
latestVersionInfo: latestVersion
|
|
206
|
+
};
|
|
207
|
+
} else {
|
|
208
|
+
if (!silent) {
|
|
209
|
+
console.log(chalk.green('ā
TRIBE CLI is up to date'));
|
|
210
|
+
}
|
|
211
|
+
return { hasUpdate: false, reason: 'Up to date' };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
} catch (error) {
|
|
215
|
+
if (!silent) {
|
|
216
|
+
console.warn(chalk.yellow(`ā ļø Could not check for updates: ${error.message}`));
|
|
217
|
+
}
|
|
218
|
+
return { hasUpdate: false, reason: 'Check failed', error: error.message };
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async autoUpdate(options = {}) {
|
|
223
|
+
const { silent = false, force = false } = options;
|
|
224
|
+
|
|
225
|
+
const updateCheck = await this.checkForUpdates({ silent, force });
|
|
226
|
+
|
|
227
|
+
if (updateCheck.hasUpdate) {
|
|
228
|
+
return await this.performUpdate(updateCheck.latestVersionInfo, { silent, force });
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return false; // No update needed
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Check if binary exists and is functional
|
|
235
|
+
isInstalled() {
|
|
236
|
+
const tribeBinary = path.join(this.tribeBinDir, 'tribe');
|
|
237
|
+
if (!fs.existsSync(tribeBinary)) return false;
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
execSync(`"${tribeBinary}" --version`, { stdio: 'ignore', timeout: 5000 });
|
|
241
|
+
return true;
|
|
242
|
+
} catch {
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async promptForUpdate(updateInfo) {
|
|
248
|
+
const readline = require('readline');
|
|
249
|
+
const rl = readline.createInterface({
|
|
250
|
+
input: process.stdin,
|
|
251
|
+
output: process.stdout
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
return new Promise((resolve) => {
|
|
255
|
+
console.log(chalk.yellow(`\nš¦ TRIBE CLI update available!`));
|
|
256
|
+
console.log(chalk.gray(` Current: ${updateInfo.currentVersion}`));
|
|
257
|
+
console.log(chalk.cyan(` Latest: ${updateInfo.latestVersion}`));
|
|
258
|
+
|
|
259
|
+
rl.question('\nWould you like to update now? (Y/n): ', (answer) => {
|
|
260
|
+
rl.close();
|
|
261
|
+
const shouldUpdate = answer.toLowerCase() !== 'n' && answer.toLowerCase() !== 'no';
|
|
262
|
+
resolve(shouldUpdate);
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
module.exports = { TribeAutoUpdater };
|
package/index.js
CHANGED
|
@@ -2,15 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Default entry point for npx @_xtribe/cli
|
|
5
|
-
* This runs the
|
|
5
|
+
* This runs the installation process with optional auto-update support
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
// Execute the installer in a way that preserves require.main
|
|
9
8
|
const { spawn } = require('child_process');
|
|
10
9
|
const path = require('path');
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
const
|
|
11
|
+
// Check if user wants auto-update features
|
|
12
|
+
const args = process.argv.slice(2);
|
|
13
|
+
const useAutoUpdate = args.includes('--check-updates') || args.includes('--auto-update');
|
|
14
|
+
|
|
15
|
+
// Choose installer based on features requested
|
|
16
|
+
const installerPath = useAutoUpdate
|
|
17
|
+
? path.join(__dirname, 'install-tribe-with-autoupdate.js')
|
|
18
|
+
: path.join(__dirname, 'install-tribe.js');
|
|
19
|
+
|
|
20
|
+
const child = spawn(process.execPath, [installerPath, ...args], {
|
|
14
21
|
stdio: 'inherit',
|
|
15
22
|
env: process.env
|
|
16
23
|
});
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const https = require('https');
|
|
7
|
+
const { execSync } = require('child_process');
|
|
8
|
+
const chalk = require('chalk');
|
|
9
|
+
const ora = require('ora');
|
|
10
|
+
const { EnhancedPathSetup } = require('./enhanced-path-setup');
|
|
11
|
+
const { TribeAutoUpdater } = require('./auto-updater');
|
|
12
|
+
|
|
13
|
+
// ASCII art for TRIBE
|
|
14
|
+
const asciiArt = `
|
|
15
|
+
āāāāāāāāāāāāāāāā āāāāāāāāāā āāāāāāāā
|
|
16
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
17
|
+
āāā āāāāāāāāāāāāāāāāāāāāāāāāā
|
|
18
|
+
āāā āāāāāāāāāāāāāāāāāāāāāāāāā
|
|
19
|
+
āāā āāā āāāāāāāāāāāāāāāāāāāāāā
|
|
20
|
+
āāā āāā āāāāāāāāāāāāā āāāāāāāā
|
|
21
|
+
|
|
22
|
+
Learn AI Engineering ⢠Maximum Impact ⢠It Doesn't Have to Be Hard`;
|
|
23
|
+
|
|
24
|
+
const platform = os.platform();
|
|
25
|
+
const arch = os.arch() === 'x64' ? 'amd64' : (os.arch() === 'arm64' ? 'arm64' : os.arch());
|
|
26
|
+
const homeDir = os.homedir();
|
|
27
|
+
const tribeDir = path.join(homeDir, '.tribe');
|
|
28
|
+
const tribeBinDir = path.join(tribeDir, 'bin');
|
|
29
|
+
const tutorDir = path.join(tribeDir, 'tutor');
|
|
30
|
+
const logsDir = path.join(tribeDir, 'logs');
|
|
31
|
+
|
|
32
|
+
// Ensure all directories exist with proper permissions (700)
|
|
33
|
+
const directories = [
|
|
34
|
+
{ path: tribeDir, name: 'base' },
|
|
35
|
+
{ path: tribeBinDir, name: 'bin' },
|
|
36
|
+
{ path: tutorDir, name: 'tutor' },
|
|
37
|
+
{ path: logsDir, name: 'logs' }
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
directories.forEach(({ path: dirPath, name }) => {
|
|
41
|
+
if (!fs.existsSync(dirPath)) {
|
|
42
|
+
fs.mkdirSync(dirPath, { recursive: true, mode: 0o700 });
|
|
43
|
+
} else {
|
|
44
|
+
// Fix permissions if directory already exists
|
|
45
|
+
try {
|
|
46
|
+
fs.chmodSync(dirPath, 0o700);
|
|
47
|
+
} catch (err) {
|
|
48
|
+
// Ignore permission errors on some systems
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
function showWelcome() {
|
|
54
|
+
console.clear();
|
|
55
|
+
console.log(chalk.cyan(asciiArt));
|
|
56
|
+
console.log(chalk.gray('Master AI Engineering Through Practice\n'));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function downloadFile(url, dest) {
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
61
|
+
const file = fs.createWriteStream(dest);
|
|
62
|
+
https.get(url, (response) => {
|
|
63
|
+
if (response.statusCode === 302 || response.statusCode === 301) {
|
|
64
|
+
return downloadFile(response.headers.location, dest).then(resolve, reject);
|
|
65
|
+
}
|
|
66
|
+
if (response.statusCode !== 200) {
|
|
67
|
+
reject(new Error(`HTTP ${response.statusCode}`));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
response.pipe(file);
|
|
71
|
+
file.on('finish', () => {
|
|
72
|
+
file.close();
|
|
73
|
+
resolve();
|
|
74
|
+
});
|
|
75
|
+
}).on('error', reject);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function installOrUpdateTribeCLI() {
|
|
80
|
+
const updater = new TribeAutoUpdater();
|
|
81
|
+
const tribeDest = path.join(tribeBinDir, 'tribe');
|
|
82
|
+
|
|
83
|
+
// Check if it's an installation or update
|
|
84
|
+
const isInstalled = updater.isInstalled();
|
|
85
|
+
const action = isInstalled ? 'Updating' : 'Installing';
|
|
86
|
+
|
|
87
|
+
const spinner = ora(`${action} CLI...`).start();
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
if (isInstalled) {
|
|
91
|
+
// Use auto-updater for existing installations
|
|
92
|
+
const updated = await updater.autoUpdate({ silent: true, force: true });
|
|
93
|
+
if (updated) {
|
|
94
|
+
spinner.succeed('CLI updated to latest version');
|
|
95
|
+
} else {
|
|
96
|
+
spinner.succeed('CLI already up to date');
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
// Fresh installation - download latest version
|
|
100
|
+
const latestVersion = await updater.fetchLatestVersion();
|
|
101
|
+
|
|
102
|
+
await downloadFile(latestVersion.downloadUrl, tribeDest);
|
|
103
|
+
fs.chmodSync(tribeDest, '755');
|
|
104
|
+
|
|
105
|
+
// Remove macOS quarantine if needed
|
|
106
|
+
if (platform === 'darwin') {
|
|
107
|
+
try {
|
|
108
|
+
execSync(`xattr -d com.apple.quarantine "${tribeDest}"`, { stdio: 'ignore' });
|
|
109
|
+
} catch {
|
|
110
|
+
// Not critical
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Save version info for future updates
|
|
115
|
+
updater.saveVersionInfo(latestVersion);
|
|
116
|
+
|
|
117
|
+
spinner.succeed('CLI installed successfully');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return true;
|
|
121
|
+
} catch (error) {
|
|
122
|
+
spinner.fail(`${action} failed: ${error.message}`);
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function installTutorCollector() {
|
|
128
|
+
const tutorCollectorDest = path.join(tribeBinDir, 'tutor-collector');
|
|
129
|
+
const githubRepo = 'TRIBE-INC/releases';
|
|
130
|
+
const tutorCollectorUrl = `https://github.com/${githubRepo}/releases/latest/download/tutor-collector-${platform}-${arch}`;
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
await downloadFile(tutorCollectorUrl, tutorCollectorDest);
|
|
134
|
+
fs.chmodSync(tutorCollectorDest, '755');
|
|
135
|
+
|
|
136
|
+
// Remove macOS quarantine if needed
|
|
137
|
+
if (platform === 'darwin') {
|
|
138
|
+
try {
|
|
139
|
+
execSync(`xattr -d com.apple.quarantine "${tutorCollectorDest}"`, { stdio: 'ignore' });
|
|
140
|
+
} catch {
|
|
141
|
+
// Not critical
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return true;
|
|
145
|
+
} catch (tutorError) {
|
|
146
|
+
// Tutor collector is optional for basic CLI functionality
|
|
147
|
+
console.warn(chalk.yellow('Warning: Could not download tutor-collector binary'));
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function setupAutoUpdate() {
|
|
153
|
+
// Create a simple update script that users can run manually or add to cron
|
|
154
|
+
const updateScript = `#!/bin/bash
|
|
155
|
+
# TRIBE CLI Auto-Update Script
|
|
156
|
+
# Run this periodically to keep TRIBE CLI up to date
|
|
157
|
+
|
|
158
|
+
echo "š Checking for TRIBE CLI updates..."
|
|
159
|
+
npx @_xtribe/cli --check-updates --auto-update
|
|
160
|
+
|
|
161
|
+
# Optional: Add to cron by running:
|
|
162
|
+
# echo "0 12 * * * $HOME/.tribe/update-tribe.sh" | crontab -
|
|
163
|
+
`;
|
|
164
|
+
|
|
165
|
+
const updateScriptPath = path.join(tribeDir, 'update-tribe.sh');
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
fs.writeFileSync(updateScriptPath, updateScript);
|
|
169
|
+
fs.chmodSync(updateScriptPath, '755');
|
|
170
|
+
return updateScriptPath;
|
|
171
|
+
} catch (error) {
|
|
172
|
+
console.warn(chalk.yellow(`Warning: Could not create update script: ${error.message}`));
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function setupPath() {
|
|
178
|
+
const pathSetup = new EnhancedPathSetup();
|
|
179
|
+
const result = await pathSetup.setup();
|
|
180
|
+
return result.success;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async function main() {
|
|
184
|
+
const args = process.argv.slice(2);
|
|
185
|
+
|
|
186
|
+
// Handle version check and auto-update
|
|
187
|
+
if (args.includes('--check-updates') || args.includes('--auto-update')) {
|
|
188
|
+
const updater = new TribeAutoUpdater();
|
|
189
|
+
|
|
190
|
+
if (args.includes('--auto-update')) {
|
|
191
|
+
console.log(chalk.blue('š Checking for updates...'));
|
|
192
|
+
const updated = await updater.autoUpdate({ silent: false, force: true });
|
|
193
|
+
if (updated) {
|
|
194
|
+
console.log(chalk.green('ā
TRIBE CLI updated successfully'));
|
|
195
|
+
} else {
|
|
196
|
+
console.log(chalk.blue('š¦ TRIBE CLI is already up to date'));
|
|
197
|
+
}
|
|
198
|
+
} else {
|
|
199
|
+
const updateCheck = await updater.checkForUpdates({ silent: false, force: true });
|
|
200
|
+
if (updateCheck.hasUpdate) {
|
|
201
|
+
console.log(chalk.yellow(`\nš¦ Update available: ${updateCheck.latestVersion}`));
|
|
202
|
+
console.log(chalk.gray(`Run 'npx @_xtribe/cli --auto-update' to update`));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
process.exit(0);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Handle help
|
|
209
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
210
|
+
console.log(chalk.bold('TRIBE CLI Installer with Auto-Update\n'));
|
|
211
|
+
console.log('Empower your AI coding agents and become 10x faster\n');
|
|
212
|
+
console.log('Usage: npx @_xtribe/cli [options]\n');
|
|
213
|
+
console.log('Options:');
|
|
214
|
+
console.log(' --help, -h Show this help message');
|
|
215
|
+
console.log(' --version Show version');
|
|
216
|
+
console.log(' --check-updates Check for CLI updates');
|
|
217
|
+
console.log(' --auto-update Update CLI to latest version');
|
|
218
|
+
process.exit(0);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Show welcome
|
|
222
|
+
showWelcome();
|
|
223
|
+
|
|
224
|
+
// Check for updates first (if CLI is already installed)
|
|
225
|
+
const updater = new TribeAutoUpdater();
|
|
226
|
+
if (updater.isInstalled()) {
|
|
227
|
+
console.log(chalk.blue('š Checking for updates...'));
|
|
228
|
+
const updateCheck = await updater.checkForUpdates({ silent: true });
|
|
229
|
+
|
|
230
|
+
if (updateCheck.hasUpdate) {
|
|
231
|
+
console.log(chalk.yellow(`š¦ New version available: ${updateCheck.latestVersion}`));
|
|
232
|
+
|
|
233
|
+
// Prompt user for update
|
|
234
|
+
const shouldUpdate = await updater.promptForUpdate(updateCheck);
|
|
235
|
+
if (shouldUpdate) {
|
|
236
|
+
const spinner = ora('Updating TRIBE CLI...').start();
|
|
237
|
+
const updated = await updater.performUpdate(updateCheck.latestVersionInfo, { silent: true });
|
|
238
|
+
if (updated) {
|
|
239
|
+
spinner.succeed(`Updated to ${updateCheck.latestVersion}`);
|
|
240
|
+
} else {
|
|
241
|
+
spinner.fail('Update failed');
|
|
242
|
+
}
|
|
243
|
+
} else {
|
|
244
|
+
console.log(chalk.gray('Skipping update. You can update later with: npx @_xtribe/cli --auto-update'));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Single spinner for entire installation
|
|
250
|
+
const spinner = ora('Installing...').start();
|
|
251
|
+
|
|
252
|
+
try {
|
|
253
|
+
// Install or update TRIBE CLI
|
|
254
|
+
spinner.text = 'Installing CLI...';
|
|
255
|
+
const cliSuccess = await installOrUpdateTribeCLI();
|
|
256
|
+
if (!cliSuccess) {
|
|
257
|
+
spinner.fail('Installation failed');
|
|
258
|
+
console.log(chalk.yellow('Please check your internet connection and try again'));
|
|
259
|
+
process.exit(1);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Install tutor collector
|
|
263
|
+
spinner.text = 'Installing tutor collector...';
|
|
264
|
+
await installTutorCollector();
|
|
265
|
+
|
|
266
|
+
// Setup PATH
|
|
267
|
+
spinner.text = 'Setting up environment...';
|
|
268
|
+
try {
|
|
269
|
+
await setupPath();
|
|
270
|
+
} catch (error) {
|
|
271
|
+
// PATH setup had issues but continue with installation
|
|
272
|
+
console.log(chalk.yellow('ā ļø PATH setup had warnings, but installation completed'));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Setup auto-update script
|
|
276
|
+
spinner.text = 'Setting up auto-update...';
|
|
277
|
+
const updateScriptPath = await setupAutoUpdate();
|
|
278
|
+
|
|
279
|
+
spinner.succeed('Ready!');
|
|
280
|
+
|
|
281
|
+
// Test if tribe command works immediately
|
|
282
|
+
let commandWorks = false;
|
|
283
|
+
try {
|
|
284
|
+
execSync('which tribe', { stdio: 'ignore', timeout: 2000 });
|
|
285
|
+
commandWorks = true;
|
|
286
|
+
} catch (error) {
|
|
287
|
+
commandWorks = false;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Show appropriate completion message
|
|
291
|
+
if (commandWorks) {
|
|
292
|
+
console.log(chalk.green('\nā
TRIBE CLI is ready to use!'));
|
|
293
|
+
console.log('Type ' + chalk.cyan.bold('tribe') + ' to get started');
|
|
294
|
+
} else {
|
|
295
|
+
console.log(chalk.yellow('\nā ļø TRIBE CLI installed successfully!'));
|
|
296
|
+
console.log(chalk.blue('To use the tribe command, please restart your terminal'));
|
|
297
|
+
console.log(chalk.gray('or run: ') + chalk.cyan.bold('source ~/.tribe/tribe-env.sh'));
|
|
298
|
+
console.log(chalk.gray('\nThen type: ') + chalk.cyan.bold('tribe') + chalk.gray(' to get started'));
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Show auto-update info
|
|
302
|
+
console.log(chalk.blue('\nš Auto-Update:'));
|
|
303
|
+
console.log(chalk.gray(' Check for updates: ') + chalk.cyan('npx @_xtribe/cli --check-updates'));
|
|
304
|
+
console.log(chalk.gray(' Auto-update: ') + chalk.cyan('npx @_xtribe/cli --auto-update'));
|
|
305
|
+
|
|
306
|
+
if (updateScriptPath) {
|
|
307
|
+
console.log(chalk.gray(' Update script: ') + chalk.cyan(updateScriptPath));
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
} catch (error) {
|
|
311
|
+
spinner.fail('Installation failed');
|
|
312
|
+
console.error(chalk.red(error.message));
|
|
313
|
+
process.exit(1);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
process.exit(0);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Export for use from index.js
|
|
320
|
+
module.exports = { main };
|
|
321
|
+
|
|
322
|
+
// Run if executed directly
|
|
323
|
+
if (require.main === module) {
|
|
324
|
+
main().catch(error => {
|
|
325
|
+
console.error(chalk.red('Error:'), error.message);
|
|
326
|
+
process.exit(1);
|
|
327
|
+
});
|
|
328
|
+
}
|
package/install-tribe.js
CHANGED
|
@@ -186,12 +186,18 @@ async function main() {
|
|
|
186
186
|
|
|
187
187
|
// Handle help
|
|
188
188
|
if (args.includes('--help') || args.includes('-h')) {
|
|
189
|
-
console.log(chalk.bold('TRIBE
|
|
189
|
+
console.log(chalk.bold('TRIBE CLI Installer\n'));
|
|
190
190
|
console.log('Empower your AI coding agents and become 10x faster\n');
|
|
191
191
|
console.log('Usage: npx @_xtribe/cli [options]\n');
|
|
192
192
|
console.log('Options:');
|
|
193
|
-
console.log(' --help, -h
|
|
194
|
-
console.log(' --version
|
|
193
|
+
console.log(' --help, -h Show this help message');
|
|
194
|
+
console.log(' --version Show version');
|
|
195
|
+
console.log(' --check-updates Check for CLI updates');
|
|
196
|
+
console.log(' --auto-update Update CLI to latest version');
|
|
197
|
+
console.log('\nAuto-Update Features:');
|
|
198
|
+
console.log(' The CLI can automatically check for and install updates');
|
|
199
|
+
console.log(' Use --check-updates to see if a newer version is available');
|
|
200
|
+
console.log(' Use --auto-update to download and install the latest version');
|
|
195
201
|
process.exit(0);
|
|
196
202
|
}
|
|
197
203
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@_xtribe/cli",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.16",
|
|
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": {
|
|
@@ -42,6 +42,8 @@
|
|
|
42
42
|
"install-tribe.js",
|
|
43
43
|
"install-tribe-minimal.js",
|
|
44
44
|
"install-tribe-autolaunch.js",
|
|
45
|
+
"install-tribe-with-autoupdate.js",
|
|
46
|
+
"auto-updater.js",
|
|
45
47
|
"setup-path.js",
|
|
46
48
|
"enhanced-path-setup.js",
|
|
47
49
|
"install.sh",
|