@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.
Files changed (53) hide show
  1. package/.vibecode/learning/fixes.json +1 -0
  2. package/.vibecode/learning/preferences.json +1 -0
  3. package/README.md +310 -49
  4. package/SESSION_NOTES.md +154 -0
  5. package/bin/vibecode.js +235 -2
  6. package/package.json +5 -2
  7. package/src/agent/decomposition.js +476 -0
  8. package/src/agent/index.js +391 -0
  9. package/src/agent/memory.js +542 -0
  10. package/src/agent/orchestrator.js +917 -0
  11. package/src/agent/self-healing.js +516 -0
  12. package/src/commands/agent.js +349 -0
  13. package/src/commands/ask.js +230 -0
  14. package/src/commands/assist.js +413 -0
  15. package/src/commands/build.js +345 -4
  16. package/src/commands/debug.js +565 -0
  17. package/src/commands/docs.js +167 -0
  18. package/src/commands/git.js +1024 -0
  19. package/src/commands/go.js +635 -0
  20. package/src/commands/learn.js +294 -0
  21. package/src/commands/migrate.js +341 -0
  22. package/src/commands/plan.js +8 -2
  23. package/src/commands/refactor.js +205 -0
  24. package/src/commands/review.js +126 -1
  25. package/src/commands/security.js +229 -0
  26. package/src/commands/shell.js +486 -0
  27. package/src/commands/templates.js +397 -0
  28. package/src/commands/test.js +194 -0
  29. package/src/commands/undo.js +281 -0
  30. package/src/commands/watch.js +556 -0
  31. package/src/commands/wizard.js +322 -0
  32. package/src/config/constants.js +5 -1
  33. package/src/config/templates.js +146 -15
  34. package/src/core/backup.js +325 -0
  35. package/src/core/error-analyzer.js +237 -0
  36. package/src/core/fix-generator.js +195 -0
  37. package/src/core/iteration.js +226 -0
  38. package/src/core/learning.js +295 -0
  39. package/src/core/session.js +18 -2
  40. package/src/core/test-runner.js +281 -0
  41. package/src/debug/analyzer.js +329 -0
  42. package/src/debug/evidence.js +228 -0
  43. package/src/debug/fixer.js +348 -0
  44. package/src/debug/image-analyzer.js +304 -0
  45. package/src/debug/index.js +378 -0
  46. package/src/debug/verifier.js +346 -0
  47. package/src/index.js +102 -0
  48. package/src/providers/claude-code.js +12 -7
  49. package/src/templates/index.js +724 -0
  50. package/src/ui/__tests__/error-translator.test.js +390 -0
  51. package/src/ui/dashboard.js +364 -0
  52. package/src/ui/error-translator.js +775 -0
  53. package/src/utils/image.js +222 -0
@@ -114,9 +114,25 @@ export async function createBlueprint(projectName, sessionId) {
114
114
  }
115
115
 
116
116
  /**
117
- * Create contract file
117
+ * Create contract file from intake and blueprint
118
118
  */
119
119
  export async function createContract(projectName, sessionId) {
120
- const template = getContractTemplate(projectName, sessionId);
120
+ // Read intake and blueprint to extract real content
121
+ let intakeContent = '';
122
+ let blueprintContent = '';
123
+
124
+ try {
125
+ intakeContent = await readSessionFile('intake.md');
126
+ } catch (e) {
127
+ // Intake not found, use empty
128
+ }
129
+
130
+ try {
131
+ blueprintContent = await readSessionFile('blueprint.md');
132
+ } catch (e) {
133
+ // Blueprint not found, use empty
134
+ }
135
+
136
+ const template = getContractTemplate(projectName, sessionId, intakeContent, blueprintContent);
121
137
  await writeSessionFile('contract.md', template);
122
138
  }
