@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.
- 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 +212 -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 +387 -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/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 +89 -0
- package/src/providers/claude-code.js +12 -7
- 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,228 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// VIBECODE DEBUG - Evidence Collector
|
|
3
|
+
// Gathers error information from multiple sources
|
|
4
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
5
|
+
|
|
6
|
+
import { exec } from 'child_process';
|
|
7
|
+
import { promisify } from 'util';
|
|
8
|
+
import fs from 'fs-extra';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
|
|
11
|
+
const execAsync = promisify(exec);
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Evidence Collector Class
|
|
15
|
+
* Gathers and parses error information from various sources
|
|
16
|
+
*/
|
|
17
|
+
export class EvidenceCollector {
|
|
18
|
+
constructor(projectPath) {
|
|
19
|
+
this.projectPath = projectPath;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Collect evidence from all available sources
|
|
24
|
+
*/
|
|
25
|
+
async collect(input) {
|
|
26
|
+
const evidence = {
|
|
27
|
+
type: 'unknown',
|
|
28
|
+
category: 'RUNTIME',
|
|
29
|
+
description: input.description || '',
|
|
30
|
+
message: '',
|
|
31
|
+
stackTrace: [],
|
|
32
|
+
files: [],
|
|
33
|
+
lines: [],
|
|
34
|
+
logs: [],
|
|
35
|
+
hasImage: false,
|
|
36
|
+
imagePath: null,
|
|
37
|
+
timestamp: new Date().toISOString()
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// From description
|
|
41
|
+
if (input.description) {
|
|
42
|
+
this.parseDescription(evidence, input.description);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// From log paste
|
|
46
|
+
if (input.log) {
|
|
47
|
+
this.parseLog(evidence, input.log);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// From image (note for future OCR)
|
|
51
|
+
if (input.image) {
|
|
52
|
+
await this.parseImage(evidence, input.image);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Auto-scan mode
|
|
56
|
+
if (input.auto) {
|
|
57
|
+
await this.autoScan(evidence);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Categorize error
|
|
61
|
+
evidence.category = this.categorizeError(evidence);
|
|
62
|
+
|
|
63
|
+
return evidence;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Parse error description text
|
|
68
|
+
*/
|
|
69
|
+
parseDescription(evidence, description) {
|
|
70
|
+
const desc = description.toLowerCase();
|
|
71
|
+
|
|
72
|
+
// Detect error type from description
|
|
73
|
+
if (desc.includes('typeerror')) evidence.type = 'TypeError';
|
|
74
|
+
else if (desc.includes('syntaxerror')) evidence.type = 'SyntaxError';
|
|
75
|
+
else if (desc.includes('referenceerror')) evidence.type = 'ReferenceError';
|
|
76
|
+
else if (desc.includes('cannot find module')) evidence.type = 'ImportError';
|
|
77
|
+
else if (desc.includes('undefined')) evidence.type = 'UndefinedError';
|
|
78
|
+
else if (desc.includes('cannot read properties')) evidence.type = 'TypeError';
|
|
79
|
+
|
|
80
|
+
// Extract file references
|
|
81
|
+
const fileMatches = description.match(/[\w\/\-\.]+\.(js|ts|tsx|jsx|json|mjs|cjs)/gi);
|
|
82
|
+
if (fileMatches) {
|
|
83
|
+
evidence.files = [...new Set(fileMatches)];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Extract line numbers
|
|
87
|
+
const lineMatches = description.match(/:(\d+)(?::\d+)?/g);
|
|
88
|
+
if (lineMatches) {
|
|
89
|
+
evidence.lines = lineMatches.map(m => parseInt(m.split(':')[1]));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Extract error message
|
|
93
|
+
const errorMatch = description.match(/(Error|TypeError|SyntaxError|ReferenceError):\s*(.+?)(?:\n|$)/i);
|
|
94
|
+
if (errorMatch) {
|
|
95
|
+
evidence.type = errorMatch[1];
|
|
96
|
+
evidence.message = errorMatch[2].trim();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Parse error log text
|
|
102
|
+
*/
|
|
103
|
+
parseLog(evidence, log) {
|
|
104
|
+
evidence.logs.push(log);
|
|
105
|
+
|
|
106
|
+
// Parse stack trace
|
|
107
|
+
const stackLines = log.split('\n').filter(line =>
|
|
108
|
+
line.trim().startsWith('at ') || line.includes('Error:')
|
|
109
|
+
);
|
|
110
|
+
evidence.stackTrace = stackLines.slice(0, 15);
|
|
111
|
+
|
|
112
|
+
// Extract error message
|
|
113
|
+
const errorMatch = log.match(/(Error|TypeError|SyntaxError|ReferenceError|RangeError):\s*(.+?)(?:\n|$)/i);
|
|
114
|
+
if (errorMatch) {
|
|
115
|
+
evidence.type = errorMatch[1];
|
|
116
|
+
evidence.message = errorMatch[2].trim();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Extract files from stack trace
|
|
120
|
+
const fileMatches = log.match(/(?:at\s+)?(?:\w+\s+)?\(?([^\s()]+\.(js|ts|tsx|jsx)):(\d+)(?::\d+)?\)?/gi);
|
|
121
|
+
if (fileMatches) {
|
|
122
|
+
for (const match of fileMatches) {
|
|
123
|
+
const fileMatch = match.match(/([^\s()]+\.(js|ts|tsx|jsx)):(\d+)/i);
|
|
124
|
+
if (fileMatch) {
|
|
125
|
+
evidence.files.push(fileMatch[1]);
|
|
126
|
+
evidence.lines.push(parseInt(fileMatch[3]));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
evidence.files = [...new Set(evidence.files)];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Parse image evidence (placeholder for OCR/Vision)
|
|
135
|
+
*/
|
|
136
|
+
async parseImage(evidence, imagePath) {
|
|
137
|
+
if (await fs.pathExists(imagePath)) {
|
|
138
|
+
evidence.hasImage = true;
|
|
139
|
+
evidence.imagePath = imagePath;
|
|
140
|
+
// Future: OCR or Claude Vision API
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Auto-scan project for errors
|
|
146
|
+
*/
|
|
147
|
+
async autoScan(evidence) {
|
|
148
|
+
const checks = [
|
|
149
|
+
{ name: 'npm test', cmd: 'npm test', softFail: true },
|
|
150
|
+
{ name: 'npm build', cmd: 'npm run build', softFail: false },
|
|
151
|
+
{ name: 'npm lint', cmd: 'npm run lint', softFail: true },
|
|
152
|
+
{ name: 'tsc', cmd: 'npx tsc --noEmit', softFail: true }
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
// Check if package.json exists
|
|
156
|
+
const pkgPath = path.join(this.projectPath, 'package.json');
|
|
157
|
+
if (!await fs.pathExists(pkgPath)) {
|
|
158
|
+
evidence.logs.push({ source: 'auto-scan', error: 'No package.json found' });
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const pkg = await fs.readJson(pkgPath);
|
|
163
|
+
|
|
164
|
+
for (const check of checks) {
|
|
165
|
+
// Skip if script doesn't exist
|
|
166
|
+
const scriptName = check.cmd.replace('npm run ', '').replace('npm ', '');
|
|
167
|
+
if (check.cmd.startsWith('npm') && scriptName !== 'test' && !pkg.scripts?.[scriptName]) {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
await execAsync(check.cmd, {
|
|
173
|
+
cwd: this.projectPath,
|
|
174
|
+
timeout: 60000,
|
|
175
|
+
maxBuffer: 10 * 1024 * 1024
|
|
176
|
+
});
|
|
177
|
+
} catch (error) {
|
|
178
|
+
const errorOutput = error.stderr || error.stdout || error.message;
|
|
179
|
+
evidence.logs.push({
|
|
180
|
+
source: check.name,
|
|
181
|
+
error: errorOutput.substring(0, 5000), // Limit size
|
|
182
|
+
exitCode: error.code
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Parse errors from output
|
|
186
|
+
this.parseLog(evidence, errorOutput);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Categorize error by type
|
|
193
|
+
*/
|
|
194
|
+
categorizeError(evidence) {
|
|
195
|
+
const type = (evidence.type || '').toLowerCase();
|
|
196
|
+
const msg = (evidence.message || evidence.description || '').toLowerCase();
|
|
197
|
+
|
|
198
|
+
if (type.includes('syntax') || msg.includes('unexpected token')) return 'SYNTAX';
|
|
199
|
+
if (type.includes('type') || msg.includes('cannot read properties')) return 'TYPE';
|
|
200
|
+
if (type.includes('reference') || msg.includes('is not defined')) return 'REFERENCE';
|
|
201
|
+
if (msg.includes('cannot find module') || msg.includes('module not found')) return 'IMPORT';
|
|
202
|
+
if (msg.includes('enoent') || msg.includes('no such file')) return 'FILE';
|
|
203
|
+
if (msg.includes('eslint') || msg.includes('lint')) return 'LINT';
|
|
204
|
+
if (msg.includes('test') || msg.includes('expect') || msg.includes('assert')) return 'TEST';
|
|
205
|
+
if (msg.includes('client component') || msg.includes('server component')) return 'NEXTJS';
|
|
206
|
+
if (msg.includes('prisma') || msg.includes('database')) return 'DATABASE';
|
|
207
|
+
|
|
208
|
+
return 'RUNTIME';
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Get evidence summary
|
|
213
|
+
*/
|
|
214
|
+
getSummary(evidence) {
|
|
215
|
+
return {
|
|
216
|
+
type: evidence.type,
|
|
217
|
+
category: evidence.category,
|
|
218
|
+
message: evidence.message || evidence.description?.substring(0, 100),
|
|
219
|
+
fileCount: evidence.files.length,
|
|
220
|
+
hasStackTrace: evidence.stackTrace.length > 0,
|
|
221
|
+
logSources: evidence.logs.length
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export function createEvidenceCollector(projectPath) {
|
|
227
|
+
return new EvidenceCollector(projectPath);
|
|
228
|
+
}
|
|
@@ -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
|
+
}
|