@nclamvn/vibecode-cli 2.2.1 → 3.0.0

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,728 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════════
2
+ // VIBECODE CLI - Deploy Command
3
+ // One-command deployment to cloud platforms
4
+ // ═══════════════════════════════════════════════════════════════════════════════
5
+
6
+ import { spawn, exec } from 'child_process';
7
+ import { promisify } from 'util';
8
+ import fs from 'fs/promises';
9
+ import path from 'path';
10
+ import chalk from 'chalk';
11
+ import inquirer from 'inquirer';
12
+ import { notifyDeployComplete } from '../utils/notifications.js';
13
+
14
+ const execAsync = promisify(exec);
15
+
16
+ // Supported deployment platforms
17
+ const PLATFORMS = {
18
+ vercel: {
19
+ name: 'Vercel',
20
+ icon: '▲',
21
+ cli: 'vercel',
22
+ installCmd: 'npm install -g vercel',
23
+ deployCmd: 'vercel',
24
+ previewCmd: 'vercel',
25
+ prodCmd: 'vercel --prod',
26
+ loginCmd: 'vercel login',
27
+ whoamiCmd: 'vercel whoami',
28
+ configFile: 'vercel.json',
29
+ recommended: true
30
+ },
31
+ netlify: {
32
+ name: 'Netlify',
33
+ icon: '◆',
34
+ cli: 'netlify',
35
+ installCmd: 'npm install -g netlify-cli',
36
+ deployCmd: 'netlify deploy',
37
+ previewCmd: 'netlify deploy',
38
+ prodCmd: 'netlify deploy --prod',
39
+ loginCmd: 'netlify login',
40
+ whoamiCmd: 'netlify status',
41
+ configFile: 'netlify.toml',
42
+ recommended: false
43
+ },
44
+ 'github-pages': {
45
+ name: 'GitHub Pages',
46
+ icon: '⬡',
47
+ cli: 'gh',
48
+ installCmd: 'npm install -g gh-pages && brew install gh',
49
+ deployCmd: 'npx gh-pages -d dist',
50
+ previewCmd: null,
51
+ prodCmd: 'npx gh-pages -d dist',
52
+ loginCmd: 'gh auth login',
53
+ whoamiCmd: 'gh auth status',
54
+ configFile: null,
55
+ recommended: false,
56
+ requiresBuild: true,
57
+ buildCmd: 'npm run build'
58
+ },
59
+ railway: {
60
+ name: 'Railway',
61
+ icon: '🚂',
62
+ cli: 'railway',
63
+ installCmd: 'npm install -g @railway/cli',
64
+ deployCmd: 'railway up',
65
+ previewCmd: 'railway up',
66
+ prodCmd: 'railway up',
67
+ loginCmd: 'railway login',
68
+ whoamiCmd: 'railway whoami',
69
+ configFile: 'railway.json',
70
+ recommended: false
71
+ },
72
+ render: {
73
+ name: 'Render',
74
+ icon: '🎨',
75
+ cli: 'render',
76
+ installCmd: 'npm install -g render-cli',
77
+ deployCmd: 'render deploy',
78
+ previewCmd: 'render deploy',
79
+ prodCmd: 'render deploy',
80
+ loginCmd: 'render login',
81
+ whoamiCmd: 'render whoami',
82
+ configFile: 'render.yaml',
83
+ recommended: false
84
+ }
85
+ };
86
+
87
+ /**
88
+ * Deploy command entry point
89
+ */
90
+ export async function deployCommand(options = {}) {
91
+ const cwd = process.cwd();
92
+
93
+ // Check deployment status
94
+ if (options.status) {
95
+ return showDeploymentStatus(cwd);
96
+ }
97
+
98
+ // Show deployment history
99
+ if (options.history) {
100
+ return showDeploymentHistory(cwd);
101
+ }
102
+
103
+ // Detect platform from options
104
+ let platform = null;
105
+ if (options.vercel) platform = 'vercel';
106
+ else if (options.netlify) platform = 'netlify';
107
+ else if (options.githubPages) platform = 'github-pages';
108
+ else if (options.railway) platform = 'railway';
109
+ else if (options.render) platform = 'render';
110
+
111
+ // Interactive mode if no platform specified
112
+ if (!platform) {
113
+ platform = await selectPlatform();
114
+ if (!platform) return;
115
+ }
116
+
117
+ const platformConfig = PLATFORMS[platform];
118
+
119
+ console.log(chalk.cyan(`
120
+ ╭────────────────────────────────────────────────────────────────────╮
121
+ │ 🚀 VIBECODE DEPLOY │
122
+ │ │
123
+ │ Platform: ${(platformConfig.icon + ' ' + platformConfig.name).padEnd(51)}│
124
+ │ │
125
+ ╰────────────────────────────────────────────────────────────────────╯
126
+ `));
127
+
128
+ // Step 1: Check if project is valid
129
+ const isValidProject = await checkProject(cwd);
130
+ if (!isValidProject) {
131
+ console.log(chalk.red(' ❌ No package.json found. Is this a valid project?\n'));
132
+ return;
133
+ }
134
+
135
+ // Step 2: Check if CLI is installed
136
+ console.log(chalk.gray(' Checking prerequisites...\n'));
137
+ const cliInstalled = await checkCLI(platformConfig.cli);
138
+
139
+ if (!cliInstalled) {
140
+ console.log(chalk.yellow(` ⚠️ ${platformConfig.name} CLI not found.\n`));
141
+
142
+ const { install } = await inquirer.prompt([{
143
+ type: 'confirm',
144
+ name: 'install',
145
+ message: `Install ${platformConfig.name} CLI?`,
146
+ default: true
147
+ }]);
148
+
149
+ if (install) {
150
+ const installed = await installCLI(platformConfig);
151
+ if (!installed) return;
152
+ } else {
153
+ console.log(chalk.gray(`\n Install manually: ${platformConfig.installCmd}\n`));
154
+ return;
155
+ }
156
+ } else {
157
+ console.log(chalk.green(` ✓ ${platformConfig.name} CLI installed`));
158
+ }
159
+
160
+ // Step 3: Check authentication
161
+ const isLoggedIn = await checkAuth(platformConfig);
162
+
163
+ if (!isLoggedIn) {
164
+ console.log(chalk.yellow('\n 🔐 Authentication required.\n'));
165
+ const authenticated = await authenticate(platformConfig);
166
+ if (!authenticated) {
167
+ console.log(chalk.red(' ❌ Authentication failed.\n'));
168
+ return;
169
+ }
170
+ } else {
171
+ console.log(chalk.green(` ✓ Authenticated`));
172
+ }
173
+
174
+ // Step 4: Check/Create config
175
+ await ensureConfig(cwd, platform, platformConfig, options);
176
+
177
+ // Step 5: Build if needed
178
+ if (platformConfig.requiresBuild) {
179
+ console.log(chalk.yellow('\n 📦 Building project...\n'));
180
+ const buildSuccess = await buildProject(cwd, platformConfig);
181
+ if (!buildSuccess) {
182
+ console.log(chalk.red(' ❌ Build failed. Fix errors and try again.\n'));
183
+ return;
184
+ }
185
+ }
186
+
187
+ // Step 6: Deploy
188
+ console.log(chalk.yellow('\n 🚀 Deploying...\n'));
189
+
190
+ const isProduction = !options.preview;
191
+ const result = await deploy(cwd, platformConfig, isProduction, options);
192
+
193
+ // Step 7: Save deployment info
194
+ await saveDeploymentInfo(cwd, platform, result);
195
+
196
+ // Step 8: Send notification if enabled
197
+ if (options.notify) {
198
+ notifyDeployComplete(result.success, platformConfig.name, result.url);
199
+ }
200
+
201
+ // Step 9: Show result
202
+ showDeploymentResult(result, platformConfig);
203
+
204
+ return result;
205
+ }
206
+
207
+ /**
208
+ * Interactive platform selection
209
+ */
210
+ async function selectPlatform() {
211
+ console.log(chalk.cyan(`
212
+ ╭────────────────────────────────────────────────────────────────────╮
213
+ │ 🚀 VIBECODE DEPLOY │
214
+ │ │
215
+ │ Deploy your project to the cloud │
216
+ │ │
217
+ ╰────────────────────────────────────────────────────────────────────╯
218
+ `));
219
+
220
+ const choices = Object.entries(PLATFORMS).map(([key, config]) => ({
221
+ name: `${config.icon} ${config.name}${config.recommended ? chalk.green(' (Recommended)') : ''}`,
222
+ value: key
223
+ }));
224
+
225
+ choices.push({ name: '👋 Cancel', value: null });
226
+
227
+ const { platform } = await inquirer.prompt([{
228
+ type: 'list',
229
+ name: 'platform',
230
+ message: 'Select deployment platform:',
231
+ choices
232
+ }]);
233
+
234
+ return platform;
235
+ }
236
+
237
+ /**
238
+ * Check if project is valid
239
+ */
240
+ async function checkProject(cwd) {
241
+ try {
242
+ await fs.access(path.join(cwd, 'package.json'));
243
+ return true;
244
+ } catch {
245
+ return false;
246
+ }
247
+ }
248
+
249
+ /**
250
+ * Check if CLI tool is installed
251
+ */
252
+ async function checkCLI(cli) {
253
+ try {
254
+ await execAsync(`which ${cli}`);
255
+ return true;
256
+ } catch {
257
+ // Also try npx for some tools
258
+ try {
259
+ await execAsync(`npx ${cli} --version`);
260
+ return true;
261
+ } catch {
262
+ return false;
263
+ }
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Install CLI tool
269
+ */
270
+ async function installCLI(platformConfig) {
271
+ console.log(chalk.yellow(`\n 📦 Installing ${platformConfig.name} CLI...\n`));
272
+
273
+ return new Promise((resolve) => {
274
+ const child = spawn('sh', ['-c', platformConfig.installCmd], {
275
+ stdio: 'inherit'
276
+ });
277
+
278
+ child.on('close', (code) => {
279
+ if (code === 0) {
280
+ console.log(chalk.green(`\n ✅ ${platformConfig.name} CLI installed\n`));
281
+ resolve(true);
282
+ } else {
283
+ console.log(chalk.red(`\n ❌ Installation failed.`));
284
+ console.log(chalk.gray(` Try manually: ${platformConfig.installCmd}\n`));
285
+ resolve(false);
286
+ }
287
+ });
288
+
289
+ child.on('error', () => {
290
+ console.log(chalk.red(`\n ❌ Installation failed.`));
291
+ resolve(false);
292
+ });
293
+ });
294
+ }
295
+
296
+ /**
297
+ * Check if user is authenticated
298
+ */
299
+ async function checkAuth(platformConfig) {
300
+ if (!platformConfig.whoamiCmd) return true;
301
+
302
+ try {
303
+ await execAsync(platformConfig.whoamiCmd);
304
+ return true;
305
+ } catch {
306
+ return false;
307
+ }
308
+ }
309
+
310
+ /**
311
+ * Authenticate with platform
312
+ */
313
+ async function authenticate(platformConfig) {
314
+ return new Promise((resolve) => {
315
+ const child = spawn('sh', ['-c', platformConfig.loginCmd], {
316
+ stdio: 'inherit'
317
+ });
318
+
319
+ child.on('close', (code) => {
320
+ if (code === 0) {
321
+ console.log(chalk.green('\n ✅ Authentication complete\n'));
322
+ resolve(true);
323
+ } else {
324
+ resolve(false);
325
+ }
326
+ });
327
+
328
+ child.on('error', () => {
329
+ resolve(false);
330
+ });
331
+ });
332
+ }
333
+
334
+ /**
335
+ * Ensure deployment config exists
336
+ */
337
+ async function ensureConfig(cwd, platform, platformConfig, options) {
338
+ if (!platformConfig.configFile) return;
339
+
340
+ const configPath = path.join(cwd, platformConfig.configFile);
341
+
342
+ try {
343
+ await fs.access(configPath);
344
+ console.log(chalk.green(` ✓ ${platformConfig.configFile} found`));
345
+ } catch {
346
+ console.log(chalk.yellow(` 📝 Creating ${platformConfig.configFile}...`));
347
+
348
+ const config = await generateConfig(cwd, platform, options);
349
+
350
+ if (platform === 'netlify') {
351
+ // Netlify uses TOML format
352
+ const toml = generateNetlifyToml(config);
353
+ await fs.writeFile(configPath, toml);
354
+ } else {
355
+ await fs.writeFile(configPath, JSON.stringify(config, null, 2));
356
+ }
357
+
358
+ console.log(chalk.green(` ✅ ${platformConfig.configFile} created`));
359
+ }
360
+ }
361
+
362
+ /**
363
+ * Generate platform config based on project type
364
+ */
365
+ async function generateConfig(cwd, platform, options) {
366
+ // Detect project type
367
+ const pkgPath = path.join(cwd, 'package.json');
368
+ let framework = 'static';
369
+ let buildCommand = 'npm run build';
370
+ let outputDirectory = 'dist';
371
+
372
+ try {
373
+ const pkg = JSON.parse(await fs.readFile(pkgPath, 'utf-8'));
374
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
375
+
376
+ if (deps.next) {
377
+ framework = 'nextjs';
378
+ outputDirectory = '.next';
379
+ buildCommand = 'next build';
380
+ } else if (deps.vite) {
381
+ framework = 'vite';
382
+ outputDirectory = 'dist';
383
+ } else if (deps['react-scripts']) {
384
+ framework = 'create-react-app';
385
+ outputDirectory = 'build';
386
+ } else if (deps.vue || deps['@vue/cli-service']) {
387
+ framework = 'vue';
388
+ outputDirectory = 'dist';
389
+ } else if (deps.nuxt) {
390
+ framework = 'nuxt';
391
+ outputDirectory = '.output';
392
+ } else if (deps['@sveltejs/kit']) {
393
+ framework = 'sveltekit';
394
+ outputDirectory = 'build';
395
+ }
396
+ } catch {}
397
+
398
+ switch (platform) {
399
+ case 'vercel':
400
+ return {
401
+ version: 2,
402
+ framework,
403
+ buildCommand,
404
+ outputDirectory,
405
+ ...(options.domain && { alias: [options.domain] })
406
+ };
407
+
408
+ case 'netlify':
409
+ return {
410
+ build: {
411
+ command: buildCommand,
412
+ publish: outputDirectory
413
+ }
414
+ };
415
+
416
+ case 'railway':
417
+ return {
418
+ build: {
419
+ builder: 'NIXPACKS'
420
+ },
421
+ deploy: {
422
+ startCommand: 'npm start'
423
+ }
424
+ };
425
+
426
+ case 'render':
427
+ return {
428
+ services: [{
429
+ type: 'web',
430
+ name: path.basename(cwd),
431
+ env: 'node',
432
+ buildCommand,
433
+ startCommand: 'npm start'
434
+ }]
435
+ };
436
+
437
+ default:
438
+ return {};
439
+ }
440
+ }
441
+
442
+ /**
443
+ * Generate Netlify TOML config
444
+ */
445
+ function generateNetlifyToml(config) {
446
+ return `[build]
447
+ command = "${config.build?.command || 'npm run build'}"
448
+ publish = "${config.build?.publish || 'dist'}"
449
+
450
+ [[redirects]]
451
+ from = "/*"
452
+ to = "/index.html"
453
+ status = 200
454
+ `;
455
+ }
456
+
457
+ /**
458
+ * Build project before deployment
459
+ */
460
+ async function buildProject(cwd, platformConfig) {
461
+ return new Promise((resolve) => {
462
+ const buildCmd = platformConfig.buildCmd || 'npm run build';
463
+
464
+ const child = spawn('sh', ['-c', buildCmd], {
465
+ cwd,
466
+ stdio: 'inherit'
467
+ });
468
+
469
+ child.on('close', (code) => {
470
+ if (code === 0) {
471
+ console.log(chalk.green(' ✅ Build complete'));
472
+ resolve(true);
473
+ } else {
474
+ resolve(false);
475
+ }
476
+ });
477
+
478
+ child.on('error', () => {
479
+ resolve(false);
480
+ });
481
+ });
482
+ }
483
+
484
+ /**
485
+ * Execute deployment
486
+ */
487
+ async function deploy(cwd, platformConfig, isProduction, options) {
488
+ return new Promise((resolve) => {
489
+ let cmd = isProduction ? platformConfig.prodCmd : platformConfig.previewCmd;
490
+
491
+ // Add domain if specified
492
+ if (options.domain && platformConfig.name === 'Vercel') {
493
+ cmd = `${cmd} --alias ${options.domain}`;
494
+ }
495
+
496
+ let output = '';
497
+ let deployUrl = '';
498
+
499
+ const child = spawn('sh', ['-c', cmd], {
500
+ cwd,
501
+ stdio: ['inherit', 'pipe', 'pipe']
502
+ });
503
+
504
+ child.stdout.on('data', (data) => {
505
+ const text = data.toString();
506
+ output += text;
507
+ process.stdout.write(text);
508
+
509
+ // Extract deployment URL from various formats
510
+ const urlPatterns = [
511
+ /https:\/\/[a-zA-Z0-9-]+\.vercel\.app[^\s]*/,
512
+ /https:\/\/[a-zA-Z0-9-]+\.netlify\.app[^\s]*/,
513
+ /https:\/\/[a-zA-Z0-9-]+\.github\.io[^\s]*/,
514
+ /https:\/\/[a-zA-Z0-9-]+\.up\.railway\.app[^\s]*/,
515
+ /https:\/\/[a-zA-Z0-9-]+\.onrender\.com[^\s]*/,
516
+ /https:\/\/[^\s]+/
517
+ ];
518
+
519
+ for (const pattern of urlPatterns) {
520
+ const match = text.match(pattern);
521
+ if (match) {
522
+ deployUrl = match[0].replace(/['")\]]+$/, ''); // Clean trailing chars
523
+ break;
524
+ }
525
+ }
526
+ });
527
+
528
+ child.stderr.on('data', (data) => {
529
+ const text = data.toString();
530
+ output += text;
531
+ process.stderr.write(text);
532
+ });
533
+
534
+ child.on('close', (code) => {
535
+ resolve({
536
+ success: code === 0,
537
+ url: deployUrl,
538
+ output,
539
+ isProduction,
540
+ timestamp: new Date().toISOString()
541
+ });
542
+ });
543
+
544
+ child.on('error', (error) => {
545
+ resolve({
546
+ success: false,
547
+ url: '',
548
+ output: error.message,
549
+ isProduction,
550
+ timestamp: new Date().toISOString()
551
+ });
552
+ });
553
+ });
554
+ }
555
+
556
+ /**
557
+ * Save deployment info to history
558
+ */
559
+ async function saveDeploymentInfo(cwd, platform, result) {
560
+ const vibecodeDir = path.join(cwd, '.vibecode');
561
+ const deploymentsPath = path.join(vibecodeDir, 'deployments.json');
562
+
563
+ await fs.mkdir(vibecodeDir, { recursive: true });
564
+
565
+ let deployments = [];
566
+ try {
567
+ const existing = await fs.readFile(deploymentsPath, 'utf-8');
568
+ deployments = JSON.parse(existing);
569
+ } catch {}
570
+
571
+ deployments.unshift({
572
+ platform,
573
+ url: result.url,
574
+ isProduction: result.isProduction,
575
+ timestamp: result.timestamp,
576
+ success: result.success
577
+ });
578
+
579
+ // Keep last 20 deployments
580
+ deployments = deployments.slice(0, 20);
581
+
582
+ await fs.writeFile(deploymentsPath, JSON.stringify(deployments, null, 2));
583
+ }
584
+
585
+ /**
586
+ * Show deployment result
587
+ */
588
+ function showDeploymentResult(result, platformConfig) {
589
+ if (result.success) {
590
+ const urlDisplay = result.url || 'Check console output above';
591
+ const urlPadded = urlDisplay.length > 50 ? urlDisplay.substring(0, 47) + '...' : urlDisplay;
592
+
593
+ console.log(chalk.green(`
594
+ ╭────────────────────────────────────────────────────────────────────╮
595
+ │ ✅ DEPLOYMENT SUCCESSFUL │
596
+ │ │
597
+ │ ${platformConfig.icon} Platform: ${platformConfig.name.padEnd(49)}│
598
+ │ 🌐 URL: ${urlPadded.padEnd(53)}│
599
+ │ 📅 Time: ${new Date().toLocaleString().padEnd(52)}│
600
+ │ 🏷️ Type: ${(result.isProduction ? 'Production' : 'Preview').padEnd(52)}│
601
+ │ │
602
+ ╰────────────────────────────────────────────────────────────────────╯
603
+ `));
604
+
605
+ if (result.url) {
606
+ console.log(chalk.cyan(` 🔗 ${result.url}\n`));
607
+ }
608
+ } else {
609
+ console.log(chalk.red(`
610
+ ╭────────────────────────────────────────────────────────────────────╮
611
+ │ ❌ DEPLOYMENT FAILED │
612
+ │ │
613
+ │ Check the error output above for details. │
614
+ │ │
615
+ │ Common fixes: │
616
+ │ • Run 'npm run build' to check for build errors │
617
+ │ • Check ${(platformConfig.configFile || 'configuration').padEnd(45)}│
618
+ │ • Verify authentication: ${platformConfig.loginCmd.padEnd(35)}│
619
+ │ │
620
+ ╰────────────────────────────────────────────────────────────────────╯
621
+ `));
622
+ }
623
+ }
624
+
625
+ /**
626
+ * Show deployment status
627
+ */
628
+ async function showDeploymentStatus(cwd) {
629
+ console.log(chalk.cyan(`
630
+ ╭────────────────────────────────────────────────────────────────────╮
631
+ │ 📊 DEPLOYMENT STATUS │
632
+ ╰────────────────────────────────────────────────────────────────────╯
633
+ `));
634
+
635
+ try {
636
+ const deploymentsPath = path.join(cwd, '.vibecode', 'deployments.json');
637
+ const deployments = JSON.parse(await fs.readFile(deploymentsPath, 'utf-8'));
638
+
639
+ if (deployments.length === 0) {
640
+ console.log(chalk.gray(' No deployments found.\n'));
641
+ return;
642
+ }
643
+
644
+ const latest = deployments[0];
645
+ const platformConfig = PLATFORMS[latest.platform];
646
+
647
+ console.log(chalk.white.bold(' Latest Deployment:\n'));
648
+ console.log(chalk.gray(` Platform: ${platformConfig?.icon || ''} ${latest.platform}`));
649
+ console.log(chalk.gray(` URL: ${latest.url || 'N/A'}`));
650
+ console.log(chalk.gray(` Type: ${latest.isProduction ? 'Production' : 'Preview'}`));
651
+ console.log(chalk.gray(` Time: ${new Date(latest.timestamp).toLocaleString()}`));
652
+ console.log(chalk.gray(` Status: ${latest.success ? chalk.green('Success') : chalk.red('Failed')}`));
653
+ console.log('');
654
+
655
+ if (latest.url) {
656
+ console.log(chalk.cyan(` 🔗 ${latest.url}\n`));
657
+ }
658
+
659
+ } catch {
660
+ console.log(chalk.gray(' No deployment history found.\n'));
661
+ console.log(chalk.gray(` Run ${chalk.cyan('vibecode deploy')} to deploy your project.\n`));
662
+ }
663
+ }
664
+
665
+ /**
666
+ * Show deployment history
667
+ */
668
+ async function showDeploymentHistory(cwd) {
669
+ console.log(chalk.cyan(`
670
+ ╭────────────────────────────────────────────────────────────────────╮
671
+ │ 📜 DEPLOYMENT HISTORY │
672
+ ╰────────────────────────────────────────────────────────────────────╯
673
+ `));
674
+
675
+ try {
676
+ const deploymentsPath = path.join(cwd, '.vibecode', 'deployments.json');
677
+ const deployments = JSON.parse(await fs.readFile(deploymentsPath, 'utf-8'));
678
+
679
+ if (deployments.length === 0) {
680
+ console.log(chalk.gray(' No deployments found.\n'));
681
+ return;
682
+ }
683
+
684
+ console.log(chalk.gray(' Status Platform Type Date'));
685
+ console.log(chalk.gray(' ─────────────────────────────────────────────────────────'));
686
+
687
+ for (const dep of deployments.slice(0, 10)) {
688
+ const platformConfig = PLATFORMS[dep.platform];
689
+ const status = dep.success ? chalk.green(' ✓') : chalk.red(' ✗');
690
+ const type = dep.isProduction ? 'prod ' : 'preview';
691
+ const date = new Date(dep.timestamp).toLocaleDateString();
692
+ const icon = platformConfig?.icon || ' ';
693
+
694
+ console.log(`${status} ${icon} ${dep.platform.padEnd(12)} ${type} ${date}`);
695
+ if (dep.url) {
696
+ console.log(chalk.gray(` ${dep.url}`));
697
+ }
698
+ }
699
+ console.log('');
700
+
701
+ } catch {
702
+ console.log(chalk.gray(' No deployment history found.\n'));
703
+ console.log(chalk.gray(` Run ${chalk.cyan('vibecode deploy')} to deploy your project.\n`));
704
+ }
705
+ }
706
+
707
+ /**
708
+ * Auto-deploy for use in go command
709
+ */
710
+ export async function autoDeploy(projectPath, options = {}) {
711
+ const originalCwd = process.cwd();
712
+
713
+ try {
714
+ process.chdir(projectPath);
715
+
716
+ return await deployCommand({
717
+ vercel: options.platform !== 'netlify',
718
+ netlify: options.platform === 'netlify',
719
+ preview: options.preview || false,
720
+ domain: options.domain
721
+ });
722
+ } catch (error) {
723
+ console.log(chalk.yellow(` ⚠️ Deploy failed: ${error.message}\n`));
724
+ return { success: false };
725
+ } finally {
726
+ process.chdir(originalCwd);
727
+ }
728
+ }