zai-mcp-server
Version:
🚀 REVOLUTIONARY AI-to-AI Collaboration Platform v6.1! NEW: Advanced Debugging Tools with Screenshot Analysis, Console Error Parsing, Automated Fix Generation, 5 Specialized Debugging Agents, Visual UI Analysis, JavaScript Error Intelligence, CSS/HTML Fix
612 lines (528 loc) • 19.2 kB
JavaScript
/**
* Automatic Fix Generator - Generates code fixes for identified issues
* Creates CSS, JavaScript, and HTML fixes with validation and safety checks
*/
export class AutomaticFixGenerator {
constructor(multiProviderAI) {
this.multiProviderAI = multiProviderAI;
this.fixTemplates = new Map();
this.safetyRules = new Map();
this.validationRules = new Map();
this.initializeFixTemplates();
this.initializeSafetyRules();
this.initializeValidationRules();
console.log('🔧 Automatic Fix Generator initialized with intelligent code generation');
}
/**
* Initialize fix templates for common issues
*/
initializeFixTemplates() {
// JavaScript fix templates
this.fixTemplates.set('undefined_property', {
type: 'javascript',
template: (context) => `
// Add null check before accessing property
if (${context.object} && ${context.object}.${context.property}) {
// Original code here
${context.originalCode}
}`,
description: 'Add null/undefined check before property access',
safetyLevel: 'high'
});
this.fixTemplates.set('function_not_defined', {
type: 'javascript',
template: (context) => `
// Check if function exists before calling
if (typeof ${context.functionName} === 'function') {
${context.originalCode}
} else {
console.warn('Function ${context.functionName} is not defined');
}`,
description: 'Add function existence check',
safetyLevel: 'high'
});
this.fixTemplates.set('missing_import', {
type: 'javascript',
template: (context) => `
// Add missing import statement
import { ${context.importName} } from '${context.modulePath}';
${context.originalCode}`,
description: 'Add missing import statement',
safetyLevel: 'medium'
});
// CSS fix templates
this.fixTemplates.set('layout_overflow', {
type: 'css',
template: (context) => `
/* Fix layout overflow */
.${context.className} {
overflow: hidden;
max-width: 100%;
box-sizing: border-box;
}`,
description: 'Fix layout overflow issues',
safetyLevel: 'high'
});
this.fixTemplates.set('accessibility_contrast', {
type: 'css',
template: (context) => `
/* Improve color contrast for accessibility */
.${context.className} {
color: ${context.newColor};
background-color: ${context.newBackground};
}`,
description: 'Improve color contrast for accessibility',
safetyLevel: 'high'
});
this.fixTemplates.set('responsive_layout', {
type: 'css',
template: (context) => `
/* Add responsive layout */
.${context.className} {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
@media (max-width: 768px) {
.${context.className} {
flex-direction: column;
}
}`,
description: 'Add responsive layout support',
safetyLevel: 'medium'
});
// React fix templates
this.fixTemplates.set('react_key_prop', {
type: 'javascript',
template: (context) => `
// Add unique key prop to list items
{${context.arrayName}.map((${context.itemName}, index) => (
<${context.componentName}
key={\`\${${context.itemName}.id || index}\`}
${context.props}
/>
))}`,
description: 'Add unique key prop to React list items',
safetyLevel: 'high'
});
this.fixTemplates.set('react_useeffect_dependency', {
type: 'javascript',
template: (context) => `
// Fix useEffect dependency array
useEffect(() => {
${context.effectCode}
}, [${context.dependencies.join(', ')}]);`,
description: 'Fix React useEffect dependency array',
safetyLevel: 'medium'
});
}
/**
* Initialize safety rules
*/
initializeSafetyRules() {
this.safetyRules.set('no_data_loss', {
check: (fix) => !fix.code.includes('delete ') && !fix.code.includes('remove('),
message: 'Fix should not delete or remove data'
});
this.safetyRules.set('no_external_calls', {
check: (fix) => !fix.code.includes('fetch(') && !fix.code.includes('XMLHttpRequest'),
message: 'Fix should not make external network calls'
});
this.safetyRules.set('no_eval', {
check: (fix) => !fix.code.includes('eval(') && !fix.code.includes('Function('),
message: 'Fix should not use eval or dynamic code execution'
});
this.safetyRules.set('preserve_functionality', {
check: (fix) => fix.code.includes(fix.context.originalCode) || fix.type === 'css',
message: 'Fix should preserve original functionality'
});
}
/**
* Initialize validation rules
*/
initializeValidationRules() {
this.validationRules.set('javascript', {
syntax: (code) => this.validateJavaScriptSyntax(code),
logic: (code) => this.validateJavaScriptLogic(code)
});
this.validationRules.set('css', {
syntax: (code) => this.validateCSSSyntax(code),
properties: (code) => this.validateCSSProperties(code)
});
this.validationRules.set('html', {
syntax: (code) => this.validateHTMLSyntax(code),
accessibility: (code) => this.validateHTMLAccessibility(code)
});
}
/**
* Generate fix for an issue
*/
async generateFix(issue, options = {}) {
console.log(`🔧 Generating fix for: ${issue.type}`);
try {
// Determine fix approach
const fixApproach = this.determineFixApproach(issue);
// Generate fix based on approach
let fix;
if (fixApproach.useTemplate) {
fix = await this.generateTemplatedFix(issue, fixApproach, options);
} else {
fix = await this.generateAIFix(issue, options);
}
if (!fix) {
console.log(`⚠️ No fix generated for issue: ${issue.type}`);
return null;
}
// Validate fix
const validation = await this.validateFix(fix, options);
if (!validation.valid) {
console.log(`❌ Fix validation failed: ${validation.errors.join(', ')}`);
return null;
}
// Apply safety checks
const safetyCheck = this.applySafetyChecks(fix, options.safetyLevel || 'high');
if (!safetyCheck.safe) {
console.log(`🚨 Fix failed safety check: ${safetyCheck.issues.join(', ')}`);
return null;
}
// Calculate confidence and impact
fix.confidence = this.calculateFixConfidence(fix, issue);
fix.estimatedImpact = this.estimateFixImpact(fix, issue);
console.log(`✅ Fix generated successfully with ${fix.confidence}% confidence`);
return fix;
} catch (error) {
console.error(`❌ Fix generation failed for ${issue.type}:`, error.message);
return null;
}
}
/**
* Determine fix approach for issue
*/
determineFixApproach(issue) {
// Check if we have a template for this issue type
const templateKey = this.findMatchingTemplate(issue);
if (templateKey) {
return {
useTemplate: true,
templateKey,
confidence: 0.9
};
}
// Use AI-generated fix
return {
useTemplate: false,
requiresAI: true,
confidence: 0.7
};
}
/**
* Find matching template for issue
*/
findMatchingTemplate(issue) {
const issueType = issue.type.toLowerCase();
const description = issue.description.toLowerCase();
// Direct template matches
if (this.fixTemplates.has(issueType)) {
return issueType;
}
// Pattern matching
if (description.includes('undefined') && description.includes('property')) {
return 'undefined_property';
}
if (description.includes('not defined') && description.includes('function')) {
return 'function_not_defined';
}
if (description.includes('key') && description.includes('prop')) {
return 'react_key_prop';
}
if (description.includes('overflow')) {
return 'layout_overflow';
}
if (description.includes('contrast') || description.includes('accessibility')) {
return 'accessibility_contrast';
}
return null;
}
/**
* Generate fix using template
*/
async generateTemplatedFix(issue, fixApproach, options) {
const template = this.fixTemplates.get(fixApproach.templateKey);
if (!template) {
throw new Error(`Template not found: ${fixApproach.templateKey}`);
}
// Extract context from issue
const context = this.extractFixContext(issue, template.type);
// Generate code from template
const code = template.template(context);
return {
id: `fix_${issue.id}_${Date.now()}`,
issueId: issue.id,
issueType: issue.type,
type: template.type,
approach: 'template',
code: code.trim(),
description: template.description,
safetyLevel: template.safetyLevel,
context,
generated: Date.now()
};
}
/**
* Generate fix using AI
*/
async generateAIFix(issue, options) {
const prompt = this.buildFixPrompt(issue, options);
const response = await this.multiProviderAI.generateResponse(prompt, {
maxTokens: 500,
temperature: 0.1
});
const parsedFix = this.parseAIFixResponse(response, issue);
return {
id: `fix_${issue.id}_${Date.now()}`,
issueId: issue.id,
issueType: issue.type,
type: parsedFix.type,
approach: 'ai_generated',
code: parsedFix.code,
description: parsedFix.description,
safetyLevel: 'medium',
context: parsedFix.context,
generated: Date.now()
};
}
/**
* Build prompt for AI fix generation
*/
buildFixPrompt(issue, options) {
let prompt = `Generate a code fix for this issue:\n\n`;
prompt += `Issue Type: ${issue.type}\n`;
prompt += `Description: ${issue.description}\n`;
prompt += `Severity: ${issue.severity}\n`;
if (issue.location) {
prompt += `Location: ${issue.location.file}:${issue.location.line}\n`;
}
if (issue.rootCause) {
prompt += `Root Cause: ${issue.rootCause}\n`;
}
prompt += `\nFramework: ${options.framework || 'vanilla JavaScript'}\n`;
prompt += `Safety Level: ${options.safetyLevel || 'high'}\n\n`;
prompt += `Requirements:\n`;
prompt += `1. Provide working code that fixes the issue\n`;
prompt += `2. Include comments explaining the fix\n`;
prompt += `3. Ensure the fix is safe and doesn't break existing functionality\n`;
prompt += `4. Use modern best practices\n\n`;
prompt += `Format the response as:\n`;
prompt += `TYPE: [javascript|css|html]\n`;
prompt += `DESCRIPTION: [brief description]\n`;
prompt += `CODE:\n[actual fix code]\n`;
return prompt;
}
/**
* Parse AI fix response
*/
parseAIFixResponse(response, issue) {
const lines = response.split('\n');
let type = 'javascript';
let description = 'AI-generated fix';
let code = '';
let inCodeSection = false;
for (const line of lines) {
if (line.startsWith('TYPE:')) {
type = line.replace('TYPE:', '').trim().toLowerCase();
} else if (line.startsWith('DESCRIPTION:')) {
description = line.replace('DESCRIPTION:', '').trim();
} else if (line.startsWith('CODE:')) {
inCodeSection = true;
} else if (inCodeSection) {
code += line + '\n';
}
}
return {
type,
description,
code: code.trim(),
context: { originalIssue: issue }
};
}
/**
* Extract context for fix generation
*/
extractFixContext(issue, fixType) {
const context = {
originalCode: issue.metadata?.originalCode || '',
className: this.extractClassName(issue),
functionName: this.extractFunctionName(issue),
variableName: this.extractVariableName(issue)
};
// Type-specific context extraction
if (fixType === 'javascript') {
context.object = this.extractObjectName(issue);
context.property = this.extractPropertyName(issue);
context.importName = this.extractImportName(issue);
context.modulePath = this.guessModulePath(issue);
} else if (fixType === 'css') {
context.newColor = this.suggestAccessibleColor(issue);
context.newBackground = this.suggestAccessibleBackground(issue);
}
return context;
}
/**
* Validate generated fix
*/
async validateFix(fix, options) {
const errors = [];
const warnings = [];
// Get validation rules for fix type
const rules = this.validationRules.get(fix.type);
if (rules) {
for (const [ruleName, ruleFunction] of Object.entries(rules)) {
try {
const result = ruleFunction(fix.code);
if (!result.valid) {
errors.push(`${ruleName}: ${result.message}`);
}
if (result.warnings) {
warnings.push(...result.warnings);
}
} catch (error) {
warnings.push(`Validation rule ${ruleName} failed: ${error.message}`);
}
}
}
return {
valid: errors.length === 0,
errors,
warnings
};
}
/**
* Apply safety checks to fix
*/
applySafetyChecks(fix, safetyLevel) {
const issues = [];
const applicableRules = this.getSafetyRulesForLevel(safetyLevel);
for (const [ruleName, rule] of applicableRules) {
try {
if (!rule.check(fix)) {
issues.push(rule.message);
}
} catch (error) {
issues.push(`Safety check ${ruleName} failed: ${error.message}`);
}
}
return {
safe: issues.length === 0,
issues,
safetyLevel
};
}
/**
* Get safety rules for level
*/
getSafetyRulesForLevel(level) {
const allRules = Array.from(this.safetyRules.entries());
if (level === 'low') {
return allRules.slice(0, 1); // Only critical rules
} else if (level === 'medium') {
return allRules.slice(0, 2); // Most important rules
} else {
return allRules; // All rules for high safety
}
}
/**
* Calculate fix confidence
*/
calculateFixConfidence(fix, issue) {
let confidence = 50; // Base confidence
// Template-based fixes are more reliable
if (fix.approach === 'template') {
confidence += 30;
}
// Higher confidence for common issue types
const commonTypes = ['undefined_property', 'function_not_defined', 'layout_overflow'];
if (commonTypes.includes(issue.type)) {
confidence += 20;
}
// Adjust based on safety level
if (fix.safetyLevel === 'high') {
confidence += 10;
} else if (fix.safetyLevel === 'low') {
confidence -= 10;
}
return Math.min(95, Math.max(10, confidence));
}
/**
* Estimate fix impact
*/
estimateFixImpact(fix, issue) {
const impact = {
scope: 'local', // local, component, global
risk: 'low', // low, medium, high
effort: 'minimal', // minimal, moderate, significant
testing: 'unit' // unit, integration, e2e
};
// Adjust based on fix type
if (fix.type === 'css') {
impact.scope = 'component';
impact.testing = 'visual';
} else if (fix.code.includes('import ') || fix.code.includes('export ')) {
impact.scope = 'global';
impact.risk = 'medium';
impact.testing = 'integration';
}
// Adjust based on issue severity
if (issue.severity === 'critical') {
impact.effort = 'moderate';
impact.testing = 'integration';
}
return impact;
}
// Helper methods for context extraction
extractClassName(issue) { return 'component'; }
extractFunctionName(issue) { return 'functionName'; }
extractVariableName(issue) { return 'variableName'; }
extractObjectName(issue) { return 'object'; }
extractPropertyName(issue) { return 'property'; }
extractImportName(issue) { return 'ImportedComponent'; }
guessModulePath(issue) { return './component'; }
suggestAccessibleColor(issue) { return '#333333'; }
suggestAccessibleBackground(issue) { return '#ffffff'; }
// Validation methods
validateJavaScriptSyntax(code) {
try {
new Function(code);
return { valid: true };
} catch (error) {
return { valid: false, message: error.message };
}
}
validateJavaScriptLogic(code) {
const warnings = [];
if (code.includes('console.log')) {
warnings.push('Contains console.log statements');
}
return { valid: true, warnings };
}
validateCSSSyntax(code) {
// Basic CSS validation
const braceCount = (code.match(/{/g) || []).length - (code.match(/}/g) || []).length;
if (braceCount !== 0) {
return { valid: false, message: 'Unmatched braces in CSS' };
}
return { valid: true };
}
validateCSSProperties(code) {
return { valid: true };
}
validateHTMLSyntax(code) {
return { valid: true };
}
validateHTMLAccessibility(code) {
const warnings = [];
if (code.includes('<img') && !code.includes('alt=')) {
warnings.push('Image missing alt attribute');
}
return { valid: true, warnings };
}
}