@nclamvn/vibecode-cli 2.0.0 → 2.2.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/.vibecode/learning/fixes.json +1 -0
- package/.vibecode/learning/preferences.json +1 -0
- package/README.md +310 -49
- package/SESSION_NOTES.md +154 -0
- package/bin/vibecode.js +235 -2
- package/package.json +5 -2
- package/src/agent/decomposition.js +476 -0
- package/src/agent/index.js +391 -0
- package/src/agent/memory.js +542 -0
- package/src/agent/orchestrator.js +917 -0
- package/src/agent/self-healing.js +516 -0
- package/src/commands/agent.js +349 -0
- package/src/commands/ask.js +230 -0
- package/src/commands/assist.js +413 -0
- package/src/commands/build.js +345 -4
- package/src/commands/debug.js +565 -0
- package/src/commands/docs.js +167 -0
- package/src/commands/git.js +1024 -0
- package/src/commands/go.js +635 -0
- package/src/commands/learn.js +294 -0
- package/src/commands/migrate.js +341 -0
- package/src/commands/plan.js +8 -2
- package/src/commands/refactor.js +205 -0
- package/src/commands/review.js +126 -1
- package/src/commands/security.js +229 -0
- package/src/commands/shell.js +486 -0
- package/src/commands/templates.js +397 -0
- package/src/commands/test.js +194 -0
- package/src/commands/undo.js +281 -0
- package/src/commands/watch.js +556 -0
- package/src/commands/wizard.js +322 -0
- package/src/config/constants.js +5 -1
- package/src/config/templates.js +146 -15
- package/src/core/backup.js +325 -0
- package/src/core/error-analyzer.js +237 -0
- package/src/core/fix-generator.js +195 -0
- package/src/core/iteration.js +226 -0
- package/src/core/learning.js +295 -0
- package/src/core/session.js +18 -2
- package/src/core/test-runner.js +281 -0
- package/src/debug/analyzer.js +329 -0
- package/src/debug/evidence.js +228 -0
- package/src/debug/fixer.js +348 -0
- package/src/debug/image-analyzer.js +304 -0
- package/src/debug/index.js +378 -0
- package/src/debug/verifier.js +346 -0
- package/src/index.js +102 -0
- package/src/providers/claude-code.js +12 -7
- package/src/templates/index.js +724 -0
- package/src/ui/__tests__/error-translator.test.js +390 -0
- package/src/ui/dashboard.js +364 -0
- package/src/ui/error-translator.js +775 -0
- package/src/utils/image.js +222 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image Analyzer for Vibecode CLI
|
|
3
|
+
* Analyze screenshots of errors using AI vision
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import fs from 'fs/promises';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { spawn } from 'child_process';
|
|
10
|
+
import { imageToBase64, saveClipboardImage, formatFileSize } from '../utils/image.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* ImageAnalyzer class for screenshot error analysis
|
|
14
|
+
*/
|
|
15
|
+
export class ImageAnalyzer {
|
|
16
|
+
constructor(projectPath = process.cwd()) {
|
|
17
|
+
this.projectPath = projectPath;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Analyze image file for errors
|
|
22
|
+
*/
|
|
23
|
+
async analyzeImage(imagePath) {
|
|
24
|
+
console.log(chalk.cyan('\n Analyzing screenshot...\n'));
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
// Read and validate image
|
|
28
|
+
const imageInfo = await imageToBase64(imagePath);
|
|
29
|
+
|
|
30
|
+
console.log(chalk.gray(` File: ${path.basename(imageInfo.path)}`));
|
|
31
|
+
console.log(chalk.gray(` Size: ${formatFileSize(imageInfo.size)}\n`));
|
|
32
|
+
|
|
33
|
+
// Run AI analysis
|
|
34
|
+
const analysis = await this.runImageAnalysis(imageInfo);
|
|
35
|
+
|
|
36
|
+
return analysis;
|
|
37
|
+
} catch (error) {
|
|
38
|
+
throw new Error(`Failed to analyze image: ${error.message}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Analyze image from clipboard
|
|
44
|
+
*/
|
|
45
|
+
async analyzeClipboard() {
|
|
46
|
+
console.log(chalk.cyan('\n Getting image from clipboard...\n'));
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const tempFile = await saveClipboardImage();
|
|
50
|
+
console.log(chalk.green(` Image captured\n`));
|
|
51
|
+
|
|
52
|
+
const result = await this.analyzeImage(tempFile);
|
|
53
|
+
|
|
54
|
+
// Cleanup temp file
|
|
55
|
+
try {
|
|
56
|
+
await fs.unlink(tempFile);
|
|
57
|
+
} catch {
|
|
58
|
+
// Ignore cleanup errors
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return result;
|
|
62
|
+
} catch (error) {
|
|
63
|
+
throw new Error(`Clipboard error: ${error.message}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Run AI analysis on image
|
|
69
|
+
*/
|
|
70
|
+
async runImageAnalysis(imageInfo) {
|
|
71
|
+
// Create analysis prompt
|
|
72
|
+
const prompt = this.buildAnalysisPrompt(imageInfo);
|
|
73
|
+
|
|
74
|
+
// Save prompt to temp file
|
|
75
|
+
const tempDir = path.join(this.projectPath, '.vibecode', 'debug');
|
|
76
|
+
await fs.mkdir(tempDir, { recursive: true });
|
|
77
|
+
|
|
78
|
+
const promptFile = path.join(tempDir, 'image-analysis.md');
|
|
79
|
+
await fs.writeFile(promptFile, prompt);
|
|
80
|
+
|
|
81
|
+
// Also save image reference
|
|
82
|
+
const imageRef = path.join(tempDir, 'screenshot-ref.txt');
|
|
83
|
+
await fs.writeFile(imageRef, imageInfo.path);
|
|
84
|
+
|
|
85
|
+
// Run Claude Code with image
|
|
86
|
+
const result = await this.runClaudeWithImage(imageInfo, promptFile);
|
|
87
|
+
|
|
88
|
+
return this.parseAnalysisResult(result);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Build analysis prompt
|
|
93
|
+
*/
|
|
94
|
+
buildAnalysisPrompt(imageInfo) {
|
|
95
|
+
return `# Screenshot Error Analysis
|
|
96
|
+
|
|
97
|
+
Analyze this screenshot and extract any error information.
|
|
98
|
+
|
|
99
|
+
## Instructions
|
|
100
|
+
1. Look for error messages, stack traces, console errors, or warnings
|
|
101
|
+
2. Identify the error type (TypeError, SyntaxError, Build Error, etc.)
|
|
102
|
+
3. Extract file names and line numbers if visible
|
|
103
|
+
4. Note any relevant context visible in the screenshot
|
|
104
|
+
5. Provide specific fix suggestions
|
|
105
|
+
|
|
106
|
+
## Response Format
|
|
107
|
+
Respond in this exact format:
|
|
108
|
+
|
|
109
|
+
**Error Type**: [type of error or "None detected"]
|
|
110
|
+
**Error Message**: [main error text]
|
|
111
|
+
**Location**: [file:line if visible]
|
|
112
|
+
**Root Cause**: [explanation of what's wrong]
|
|
113
|
+
**Suggested Fix**: [how to fix it]
|
|
114
|
+
**Confidence**: [High/Medium/Low]
|
|
115
|
+
|
|
116
|
+
If no error is visible, respond with:
|
|
117
|
+
**Error Type**: None detected
|
|
118
|
+
**Error Message**: No error visible in screenshot
|
|
119
|
+
`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Run Claude Code with image input
|
|
124
|
+
*/
|
|
125
|
+
async runClaudeWithImage(imageInfo, promptFile) {
|
|
126
|
+
return new Promise((resolve) => {
|
|
127
|
+
let output = '';
|
|
128
|
+
let errorOutput = '';
|
|
129
|
+
|
|
130
|
+
// Use claude with image path
|
|
131
|
+
// Claude Code can read images directly
|
|
132
|
+
const args = [
|
|
133
|
+
'--print',
|
|
134
|
+
'-p', `Analyze this image for errors: ${imageInfo.path}
|
|
135
|
+
|
|
136
|
+
Look for:
|
|
137
|
+
- Error messages
|
|
138
|
+
- Stack traces
|
|
139
|
+
- Console errors
|
|
140
|
+
- Type errors
|
|
141
|
+
- Build failures
|
|
142
|
+
|
|
143
|
+
Respond with:
|
|
144
|
+
**Error Type**:
|
|
145
|
+
**Error Message**:
|
|
146
|
+
**Location**:
|
|
147
|
+
**Root Cause**:
|
|
148
|
+
**Suggested Fix**:
|
|
149
|
+
**Confidence**:
|
|
150
|
+
|
|
151
|
+
If no error found, say "No error detected"`
|
|
152
|
+
];
|
|
153
|
+
|
|
154
|
+
const child = spawn('claude', args, {
|
|
155
|
+
cwd: this.projectPath,
|
|
156
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
child.stdout.on('data', (data) => {
|
|
160
|
+
output += data.toString();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
child.stderr.on('data', (data) => {
|
|
164
|
+
errorOutput += data.toString();
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
child.on('close', (code) => {
|
|
168
|
+
resolve({
|
|
169
|
+
success: code === 0,
|
|
170
|
+
output: output.trim(),
|
|
171
|
+
error: errorOutput.trim()
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
child.on('error', (error) => {
|
|
176
|
+
resolve({
|
|
177
|
+
success: false,
|
|
178
|
+
output: '',
|
|
179
|
+
error: error.message
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Timeout after 60 seconds
|
|
184
|
+
setTimeout(() => {
|
|
185
|
+
child.kill();
|
|
186
|
+
resolve({
|
|
187
|
+
success: false,
|
|
188
|
+
output: '',
|
|
189
|
+
error: 'Analysis timed out'
|
|
190
|
+
});
|
|
191
|
+
}, 60000);
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Parse AI analysis result
|
|
197
|
+
*/
|
|
198
|
+
parseAnalysisResult(result) {
|
|
199
|
+
const analysis = {
|
|
200
|
+
errorType: null,
|
|
201
|
+
errorMessage: null,
|
|
202
|
+
location: null,
|
|
203
|
+
rootCause: null,
|
|
204
|
+
suggestedFix: null,
|
|
205
|
+
confidence: 'Low',
|
|
206
|
+
raw: result?.output || '',
|
|
207
|
+
success: result?.success || false
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const output = result?.output || '';
|
|
211
|
+
|
|
212
|
+
// Extract fields using regex
|
|
213
|
+
const patterns = {
|
|
214
|
+
errorType: /\*\*Error Type\*\*:\s*(.+?)(?:\n|$)/i,
|
|
215
|
+
errorMessage: /\*\*Error Message\*\*:\s*(.+?)(?:\n|$)/i,
|
|
216
|
+
location: /\*\*Location\*\*:\s*(.+?)(?:\n|$)/i,
|
|
217
|
+
rootCause: /\*\*Root Cause\*\*:\s*(.+?)(?:\n|$)/i,
|
|
218
|
+
suggestedFix: /\*\*Suggested Fix\*\*:\s*(.+?)(?:\n|$)/i,
|
|
219
|
+
confidence: /\*\*Confidence\*\*:\s*(.+?)(?:\n|$)/i
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
for (const [key, pattern] of Object.entries(patterns)) {
|
|
223
|
+
const match = output.match(pattern);
|
|
224
|
+
if (match && match[1]) {
|
|
225
|
+
const value = match[1].trim();
|
|
226
|
+
if (value && value.toLowerCase() !== 'none' && value.toLowerCase() !== 'n/a') {
|
|
227
|
+
analysis[key] = value;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Check if error was actually detected
|
|
233
|
+
if (analysis.errorType?.toLowerCase().includes('none detected') ||
|
|
234
|
+
analysis.errorMessage?.toLowerCase().includes('no error')) {
|
|
235
|
+
analysis.errorType = null;
|
|
236
|
+
analysis.errorMessage = null;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return analysis;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Format analysis for display
|
|
244
|
+
*/
|
|
245
|
+
formatAnalysis(analysis) {
|
|
246
|
+
// No error case
|
|
247
|
+
if (!analysis.errorType && !analysis.errorMessage) {
|
|
248
|
+
return chalk.yellow('\n No error detected in screenshot.\n');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
let output = chalk.cyan(`
|
|
252
|
+
+----------------------------------------------------------------------+
|
|
253
|
+
| SCREENSHOT ANALYSIS |
|
|
254
|
+
+----------------------------------------------------------------------+
|
|
255
|
+
`);
|
|
256
|
+
|
|
257
|
+
if (analysis.errorType) {
|
|
258
|
+
output += `\n ${chalk.white('Error Type:')} ${chalk.red(analysis.errorType)}`;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (analysis.errorMessage) {
|
|
262
|
+
output += `\n ${chalk.white('Message:')} ${chalk.yellow(analysis.errorMessage)}`;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (analysis.location) {
|
|
266
|
+
output += `\n ${chalk.white('Location:')} ${chalk.gray(analysis.location)}`;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (analysis.confidence) {
|
|
270
|
+
const confColor = analysis.confidence === 'High' ? chalk.green :
|
|
271
|
+
analysis.confidence === 'Medium' ? chalk.yellow : chalk.gray;
|
|
272
|
+
output += `\n ${chalk.white('Confidence:')} ${confColor(analysis.confidence)}`;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (analysis.rootCause) {
|
|
276
|
+
output += `\n\n ${chalk.cyan('Root Cause:')}`;
|
|
277
|
+
output += `\n ${chalk.gray(analysis.rootCause)}`;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (analysis.suggestedFix) {
|
|
281
|
+
output += `\n\n ${chalk.green('Suggested Fix:')}`;
|
|
282
|
+
output += `\n ${chalk.white(analysis.suggestedFix)}`;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
output += '\n';
|
|
286
|
+
|
|
287
|
+
return output;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Helper function for direct use
|
|
293
|
+
*/
|
|
294
|
+
export async function analyzeScreenshot(imagePathOrClipboard, options = {}) {
|
|
295
|
+
const analyzer = new ImageAnalyzer(options.projectPath || process.cwd());
|
|
296
|
+
|
|
297
|
+
if (imagePathOrClipboard === 'clipboard') {
|
|
298
|
+
return await analyzer.analyzeClipboard();
|
|
299
|
+
} else {
|
|
300
|
+
return await analyzer.analyzeImage(imagePathOrClipboard);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export default ImageAnalyzer;
|
|
@@ -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';
|