Files
bigwo/dev-assistant-mcp/dist/tools/projectScan.js

196 lines
7.3 KiB
JavaScript
Raw Normal View History

2026-03-12 12:47:56 +08:00
import { existsSync, readFileSync, readdirSync, statSync } from "fs";
import { join, extname } from "path";
export const projectScanTool = {
name: "project_scan",
description: "扫描分析项目结构。返回项目类型、技术栈、文件结构、依赖列表、可用脚本、配置文件等全局信息,帮助快速理解项目。",
inputSchema: {
type: "object",
properties: {
project_path: {
type: "string",
description: "项目根目录(绝对路径)",
},
max_depth: {
type: "number",
description: "目录扫描最大深度(默认 3",
},
},
required: ["project_path"],
},
};
function scanDir(dir, depth, maxDepth, prefix = "") {
if (depth > maxDepth)
return [];
const lines = [];
try {
const entries = readdirSync(dir).filter((e) => !e.startsWith(".") && e !== "node_modules" && e !== "__pycache__" && e !== "dist" && e !== "build" && e !== ".git");
for (const entry of entries.slice(0, 30)) { // 限制每层最多30项
const fullPath = join(dir, entry);
try {
const stat = statSync(fullPath);
if (stat.isDirectory()) {
lines.push(`${prefix}📂 ${entry}/`);
lines.push(...scanDir(fullPath, depth + 1, maxDepth, prefix + " "));
}
else {
const size = stat.size;
const sizeStr = size < 1024 ? `${size}B` : size < 1024 * 1024 ? `${(size / 1024).toFixed(0)}KB` : `${(size / 1024 / 1024).toFixed(1)}MB`;
lines.push(`${prefix}📄 ${entry} (${sizeStr})`);
}
}
catch { }
}
if (entries.length > 30) {
lines.push(`${prefix}... 还有 ${entries.length - 30} 个条目`);
}
}
catch { }
return lines;
}
function countFiles(dir, ext, depth = 0) {
if (depth > 5)
return 0;
let count = 0;
try {
for (const entry of readdirSync(dir)) {
if (entry.startsWith(".") || entry === "node_modules" || entry === "__pycache__" || entry === "dist")
continue;
const fullPath = join(dir, entry);
try {
const stat = statSync(fullPath);
if (stat.isDirectory())
count += countFiles(fullPath, ext, depth + 1);
else if (extname(entry) === ext)
count++;
}
catch { }
}
}
catch { }
return count;
}
export async function executeProjectScan(args) {
const { project_path, max_depth = 3 } = args;
const hasFile = (name) => existsSync(join(project_path, name));
const readJson = (name) => {
try {
return JSON.parse(readFileSync(join(project_path, name), "utf-8"));
}
catch {
return null;
}
};
const output = [
`# 项目分析报告`,
``,
`📂 路径: ${project_path}`,
``,
];
// 项目类型和技术栈检测
const techStack = [];
const configs = [];
if (hasFile("package.json"))
techStack.push("Node.js");
if (hasFile("tsconfig.json"))
techStack.push("TypeScript");
if (hasFile("next.config.js") || hasFile("next.config.mjs") || hasFile("next.config.ts"))
techStack.push("Next.js");
if (hasFile("vite.config.ts") || hasFile("vite.config.js"))
techStack.push("Vite");
if (hasFile("webpack.config.js"))
techStack.push("Webpack");
if (hasFile("requirements.txt") || hasFile("pyproject.toml"))
techStack.push("Python");
if (hasFile("Cargo.toml"))
techStack.push("Rust");
if (hasFile("go.mod"))
techStack.push("Go");
if (hasFile("pom.xml") || hasFile("build.gradle"))
techStack.push("Java");
if (hasFile("docker-compose.yml") || hasFile("Dockerfile"))
techStack.push("Docker");
if (hasFile(".eslintrc.js") || hasFile("eslint.config.js"))
configs.push("ESLint");
if (hasFile(".prettierrc") || hasFile(".prettierrc.json"))
configs.push("Prettier");
if (hasFile("jest.config.js") || hasFile("jest.config.ts"))
configs.push("Jest");
if (hasFile("vitest.config.ts"))
configs.push("Vitest");
if (hasFile(".env") || hasFile(".env.example"))
configs.push(".env");
output.push(`## 技术栈`, techStack.length > 0 ? techStack.join(" + ") : "未检测到", ``);
if (configs.length > 0) {
output.push(`## 工具配置`, configs.join(", "), ``);
}
// package.json 分析
const pkg = readJson("package.json");
if (pkg) {
output.push(`## package.json`);
if (pkg.name)
output.push(`- **名称**: ${pkg.name}`);
if (pkg.version)
output.push(`- **版本**: ${pkg.version}`);
if (pkg.description)
output.push(`- **描述**: ${pkg.description}`);
if (pkg.scripts && Object.keys(pkg.scripts).length > 0) {
output.push(``, `### 可用脚本`);
for (const [name, cmd] of Object.entries(pkg.scripts)) {
output.push(`- \`npm run ${name}\`${cmd}`);
}
}
if (pkg.dependencies) {
const deps = Object.entries(pkg.dependencies);
output.push(``, `### 依赖 (${deps.length})`);
for (const [name, ver] of deps.slice(0, 20)) {
output.push(`- ${name}: ${ver}`);
}
if (deps.length > 20)
output.push(`... 还有 ${deps.length - 20}`);
}
if (pkg.devDependencies) {
const devDeps = Object.entries(pkg.devDependencies);
output.push(``, `### 开发依赖 (${devDeps.length})`);
for (const [name, ver] of devDeps.slice(0, 15)) {
output.push(`- ${name}: ${ver}`);
}
if (devDeps.length > 15)
output.push(`... 还有 ${devDeps.length - 15}`);
}
output.push(``);
}
// Python 依赖
if (hasFile("requirements.txt")) {
try {
const reqs = readFileSync(join(project_path, "requirements.txt"), "utf-8")
.split("\n").filter((l) => l.trim() && !l.startsWith("#"));
output.push(`## Python 依赖 (${reqs.length})`);
for (const r of reqs.slice(0, 20))
output.push(`- ${r.trim()}`);
if (reqs.length > 20)
output.push(`... 还有 ${reqs.length - 20}`);
output.push(``);
}
catch { }
}
// 文件统计
const fileCounts = {};
for (const ext of [".ts", ".tsx", ".js", ".jsx", ".py", ".css", ".html", ".json", ".md"]) {
const count = countFiles(project_path, ext);
if (count > 0)
fileCounts[ext] = count;
}
if (Object.keys(fileCounts).length > 0) {
output.push(`## 文件统计`);
for (const [ext, count] of Object.entries(fileCounts).sort((a, b) => b[1] - a[1])) {
output.push(`- ${ext}: ${count} 个文件`);
}
output.push(``);
}
// 目录树
output.push(`## 目录结构`);
const tree = scanDir(project_path, 0, max_depth);
output.push(...tree);
return output.join("\n");
}
//# sourceMappingURL=projectScan.js.map