@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.
Files changed (51) 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 +212 -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 +387 -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/test.js +194 -0
  28. package/src/commands/undo.js +281 -0
  29. package/src/commands/watch.js +556 -0
  30. package/src/commands/wizard.js +322 -0
  31. package/src/config/constants.js +5 -1
  32. package/src/config/templates.js +146 -15
  33. package/src/core/backup.js +325 -0
  34. package/src/core/error-analyzer.js +237 -0
  35. package/src/core/fix-generator.js +195 -0
  36. package/src/core/iteration.js +226 -0
  37. package/src/core/learning.js +295 -0
  38. package/src/core/session.js +18 -2
  39. package/src/core/test-runner.js +281 -0
  40. package/src/debug/analyzer.js +329 -0
  41. package/src/debug/evidence.js +228 -0
  42. package/src/debug/fixer.js +348 -0
  43. package/src/debug/image-analyzer.js +304 -0
  44. package/src/debug/index.js +378 -0
  45. package/src/debug/verifier.js +346 -0
  46. package/src/index.js +89 -0
  47. package/src/providers/claude-code.js +12 -7
  48. package/src/ui/__tests__/error-translator.test.js +390 -0
  49. package/src/ui/dashboard.js +364 -0
  50. package/src/ui/error-translator.js +775 -0
  51. 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
+ }