@nclamvn/vibecode-cli 2.2.0 → 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,473 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════════
2
+ // VIBECODE CLI - Feedback Command
3
+ // Interactive feedback mode for incremental changes
4
+ // ═══════════════════════════════════════════════════════════════════════════════
5
+
6
+ import { spawn } from 'child_process';
7
+ import fs from 'fs/promises';
8
+ import path from 'path';
9
+ import chalk from 'chalk';
10
+ import readline from 'readline';
11
+ import net from 'net';
12
+ import { BackupManager } from '../core/backup.js';
13
+ import { spawnClaudeCode, buildPromptWithContext } from '../providers/index.js';
14
+
15
+ /**
16
+ * Feedback command entry point
17
+ */
18
+ export async function feedbackCommand(options = {}) {
19
+ const cwd = process.cwd();
20
+
21
+ // Check if valid project
22
+ const isValid = await isValidProject(cwd);
23
+ if (!isValid) {
24
+ console.log(chalk.red(`
25
+ ╭────────────────────────────────────────────────────────────────────╮
26
+ │ ❌ NOT A VALID PROJECT │
27
+ │ │
28
+ │ Run this command inside a project with package.json, or use: │
29
+ │ vibecode go "description" --feedback │
30
+ │ │
31
+ ╰────────────────────────────────────────────────────────────────────╯
32
+ `));
33
+ return;
34
+ }
35
+
36
+ // Initialize
37
+ const projectName = path.basename(cwd);
38
+ const backup = new BackupManager(cwd);
39
+ const changeHistory = [];
40
+ let devProcess = null;
41
+ let changeCount = 0;
42
+ const port = parseInt(options.port) || 3000;
43
+
44
+ console.log(chalk.cyan(`
45
+ ╭────────────────────────────────────────────────────────────────────╮
46
+ │ 💬 VIBECODE FEEDBACK MODE │
47
+ │ │
48
+ │ Project: ${projectName.padEnd(52)}│
49
+ │ │
50
+ │ Commands: │
51
+ │ • Type your changes in natural language │
52
+ │ • 'undo' - Revert last change │
53
+ │ • 'history' - Show change history │
54
+ │ • 'preview' - Open/refresh preview │
55
+ │ • 'status' - Show current status │
56
+ │ • 'files' - List recently changed files │
57
+ │ • 'done' or 'exit' - End session │
58
+ │ │
59
+ ╰────────────────────────────────────────────────────────────────────╯
60
+ `));
61
+
62
+ // Start dev server if preview mode
63
+ if (options.preview) {
64
+ console.log(chalk.yellow(' 🚀 Starting preview server...\n'));
65
+ devProcess = await startDevServer(cwd, port);
66
+ const serverReady = await waitForServer(port);
67
+
68
+ if (serverReady) {
69
+ await openBrowser(`http://localhost:${port}`);
70
+ console.log(chalk.green(` ✅ Preview ready at http://localhost:${port}\n`));
71
+ } else {
72
+ console.log(chalk.yellow(` ⚠️ Server may still be starting...\n`));
73
+ }
74
+ }
75
+
76
+ // Create readline interface
77
+ const rl = readline.createInterface({
78
+ input: process.stdin,
79
+ output: process.stdout,
80
+ prompt: chalk.green('feedback> ')
81
+ });
82
+
83
+ rl.prompt();
84
+
85
+ rl.on('line', async (line) => {
86
+ const input = line.trim();
87
+ const inputLower = input.toLowerCase();
88
+
89
+ // Exit commands
90
+ if (inputLower === 'done' || inputLower === 'exit' || inputLower === 'quit' || inputLower === 'q') {
91
+ await endSession(devProcess, changeCount, rl);
92
+ return;
93
+ }
94
+
95
+ // Undo command
96
+ if (inputLower === 'undo') {
97
+ if (changeHistory.length === 0) {
98
+ console.log(chalk.yellow(' No changes to undo.\n'));
99
+ } else {
100
+ const lastChange = changeHistory.pop();
101
+ try {
102
+ await backup.restoreBackup(lastChange.backupId);
103
+ changeCount--;
104
+ console.log(chalk.green(` ↩️ Reverted: "${lastChange.description}"\n`));
105
+
106
+ if (options.preview) {
107
+ console.log(chalk.gray(' 🔄 Preview will refresh automatically.\n'));
108
+ }
109
+ } catch (error) {
110
+ console.log(chalk.red(` ❌ Undo failed: ${error.message}\n`));
111
+ }
112
+ }
113
+ rl.prompt();
114
+ return;
115
+ }
116
+
117
+ // History command
118
+ if (inputLower === 'history') {
119
+ if (changeHistory.length === 0) {
120
+ console.log(chalk.gray(' No changes yet.\n'));
121
+ } else {
122
+ console.log(chalk.cyan('\n 📜 Change History:\n'));
123
+ changeHistory.forEach((change, i) => {
124
+ const time = new Date(change.timestamp).toLocaleTimeString();
125
+ console.log(chalk.gray(` ${i + 1}. [${time}] ${change.description}`));
126
+ });
127
+ console.log('');
128
+ }
129
+ rl.prompt();
130
+ return;
131
+ }
132
+
133
+ // Preview command
134
+ if (inputLower === 'preview') {
135
+ if (!devProcess) {
136
+ console.log(chalk.yellow(' 🚀 Starting preview server...\n'));
137
+ devProcess = await startDevServer(cwd, port);
138
+ await waitForServer(port);
139
+ }
140
+ await openBrowser(`http://localhost:${port}`);
141
+ console.log(chalk.green(` ✅ Preview opened at http://localhost:${port}\n`));
142
+ rl.prompt();
143
+ return;
144
+ }
145
+
146
+ // Status command
147
+ if (inputLower === 'status') {
148
+ console.log(chalk.cyan(`\n 📊 Session Status:`));
149
+ console.log(chalk.gray(` ─────────────────────────────────────`));
150
+ console.log(chalk.white(` Project: ${projectName}`));
151
+ console.log(chalk.white(` Changes: ${changeCount}`));
152
+ console.log(chalk.white(` Preview: ${devProcess ? chalk.green('Running') : chalk.gray('Not running')}`));
153
+ console.log(chalk.white(` Undoable: ${changeHistory.length}`));
154
+ console.log('');
155
+ rl.prompt();
156
+ return;
157
+ }
158
+
159
+ // Files command
160
+ if (inputLower === 'files') {
161
+ await showRecentFiles(cwd);
162
+ rl.prompt();
163
+ return;
164
+ }
165
+
166
+ // Help command
167
+ if (inputLower === 'help' || inputLower === '?') {
168
+ showHelp();
169
+ rl.prompt();
170
+ return;
171
+ }
172
+
173
+ // Clear command
174
+ if (inputLower === 'clear') {
175
+ console.clear();
176
+ console.log(chalk.cyan(` 💬 Feedback Mode - ${changeCount} changes applied\n`));
177
+ rl.prompt();
178
+ return;
179
+ }
180
+
181
+ // Empty or too short input
182
+ if (!input || input.length < 3) {
183
+ rl.prompt();
184
+ return;
185
+ }
186
+
187
+ // Process change request
188
+ console.log(chalk.yellow('\n 🔄 Processing change...\n'));
189
+
190
+ try {
191
+ // Create backup before change
192
+ const backupId = await backup.createBackup(`feedback-${changeCount + 1}`);
193
+
194
+ // Build prompt for Claude
195
+ const prompt = buildChangePrompt(cwd, input);
196
+
197
+ // Build full prompt with context
198
+ const fullPrompt = await buildPromptWithContext(prompt, cwd);
199
+
200
+ // Execute change with Claude
201
+ const result = await spawnClaudeCode(fullPrompt, { cwd });
202
+
203
+ // Record change
204
+ const description = input.substring(0, 50) + (input.length > 50 ? '...' : '');
205
+ changeHistory.push({
206
+ description,
207
+ backupId,
208
+ timestamp: new Date().toISOString(),
209
+ files: result.filesChanged || []
210
+ });
211
+ changeCount++;
212
+
213
+ console.log(chalk.green(`\n ✅ Change #${changeCount} applied: "${description}"`));
214
+
215
+ if (options.preview) {
216
+ console.log(chalk.gray(' 🔄 Preview refreshing...\n'));
217
+ } else {
218
+ console.log('');
219
+ }
220
+
221
+ } catch (error) {
222
+ console.log(chalk.red(`\n ❌ Error: ${error.message}`));
223
+ console.log(chalk.gray(' Try rephrasing your request or be more specific.\n'));
224
+ }
225
+
226
+ rl.prompt();
227
+ });
228
+
229
+ rl.on('close', () => {
230
+ if (devProcess) {
231
+ devProcess.kill();
232
+ }
233
+ process.exit(0);
234
+ });
235
+
236
+ // Handle Ctrl+C
237
+ process.on('SIGINT', async () => {
238
+ await endSession(devProcess, changeCount, rl);
239
+ });
240
+ }
241
+
242
+ /**
243
+ * End feedback session
244
+ */
245
+ async function endSession(devProcess, changeCount, rl) {
246
+ console.log(chalk.cyan(`\n
247
+ ╭────────────────────────────────────────────────────────────────────╮
248
+ │ 👋 FEEDBACK SESSION ENDED │
249
+ │ │
250
+ │ Total changes applied: ${String(changeCount).padEnd(38)}│
251
+ │ │
252
+ │ Your changes have been saved. Use 'vibecode undo' to rollback. │
253
+ │ │
254
+ ╰────────────────────────────────────────────────────────────────────╯
255
+ `));
256
+
257
+ if (devProcess) {
258
+ devProcess.kill();
259
+ }
260
+ rl.close();
261
+ process.exit(0);
262
+ }
263
+
264
+ /**
265
+ * Build change prompt for Claude
266
+ */
267
+ function buildChangePrompt(cwd, request) {
268
+ return `
269
+ # Incremental Change Request
270
+
271
+ ## Project: ${path.basename(cwd)}
272
+
273
+ ## User Request:
274
+ ${request}
275
+
276
+ ## Instructions:
277
+ 1. Make ONLY the requested change - nothing more, nothing less
278
+ 2. Preserve ALL existing functionality and code
279
+ 3. Keep changes minimal and surgically precise
280
+ 4. Update any related files if absolutely necessary
281
+ 5. Do NOT remove, modify, or refactor unrelated code
282
+ 6. Do NOT add comments explaining changes
283
+ 7. Do NOT create backup files
284
+
285
+ ## Critical Rules:
286
+ - This is an INCREMENTAL change, not a rebuild
287
+ - Make the smallest possible change to fulfill the request
288
+ - Maintain exact code style and formatting of existing code
289
+ - If the request is unclear, make a reasonable interpretation
290
+ - Do NOT add extra features or "improvements"
291
+
292
+ ## Apply the change now.
293
+ `;
294
+ }
295
+
296
+ /**
297
+ * Check if directory is a valid project
298
+ */
299
+ async function isValidProject(cwd) {
300
+ try {
301
+ await fs.access(path.join(cwd, 'package.json'));
302
+ return true;
303
+ } catch {
304
+ return false;
305
+ }
306
+ }
307
+
308
+ /**
309
+ * Start development server
310
+ */
311
+ async function startDevServer(cwd, port = 3000) {
312
+ // Detect project type and appropriate dev command
313
+ let devCmd = 'npm run dev';
314
+
315
+ try {
316
+ const pkgPath = path.join(cwd, 'package.json');
317
+ const pkg = JSON.parse(await fs.readFile(pkgPath, 'utf-8'));
318
+ const scripts = pkg.scripts || {};
319
+
320
+ if (scripts.dev) {
321
+ devCmd = 'npm run dev';
322
+ } else if (scripts.start) {
323
+ devCmd = 'npm run start';
324
+ }
325
+ } catch {}
326
+
327
+ return new Promise((resolve) => {
328
+ const child = spawn('sh', ['-c', devCmd], {
329
+ cwd,
330
+ stdio: ['ignore', 'pipe', 'pipe'],
331
+ env: { ...process.env, PORT: String(port) },
332
+ detached: false
333
+ });
334
+
335
+ child.stdout.on('data', () => {});
336
+ child.stderr.on('data', () => {});
337
+
338
+ // Give it time to start
339
+ setTimeout(() => resolve(child), 2000);
340
+ });
341
+ }
342
+
343
+ /**
344
+ * Wait for server to be ready
345
+ */
346
+ async function waitForServer(port, maxAttempts = 30) {
347
+ for (let i = 0; i < maxAttempts; i++) {
348
+ const isReady = await checkPort(port);
349
+ if (isReady) return true;
350
+ await sleep(500);
351
+ }
352
+ return false;
353
+ }
354
+
355
+ /**
356
+ * Check if port is in use (server running)
357
+ */
358
+ function checkPort(port) {
359
+ return new Promise((resolve) => {
360
+ const socket = new net.Socket();
361
+ socket.setTimeout(500);
362
+ socket.on('connect', () => { socket.destroy(); resolve(true); });
363
+ socket.on('timeout', () => { socket.destroy(); resolve(false); });
364
+ socket.on('error', () => { socket.destroy(); resolve(false); });
365
+ socket.connect(port, '127.0.0.1');
366
+ });
367
+ }
368
+
369
+ /**
370
+ * Open URL in browser
371
+ */
372
+ async function openBrowser(url) {
373
+ try {
374
+ const open = (await import('open')).default;
375
+ await open(url);
376
+ } catch {
377
+ // Fallback
378
+ const { exec } = await import('child_process');
379
+ const { promisify } = await import('util');
380
+ const execAsync = promisify(exec);
381
+
382
+ const platform = process.platform;
383
+ const commands = {
384
+ darwin: `open "${url}"`,
385
+ win32: `start "" "${url}"`,
386
+ linux: `xdg-open "${url}"`
387
+ };
388
+
389
+ if (commands[platform]) {
390
+ try {
391
+ await execAsync(commands[platform]);
392
+ } catch {}
393
+ }
394
+ }
395
+ }
396
+
397
+ /**
398
+ * Show recently changed files
399
+ */
400
+ async function showRecentFiles(cwd) {
401
+ console.log(chalk.cyan('\n 📁 Recent Files:\n'));
402
+
403
+ try {
404
+ const { exec } = await import('child_process');
405
+ const { promisify } = await import('util');
406
+ const execAsync = promisify(exec);
407
+
408
+ // Get recently modified files (last 5 minutes)
409
+ const { stdout } = await execAsync(
410
+ `find . -type f -mmin -5 -not -path "*/node_modules/*" -not -path "*/.git/*" -not -path "*/.next/*" -not -path "*/dist/*" 2>/dev/null | head -20`,
411
+ { cwd }
412
+ );
413
+
414
+ const files = stdout.trim().split('\n').filter(f => f);
415
+
416
+ if (files.length === 0) {
417
+ console.log(chalk.gray(' No recently modified files.\n'));
418
+ } else {
419
+ files.forEach(file => {
420
+ console.log(chalk.gray(` ${file}`));
421
+ });
422
+ console.log('');
423
+ }
424
+ } catch {
425
+ console.log(chalk.gray(' Could not list files.\n'));
426
+ }
427
+ }
428
+
429
+ /**
430
+ * Show help
431
+ */
432
+ function showHelp() {
433
+ console.log(chalk.cyan(`
434
+ 📖 Feedback Mode Commands:
435
+ ─────────────────────────────────────────────────────────────────
436
+
437
+ ${chalk.white('Natural language')} Describe the change you want
438
+ ${chalk.green('undo')} Revert the last change
439
+ ${chalk.green('history')} Show all changes made this session
440
+ ${chalk.green('preview')} Open preview in browser
441
+ ${chalk.green('status')} Show session status
442
+ ${chalk.green('files')} Show recently modified files
443
+ ${chalk.green('clear')} Clear the screen
444
+ ${chalk.green('done')} / ${chalk.green('exit')} End the session
445
+
446
+ ${chalk.gray('Examples:')}
447
+ ${chalk.gray('> Change the header color to blue')}
448
+ ${chalk.gray('> Add a contact form section')}
449
+ ${chalk.gray('> Remove the pricing table')}
450
+ ${chalk.gray('> Make the logo bigger')}
451
+ `));
452
+ }
453
+
454
+ /**
455
+ * Sleep helper
456
+ */
457
+ function sleep(ms) {
458
+ return new Promise(resolve => setTimeout(resolve, ms));
459
+ }
460
+
461
+ /**
462
+ * Start feedback mode for use in go.js
463
+ */
464
+ export async function startFeedbackMode(projectPath, options = {}) {
465
+ const originalCwd = process.cwd();
466
+
467
+ try {
468
+ process.chdir(projectPath);
469
+ await feedbackCommand(options);
470
+ } finally {
471
+ process.chdir(originalCwd);
472
+ }
473
+ }
@@ -42,6 +42,11 @@ import { runTests } from '../core/test-runner.js';
42
42
  import { analyzeErrors } from '../core/error-analyzer.js';
