@nclamvn/vibecode-cli 1.3.0 → 1.6.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.
@@ -22,6 +22,8 @@ import { STATES } from '../config/constants.js';
22
22
  import { getBuildReportTemplate } from '../config/templates.js';
23
23
  import { ensureDir, pathExists, appendToFile, readMarkdown, writeJson } from '../utils/files.js';
24
24
  import { printBox, printError, printSuccess, printWarning, printNextStep } from '../ui/output.js';
25
+ import { showError, inlineError } from '../ui/error-translator.js';
26
+ import { BackupManager } from '../core/backup.js';
25
27
  import {
26
28
  spawnClaudeCode,
27
29
  isClaudeCodeAvailable,
@@ -75,7 +77,7 @@ export async function buildCommand(options = {}) {
75
77
  }
76
78
 
77
79
  } catch (error) {
78
- printError(error.message);
80
+ showError(error, { verbose: options.verbose });
79
81
  process.exit(1);
80
82
  }
81
83
  }
@@ -85,6 +87,10 @@ export async function buildCommand(options = {}) {
85
87
  * "Contract LOCKED = License to build"
86
88
  */
87
89
  async function handleAutoBuild(currentState, projectName, sessionId, sessionPath, specHash, options) {
90
+ // Create backup before build
91
+ const backup = new BackupManager();
92
+ await backup.createBackup('build-auto');
93
+
88
94
  // Check state - must be PLAN_CREATED or BUILD_IN_PROGRESS or REVIEW_FAILED
89
95
  const validStates = [STATES.PLAN_CREATED, STATES.BUILD_IN_PROGRESS, STATES.REVIEW_FAILED];
90
96
  if (!validStates.includes(currentState)) {
@@ -227,7 +233,7 @@ ${evidence.screenshots > 0 ? ` ✅ ${evidence.screenshots} screenshots` : '
227
233
 
228
234
  } catch (error) {
229
235
  await appendToFile(logPath, `\nERROR: ${error.message}\n`);
230
- printError(`Auto build failed: ${error.message}`);
236
+ showError(error);
231
237
  process.exit(1);
232
238
  }
233
239
  }
@@ -240,6 +246,10 @@ async function handleIterativeBuild(currentState, projectName, sessionId, sessio
240
246
  const maxIterations = options.max || 3;
241
247
  const strictMode = options.strict || false;
242
248
 
249
+ // Create backup before iterative build
250
+ const backup = new BackupManager();
251
+ await backup.createBackup('build-iterate');
252
+
243
253
  // Check state - must be PLAN_CREATED or BUILD_IN_PROGRESS or REVIEW_FAILED
244
254
  const validStates = [STATES.PLAN_CREATED, STATES.BUILD_IN_PROGRESS, STATES.REVIEW_FAILED];
245
255
  if (!validStates.includes(currentState)) {
@@ -451,7 +461,7 @@ Starting build-test-fix loop...`;
451
461
  }
452
462
 
453
463
  } catch (error) {
454
- console.log(chalk.red(`\n Build error: ${error.message}`));
464
+ console.log('\n' + inlineError(error));
455
465
  await logIteration(logPath, iteration, `Error: ${error.message}`);
456
466
 
457
467
  iterationState = finalizeIterationState(iterationState, 'error');
@@ -0,0 +1,457 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════════
2
+ // VIBECODE CLI - Debug Command
3
+ // Phase G: Intelligent 9-Step Bug Fixing
4
+ // ═══════════════════════════════════════════════════════════════════════════════
5
+
6
+ import chalk from 'chalk';
7
+ import ora from 'ora';
8
+ import path from 'path';
9
+ import fs from 'fs-extra';
10
+ import readline from 'readline';
11
+ import { printBox, printError, printSuccess, printWarning, printNextStep } from '../ui/output.js';
12
+ import { createDebugEngine } from '../debug/index.js';
13
+
14
+ /**
15
+ * Debug Command Entry Point
16
+ * Usage:
17
+ * vibecode debug - Interactive mode (default)
18
+ * vibecode debug "error description" - Debug with description
19
+ * vibecode debug --log - Paste error log
20
+ * vibecode debug --image <path> - Debug from screenshot
21
+ * vibecode debug --auto - Auto-scan and fix
22
+ */
23
+ export async function debugCommand(args = [], options = {}) {
24
+ try {
25
+ const projectPath = process.cwd();
26
+
27
+ // Parse command arguments
28
+ const input = parseDebugInput(args, options);
29
+
30
+ // If interactive mode or no input provided, enter interactive mode
31
+ if (options.interactive || (!input.description && !input.log && !input.image && !input.auto)) {
32
+ return interactiveDebug(projectPath, options);
33
+ }
34
+
35
+ // Create debug engine
36
+ const engine = createDebugEngine(projectPath, {
37
+ autoFix: options.autoFix !== false,
38
+ maxAttempts: options.attempts || 3,
39
+ verbose: options.verbose || false,
40
+ interactive: !options.quiet
41
+ });
42
+
43
+ // Show intro
44
+ console.log();
45
+ printBox(`🔍 VIBECODE DEBUG
46
+
47
+ Intelligent 9-Step Bug Fixing
48
+ Mode: ${input.auto ? 'Auto-Scan' : input.log ? 'Log Analysis' : input.image ? 'Image Analysis' : 'Description'}
49
+ Max Attempts: ${options.attempts || 3}`, { borderColor: 'cyan' });
50
+ console.log();
51
+
52
+ // Run debug workflow
53
+ const result = await engine.debug(input);
54
+
55
+ // Show final result
56
+ await showDebugResult(result, projectPath);
57
+
58
+ } catch (error) {
59
+ printError(`Debug failed: ${error.message}`);
60
+ if (options.verbose) {
61
+ console.error(error.stack);
62
+ }
63
+ process.exit(1);
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Parse debug input from args and options
69
+ */
70
+ function parseDebugInput(args, options) {
71
+ const input = {
72
+ description: null,
73
+ log: null,
74
+ image: null,
75
+ auto: options.auto || false
76
+ };
77
+
78
+ // Join args as description
79
+ if (args.length > 0) {
80
+ input.description = args.join(' ');
81
+ }
82
+
83
+ // Log paste mode
84
+ if (options.log) {
85
+ if (typeof options.log === 'string') {
86
+ input.log = options.log;
87
+ } else {
88
+ // Will need to read from stdin or file
89
+ input.log = options.logContent || null;
90
+ }
91
+ }
92
+
93
+ // Image mode
94
+ if (options.image) {
95
+ input.image = options.image;
96
+ }
97
+
98
+ return input;
99
+ }
100
+
101
+ /**
102
+ * Show debug result summary
103
+ */
104
+ async function showDebugResult(result, projectPath) {
105
+ console.log();
106
+
107
+ if (result.status === 'resolved') {
108
+ const content = `✅ BUG FIXED!
109
+
110
+ Status: ${result.status}
111
+ Attempts: ${result.session.attempts}
112
+ Steps: ${result.session.steps}
113
+
114
+ Documentation saved to:
115
+ .vibecode/debug/fixes.md
116
+
117
+ Prevention rules added to:
118
+ CLAUDE.md`;
119
+
120
+ printBox(content, { borderColor: 'green' });
121
+ printNextStep('Build your project to verify the fix');
122
+
123
+ } else if (result.status === 'no_error') {
124
+ printSuccess('No errors found to fix!');
125
+
126
+ } else if (result.status === 'no_hypothesis') {
127
+ printWarning('Could not determine how to fix this error.');
128
+ console.log(chalk.gray('\nTry:'));
129
+ console.log(chalk.gray(' • Providing more details about the error'));
130
+ console.log(chalk.gray(' • Pasting the full error log with --log'));
131
+ console.log(chalk.gray(' • Running vibecode debug --auto to scan project'));
132
+
133
+ } else {
134
+ // Show escalation options - user NEVER bế tắc
135
+ console.log(chalk.yellow(`
136
+ ╭───────────────────────────────────────────────────────────────────╮
137
+ │ │
138
+ │ ⚠️ COULD NOT AUTO-RESOLVE │
139
+ │ │
140
+ │ Status: ${result.status.padEnd(47)}│
141
+ │ Attempts: ${String(result.session.attempts).padEnd(45)}│
142
+ │ │
143
+ │ Don't worry! You have options: │
144
+ │ │
145
+ │ 1. vibecode assist "describe your issue" │
146
+ │ → Direct AI expert access with full context │
147
+ │ │
148
+ │ 2. vibecode debug -i │
149
+ │ → Interactive debug with more context │
150
+ │ │
151
+ │ 3. vibecode debug --attempts 5 │
152
+ │ → Retry with more attempts │
153
+ │ │
154
+ ╰───────────────────────────────────────────────────────────────────╯
155
+ `));
156
+
157
+ // Ask if user wants to auto-escalate to summon
158
+ const rl = readline.createInterface({
159
+ input: process.stdin,
160
+ output: process.stdout
161
+ });
162
+
163
+ try {
164
+ const answer = await new Promise(resolve => {
165
+ rl.question(chalk.cyan('\n🤝 Escalate to AI Assist? (Y/n): '), resolve);
166
+ });
167
+ rl.close();
168
+
169
+ if (answer.toLowerCase() !== 'n') {
170
+ const { assistCommand } = await import('./assist.js');
171
+ const errorMsg = result.message || 'the error that occurred';
172
+ await assistCommand([`Fix ${errorMsg}`], {});
173
+ }
174
+ } catch {
175
+ rl.close();
176
+ }
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Show debug command help
182
+ */
183
+ function showDebugHelp() {
184
+ console.log();
185
+ printBox(`🔍 VIBECODE DEBUG - Help
186
+
187
+ Intelligent 9-step bug fixing powered by AI.
188
+
189
+ Usage:
190
+ vibecode debug "error description" Quick fix from description
191
+ vibecode debug --auto Auto-scan project for errors
192
+ vibecode debug --log "error text" Fix from error log
193
+ vibecode debug --image path/to/img Fix from screenshot
194
+
195
+ Options:
196
+ --auto Auto-scan mode - find and fix errors
197
+ --log <text> Provide error log directly
198
+ --image <path> Provide error screenshot
199
+ --attempts <n> Max fix attempts (default: 3)
200
+ --verbose Show detailed output
201
+ --quiet Minimal output
202
+
203
+ Examples:
204
+ vibecode debug "Cannot read property of undefined"
205
+ vibecode debug --auto
206
+ vibecode debug --log "TypeError: x is not a function"
207
+ vibecode debug --attempts 5 "module not found"`, { borderColor: 'cyan' });
208
+ console.log();
209
+ }
210
+
211
+ /**
212
+ * Interactive Debug Mode
213
+ * REPL-like interface for debugging
214
+ */
215
+ async function interactiveDebug(projectPath, options) {
216
+ const engine = createDebugEngine(projectPath, {
217
+ autoFix: true,
218
+ maxAttempts: options.attempts || 3,
219
+ verbose: options.verbose || false,
220
+ interactive: true
221
+ });
222
+
223
+ // Show welcome banner
224
+ console.log(chalk.cyan(`
225
+ ╭────────────────────────────────────────────────────────────────────╮
226
+ │ │
227
+ │ 🔍 VIBECODE DEBUG (Interactive Mode) │
228
+ │ │
229
+ │ Commands: │
230
+ │ • Type error description or paste log │
231
+ │ • /scan - Auto-scan project for errors │
232
+ │ • /fix - Apply last suggested fix │
233
+ │ • /retry - Retry with current context │
234
+ │ • /clear - Clear context │
235
+ │ • /help - Show help │
236
+ │ • /quit - Exit │
237
+ │ │
238
+ ╰────────────────────────────────────────────────────────────────────╯
239
+ `));
240
+
241
+ const rl = readline.createInterface({
242
+ input: process.stdin,
243
+ output: process.stdout,
244
+ prompt: chalk.green('debug> ')
245
+ });
246
+
247
+ // Session context
248
+ const context = {
249
+ errors: [],
250
+ lastAnalysis: null,
251
+ lastFix: null,
252
+ lastEvidence: null
253
+ };
254
+
255
+ rl.prompt();
256
+
257
+ rl.on('line', async (line) => {
258
+ const input = line.trim();
259
+
260
+ if (!input) {
261
+ rl.prompt();
262
+ return;
263
+ }
264
+
265
+ // Handle commands
266
+ if (input.startsWith('/')) {
267
+ await handleDebugCommand(input, engine, context, rl);
268
+ } else {
269
+ // Treat as error description or log
270
+ await handleDebugInput(input, engine, context);
271
+ }
272
+
273
+ rl.prompt();
274
+ });
275
+
276
+ rl.on('close', () => {
277
+ console.log(chalk.cyan('\n👋 Debug session ended.\n'));
278
+ process.exit(0);
279
+ });
280
+ }
281
+
282
+ /**
283
+ * Handle debug commands (/scan, /fix, etc.)
284
+ */
285
+ async function handleDebugCommand(cmd, engine, context, rl) {
286
+ const command = cmd.toLowerCase().split(' ')[0];
287
+
288
+ switch (command) {
289
+ case '/scan':
290
+ console.log(chalk.blue('\n🔍 Scanning project for errors...\n'));
291
+ try {
292
+ const result = await engine.debug({ auto: true });
293
+ context.lastAnalysis = result;
294
+ context.lastEvidence = result.evidence;
295
+
296
+ if (result.status === 'resolved') {
297
+ console.log(chalk.green('\n✅ All errors resolved!'));
298
+ } else if (result.status === 'no_error') {
299
+ console.log(chalk.green('\n✅ No errors found!'));
300
+ } else {
301
+ console.log(chalk.yellow('\n⚠️ Some issues found. Use /fix to apply fixes.'));
302
+ }
303
+ } catch (error) {
304
+ console.log(chalk.red(`\nScan error: ${error.message}`));
305
+ }
306
+ break;
307
+
308
+ case '/fix':
309
+ if (context.lastAnalysis && context.lastAnalysis.fixPrompt) {
310
+ console.log(chalk.blue('\n🔧 Applying fix...\n'));
311
+ try {
312
+ const fixResult = await engine.applyFix(context.lastAnalysis.fixPrompt);
313
+ if (fixResult.success) {
314
+ console.log(chalk.green('\n✅ Fix applied! Run /scan to verify.'));
315
+ } else {
316
+ console.log(chalk.yellow(`\n⚠️ Fix may have issues: ${fixResult.error || 'Check output above'}`));
317
+ }
318
+ } catch (error) {
319
+ console.log(chalk.red(`\nFix error: ${error.message}`));
320
+ }
321
+ } else if (context.errors.length > 0) {
322
+ // Try to generate fix from accumulated context
323
+ console.log(chalk.blue('\n🔧 Generating fix from context...\n'));
324
+ try {
325
+ const result = await engine.debug({ description: context.errors.join('\n\n') });
326
+ context.lastAnalysis = result;
327
+ } catch (error) {
328
+ console.log(chalk.red(`\nError: ${error.message}`));
329
+ }
330
+ } else {
331
+ console.log(chalk.yellow('\nNo analysis to fix. Run /scan or describe an error first.'));
332
+ }
333
+ break;
334
+
335
+ case '/retry':
336
+ if (context.errors.length > 0) {
337
+ console.log(chalk.blue('\n🔄 Retrying with accumulated context...\n'));
338
+ try {
339
+ const result = await engine.debug({ description: context.errors.join('\n\n') });
340
+ context.lastAnalysis = result;
341
+ } catch (error) {
342
+ console.log(chalk.red(`\nRetry error: ${error.message}`));
343
+ }
344
+ } else {
345
+ console.log(chalk.yellow('\nNo context to retry with. Describe an error first.'));
346
+ }
347
+ break;
348
+
349
+ case '/clear':
350
+ context.errors = [];
351
+ context.lastAnalysis = null;
352
+ context.lastFix = null;
353
+ context.lastEvidence = null;
354
+ console.log(chalk.green('\n✓ Context cleared.\n'));
355
+ break;
356
+
357
+ case '/context':
358
+ console.log(chalk.cyan('\n📋 Current Context:'));
359
+ console.log(chalk.gray(` Errors collected: ${context.errors.length}`));
360
+ console.log(chalk.gray(` Last analysis: ${context.lastAnalysis ? 'Yes' : 'No'}`));
361
+ if (context.errors.length > 0) {
362
+ console.log(chalk.gray('\n Recent errors:'));
363
+ context.errors.slice(-3).forEach((e, i) => {
364
+ console.log(chalk.gray(` ${i + 1}. ${e.substring(0, 60)}${e.length > 60 ? '...' : ''}`));
365
+ });
366
+ }
367
+ console.log();
368
+ break;
369
+
370
+ case '/help':
371
+ console.log(chalk.cyan(`
372
+ Commands:
373
+ /scan Auto-scan project for errors
374
+ /fix Apply last suggested fix
375
+ /retry Retry with accumulated context
376
+ /clear Clear all context
377
+ /context Show current context
378
+ /help Show this help
379
+ /quit Exit debug mode
380
+
381
+ Tips:
382
+ • Paste error messages directly for analysis
383
+ • Describe what's wrong in plain English
384
+ • Use /scan for automatic error detection
385
+ • Context accumulates - use /clear to reset
386
+ `));
387
+ break;
388
+
389
+ case '/quit':
390
+ case '/exit':
391
+ case '/q':
392
+ rl.close();
393
+ break;
394
+
395
+ default:
396
+ console.log(chalk.yellow(`\nUnknown command: ${cmd}`));
397
+ console.log(chalk.gray('Type /help for available commands.\n'));
398
+ }
399
+ }
400
+
401
+ /**
402
+ * Handle user input (error description or log)
403
+ */
404
+ async function handleDebugInput(input, engine, context) {
405
+ // Add to context
406
+ context.errors.push(input);
407
+
408
+ // Show spinner
409
+ const spinner = ora('Analyzing error...').start();
410
+
411
+ try {
412
+ const result = await engine.debug({ description: input });
413
+ context.lastAnalysis = result;
414
+
415
+ spinner.stop();
416
+
417
+ if (result.status === 'resolved') {
418
+ console.log(chalk.green('\n✅ Error analyzed and fixed!\n'));
419
+ } else if (result.status === 'no_error') {
420
+ console.log(chalk.green('\n✅ No actionable error found.\n'));
421
+ } else {
422
+ console.log(chalk.yellow('\n⚠️ Analysis complete. Use /fix to apply suggested fix.\n'));
423
+
424
+ // Show brief analysis
425
+ if (result.session && result.session.steps > 0) {
426
+ console.log(chalk.gray(` Steps completed: ${result.session.steps}`));
427
+ console.log(chalk.gray(` Attempts: ${result.session.attempts}`));
428
+ }
429
+ }
430
+ } catch (error) {
431
+ spinner.stop();
432
+ console.log(chalk.red(`\nAnalysis error: ${error.message}\n`));
433
+ }
434
+ }
435
+
436
+ /**
437
+ * Interactive log paste mode (for future enhancement)
438
+ */
439
+ async function interactiveLogMode() {
440
+ console.log(chalk.cyan('Paste your error log below (end with Ctrl+D):'));
441
+ console.log(chalk.gray('---'));
442
+
443
+ let logContent = '';
444
+
445
+ process.stdin.setEncoding('utf8');
446
+
447
+ return new Promise((resolve) => {
448
+ process.stdin.on('data', (chunk) => {
449
+ logContent += chunk;
450
+ });
451
+
452
+ process.stdin.on('end', () => {
453
+ console.log(chalk.gray('---'));
454
+ resolve(logContent.trim());
455
+ });
456
+ });
457
+ }