@nclamvn/vibecode-cli 1.2.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +310 -49
- package/bin/vibecode.js +65 -0
- package/package.json +1 -1
- package/src/agent/decomposition.js +476 -0
- package/src/agent/index.js +325 -0
- package/src/agent/memory.js +542 -0
- package/src/agent/orchestrator.js +644 -0
- package/src/agent/self-healing.js +516 -0
- package/src/commands/agent.js +255 -0
- package/src/commands/assist.js +413 -0
- package/src/commands/build.js +6 -5
- package/src/commands/debug.js +457 -0
- package/src/commands/go.js +380 -0
- package/src/commands/plan.js +8 -2
- package/src/config/templates.js +146 -15
- package/src/core/session.js +18 -2
- package/src/core/test-runner.js +38 -5
- package/src/debug/analyzer.js +329 -0
- package/src/debug/evidence.js +228 -0
- package/src/debug/fixer.js +348 -0
- package/src/debug/index.js +349 -0
- package/src/debug/verifier.js +346 -0
- package/src/index.js +31 -0
- package/src/providers/claude-code.js +12 -7
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// VIBECODE AGENT - Self-Healing Engine
|
|
3
|
+
// Automatic error analysis, fix generation, and recovery
|
|
4
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
5
|
+
|
|
6
|
+
import { analyzeErrors } from '../core/error-analyzer.js';
|
|
7
|
+
import { generateFixPrompt } from '../core/fix-generator.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Error categories for intelligent handling
|
|
11
|
+
*/
|
|
12
|
+
const ERROR_CATEGORIES = {
|
|
13
|
+
SYNTAX: {
|
|
14
|
+
patterns: [/SyntaxError/, /Unexpected token/, /Parse error/],
|
|
15
|
+
strategy: 'syntax_fix',
|
|
16
|
+
canAutoFix: true
|
|
17
|
+
},
|
|
18
|
+
IMPORT: {
|
|
19
|
+
patterns: [/Cannot find module/, /Module not found/, /import.*from/],
|
|
20
|
+
strategy: 'import_fix',
|
|
21
|
+
canAutoFix: true
|
|
22
|
+
},
|
|
23
|
+
TYPE: {
|
|
24
|
+
patterns: [/TypeError/, /is not a function/, /undefined is not/],
|
|
25
|
+
strategy: 'type_fix',
|
|
26
|
+
canAutoFix: true
|
|
27
|
+
},
|
|
28
|
+
REFERENCE: {
|
|
29
|
+
patterns: [/ReferenceError/, /is not defined/],
|
|
30
|
+
strategy: 'reference_fix',
|
|
31
|
+
canAutoFix: true
|
|
32
|
+
},
|
|
33
|
+
DEPENDENCY: {
|
|
34
|
+
patterns: [/peer dep/, /ERESOLVE/, /npm ERR!/, /dependency/],
|
|
35
|
+
strategy: 'dependency_fix',
|
|
36
|
+
canAutoFix: true
|
|
37
|
+
},
|
|
38
|
+
BUILD: {
|
|
39
|
+
patterns: [/Build failed/, /Compilation error/, /webpack/],
|
|
40
|
+
strategy: 'build_fix',
|
|
41
|
+
canAutoFix: true
|
|
42
|
+
},
|
|
43
|
+
TEST: {
|
|
44
|
+
patterns: [/test failed/i, /Tests failed/i, /expect\(/, /AssertionError/, /FAIL/i],
|
|
45
|
+
strategy: 'test_fix',
|
|
46
|
+
canAutoFix: true
|
|
47
|
+
},
|
|
48
|
+
RUNTIME: {
|
|
49
|
+
patterns: [/at runtime/, /ENOENT/, /EACCES/, /ECONNREFUSED/],
|
|
50
|
+
strategy: 'runtime_fix',
|
|
51
|
+
canAutoFix: false
|
|
52
|
+
},
|
|
53
|
+
UNKNOWN: {
|
|
54
|
+
patterns: [],
|
|
55
|
+
strategy: 'generic_fix',
|
|
56
|
+
canAutoFix: false
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Self-Healing Engine Class
|
|
62
|
+
* Analyzes errors and generates fixes automatically
|
|
63
|
+
*/
|
|
64
|
+
export class SelfHealingEngine {
|
|
65
|
+
constructor(memoryEngine = null) {
|
|
66
|
+
this.memory = memoryEngine;
|
|
67
|
+
this.healingHistory = [];
|
|
68
|
+
this.maxRetries = 3;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Set memory engine for learning
|
|
73
|
+
*/
|
|
74
|
+
setMemoryEngine(memoryEngine) {
|
|
75
|
+
this.memory = memoryEngine;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Analyze an error and determine fix strategy
|
|
80
|
+
*/
|
|
81
|
+
analyzeError(error) {
|
|
82
|
+
const errorStr = typeof error === 'string' ? error : error.message || String(error);
|
|
83
|
+
const errorLower = errorStr.toLowerCase();
|
|
84
|
+
|
|
85
|
+
// Determine category
|
|
86
|
+
let category = 'UNKNOWN';
|
|
87
|
+
for (const [cat, config] of Object.entries(ERROR_CATEGORIES)) {
|
|
88
|
+
if (config.patterns.some(p => p.test(errorStr))) {
|
|
89
|
+
category = cat;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const config = ERROR_CATEGORIES[category];
|
|
95
|
+
|
|
96
|
+
// Extract file and line info if present
|
|
97
|
+
const fileMatch = errorStr.match(/(?:at |in |file:?\s*)([^\s:]+):(\d+)/i);
|
|
98
|
+
const file = fileMatch ? fileMatch[1] : null;
|
|
99
|
+
const line = fileMatch ? parseInt(fileMatch[2]) : null;
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
original: errorStr,
|
|
103
|
+
category,
|
|
104
|
+
strategy: config.strategy,
|
|
105
|
+
canAutoFix: config.canAutoFix,
|
|
106
|
+
file,
|
|
107
|
+
line,
|
|
108
|
+
severity: this.calculateSeverity(category, errorStr),
|
|
109
|
+
suggestedActions: this.suggestActions(category, errorStr)
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Calculate error severity
|
|
115
|
+
*/
|
|
116
|
+
calculateSeverity(category, errorStr) {
|
|
117
|
+
// Critical: blocks all builds
|
|
118
|
+
if (['SYNTAX', 'IMPORT', 'DEPENDENCY'].includes(category)) {
|
|
119
|
+
return 'critical';
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// High: blocks module build
|
|
123
|
+
if (['TYPE', 'REFERENCE', 'BUILD'].includes(category)) {
|
|
124
|
+
return 'high';
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Medium: can continue but needs fixing
|
|
128
|
+
if (category === 'TEST') {
|
|
129
|
+
return 'medium';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Low: warning-level
|
|
133
|
+
return 'low';
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Suggest actions based on error type
|
|
138
|
+
*/
|
|
139
|
+
suggestActions(category, errorStr) {
|
|
140
|
+
const actions = [];
|
|
141
|
+
|
|
142
|
+
switch (category) {
|
|
143
|
+
case 'SYNTAX':
|
|
144
|
+
actions.push('Check for missing brackets, semicolons, or quotes');
|
|
145
|
+
actions.push('Verify JSX syntax if using React');
|
|
146
|
+
break;
|
|
147
|
+
|
|
148
|
+
case 'IMPORT':
|
|
149
|
+
actions.push('Install missing package with npm install');
|
|
150
|
+
actions.push('Check import path is correct');
|
|
151
|
+
actions.push('Verify file exists at specified path');
|
|
152
|
+
break;
|
|
153
|
+
|
|
154
|
+
case 'TYPE':
|
|
155
|
+
actions.push('Check if variable is properly initialized');
|
|
156
|
+
actions.push('Verify function exists before calling');
|
|
157
|
+
actions.push('Add null/undefined checks');
|
|
158
|
+
break;
|
|
159
|
+
|
|
160
|
+
case 'REFERENCE':
|
|
161
|
+
actions.push('Declare variable before using');
|
|
162
|
+
actions.push('Check for typos in variable names');
|
|
163
|
+
actions.push('Verify import statement');
|
|
164
|
+
break;
|
|
165
|
+
|
|
166
|
+
case 'DEPENDENCY':
|
|
167
|
+
actions.push('Run npm install to install dependencies');
|
|
168
|
+
actions.push('Check for version conflicts');
|
|
169
|
+
actions.push('Try npm install --legacy-peer-deps');
|
|
170
|
+
break;
|
|
171
|
+
|
|
172
|
+
case 'BUILD':
|
|
173
|
+
actions.push('Check build configuration');
|
|
174
|
+
actions.push('Verify all source files are valid');
|
|
175
|
+
actions.push('Check for circular dependencies');
|
|
176
|
+
break;
|
|
177
|
+
|
|
178
|
+
case 'TEST':
|
|
179
|
+
actions.push('Review test expectations');
|
|
180
|
+
actions.push('Check mock data and fixtures');
|
|
181
|
+
actions.push('Verify component behavior');
|
|
182
|
+
break;
|
|
183
|
+
|
|
184
|
+
default:
|
|
185
|
+
actions.push('Review error message for details');
|
|
186
|
+
actions.push('Check logs for more context');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return actions;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Generate a fix for the error
|
|
194
|
+
*/
|
|
195
|
+
async generateFix(error, context = {}) {
|
|
196
|
+
const analysis = this.analyzeError(error);
|
|
197
|
+
|
|
198
|
+
// Check memory for similar past fixes
|
|
199
|
+
let historicalFix = null;
|
|
200
|
+
if (this.memory) {
|
|
201
|
+
const similar = this.memory.findSimilarErrors(analysis.original);
|
|
202
|
+
if (similar.length > 0 && similar[0].fix) {
|
|
203
|
+
historicalFix = similar[0];
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Use existing fix-generator for detailed prompt
|
|
208
|
+
const fixPrompt = generateFixPrompt([{
|
|
209
|
+
type: (analysis?.category || 'unknown').toLowerCase(),
|
|
210
|
+
message: analysis?.original || String(error),
|
|
211
|
+
file: analysis?.file,
|
|
212
|
+
line: analysis?.line,
|
|
213
|
+
priority: analysis?.severity || 'medium'
|
|
214
|
+
}], context.files || []);
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
analysis,
|
|
218
|
+
historicalFix,
|
|
219
|
+
prompt: this.enhanceFixPrompt(fixPrompt, analysis, context),
|
|
220
|
+
canAutoFix: analysis.canAutoFix,
|
|
221
|
+
estimatedDifficulty: this.estimateDifficulty(analysis)
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Enhance fix prompt with additional context
|
|
227
|
+
*/
|
|
228
|
+
enhanceFixPrompt(basePrompt, analysis, context) {
|
|
229
|
+
let enhanced = basePrompt;
|
|
230
|
+
|
|
231
|
+
// Add category-specific instructions
|
|
232
|
+
switch (analysis?.category) {
|
|
233
|
+
case 'IMPORT':
|
|
234
|
+
enhanced += `\n\n## Import Fix Guidelines
|
|
235
|
+
- If package is missing, create a minimal implementation or use a different approach
|
|
236
|
+
- If path is wrong, check the actual file structure
|
|
237
|
+
- Consider if the import should be a devDependency`;
|
|
238
|
+
break;
|
|
239
|
+
|
|
240
|
+
case 'TYPE':
|
|
241
|
+
enhanced += `\n\n## Type Fix Guidelines
|
|
242
|
+
- Add proper type checking before operations
|
|
243
|
+
- Initialize variables with appropriate default values
|
|
244
|
+
- Consider using optional chaining (?.) for nested access`;
|
|
245
|
+
break;
|
|
246
|
+
|
|
247
|
+
case 'TEST':
|
|
248
|
+
enhanced += `\n\n## Test Fix Guidelines
|
|
249
|
+
- Match implementation behavior, not the other way around
|
|
250
|
+
- Update test expectations if implementation is correct
|
|
251
|
+
- Ensure mocks return appropriate data`;
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Add context from previous modules
|
|
256
|
+
if (context.completedModules && context.completedModules.length > 0) {
|
|
257
|
+
enhanced += `\n\n## Context from Previous Modules
|
|
258
|
+
The following modules are already built and should be referenced:
|
|
259
|
+
${context.completedModules.map(m => `- ${m}`).join('\n')}`;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Add historical fix info if available
|
|
263
|
+
if (context.historicalFix) {
|
|
264
|
+
enhanced += `\n\n## Similar Error Fixed Before
|
|
265
|
+
Previous fix: ${context.historicalFix}
|
|
266
|
+
Apply similar approach if applicable.`;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return enhanced;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Estimate fix difficulty
|
|
274
|
+
*/
|
|
275
|
+
estimateDifficulty(analysis) {
|
|
276
|
+
if (analysis.severity === 'critical') return 'complex';
|
|
277
|
+
if (analysis.severity === 'high') return 'moderate';
|
|
278
|
+
if (!analysis.canAutoFix) return 'complex';
|
|
279
|
+
return 'simple';
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Attempt to heal (fix and retry)
|
|
284
|
+
*/
|
|
285
|
+
async heal(error, moduleId, context = {}) {
|
|
286
|
+
const fix = await this.generateFix(error, context);
|
|
287
|
+
|
|
288
|
+
const healingRecord = {
|
|
289
|
+
id: `heal_${Date.now()}`,
|
|
290
|
+
moduleId,
|
|
291
|
+
error: fix.analysis,
|
|
292
|
+
fix,
|
|
293
|
+
attempt: (context.attempt || 0) + 1,
|
|
294
|
+
timestamp: new Date().toISOString(),
|
|
295
|
+
status: 'pending'
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
this.healingHistory.push(healingRecord);
|
|
299
|
+
|
|
300
|
+
// Record in memory
|
|
301
|
+
if (this.memory && fix?.analysis) {
|
|
302
|
+
await this.memory.recordError({
|
|
303
|
+
message: fix.analysis.original || String(error),
|
|
304
|
+
type: fix.analysis.category || 'UNKNOWN',
|
|
305
|
+
moduleId,
|
|
306
|
+
file: fix.analysis.file,
|
|
307
|
+
line: fix.analysis.line,
|
|
308
|
+
severity: fix.analysis.severity || 'medium'
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return {
|
|
313
|
+
...healingRecord,
|
|
314
|
+
shouldRetry: fix.canAutoFix && healingRecord.attempt <= this.maxRetries,
|
|
315
|
+
prompt: fix.prompt
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Report successful fix
|
|
321
|
+
*/
|
|
322
|
+
async reportSuccess(healingId) {
|
|
323
|
+
const record = this.healingHistory.find(h => h.id === healingId);
|
|
324
|
+
if (record) {
|
|
325
|
+
record.status = 'success';
|
|
326
|
+
record.completedAt = new Date().toISOString();
|
|
327
|
+
|
|
328
|
+
// Record in memory
|
|
329
|
+
if (this.memory && record.error) {
|
|
330
|
+
await this.memory.recordLearning({
|
|
331
|
+
type: 'fix_success',
|
|
332
|
+
errorType: record.error?.category || 'UNKNOWN',
|
|
333
|
+
module: record.moduleId,
|
|
334
|
+
outcome: 'success',
|
|
335
|
+
details: `Fixed ${record.error?.category || 'unknown'} error in ${record.error?.file || 'unknown file'}`
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return record;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Report failed fix
|
|
344
|
+
*/
|
|
345
|
+
async reportFailure(healingId, reason) {
|
|
346
|
+
const record = this.healingHistory.find(h => h.id === healingId);
|
|
347
|
+
if (record) {
|
|
348
|
+
record.status = 'failed';
|
|
349
|
+
record.failureReason = reason;
|
|
350
|
+
record.completedAt = new Date().toISOString();
|
|
351
|
+
|
|
352
|
+
// Record in memory
|
|
353
|
+
if (this.memory) {
|
|
354
|
+
await this.memory.recordLearning({
|
|
355
|
+
type: 'fix_failure',
|
|
356
|
+
errorType: record.error?.category,
|
|
357
|
+
module: record.moduleId,
|
|
358
|
+
outcome: 'failure',
|
|
359
|
+
reason
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return record;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Batch analyze multiple errors
|
|
368
|
+
*/
|
|
369
|
+
batchAnalyze(errors) {
|
|
370
|
+
const analyzed = errors.map(e => this.analyzeError(e));
|
|
371
|
+
|
|
372
|
+
// Sort by severity and group by category
|
|
373
|
+
const grouped = {};
|
|
374
|
+
for (const error of analyzed) {
|
|
375
|
+
const cat = error?.category || 'UNKNOWN';
|
|
376
|
+
if (!grouped[cat]) {
|
|
377
|
+
grouped[cat] = [];
|
|
378
|
+
}
|
|
379
|
+
grouped[cat].push(error);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Prioritize fixes
|
|
383
|
+
const priority = ['SYNTAX', 'IMPORT', 'DEPENDENCY', 'REFERENCE', 'TYPE', 'BUILD', 'TEST', 'RUNTIME', 'UNKNOWN'];
|
|
384
|
+
const ordered = [];
|
|
385
|
+
for (const cat of priority) {
|
|
386
|
+
if (grouped[cat]) {
|
|
387
|
+
ordered.push(...grouped[cat]);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return {
|
|
392
|
+
total: errors.length,
|
|
393
|
+
byCategory: grouped,
|
|
394
|
+
prioritized: ordered,
|
|
395
|
+
criticalCount: analyzed.filter(e => e.severity === 'critical').length,
|
|
396
|
+
canAutoFixCount: analyzed.filter(e => e.canAutoFix).length
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Generate combined fix prompt for multiple errors
|
|
402
|
+
*/
|
|
403
|
+
generateCombinedFixPrompt(errors, context = {}) {
|
|
404
|
+
const batchAnalysis = this.batchAnalyze(errors);
|
|
405
|
+
|
|
406
|
+
let prompt = `# Fix Multiple Errors\n\n`;
|
|
407
|
+
prompt += `Found ${batchAnalysis.total} errors (${batchAnalysis.criticalCount} critical)\n\n`;
|
|
408
|
+
|
|
409
|
+
// Group by file for efficient fixing
|
|
410
|
+
const byFile = {};
|
|
411
|
+
for (const error of batchAnalysis.prioritized) {
|
|
412
|
+
const file = error.file || 'unknown';
|
|
413
|
+
if (!byFile[file]) {
|
|
414
|
+
byFile[file] = [];
|
|
415
|
+
}
|
|
416
|
+
byFile[file].push(error);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
for (const [file, fileErrors] of Object.entries(byFile)) {
|
|
420
|
+
prompt += `## File: ${file}\n\n`;
|
|
421
|
+
for (const error of fileErrors) {
|
|
422
|
+
const cat = error?.category || 'UNKNOWN';
|
|
423
|
+
prompt += `### ${cat} Error${error?.line ? ` (line ${error.line})` : ''}\n`;
|
|
424
|
+
prompt += `${error?.original || 'Unknown error'}\n\n`;
|
|
425
|
+
prompt += `Actions:\n${(error?.suggestedActions || ['Review error']).map(a => `- ${a}`).join('\n')}\n\n`;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
prompt += `## Fix Instructions\n`;
|
|
430
|
+
prompt += `1. Start with SYNTAX and IMPORT errors (they often cause other errors)\n`;
|
|
431
|
+
prompt += `2. Fix one file at a time, top to bottom\n`;
|
|
432
|
+
prompt += `3. Verify each fix before moving to the next\n`;
|
|
433
|
+
prompt += `4. Run tests after all fixes\n`;
|
|
434
|
+
|
|
435
|
+
return prompt;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Get healing statistics
|
|
440
|
+
*/
|
|
441
|
+
getStats() {
|
|
442
|
+
const total = this.healingHistory.length;
|
|
443
|
+
const successful = this.healingHistory.filter(h => h.status === 'success').length;
|
|
444
|
+
const failed = this.healingHistory.filter(h => h.status === 'failed').length;
|
|
445
|
+
|
|
446
|
+
const byCategory = {};
|
|
447
|
+
for (const record of this.healingHistory) {
|
|
448
|
+
const cat = record.error?.category || 'UNKNOWN';
|
|
449
|
+
if (!byCategory[cat]) {
|
|
450
|
+
byCategory[cat] = { total: 0, success: 0, failed: 0 };
|
|
451
|
+
}
|
|
452
|
+
byCategory[cat].total++;
|
|
453
|
+
if (record.status === 'success') byCategory[cat].success++;
|
|
454
|
+
if (record.status === 'failed') byCategory[cat].failed++;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return {
|
|
458
|
+
total,
|
|
459
|
+
successful,
|
|
460
|
+
failed,
|
|
461
|
+
pending: total - successful - failed,
|
|
462
|
+
successRate: total > 0 ? (successful / total * 100).toFixed(1) + '%' : 'N/A',
|
|
463
|
+
byCategory
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Check if module should be skipped due to repeated failures
|
|
469
|
+
*/
|
|
470
|
+
shouldSkipModule(moduleId) {
|
|
471
|
+
const moduleRecords = this.healingHistory.filter(h => h.moduleId === moduleId);
|
|
472
|
+
const failures = moduleRecords.filter(h => h.status === 'failed').length;
|
|
473
|
+
return failures >= this.maxRetries;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Get recovery suggestions for a stuck module
|
|
478
|
+
*/
|
|
479
|
+
getRecoverySuggestions(moduleId) {
|
|
480
|
+
const moduleRecords = this.healingHistory.filter(h => h.moduleId === moduleId);
|
|
481
|
+
|
|
482
|
+
if (moduleRecords.length === 0) {
|
|
483
|
+
return ['No healing attempts recorded for this module'];
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const suggestions = [];
|
|
487
|
+
const categories = [...new Set(moduleRecords.map(r => r.error?.category))];
|
|
488
|
+
|
|
489
|
+
if (categories.includes('DEPENDENCY')) {
|
|
490
|
+
suggestions.push('Try running: npm install --legacy-peer-deps');
|
|
491
|
+
suggestions.push('Check package.json for version conflicts');
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
if (categories.includes('IMPORT')) {
|
|
495
|
+
suggestions.push('Verify all import paths match actual file structure');
|
|
496
|
+
suggestions.push('Consider using relative imports instead of aliases');
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
if (moduleRecords.length >= this.maxRetries) {
|
|
500
|
+
suggestions.push('Consider simplifying the module requirements');
|
|
501
|
+
suggestions.push('Split the module into smaller, more focused components');
|
|
502
|
+
suggestions.push('Manual intervention may be required');
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
return suggestions;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Create self-healing engine instance
|
|
511
|
+
*/
|
|
512
|
+
export function createSelfHealingEngine(memoryEngine = null) {
|
|
513
|
+
return new SelfHealingEngine(memoryEngine);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
export { ERROR_CATEGORIES };
|