@nclamvn/vibecode-cli 2.0.0 → 2.1.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.
Files changed (51) hide show
  1. package/.vibecode/learning/fixes.json +1 -0
  2. package/.vibecode/learning/preferences.json +1 -0
  3. package/README.md +310 -49
  4. package/SESSION_NOTES.md +154 -0
  5. package/bin/vibecode.js +212 -2
  6. package/package.json +5 -2
  7. package/src/agent/decomposition.js +476 -0
  8. package/src/agent/index.js +391 -0
  9. package/src/agent/memory.js +542 -0
  10. package/src/agent/orchestrator.js +917 -0
  11. package/src/agent/self-healing.js +516 -0
  12. package/src/commands/agent.js +349 -0
  13. package/src/commands/ask.js +230 -0
  14. package/src/commands/assist.js +413 -0
  15. package/src/commands/build.js +345 -4
  16. package/src/commands/debug.js +565 -0
  17. package/src/commands/docs.js +167 -0
  18. package/src/commands/git.js +1024 -0
  19. package/src/commands/go.js +387 -0
  20. package/src/commands/learn.js +294 -0
  21. package/src/commands/migrate.js +341 -0
  22. package/src/commands/plan.js +8 -2
  23. package/src/commands/refactor.js +205 -0
  24. package/src/commands/review.js +126 -1
  25. package/src/commands/security.js +229 -0
  26. package/src/commands/shell.js +486 -0
  27. package/src/commands/test.js +194 -0
  28. package/src/commands/undo.js +281 -0
  29. package/src/commands/watch.js +556 -0
  30. package/src/commands/wizard.js +322 -0
  31. package/src/config/constants.js +5 -1
  32. package/src/config/templates.js +146 -15
  33. package/src/core/backup.js +325 -0
  34. package/src/core/error-analyzer.js +237 -0
  35. package/src/core/fix-generator.js +195 -0
  36. package/src/core/iteration.js +226 -0
  37. package/src/core/learning.js +295 -0
  38. package/src/core/session.js +18 -2
  39. package/src/core/test-runner.js +281 -0
  40. package/src/debug/analyzer.js +329 -0
  41. package/src/debug/evidence.js +228 -0
  42. package/src/debug/fixer.js +348 -0
  43. package/src/debug/image-analyzer.js +304 -0
  44. package/src/debug/index.js +378 -0
  45. package/src/debug/verifier.js +346 -0
  46. package/src/index.js +89 -0
  47. package/src/providers/claude-code.js +12 -7
  48. package/src/ui/__tests__/error-translator.test.js +390 -0
  49. package/src/ui/dashboard.js +364 -0
  50. package/src/ui/error-translator.js +775 -0
  51. package/src/utils/image.js +222 -0
@@ -20,14 +20,29 @@ import { getCurrentState, transitionTo } from '../core/state-machine.js';
20
20
  import { getSpecHash } from '../core/contract.js';
21
21
  import { STATES } from '../config/constants.js';
22
22
  import { getBuildReportTemplate } from '../config/templates.js';
23
- import { ensureDir, pathExists, appendToFile, readMarkdown } from '../utils/files.js';
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,
28
30
  buildPromptWithContext,
29
31
  getProviderInfo
30
32
  } from '../providers/index.js';
33
+ // Phase D: Iterative Build
34
+ import { runTests, formatTestResults } from '../core/test-runner.js';
35
+ import { analyzeErrors, formatErrors, createErrorSummary } from '../core/error-analyzer.js';
36
+ import { generateFixPrompt, areErrorsFixable, estimateFixComplexity } from '../core/fix-generator.js';
37
+ import {
38
+ createIterationState,
39
+ recordIteration,
40
+ canContinue,
41
+ finalizeIterationState,
42
+ saveIterationState,
43
+ formatIterationSummary,
44
+ logIteration
45
+ } from '../core/iteration.js';
31
46
 
32
47
  const execAsync = promisify(exec);
33
48
 
