@nclamvn/vibecode-cli 1.2.0 → 1.5.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/README.md +310 -49
- package/bin/vibecode.js +65 -0
- package/package.json +1 -1
- package/src/agent/decomposition.js +476 -0
- package/src/agent/index.js +325 -0
- package/src/agent/memory.js +542 -0
- package/src/agent/orchestrator.js +644 -0
- package/src/agent/self-healing.js +516 -0
- package/src/commands/agent.js +255 -0
- package/src/commands/assist.js +413 -0
- package/src/commands/build.js +6 -5
- package/src/commands/debug.js +457 -0
- package/src/commands/go.js +380 -0
- package/src/commands/plan.js +8 -2
- package/src/config/templates.js +146 -15
- package/src/core/session.js +18 -2
- package/src/core/test-runner.js +38 -5
- package/src/debug/analyzer.js +329 -0
- package/src/debug/evidence.js +228 -0
- package/src/debug/fixer.js +348 -0
- package/src/debug/index.js +349 -0
- package/src/debug/verifier.js +346 -0
- package/src/index.js +31 -0
- package/src/providers/claude-code.js +12 -7
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// VIBECODE DEBUG - Fix Generator
|
|
3
|
+
// Generates fix prompts and documents applied fixes
|
|
4
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
5
|
+
|
|
6
|
+
import fs from 'fs-extra';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Fix Generator Class
|
|
11
|
+
* Creates targeted fix prompts and maintains fix documentation
|
|
12
|
+
*/
|
|
13
|
+
export class FixGenerator {
|
|
14
|
+
constructor(projectPath) {
|
|
15
|
+
this.projectPath = projectPath;
|
|
16
|
+
this.debugDir = path.join(projectPath, '.vibecode', 'debug');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Generate a fix prompt for Claude Code
|
|
21
|
+
*/
|
|
22
|
+
generateFixPrompt(evidence, hypothesis) {
|
|
23
|
+
const parts = [];
|
|
24
|
+
|
|
25
|
+
// Header with context
|
|
26
|
+
parts.push('# Debug Fix Request');
|
|
27
|
+
parts.push('');
|
|
28
|
+
parts.push('## Error Information');
|
|
29
|
+
parts.push(`- **Type**: ${evidence.type || 'Unknown'}`);
|
|
30
|
+
parts.push(`- **Category**: ${evidence.category}`);
|
|
31
|
+
|
|
32
|
+
if (evidence.message) {
|
|
33
|
+
parts.push(`- **Message**: ${evidence.message}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Files involved
|
|
37
|
+
if (evidence.files.length > 0) {
|
|
38
|
+
parts.push('');
|
|
39
|
+
parts.push('## Affected Files');
|
|
40
|
+
for (const file of evidence.files.slice(0, 5)) {
|
|
41
|
+
parts.push(`- ${file}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Line numbers
|
|
46
|
+
if (evidence.lines.length > 0) {
|
|
47
|
+
parts.push('');
|
|
48
|
+
parts.push('## Line Numbers');
|
|
49
|
+
parts.push(`Lines: ${evidence.lines.slice(0, 10).join(', ')}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Stack trace (condensed)
|
|
53
|
+
if (evidence.stackTrace.length > 0) {
|
|
54
|
+
parts.push('');
|
|
55
|
+
parts.push('## Stack Trace (top 5 frames)');
|
|
56
|
+
parts.push('```');
|
|
57
|
+
for (const line of evidence.stackTrace.slice(0, 5)) {
|
|
58
|
+
parts.push(line);
|
|
59
|
+
}
|
|
60
|
+
parts.push('```');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Hypothesis and fix
|
|
64
|
+
parts.push('');
|
|
65
|
+
parts.push('## Diagnosis');
|
|
66
|
+
parts.push(`**Root Cause**: ${hypothesis.rootCause}`);
|
|
67
|
+
parts.push('');
|
|
68
|
+
parts.push(`**Suggested Fix**: ${hypothesis.description}`);
|
|
69
|
+
parts.push(`**Confidence**: ${Math.round(hypothesis.confidence * 100)}%`);
|
|
70
|
+
|
|
71
|
+
// Category-specific instructions
|
|
72
|
+
parts.push('');
|
|
73
|
+
parts.push('## Fix Instructions');
|
|
74
|
+
parts.push(this.getCategoryInstructions(evidence.category, evidence));
|
|
75
|
+
|
|
76
|
+
// Verification requirement
|
|
77
|
+
parts.push('');
|
|
78
|
+
parts.push('## Verification');
|
|
79
|
+
parts.push('After fixing, ensure:');
|
|
80
|
+
parts.push('1. The original error no longer occurs');
|
|
81
|
+
parts.push('2. No new errors are introduced');
|
|
82
|
+
parts.push('3. Related tests pass (if applicable)');
|
|
83
|
+
|
|
84
|
+
return parts.join('\n');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Get category-specific fix instructions
|
|
89
|
+
*/
|
|
90
|
+
getCategoryInstructions(category, evidence) {
|
|
91
|
+
const instructions = {
|
|
92
|
+
SYNTAX: `
|
|
93
|
+
1. Navigate to the file(s) with syntax errors
|
|
94
|
+
2. Check the indicated line numbers for:
|
|
95
|
+
- Missing or extra brackets, parentheses, braces
|
|
96
|
+
- Missing semicolons or commas
|
|
97
|
+
- Incorrect JSX syntax (unclosed tags, invalid expressions)
|
|
98
|
+
3. Fix the syntax errors
|
|
99
|
+
4. Run the linter/compiler to verify`,
|
|
100
|
+
|
|
101
|
+
TYPE: `
|
|
102
|
+
1. Locate the code accessing ${evidence.message?.match(/property '(\w+)'/)?.[1] || 'the property'}
|
|
103
|
+
2. Add null/undefined check before access:
|
|
104
|
+
- Use optional chaining: \`obj?.property\`
|
|
105
|
+
- Or guard clause: \`if (obj) { ... }\`
|
|
106
|
+
3. Consider why the value might be undefined:
|
|
107
|
+
- Is data not loaded yet?
|
|
108
|
+
- Is there a race condition?
|
|
109
|
+
- Is the property name correct?`,
|
|
110
|
+
|
|
111
|
+
REFERENCE: `
|
|
112
|
+
1. Find where the undefined variable is used
|
|
113
|
+
2. Either:
|
|
114
|
+
- Import it from the correct module
|
|
115
|
+
- Define it before use
|
|
116
|
+
- Check for typos in the variable name
|
|
117
|
+
3. If it's a function/class, ensure it's exported from the source`,
|
|
118
|
+
|
|
119
|
+
IMPORT: `
|
|
120
|
+
1. Check if the module/package exists:
|
|
121
|
+
- For npm packages: \`npm install <package>\`
|
|
122
|
+
- For local files: verify the path is correct
|
|
123
|
+
2. If the export doesn't exist:
|
|
124
|
+
- Check the source module for available exports
|
|
125
|
+
- Use default vs named import correctly
|
|
126
|
+
3. Verify the file extension if needed`,
|
|
127
|
+
|
|
128
|
+
FILE: `
|
|
129
|
+
1. Check if the referenced file/directory exists
|
|
130
|
+
2. Either:
|
|
131
|
+
- Create the missing file/directory
|
|
132
|
+
- Fix the path in the code/config
|
|
133
|
+
3. Verify permissions if needed`,
|
|
134
|
+
|
|
135
|
+
LINT: `
|
|
136
|
+
1. Review the specific linting rule violation
|
|
137
|
+
2. Either:
|
|
138
|
+
- Fix the code to comply with the rule
|
|
139
|
+
- Add eslint-disable comment if intentional (with explanation)
|
|
140
|
+
3. Consider if the rule should be configured differently`,
|
|
141
|
+
|
|
142
|
+
TEST: `
|
|
143
|
+
1. Review the failing test assertion
|
|
144
|
+
2. Determine if:
|
|
145
|
+
- The test expectation is wrong (update test)
|
|
146
|
+
- The implementation is wrong (fix code)
|
|
147
|
+
- The test is testing the wrong thing
|
|
148
|
+
3. Ensure the fix doesn't break other tests`,
|
|
149
|
+
|
|
150
|
+
NEXTJS: `
|
|
151
|
+
1. Identify the Server/Client Component boundary issue
|
|
152
|
+
2. Common fixes:
|
|
153
|
+
- Don't pass functions as props to Client Components
|
|
154
|
+
- Use "use client" directive where needed
|
|
155
|
+
- Move client-only code to useEffect
|
|
156
|
+
- Use serializable props (strings, numbers, objects)
|
|
157
|
+
3. Check if component should be client or server`,
|
|
158
|
+
|
|
159
|
+
DATABASE: `
|
|
160
|
+
1. For Prisma issues:
|
|
161
|
+
- Run: \`npx prisma generate\`
|
|
162
|
+
- Run: \`npx prisma db push\` or \`npx prisma migrate dev\`
|
|
163
|
+
2. Check DATABASE_URL in .env
|
|
164
|
+
3. Verify database connection and permissions
|
|
165
|
+
4. Check schema matches database state`,
|
|
166
|
+
|
|
167
|
+
RUNTIME: `
|
|
168
|
+
1. Add console.log statements to trace the issue
|
|
169
|
+
2. Check the error stack trace for the source
|
|
170
|
+
3. Look for:
|
|
171
|
+
- Async/await issues
|
|
172
|
+
- State management bugs
|
|
173
|
+
- Race conditions
|
|
174
|
+
4. Use debugger breakpoints if available`
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
return instructions[category] || `
|
|
178
|
+
1. Review the error message and stack trace
|
|
179
|
+
2. Locate the source of the error
|
|
180
|
+
3. Apply the suggested fix
|
|
181
|
+
4. Test to verify the fix works`;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Document a fix in the debug log
|
|
186
|
+
*/
|
|
187
|
+
async documentFix(fixInfo) {
|
|
188
|
+
await fs.ensureDir(this.debugDir);
|
|
189
|
+
|
|
190
|
+
const fixesFile = path.join(this.debugDir, 'fixes.md');
|
|
191
|
+
const timestamp = new Date().toISOString();
|
|
192
|
+
|
|
193
|
+
const entry = `
|
|
194
|
+
## Fix Applied - ${timestamp}
|
|
195
|
+
|
|
196
|
+
**Error Type**: ${fixInfo.errorType || 'Unknown'}
|
|
197
|
+
**Category**: ${fixInfo.category}
|
|
198
|
+
**Confidence**: ${Math.round((fixInfo.confidence || 0) * 100)}%
|
|
199
|
+
|
|
200
|
+
### Root Cause
|
|
201
|
+
${fixInfo.rootCause || 'Not determined'}
|
|
202
|
+
|
|
203
|
+
### Fix Applied
|
|
204
|
+
${fixInfo.description || 'Manual fix'}
|
|
205
|
+
|
|
206
|
+
### Files Modified
|
|
207
|
+
${(fixInfo.files || []).map(f => `- ${f}`).join('\n') || '- Unknown'}
|
|
208
|
+
|
|
209
|
+
### Verification
|
|
210
|
+
- Status: ${fixInfo.verified ? '✅ Verified' : '⏳ Pending verification'}
|
|
211
|
+
${fixInfo.verificationOutput ? `- Output: ${fixInfo.verificationOutput}` : ''}
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
`;
|
|
215
|
+
|
|
216
|
+
// Append to fixes log
|
|
217
|
+
let content = '';
|
|
218
|
+
if (await fs.pathExists(fixesFile)) {
|
|
219
|
+
content = await fs.readFile(fixesFile, 'utf-8');
|
|
220
|
+
} else {
|
|
221
|
+
content = `# Vibecode Debug - Fix History
|
|
222
|
+
|
|
223
|
+
This file tracks all fixes applied through the Vibecode debug system.
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
`;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
await fs.writeFile(fixesFile, content + entry);
|
|
230
|
+
return fixesFile;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Update CLAUDE.md with prevention rules
|
|
235
|
+
*/
|
|
236
|
+
async updateClaudeMd(fix) {
|
|
237
|
+
const claudeMdPath = path.join(this.projectPath, 'CLAUDE.md');
|
|
238
|
+
let content = '';
|
|
239
|
+
|
|
240
|
+
if (await fs.pathExists(claudeMdPath)) {
|
|
241
|
+
content = await fs.readFile(claudeMdPath, 'utf-8');
|
|
242
|
+
} else {
|
|
243
|
+
content = `# CLAUDE.md - Project Guidelines
|
|
244
|
+
|
|
245
|
+
This file contains project-specific guidelines and lessons learned.
|
|
246
|
+
|
|
247
|
+
`;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Check if we already have a debugging section
|
|
251
|
+
const debugSection = '## Debugging History & Prevention';
|
|
252
|
+
if (!content.includes(debugSection)) {
|
|
253
|
+
content += `\n${debugSection}\n\n`;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Generate prevention rule
|
|
257
|
+
const preventionRule = this.generatePreventionRule(fix);
|
|
258
|
+
|
|
259
|
+
// Add to the debugging section
|
|
260
|
+
const sectionIndex = content.indexOf(debugSection);
|
|
261
|
+
const insertPoint = content.indexOf('\n\n', sectionIndex + debugSection.length);
|
|
262
|
+
|
|
263
|
+
if (insertPoint !== -1) {
|
|
264
|
+
content = content.slice(0, insertPoint) +
|
|
265
|
+
'\n' + preventionRule +
|
|
266
|
+
content.slice(insertPoint);
|
|
267
|
+
} else {
|
|
268
|
+
content += preventionRule + '\n';
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
await fs.writeFile(claudeMdPath, content);
|
|
272
|
+
return claudeMdPath;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Generate a prevention rule from a fix
|
|
277
|
+
*/
|
|
278
|
+
generatePreventionRule(fix) {
|
|
279
|
+
const rules = {
|
|
280
|
+
SYNTAX: `- ⚠️ Check syntax carefully: ${fix.message || 'brackets, semicolons, JSX tags'}`,
|
|
281
|
+
TYPE: `- ⚠️ Always use optional chaining (?.) when accessing: ${fix.files?.[0] || 'nested properties'}`,
|
|
282
|
+
REFERENCE: `- ⚠️ Ensure imports exist before using: ${fix.message?.match(/'(\w+)'/)?.[1] || 'variables'}`,
|
|
283
|
+
IMPORT: `- ⚠️ Verify module paths and exports: ${fix.files?.[0] || 'check imports'}`,
|
|
284
|
+
NEXTJS: `- ⚠️ Never pass functions to Client Components - use formatType strings instead`,
|
|
285
|
+
DATABASE: `- ⚠️ Run prisma generate after schema changes`,
|
|
286
|
+
TEST: `- ⚠️ Keep tests in sync with implementation`,
|
|
287
|
+
LINT: `- ⚠️ Follow linting rules: ${fix.message || 'check ESLint config'}`,
|
|
288
|
+
FILE: `- ⚠️ Verify file paths before referencing`,
|
|
289
|
+
RUNTIME: `- ⚠️ Handle edge cases: ${fix.rootCause || 'check for null/undefined'}`
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
const timestamp = new Date().toISOString().split('T')[0];
|
|
293
|
+
const rule = rules[fix.category] || `- ⚠️ Avoid: ${fix.rootCause || fix.description}`;
|
|
294
|
+
|
|
295
|
+
return `### [${timestamp}] ${fix.category} Fix
|
|
296
|
+
${rule}
|
|
297
|
+
`;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Generate a minimal fix prompt (for auto-fix mode)
|
|
302
|
+
*/
|
|
303
|
+
generateMinimalPrompt(evidence, hypothesis) {
|
|
304
|
+
const parts = [
|
|
305
|
+
`Fix this ${evidence.category} error:`,
|
|
306
|
+
'',
|
|
307
|
+
`Error: ${evidence.message || evidence.description}`,
|
|
308
|
+
''
|
|
309
|
+
];
|
|
310
|
+
|
|
311
|
+
if (evidence.files.length > 0) {
|
|
312
|
+
parts.push(`File: ${evidence.files[0]}`);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (evidence.lines.length > 0) {
|
|
316
|
+
parts.push(`Line: ${evidence.lines[0]}`);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
parts.push('');
|
|
320
|
+
parts.push(`Fix: ${hypothesis.description}`);
|
|
321
|
+
|
|
322
|
+
return parts.join('\n');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Create a fix attempt record
|
|
327
|
+
*/
|
|
328
|
+
createFixAttempt(evidence, hypothesis) {
|
|
329
|
+
return {
|
|
330
|
+
id: `fix-${Date.now()}`,
|
|
331
|
+
timestamp: new Date().toISOString(),
|
|
332
|
+
errorType: evidence.type,
|
|
333
|
+
category: evidence.category,
|
|
334
|
+
message: evidence.message,
|
|
335
|
+
files: evidence.files,
|
|
336
|
+
lines: evidence.lines,
|
|
337
|
+
rootCause: hypothesis.rootCause,
|
|
338
|
+
description: hypothesis.description,
|
|
339
|
+
confidence: hypothesis.confidence,
|
|
340
|
+
status: 'pending',
|
|
341
|
+
verified: false
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export function createFixGenerator(projectPath) {
|
|
347
|
+
return new FixGenerator(projectPath);
|
|
348
|
+
}
|
|
@@ -0,0 +1,349 @@
|
|
|
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
|
+
|
|
31
|
+
const execAsync = promisify(exec);
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Debug Engine Class
|
|
35
|
+
* Main orchestrator for the debugging workflow
|
|
36
|
+
*/
|
|
37
|
+
export class DebugEngine {
|
|
38
|
+
constructor(projectPath, options = {}) {
|
|
39
|
+
this.projectPath = projectPath;
|
|
40
|
+
this.options = {
|
|
41
|
+
autoFix: options.autoFix || false,
|
|
42
|
+
maxAttempts: options.maxAttempts || 3,
|
|
43
|
+
verbose: options.verbose || false,
|
|
44
|
+
interactive: options.interactive !== false,
|
|
45
|
+
...options
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Initialize components
|
|
49
|
+
this.evidence = new EvidenceCollector(projectPath);
|
|
50
|
+
this.analyzer = new RootCauseAnalyzer();
|
|
51
|
+
this.fixer = new FixGenerator(projectPath);
|
|
52
|
+
this.verifier = new FixVerifier(projectPath);
|
|
53
|
+
|
|
54
|
+
// Debug session state
|
|
55
|
+
this.session = {
|
|
56
|
+
id: `debug-${Date.now()}`,
|
|
57
|
+
startTime: new Date().toISOString(),
|
|
58
|
+
steps: [],
|
|
59
|
+
attempts: 0,
|
|
60
|
+
resolved: false
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Run the full debug workflow
|
|
66
|
+
*/
|
|
67
|
+
async debug(input) {
|
|
68
|
+
console.log(chalk.cyan.bold('\n🔍 VIBECODE DEBUG - Intelligent Bug Fixing\n'));
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
// Step 1: EVIDENCE - Gather error information
|
|
72
|
+
this.logStep(1, 'EVIDENCE', 'Gathering error information...');
|
|
73
|
+
const evidence = await this.evidence.collect(input);
|
|
74
|
+
this.session.steps.push({ step: 'EVIDENCE', data: evidence });
|
|
75
|
+
this.logEvidence(evidence);
|
|
76
|
+
|
|
77
|
+
// Step 2: REPRODUCE - Confirm error exists (if auto mode)
|
|
78
|
+
if (input.auto) {
|
|
79
|
+
this.logStep(2, 'REPRODUCE', 'Confirming error exists...');
|
|
80
|
+
const reproduced = await this.reproduce(evidence);
|
|
81
|
+
this.session.steps.push({ step: 'REPRODUCE', data: reproduced });
|
|
82
|
+
|
|
83
|
+
if (!reproduced.errorFound) {
|
|
84
|
+
console.log(chalk.green(' ✓ No errors found during reproduction check'));
|
|
85
|
+
return this.createResult('no_error', 'No errors found to fix');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Step 3: ANALYZE - Identify root cause
|
|
90
|
+
this.logStep(3, 'ANALYZE', 'Analyzing root cause...');
|
|
91
|
+
const analysis = await this.analyzer.analyze(evidence);
|
|
92
|
+
this.session.steps.push({ step: 'ANALYZE', data: analysis });
|
|
93
|
+
this.logAnalysis(analysis);
|
|
94
|
+
|
|
95
|
+
// Step 4: HYPOTHESIZE - Generate fix hypotheses
|
|
96
|
+
this.logStep(4, 'HYPOTHESIZE', 'Generating fix hypotheses...');
|
|
97
|
+
const hypotheses = this.analyzer.buildHypotheses(analysis);
|
|
98
|
+
this.session.steps.push({ step: 'HYPOTHESIZE', data: hypotheses });
|
|
99
|
+
this.logHypotheses(hypotheses);
|
|
100
|
+
|
|
101
|
+
if (hypotheses.length === 0) {
|
|
102
|
+
console.log(chalk.yellow(' ⚠ No fix hypotheses generated'));
|
|
103
|
+
return this.createResult('no_hypothesis', 'Could not generate fix hypotheses');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Step 5-7: TEST, FIX, VERIFY - Attempt fixes
|
|
107
|
+
let fixResult = null;
|
|
108
|
+
for (let attempt = 0; attempt < this.options.maxAttempts && !this.session.resolved; attempt++) {
|
|
109
|
+
this.session.attempts = attempt + 1;
|
|
110
|
+
|
|
111
|
+
const hypothesis = hypotheses[attempt % hypotheses.length];
|
|
112
|
+
console.log(chalk.cyan(`\n Attempt ${attempt + 1}/${this.options.maxAttempts}:`));
|
|
113
|
+
|
|
114
|
+
// Step 5: TEST - Generate fix prompt
|
|
115
|
+
this.logStep(5, 'TEST', 'Preparing fix...');
|
|
116
|
+
const fixPrompt = this.fixer.generateFixPrompt(evidence, hypothesis);
|
|
117
|
+
const fixAttempt = this.fixer.createFixAttempt(evidence, hypothesis);
|
|
118
|
+
|
|
119
|
+
// Step 6: FIX - Apply fix via Claude Code
|
|
120
|
+
this.logStep(6, 'FIX', 'Applying fix via Claude Code...');
|
|
121
|
+
fixResult = await this.applyFix(fixPrompt);
|
|
122
|
+
fixAttempt.status = fixResult.success ? 'applied' : 'failed';
|
|
123
|
+
|
|
124
|
+
if (!fixResult.success) {
|
|
125
|
+
console.log(chalk.yellow(` ⚠ Fix application failed: ${fixResult.error}`));
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Step 7: VERIFY - Confirm fix works
|
|
130
|
+
this.logStep(7, 'VERIFY', 'Verifying fix...');
|
|
131
|
+
const verification = await this.verifier.verify(evidence, fixAttempt);
|
|
132
|
+
this.session.steps.push({ step: 'VERIFY', data: verification });
|
|
133
|
+
|
|
134
|
+
if (verification.passed) {
|
|
135
|
+
console.log(chalk.green(' ✓ Fix verified successfully!'));
|
|
136
|
+
this.session.resolved = true;
|
|
137
|
+
fixAttempt.verified = true;
|
|
138
|
+
|
|
139
|
+
// Step 8: DOCUMENT - Log the fix
|
|
140
|
+
this.logStep(8, 'DOCUMENT', 'Documenting fix...');
|
|
141
|
+
await this.fixer.documentFix(fixAttempt);
|
|
142
|
+
|
|
143
|
+
// Step 9: PREVENT - Add prevention rules
|
|
144
|
+
this.logStep(9, 'PREVENT', 'Adding prevention rules...');
|
|
145
|
+
await this.fixer.updateClaudeMd(fixAttempt);
|
|
146
|
+
|
|
147
|
+
console.log(chalk.green.bold('\n✅ Bug fixed and documented!\n'));
|
|
148
|
+
} else {
|
|
149
|
+
console.log(chalk.yellow(' ⚠ Verification failed, trying next approach...'));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return this.createResult(
|
|
154
|
+
this.session.resolved ? 'resolved' : 'unresolved',
|
|
155
|
+
this.session.resolved ? 'Bug fixed successfully' : 'Could not resolve after max attempts',
|
|
156
|
+
fixResult
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
} catch (error) {
|
|
160
|
+
console.log(chalk.red(`\n❌ Debug error: ${error.message}\n`));
|
|
161
|
+
return this.createResult('error', error.message);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Quick debug mode - minimal interaction
|
|
167
|
+
*/
|
|
168
|
+
async quickDebug(description) {
|
|
169
|
+
return this.debug({
|
|
170
|
+
description,
|
|
171
|
+
auto: false
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Auto scan and fix mode
|
|
177
|
+
*/
|
|
178
|
+
async autoDebug() {
|
|
179
|
+
console.log(chalk.cyan('🔄 Auto-scanning project for errors...\n'));
|
|
180
|
+
|
|
181
|
+
return this.debug({
|
|
182
|
+
auto: true
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Reproduce the error to confirm it exists
|
|
188
|
+
*/
|
|
189
|
+
async reproduce(evidence) {
|
|
190
|
+
const result = {
|
|
191
|
+
errorFound: false,
|
|
192
|
+
output: ''
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// Try to reproduce based on category
|
|
196
|
+
try {
|
|
197
|
+
const checks = {
|
|
198
|
+
SYNTAX: 'npx tsc --noEmit',
|
|
199
|
+
TYPE: 'npx tsc --noEmit',
|
|
200
|
+
IMPORT: 'npx tsc --noEmit',
|
|
201
|
+
BUILD: 'npm run build',
|
|
202
|
+
LINT: 'npm run lint',
|
|
203
|
+
TEST: 'npm test'
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const command = checks[evidence.category] || 'npm run build';
|
|
207
|
+
|
|
208
|
+
await execAsync(command, {
|
|
209
|
+
cwd: this.projectPath,
|
|
210
|
+
timeout: 60000
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
result.output = 'Command succeeded';
|
|
214
|
+
} catch (error) {
|
|
215
|
+
result.errorFound = true;
|
|
216
|
+
result.output = error.stderr || error.stdout || error.message;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return result;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Apply a fix using Claude Code
|
|
224
|
+
*/
|
|
225
|
+
async applyFix(prompt) {
|
|
226
|
+
const result = {
|
|
227
|
+
success: false,
|
|
228
|
+
output: '',
|
|
229
|
+
error: null
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
try {
|
|
233
|
+
// Check if Claude Code is available
|
|
234
|
+
const available = await isClaudeCodeAvailable();
|
|
235
|
+
if (!available) {
|
|
236
|
+
throw new Error('Claude Code CLI not found. Install with: npm install -g @anthropic-ai/claude-code');
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Save prompt to debug folder for reference
|
|
240
|
+
const promptFile = path.join(this.projectPath, '.vibecode', 'debug', 'current-fix.md');
|
|
241
|
+
await fs.ensureDir(path.dirname(promptFile));
|
|
242
|
+
await fs.writeFile(promptFile, prompt);
|
|
243
|
+
|
|
244
|
+
if (this.options.verbose) {
|
|
245
|
+
console.log(chalk.gray(` Running Claude Code with fix prompt...`));
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Use spawnClaudeCode which handles temp file properly
|
|
249
|
+
const spawnResult = await spawnClaudeCode(prompt, {
|
|
250
|
+
cwd: this.projectPath
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
result.success = spawnResult.success;
|
|
254
|
+
result.output = `Exit code: ${spawnResult.code}`;
|
|
255
|
+
|
|
256
|
+
} catch (error) {
|
|
257
|
+
result.error = error.message;
|
|
258
|
+
result.output = '';
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return result;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Log a debug step
|
|
266
|
+
*/
|
|
267
|
+
logStep(number, name, message) {
|
|
268
|
+
const stepLabel = chalk.cyan(`[${number}/9 ${name}]`);
|
|
269
|
+
console.log(`${stepLabel} ${message}`);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Log evidence summary
|
|
274
|
+
*/
|
|
275
|
+
logEvidence(evidence) {
|
|
276
|
+
console.log(chalk.gray(` Type: ${evidence.type || 'Unknown'}`));
|
|
277
|
+
console.log(chalk.gray(` Category: ${evidence.category}`));
|
|
278
|
+
if (evidence.message) {
|
|
279
|
+
console.log(chalk.gray(` Message: ${evidence.message.substring(0, 100)}${evidence.message.length > 100 ? '...' : ''}`));
|
|
280
|
+
}
|
|
281
|
+
if (evidence.files.length > 0) {
|
|
282
|
+
console.log(chalk.gray(` Files: ${evidence.files.slice(0, 3).join(', ')}${evidence.files.length > 3 ? '...' : ''}`));
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Log analysis results
|
|
288
|
+
*/
|
|
289
|
+
logAnalysis(analysis) {
|
|
290
|
+
console.log(chalk.gray(` Root cause: ${analysis.rootCause || 'Unknown'}`));
|
|
291
|
+
console.log(chalk.gray(` Confidence: ${Math.round(analysis.confidence * 100)}%`));
|
|
292
|
+
if (analysis.patterns.length > 0) {
|
|
293
|
+
console.log(chalk.gray(` Patterns: ${analysis.patterns.join(', ')}`));
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Log hypotheses
|
|
299
|
+
*/
|
|
300
|
+
logHypotheses(hypotheses) {
|
|
301
|
+
console.log(chalk.gray(` Generated ${hypotheses.length} hypothesis(es):`));
|
|
302
|
+
for (const h of hypotheses.slice(0, 3)) {
|
|
303
|
+
console.log(chalk.gray(` - ${h.description.substring(0, 60)}... (${Math.round(h.confidence * 100)}%)`));
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Create result object
|
|
309
|
+
*/
|
|
310
|
+
createResult(status, message, fixResult = null) {
|
|
311
|
+
return {
|
|
312
|
+
status,
|
|
313
|
+
message,
|
|
314
|
+
session: {
|
|
315
|
+
id: this.session.id,
|
|
316
|
+
attempts: this.session.attempts,
|
|
317
|
+
resolved: this.session.resolved,
|
|
318
|
+
steps: this.session.steps.length
|
|
319
|
+
},
|
|
320
|
+
fixResult
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Get debug session summary
|
|
326
|
+
*/
|
|
327
|
+
getSessionSummary() {
|
|
328
|
+
return {
|
|
329
|
+
id: this.session.id,
|
|
330
|
+
startTime: this.session.startTime,
|
|
331
|
+
attempts: this.session.attempts,
|
|
332
|
+
resolved: this.session.resolved,
|
|
333
|
+
stepsCompleted: this.session.steps.map(s => s.step)
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Create a new debug engine instance
|
|
340
|
+
*/
|
|
341
|
+
export function createDebugEngine(projectPath, options = {}) {
|
|
342
|
+
return new DebugEngine(projectPath, options);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Re-export components
|
|
346
|
+
export { EvidenceCollector } from './evidence.js';
|
|
347
|
+
export { RootCauseAnalyzer } from './analyzer.js';
|
|
348
|
+
export { FixGenerator } from './fixer.js';
|
|
349
|
+
export { FixVerifier } from './verifier.js';
|