@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.
@@ -0,0 +1,378 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════════
2
+ // VIBECODE DEBUG - Main Debug Engine
3
+ // Orchestrates the 9-step intelligent debugging workflow
4
+ // ═══════════════════════════════════════════════════════════════════════════════
5
+ //
6
+ // The 9-Step Debug Protocol:
7
+ // 1. EVIDENCE - Gather error information
8
+ // 2. REPRODUCE - Confirm the error exists
9
+ // 3. ANALYZE - Identify root cause
10
+ // 4. HYPOTHESIZE - Generate fix hypotheses
11
+ // 5. TEST - Test hypothesis validity
12
+ // 6. FIX - Apply the fix via Claude Code
13
+ // 7. VERIFY - Confirm the fix works
14
+ // 8. DOCUMENT - Log the fix for future reference
15
+ // 9. PREVENT - Add prevention rules to CLAUDE.md
16
+ //
17
+ // ═══════════════════════════════════════════════════════════════════════════════
18
+
19
+ import { exec } from 'child_process';
20
+ import { promisify } from 'util';
21
+ import fs from 'fs-extra';
22
+ import path from 'path';
23
+ import chalk from 'chalk';
24
+
25
+ import { EvidenceCollector } from './evidence.js';
26
+ import { RootCauseAnalyzer } from './analyzer.js';
27
+ import { FixGenerator } from './fixer.js';
28
+ import { FixVerifier } from './verifier.js';
29
+ import { spawnClaudeCode, isClaudeCodeAvailable } from '../providers/index.js';
30
+ import { translateError, formatTranslatedError } from '../ui/error-translator.js';
31
+ import { getLearningEngine } from '../core/learning.js';
32
+ import { askFeedback, showLearningSuggestion } from '../commands/learn.js';
33
+
34
+ const execAsync = promisify(exec);
35
+
36
+ /**
37
+ * Debug Engine Class
38
+ * Main orchestrator for the debugging workflow
39
+ */
40
+ export class DebugEngine {
41
+ constructor(projectPath, options = {}) {
42
+ this.projectPath = projectPath;
43
+ this.options = {
44
+ autoFix: options.autoFix || false,
45
+ maxAttempts: options.maxAttempts || 3,
46
+ verbose: options.verbose || false,
47
+ interactive: options.interactive !== false,
48
+ ...options
49
+ };
50
+
51
+ // Initialize components
52
+ this.evidence = new EvidenceCollector(projectPath);
53
+ this.analyzer = new RootCauseAnalyzer();
54
+ this.fixer = new FixGenerator(projectPath);
55
+ this.verifier = new FixVerifier(projectPath);
56
+
57
+ // Debug session state
58
+ this.session = {
59
+ id: `debug-${Date.now()}`,
60
+ startTime: new Date().toISOString(),
61
+ steps: [],
62
+ attempts: 0,
63
+ resolved: false
64
+ };
65
+ }
66
+
67
+ /**
68
+ * Run the full debug workflow
69
+ */
70
+ async debug(input) {
71
+ console.log(chalk.cyan.bold('\n🔍 VIBECODE DEBUG - Intelligent Bug Fixing\n'));
72
+
73
+ try {
74
+ // Step 1: EVIDENCE - Gather error information
75
+ this.logStep(1, 'EVIDENCE', 'Gathering error information...');
76
+ const evidence = await this.evidence.collect(input);
77
+ this.session.steps.push({ step: 'EVIDENCE', data: evidence });
78
+ this.logEvidence(evidence);
79
+
80
+ // Step 2: REPRODUCE - Confirm error exists (if auto mode)
81
+ if (input.auto) {
82
+ this.logStep(2, 'REPRODUCE', 'Confirming error exists...');
83
+ const reproduced = await this.reproduce(evidence);
84
+ this.session.steps.push({ step: 'REPRODUCE', data: reproduced });
85
+
86
+ if (!reproduced.errorFound) {
87
+ console.log(chalk.green(' ✓ No errors found during reproduction check'));
88
+ return this.createResult('no_error', 'No errors found to fix');
89
+ }
90
+ }
91
+
92
+ // Step 3: ANALYZE - Identify root cause
93
+ this.logStep(3, 'ANALYZE', 'Analyzing root cause...');
94
+ const analysis = await this.analyzer.analyze(evidence);
95
+ this.session.steps.push({ step: 'ANALYZE', data: analysis });
96
+ this.logAnalysis(analysis);
97
+
98
+ // Step 4: HYPOTHESIZE - Generate fix hypotheses
99
+ this.logStep(4, 'HYPOTHESIZE', 'Generating fix hypotheses...');
100
+ const hypotheses = this.analyzer.buildHypotheses(analysis);
101
+ this.session.steps.push({ step: 'HYPOTHESIZE', data: hypotheses });
102
+ this.logHypotheses(hypotheses);
103
+
104
+ if (hypotheses.length === 0) {
105
+ console.log(chalk.yellow(' ⚠ No fix hypotheses generated'));
106
+ return this.createResult('no_hypothesis', 'Could not generate fix hypotheses');
107
+ }
108
+
109
+ // Check for learning-based suggestions
110
+ await showLearningSuggestion(evidence.type, evidence.category);
111
+
112
+ // Step 5-7: TEST, FIX, VERIFY - Attempt fixes
113
+ let fixResult = null;
114
+ for (let attempt = 0; attempt < this.options.maxAttempts && !this.session.resolved; attempt++) {
115
+ this.session.attempts = attempt + 1;
116
+
117
+ const hypothesis = hypotheses[attempt % hypotheses.length];
118
+ console.log(chalk.cyan(`\n Attempt ${attempt + 1}/${this.options.maxAttempts}:`));
119
+
120
+ // Step 5: TEST - Generate fix prompt
121
+ this.logStep(5, 'TEST', 'Preparing fix...');
122
+ const fixPrompt = this.fixer.generateFixPrompt(evidence, hypothesis);
123
+ const fixAttempt = this.fixer.createFixAttempt(evidence, hypothesis);
124
+
125
+ // Step 6: FIX - Apply fix via Claude Code
126
+ this.logStep(6, 'FIX', 'Applying fix via Claude Code...');
127
+ fixResult = await this.applyFix(fixPrompt);
128
+ fixAttempt.status = fixResult.success ? 'applied' : 'failed';
129
+
130
+ if (!fixResult.success) {
131
+ console.log(chalk.yellow(` ⚠ Fix application failed: ${fixResult.error}`));
132
+ continue;
133
+ }
134
+
135
+ // Step 7: VERIFY - Confirm fix works
136
+ this.logStep(7, 'VERIFY', 'Verifying fix...');
137
+ const verification = await this.verifier.verify(evidence, fixAttempt);
138
+ this.session.steps.push({ step: 'VERIFY', data: verification });
139
+
140
+ if (verification.passed) {
141
+ console.log(chalk.green(' ✓ Fix verified successfully!'));
142
+ this.session.resolved = true;
143
+ fixAttempt.verified = true;
144
+
145
+ // Step 8: DOCUMENT - Log the fix
146
+ this.logStep(8, 'DOCUMENT', 'Documenting fix...');
147
+ await this.fixer.documentFix(fixAttempt);
148
+
149
+ // Step 9: PREVENT - Add prevention rules
150
+ this.logStep(9, 'PREVENT', 'Adding prevention rules...');
151
+ await this.fixer.updateClaudeMd(fixAttempt);
152
+
153
+ console.log(chalk.green.bold('\n✅ Bug fixed and documented!\n'));
154
+
155
+ // Ask for feedback to improve future suggestions
156
+ if (this.options.interactive) {
157
+ await askFeedback({
158
+ errorType: evidence.type,
159
+ errorMessage: evidence.message,
160
+ errorCategory: evidence.category,
161
+ fixApplied: fixAttempt.description || hypothesis.description
162
+ });
163
+ }
164
+ } else {
165
+ console.log(chalk.yellow(' ⚠ Verification failed, trying next approach...'));
166
+ }
167
+ }
168
+
169
+ return this.createResult(
170
+ this.session.resolved ? 'resolved' : 'unresolved',
171
+ this.session.resolved ? 'Bug fixed successfully' : 'Could not resolve after max attempts',
172
+ fixResult
173
+ );
174
+
175
+ } catch (error) {
176
+ console.log(chalk.red(`\n❌ Debug error: ${error.message}\n`));
177
+ return this.createResult('error', error.message);
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Quick debug mode - minimal interaction
183
+ */
184
+ async quickDebug(description) {
185
+ return this.debug({
186
+ description,
187
+ auto: false
188
+ });
189
+ }
190
+
191
+ /**
192
+ * Auto scan and fix mode
193
+ */
194
+ async autoDebug() {
195
+ console.log(chalk.cyan('🔄 Auto-scanning project for errors...\n'));
196
+
197
+ return this.debug({
198
+ auto: true
199
+ });
200
+ }
201
+
202
+ /**
203
+ * Reproduce the error to confirm it exists
204
+ */
205
+ async reproduce(evidence) {
206
+ const result = {
207
+ errorFound: false,
208
+ output: ''
209
+ };
210
+
211
+ // Try to reproduce based on category
212
+ try {
213
+ const checks = {
214
+ SYNTAX: 'npx tsc --noEmit',
215
+ TYPE: 'npx tsc --noEmit',
216
+ IMPORT: 'npx tsc --noEmit',
217
+ BUILD: 'npm run build',
218
+ LINT: 'npm run lint',
219
+ TEST: 'npm test'
220
+ };
221
+
222
+ const command = checks[evidence.category] || 'npm run build';
223
+
224
+ await execAsync(command, {
225
+ cwd: this.projectPath,
226
+ timeout: 60000
227
+ });
228
+
229
+ result.output = 'Command succeeded';
230
+ } catch (error) {
231
+ result.errorFound = true;
232
+ result.output = error.stderr || error.stdout || error.message;
233
+ }
234
+
235
+ return result;
236
+ }
237
+
238
+ /**
239
+ * Apply a fix using Claude Code
240
+ */
241
+ async applyFix(prompt) {
242
+ const result = {
243
+ success: false,
244
+ output: '',
245
+ error: null
246
+ };
247
+
248
+ try {
249
+ // Check if Claude Code is available
250
+ const available = await isClaudeCodeAvailable();
251
+ if (!available) {
252
+ throw new Error('Claude Code CLI not found. Install with: npm install -g @anthropic-ai/claude-code');
253
+ }
254
+
255
+ // Save prompt to debug folder for reference
256
+ const promptFile = path.join(this.projectPath, '.vibecode', 'debug', 'current-fix.md');
257
+ await fs.ensureDir(path.dirname(promptFile));
258
+ await fs.writeFile(promptFile, prompt);
259
+
260
+ if (this.options.verbose) {
261
+ console.log(chalk.gray(` Running Claude Code with fix prompt...`));
262
+ }
263
+
264
+ // Use spawnClaudeCode which handles temp file properly
265
+ const spawnResult = await spawnClaudeCode(prompt, {
266
+ cwd: this.projectPath
267
+ });
268
+
269
+ result.success = spawnResult.success;
270
+ result.output = `Exit code: ${spawnResult.code}`;
271
+
272
+ } catch (error) {
273
+ result.error = error.message;
274
+ result.output = '';
275
+ }
276
+
277
+ return result;
278
+ }
279
+
280
+ /**
281
+ * Log a debug step
282
+ */
283
+ logStep(number, name, message) {
284
+ const stepLabel = chalk.cyan(`[${number}/9 ${name}]`);
285
+ console.log(`${stepLabel} ${message}`);
286
+ }
287
+
288
+ /**
289
+ * Log evidence summary
290
+ */
291
+ logEvidence(evidence) {
292
+ console.log(chalk.gray(` Type: ${evidence.type || 'Unknown'}`));
293
+ console.log(chalk.gray(` Category: ${evidence.category}`));
294
+
295
+ if (evidence.message) {
296
+ // Translate error for human-friendly display
297
+ const translated = translateError(evidence.message);
298
+ console.log(chalk.yellow(` Error: ${translated.title}`));
299
+ console.log(chalk.gray(` → ${translated.description.substring(0, 80)}${translated.description.length > 80 ? '...' : ''}`));
300
+
301
+ // Show suggestions
302
+ if (translated.suggestions && translated.suggestions.length > 0) {
303
+ console.log(chalk.gray(` Suggestions:`));
304
+ for (const s of translated.suggestions.slice(0, 2)) {
305
+ console.log(chalk.gray(` • ${s}`));
306
+ }
307
+ }
308
+ }
309
+
310
+ if (evidence.files.length > 0) {
311
+ console.log(chalk.gray(` Files: ${evidence.files.slice(0, 3).join(', ')}${evidence.files.length > 3 ? '...' : ''}`));
312
+ }
313
+ }
314
+
315
+ /**
316
+ * Log analysis results
317
+ */
318
+ logAnalysis(analysis) {
319
+ console.log(chalk.gray(` Root cause: ${analysis.rootCause || 'Unknown'}`));
320
+ console.log(chalk.gray(` Confidence: ${Math.round(analysis.confidence * 100)}%`));
321
+ if (analysis.patterns.length > 0) {
322
+ console.log(chalk.gray(` Patterns: ${analysis.patterns.join(', ')}`));
323
+ }
324
+ }
325
+
326
+ /**
327
+ * Log hypotheses
328
+ */
329
+ logHypotheses(hypotheses) {
330
+ console.log(chalk.gray(` Generated ${hypotheses.length} hypothesis(es):`));
331
+ for (const h of hypotheses.slice(0, 3)) {
332
+ console.log(chalk.gray(` - ${h.description.substring(0, 60)}... (${Math.round(h.confidence * 100)}%)`));
333
+ }
334
+ }
335
+
336
+ /**
337
+ * Create result object
338
+ */
339
+ createResult(status, message, fixResult = null) {
340
+ return {
341
+ status,
342
+ message,
343
+ session: {
344
+ id: this.session.id,
345
+ attempts: this.session.attempts,
346
+ resolved: this.session.resolved,
347
+ steps: this.session.steps.length
348
+ },
349
+ fixResult
350
+ };
351
+ }
352
+
353
+ /**
354
+ * Get debug session summary
355
+ */
356
+ getSessionSummary() {
357
+ return {
358
+ id: this.session.id,
359
+ startTime: this.session.startTime,
360
+ attempts: this.session.attempts,
361
+ resolved: this.session.resolved,
362
+ stepsCompleted: this.session.steps.map(s => s.step)
363
+ };
364
+ }
365
+ }
366
+
367
+ /**
368
+ * Create a new debug engine instance
369
+ */
370
+ export function createDebugEngine(projectPath, options = {}) {
371
+ return new DebugEngine(projectPath, options);
372
+ }
373
+
374
+ // Re-export components
375
+ export { EvidenceCollector } from './evidence.js';
376
+ export { RootCauseAnalyzer } from './analyzer.js';
377
+ export { FixGenerator } from './fixer.js';
378
+ export { FixVerifier } from './verifier.js';