@@ -46,7 +61,10 @@ export async function buildCommand(options = {}) {
46
61
  const specHash = await getSpecHash();
47
62
 
48
63
  // Handle different build modes
49
- if (options.auto) {
64
+ if (options.iterate) {
65
+ // Phase D: Iterative Build
66
+ await handleIterativeBuild(currentState, projectName, sessionId, sessionPath, specHash, options);
67
+ } else if (options.auto) {
50
68
  await handleAutoBuild(currentState, projectName, sessionId, sessionPath, specHash, options);
51
69
  } else if (options.start) {
52
70
  await handleBuildStart(currentState, projectName, sessionId, sessionPath, specHash);
@@ -59,7 +77,7 @@ export async function buildCommand(options = {}) {
59
77
  }
60
78
 
61
79
  } catch (error) {
62
- printError(error.message);
80
+ showError(error, { verbose: options.verbose });
63
81
  process.exit(1);
64
82
  }
65
83
  }
@@ -69,6 +87,10 @@ export async function buildCommand(options = {}) {
69
87
  * "Contract LOCKED = License to build"
70
88
  */
71
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
+
72
94
  // Check state - must be PLAN_CREATED or BUILD_IN_PROGRESS or REVIEW_FAILED
73
95
  const validStates = [STATES.PLAN_CREATED, STATES.BUILD_IN_PROGRESS, STATES.REVIEW_FAILED];
74
96
  if (!validStates.includes(currentState)) {
@@ -211,9 +233,328 @@ ${evidence.screenshots > 0 ? ` ✅ ${evidence.screenshots} screenshots` : '
211
233
 
212
234
  } catch (error) {
213
235
  await appendToFile(logPath, `\nERROR: ${error.message}\n`);
214
- printError(`Auto build failed: ${error.message}`);
236
+ showError(error);
237
+ process.exit(1);
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Handle --iterate mode: Build-Test-Fix loop
243
+ * "Build until tests pass or max iterations reached"
244
+ */
245
+ async function handleIterativeBuild(currentState, projectName, sessionId, sessionPath, specHash, options) {
246
+ const maxIterations = options.max || 3;
247
+ const strictMode = options.strict || false;
248
+
249
+ // Create backup before iterative build
250
+ const backup = new BackupManager();
251
+ await backup.createBackup('build-iterate');
252
+
253
+ // Check state - must be PLAN_CREATED or BUILD_IN_PROGRESS or REVIEW_FAILED
254
+ const validStates = [STATES.PLAN_CREATED, STATES.BUILD_IN_PROGRESS, STATES.REVIEW_FAILED];
255
+ if (!validStates.includes(currentState)) {
256
+ printError(`Cannot iterate in state: ${currentState}`);
257
+ console.log('Run `vibecode plan` first to create execution plan.');
258
+ process.exit(1);
259
+ }
260
+
261
+ // Check if Claude Code is available
262
+ const available = await isClaudeCodeAvailable();
263
+ if (!available) {
264
+ printError('Claude Code CLI not found.');
265
+ console.log(chalk.gray('Install with: npm install -g @anthropic-ai/claude-code'));
215
266
  process.exit(1);
216
267
  }
268
+
269
+ // Check coder_pack.md exists
270
+ if (!await sessionFileExists('coder_pack.md')) {
271
+ printError('coder_pack.md not found. Run `vibecode plan` first.');
272
+ process.exit(1);
273
+ }
274
+
275
+ // Setup directories
276
+ const evidencePath = path.join(sessionPath, 'evidence');
277
+ await ensureDir(evidencePath);
278
+ await ensureDir(path.join(evidencePath, 'screenshots'));
279
+ const iterationDir = path.join(sessionPath, 'iterations');
280
+ await ensureDir(iterationDir);
281
+ const logPath = path.join(evidencePath, 'build.log');
282
+
283
+ // Initialize iteration state
284
+ let iterationState = createIterationState(sessionId, maxIterations);
285
+
286
+ // Save initial state
287
+ const stateData = await loadState();
288
+ const startTime = new Date().toISOString();
289
+ stateData.build_started = startTime;
290
+ stateData.build_provider = 'claude-code';
291
+ stateData.build_mode = 'iterate';
292
+ stateData.max_iterations = maxIterations;
293
+ await saveState(stateData);
294
+
295
+ // Transition to BUILD_IN_PROGRESS if not already
296
+ if (currentState !== STATES.BUILD_IN_PROGRESS) {
297
+ await transitionTo(STATES.BUILD_IN_PROGRESS, 'iterative_build_started');
298
+ }
299
+
300
+ // Load coder pack
301
+ const originalCoderPack = await readSessionFile('coder_pack.md');
302
+
303
+ // Log start
304
+ await appendToFile(logPath, `\n${'='.repeat(60)}\n`);
305
+ await appendToFile(logPath, `ITERATIVE BUILD STARTED: ${startTime}\n`);
306
+ await appendToFile(logPath, `Max Iterations: ${maxIterations}\n`);
307
+ await appendToFile(logPath, `Strict Mode: ${strictMode}\n`);
308
+ await appendToFile(logPath, `${'='.repeat(60)}\n\n`);
309
+
310
+ // Show starting message
311
+ const providerInfo = getProviderInfo();
312
+
313
+ const content = `🔄 ITERATIVE BUILD
314
+
315
+ Project: ${projectName}
316
+ Session: ${sessionId}
317
+ Spec Hash: ${specHash}
318
+
319
+ Provider: ${providerInfo.name}
320
+ Max Iterations: ${maxIterations}
321
+ Strict Mode: ${strictMode ? 'Yes' : 'No'}
322
+
323
+ Starting build-test-fix loop...`;
324
+
325
+ console.log();
326
+ printBox(content, { borderColor: 'magenta' });
327
+ console.log();
328
+
329
+ // Build-Test-Fix Loop
330
+ let currentPrompt = await buildPromptWithContext(originalCoderPack, process.cwd());
331
+ let loopResult = { success: false, reason: '' };
332
+
333
+ while (true) {
334
+ const iteration = iterationState.currentIteration + 1;
335
+ const iterationStart = Date.now();
336
+
337
+ console.log(chalk.cyan(`\n${'─'.repeat(60)}`));
338
+ console.log(chalk.cyan(`│ ITERATION ${iteration}/${maxIterations}`));
339
+ console.log(chalk.cyan(`${'─'.repeat(60)}\n`));
340
+
341
+ await logIteration(logPath, iteration, 'Starting iteration');
342
+
343
+ // Step 1: Run Claude Code
344
+ console.log(chalk.yellow('▶ Running Claude Code...'));
345
+ console.log();
346
+
347
+ try {
348
+ const buildResult = await spawnClaudeCode(currentPrompt, {
349
+ cwd: process.cwd(),
350
+ logPath: logPath
351
+ });
352
+
353
+ console.log();
354
+ await logIteration(logPath, iteration, `Claude Code exited with code: ${buildResult.code}`);
355
+
356
+ // Capture evidence for this iteration
357
+ await captureGitDiff(evidencePath);
358
+ const iterEvidencePath = path.join(iterationDir, `iteration-${iteration}-diff.txt`);
359
+ try {
360
+ const { stdout } = await execAsync('git diff HEAD', { maxBuffer: 10 * 1024 * 1024 });
361
+ if (stdout.trim()) {
362
+ const fs = await import('fs-extra');
363
+ await fs.default.writeFile(iterEvidencePath, stdout, 'utf-8');
364
+ }
365
+ } catch (e) { /* ignore */ }
366
+
367
+ // Step 2: Run Tests
368
+ console.log(chalk.yellow('▶ Running tests...'));
369
+ const spinner = ora('Testing...').start();
370
+
371
+ const testResult = await runTests(process.cwd());
372
+ const iterationDuration = Date.now() - iterationStart;
373
+
374
+ if (testResult.passed) {
375
+ spinner.succeed('All tests passed!');
376
+
377
+ // Record successful iteration
378
+ iterationState = recordIteration(iterationState, {
379
+ passed: true,
380
+ errorCount: 0,
381
+ errorTypes: [],
382
+ affectedFiles: [],
383
+ duration: iterationDuration,
384
+ action: 'build'
385
+ });
386
+
387
+ // Finalize as success
388
+ iterationState = finalizeIterationState(iterationState, 'success');
389
+ await saveIterationState(sessionPath, iterationState);
390
+
391
+ loopResult = { success: true, reason: 'All tests passed' };
392
+ break;
393
+
394
+ } else {
395
+ spinner.fail(`Tests failed: ${testResult.summary.failed}/${testResult.summary.total}`);
396
+
397
+ // Step 3: Analyze Errors
398
+ const analyzedErrors = analyzeErrors(testResult);
399
+ const summary = createErrorSummary(analyzedErrors);
400
+
401
+ console.log();
402
+ console.log(formatErrors(analyzedErrors));
403
+
404
+ await logIteration(logPath, iteration, `Found ${analyzedErrors.length} errors`);
405
+
406
+ // Save error analysis for this iteration
407
+ await writeJson(path.join(iterationDir, `iteration-${iteration}-errors.json`), {
408
+ iteration,
409
+ timestamp: new Date().toISOString(),
410
+ summary,
411
+ errors: analyzedErrors
412
+ });
413
+
414
+ // Record failed iteration
415
+ iterationState = recordIteration(iterationState, {
416
+ passed: false,
417
+ errorCount: analyzedErrors.length,
418
+ errorTypes: [...new Set(analyzedErrors.map(e => e.type))],
419
+ affectedFiles: [...new Set(analyzedErrors.filter(e => e.file).map(e => e.file))],
420
+ duration: iterationDuration,
421
+ action: 'build'
422
+ });
423
+
424
+ // Check if errors are fixable
425
+ const fixableCheck = areErrorsFixable(analyzedErrors);
426
+ if (!fixableCheck.fixable) {
427
+ console.log(chalk.red(`\n⚠️ ${fixableCheck.reason}`));
428
+ await logIteration(logPath, iteration, `Errors not fixable: ${fixableCheck.reason}`);
429
+
430
+ iterationState = finalizeIterationState(iterationState, 'unfixable');
431
+ await saveIterationState(sessionPath, iterationState);
432
+
433
+ loopResult = { success: false, reason: fixableCheck.reason };
434
+ break;
435
+ }
436
+
437
+ // Check if we can continue
438
+ const continueCheck = canContinue(iterationState);
439
+ if (!continueCheck.canContinue) {
440
+ console.log(chalk.yellow(`\n⚠️ ${continueCheck.reason}`));
441
+ await logIteration(logPath, iteration, continueCheck.reason);
442
+
443
+ iterationState = finalizeIterationState(iterationState, 'max_reached');
444
+ await saveIterationState(sessionPath, iterationState);
445
+
446
+ loopResult = { success: false, reason: continueCheck.reason };
447
+ break;
448
+ }
449
+
450
+ // Step 4: Generate Fix Prompt
451
+ const complexity = estimateFixComplexity(analyzedErrors);
452
+ console.log(chalk.gray(`\nFix complexity: ${complexity}`));
453
+ console.log(chalk.yellow(`\n▶ Generating fix prompt for iteration ${iteration + 1}...`));
454
+
455
+ currentPrompt = generateFixPrompt(analyzedErrors, originalCoderPack, iteration + 1);
456
+
457
+ // Save fix prompt for evidence
458
+ await writeSessionFile(`iterations/fix-prompt-${iteration + 1}.md`, currentPrompt);
459
+
460
+ await logIteration(logPath, iteration, 'Generated fix prompt for next iteration');
461
+ }
462
+
463
+ } catch (error) {
464
+ console.log('\n' + inlineError(error));
465
+ await logIteration(logPath, iteration, `Error: ${error.message}`);
466
+
467
+ iterationState = finalizeIterationState(iterationState, 'error');
468
+ await saveIterationState(sessionPath, iterationState);
469
+
470
+ loopResult = { success: false, reason: error.message };
471
+ break;
472
+ }
473
+ }
474
+
475
+ // Final Summary
476
+ const endTime = new Date().toISOString();
477
+ await appendToFile(logPath, `\n${'='.repeat(60)}\n`);
478
+ await appendToFile(logPath, `ITERATIVE BUILD COMPLETED: ${endTime}\n`);
479
+ await appendToFile(logPath, `Result: ${loopResult.success ? 'SUCCESS' : 'FAILED'}\n`);
480
+ await appendToFile(logPath, `Iterations: ${iterationState.currentIteration}\n`);
481
+ await appendToFile(logPath, `${'='.repeat(60)}\n`);
482
+
483
+ // Save final iteration state
484
+ await saveIterationState(sessionPath, iterationState);
485
+
486
+ // Check evidence
487
+ const evidence = await checkEvidence(evidencePath);
488
+
489
+ // Generate build report
490
+ const reportContent = getBuildReportTemplate(
491
+ projectName,
492
+ sessionId,
493
+ specHash,
494
+ startTime,
495
+ endTime,
496
+ evidence
497
+ );
498
+ await writeSessionFile('build_report.md', reportContent);
499
+
500
+ // Update state - RELOAD to get current state (after BUILD_IN_PROGRESS transition)
501
+ const finalStateData = await loadState();
502
+ finalStateData.build_completed = endTime;
503
+ finalStateData.iterations = iterationState.currentIteration;
504
+ finalStateData.iteration_result = loopResult.success ? 'success' : 'failed';
505
+ await saveState(finalStateData);
506
+
507
+ console.log();
508
+ console.log(chalk.cyan('─'.repeat(60)));
509
+ console.log();
510
+
511
+ if (loopResult.success) {
512
+ await transitionTo(STATES.BUILD_DONE, 'iterative_build_success');
513
+
514
+ const duration = Math.round((new Date(endTime) - new Date(startTime)) / 1000 / 60);
515
+
516
+ const successContent = `✅ ITERATIVE BUILD SUCCESS
517
+
518
+ Project: ${projectName}
519
+ Iterations: ${iterationState.currentIteration}/${maxIterations}
520
+ Duration: ${duration} minutes
521
+ Result: All tests passed!
522
+
523
+ Evidence:
524
+ ${evidence.hasDiff ? ' ✅ changes.diff' : ' ⬜ changes.diff'}
525
+ ${evidence.hasLog ? ' ✅ build.log' : ' ⬜ build.log'}
526
+ ✅ ${iterationState.currentIteration} iteration records`;
527
+
528
+ printBox(successContent, { borderColor: 'green' });
529
+ printNextStep('Run `vibecode review` to validate your build');
530
+
531
+ } else {
532
+ // Still transition to BUILD_DONE but with failure note
533
+ await transitionTo(STATES.BUILD_DONE, 'iterative_build_completed_with_errors');
534
+
535
+ const failContent = `⚠️ ITERATIVE BUILD INCOMPLETE
536
+
537
+ Project: ${projectName}
538
+ Iterations: ${iterationState.currentIteration}/${maxIterations}
539
+ Result: ${loopResult.reason}
540
+
541
+ Evidence saved in:
542
+ ${iterationDir}/
543
+
544
+ Check iteration logs for details.`;
545
+
546
+ printBox(failContent, { borderColor: 'yellow' });
547
+
548
+ if (strictMode) {
549
+ printError('Strict mode: Build failed with errors');
550
+ process.exit(1);
551
+ } else {
552
+ console.log(chalk.gray('\nYou can:'));
553
+ console.log(chalk.gray(' • Run `vibecode build --iterate` to try again'));
554
+ console.log(chalk.gray(' • Run `vibecode review` to review current state'));
555
+ console.log(chalk.gray(' • Fix errors manually and run `vibecode build --complete`'));
556
+ }
557
+ }
217
558
  }
218
559
 
219
560
  async function handleBuildStart(currentState, projectName, sessionId, sessionPath, specHash) {