@nclamvn/vibecode-cli 1.1.0 → 1.3.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.
package/bin/vibecode.js CHANGED
@@ -16,6 +16,7 @@ import {
16
16
  buildCommand,
17
17
  reviewCommand,
18
18
  snapshotCommand,
19
+ configCommand,
19
20
  VERSION
20
21
  } from '../src/index.js';
21
22
 
@@ -77,6 +78,11 @@ program
77
78
  .option('-s, --start', 'Start the build process')
78
79
  .option('-c, --complete', 'Mark build as complete')
79
80
  .option('-e, --evidence', 'Capture evidence snapshot')
81
+ .option('-a, --auto', 'Auto-build with Claude Code (--dangerously-skip-permissions)')
82
+ .option('-i, --iterate', 'Iterative build: build-test-fix loop until tests pass')
83
+ .option('-m, --max <n>', 'Max iterations for --iterate mode', parseInt)
84
+ .option('--strict', 'Strict mode: exit with error if tests fail after max iterations')
85
+ .option('--provider <name>', 'Provider to use: claude-code', 'claude-code')
80
86
  .action(buildCommand);
81
87
 
82
88
  program
@@ -93,6 +99,17 @@ program
93
99
  .option('--major', 'Major version bump')
94
100
  .action(snapshotCommand);
95
101
 
102
+ // ─────────────────────────────────────────────────────────────────────────────
103
+ // Phase C Commands - AI Integration
104
+ // ─────────────────────────────────────────────────────────────────────────────
105
+
106
+ program
107
+ .command('config')
108
+ .description('Manage Vibecode configuration')
109
+ .option('--show', 'Show current configuration')
110
+ .option('--provider <name>', 'Set default AI provider')
111
+ .action(configCommand);
112
+
96
113
  // ─────────────────────────────────────────────────────────────────────────────
97
114
  // Parse
98
115
  // ─────────────────────────────────────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nclamvn/vibecode-cli",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "Build software with discipline - AI coding with guardrails",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -1,5 +1,6 @@
1
1
  // ═══════════════════════════════════════════════════════════════════════════════
2
2
  // VIBECODE CLI - Build Command
3
+ // "Claude/LLM là PIPELINE, là KIẾN TRÚC SƯ"
3
4
  // ═══════════════════════════════════════════════════════════════════════════════
4
5
 
5
6
  import chalk from 'chalk';
@@ -12,14 +13,34 @@ import {
12
13
  getCurrentSessionId,
13
14
  getCurrentSessionPath,
14
15
  writeSessionFile,
15
- sessionFileExists
16
+ sessionFileExists,
17
+ readSessionFile
16
18
  } from '../core/session.js';
17
19
  import { getCurrentState, transitionTo } from '../core/state-machine.js';
18
20
  import { getSpecHash } from '../core/contract.js';
19
21
  import { STATES } from '../config/constants.js';
20
22
  import { getBuildReportTemplate } from '../config/templates.js';
21
- import { ensureDir, pathExists, appendToFile } from '../utils/files.js';
23
+ import { ensureDir, pathExists, appendToFile, readMarkdown, writeJson } from '../utils/files.js';
22
24
  import { printBox, printError, printSuccess, printWarning, printNextStep } from '../ui/output.js';
25
+ import {
26
+ spawnClaudeCode,
27
+ isClaudeCodeAvailable,
28
+ buildPromptWithContext,
29
+ getProviderInfo
30
+ } from '../providers/index.js';
31
+ // Phase D: Iterative Build
32
+ import { runTests, formatTestResults } from '../core/test-runner.js';
33
+ import { analyzeErrors, formatErrors, createErrorSummary } from '../core/error-analyzer.js';
34
+ import { generateFixPrompt, areErrorsFixable, estimateFixComplexity } from '../core/fix-generator.js';
35
+ import {
36
+ createIterationState,
37
+ recordIteration,
38
+ canContinue,
39
+ finalizeIterationState,
40
+ saveIterationState,
41
+ formatIterationSummary,
42
+ logIteration
43
+ } from '../core/iteration.js';
23
44
 
24
45
  const execAsync = promisify(exec);
25
46
 
