Files
bigwo/dev-assistant-mcp/dist/tools/projectScan.js
2026-03-12 12:47:56 +08:00

196 lines
7.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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