vibecodingmachine-cli 2025.12.1-534 → 2025.12.6-1702
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/bin/vibecodingmachine.js +92 -4
- package/package.json +5 -2
- package/repro_open.js +13 -0
- package/scripts/postinstall.js +80 -0
- package/src/commands/auto-direct.js +401 -97
- package/src/commands/auto.js +343 -115
- package/src/commands/computers.js +306 -0
- package/src/commands/requirements-remote.js +304 -0
- package/src/commands/requirements.js +204 -13
- package/src/commands/sync.js +280 -0
- package/src/utils/asset-cleanup.js +61 -0
- package/src/utils/auth.js +84 -7
- package/src/utils/auto-mode-simple-ui.js +2 -22
- package/src/utils/first-run.js +293 -0
- package/src/utils/interactive.js +1027 -217
- package/src/utils/kiro-installer.js +146 -0
- package/src/utils/provider-registry.js +8 -0
- package/src/utils/status-card.js +2 -1
package/src/utils/auth.js
CHANGED
|
@@ -31,7 +31,30 @@ class CLIAuth {
|
|
|
31
31
|
* Check if authenticated (uses shared storage)
|
|
32
32
|
*/
|
|
33
33
|
async isAuthenticated() {
|
|
34
|
-
|
|
34
|
+
// First check if current token is valid
|
|
35
|
+
const isValid = await sharedAuth.isAuthenticated();
|
|
36
|
+
if (isValid) return true;
|
|
37
|
+
|
|
38
|
+
// If not valid, try to refresh
|
|
39
|
+
try {
|
|
40
|
+
const refreshToken = await sharedAuth.getRefreshToken();
|
|
41
|
+
if (refreshToken) {
|
|
42
|
+
// console.log(chalk.gray('Refreshing session...'));
|
|
43
|
+
const newTokens = await this._refreshTokens(refreshToken);
|
|
44
|
+
|
|
45
|
+
// Validate new ID token
|
|
46
|
+
await this._validateToken(newTokens.id_token);
|
|
47
|
+
|
|
48
|
+
// Save new tokens
|
|
49
|
+
await sharedAuth.saveToken(newTokens);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
} catch (error) {
|
|
53
|
+
// Refresh failed (token expired or revoked), user needs to login again
|
|
54
|
+
// console.log(chalk.gray('Session refresh failed, please login again.'));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return false;
|
|
35
58
|
}
|
|
36
59
|
|
|
37
60
|
/**
|
|
@@ -82,7 +105,7 @@ class CLIAuth {
|
|
|
82
105
|
res.on('end', () => {
|
|
83
106
|
if (res.statusCode === 200) {
|
|
84
107
|
const tokens = JSON.parse(data);
|
|
85
|
-
resolve(tokens
|
|
108
|
+
resolve(tokens); // Return full tokens object
|
|
86
109
|
} else {
|
|
87
110
|
reject(new Error(`Token exchange failed: ${res.statusCode} - ${data}`));
|
|
88
111
|
}
|
|
@@ -97,7 +120,53 @@ class CLIAuth {
|
|
|
97
120
|
req.end();
|
|
98
121
|
});
|
|
99
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* Refresh tokens using refresh_token
|
|
125
|
+
*/
|
|
126
|
+
async _refreshTokens(refreshToken) {
|
|
127
|
+
const https = require('https');
|
|
128
|
+
|
|
129
|
+
const tokenEndpoint = `https://${COGNITO_DOMAIN}/oauth2/token`;
|
|
130
|
+
const postData = new URLSearchParams({
|
|
131
|
+
grant_type: 'refresh_token',
|
|
132
|
+
client_id: CLIENT_ID,
|
|
133
|
+
refresh_token: refreshToken
|
|
134
|
+
}).toString();
|
|
135
|
+
|
|
136
|
+
return new Promise((resolve, reject) => {
|
|
137
|
+
const options = {
|
|
138
|
+
method: 'POST',
|
|
139
|
+
headers: {
|
|
140
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
141
|
+
'Content-Length': postData.length
|
|
142
|
+
}
|
|
143
|
+
};
|
|
100
144
|
|
|
145
|
+
const req = https.request(tokenEndpoint, options, (res) => {
|
|
146
|
+
let data = '';
|
|
147
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
148
|
+
res.on('end', () => {
|
|
149
|
+
if (res.statusCode === 200) {
|
|
150
|
+
const tokens = JSON.parse(data);
|
|
151
|
+
// Cognito might not return a new refresh token, so we keep the old one if not provided
|
|
152
|
+
if (!tokens.refresh_token) {
|
|
153
|
+
tokens.refresh_token = refreshToken;
|
|
154
|
+
}
|
|
155
|
+
resolve(tokens);
|
|
156
|
+
} else {
|
|
157
|
+
reject(new Error(`Token refresh failed: ${res.statusCode} - ${data}`));
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
req.on('error', (error) => {
|
|
163
|
+
reject(new Error(`Token refresh request failed: ${error.message}`));
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
req.write(postData);
|
|
167
|
+
req.end();
|
|
168
|
+
});
|
|
169
|
+
}
|
|
101
170
|
/**
|
|
102
171
|
* Validate JWT token with signature verification
|
|
103
172
|
* @param {string} idToken - JWT token to validate
|
|
@@ -312,18 +381,22 @@ class CLIAuth {
|
|
|
312
381
|
try {
|
|
313
382
|
// Exchange code for tokens
|
|
314
383
|
console.log(chalk.gray('Exchanging authorization code for tokens...'));
|
|
315
|
-
|
|
384
|
+
// Exchange code for tokens
|
|
385
|
+
console.log(chalk.gray('Exchanging authorization code for tokens...'));
|
|
386
|
+
const tokens = await this._exchangeCodeForTokens(
|
|
316
387
|
code,
|
|
317
388
|
codeVerifier,
|
|
318
389
|
`http://localhost:${PORT}/callback`
|
|
319
390
|
);
|
|
320
391
|
|
|
392
|
+
const idToken = tokens.id_token;
|
|
393
|
+
|
|
321
394
|
// Validate token (signature + claims)
|
|
322
395
|
console.log(chalk.gray('Validating token...'));
|
|
323
396
|
await this._validateToken(idToken);
|
|
324
397
|
|
|
325
398
|
// Save token
|
|
326
|
-
await sharedAuth.saveToken(
|
|
399
|
+
await sharedAuth.saveToken(tokens);
|
|
327
400
|
|
|
328
401
|
// Show success page
|
|
329
402
|
res.writeHead(200, {
|
|
@@ -428,7 +501,7 @@ class CLIAuth {
|
|
|
428
501
|
serverClosed = true;
|
|
429
502
|
server.close();
|
|
430
503
|
console.log(chalk.green('✓ Authentication successful!\n'));
|
|
431
|
-
resolve(
|
|
504
|
+
resolve(tokens.id_token);
|
|
432
505
|
}
|
|
433
506
|
} catch (error) {
|
|
434
507
|
// Token exchange or validation failed
|
|
@@ -560,12 +633,16 @@ class CLIAuth {
|
|
|
560
633
|
|
|
561
634
|
// Exchange code for tokens
|
|
562
635
|
console.log(chalk.gray('Exchanging authorization code for tokens...'));
|
|
563
|
-
|
|
636
|
+
// Exchange code for tokens
|
|
637
|
+
console.log(chalk.gray('Exchanging authorization code for tokens...'));
|
|
638
|
+
const tokens = await this._exchangeCodeForTokens(
|
|
564
639
|
code,
|
|
565
640
|
codeVerifier,
|
|
566
641
|
'http://localhost:3000/callback'
|
|
567
642
|
);
|
|
568
643
|
|
|
644
|
+
const idToken = tokens.id_token;
|
|
645
|
+
|
|
569
646
|
// Validate JWT token
|
|
570
647
|
console.log(chalk.gray('Validating token...'));
|
|
571
648
|
try {
|
|
@@ -577,7 +654,7 @@ class CLIAuth {
|
|
|
577
654
|
}
|
|
578
655
|
|
|
579
656
|
// Save token using shared storage (only if validation passed)
|
|
580
|
-
await sharedAuth.saveToken(
|
|
657
|
+
await sharedAuth.saveToken(tokens);
|
|
581
658
|
|
|
582
659
|
console.log(chalk.green('\n✓ Authentication successful!'));
|
|
583
660
|
return idToken;
|
|
@@ -13,9 +13,6 @@ class AutoModeSimpleUI {
|
|
|
13
13
|
this.chatCount = 0;
|
|
14
14
|
this.maxChats = null;
|
|
15
15
|
this.progress = 0;
|
|
16
|
-
this.outputLineCount = 0; // Track output lines for periodic re-render
|
|
17
|
-
this.lastCardPrintTime = Date.now(); // Track when we last printed the card
|
|
18
|
-
this.lastCardPrintLine = 0; // Track line count when we last printed the card
|
|
19
16
|
|
|
20
17
|
// Print header once
|
|
21
18
|
console.log('\n' + chalk.bold.cyan('═══════════════════════════════════════════════════════════'));
|
|
@@ -109,30 +106,13 @@ class AutoModeSimpleUI {
|
|
|
109
106
|
borderColor: 'magenta',
|
|
110
107
|
backgroundColor: 'black'
|
|
111
108
|
});
|
|
112
|
-
|
|
113
|
-
// Print the card with a separator line before it for visibility
|
|
114
|
-
if (this.outputLineCount > 0) {
|
|
115
|
-
// Add separator when re-printing after output
|
|
116
|
-
console.log(chalk.gray('─'.repeat(80)));
|
|
117
|
-
}
|
|
109
|
+
|
|
118
110
|
console.log(card);
|
|
119
|
-
this.lastCardPrintTime = Date.now();
|
|
120
|
-
this.lastCardPrintLine = this.outputLineCount;
|
|
121
111
|
}
|
|
122
112
|
|
|
123
113
|
appendOutput(line) {
|
|
124
|
-
//
|
|
125
|
-
// Re-print every 20 lines of output, or if more than 5 seconds have passed
|
|
126
|
-
const linesSinceLastCard = this.outputLineCount - this.lastCardPrintLine;
|
|
127
|
-
const timeSinceLastCard = Date.now() - this.lastCardPrintTime;
|
|
128
|
-
|
|
129
|
-
if (linesSinceLastCard >= 20 || timeSinceLastCard > 5000) {
|
|
130
|
-
this.printWorkflowCard();
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Increment output line counter
|
|
114
|
+
// Just print the line - no automatic card re-printing
|
|
134
115
|
if (line && line.trim()) {
|
|
135
|
-
this.outputLineCount++;
|
|
136
116
|
console.log(line);
|
|
137
117
|
}
|
|
138
118
|
}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
const inquirer = require('inquirer');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const boxen = require('boxen');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
const { getProviderDefinitions, saveProviderPreferences, getDefaultProviderOrder } = require('./provider-registry');
|
|
8
|
+
const { isKiroInstalled, installKiro } = require('./kiro-installer');
|
|
9
|
+
|
|
10
|
+
async function checkFirstRun() {
|
|
11
|
+
const configDir = path.join(os.homedir(), '.vibecodingmachine');
|
|
12
|
+
|
|
13
|
+
// If directory exists, we assume it's not the first run
|
|
14
|
+
if (await fs.pathExists(configDir)) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// FIRST RUN EXPERIENCE
|
|
19
|
+
console.clear();
|
|
20
|
+
|
|
21
|
+
// 1. Big Welcome Banner
|
|
22
|
+
console.log('\n' + boxen(
|
|
23
|
+
chalk.bold.cyan('Welcome to Vibe Coding Machine') + '\n\n' +
|
|
24
|
+
chalk.white('The future of coding is here.') + '\n' +
|
|
25
|
+
chalk.gray('Just describe what you want, and we code it for you.'),
|
|
26
|
+
{
|
|
27
|
+
padding: 1,
|
|
28
|
+
margin: 1,
|
|
29
|
+
borderStyle: 'double',
|
|
30
|
+
borderColor: 'cyan',
|
|
31
|
+
align: 'center'
|
|
32
|
+
}
|
|
33
|
+
));
|
|
34
|
+
|
|
35
|
+
console.log(chalk.cyan('Let\'s get you set up with the best AI coding environment.\n'));
|
|
36
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
37
|
+
|
|
38
|
+
// --- NEW: Vibe Coding Introduction ---
|
|
39
|
+
const { showIntro } = await inquirer.prompt([
|
|
40
|
+
{
|
|
41
|
+
type: 'confirm',
|
|
42
|
+
name: 'showIntro',
|
|
43
|
+
message: 'Are you new to Vibe Coding? Would you like a quick 30-second tour?',
|
|
44
|
+
default: true
|
|
45
|
+
}
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
if (showIntro) {
|
|
49
|
+
const introContent = [
|
|
50
|
+
chalk.bold.cyan('╔════════════════════════ Vibe Coding 101 ════════════════════════╗'),
|
|
51
|
+
chalk.cyan('║ ║'),
|
|
52
|
+
chalk.cyan('║ 1. What is an IDE? ║'),
|
|
53
|
+
chalk.cyan('║ Think of it like MS Word, but for writing code. ║'),
|
|
54
|
+
chalk.cyan('║ You need one to build apps, but don\'t worry—we\'ll install ║'),
|
|
55
|
+
chalk.cyan('║ the best ones for you automatically. ║'),
|
|
56
|
+
chalk.cyan('║ ║'),
|
|
57
|
+
chalk.cyan('║ 2. How it works ║'),
|
|
58
|
+
chalk.cyan('║ You simply describe the app or web page you want. ║'),
|
|
59
|
+
chalk.cyan('║ VibeCodingMachine then takes over your keyboard to write ║'),
|
|
60
|
+
chalk.cyan('║ the code inside the IDE (GUI mode) or runs in the ║'),
|
|
61
|
+
chalk.cyan('║ background (CLI mode) which is even faster. ║'),
|
|
62
|
+
chalk.cyan('║ ║'),
|
|
63
|
+
chalk.bold.cyan('╚═════════════════════════════════════════════════════════════════╝')
|
|
64
|
+
].join('\n');
|
|
65
|
+
|
|
66
|
+
console.log('\n' + introContent + '\n');
|
|
67
|
+
|
|
68
|
+
await inquirer.prompt([{
|
|
69
|
+
type: 'input',
|
|
70
|
+
name: 'continue',
|
|
71
|
+
message: 'Press Enter to continue...'
|
|
72
|
+
}]);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
// 2. Detect IDEs
|
|
79
|
+
const definitions = getProviderDefinitions();
|
|
80
|
+
const ideDefinitions = definitions.filter(d => d.type === 'ide');
|
|
81
|
+
|
|
82
|
+
const detectedIDEs = [];
|
|
83
|
+
const otherIDEs = [];
|
|
84
|
+
|
|
85
|
+
// Simple simulation of detection for standard apps on macOS
|
|
86
|
+
// In a real scenario, we might check other paths or registry on Windows
|
|
87
|
+
const platform = os.platform();
|
|
88
|
+
const getAppPath = (name) => platform === 'darwin' ? `/Applications/${name}.app` : null;
|
|
89
|
+
|
|
90
|
+
for (const ide of ideDefinitions) {
|
|
91
|
+
let installed = false;
|
|
92
|
+
|
|
93
|
+
if (ide.id === 'kiro') {
|
|
94
|
+
installed = isKiroInstalled();
|
|
95
|
+
} else if (ide.id === 'cursor') {
|
|
96
|
+
if (platform === 'darwin') installed = await fs.pathExists(getAppPath('Cursor'));
|
|
97
|
+
} else if (ide.id === 'windsurf') {
|
|
98
|
+
if (platform === 'darwin') installed = await fs.pathExists(getAppPath('Windsurf'));
|
|
99
|
+
} else if (ide.id === 'vscode') {
|
|
100
|
+
if (platform === 'darwin') installed = await fs.pathExists(getAppPath('Visual Studio Code'));
|
|
101
|
+
} else if (ide.id === 'antigravity') {
|
|
102
|
+
// Antigravity is just a provider here, maybe assume installed if running?
|
|
103
|
+
// Or check for some component. For now default to true or false based on something simple.
|
|
104
|
+
installed = true; // Assume available
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (installed) {
|
|
108
|
+
detectedIDEs.push(ide);
|
|
109
|
+
} else {
|
|
110
|
+
otherIDEs.push(ide);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 3. Status Report & Selection
|
|
115
|
+
if (detectedIDEs.length > 0) {
|
|
116
|
+
console.log(chalk.green('\n✓ We found these IDEs and enabled them for you:'));
|
|
117
|
+
detectedIDEs.forEach(ide => {
|
|
118
|
+
console.log(chalk.gray(` • ${ide.name}`));
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
let selectedIDEs = [];
|
|
123
|
+
|
|
124
|
+
// Only prompt for uninstalled IDEs if there are any
|
|
125
|
+
if (otherIDEs.length > 0) {
|
|
126
|
+
console.log(); // Spacing
|
|
127
|
+
const choices = otherIDEs.map(ide => ({
|
|
128
|
+
name: ide.name,
|
|
129
|
+
value: ide.id,
|
|
130
|
+
checked: true // Select by default as requested
|
|
131
|
+
}));
|
|
132
|
+
|
|
133
|
+
const response = await inquirer.prompt([{
|
|
134
|
+
type: 'checkbox',
|
|
135
|
+
name: 'selectedIDEs',
|
|
136
|
+
message: 'Select additional IDEs to install & enable:',
|
|
137
|
+
choices: choices,
|
|
138
|
+
pageSize: 10
|
|
139
|
+
}]);
|
|
140
|
+
selectedIDEs = response.selectedIDEs;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// 4. Handle Installations
|
|
144
|
+
if (selectedIDEs.includes('kiro')) {
|
|
145
|
+
const kiroInstalled = isKiroInstalled();
|
|
146
|
+
if (!kiroInstalled) {
|
|
147
|
+
// installKiro logs its own inputs, but we want to handle failure gracefully
|
|
148
|
+
const success = await installKiro();
|
|
149
|
+
if (!success) {
|
|
150
|
+
console.log(chalk.yellow('\n⚠️ Kiro installation failed or was skipped.'));
|
|
151
|
+
console.log(chalk.gray('You can continue setting up other IDEs.'));
|
|
152
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
153
|
+
} else {
|
|
154
|
+
// Post-install notification with auto-launch
|
|
155
|
+
console.log('\n' + boxen(
|
|
156
|
+
chalk.bold.yellow('⚠️ ACTION REQUIRED: SIGN IN ') + '\n\n' +
|
|
157
|
+
chalk.white('1. I will launch AWS Kiro IDE for you.') + '\n' +
|
|
158
|
+
chalk.white('2. Sign in with Google, GitHub, or AWS Builder ID.') + '\n' +
|
|
159
|
+
chalk.white('3. Click "Skip all" to skip survey questions.') + '\n' +
|
|
160
|
+
chalk.white('4. Once signed in, you can minimize or close the IDE.') + '\n\n' +
|
|
161
|
+
chalk.cyan('You must sign in for the CLI to control the IDE.'),
|
|
162
|
+
{
|
|
163
|
+
padding: 1,
|
|
164
|
+
margin: 1,
|
|
165
|
+
borderStyle: 'round',
|
|
166
|
+
borderColor: 'yellow'
|
|
167
|
+
}
|
|
168
|
+
));
|
|
169
|
+
|
|
170
|
+
await inquirer.prompt([{
|
|
171
|
+
type: 'input',
|
|
172
|
+
name: 'launch',
|
|
173
|
+
message: 'Press Enter and I will launch Kiro for you to sign in to...'
|
|
174
|
+
}]);
|
|
175
|
+
|
|
176
|
+
const { exec } = require('child_process');
|
|
177
|
+
console.log(chalk.gray('Launching AWS Kiro...'));
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
// Try to open AWS Kiro
|
|
181
|
+
exec('open -a "AWS Kiro"', (error) => {
|
|
182
|
+
if (error) {
|
|
183
|
+
// Fallback to just Kiro
|
|
184
|
+
exec('open -a "Kiro"', (err) => {
|
|
185
|
+
if (err) {
|
|
186
|
+
console.log(chalk.red('Could not auto-launch Kiro. Please open it manually.'));
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
} catch (e) {
|
|
192
|
+
// Ignore errors, user can launch manually
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
await inquirer.prompt([{
|
|
196
|
+
type: 'input',
|
|
197
|
+
name: 'continue',
|
|
198
|
+
message: 'Press Enter once you have signed in...'
|
|
199
|
+
}]);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// 5. Configure Preferences
|
|
205
|
+
// Enable detected IDEs AND selected IDEs, disable others
|
|
206
|
+
const defaultOrder = getDefaultProviderOrder();
|
|
207
|
+
const enabledMap = {};
|
|
208
|
+
|
|
209
|
+
defaultOrder.forEach(id => {
|
|
210
|
+
// Enable if it was detected OR selected
|
|
211
|
+
// For LLMs (not 'ide' type), keep enabled by default
|
|
212
|
+
const def = definitions.find(d => d.id === id);
|
|
213
|
+
if (def && def.type === 'ide') {
|
|
214
|
+
const isDetected = detectedIDEs.some(d => d.id === id);
|
|
215
|
+
const isSelected = selectedIDEs.includes(id);
|
|
216
|
+
enabledMap[id] = isDetected || isSelected;
|
|
217
|
+
} else {
|
|
218
|
+
enabledMap[id] = true; // Keep LLMs enabled by default
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// Save initial preferences. provider-registry.js usually saves to ~/.config/vibecodingmachine/config.json
|
|
223
|
+
// But here we are checking ~/.vibecodingmachine.
|
|
224
|
+
// We should create the ~/.vibecodingmachine directory to mark first run as complete.
|
|
225
|
+
await fs.ensureDir(configDir);
|
|
226
|
+
await saveProviderPreferences(defaultOrder, enabledMap);
|
|
227
|
+
|
|
228
|
+
// --- NEW: CLI Usage Onboarding ---
|
|
229
|
+
// Moved here so IDEs are set up first
|
|
230
|
+
const { isFamiliar } = await inquirer.prompt([
|
|
231
|
+
{
|
|
232
|
+
type: 'confirm',
|
|
233
|
+
name: 'isFamiliar',
|
|
234
|
+
message: 'Are you familiar with how to use VibeCodingMachine CLI?',
|
|
235
|
+
default: false
|
|
236
|
+
}
|
|
237
|
+
]);
|
|
238
|
+
|
|
239
|
+
if (!isFamiliar) {
|
|
240
|
+
const cliUsageContent = [
|
|
241
|
+
chalk.bold.green('╔════════════════════════ CLI Crash Course ═══════════════════════╗'),
|
|
242
|
+
chalk.green('║ ║'),
|
|
243
|
+
chalk.green('║ 1. Navigation ║'),
|
|
244
|
+
chalk.green('║ [↑/↓] Arrow keys to move selection ║'),
|
|
245
|
+
chalk.green('║ [Space] to toggle checkboxes [x] ║'),
|
|
246
|
+
chalk.green('║ [Enter] to confirm your choice ║'),
|
|
247
|
+
chalk.green('║ ║'),
|
|
248
|
+
chalk.green('║ 2. Key Features ║'),
|
|
249
|
+
chalk.green('║ • Auto Mode: The autonomous coding agent ║'),
|
|
250
|
+
chalk.green('║ • Direct Mode: Chat directly with LLMs ║'),
|
|
251
|
+
chalk.green('║ • IDE Setup: Install and configure tools ║'),
|
|
252
|
+
chalk.green('║ ║'),
|
|
253
|
+
chalk.green('║ 3. Pro Tip ║'),
|
|
254
|
+
chalk.green('║ If you ever get stuck, just restart the CLI with \'vcm\'. ║'),
|
|
255
|
+
chalk.green('║ ║'),
|
|
256
|
+
chalk.bold.green('╚═════════════════════════════════════════════════════════════════╝')
|
|
257
|
+
].join('\n');
|
|
258
|
+
|
|
259
|
+
console.log('\n' + cliUsageContent + '\n');
|
|
260
|
+
|
|
261
|
+
await inquirer.prompt([{
|
|
262
|
+
type: 'input',
|
|
263
|
+
name: 'continue',
|
|
264
|
+
message: 'Press Enter to continue...'
|
|
265
|
+
}]);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// 6. Explanation
|
|
269
|
+
console.log('\n' + boxen(
|
|
270
|
+
chalk.bold.green(' Setup Complete! ') + '\n\n' +
|
|
271
|
+
chalk.white('Vibe Coding is about flow. You describe, we code.') + '\n' +
|
|
272
|
+
chalk.white('Using your local IDEs + Cloud LLMs for maximum power.'),
|
|
273
|
+
{
|
|
274
|
+
padding: 1,
|
|
275
|
+
margin: 1,
|
|
276
|
+
borderStyle: 'round',
|
|
277
|
+
borderColor: 'green'
|
|
278
|
+
}
|
|
279
|
+
));
|
|
280
|
+
|
|
281
|
+
console.log(chalk.cyan('Press any key to continue to the main menu...'));
|
|
282
|
+
|
|
283
|
+
await new Promise((resolve) => {
|
|
284
|
+
process.stdin.setRawMode(true);
|
|
285
|
+
process.stdin.resume();
|
|
286
|
+
process.stdin.once('data', () => {
|
|
287
|
+
process.stdin.setRawMode(false);
|
|
288
|
+
resolve();
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
module.exports = { checkFirstRun };
|