133 lines
5.6 KiB
JavaScript
133 lines
5.6 KiB
JavaScript
import { exec } from "child_process";
|
||
import { promisify } from "util";
|
||
import { existsSync } from "fs";
|
||
import { join } from "path";
|
||
const execAsync = promisify(exec);
|
||
export const lintCheckTool = {
|
||
name: "lint_check",
|
||
description: "对项目执行代码检查。自动检测项目类型并运行对应的 lint 工具(ESLint、TypeScript 编译检查、Pylint 等),返回结构化的错误列表。",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
project_path: {
|
||
type: "string",
|
||
description: "项目根目录(绝对路径)",
|
||
},
|
||
fix: {
|
||
type: "boolean",
|
||
description: "是否自动修复可修复的问题(默认 false)",
|
||
},
|
||
files: {
|
||
type: "string",
|
||
description: "指定检查的文件或 glob(可选,默认检查整个项目)",
|
||
},
|
||
},
|
||
required: ["project_path"],
|
||
},
|
||
};
|
||
async function runCommand(cmd, cwd, timeout = 30000) {
|
||
try {
|
||
const { stdout, stderr } = await execAsync(cmd, {
|
||
cwd,
|
||
timeout,
|
||
maxBuffer: 1024 * 1024 * 5,
|
||
shell: process.platform === "win32" ? "powershell.exe" : "/bin/bash",
|
||
});
|
||
return { stdout, stderr, code: 0 };
|
||
}
|
||
catch (error) {
|
||
return {
|
||
stdout: error.stdout || "",
|
||
stderr: error.stderr || "",
|
||
code: error.code ?? 1,
|
||
};
|
||
}
|
||
}
|
||
export async function executeLintCheck(args) {
|
||
const { project_path, fix = false, files } = args;
|
||
const results = [];
|
||
const hasFile = (name) => existsSync(join(project_path, name));
|
||
const hasNodeModules = hasFile("node_modules");
|
||
// TypeScript 编译检查
|
||
if (hasFile("tsconfig.json")) {
|
||
const result = await runCommand("npx tsc --noEmit --pretty", project_path);
|
||
const errorCount = (result.stdout.match(/error TS/g) || []).length;
|
||
results.push({
|
||
tool: "TypeScript (tsc --noEmit)",
|
||
success: result.code === 0,
|
||
errorCount,
|
||
warningCount: 0,
|
||
output: result.stdout || result.stderr || "(无输出)",
|
||
});
|
||
}
|
||
// ESLint
|
||
if (hasFile(".eslintrc.js") || hasFile(".eslintrc.json") || hasFile(".eslintrc.yml") || hasFile("eslint.config.js") || hasFile("eslint.config.mjs")) {
|
||
const target = files || "src/";
|
||
const fixFlag = fix ? " --fix" : "";
|
||
const result = await runCommand(`npx eslint ${target}${fixFlag} --format stylish`, project_path);
|
||
const errorMatch = result.stdout.match(/(\d+) errors?/);
|
||
const warnMatch = result.stdout.match(/(\d+) warnings?/);
|
||
results.push({
|
||
tool: `ESLint${fix ? " (--fix)" : ""}`,
|
||
success: result.code === 0,
|
||
errorCount: errorMatch ? parseInt(errorMatch[1]) : 0,
|
||
warningCount: warnMatch ? parseInt(warnMatch[1]) : 0,
|
||
output: result.stdout || result.stderr || "✅ 无问题",
|
||
});
|
||
}
|
||
// Python: pylint / flake8
|
||
if (hasFile("requirements.txt") || hasFile("setup.py") || hasFile("pyproject.toml")) {
|
||
const target = files || ".";
|
||
// 优先 flake8
|
||
const result = await runCommand(`python -m flake8 ${target} --max-line-length=120 --count`, project_path);
|
||
if (result.code !== 127) { // 127 = command not found
|
||
const lines = result.stdout.trim().split("\n").filter(Boolean);
|
||
results.push({
|
||
tool: "Flake8",
|
||
success: result.code === 0,
|
||
errorCount: lines.length,
|
||
warningCount: 0,
|
||
output: result.stdout || result.stderr || "✅ 无问题",
|
||
});
|
||
}
|
||
}
|
||
// 如果什么检查工具都没找到
|
||
if (results.length === 0) {
|
||
// 尝试通用的 package.json lint 脚本
|
||
if (hasFile("package.json")) {
|
||
const result = await runCommand("npm run lint 2>&1 || echo LINT_SCRIPT_NOT_FOUND", project_path);
|
||
if (!result.stdout.includes("LINT_SCRIPT_NOT_FOUND") && !result.stdout.includes("Missing script")) {
|
||
results.push({
|
||
tool: "npm run lint",
|
||
success: result.code === 0,
|
||
errorCount: result.code === 0 ? 0 : 1,
|
||
warningCount: 0,
|
||
output: result.stdout || result.stderr,
|
||
});
|
||
}
|
||
}
|
||
}
|
||
if (results.length === 0) {
|
||
return `# Lint 检查结果\n\n⚠️ 未检测到 lint 工具配置(ESLint、tsconfig、pylint 等)\n项目路径: ${project_path}\n\n建议:\n- TypeScript 项目:添加 tsconfig.json\n- JS 项目:npm init @eslint/config\n- Python 项目:pip install flake8`;
|
||
}
|
||
// 组装报告
|
||
const totalErrors = results.reduce((sum, r) => sum + r.errorCount, 0);
|
||
const totalWarnings = results.reduce((sum, r) => sum + r.warningCount, 0);
|
||
const allPassed = results.every((r) => r.success);
|
||
const output = [
|
||
`# Lint 检查报告`,
|
||
``,
|
||
`📂 项目: ${project_path}`,
|
||
`${allPassed ? "✅ 全部通过" : "❌ 发现问题"} | 错误: ${totalErrors} | 警告: ${totalWarnings}`,
|
||
``,
|
||
];
|
||
for (const r of results) {
|
||
output.push(`## ${r.success ? "✅" : "❌"} ${r.tool}`, `错误: ${r.errorCount} | 警告: ${r.warningCount}`, "```", r.output.slice(0, 3000), // 限制输出长度
|
||
"```", ``);
|
||
}
|
||
if (!allPassed && !fix) {
|
||
output.push(`💡 提示: 可以设置 fix=true 自动修复可修复的问题`);
|
||
}
|
||
return output.join("\n");
|
||
}
|
||
//# sourceMappingURL=lintCheck.js.map
|