43
43
  import { ensureDir, appendToFile } from '../utils/files.js';
44
44
  import { getTemplate, getCategoryIcon } from '../templates/index.js';
45
+ import { autoGenerateImages } from './images.js';
46
+ import { autoDeploy } from './deploy.js';
47
+ import { startFeedbackMode } from './feedback.js';
48
+ import { notifyBuildComplete, notifyDeployComplete } from '../utils/notifications.js';
49
+ import { addToHistory } from '../utils/history.js';
45
50
 
46
51
  const execAsync = promisify(exec);
47
52
 
@@ -255,13 +260,91 @@ export async function goCommand(description, options = {}) {
255
260
  console.log(renderProgressBar(100));
256
261
  console.log();
257
262
 
263
+ // Generate images if requested
264
+ if (options.withImages) {
265
+ console.log(chalk.cyan('\n 📸 Generating project images...\n'));
266
+ try {
267
+ const imageResults = await autoGenerateImages(projectPath, {
268
+ template: null,
269
+ theme: 'tech'
270
+ });
271
+ results.imagesGenerated = imageResults.downloaded.length;
272
+ console.log(chalk.green(` ✅ ${results.imagesGenerated} images generated\n`));
273
+ } catch (error) {
274
+ console.log(chalk.yellow(` ⚠️ Image generation failed: ${error.message}\n`));
275
+ }
276
+ }
277
+
258
278
  // Show summary
259
279
  const duration = ((Date.now() - startTime) / 1000 / 60).toFixed(1);
260
280
  showMagicSummary(projectName, projectPath, duration, results, options);
261
281
 
262
- // Auto-open if requested
263
- if (options.open) {
282
+ // Add to history
283
+ await addToHistory(`vibecode go "${desc}"`, desc, {
284
+ projectName,
285
+ projectPath,
286
+ duration,
287
+ filesCreated: results.filesCreated,
288
+ testsPassed: results.allPassed
289
+ });
290
+
291
+ // Send notification if enabled
292
+ if (options.notify) {
293
+ notifyBuildComplete(results.allPassed, projectName);
294
+ }
295
+
296
+ // Auto preview if requested
297
+ if (options.preview) {
298
+ console.log(chalk.cyan('\n 🚀 Starting preview...\n'));
299
+ await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for files to settle
300
+
301
+ try {
302
+ const { autoPreview } = await import('./preview.js');
303
+ await autoPreview(projectPath, {
304
+ qr: options.qr,
305
+ port: options.port
306
+ });
307
+ } catch (error) {
308
+ console.log(chalk.yellow(` ⚠️ Preview failed: ${error.message}`));
309
+ console.log(chalk.gray(` Run manually: cd ${projectName} && vibecode preview\n`));
310
+ }
311
+ } else if (options.open) {
264
312
  await openProject(projectPath);
313
+ } else {
314
+ // Show preview hint
315
+ console.log(chalk.gray(` 💡 Quick preview: ${chalk.cyan(`cd ${projectName} && vibecode preview`)}\n`));
316
+ }
317
+
318
+ // Auto deploy if requested
319
+ if (options.deploy) {
320
+ console.log(chalk.cyan('\n 🚀 Deploying to cloud...\n'));
321
+ try {
322
+ const deployResult = await autoDeploy(projectPath, {
323
+ platform: options.deployPlatform || 'vercel',
324
+ preview: false
325
+ });
326
+ if (deployResult?.url) {
327
+ results.deployUrl = deployResult.url;
328
+ if (options.notify) {
329
+ notifyDeployComplete(true, options.deployPlatform || 'Vercel', deployResult.url);
330
+ }
331
+ }
332
+ } catch (error) {
333
+ console.log(chalk.yellow(` ⚠️ Deploy failed: ${error.message}`));
334
+ console.log(chalk.gray(` Run manually: cd ${projectName} && vibecode deploy\n`));
335
+ if (options.notify) {
336
+ notifyDeployComplete(false, options.deployPlatform || 'Vercel');
337
+ }
338
+ }
339
+ }
340
+
341
+ // Enter feedback mode if requested
342
+ if (options.feedback) {
343
+ console.log(chalk.cyan('\n 💬 Entering feedback mode...\n'));
344
+ await startFeedbackMode(projectPath, {
345
+ preview: options.preview || true,
346
+ port: options.port || '3000'
347
+ });
265
348
  }
266
349
 
267
350
  } catch (error) {
@@ -568,13 +651,92 @@ ${prompt}
568
651
  console.log(renderProgressBar(100));
569
652
  console.log();
570
653
 
654
+ // Generate images if requested
655
+ if (options.withImages) {
656
+ console.log(chalk.cyan('\n 📸 Generating project images...\n'));
657
+ try {
658
+ const imageResults = await autoGenerateImages(projectPath, {
659
+ template: templateId,
660
+ theme: 'tech'
661
+ });
662
+ results.imagesGenerated = imageResults.downloaded.length;
663
+ console.log(chalk.green(` ✅ ${results.imagesGenerated} images generated\n`));
664
+ } catch (error) {
665
+ console.log(chalk.yellow(` ⚠️ Image generation failed: ${error.message}\n`));
666
+ }
667
+ }
668
+
571
669
  // Show summary
572
670
  const duration = ((Date.now() - startTime) / 1000 / 60).toFixed(1);
573
671
  showTemplateSummary(template, projectName, projectPath, duration, results, options);
574
672
 
575
- // Auto-open if requested
576
- if (options.preview || options.open) {
673
+ // Add to history
674
+ await addToHistory(`vibecode go --template ${templateId}`, template.name, {
675
+ template: templateId,
676
+ projectName,
677
+ projectPath,
678
+ duration,
679
+ filesCreated: results.filesCreated,
680
+ testsPassed: results.allPassed
681
+ });
682
+
683
+ // Send notification if enabled
684
+ if (options.notify) {
685
+ notifyBuildComplete(results.allPassed, projectName);
686
+ }
687
+
688
+ // Auto preview if requested
689
+ if (options.preview) {
690
+ console.log(chalk.cyan('\n 🚀 Starting preview...\n'));
691
+ await sleep(1000); // Wait for files to settle
692
+
693
+ try {
694
+ const { autoPreview } = await import('./preview.js');
695
+ await autoPreview(projectPath, {
696
+ qr: options.qr,
697
+ port: options.port
698
+ });
699
+ } catch (error) {
700
+ console.log(chalk.yellow(` ⚠️ Preview failed: ${error.message}`));
701
+ console.log(chalk.gray(` Run manually: cd ${projectName} && vibecode preview\n`));
702
+ }
703
+ } else if (options.open) {
577
704
  await openProject(projectPath);
705
+ } else {
706
+ // Show preview hint
707
+ console.log(chalk.gray(` 💡 Quick preview: ${chalk.cyan(`cd ${projectName} && vibecode preview`)}\n`));
708
+ }
709
+
710
+ // Auto deploy if requested
711
+ if (options.deploy) {
712
+ console.log(chalk.cyan('\n 🚀 Deploying to cloud...\n'));
713
+ try {
714
+ const deployResult = await autoDeploy(projectPath, {
715
+ platform: options.deployPlatform || 'vercel',
716
+ preview: false
717
+ });
718
+ if (deployResult?.url) {
719
+ results.deployUrl = deployResult.url;
720
+ if (options.notify) {
721
+ notifyDeployComplete(true, options.deployPlatform || 'Vercel', deployResult.url);
722
+ }
723
+ }
724
+ } catch (error) {
725
+ console.log(chalk.yellow(` ⚠️ Deploy failed: ${error.message}`));
726
+ console.log(chalk.gray(` Run manually: cd ${projectName} && vibecode deploy\n`));
727
+ if (options.notify) {
728
+ notifyDeployComplete(false, options.deployPlatform || 'Vercel');
729
+ }
730
+ }
731
+ }
732
+
733
+ // Enter feedback mode if requested
734
+ if (options.feedback) {
735
+ console.log(chalk.cyan('\n 💬 Entering feedback mode...\n'));
736
+ await startFeedbackMode(projectPath, {
737
+ preview: options.preview || true,
738
+ port: options.port || '3000'
739
+ });
578
740
  }
579
741
 
580
742
  } catch (error) {
@@ -586,6 +748,10 @@ ${prompt}
586
748
  }
587
749
  }
588
750
 
751
+ function sleep(ms) {
752
+ return new Promise(resolve => setTimeout(resolve, ms));
753
+ }
754
+
589
755
  /**
590
756
  * Show template mode header
591
757
  */