@@ -0,0 +1,281 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════════
2
+ // VIBECODE CLI - Test Runner
3
+ // Automated test execution for iterative builds
4
+ // ═══════════════════════════════════════════════════════════════════════════════
5
+
6
+ import { exec } from 'child_process';
7
+ import { promisify } from 'util';
8
+ import path from 'path';
9
+ import { pathExists, readJson } from '../utils/files.js';
10
+
11
+ const execAsync = promisify(exec);
12
+
13
+ /**
14
+ * Run all available tests for a project
15
+ * @param {string} projectPath - Path to the project
16
+ * @returns {Promise<TestResults>}
17
+ */
18
+ export async function runTests(projectPath) {
19
+ const results = {
20
+ passed: true,
21
+ tests: [],
22
+ errors: [],
23
+ summary: {
24
+ total: 0,
25
+ passed: 0,
26
+ failed: 0
27
+ },
28
+ duration: 0
29
+ };
30
+
31
+ const startTime = Date.now();
32
+
33
+ // 1. Check if package.json exists
34
+ const packageJsonPath = path.join(projectPath, 'package.json');
35
+ const hasPackageJson = await pathExists(packageJsonPath);
36
+
37
+ if (hasPackageJson) {
38
+ const pkg = await readJson(packageJsonPath);
39
+
40
+ // 2. Run npm test (if script exists)
41
+ if (pkg.scripts?.test && !pkg.scripts.test.includes('no test specified')) {
42
+ const npmTest = await runCommand('npm test', projectPath, 'npm test');
43
+ results.tests.push(npmTest);
44
+ }
45
+
46
+ // 3. Run npm run lint (if script exists) - soft fail (warnings only)
47
+ if (pkg.scripts?.lint) {
48
+ const npmLint = await runCommand('npm run lint', projectPath, 'npm lint');
49
+ npmLint.softFail = true; // Lint errors are warnings, don't block build
50
+ results.tests.push(npmLint);
51
+ }
52
+
53
+ // 4. Run npm run build (if script exists) - check for build errors
54
+ if (pkg.scripts?.build) {
55
+ const npmBuild = await runCommand('npm run build', projectPath, 'npm build');
56
+ results.tests.push(npmBuild);
57
+ }
58
+
59
+ // 5. Run TypeScript check if tsconfig exists
60
+ const tsconfigPath = path.join(projectPath, 'tsconfig.json');
61
+ if (await pathExists(tsconfigPath)) {
62
+ const tscCheck = await runCommand('npx tsc --noEmit', projectPath, 'typescript');
63
+ results.tests.push(tscCheck);
64
+ }
65
+ }
66
+
67
+ // 6. Check for syntax errors in JS files
68
+ const syntaxCheck = await checkJsSyntax(projectPath);
69
+ if (syntaxCheck.ran) {
70
+ results.tests.push(syntaxCheck);
71
+ }
72
+
73
+ // 7. Aggregate results
74
+ // Separate hard tests from soft-fail tests (like lint)
75
+ const hardTests = results.tests.filter(t => !t.softFail);
76
+ const softTests = results.tests.filter(t => t.softFail);
77
+
78
+ results.summary.total = results.tests.length;
79
+ results.summary.passed = results.tests.filter(t => t.passed).length;
80
+ results.summary.failed = results.tests.filter(t => !t.passed).length;
81
+ results.summary.warnings = softTests.filter(t => !t.passed).length;
82
+
83
+ // Only hard tests determine pass/fail
84
+ results.passed = hardTests.length === 0 || hardTests.every(t => t.passed);
85
+
86
+ // Collect errors, but mark soft-fail errors as warnings
87
+ results.errors = results.tests.filter(t => !t.passed && !t.softFail).flatMap(t => t.errors || []);
88
+ results.warnings = softTests.filter(t => !t.passed).flatMap(t => t.errors || []);
89
+ results.duration = Date.now() - startTime;
90
+
91
+ return results;
92
+ }
93
+
94
+ /**
95
+ * Run a single command and capture results
96
+ */
97
+ async function runCommand(command, cwd, name) {
98
+ const result = {
99
+ name,
100
+ command,
101
+ passed: false,
102
+ ran: true,
103
+ output: '',
104
+ errors: [],
105
+ duration: 0
106
+ };
107
+
108
+ const startTime = Date.now();
109
+
110
+ try {
111
+ const { stdout, stderr } = await execAsync(command, {
112
+ cwd,
113
+ timeout: 120000, // 2 minute timeout
114
+ maxBuffer: 10 * 1024 * 1024
115
+ });
116
+
117
+ result.passed = true;
118
+ result.output = stdout + stderr;
119
+ result.duration = Date.now() - startTime;
120
+
121
+ } catch (error) {
122
+ result.passed = false;
123
+ result.output = error.stdout || '';
124
+ result.error = error.stderr || error.message;
125
+ result.exitCode = error.code;
126
+ result.duration = Date.now() - startTime;
127
+
128
+ // Parse errors from output
129
+ result.errors = parseErrors(error.stderr || error.stdout || error.message, name);
130
+ }
131
+
132
+ return result;
133
+ }
134
+
135
+ /**
136
+ * Check JavaScript syntax errors
137
+ */
138
+ async function checkJsSyntax(projectPath) {
139
+ const result = {
140
+ name: 'syntax-check',
141
+ passed: true,
142
+ ran: false,
143
+ errors: []
144
+ };
145
+
146
+ try {
147
+ // Find JS/TS files (limited to src/ to avoid node_modules)
148
+ const { stdout } = await execAsync(
149
+ 'find src -name "*.js" -o -name "*.ts" -o -name "*.jsx" -o -name "*.tsx" 2>/dev/null | head -20',
150
+ { cwd: projectPath }
151
+ );
152
+
153
+ const files = stdout.trim().split('\n').filter(f => f);
154
+ if (files.length === 0) return result;
155
+
156
+ result.ran = true;
157
+
158
+ // Check each file for syntax errors using node --check
159
+ for (const file of files) {
160
+ if (file.endsWith('.ts') || file.endsWith('.tsx')) continue; // Skip TS files
161
+
162
+ try {
163
+ await execAsync(`node --check "${file}"`, { cwd: projectPath });
164
+ } catch (error) {
165
+ result.passed = false;
166
+ result.errors.push({
167
+ file,
168
+ message: error.message,
169
+ type: 'syntax'
170
+ });
171
+ }
172
+ }
173
+ } catch (error) {
174
+ // find command failed, skip syntax check
175
+ }
176
+
177
+ return result;
178
+ }
179
+
180
+ /**
181
+ * Parse error messages into structured format
182
+ */
183
+ function parseErrors(errorOutput, source) {
184
+ const errors = [];
185
+ const lines = errorOutput.split('\n');
186
+
187
+ for (const line of lines) {
188
+ // Match common error patterns
189
+ // Pattern: file.js:10:5: error message
190
+ const fileLineMatch = line.match(/([^\s:]+):(\d+):(\d+)?:?\s*(.+)/);
191
+ if (fileLineMatch) {
192
+ errors.push({
193
+ source,
194
+ file: fileLineMatch[1],
195
+ line: parseInt(fileLineMatch[2]),
196
+ column: fileLineMatch[3] ? parseInt(fileLineMatch[3]) : null,
197
+ message: fileLineMatch[4].trim(),
198
+ raw: line
199
+ });
200
+ continue;
201
+ }
202
+
203
+ // Pattern: Error: message
204
+ const errorMatch = line.match(/^(Error|TypeError|SyntaxError|ReferenceError):\s*(.+)/);
205
+ if (errorMatch) {
206
+ errors.push({
207
+ source,
208
+ type: errorMatch[1],
209
+ message: errorMatch[2].trim(),
210
+ raw: line
211
+ });
212
+ continue;
213
+ }
214
+
215
+ // Pattern: ✖ or ✗ or FAIL
216
+ if (line.includes('✖') || line.includes('✗') || line.includes('FAIL')) {
217
+ errors.push({
218
+ source,
219
+ message: line.trim(),
220
+ raw: line
221
+ });
222
+ }
223
+ }
224
+
225
+ // If no structured errors found, add the whole output
226
+ if (errors.length === 0 && errorOutput.trim()) {
227
+ errors.push({
228
+ source,
229
+ message: errorOutput.substring(0, 500),
230
+ raw: errorOutput
231
+ });
232
+ }
233
+
234
+ return errors;
235
+ }
236
+
237
+ /**
238
+ * Format test results for display
239
+ */
240
+ export function formatTestResults(results) {
241
+ const lines = [];
242
+
243
+ lines.push(`Tests: ${results.summary.passed}/${results.summary.total} passed`);
244
+ if (results.summary.warnings > 0) {
245
+ lines.push(`Warnings: ${results.summary.warnings} (lint)`);
246
+ }
247
+ lines.push(`Duration: ${(results.duration / 1000).toFixed(1)}s`);
248
+
249
+ // Show hard failures
250
+ const hardFailures = results.tests.filter(t => !t.passed && !t.softFail);
251
+ if (hardFailures.length > 0) {
252
+ lines.push('');
253
+ lines.push('Failed tests:');
254
+ for (const test of hardFailures) {
255
+ lines.push(` ❌ ${test.name}`);
256
+ for (const error of test.errors || []) {
257
+ const loc = error.file ? `${error.file}:${error.line || '?'}` : '';
258
+ lines.push(` ${loc} ${error.message?.substring(0, 80) || ''}`);
259
+ }
260
+ }
261
+ }
262
+
263
+ // Show soft failures (warnings)
264
+ const softFailures = results.tests.filter(t => !t.passed && t.softFail);
265
+ if (softFailures.length > 0) {
266
+ lines.push('');
267
+ lines.push('Warnings (non-blocking):');
268
+ for (const test of softFailures) {
269
+ lines.push(` ⚠️ ${test.name}`);
270
+ for (const error of (test.errors || []).slice(0, 3)) {
271
+ const loc = error.file ? `${error.file}:${error.line || '?'}` : '';
272
+ lines.push(` ${loc} ${error.message?.substring(0, 80) || ''}`);
273
+ }
274
+ if ((test.errors?.length || 0) > 3) {
275
+ lines.push(` ... and ${test.errors.length - 3} more`);
276
+ }
277
+ }
278
+ }
279
+
280
+ return lines.join('\n');
281
+ }
@@ -0,0 +1,329 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════════
2
+ // VIBECODE DEBUG - Root Cause Analyzer
3
+ // Analyzes evidence to determine root cause and generate hypotheses
4
+ // ═══════════════════════════════════════════════════════════════════════════════
5
+
6
+ /**
7
+ * Root Cause Analyzer Class
8
+ * Uses pattern matching and heuristics to identify bug sources
9
+ */
10
+ export class RootCauseAnalyzer {
11
+ constructor() {
12
+ this.patterns = this.initPatterns();
13
+ }
14
+
15
+ /**
16
+ * Analyze evidence to find root cause
17
+ */
18
+ async analyze(evidence) {
19
+ const analysis = {
20
+ category: evidence.category,
21
+ rootCause: null,
22
+ suggestedFix: null,
23
+ relatedFiles: evidence.files || [],
24
+ confidence: 0,
25
+ patterns: []
26
+ };
27
+
28
+ // Match against known patterns
29
+ for (const pattern of this.patterns) {
30
+ if (this.matchesPattern(evidence, pattern)) {
31
+ analysis.patterns.push(pattern.name);
32
+
33
+ if (pattern.confidence > analysis.confidence) {
34
+ analysis.rootCause = pattern.rootCause;
35
+ analysis.suggestedFix = pattern.fix;
36
+ analysis.confidence = pattern.confidence;
37
+ }
38
+ }
39
+ }
40
+
41
+ // If no pattern matched, use generic analysis
42
+ if (!analysis.rootCause) {
43
+ analysis.rootCause = this.inferRootCause(evidence);
44
+ analysis.confidence = 0.3;
45
+ }
46
+
47
+ return analysis;
48
+ }
49
+
50
+ /**
51
+ * Build hypotheses from analysis
52
+ */
53
+ buildHypotheses(analysis) {
54
+ const hypotheses = [];
55
+
56
+ // Primary hypothesis from analysis
57
+ if (analysis.suggestedFix) {
58
+ hypotheses.push({
59
+ description: analysis.suggestedFix,
60
+ confidence: analysis.confidence,
61
+ category: analysis.category,
62
+ rootCause: analysis.rootCause
63
+ });
64
+ }
65
+
66
+ // Add category-specific hypotheses
67
+ const categoryFixes = this.getCategoryFixes(analysis.category);
68
+ for (const fix of categoryFixes) {
69
+ if (!hypotheses.find(h => h.description === fix.description)) {
70
+ hypotheses.push({
71
+ ...fix,
72
+ category: analysis.category
73
+ });
74
+ }
75
+ }
76
+
77
+ // Sort by confidence
78
+ return hypotheses.sort((a, b) => b.confidence - a.confidence);
79
+ }
80
+
81
+ /**
82
+ * Initialize pattern database
83
+ */
84
+ initPatterns() {
85
+ return [
86
+ // Next.js specific
87
+ {
88
+ name: 'nextjs-server-client-boundary',
89
+ match: (e) => {
90
+ const msg = (e.message || e.description || '').toLowerCase();
91
+ return msg.includes('functions cannot be passed directly to client components') ||
92
+ msg.includes('client component') && msg.includes('server');
93
+ },
94
+ rootCause: 'Server/Client Component boundary violation in Next.js',
95
+ fix: 'Convert function props to serializable format (e.g., use formatType string instead of formatValue function). Or mark the function with "use server".',
96
+ confidence: 0.95
97
+ },
98
+ {
99
+ name: 'nextjs-hydration',
100
+ match: (e) => {
101
+ const msg = (e.message || e.description || '').toLowerCase();
102
+ return msg.includes('hydration') || msg.includes('text content does not match');
103
+ },
104
+ rootCause: 'Hydration mismatch between server and client render',
105
+ fix: 'Ensure server and client render identical content. Use useEffect for client-only code.',
106
+ confidence: 0.85
107
+ },
108
+
109
+ // Type errors
110
+ {
111
+ name: 'undefined-property-access',
112
+ match: (e) => {
113
+ const msg = (e.message || '').toLowerCase();
114
+ return msg.includes('cannot read properties of undefined') ||
115
+ msg.includes('cannot read property') && msg.includes('undefined');
116
+ },
117
+ rootCause: 'Accessing property on undefined object',
118
+ fix: 'Add null check or optional chaining (?.) before property access',
119
+ confidence: 0.85
120
+ },
121
+ {
122
+ name: 'null-property-access',
123
+ match: (e) => {
124
+ const msg = (e.message || '').toLowerCase();
125
+ return msg.includes('cannot read properties of null');
126
+ },
127
+ rootCause: 'Accessing property on null value',
128
+ fix: 'Add null check before property access. Check if data is loaded before accessing.',
129
+ confidence: 0.85
130
+ },
131
+
132
+ // Import errors
133
+ {
134
+ name: 'module-not-found',
135
+ match: (e) => {
136
+ const msg = (e.message || e.description || '').toLowerCase();
137
+ return msg.includes('cannot find module') || msg.includes('module not found');
138
+ },
139
+ rootCause: 'Missing import or incorrect module path',
140
+ fix: 'Install missing package with npm install, or fix import path',
141
+ confidence: 0.9
142
+ },
143
+ {
144
+ name: 'export-not-found',
145
+ match: (e) => {
146
+ const msg = (e.message || '').toLowerCase();
147
+ return msg.includes('does not provide an export named') ||
148
+ msg.includes('is not exported from');
149
+ },
150
+ rootCause: 'Importing non-existent export',
151
+ fix: 'Check export name in source module. Use correct named or default import.',
152
+ confidence: 0.9
153
+ },
154
+
155
+ // Syntax errors
156
+ {
157
+ name: 'unexpected-token',
158
+ match: (e) => {
159
+ const msg = (e.message || '').toLowerCase();
160
+ return msg.includes('unexpected token') || e.type === 'SyntaxError';
161
+ },
162
+ rootCause: 'Syntax error in JavaScript/TypeScript code',
163
+ fix: 'Fix syntax error at indicated line. Check for missing brackets, semicolons, or incorrect syntax.',
164
+ confidence: 0.9
165
+ },
166
+ {
167
+ name: 'jsx-syntax',
168
+ match: (e) => {
169
+ const msg = (e.message || '').toLowerCase();
170
+ return msg.includes('jsx') && (msg.includes('unexpected') || msg.includes('syntax'));
171
+ },
172
+ rootCause: 'JSX syntax error',
173
+ fix: 'Check JSX syntax. Ensure proper closing tags and valid JSX expressions.',
174
+ confidence: 0.85
175
+ },
176
+
177
+ // Reference errors
178
+ {
179
+ name: 'undefined-variable',
180
+ match: (e) => {
181
+ const msg = (e.message || '').toLowerCase();
182
+ return msg.includes('is not defined') || e.type === 'ReferenceError';
183
+ },
184
+ rootCause: 'Using undefined variable',
185
+ fix: 'Define the variable before use, or import it from the correct module',
186
+ confidence: 0.85
187
+ },
188
+
189
+ // Database errors
190
+ {
191
+ name: 'prisma-client',
192
+ match: (e) => {
193
+ const msg = (e.message || '').toLowerCase();
194
+ return msg.includes('prisma') || msg.includes('@prisma/client');
195
+ },
196
+ rootCause: 'Prisma database client error',
197
+ fix: 'Run npx prisma generate and npx prisma db push. Check DATABASE_URL.',
198
+ confidence: 0.8
199
+ },
200
+
201
+ // Auth errors
202
+ {
203
+ name: 'nextauth-error',
204
+ match: (e) => {
205
+ const msg = (e.message || '').toLowerCase();
206
+ return msg.includes('next-auth') || msg.includes('nextauth');
207
+ },
208
+ rootCause: 'NextAuth configuration error',
209
+ fix: 'Check NEXTAUTH_URL and NEXTAUTH_SECRET in .env. Verify auth provider config.',
210
+ confidence: 0.8
211
+ },
212
+
213
+ // ESLint/Lint errors
214
+ {
215
+ name: 'eslint-error',
216
+ match: (e) => e.category === 'LINT' || (e.message || '').toLowerCase().includes('eslint'),
217
+ rootCause: 'ESLint code style violation',
218
+ fix: 'Fix the linting error or add eslint-disable comment if intentional',
219
+ confidence: 0.75
220
+ },
221
+
222
+ // Test failures
223
+ {
224
+ name: 'test-assertion',
225
+ match: (e) => {
226
+ const msg = (e.message || '').toLowerCase();
227
+ return msg.includes('expect') || msg.includes('assertion') || e.category === 'TEST';
228
+ },
229
+ rootCause: 'Test assertion failure',
230
+ fix: 'Review test expectations and implementation. Update test or fix code.',
231
+ confidence: 0.7
232
+ }
233
+ ];
234
+ }
235
+
236
+ /**
237
+ * Check if evidence matches a pattern
238
+ */
239
+ matchesPattern(evidence, pattern) {
240
+ try {
241
+ return pattern.match(evidence);
242
+ } catch {
243
+ return false;
244
+ }
245
+ }
246
+
247
+ /**
248
+ * Infer root cause when no pattern matches
249
+ */
250
+ inferRootCause(evidence) {
251
+ const parts = [];
252
+
253
+ if (evidence.type && evidence.type !== 'unknown') {
254
+ parts.push(evidence.type);
255
+ }
256
+
257
+ if (evidence.files.length > 0) {
258
+ parts.push(`in ${evidence.files[0]}`);
259
+ }
260
+
261
+ if (evidence.lines.length > 0) {
262
+ parts.push(`at line ${evidence.lines[0]}`);
263
+ }
264
+
265
+ if (parts.length === 0) {
266
+ return 'Unknown error - manual investigation needed';
267
+ }
268
+
269
+ return parts.join(' ');
270
+ }
271
+
272
+ /**
273
+ * Get category-specific fixes
274
+ */
275
+ getCategoryFixes(category) {
276
+ const fixes = {
277
+ SYNTAX: [
278
+ { description: 'Check for missing brackets, parentheses, or semicolons', confidence: 0.6 },
279
+ { description: 'Verify JSX syntax and proper tag closing', confidence: 0.5 }
280
+ ],
281
+ TYPE: [
282
+ { description: 'Add null/undefined check before property access', confidence: 0.6 },
283
+ { description: 'Use optional chaining (?.) for nested access', confidence: 0.6 },
284
+ { description: 'Verify variable types match expected types', confidence: 0.5 }
285
+ ],
286
+ REFERENCE: [
287
+ { description: 'Import or define the missing variable', confidence: 0.6 },
288
+ { description: 'Check for typos in variable names', confidence: 0.5 }
289
+ ],
290
+ IMPORT: [
291
+ { description: 'Install missing package: npm install <package>', confidence: 0.7 },
292
+ { description: 'Fix import path to correct location', confidence: 0.6 },
293
+ { description: 'Check if export exists in source module', confidence: 0.5 }
294
+ ],
295
+ FILE: [
296
+ { description: 'Create missing file or directory', confidence: 0.6 },
297
+ { description: 'Fix file path in configuration', confidence: 0.5 }
298
+ ],
299
+ LINT: [
300
+ { description: 'Fix code style violation', confidence: 0.6 },
301
+ { description: 'Add eslint-disable comment if intentional', confidence: 0.4 }
302
+ ],
303
+ TEST: [
304
+ { description: 'Update test expectations to match implementation', confidence: 0.5 },
305
+ { description: 'Fix implementation to pass test', confidence: 0.5 }
306
+ ],
307
+ NEXTJS: [
308
+ { description: 'Check Server/Client Component boundaries', confidence: 0.7 },
309
+ { description: 'Move client-only code to useEffect', confidence: 0.6 }
310
+ ],
311
+ DATABASE: [
312
+ { description: 'Run prisma generate and db push', confidence: 0.7 },
313
+ { description: 'Check DATABASE_URL environment variable', confidence: 0.6 }
314
+ ],
315
+ RUNTIME: [
316
+ { description: 'Debug runtime error with console.log', confidence: 0.3 },
317
+ { description: 'Check error stack trace for source', confidence: 0.4 }
318
+ ]
319
+ };
320
+
321
+ return fixes[category] || [
322
+ { description: 'Investigate error message and stack trace', confidence: 0.2 }
323
+ ];
324
+ }
325
+ }
326
+
327
+ export function createRootCauseAnalyzer() {
328
+ return new RootCauseAnalyzer();
329
+ }