mini-claude-code
Version:
Advanced AI-powered coding assistant with streaming responses, context memory, intelligent auto-completion, error handling, test generation, and task planning
782 lines (688 loc) • 21.3 kB
JavaScript
const fs = require('fs-extra');
const path = require('path');
const { spawn } = require('child_process');
class IntelligentErrorHandler {
constructor(toolManager) {
this.toolManager = toolManager;
this.name = 'IntelligentErrorHandler';
// 常见错误模式和修复策略
this.errorPatterns = {
// JavaScript/TypeScript 错误
syntax: {
patterns: [
/SyntaxError: (.+)/,
/Unexpected token (.+)/,
/Missing \) after argument list/,
/Unexpected end of input/
],
severity: 'high',
autoFixable: true
},
import: {
patterns: [
/Cannot find module ['"](.+)['"]/,
/Module not found: Error: Can't resolve ['"](.+)['"]/,
/Cannot resolve module ['"](.+)['"]/
],
severity: 'medium',
autoFixable: true
},
type: {
patterns: [
/(.+) is not defined/,
/(.+) is not a function/,
/Cannot read property ['"](.+)['"] of (.+)/,
/Property ['"](.+)['"] does not exist on type ['"](.+)['"]/
],
severity: 'medium',
autoFixable: false
},
react: {
patterns: [
/Each child in a list should have a unique "key" prop/,
/Warning: (.+) is using uppercase HTML/,
/Warning: React does not recognize the `(.+)` prop/
],
severity: 'low',
autoFixable: true
},
eslint: {
patterns: [
/(.+) is assigned a value but never used/,
/Missing semicolon/,
/Unexpected console statement/,
/'(.+)' is not defined no-undef/
],
severity: 'low',
autoFixable: true
}
};
// 修复策略映射
this.fixStrategies = {
missingImport: this.fixMissingImport.bind(this),
syntaxError: this.fixSyntaxError.bind(this),
undefinedVariable: this.fixUndefinedVariable.bind(this),
reactKey: this.fixReactKey.bind(this),
unusedVariable: this.fixUnusedVariable.bind(this),
missingSemicolon: this.fixMissingSemicolon.bind(this)
};
}
/**
* 分析错误信息并提供修复建议
*/
async analyzeError(errorMessage, filePath = null, context = {}) {
try {
console.log(`🔍 Analyzing error: ${errorMessage.substring(0, 100)}...`);
const errorAnalysis = {
originalError: errorMessage,
filePath,
detectedType: null,
severity: 'unknown',
autoFixable: false,
suggestions: [],
aiAnalysis: null
};
// 本地模式识别
const localAnalysis = this.classifyErrorLocally(errorMessage);
if (localAnalysis) {
errorAnalysis.detectedType = localAnalysis.type;
errorAnalysis.severity = localAnalysis.severity;
errorAnalysis.autoFixable = localAnalysis.autoFixable;
errorAnalysis.suggestions = localAnalysis.suggestions;
}
// AI 深度分析
if (this.toolManager.ai.isAvailable()) {
const aiAnalysis = await this.getAIErrorAnalysis(errorMessage, filePath, context);
if (aiAnalysis.success) {
errorAnalysis.aiAnalysis = aiAnalysis.analysis;
errorAnalysis.suggestions = [
...errorAnalysis.suggestions,
...aiAnalysis.analysis.suggestions
];
}
}
return {
success: true,
analysis: errorAnalysis
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
/**
* 本地错误分类
*/
classifyErrorLocally(errorMessage) {
for (const [type, config] of Object.entries(this.errorPatterns)) {
for (const pattern of config.patterns) {
const match = errorMessage.match(pattern);
if (match) {
return {
type,
severity: config.severity,
autoFixable: config.autoFixable,
match: match[1] || match[0],
suggestions: this.getLocalSuggestions(type, match)
};
}
}
}
return null;
}
/**
* 获取本地修复建议
*/
getLocalSuggestions(errorType, match) {
const suggestions = {
syntax: [
'检查语法错误,特别是括号、分号和引号',
'使用代码格式化工具检查代码结构',
'检查函数定义和调用的语法'
],
import: [
`检查模块 "${match[1]}" 是否已安装`,
'验证导入路径是否正确',
'检查文件扩展名是否正确',
'考虑使用相对路径或绝对路径'
],
type: [
`检查变量 "${match[1]}" 是否已定义`,
'验证对象属性是否存在',
'添加类型检查或默认值',
'检查函数调用的参数类型'
],
react: [
'为列表项添加唯一的 key 属性',
'检查 React 组件的 prop 命名',
'验证 JSX 语法和组件使用'
],
eslint: [
'移除未使用的变量或添加 eslint-disable',
'添加缺失的分号',
'移除 console.log 语句或添加注释'
]
};
return suggestions[errorType] || ['使用 AI 分析获取更详细的修复建议'];
}
/**
* AI 错误分析
*/
async getAIErrorAnalysis(errorMessage, filePath, context) {
try {
const fileContent = filePath && await fs.pathExists(filePath)
? await fs.readFile(filePath, 'utf8')
: null;
const contextPrompt = this.toolManager.memory?.generateContextPrompt(
`错误分析: ${errorMessage}`,
this.toolManager.context.projectInfo
) || '';
const prompt = `请分析以下错误并提供详细的修复建议:
错误信息: ${errorMessage}
${filePath ? `文件路径: ${filePath}` : ''}
${fileContent ? `\n代码内容:\n\`\`\`\n${fileContent.substring(0, 2000)}\n\`\`\`` : ''}
项目上下文:
${contextPrompt}
请提供JSON格式的分析结果,包含:
1. errorType: 错误类型
2. rootCause: 根本原因
3. severity: 严重程度 (high/medium/low)
4. suggestions: 修复建议数组
5. codeExample: 修复代码示例(如果适用)
6. preventionTips: 预防建议
只返回JSON,不要包含其他文字。`;
const result = await this.toolManager.ai.chat(prompt, {
temperature: 0.3,
maxTokens: 1500,
timeout: 20000
});
if (result.success) {
try {
const analysis = JSON.parse(result.response);
return {
success: true,
analysis
};
} catch (parseError) {
return {
success: true,
analysis: {
errorType: 'unknown',
rootCause: result.response.substring(0, 200),
severity: 'medium',
suggestions: [result.response.substring(0, 500)],
codeExample: null,
preventionTips: []
}
};
}
}
return { success: false, error: result.error };
} catch (error) {
return { success: false, error: error.message };
}
}
/**
* 自动修复错误
*/
async autoFix(filePath, errorAnalysis, options = {}) {
try {
if (!errorAnalysis.autoFixable) {
return {
success: false,
error: 'This error type is not auto-fixable',
manual: true
};
}
const { detectedType, match } = errorAnalysis;
const fixStrategy = this.getFixStrategy(detectedType);
if (!fixStrategy) {
return {
success: false,
error: 'No fix strategy available for this error type'
};
}
console.log(`🔧 Attempting to auto-fix ${detectedType} error in ${filePath}`);
const fixResult = await fixStrategy(filePath, match, errorAnalysis, options);
if (fixResult.success) {
// 保存修复记录到记忆系统
if (this.toolManager.memory) {
await this.toolManager.memory.addCodeSnippet(
fixResult.fixedCode || 'Auto-fix applied',
`Auto-fix: ${detectedType} error`,
'auto-fix',
{
projectPath: this.toolManager.context.currentDirectory,
errorType: detectedType,
filePath
}
);
}
}
return fixResult;
} catch (error) {
return {
success: false,
error: error.message
};
}
}
/**
* 获取修复策略
*/
getFixStrategy(errorType) {
const strategyMap = {
import: this.fixStrategies.missingImport,
syntax: this.fixStrategies.syntaxError,
type: this.fixStrategies.undefinedVariable,
react: this.fixStrategies.reactKey,
eslint: this.fixStrategies.unusedVariable
};
return strategyMap[errorType];
}
/**
* 修复缺失导入
*/
async fixMissingImport(filePath, missingModule, errorAnalysis) {
try {
const content = await fs.readFile(filePath, 'utf8');
const lines = content.split('\n');
// 检测导入风格 (ES6 vs CommonJS)
const hasESImports = content.includes('import ') || content.includes('export ');
let importStatement;
if (hasESImports) {
importStatement = this.generateES6Import(missingModule);
} else {
importStatement = `const ${missingModule} = require('${missingModule}');`;
}
// 找到插入位置(其他导入语句之后)
let insertIndex = 0;
for (let i = 0; i < lines.length; i++) {
if (lines[i].includes('import ') || lines[i].includes('require(')) {
insertIndex = i + 1;
} else if (lines[i].trim() && !lines[i].startsWith('//') && !lines[i].startsWith('/*')) {
break;
}
}
lines.splice(insertIndex, 0, importStatement);
const fixedContent = lines.join('\n');
await fs.writeFile(filePath, fixedContent);
return {
success: true,
message: `Added import for ${missingModule}`,
fixedCode: importStatement,
changes: [`Added: ${importStatement}`]
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
/**
* 生成 ES6 导入语句
*/
generateES6Import(moduleName) {
// 常见的默认导入模块
const defaultImports = ['react', 'vue', 'axios', 'lodash', 'moment'];
if (defaultImports.includes(moduleName.toLowerCase())) {
const capitalizedName = moduleName.charAt(0).toUpperCase() + moduleName.slice(1);
return `import ${capitalizedName} from '${moduleName}';`;
}
// 命名导入
return `import { ${moduleName} } from '${moduleName}';`;
}
/**
* 修复语法错误
*/
async fixSyntaxError(filePath, syntaxError, errorAnalysis) {
try {
// 对于语法错误,使用 AI 进行修复
if (!this.toolManager.ai.isAvailable()) {
return {
success: false,
error: 'AI required for syntax error fixing'
};
}
const content = await fs.readFile(filePath, 'utf8');
const prompt = `请修复以下代码中的语法错误:
错误信息: ${syntaxError}
代码内容:
\`\`\`javascript
${content}
\`\`\`
请返回修复后的完整代码,不要包含解释文字。`;
const result = await this.toolManager.ai.chat(prompt, {
temperature: 0.2,
maxTokens: 3000,
timeout: 25000
});
if (result.success) {
// 提取代码块
const codeMatch = result.response.match(/```(?:javascript|js|typescript|ts)?\n?([\s\S]*?)\n?```/);
const fixedCode = codeMatch ? codeMatch[1] : result.response;
// 创建备份
await fs.writeFile(`${filePath}.backup`, content);
// 写入修复后的代码
await fs.writeFile(filePath, fixedCode);
return {
success: true,
message: 'Syntax error fixed using AI',
fixedCode: fixedCode,
changes: ['AI-generated syntax fix'],
backup: `${filePath}.backup`
};
}
return {
success: false,
error: 'AI could not fix the syntax error'
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
/**
* 修复未定义变量
*/
async fixUndefinedVariable(filePath, variable, errorAnalysis) {
try {
const content = await fs.readFile(filePath, 'utf8');
// 检查是否是常见的全局变量或 API
const globalFixes = this.getGlobalVariableFix(variable);
if (globalFixes) {
const lines = content.split('\n');
lines.unshift(globalFixes);
await fs.writeFile(filePath, lines.join('\n'));
return {
success: true,
message: `Added declaration for global variable ${variable}`,
fixedCode: globalFixes,
changes: [`Added: ${globalFixes}`]
};
}
return {
success: false,
error: 'Cannot auto-fix undefined variable',
manual: true,
suggestion: `Please define the variable '${variable}' or check if it should be imported`
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
/**
* 获取全局变量修复
*/
getGlobalVariableFix(variable) {
const globalFixes = {
'window': '/* global window */',
'document': '/* global document */',
'console': '/* global console */',
'process': '/* global process */',
'Buffer': '/* global Buffer */',
'global': '/* global global */',
'require': '/* global require */',
'module': '/* global module */',
'exports': '/* global exports */'
};
return globalFixes[variable];
}
/**
* 修复 React key 警告
*/
async fixReactKey(filePath, keyError, errorAnalysis) {
try {
const content = await fs.readFile(filePath, 'utf8');
// 使用正则表达式查找需要添加 key 的地方
const mapPattern = /\.map\s*\(\s*\(([^)]+)\)\s*=>\s*(<[^>]+>)/g;
let fixedContent = content;
let matches = [];
let match;
while ((match = mapPattern.exec(content)) !== null) {
const [fullMatch, paramName, elementStart] = match;
// 检查是否已经有 key 属性
if (!elementStart.includes('key=')) {
const param = paramName.split(',')[0].trim();
const fixedElement = elementStart.replace('>', ` key={${param}.id || index}>`);
fixedContent = fixedContent.replace(fullMatch, fullMatch.replace(elementStart, fixedElement));
matches.push(fixedElement);
}
}
if (matches.length > 0) {
await fs.writeFile(filePath, fixedContent);
return {
success: true,
message: `Added ${matches.length} key prop(s) to React elements`,
fixedCode: matches.join('\n'),
changes: matches.map(m => `Added key prop: ${m}`)
};
}
return {
success: false,
error: 'No map functions found that need key props'
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
/**
* 修复未使用变量
*/
async fixUnusedVariable(filePath, variable, errorAnalysis) {
try {
const content = await fs.readFile(filePath, 'utf8');
const lines = content.split('\n');
let fixedLines = [];
let removed = [];
for (let line of lines) {
// 检查是否是未使用变量的声明行
if (line.includes(`const ${variable}`) ||
line.includes(`let ${variable}`) ||
line.includes(`var ${variable}`)) {
// 添加 eslint 注释或删除该行
if (line.trim().endsWith(';')) {
removed.push(line.trim());
continue; // 删除该行
}
}
fixedLines.push(line);
}
if (removed.length > 0) {
await fs.writeFile(filePath, fixedLines.join('\n'));
return {
success: true,
message: `Removed ${removed.length} unused variable declaration(s)`,
fixedCode: removed.join('\n'),
changes: removed.map(r => `Removed: ${r}`)
};
}
return {
success: false,
error: 'No unused variable declarations found to remove'
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
/**
* 修复缺失分号
*/
async fixMissingSemicolon(filePath, missingLine, errorAnalysis) {
try {
const content = await fs.readFile(filePath, 'utf8');
const lines = content.split('\n');
// 自动添加分号的模式
const needsSemicolon = /^[\s]*[^\/\*\s].*[^;{}\s][\s]*$/;
let fixedLines = lines.map(line => {
if (needsSemicolon.test(line) &&
!line.includes('//') &&
!line.includes('if ') &&
!line.includes('for ') &&
!line.includes('while ') &&
!line.includes('function ')) {
return line.trimEnd() + ';';
}
return line;
});
await fs.writeFile(filePath, fixedLines.join('\n'));
return {
success: true,
message: 'Added missing semicolons',
fixedCode: 'Added semicolons to appropriate lines',
changes: ['Added missing semicolons']
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
/**
* 验证修复结果
*/
async validateFix(filePath, originalError) {
try {
// 尝试运行基本语法检查
if (filePath.endsWith('.js') || filePath.endsWith('.jsx')) {
return await this.validateJavaScript(filePath);
} else if (filePath.endsWith('.ts') || filePath.endsWith('.tsx')) {
return await this.validateTypeScript(filePath);
}
return {
success: true,
message: 'File appears to be valid'
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
/**
* 验证 JavaScript 文件
*/
async validateJavaScript(filePath) {
return new Promise((resolve) => {
const child = spawn('node', ['--check', filePath]);
let stderr = '';
child.stderr.on('data', (data) => {
stderr += data.toString();
});
child.on('close', (code) => {
if (code === 0) {
resolve({
success: true,
message: 'JavaScript syntax is valid'
});
} else {
resolve({
success: false,
error: stderr,
stillHasErrors: true
});
}
});
});
}
/**
* 验证 TypeScript 文件
*/
async validateTypeScript(filePath) {
return new Promise((resolve) => {
const child = spawn('npx', ['tsc', '--noEmit', filePath]);
let stderr = '';
child.stderr.on('data', (data) => {
stderr += data.toString();
});
child.on('close', (code) => {
if (code === 0) {
resolve({
success: true,
message: 'TypeScript compilation successful'
});
} else {
resolve({
success: false,
error: stderr,
stillHasErrors: true
});
}
});
});
}
/**
* 获取错误处理统计
*/
getStats() {
return {
supportedErrorTypes: Object.keys(this.errorPatterns).length,
autoFixableTypes: Object.values(this.errorPatterns)
.filter(pattern => pattern.autoFixable).length,
fixStrategies: Object.keys(this.fixStrategies).length
};
}
/**
* 批量错误检查
*/
async checkProject(projectPath = '.') {
try {
const results = [];
const files = await this.findCodeFiles(projectPath);
for (const file of files) {
const validation = await this.validateFix(file);
if (!validation.success && validation.stillHasErrors) {
const analysis = await this.analyzeError(validation.error, file);
results.push({
file,
error: validation.error,
analysis: analysis.success ? analysis.analysis : null
});
}
}
return {
success: true,
results,
totalFiles: files.length,
errorFiles: results.length
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
/**
* 查找代码文件
*/
async findCodeFiles(projectPath) {
const glob = require('glob');
const pattern = path.join(projectPath, '**/*.{js,jsx,ts,tsx}');
return new Promise((resolve, reject) => {
glob(pattern, {
ignore: ['**/node_modules/**', '**/dist/**', '**/build/**']
}, (err, files) => {
if (err) reject(err);
else resolve(files);
});
});
}
}
module.exports = IntelligentErrorHandler;