@@ -38,7 +59,12 @@ export async function buildCommand(options = {}) {
38
59
  const specHash = await getSpecHash();
39
60
 
40
61
  // Handle different build modes
41
- if (options.start) {
62
+ if (options.iterate) {
63
+ // Phase D: Iterative Build
64
+ await handleIterativeBuild(currentState, projectName, sessionId, sessionPath, specHash, options);
65
+ } else if (options.auto) {
66
+ await handleAutoBuild(currentState, projectName, sessionId, sessionPath, specHash, options);
67
+ } else if (options.start) {
42
68
  await handleBuildStart(currentState, projectName, sessionId, sessionPath, specHash);
43
69
  } else if (options.complete) {
44
70
  await handleBuildComplete(currentState, projectName, sessionId, sessionPath, specHash);
@@ -54,6 +80,473 @@ export async function buildCommand(options = {}) {
54
80
  }
55
81
  }
56
82
 
83
+ /**
84
+ * Handle --auto mode: Spawn Claude Code with optimal config
85
+ * "Contract LOCKED = License to build"
86
+ */
87
+ async function handleAutoBuild(currentState, projectName, sessionId, sessionPath, specHash, options) {
88
+ // Check state - must be PLAN_CREATED or BUILD_IN_PROGRESS or REVIEW_FAILED
89
+ const validStates = [STATES.PLAN_CREATED, STATES.BUILD_IN_PROGRESS, STATES.REVIEW_FAILED];
90
+ if (!validStates.includes(currentState)) {
91
+ printError(`Cannot auto-build in state: ${currentState}`);
92
+ console.log('Run `vibecode plan` first to create execution plan.');
93
+ process.exit(1);
94
+ }
95
+
96
+ // Check if Claude Code is available
97
+ const available = await isClaudeCodeAvailable();
98
+ if (!available) {
99
+ printError('Claude Code CLI not found.');
100
+ console.log(chalk.gray('Install with: npm install -g @anthropic-ai/claude-code'));
101
+ console.log(chalk.gray('Or use manual build: vibecode build --start'));
102
+ process.exit(1);
103
+ }
104
+
105
+ // Check coder_pack.md exists
106
+ if (!await sessionFileExists('coder_pack.md')) {
107
+ printError('coder_pack.md not found. Run `vibecode plan` first.');
108
+ process.exit(1);
109
+ }
110
+
111
+ // Setup evidence directory
112
+ const evidencePath = path.join(sessionPath, 'evidence');
113
+ await ensureDir(evidencePath);
114
+ await ensureDir(path.join(evidencePath, 'screenshots'));
115
+ const logPath = path.join(evidencePath, 'build.log');
116
+
117
+ // Save build start time
118
+ const stateData = await loadState();
119
+ const startTime = new Date().toISOString();
120
+ stateData.build_started = startTime;
121
+ stateData.build_provider = 'claude-code';
122
+ stateData.build_mode = 'auto';
123
+ await saveState(stateData);
124
+
125
+ // Transition to BUILD_IN_PROGRESS if not already
126
+ if (currentState !== STATES.BUILD_IN_PROGRESS) {
127
+ await transitionTo(STATES.BUILD_IN_PROGRESS, 'auto_build_started');
128
+ }
129
+
130
+ // Load coder pack
131
+ const coderPackContent = await readSessionFile('coder_pack.md');
132
+
133
+ // Build full prompt with CLAUDE.md injection if exists
134
+ const fullPrompt = await buildPromptWithContext(coderPackContent, process.cwd());
135
+
136
+ // Log start
137
+ await appendToFile(logPath, `\n${'='.repeat(60)}\n`);
138
+ await appendToFile(logPath, `AUTO BUILD STARTED: ${startTime}\n`);
139
+ await appendToFile(logPath, `Provider: Claude Code (--dangerously-skip-permissions)\n`);
140
+ await appendToFile(logPath, `${'='.repeat(60)}\n\n`);
141
+
142
+ // Show starting message
143
+ const providerInfo = getProviderInfo();
144
+
145
+ const content = `🤖 AUTO BUILD
146
+
147
+ Project: ${projectName}
148
+ Session: ${sessionId}
149
+ Spec Hash: ${specHash}
150
+
151
+ Provider: ${providerInfo.name}
152
+ Mode: ${providerInfo.mode}
153
+
154
+ Starting AI build session...
155
+ Contract LOCKED = License to build`;
156
+
157
+ console.log();
158
+ printBox(content, { borderColor: 'magenta' });
159
+ console.log();
160
+ console.log(chalk.magenta('─'.repeat(60)));
161
+ console.log(chalk.magenta('│ CLAUDE CODE OUTPUT'));
162
+ console.log(chalk.magenta('─'.repeat(60)));
163
+ console.log();
164
+
165
+ // Spawn Claude Code
166
+ try {
167
+ const result = await spawnClaudeCode(fullPrompt, {
168
+ cwd: process.cwd(),
169
+ logPath: logPath
170
+ });
171
+
172
+ console.log();
173
+ console.log(chalk.magenta('─'.repeat(60)));
174
+ console.log();
175
+
176
+ // Capture evidence
177
+ await captureGitDiff(evidencePath);
178
+
179
+ const endTime = new Date().toISOString();
180
+ await appendToFile(logPath, `\n${'='.repeat(60)}\n`);
181
+ await appendToFile(logPath, `AUTO BUILD COMPLETED: ${endTime}\n`);
182
+ await appendToFile(logPath, `Exit code: ${result.code}\n`);
183
+ await appendToFile(logPath, `${'='.repeat(60)}\n`);
184
+
185
+ // Check evidence
186
+ const evidence = await checkEvidence(evidencePath);
187
+
188
+ // Generate build report
189
+ const reportContent = getBuildReportTemplate(
190
+ projectName,
191
+ sessionId,
192
+ specHash,
193
+ startTime,
194
+ endTime,
195
+ evidence
196
+ );
197
+ await writeSessionFile('build_report.md', reportContent);
198
+
199
+ // Update state
200
+ stateData.build_completed = endTime;
201
+ await saveState(stateData);
202
+
203
+ if (result.success) {
204
+ await transitionTo(STATES.BUILD_DONE, 'auto_build_completed');
205
+
206
+ const duration = Math.round((new Date(endTime) - new Date(startTime)) / 1000 / 60);
207
+
208
+ const successContent = `✅ AUTO BUILD COMPLETED
209
+
210
+ Project: ${projectName}
211
+ Duration: ${duration} minutes
212
+ Provider: Claude Code
213
+
214
+ Evidence:
215
+ ${evidence.hasDiff ? ' ✅ changes.diff' : ' ⬜ changes.diff'}
216
+ ${evidence.hasLog ? ' ✅ build.log' : ' ⬜ build.log'}
217
+ ${evidence.screenshots > 0 ? ` ✅ ${evidence.screenshots} screenshots` : ' ⬜ No screenshots'}`;
218
+
219
+ printBox(successContent, { borderColor: 'green' });
220
+ printNextStep('Run `vibecode review` to validate your build');
221
+
222
+ } else {
223
+ printWarning(`Claude Code exited with code: ${result.code}`);
224
+ console.log(chalk.gray('Check build.log for details.'));
225
+ console.log(chalk.gray('Run `vibecode build --auto` to retry.'));
226
+ }
227
+
228
+ } catch (error) {
229
+ await appendToFile(logPath, `\nERROR: ${error.message}\n`);
230
+ printError(`Auto build failed: ${error.message}`);
231
+ process.exit(1);
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Handle --iterate mode: Build-Test-Fix loop
237
+ * "Build until tests pass or max iterations reached"
238
+ */
239
+ async function handleIterativeBuild(currentState, projectName, sessionId, sessionPath, specHash, options) {
240
+ const maxIterations = options.max || 3;
241
+ const strictMode = options.strict || false;
242
+
243
+ // Check state - must be PLAN_CREATED or BUILD_IN_PROGRESS or REVIEW_FAILED
244
+ const validStates = [STATES.PLAN_CREATED, STATES.BUILD_IN_PROGRESS, STATES.REVIEW_FAILED];
245
+ if (!validStates.includes(currentState)) {
246
+ printError(`Cannot iterate in state: ${currentState}`);
247
+ console.log('Run `vibecode plan` first to create execution plan.');
248
+ process.exit(1);
249
+ }
250
+
251
+ // Check if Claude Code is available
252
+ const available = await isClaudeCodeAvailable();
253
+ if (!available) {
254
+ printError('Claude Code CLI not found.');
255
+ console.log(chalk.gray('Install with: npm install -g @anthropic-ai/claude-code'));
256
+ process.exit(1);
257
+ }
258
+
259
+ // Check coder_pack.md exists
260
+ if (!await sessionFileExists('coder_pack.md')) {
261
+ printError('coder_pack.md not found. Run `vibecode plan` first.');
262
+ process.exit(1);
263
+ }
264
+
265
+ // Setup directories
266
+ const evidencePath = path.join(sessionPath, 'evidence');
267
+ await ensureDir(evidencePath);
268
+ await ensureDir(path.join(evidencePath, 'screenshots'));
269
+ const iterationDir = path.join(sessionPath, 'iterations');
270
+ await ensureDir(iterationDir);
271
+ const logPath = path.join(evidencePath, 'build.log');
272
+
273
+ // Initialize iteration state
274
+ let iterationState = createIterationState(sessionId, maxIterations);
275
+
276
+ // Save initial state
277
+ const stateData = await loadState();
278
+ const startTime = new Date().toISOString();
279
+ stateData.build_started = startTime;
280
+ stateData.build_provider = 'claude-code';
281
+ stateData.build_mode = 'iterate';
282
+ stateData.max_iterations = maxIterations;
283
+ await saveState(stateData);
284
+
285
+ // Transition to BUILD_IN_PROGRESS if not already
286
+ if (currentState !== STATES.BUILD_IN_PROGRESS) {
287
+ await transitionTo(STATES.BUILD_IN_PROGRESS, 'iterative_build_started');
288
+ }
289
+
290
+ // Load coder pack
291
+ const originalCoderPack = await readSessionFile('coder_pack.md');
292
+
293
+ // Log start
294
+ await appendToFile(logPath, `\n${'='.repeat(60)}\n`);
295
+ await appendToFile(logPath, `ITERATIVE BUILD STARTED: ${startTime}\n`);
296
+ await appendToFile(logPath, `Max Iterations: ${maxIterations}\n`);
297
+ await appendToFile(logPath, `Strict Mode: ${strictMode}\n`);
298
+ await appendToFile(logPath, `${'='.repeat(60)}\n\n`);
299
+
300
+ // Show starting message
301
+ const providerInfo = getProviderInfo();
302
+
303
+ const content = `🔄 ITERATIVE BUILD
304
+
305
+ Project: ${projectName}
306
+ Session: ${sessionId}
307
+ Spec Hash: ${specHash}
308
+
309
+ Provider: ${providerInfo.name}
310
+ Max Iterations: ${maxIterations}
311
+ Strict Mode: ${strictMode ? 'Yes' : 'No'}
312
+
313
+ Starting build-test-fix loop...`;
314
+
315
+ console.log();
316
+ printBox(content, { borderColor: 'magenta' });
317
+ console.log();
318
+
319
+ // Build-Test-Fix Loop
320
+ let currentPrompt = await buildPromptWithContext(originalCoderPack, process.cwd());
321
+ let loopResult = { success: false, reason: '' };
322
+
323
+ while (true) {
324
+ const iteration = iterationState.currentIteration + 1;
325
+ const iterationStart = Date.now();
326
+
327
+ console.log(chalk.cyan(`\n${'─'.repeat(60)}`));
328
+ console.log(chalk.cyan(`│ ITERATION ${iteration}/${maxIterations}`));
329
+ console.log(chalk.cyan(`${'─'.repeat(60)}\n`));
330
+
331
+ await logIteration(logPath, iteration, 'Starting iteration');
332
+
333
+ // Step 1: Run Claude Code
334
+ console.log(chalk.yellow('▶ Running Claude Code...'));
335
+ console.log();
336
+
337
+ try {
338
+ const buildResult = await spawnClaudeCode(currentPrompt, {
339
+ cwd: process.cwd(),
340
+ logPath: logPath
341
+ });
342
+
343
+ console.log();
344
+ await logIteration(logPath, iteration, `Claude Code exited with code: ${buildResult.code}`);
345
+
346
+ // Capture evidence for this iteration
347
+ await captureGitDiff(evidencePath);
348
+ const iterEvidencePath = path.join(iterationDir, `iteration-${iteration}-diff.txt`);
349
+ try {
350
+ const { stdout } = await execAsync('git diff HEAD', { maxBuffer: 10 * 1024 * 1024 });
351
+ if (stdout.trim()) {
352
+ const fs = await import('fs-extra');
353
+ await fs.default.writeFile(iterEvidencePath, stdout, 'utf-8');
354
+ }
355
+ } catch (e) { /* ignore */ }
356
+
357
+ // Step 2: Run Tests
358
+ console.log(chalk.yellow('▶ Running tests...'));
359
+ const spinner = ora('Testing...').start();
360
+
361
+ const testResult = await runTests(process.cwd());
362
+ const iterationDuration = Date.now() - iterationStart;
363
+
364
+ if (testResult.passed) {
365
+ spinner.succeed('All tests passed!');
366
+
367
+ // Record successful iteration
368
+ iterationState = recordIteration(iterationState, {
369
+ passed: true,
370
+ errorCount: 0,
371
+ errorTypes: [],
372
+ affectedFiles: [],
373
+ duration: iterationDuration,
374
+ action: 'build'
375
+ });
376
+
377
+ // Finalize as success
378
+ iterationState = finalizeIterationState(iterationState, 'success');
379
+ await saveIterationState(sessionPath, iterationState);
380
+
381
+ loopResult = { success: true, reason: 'All tests passed' };
382
+ break;
383
+
384
+ } else {
385
+ spinner.fail(`Tests failed: ${testResult.summary.failed}/${testResult.summary.total}`);
386
+
387
+ // Step 3: Analyze Errors
388
+ const analyzedErrors = analyzeErrors(testResult);
389
+ const summary = createErrorSummary(analyzedErrors);
390
+
391
+ console.log();
392
+ console.log(formatErrors(analyzedErrors));
393
+
394
+ await logIteration(logPath, iteration, `Found ${analyzedErrors.length} errors`);
395
+
396
+ // Save error analysis for this iteration
397
+ await writeJson(path.join(iterationDir, `iteration-${iteration}-errors.json`), {
398
+ iteration,
399
+ timestamp: new Date().toISOString(),
400
+ summary,
401
+ errors: analyzedErrors
402
+ });
403
+
404
+ // Record failed iteration
405
+ iterationState = recordIteration(iterationState, {
406
+ passed: false,
407
+ errorCount: analyzedErrors.length,
408
+ errorTypes: [...new Set(analyzedErrors.map(e => e.type))],
409
+ affectedFiles: [...new Set(analyzedErrors.filter(e => e.file).map(e => e.file))],
410
+ duration: iterationDuration,
411
+ action: 'build'
412
+ });
413
+
414
+ // Check if errors are fixable
415
+ const fixableCheck = areErrorsFixable(analyzedErrors);
416
+ if (!fixableCheck.fixable) {
417
+ console.log(chalk.red(`\n⚠️ ${fixableCheck.reason}`));
418
+ await logIteration(logPath, iteration, `Errors not fixable: ${fixableCheck.reason}`);
419
+
420
+ iterationState = finalizeIterationState(iterationState, 'unfixable');
421
+ await saveIterationState(sessionPath, iterationState);
422
+
423
+ loopResult = { success: false, reason: fixableCheck.reason };
424
+ break;
425
+ }
426
+
427
+ // Check if we can continue
428
+ const continueCheck = canContinue(iterationState);
429
+ if (!continueCheck.canContinue) {
430
+ console.log(chalk.yellow(`\n⚠️ ${continueCheck.reason}`));
431
+ await logIteration(logPath, iteration, continueCheck.reason);
432
+
433
+ iterationState = finalizeIterationState(iterationState, 'max_reached');
434
+ await saveIterationState(sessionPath, iterationState);
435
+
436
+ loopResult = { success: false, reason: continueCheck.reason };
437
+ break;
438
+ }
439
+
440
+ // Step 4: Generate Fix Prompt
441
+ const complexity = estimateFixComplexity(analyzedErrors);
442
+ console.log(chalk.gray(`\nFix complexity: ${complexity}`));
443
+ console.log(chalk.yellow(`\n▶ Generating fix prompt for iteration ${iteration + 1}...`));
444
+
445
+ currentPrompt = generateFixPrompt(analyzedErrors, originalCoderPack, iteration + 1);
446
+
447
+ // Save fix prompt for evidence
448
+ await writeSessionFile(`iterations/fix-prompt-${iteration + 1}.md`, currentPrompt);
449
+
450
+ await logIteration(logPath, iteration, 'Generated fix prompt for next iteration');
451
+ }
452
+
453
+ } catch (error) {
454
+ console.log(chalk.red(`\n❌ Build error: ${error.message}`));
455
+ await logIteration(logPath, iteration, `Error: ${error.message}`);
456
+
457
+ iterationState = finalizeIterationState(iterationState, 'error');
458
+ await saveIterationState(sessionPath, iterationState);
459
+
460
+ loopResult = { success: false, reason: error.message };
461
+ break;
462
+ }
463
+ }
464
+
465
+ // Final Summary
466
+ const endTime = new Date().toISOString();
467
+ await appendToFile(logPath, `\n${'='.repeat(60)}\n`);
468
+ await appendToFile(logPath, `ITERATIVE BUILD COMPLETED: ${endTime}\n`);
469
+ await appendToFile(logPath, `Result: ${loopResult.success ? 'SUCCESS' : 'FAILED'}\n`);
470
+ await appendToFile(logPath, `Iterations: ${iterationState.currentIteration}\n`);
471
+ await appendToFile(logPath, `${'='.repeat(60)}\n`);
472
+
473
+ // Save final iteration state
474
+ await saveIterationState(sessionPath, iterationState);
475
+
476
+ // Check evidence
477
+ const evidence = await checkEvidence(evidencePath);
478
+
479
+ // Generate build report
480
+ const reportContent = getBuildReportTemplate(
481
+ projectName,
482
+ sessionId,
483
+ specHash,
484
+ startTime,
485
+ endTime,
486
+ evidence
487
+ );
488
+ await writeSessionFile('build_report.md', reportContent);
489
+
490
+ // Update state - RELOAD to get current state (after BUILD_IN_PROGRESS transition)
491
+ const finalStateData = await loadState();
492
+ finalStateData.build_completed = endTime;
493
+ finalStateData.iterations = iterationState.currentIteration;
494
+ finalStateData.iteration_result = loopResult.success ? 'success' : 'failed';
495
+ await saveState(finalStateData);
496
+
497
+ console.log();
498
+ console.log(chalk.cyan('─'.repeat(60)));
499
+ console.log();
500
+
501
+ if (loopResult.success) {
502
+ await transitionTo(STATES.BUILD_DONE, 'iterative_build_success');
503
+
504
+ const duration = Math.round((new Date(endTime) - new Date(startTime)) / 1000 / 60);
505
+
506
+ const successContent = `✅ ITERATIVE BUILD SUCCESS
507
+
508
+ Project: ${projectName}
509
+ Iterations: ${iterationState.currentIteration}/${maxIterations}
510
+ Duration: ${duration} minutes
511
+ Result: All tests passed!
512
+
513
+ Evidence:
514
+ ${evidence.hasDiff ? ' ✅ changes.diff' : ' ⬜ changes.diff'}
515
+ ${evidence.hasLog ? ' ✅ build.log' : ' ⬜ build.log'}
516
+ ✅ ${iterationState.currentIteration} iteration records`;
517
+
518
+ printBox(successContent, { borderColor: 'green' });
519
+ printNextStep('Run `vibecode review` to validate your build');
520
+
521
+ } else {
522
+ // Still transition to BUILD_DONE but with failure note
523
+ await transitionTo(STATES.BUILD_DONE, 'iterative_build_completed_with_errors');
524
+
525
+ const failContent = `⚠️ ITERATIVE BUILD INCOMPLETE
526
+
527
+ Project: ${projectName}
528
+ Iterations: ${iterationState.currentIteration}/${maxIterations}
529
+ Result: ${loopResult.reason}
530
+
531
+ Evidence saved in:
532
+ ${iterationDir}/
533
+
534
+ Check iteration logs for details.`;
535
+
536
+ printBox(failContent, { borderColor: 'yellow' });
537
+
538
+ if (strictMode) {
539
+ printError('Strict mode: Build failed with errors');
540
+ process.exit(1);
541
+ } else {
542
+ console.log(chalk.gray('\nYou can:'));
543
+ console.log(chalk.gray(' • Run `vibecode build --iterate` to try again'));
544
+ console.log(chalk.gray(' • Run `vibecode review` to review current state'));
545
+ console.log(chalk.gray(' • Fix errors manually and run `vibecode build --complete`'));
546
+ }
547
+ }
548
+ }
549
+
57
550
  async function handleBuildStart(currentState, projectName, sessionId, sessionPath, specHash) {
58
551
  const spinner = ora('Starting build...').start();
59
552