Update code

This commit is contained in:
User
2026-03-12 12:47:56 +08:00
parent 92e7fc5bda
commit 9dab61345c
9383 changed files with 1463454 additions and 1 deletions

View File

@@ -0,0 +1,140 @@
import { exec } from "child_process";
import { promisify } from "util";
import { existsSync } from "fs";
import { join } from "path";
const execAsync = promisify(exec);
export const autoFixTool = {
name: "auto_fix",
description:
"自动修复代码问题。运行 Prettier 格式化、ESLint --fix 自动修复、以及其他自动修复工具。返回修复前后的变更摘要。",
inputSchema: {
type: "object" as const,
properties: {
project_path: {
type: "string",
description: "项目根目录(绝对路径)",
},
files: {
type: "string",
description: "指定修复的文件或 glob可选默认修复整个项目",
},
tools: {
type: "string",
description: "指定修复工具逗号分隔可选prettier, eslint, autopep8。默认自动检测。",
},
},
required: ["project_path"],
},
};
async function runFix(cmd: string, cwd: string): Promise<{ success: boolean; output: string }> {
try {
const { stdout, stderr } = await execAsync(cmd, {
cwd,
timeout: 30000,
maxBuffer: 1024 * 1024 * 5,
shell: process.platform === "win32" ? "powershell.exe" : "/bin/bash",
});
return { success: true, output: stdout || stderr || "✅ 修复完成" };
} catch (error: any) {
return { success: false, output: error.stdout || error.stderr || error.message };
}
}
export async function executeAutoFix(args: {
project_path: string;
files?: string;
tools?: string;
}): Promise<string> {
const { project_path, files, tools } = args;
const hasFile = (name: string) => existsSync(join(project_path, name));
const fixResults: { tool: string; success: boolean; output: string }[] = [];
// 先获取 git diff 作为修复前基线
let diffBefore = "";
try {
const { stdout } = await execAsync("git diff --stat", { cwd: project_path, timeout: 5000 });
diffBefore = stdout;
} catch {}
const requestedTools = tools ? tools.split(",").map((t) => t.trim().toLowerCase()) : [];
const autoDetect = requestedTools.length === 0;
// Prettier
if (autoDetect ? (hasFile(".prettierrc") || hasFile(".prettierrc.json") || hasFile("prettier.config.js")) : requestedTools.includes("prettier")) {
const target = files || ".";
const result = await runFix(`npx prettier --write "${target}"`, project_path);
fixResults.push({ tool: "Prettier", ...result });
}
// ESLint --fix
if (autoDetect ? (hasFile(".eslintrc.js") || hasFile(".eslintrc.json") || hasFile("eslint.config.js") || hasFile("eslint.config.mjs")) : requestedTools.includes("eslint")) {
const target = files || "src/";
const result = await runFix(`npx eslint "${target}" --fix`, project_path);
fixResults.push({ tool: "ESLint --fix", ...result });
}
// Python autopep8
if (autoDetect ? (hasFile("requirements.txt") || hasFile("pyproject.toml")) : requestedTools.includes("autopep8")) {
const target = files || ".";
const result = await runFix(`python -m autopep8 --in-place --recursive "${target}"`, project_path);
if (!result.output.includes("No module named")) {
fixResults.push({ tool: "autopep8", ...result });
}
}
// 如果没有检测到工具配置,尝试 package.json 中的 format 脚本
if (fixResults.length === 0 && hasFile("package.json")) {
const result = await runFix("npm run format 2>&1 || npm run lint:fix 2>&1 || echo NO_FIX_SCRIPT", project_path);
if (!result.output.includes("NO_FIX_SCRIPT") && !result.output.includes("Missing script")) {
fixResults.push({ tool: "npm script (format/lint:fix)", ...result });
}
}
// 获取修复后的 diff
let diffAfter = "";
try {
const { stdout } = await execAsync("git diff --stat", { cwd: project_path, timeout: 5000 });
diffAfter = stdout;
} catch {}
// 组装报告
const output: string[] = [
`# 自动修复报告`,
``,
`📂 项目: ${project_path}`,
``,
];
if (fixResults.length === 0) {
output.push(
"⚠️ 未检测到格式化/修复工具",
"",
"建议安装:",
"- `npm install -D prettier` — 代码格式化",
"- `npm install -D eslint` — 代码质量检查和自动修复",
"- `pip install autopep8` — Python 代码格式化",
);
} else {
for (const r of fixResults) {
output.push(
`## ${r.success ? "✅" : "⚠️"} ${r.tool}`,
"```",
r.output.slice(0, 2000),
"```",
``
);
}
if (diffAfter && diffAfter !== diffBefore) {
output.push("## 变更摘要 (git diff --stat)", "```", diffAfter, "```");
} else if (fixResults.some((r) => r.success)) {
output.push("📝 修复完成,代码已更新");
}
}
return output.join("\n");
}

View File

@@ -0,0 +1,144 @@
import { exec } from "child_process";
import { promisify } from "util";
import { existsSync, readFileSync } from "fs";
import { join } from "path";
const execAsync = promisify(exec);
export const buildProjectTool = {
name: "build_project",
description:
"构建项目。自动检测构建方式npm run build、tsc、vite build、webpack 等),执行构建并返回结果。如果构建失败,返回错误详情供自动修复。",
inputSchema: {
type: "object" as const,
properties: {
project_path: {
type: "string",
description: "项目根目录(绝对路径)",
},
command: {
type: "string",
description: "自定义构建命令(可选,默认自动检测)",
},
},
required: ["project_path"],
},
};
async function runBuild(cmd: string, cwd: string): Promise<{ stdout: string; stderr: string; code: number; duration: number }> {
const start = Date.now();
try {
const { stdout, stderr } = await execAsync(cmd, {
cwd,
timeout: 300000, // 5分钟
maxBuffer: 1024 * 1024 * 10,
shell: process.platform === "win32" ? "powershell.exe" : "/bin/bash",
});
return { stdout, stderr, code: 0, duration: Date.now() - start };
} catch (error: any) {
return {
stdout: error.stdout || "",
stderr: error.stderr || "",
code: error.code ?? 1,
duration: Date.now() - start,
};
}
}
export async function executeBuildProject(args: {
project_path: string;
command?: string;
}): Promise<string> {
const { project_path, command } = args;
const hasFile = (name: string) => existsSync(join(project_path, name));
let buildCmd = command || "";
let buildTool = "自定义";
if (!buildCmd) {
// 自动检测构建方式
if (hasFile("package.json")) {
try {
const pkg = JSON.parse(readFileSync(join(project_path, "package.json"), "utf-8"));
if (pkg.scripts?.build) {
buildCmd = "npm run build";
buildTool = `npm (${pkg.scripts.build})`;
} else if (hasFile("tsconfig.json")) {
buildCmd = "npx tsc";
buildTool = "TypeScript (tsc)";
}
} catch {}
} else if (hasFile("Makefile")) {
buildCmd = "make";
buildTool = "Make";
} else if (hasFile("setup.py") || hasFile("pyproject.toml")) {
buildCmd = "python -m build 2>&1 || python setup.py build 2>&1";
buildTool = "Python";
}
}
if (!buildCmd) {
return `# 构建结果\n\n⚠ 未检测到构建配置\n项目路径: ${project_path}\n\n建议手动指定 command 参数`;
}
// 检查依赖是否安装
if (hasFile("package.json") && !hasFile("node_modules")) {
const installResult = await runBuild("npm install", project_path);
if (installResult.code !== 0) {
return `# 构建失败\n\n❌ 依赖安装失败 (npm install)\n\n\`\`\`\n${installResult.stderr || installResult.stdout}\n\`\`\``;
}
}
const result = await runBuild(buildCmd, project_path);
const fullOutput = (result.stdout + "\n" + result.stderr).trim();
// 提取错误信息
const errors: string[] = [];
const lines = fullOutput.split("\n");
for (const line of lines) {
if (/error\s+TS\d+/i.test(line) || /Error:/i.test(line) || /ERROR/i.test(line) || /Failed/i.test(line)) {
errors.push(line.trim());
}
}
const output: string[] = [
`# 构建报告`,
``,
`📂 项目: ${project_path}`,
`🔧 工具: ${buildTool}`,
`⏱️ 耗时: ${(result.duration / 1000).toFixed(1)}s`,
`${result.code === 0 ? "✅ **构建成功**" : "❌ **构建失败**"}`,
``,
`## 命令`,
`\`${buildCmd}\``,
``,
];
if (errors.length > 0 && result.code !== 0) {
output.push(
`## 错误摘要(${errors.length} 项)`,
...errors.slice(0, 20).map((e) => `- ${e}`),
errors.length > 20 ? `\n... 还有 ${errors.length - 20} 项错误` : "",
``
);
}
output.push(
`## 完整输出`,
"```",
fullOutput.slice(0, 5000),
"```",
);
if (result.code !== 0) {
output.push(
``,
`## 自动修复建议`,
`1. 使用 code_debug 分析上方错误`,
`2. 修复代码后重新运行 build_project`,
`3. 或使用 lint_check 先检查代码质量`,
);
}
return output.join("\n");
}

View File

@@ -0,0 +1,218 @@
export const codeDebugTool = {
name: "code_debug",
description:
"分析代码错误,返回结构化的调试信息:错误分类、常见原因、排查步骤和修复模式。",
inputSchema: {
type: "object" as const,
properties: {
code: {
type: "string",
description: "有问题的代码",
},
error_message: {
type: "string",
description: "错误信息或堆栈跟踪",
},
language: {
type: "string",
description: "编程语言",
},
expected_behavior: {
type: "string",
description: "期望的正确行为(可选)",
},
},
required: ["code", "error_message", "language"],
},
};
interface ErrorPattern {
pattern: RegExp;
category: string;
commonCauses: string[];
fixStrategies: string[];
}
const errorPatterns: ErrorPattern[] = [
{
pattern: /TypeError.*(?:undefined|null|is not a function|Cannot read prop)/i,
category: "类型错误 (TypeError)",
commonCauses: [
"访问了 undefined/null 的属性",
"函数调用目标不是函数",
"异步操作返回值未正确 await",
"解构赋值时对象为空",
],
fixStrategies: [
"使用可选链 ?. 和空值合并 ??",
"添加前置空值检查",
"确认异步操作是否正确 await",
"检查 import 路径和导出方式是否匹配",
],
},
{
pattern: /ReferenceError.*is not defined/i,
category: "引用错误 (ReferenceError)",
commonCauses: [
"变量/函数未声明就使用",
"拼写错误",
"作用域问题(块级作用域、闭包)",
"缺少 import 语句",
],
fixStrategies: [
"检查变量名拼写",
"确认 import/require 语句",
"检查变量声明的作用域",
],
},
{
pattern: /SyntaxError/i,
category: "语法错误 (SyntaxError)",
commonCauses: [
"括号/引号不匹配",
"缺少分号或逗号",
"关键字拼写错误",
"ESM/CJS 混用",
],
fixStrategies: [
"检查报错行及前几行的括号/引号匹配",
"确认模块系统一致性import vs require",
"使用格式化工具自动修复",
],
},
{
pattern: /ECONNREFUSED|ETIMEDOUT|ENOTFOUND|fetch failed|network/i,
category: "网络错误",
commonCauses: [
"目标服务未启动",
"URL/端口配置错误",
"DNS 解析失败",
"防火墙/代理阻断",
"SSL 证书问题",
],
fixStrategies: [
"确认目标服务运行状态和端口",
"检查 URL 配置(协议、域名、端口)",
"添加重试机制和超时控制",
"检查网络连通性",
],
},
{
pattern: /ENOENT|no such file|Module not found|Cannot find module/i,
category: "文件/模块未找到",
commonCauses: [
"文件路径错误",
"依赖未安装(缺少 npm install",
"相对路径 vs 绝对路径搞混",
"文件扩展名不匹配",
],
fixStrategies: [
"检查文件路径和大小写",
"运行 npm install 安装依赖",
"检查 tsconfig/webpack 的路径别名配置",
"确认文件扩展名(.js/.ts/.mjs",
],
},
{
pattern: /Permission denied|EACCES|403|401|Unauthorized/i,
category: "权限错误",
commonCauses: [
"文件权限不足",
"API 认证失败Token 过期/错误)",
"CORS 跨域限制",
],
fixStrategies: [
"检查文件/目录权限chmod",
"检查 API Key / Token 是否有效",
"配置正确的 CORS 头",
],
},
{
pattern: /out of memory|heap|stack overflow|Maximum call stack/i,
category: "内存/栈溢出",
commonCauses: [
"无限递归",
"处理超大数据集未分页",
"内存泄漏(未清理的引用)",
],
fixStrategies: [
"检查递归终止条件",
"对大数据使用流式处理或分页",
"使用 --max-old-space-size 调整堆内存",
"排查事件监听器和定时器泄漏",
],
},
];
export async function executeCodeDebug(args: {
code: string;
error_message: string;
language: string;
expected_behavior?: string;
}): Promise<string> {
const { code, error_message, language, expected_behavior } = args;
// 匹配错误模式
const matched = errorPatterns.filter((ep) => ep.pattern.test(error_message));
const categories = matched.length > 0 ? matched : [{
category: "未分类错误",
commonCauses: ["请仔细阅读完整错误信息和堆栈"],
fixStrategies: ["根据错误信息定位问题代码行", "添加日志逐步排查"],
}];
// 从堆栈中提取行号
const lineRefs: string[] = [];
const lineRegex = /(?:at\s+.+?|File\s+.+?):(\d+)(?::(\d+))?/g;
let match;
while ((match = lineRegex.exec(error_message)) !== null) {
lineRefs.push(`${match[1]}${match[2] ? `:${match[2]}` : ""}`);
}
const output: string[] = [
`# 调试分析报告`,
``,
`**语言**: ${language}`,
``,
`## 错误信息`,
"```",
error_message,
"```",
``,
];
if (lineRefs.length > 0) {
output.push(`## 堆栈中的关键位置`, ...lineRefs.map((l) => `- ${l}`), ``);
}
for (const cat of categories) {
output.push(
`## 错误类型: ${cat.category}`,
``,
`### 常见原因`,
...cat.commonCauses.map((c) => `- ${c}`),
``,
`### 修复策略`,
...cat.fixStrategies.map((s) => `- ${s}`),
``
);
}
if (expected_behavior) {
output.push(`## 期望行为`, expected_behavior, ``);
}
output.push(
`## 问题代码`,
"```" + language,
code,
"```",
``,
`## 排查步骤`,
`1. 根据以上错误分类和常见原因,定位问题代码行`,
`2. 在可疑位置添加日志输出,验证变量值`,
`3. 根据修复策略实施修改`,
`4. 编写测试用例确认修复有效`,
);
return output.join("\n");
}

View File

@@ -0,0 +1,163 @@
export const codeReviewTool = {
name: "code_review",
description:
"审查代码质量。根据审查重点返回结构化的检查清单和分析结果,涵盖 bug、安全、性能、风格等维度。",
inputSchema: {
type: "object" as const,
properties: {
code: {
type: "string",
description: "要审查的代码",
},
language: {
type: "string",
description: "编程语言(如 typescript, python, java 等)",
},
focus: {
type: "string",
description:
"审查重点可选security=安全性, performance=性能, style=代码风格, all=全面审查",
enum: ["security", "performance", "style", "all"],
},
},
required: ["code", "language"],
},
};
const reviewChecklists: Record<string, string[]> = {
security: [
"检查是否存在 SQL 注入、XSS、CSRF 等注入攻击风险",
"检查是否硬编码了密钥、密码、Token 等敏感信息",
"检查用户输入是否经过验证和清洗",
"检查权限控制是否完善,是否有越权风险",
"检查是否使用了不安全的加密算法或随机数生成",
"检查第三方依赖是否有已知安全漏洞",
],
performance: [
"检查是否存在不必要的循环嵌套或 O(n²) 以上复杂度",
"检查是否有内存泄漏(未清理的定时器、事件监听、闭包)",
"检查是否有不必要的重复计算,是否需要缓存/记忆化",
"检查异步操作是否可以并行化Promise.all",
"检查是否有阻塞主线程的同步操作",
"检查数据库查询是否有 N+1 问题",
],
style: [
"检查命名是否清晰表达意图(变量、函数、类)",
"检查函数长度是否超过 50 行,是否需要拆分",
"检查嵌套层级是否过深(>3 层),是否可用 guard clause",
"检查是否有重复代码DRY 原则)",
"检查注释是否准确且必要,而非冗余",
"检查是否遵循语言社区的标准代码风格",
],
};
export async function executeCodeReview(args: {
code: string;
language: string;
focus?: string;
}): Promise<string> {
const { code, language, focus = "all" } = args;
const lines = code.split("\n");
const lineCount = lines.length;
// 基础代码统计
const stats = {
totalLines: lineCount,
blankLines: lines.filter((l) => l.trim() === "").length,
commentLines: lines.filter((l) => {
const t = l.trim();
return t.startsWith("//") || t.startsWith("#") || t.startsWith("/*") || t.startsWith("*");
}).length,
codeLines: 0,
maxLineLength: Math.max(...lines.map((l) => l.length)),
maxNestingDepth: 0,
};
stats.codeLines = stats.totalLines - stats.blankLines - stats.commentLines;
// 计算最大嵌套深度
let depth = 0;
for (const line of lines) {
const opens = (line.match(/{/g) || []).length;
const closes = (line.match(/}/g) || []).length;
depth += opens - closes;
if (depth > stats.maxNestingDepth) stats.maxNestingDepth = depth;
}
// 检测潜在问题
const issues: string[] = [];
// 通用检查
if (stats.maxLineLength > 120) issues.push(`⚠️ 存在超长行(最长 ${stats.maxLineLength} 字符),建议不超过 120`);
if (stats.maxNestingDepth > 4) issues.push(`⚠️ 嵌套层级过深(${stats.maxNestingDepth} 层),建议重构`);
if (stats.codeLines > 300) issues.push(`⚠️ 文件过长(${stats.codeLines} 行代码),建议拆分`);
if (stats.commentLines === 0 && stats.codeLines > 30) issues.push("🔵 缺少注释,建议为关键逻辑添加说明");
// 安全检查
if (focus === "security" || focus === "all") {
if (/eval\s*\(/.test(code)) issues.push("🔴 使用了 eval(),存在代码注入风险");
if (/innerHTML\s*=/.test(code)) issues.push("🔴 使用了 innerHTML存在 XSS 风险");
if (/(password|secret|api_?key|token)\s*=\s*["'][^"']+["']/i.test(code))
issues.push("🔴 可能硬编码了敏感信息");
if (/\bhttp:\/\//.test(code)) issues.push("🟡 使用了 HTTP 而非 HTTPS");
if (/exec\s*\(|spawn\s*\(/.test(code) && !/sanitize|escape|validate/.test(code))
issues.push("🟡 执行了外部命令,确认输入已清洗");
}
// 性能检查
if (focus === "performance" || focus === "all") {
if (/for\s*\(.*for\s*\(/s.test(code)) issues.push("🟡 ");
if (/setTimeout|setInterval/.test(code) && !/clearTimeout|clearInterval/.test(code))
issues.push("<22> 设置了定时器但未见清理,可能内存泄漏");
if (/\.forEach\(.*await/.test(code)) issues.push("🟡 在 forEach 中使用 await不会等待完成建议用 for...of");
if (/new RegExp\(/.test(code) && /for|while|map|forEach/.test(code))
issues.push("🔵 循环中创建正则表达式,建议提取到循环外");
}
// 风格检查
if (focus === "style" || focus === "all") {
if (/var\s+/.test(code) && (language === "typescript" || language === "javascript"))
issues.push("🔵 使用了 var建议改用 const/let");
if (/console\.log/.test(code)) issues.push("🔵 包含 console.log确认是否需要在生产环境移除");
if (/any/.test(code) && language === "typescript") issues.push("🔵 使用了 any 类型,建议使用具体类型");
if (/TODO|FIXME|HACK|XXX/.test(code)) issues.push("🔵 存在 TODO/FIXME 标记,建议处理");
}
// 获取对应的检查清单
let checklist: string[];
if (focus === "all") {
checklist = [
...reviewChecklists.security,
...reviewChecklists.performance,
...reviewChecklists.style,
];
} else {
checklist = reviewChecklists[focus] || reviewChecklists.style;
}
// 组装结果
const output = [
`# 代码审查报告`,
``,
`**语言**: ${language} | **审查重点**: ${focus}`,
``,
`## 代码统计`,
`- 总行数: ${stats.totalLines}(代码 ${stats.codeLines} / 注释 ${stats.commentLines} / 空行 ${stats.blankLines}`,
`- 最长行: ${stats.maxLineLength} 字符`,
`- 最大嵌套深度: ${stats.maxNestingDepth}`,
``,
`## 自动检测到的问题(${issues.length} 项)`,
issues.length > 0 ? issues.map((i) => `- ${i}`).join("\n") : "- ✅ 未检测到明显问题",
``,
`## 人工审查清单`,
`请逐项检查以下内容:`,
...checklist.map((item, idx) => `${idx + 1}. ${item}`),
``,
`## 待审查代码`,
"```" + language,
code,
"```",
];
return output.join("\n");
}

View File

@@ -0,0 +1,200 @@
export const codeWriteTool = {
name: "code_write",
description:
"根据需求描述生成代码编写指引。返回结构化的需求分析、技术方案、代码骨架和最佳实践建议。",
inputSchema: {
type: "object" as const,
properties: {
requirement: {
type: "string",
description: "功能需求描述",
},
language: {
type: "string",
description: "编程语言(如 typescript, python, java 等)",
},
framework: {
type: "string",
description: "框架(可选,如 react, express, fastapi 等)",
},
context: {
type: "string",
description: "额外上下文(可选,如现有代码片段、接口定义等)",
},
},
required: ["requirement", "language"],
},
};
const languageBestPractices: Record<string, string[]> = {
typescript: [
"使用严格类型,避免 any",
"用 interface/type 定义数据结构",
"async/await 处理异步",
"使用 const 优先,必要时 let",
"错误处理使用 try-catch + 自定义 Error 类",
],
javascript: [
"使用 const/let禁用 var",
"使用 JSDoc 注释函数签名",
"async/await 处理异步",
"使用解构赋值简化代码",
"模块化:每个文件单一职责",
],
python: [
"遵循 PEP 8 风格规范",
"使用 type hints 标注类型",
"使用 dataclass/Pydantic 定义数据模型",
"用 with 语句管理资源",
"异常处理:捕获具体异常而非 Exception",
],
java: [
"遵循 Java 命名约定",
"使用 Optional 避免 NullPointerException",
"用 Stream API 处理集合",
"接口优先于实现",
"使用 Lombok 减少样板代码",
],
};
const frameworkTemplates: Record<string, string> = {
react: `// React 组件骨架
import React, { useState, useEffect } from 'react';
interface Props {
// TODO: 定义 props
}
export function ComponentName({ }: Props) {
const [state, setState] = useState();
useEffect(() => {
// TODO: 副作用逻辑
}, []);
return (
<div>
{/* TODO: JSX 结构 */}
</div>
);
}`,
express: `// Express 路由骨架
import express from 'express';
const router = express.Router();
router.get('/', async (req, res) => {
try {
// TODO: 业务逻辑
res.json({ success: true, data: {} });
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
});
export default router;`,
fastapi: `# FastAPI 路由骨架
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
router = APIRouter()
class RequestModel(BaseModel):
# TODO: 定义请求模型
pass
class ResponseModel(BaseModel):
success: bool
data: dict = {}
@router.get("/", response_model=ResponseModel)
async def handler():
try:
# TODO: 业务逻辑
return ResponseModel(success=True, data={})
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))`,
vue: `<!-- Vue 组件骨架 -->
<template>
<div>
<!-- TODO: 模板结构 -->
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
// TODO: 定义响应式状态
const state = ref();
onMounted(() => {
// TODO: 初始化逻辑
});
</script>`,
};
export async function executeCodeWrite(args: {
requirement: string;
language: string;
framework?: string;
context?: string;
}): Promise<string> {
const { requirement, language, framework, context } = args;
const practices = languageBestPractices[language.toLowerCase()] || [
"遵循语言社区标准",
"添加类型标注",
"做好错误处理",
"保持函数单一职责",
];
const output: string[] = [
`# 代码编写指引`,
``,
`## 需求`,
requirement,
``,
`## 技术规格`,
`- **语言**: ${language}`,
];
if (framework) {
output.push(`- **框架**: ${framework}`);
}
output.push(
``,
`## ${language} 最佳实践`,
...practices.map((p) => `- ${p}`),
``
);
if (framework && frameworkTemplates[framework.toLowerCase()]) {
output.push(
`## 代码骨架模板`,
"```" + language,
frameworkTemplates[framework.toLowerCase()],
"```",
``
);
}
output.push(
`## 编写要求`,
`1. 代码必须完整可运行,包含所有必要的 import`,
`2. 遵循 ${language} 最佳实践和命名规范`,
`3. 关键逻辑添加简洁注释`,
`4. 包含必要的错误处理和边界检查`,
`5. 函数保持单一职责,不超过 50 行`,
``
);
if (context) {
output.push(`## 参考上下文`, "```", context, "```", ``);
}
output.push(
`## 请根据以上指引生成完整代码`
);
return output.join("\n");
}

View File

@@ -0,0 +1,183 @@
import { exec } from "child_process";
import { promisify } from "util";
import { existsSync, readFileSync } from "fs";
import { join } from "path";
const execAsync = promisify(exec);
export const depManageTool = {
name: "dep_manage",
description:
"依赖管理。安装/更新/删除依赖、检查过时包、安全漏洞审计、分析 bundle 大小。支持 npm 和 pip。",
inputSchema: {
type: "object" as const,
properties: {
project_path: {
type: "string",
description: "项目根目录(绝对路径)",
},
action: {
type: "string",
description: "操作类型",
enum: ["install", "add", "remove", "update", "outdated", "audit", "list", "why"],
},
packages: {
type: "string",
description: "包名多个用空格分隔add/remove/why 时必填",
},
dev: {
type: "boolean",
description: "是否为开发依赖(默认 false",
},
},
required: ["project_path", "action"],
},
};
async function run(cmd: string, cwd: string, timeout = 120000): Promise<{ stdout: string; stderr: string; code: number }> {
try {
const { stdout, stderr } = await execAsync(cmd, {
cwd, timeout,
maxBuffer: 1024 * 1024 * 10,
shell: process.platform === "win32" ? "powershell.exe" : "/bin/bash",
});
return { stdout, stderr, code: 0 };
} catch (error: any) {
return { stdout: error.stdout || "", stderr: error.stderr || "", code: error.code ?? 1 };
}
}
export async function executeDepManage(args: {
project_path: string;
action: string;
packages?: string;
dev?: boolean;
}): Promise<string> {
const { project_path, action, packages, dev = false } = args;
const hasFile = (name: string) => existsSync(join(project_path, name));
const isNode = hasFile("package.json");
const isPython = hasFile("requirements.txt") || hasFile("pyproject.toml");
if (!isNode && !isPython) {
return "❌ 未检测到 package.json 或 requirements.txt";
}
let cmd = "";
let title = "";
if (isNode) {
switch (action) {
case "install":
cmd = "npm install";
title = "npm install";
break;
case "add":
if (!packages) return "❌ add 需要 packages 参数";
cmd = `npm install ${packages}${dev ? " --save-dev" : ""}`;
title = `npm install ${packages}${dev ? " (dev)" : ""}`;
break;
case "remove":
if (!packages) return "❌ remove 需要 packages 参数";
cmd = `npm uninstall ${packages}`;
title = `npm uninstall ${packages}`;
break;
case "update":
cmd = packages ? `npm update ${packages}` : "npm update";
title = `npm update${packages ? ` ${packages}` : ""}`;
break;
case "outdated":
cmd = "npm outdated --long 2>&1 || true";
title = "npm outdated过时依赖检查";
break;
case "audit":
cmd = "npm audit 2>&1 || true";
title = "npm audit安全漏洞审计";
break;
case "list":
cmd = "npm list --depth=0 2>&1";
title = "npm list已安装依赖";
break;
case "why":
if (!packages) return "❌ why 需要 packages 参数";
cmd = `npm why ${packages} 2>&1`;
title = `npm why ${packages}`;
break;
}
} else if (isPython) {
switch (action) {
case "install":
cmd = hasFile("requirements.txt") ? "pip install -r requirements.txt" : "pip install -e .";
title = "pip install";
break;
case "add":
if (!packages) return "❌ add 需要 packages 参数";
cmd = `pip install ${packages}`;
title = `pip install ${packages}`;
break;
case "remove":
if (!packages) return "❌ remove 需要 packages 参数";
cmd = `pip uninstall -y ${packages}`;
title = `pip uninstall ${packages}`;
break;
case "update":
cmd = packages ? `pip install --upgrade ${packages}` : "pip install --upgrade -r requirements.txt";
title = `pip upgrade${packages ? ` ${packages}` : ""}`;
break;
case "outdated":
cmd = "pip list --outdated 2>&1";
title = "pip outdated";
break;
case "audit":
cmd = "pip-audit 2>&1 || pip check 2>&1";
title = "pip audit / check";
break;
case "list":
cmd = "pip list 2>&1";
title = "pip list";
break;
case "why":
if (!packages) return "❌ why 需要 packages 参数";
cmd = `pip show ${packages} 2>&1`;
title = `pip show ${packages}`;
break;
}
}
if (!cmd) return `❌ 未知操作: ${action}`;
const result = await run(cmd, project_path);
const fullOutput = [result.stdout, result.stderr].filter(Boolean).join("\n").trim();
const icon = result.code === 0 ? "✅" : "⚠️";
const output: string[] = [
`# ${icon} ${title}`,
``,
`📂 ${project_path}`,
`📦 ${isNode ? "npm" : "pip"}`,
``,
"```",
fullOutput.slice(0, 6000) || "(无输出)",
"```",
];
// audit 额外解析
if (action === "audit" && isNode) {
const criticalMatch = fullOutput.match(/(\d+)\s+(critical|high)/gi);
if (criticalMatch && criticalMatch.length > 0) {
output.push(``, `⚠️ **发现高危漏洞!** 建议运行 \`npm audit fix\`\`npm audit fix --force\``);
} else if (result.code === 0) {
output.push(``, `✅ 未发现已知安全漏洞`);
}
}
// outdated 额外解析
if (action === "outdated" && fullOutput.trim()) {
const lines = fullOutput.trim().split("\n").filter((l) => l.trim());
if (lines.length > 1) {
output.push(``, `📊 发现 ${lines.length - 1} 个可更新的包`);
output.push(`💡 运行 \`dep_manage action=update\` 更新所有包`);
}
}
return output.join("\n");
}

View File

@@ -0,0 +1,271 @@
import { exec, spawn, ChildProcess } from "child_process";
import { promisify } from "util";
import { existsSync, readFileSync } from "fs";
import { join } from "path";
const execAsync = promisify(exec);
// 进程管理器 - 跟踪所有启动的开发服务器
const managedProcesses: Map<string, {
process: ChildProcess;
command: string;
cwd: string;
startTime: number;
logs: string[];
}> = new Map();
export const devServerTool = {
name: "dev_server",
description:
"开发服务器管理。启动/停止/重启开发服务器,查看运行状态和实时日志。支持自动检测项目的 dev 命令。",
inputSchema: {
type: "object" as const,
properties: {
project_path: {
type: "string",
description: "项目根目录(绝对路径)",
},
action: {
type: "string",
description: "操作类型",
enum: ["start", "stop", "restart", "status", "logs", "list"],
},
command: {
type: "string",
description: "自定义启动命令(可选,默认自动检测 npm run dev 等)",
},
name: {
type: "string",
description: "服务器名称/标识(可选,默认使用目录名)",
},
tail: {
type: "number",
description: "显示最近几行日志(默认 30",
},
},
required: ["action"],
},
};
function getServerName(projectPath?: string, name?: string): string {
if (name) return name;
if (projectPath) return projectPath.split(/[/\\]/).pop() || "server";
return "default";
}
function detectDevCommand(projectPath: string): string | null {
const pkgPath = join(projectPath, "package.json");
if (existsSync(pkgPath)) {
try {
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
const scripts = pkg.scripts || {};
// 按优先级检测
for (const name of ["dev", "start:dev", "serve", "start"]) {
if (scripts[name]) return `npm run ${name}`;
}
} catch {}
}
if (existsSync(join(projectPath, "manage.py"))) {
return "python manage.py runserver";
}
if (existsSync(join(projectPath, "main.py"))) {
return "python main.py";
}
if (existsSync(join(projectPath, "app.py"))) {
return "python app.py";
}
return null;
}
export async function executeDevServer(args: {
project_path?: string;
action: string;
command?: string;
name?: string;
tail?: number;
}): Promise<string> {
const { project_path, action, command, name, tail = 30 } = args;
switch (action) {
case "list": {
if (managedProcesses.size === 0) {
return "# 开发服务器\n\n_没有正在运行的服务器_";
}
const output: string[] = ["# 运行中的开发服务器", ""];
for (const [key, info] of managedProcesses) {
const running = !info.process.killed && info.process.exitCode === null;
const uptime = Math.round((Date.now() - info.startTime) / 1000);
output.push(
`## ${running ? "🟢" : "🔴"} ${key}`,
`- 命令: \`${info.command}\``,
`- 目录: ${info.cwd}`,
`- PID: ${info.process.pid}`,
`- 运行时间: ${uptime}s`,
`- 状态: ${running ? "运行中" : "已停止"}`,
``
);
}
return output.join("\n");
}
case "start": {
if (!project_path) return "❌ start 需要 project_path 参数";
const serverName = getServerName(project_path, name);
const existing = managedProcesses.get(serverName);
if (existing && !existing.process.killed && existing.process.exitCode === null) {
return `⚠️ 服务器 "${serverName}" 已在运行中 (PID: ${existing.process.pid})\n\n使用 action=restart 重启,或 action=stop 先停止`;
}
const startCmd = command || detectDevCommand(project_path);
if (!startCmd) {
return `❌ 未检测到启动命令,请手动指定 command 参数\n\n常见命令:\n- npm run dev\n- npm start\n- python app.py`;
}
// 解析命令
const isWin = process.platform === "win32";
const child = spawn(isWin ? "cmd" : "sh", [isWin ? "/c" : "-c", startCmd], {
cwd: project_path,
stdio: ["ignore", "pipe", "pipe"],
detached: false,
});
const logs: string[] = [];
const maxLogs = 200;
const addLog = (data: Buffer, stream: string) => {
const lines = data.toString().split("\n").filter(Boolean);
for (const line of lines) {
logs.push(`[${stream}] ${line}`);
if (logs.length > maxLogs) logs.shift();
}
};
child.stdout?.on("data", (data) => addLog(data, "out"));
child.stderr?.on("data", (data) => addLog(data, "err"));
managedProcesses.set(serverName, {
process: child,
command: startCmd,
cwd: project_path,
startTime: Date.now(),
logs,
});
// 等待一会检查是否立即崩溃
await new Promise((r) => setTimeout(r, 2000));
const crashed = child.exitCode !== null;
if (crashed) {
const output = logs.join("\n");
managedProcesses.delete(serverName);
return `# ❌ 服务器启动失败\n\n命令: \`${startCmd}\`\n退出码: ${child.exitCode}\n\n\`\`\`\n${output.slice(0, 3000)}\n\`\`\``;
}
return [
`# ✅ 服务器已启动`,
``,
`- 名称: ${serverName}`,
`- 命令: \`${startCmd}\``,
`- PID: ${child.pid}`,
`- 目录: ${project_path}`,
``,
`最近日志:`,
"```",
logs.slice(-10).join("\n") || "(等待输出...)",
"```",
``,
`💡 使用 \`dev_server action=logs\` 查看实时日志`,
].join("\n");
}
case "stop": {
const serverName = getServerName(project_path, name);
const info = managedProcesses.get(serverName);
if (!info) {
return `❌ 未找到服务器 "${serverName}"\n\n使用 action=list 查看所有运行中的服务器`;
}
const pid = info.process.pid;
try {
// Windows 需要 taskkill 杀进程树
if (process.platform === "win32" && pid) {
await execAsync(`taskkill /PID ${pid} /T /F`).catch(() => {});
} else {
info.process.kill("SIGTERM");
}
} catch {}
managedProcesses.delete(serverName);
return `# ✅ 服务器已停止\n\n- 名称: ${serverName}\n- PID: ${pid}`;
}
case "restart": {
const serverName = getServerName(project_path, name);
const info = managedProcesses.get(serverName);
if (info) {
try {
if (process.platform === "win32" && info.process.pid) {
await execAsync(`taskkill /PID ${info.process.pid} /T /F`).catch(() => {});
} else {
info.process.kill("SIGTERM");
}
} catch {}
managedProcesses.delete(serverName);
await new Promise((r) => setTimeout(r, 1000));
}
// 重新启动
return executeDevServer({
project_path: info?.cwd || project_path,
action: "start",
command: command || info?.command,
name: serverName,
});
}
case "status": {
const serverName = getServerName(project_path, name);
const info = managedProcesses.get(serverName);
if (!info) {
return `❌ 未找到服务器 "${serverName}"`;
}
const running = !info.process.killed && info.process.exitCode === null;
const uptime = Math.round((Date.now() - info.startTime) / 1000);
return [
`# ${running ? "🟢" : "🔴"} ${serverName}`,
``,
`- 状态: ${running ? "运行中" : `已停止 (退出码: ${info.process.exitCode})`}`,
`- 命令: \`${info.command}\``,
`- PID: ${info.process.pid}`,
`- 运行时间: ${uptime}s`,
`- 目录: ${info.cwd}`,
].join("\n");
}
case "logs": {
const serverName = getServerName(project_path, name);
const info = managedProcesses.get(serverName);
if (!info) {
return `❌ 未找到服务器 "${serverName}"`;
}
const recentLogs = info.logs.slice(-tail);
return [
`# 📋 ${serverName} 日志(最近 ${tail} 行)`,
``,
"```",
recentLogs.join("\n") || "(无日志)",
"```",
].join("\n");
}
default:
return `❌ 未知操作: ${action}`;
}
}

View File

@@ -0,0 +1,209 @@
export const docWriteTool = {
name: "doc_write",
description:
"为代码生成文档指引。分析代码结构,提取函数/类/接口信息,返回结构化的文档框架和编写指引。",
inputSchema: {
type: "object" as const,
properties: {
code: {
type: "string",
description: "需要生成文档的代码",
},
doc_type: {
type: "string",
description: "文档类型",
enum: ["readme", "api", "inline", "changelog", "jsdoc"],
},
language: {
type: "string",
description: "编程语言",
},
project_name: {
type: "string",
description: "项目名称(可选,用于 README",
},
extra_info: {
type: "string",
description: "额外信息(可选,如版本号、变更内容等)",
},
},
required: ["code", "doc_type", "language"],
},
};
interface CodeSymbol {
type: "function" | "class" | "interface" | "variable" | "route" | "export";
name: string;
line: number;
signature?: string;
}
function extractSymbols(code: string, language: string): CodeSymbol[] {
const symbols: CodeSymbol[] = [];
const lines = code.split("\n");
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
const lineNum = i + 1;
// 函数
let m: RegExpMatchArray | null;
if ((m = line.match(/(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)/))) {
symbols.push({ type: "function", name: m[1], line: lineNum, signature: m[0] });
}
// 箭头函数 / const 赋值
else if ((m = line.match(/(?:export\s+)?(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?\(?([^)]*)\)?\s*=>/))) {
symbols.push({ type: "function", name: m[1], line: lineNum, signature: m[0] });
}
// 类
else if ((m = line.match(/(?:export\s+)?class\s+(\w+)/))) {
symbols.push({ type: "class", name: m[1], line: lineNum });
}
// 接口 (TS)
else if ((m = line.match(/(?:export\s+)?interface\s+(\w+)/))) {
symbols.push({ type: "interface", name: m[1], line: lineNum });
}
// 路由 (Express/FastAPI)
else if ((m = line.match(/(?:router|app)\.(get|post|put|delete|patch)\s*\(\s*['"]([^'"]+)['"]/))) {
symbols.push({ type: "route", name: `${m[1].toUpperCase()} ${m[2]}`, line: lineNum });
}
// Python 函数
else if ((m = line.match(/(?:async\s+)?def\s+(\w+)\s*\(([^)]*)\)/))) {
symbols.push({ type: "function", name: m[1], line: lineNum, signature: m[0] });
}
// Python 类
else if ((m = line.match(/class\s+(\w+)(?:\(([^)]*)\))?:/))) {
symbols.push({ type: "class", name: m[1], line: lineNum });
}
// export default
else if ((m = line.match(/export\s+default\s+(\w+)/))) {
symbols.push({ type: "export", name: m[1], line: lineNum });
}
}
return symbols;
}
const docTypeInstructions: Record<string, string> = {
readme: `请生成 README.md包含以下章节
# 项目名称
## 简介(一句话描述)
## 功能特性
## 快速开始(安装、配置、运行)
## API / 使用说明
## 项目结构
## License`,
api: `请生成 API 文档,每个端点/函数包含:
- 描述
- 请求方法和路径(如适用)
- 参数(名称、类型、是否必填、描述)
- 返回值(类型、描述)
- 示例代码
- 错误码(如适用)`,
inline: `请为代码添加内联注释:
- 每个函数/方法添加文档注释JSDoc / docstring
- 关键逻辑添加简洁的解释性注释
- 不要过度注释显而易见的代码
- 返回带注释的完整代码`,
changelog: `请生成 CHANGELOG.md遵循 Keep a Changelog 格式:
## [版本号] - 日期
### Added新增
### Changed变更
### Fixed修复
### Removed移除`,
jsdoc: `请为所有函数/类/接口生成文档注释:
- @param 参数(类型、描述)
- @returns 返回值(类型、描述)
- @throws 可能的异常
- @example 使用示例
- 返回带完整文档注释的代码`,
};
export async function executeDocWrite(args: {
code: string;
doc_type: string;
language: string;
project_name?: string;
extra_info?: string;
}): Promise<string> {
const { code, doc_type, language, project_name, extra_info } = args;
const symbols = extractSymbols(code, language);
const lines = code.split("\n");
const output: string[] = [
`# 文档生成指引`,
``,
`**文档类型**: ${doc_type} | **语言**: ${language}${project_name ? ` | **项目**: ${project_name}` : ""}`,
``,
];
// 代码结构分析
output.push(`## 代码结构分析`, ``);
const functions = symbols.filter((s) => s.type === "function");
const classes = symbols.filter((s) => s.type === "class");
const interfaces = symbols.filter((s) => s.type === "interface");
const routes = symbols.filter((s) => s.type === "route");
if (functions.length > 0) {
output.push(`### 函数 (${functions.length})`);
for (const f of functions) {
output.push(`- **${f.name}** (行 ${f.line})${f.signature ? `: \`${f.signature}\`` : ""}`);
}
output.push(``);
}
if (classes.length > 0) {
output.push(`### 类 (${classes.length})`);
for (const c of classes) {
output.push(`- **${c.name}** (行 ${c.line})`);
}
output.push(``);
}
if (interfaces.length > 0) {
output.push(`### 接口 (${interfaces.length})`);
for (const i of interfaces) {
output.push(`- **${i.name}** (行 ${i.line})`);
}
output.push(``);
}
if (routes.length > 0) {
output.push(`### API 路由 (${routes.length})`);
for (const r of routes) {
output.push(`- **${r.name}** (行 ${r.line})`);
}
output.push(``);
}
if (symbols.length === 0) {
output.push(`_未检测到标准的函数/类/接口/路由定义_`, ``);
}
// 统计
output.push(
`### 文件统计`,
`- 总行数: ${lines.length}`,
`- 代码符号: ${symbols.length}`,
``
);
// 文档编写指引
const instruction = docTypeInstructions[doc_type] || docTypeInstructions.readme;
output.push(`## 文档编写指引`, ``, instruction, ``);
if (extra_info) {
output.push(`## 补充信息`, extra_info, ``);
}
// 附上源代码
output.push(`## 源代码`, "```" + language, code, "```");
return output.join("\n");
}

View File

@@ -0,0 +1,211 @@
import { exec } from "child_process";
import { promisify } from "util";
import { platform, totalmem, freemem, cpus, hostname, userInfo } from "os";
import { existsSync } from "fs";
const execAsync = promisify(exec);
export const envCheckTool = {
name: "env_check",
description:
"环境检测。检查 Node.js、Python、Git 等工具版本检测端口占用查看系统资源CPU/内存/磁盘),验证开发环境是否就绪。",
inputSchema: {
type: "object" as const,
properties: {
checks: {
type: "string",
description: "要检查的项目逗号分隔可选all, node, python, git, ports, system, docker。默认 all。",
},
ports: {
type: "string",
description: "要检测的端口号,逗号分隔(如 3000,8080,5173",
},
},
required: [],
},
};
async function getVersion(cmd: string): Promise<string> {
try {
const { stdout } = await execAsync(cmd, { timeout: 5000 });
return stdout.trim().split("\n")[0];
} catch {
return "❌ 未安装";
}
}
async function checkPort(port: number): Promise<boolean> {
try {
const cmd = process.platform === "win32"
? `netstat -ano | findstr :${port}`
: `lsof -i :${port} 2>/dev/null || ss -tlnp | grep :${port}`;
const { stdout } = await execAsync(cmd, { timeout: 5000 });
return stdout.trim().length > 0;
} catch {
return false;
}
}
async function getDiskSpace(): Promise<string> {
try {
const cmd = process.platform === "win32"
? 'powershell -command "Get-PSDrive -PSProvider FileSystem | Select-Object Name, @{N=\'Used(GB)\';E={[math]::Round($_.Used/1GB,1)}}, @{N=\'Free(GB)\';E={[math]::Round($_.Free/1GB,1)}} | Format-Table -AutoSize"'
: "df -h / /home 2>/dev/null";
const { stdout } = await execAsync(cmd, { timeout: 10000 });
return stdout.trim();
} catch {
return "无法获取";
}
}
export async function executeEnvCheck(args: {
checks?: string;
ports?: string;
}): Promise<string> {
const { checks = "all", ports } = args;
const checkList = checks.split(",").map((c) => c.trim().toLowerCase());
const doAll = checkList.includes("all");
const output: string[] = [
`# 环境检测报告`,
``,
`🖥️ ${hostname()} | ${platform()} | ${userInfo().username}`,
`📅 ${new Date().toLocaleString("zh-CN")}`,
``,
];
// 系统资源
if (doAll || checkList.includes("system")) {
const totalMem = (totalmem() / 1024 / 1024 / 1024).toFixed(1);
const freeMem = (freemem() / 1024 / 1024 / 1024).toFixed(1);
const usedMem = ((totalmem() - freemem()) / 1024 / 1024 / 1024).toFixed(1);
const memPercent = ((1 - freemem() / totalmem()) * 100).toFixed(0);
const cpuInfo = cpus();
const cpuModel = cpuInfo[0]?.model || "未知";
output.push(
`## 💻 系统资源`,
``,
`| 项目 | 值 |`,
`|------|------|`,
`| CPU | ${cpuModel} |`,
`| 核心数 | ${cpuInfo.length} |`,
`| 内存 | ${usedMem} / ${totalMem} GB (${memPercent}%) |`,
`| 空闲内存 | ${freeMem} GB |`,
``
);
const disk = await getDiskSpace();
if (disk !== "无法获取") {
output.push(`### 磁盘空间`, "```", disk, "```", ``);
}
}
// Node.js
if (doAll || checkList.includes("node")) {
const [nodeVer, npmVer, npxVer, yarnVer, pnpmVer] = await Promise.all([
getVersion("node --version"),
getVersion("npm --version"),
getVersion("npx --version"),
getVersion("yarn --version"),
getVersion("pnpm --version"),
]);
output.push(
`## 📦 Node.js 生态`,
``,
`| 工具 | 版本 |`,
`|------|------|`,
`| Node.js | ${nodeVer} |`,
`| npm | ${npmVer} |`,
`| npx | ${npxVer} |`,
`| yarn | ${yarnVer} |`,
`| pnpm | ${pnpmVer} |`,
``
);
}
// Python
if (doAll || checkList.includes("python")) {
const [pyVer, py3Ver, pipVer] = await Promise.all([
getVersion("python --version"),
getVersion("python3 --version"),
getVersion("pip --version"),
]);
output.push(
`## 🐍 Python 生态`,
``,
`| 工具 | 版本 |`,
`|------|------|`,
`| Python | ${pyVer} |`,
`| Python3 | ${py3Ver} |`,
`| pip | ${pipVer} |`,
``
);
}
// Git
if (doAll || checkList.includes("git")) {
const gitVer = await getVersion("git --version");
const gitUser = await getVersion("git config --global user.name");
const gitEmail = await getVersion("git config --global user.email");
output.push(
`## 🔧 Git`,
``,
`| 项目 | 值 |`,
`|------|------|`,
`| 版本 | ${gitVer} |`,
`| 用户名 | ${gitUser} |`,
`| 邮箱 | ${gitEmail} |`,
``
);
}
// Docker
if (doAll || checkList.includes("docker")) {
const [dockerVer, composeVer] = await Promise.all([
getVersion("docker --version"),
getVersion("docker compose version 2>&1 || docker-compose --version 2>&1"),
]);
output.push(
`## 🐳 Docker`,
``,
`| 工具 | 版本 |`,
`|------|------|`,
`| Docker | ${dockerVer} |`,
`| Compose | ${composeVer} |`,
``
);
}
// 端口检测
if (doAll || checkList.includes("ports") || ports) {
const portsToCheck = ports
? ports.split(",").map((p) => parseInt(p.trim())).filter(Boolean)
: [3000, 3001, 5173, 5174, 8080, 8081, 4000, 4173, 5000, 5500];
const portResults = await Promise.all(
portsToCheck.map(async (port) => ({
port,
occupied: await checkPort(port),
}))
);
output.push(
`## 🔌 端口状态`,
``,
`| 端口 | 状态 |`,
`|------|------|`,
...portResults.map((r) => `| ${r.port} | ${r.occupied ? "🔴 已占用" : "🟢 空闲"} |`),
``
);
}
// 总结
output.push(`---`, `_检测完成_`);
return output.join("\n");
}

View File

@@ -0,0 +1,67 @@
import { exec, spawn } from "child_process";
import { promisify } from "util";
const execAsync = promisify(exec);
export const execCommandTool = {
name: "exec_command",
description:
"在本地电脑上执行 Shell 命令。用于运行编译、测试、构建、安装依赖等任何命令。返回 stdout、stderr 和退出码。",
inputSchema: {
type: "object" as const,
properties: {
command: {
type: "string",
description: "要执行的命令(如 npm install, tsc --noEmit, python script.py",
},
cwd: {
type: "string",
description: "工作目录(绝对路径)",
},
timeout: {
type: "number",
description: "超时时间(毫秒),默认 60000",
},
},
required: ["command"],
},
};
export async function executeExecCommand(args: {
command: string;
cwd?: string;
timeout?: number;
}): Promise<string> {
const { command, cwd, timeout = 60000 } = args;
const workDir = cwd || process.cwd();
try {
const { stdout, stderr } = await execAsync(command, {
cwd: workDir,
timeout,
maxBuffer: 1024 * 1024 * 10, // 10MB
shell: process.platform === "win32" ? "powershell.exe" : "/bin/bash",
});
const output: string[] = [
`$ ${command}`,
`📂 ${workDir}`,
`✅ 退出码: 0`,
];
if (stdout.trim()) output.push(`\n--- stdout ---\n${stdout.trim()}`);
if (stderr.trim()) output.push(`\n--- stderr ---\n${stderr.trim()}`);
return output.join("\n");
} catch (error: any) {
const output: string[] = [
`$ ${command}`,
`📂 ${workDir}`,
`❌ 退出码: ${error.code ?? "unknown"}`,
];
if (error.stdout?.trim()) output.push(`\n--- stdout ---\n${error.stdout.trim()}`);
if (error.stderr?.trim()) output.push(`\n--- stderr ---\n${error.stderr.trim()}`);
if (error.killed) output.push(`\n⏰ 命令超时(${timeout}ms被终止`);
return output.join("\n");
}
}

View File

@@ -0,0 +1,191 @@
import { readFileSync, writeFileSync, existsSync, mkdirSync, watchFile, unwatchFile } from "fs";
import { dirname, join, extname } from "path";
export const readFileTool = {
name: "read_local_file",
description:
"读取本地文件内容。支持任意文本文件,返回带行号的内容。",
inputSchema: {
type: "object" as const,
properties: {
path: {
type: "string",
description: "文件绝对路径",
},
start_line: {
type: "number",
description: "起始行号(可选,从 1 开始)",
},
end_line: {
type: "number",
description: "结束行号(可选)",
},
},
required: ["path"],
},
};
export const writeFileTool = {
name: "write_local_file",
description:
"写入内容到本地文件。如果文件不存在会自动创建(含父目录)。支持完整写入或按行替换。",
inputSchema: {
type: "object" as const,
properties: {
path: {
type: "string",
description: "文件绝对路径",
},
content: {
type: "string",
description: "要写入的完整文件内容",
},
create_dirs: {
type: "boolean",
description: "是否自动创建父目录(默认 true",
},
},
required: ["path", "content"],
},
};
export const patchFileTool = {
name: "patch_file",
description:
"精确修改本地文件。查找指定文本并替换为新文本,支持多次替换。适合自动纠错和代码修改。",
inputSchema: {
type: "object" as const,
properties: {
path: {
type: "string",
description: "文件绝对路径",
},
old_text: {
type: "string",
description: "要查找并替换的原文本(必须精确匹配)",
},
new_text: {
type: "string",
description: "替换后的新文本",
},
replace_all: {
type: "boolean",
description: "是否替换所有匹配项(默认 false只替换第一个",
},
},
required: ["path", "old_text", "new_text"],
},
};
export async function executeReadFile(args: {
path: string;
start_line?: number;
end_line?: number;
}): Promise<string> {
const { path: filePath, start_line, end_line } = args;
if (!existsSync(filePath)) {
return `❌ 文件不存在: ${filePath}`;
}
try {
const content = readFileSync(filePath, "utf-8");
const lines = content.split("\n");
const start = (start_line || 1) - 1;
const end = end_line || lines.length;
const selected = lines.slice(start, end);
const numbered = selected.map((line, i) => `${String(start + i + 1).padStart(4)} | ${line}`).join("\n");
return [
`📄 ${filePath}`,
`行数: ${lines.length} | 显示: ${start + 1}-${Math.min(end, lines.length)}`,
`类型: ${extname(filePath) || "unknown"}`,
``,
numbered,
].join("\n");
} catch (error: any) {
return `❌ 读取失败: ${error.message}`;
}
}
export async function executeWriteFile(args: {
path: string;
content: string;
create_dirs?: boolean;
}): Promise<string> {
const { path: filePath, content, create_dirs = true } = args;
try {
if (create_dirs) {
const dir = dirname(filePath);
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}
}
const existed = existsSync(filePath);
const oldLines = existed ? readFileSync(filePath, "utf-8").split("\n").length : 0;
writeFileSync(filePath, content, "utf-8");
const newLines = content.split("\n").length;
return [
`✅ 文件已${existed ? "更新" : "创建"}: ${filePath}`,
existed ? `行数变化: ${oldLines}${newLines}` : `行数: ${newLines}`,
].join("\n");
} catch (error: any) {
return `❌ 写入失败: ${error.message}`;
}
}
export async function executePatchFile(args: {
path: string;
old_text: string;
new_text: string;
replace_all?: boolean;
}): Promise<string> {
const { path: filePath, old_text, new_text, replace_all = false } = args;
if (!existsSync(filePath)) {
return `❌ 文件不存在: ${filePath}`;
}
try {
const content = readFileSync(filePath, "utf-8");
if (!content.includes(old_text)) {
return `❌ 未找到匹配文本。\n\n搜索内容:\n\`\`\`\n${old_text.slice(0, 200)}\n\`\`\`\n\n提示: old_text 必须精确匹配文件中的内容(包括空格和换行)`;
}
const occurrences = content.split(old_text).length - 1;
let newContent: string;
if (replace_all) {
newContent = content.split(old_text).join(new_text);
} else {
const idx = content.indexOf(old_text);
newContent = content.substring(0, idx) + new_text + content.substring(idx + old_text.length);
}
writeFileSync(filePath, newContent, "utf-8");
const replacedCount = replace_all ? occurrences : 1;
return [
`✅ 文件已修改: ${filePath}`,
`替换: ${replacedCount}${occurrences > 1 && !replace_all ? `(共找到 ${occurrences} 处匹配,仅替换第 1 处)` : ""}`,
``,
`--- 旧代码 ---`,
"```",
old_text.slice(0, 500),
"```",
``,
`+++ 新代码 +++`,
"```",
new_text.slice(0, 500),
"```",
].join("\n");
} catch (error: any) {
return `❌ 修改失败: ${error.message}`;
}
}

View File

@@ -0,0 +1,198 @@
import { exec } from "child_process";
import { promisify } from "util";
const execAsync = promisify(exec);
export const gitOpsTool = {
name: "git_ops",
description:
"Git 全套操作。支持 status、diff、add、commit、push、pull、log、branch、checkout、stash、reset 等所有常用 Git 命令。自动解析输出为结构化报告。",
inputSchema: {
type: "object" as const,
properties: {
project_path: {
type: "string",
description: "项目根目录(绝对路径)",
},
action: {
type: "string",
description: "Git 操作",
enum: [
"status", "diff", "diff_staged", "add", "add_all", "commit",
"push", "pull", "log", "branch", "branch_create", "checkout",
"stash", "stash_pop", "reset_soft", "reset_hard", "remote", "init",
],
},
message: {
type: "string",
description: "commit 消息commit 时必填)",
},
target: {
type: "string",
description: "目标参数(文件路径/分支名/commit hash 等)",
},
count: {
type: "number",
description: "log 显示条数(默认 10",
},
},
required: ["project_path", "action"],
},
};
async function git(args: string, cwd: string): Promise<{ stdout: string; stderr: string; code: number }> {
try {
const { stdout, stderr } = await execAsync(`git ${args}`, {
cwd,
timeout: 30000,
maxBuffer: 1024 * 1024 * 5,
});
return { stdout, stderr, code: 0 };
} catch (error: any) {
return { stdout: error.stdout || "", stderr: error.stderr || "", code: error.code ?? 1 };
}
}
export async function executeGitOps(args: {
project_path: string;
action: string;
message?: string;
target?: string;
count?: number;
}): Promise<string> {
const { project_path, action, message, target, count = 10 } = args;
let result: { stdout: string; stderr: string; code: number };
let title = "";
switch (action) {
case "status":
title = "Git Status";
result = await git("status --short --branch", project_path);
if (result.code === 0) {
const lines = result.stdout.trim().split("\n");
const branch = lines[0] || "";
const changes = lines.slice(1);
const staged = changes.filter((l) => /^[MADRC]/.test(l));
const unstaged = changes.filter((l) => /^.[MADRC]/.test(l));
const untracked = changes.filter((l) => l.startsWith("??"));
return [
`# ${title}`, `📂 ${project_path}`, `🌿 ${branch}`, ``,
`| 状态 | 数量 |`, `|------|------|`,
`| ✅ 已暂存 | ${staged.length} |`,
`| 📝 已修改 | ${unstaged.length} |`,
`| ❓ 未跟踪 | ${untracked.length} |`, ``,
changes.length > 0 ? "```\n" + result.stdout.trim() + "\n```" : "✨ 工作区干净",
].join("\n");
}
break;
case "diff":
title = "Git Diff (工作区)";
result = await git(`diff ${target ? `-- "${target}"` : ""} --stat`, project_path);
if (result.stdout.trim()) {
const detail = await git(`diff ${target ? `-- "${target}"` : ""} --no-color`, project_path);
return `# ${title}\n\n## 摘要\n\`\`\`\n${result.stdout.trim()}\n\`\`\`\n\n## 详细\n\`\`\`diff\n${detail.stdout.slice(0, 8000)}\n\`\`\``;
}
return `# ${title}\n\n✨ 无差异`;
case "diff_staged":
title = "Git Diff (暂存区)";
result = await git("diff --cached --stat", project_path);
if (result.stdout.trim()) {
const detail = await git("diff --cached --no-color", project_path);
return `# ${title}\n\n## 摘要\n\`\`\`\n${result.stdout.trim()}\n\`\`\`\n\n## 详细\n\`\`\`diff\n${detail.stdout.slice(0, 8000)}\n\`\`\``;
}
return `# ${title}\n\n✨ 暂存区无内容`;
case "add":
title = "Git Add";
result = await git(`add "${target || "."}"`, project_path);
break;
case "add_all":
title = "Git Add All";
result = await git("add -A", project_path);
break;
case "commit":
if (!message) return "❌ commit 需要提供 message 参数";
title = "Git Commit";
result = await git(`commit -m "${message.replace(/"/g, '\\"')}"`, project_path);
break;
case "push":
title = "Git Push";
result = await git(`push ${target || ""}`, project_path);
break;
case "pull":
title = "Git Pull";
result = await git(`pull ${target || ""}`, project_path);
break;
case "log":
title = "Git Log";
result = await git(`log --oneline --graph --decorate -n ${count}`, project_path);
if (result.code === 0) {
return `# ${title} (最近 ${count} 条)\n\n\`\`\`\n${result.stdout.trim()}\n\`\`\``;
}
break;
case "branch":
title = "Git Branch";
result = await git("branch -a -v", project_path);
break;
case "branch_create":
if (!target) return "❌ 创建分支需要提供 target分支名";
title = `Git Branch Create: ${target}`;
result = await git(`checkout -b "${target}"`, project_path);
break;
case "checkout":
if (!target) return "❌ checkout 需要提供 target分支名或文件";
title = `Git Checkout: ${target}`;
result = await git(`checkout "${target}"`, project_path);
break;
case "stash":
title = "Git Stash";
result = await git(`stash${message ? ` push -m "${message}"` : ""}`, project_path);
break;
case "stash_pop":
title = "Git Stash Pop";
result = await git("stash pop", project_path);
break;
case "reset_soft":
title = "Git Reset (soft)";
result = await git(`reset --soft ${target || "HEAD~1"}`, project_path);
break;
case "reset_hard":
title = "Git Reset (hard) ⚠️";
result = await git(`reset --hard ${target || "HEAD"}`, project_path);
break;
case "remote":
title = "Git Remote";
result = await git("remote -v", project_path);
break;
case "init":
title = "Git Init";
result = await git("init", project_path);
break;
default:
return `❌ 未知 Git 操作: ${action}`;
}
const output = [result.stdout, result.stderr].filter(Boolean).join("\n").trim();
const icon = result.code === 0 ? "✅" : "❌";
return `# ${icon} ${title}\n\n📂 ${project_path}\n\n\`\`\`\n${output || "(无输出)"}\n\`\`\``;
}

View File

@@ -0,0 +1,165 @@
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" as const,
properties: {
project_path: {
type: "string",
description: "项目根目录(绝对路径)",
},
fix: {
type: "boolean",
description: "是否自动修复可修复的问题(默认 false",
},
files: {
type: "string",
description: "指定检查的文件或 glob可选默认检查整个项目",
},
},
required: ["project_path"],
},
};
interface LintResult {
tool: string;
success: boolean;
errorCount: number;
warningCount: number;
output: string;
}
async function runCommand(cmd: string, cwd: string, timeout = 30000): Promise<{ stdout: string; stderr: string; code: number }> {
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: any) {
return {
stdout: error.stdout || "",
stderr: error.stderr || "",
code: error.code ?? 1,
};
}
}
export async function executeLintCheck(args: {
project_path: string;
fix?: boolean;
files?: string;
}): Promise<string> {
const { project_path, fix = false, files } = args;
const results: LintResult[] = [];
const hasFile = (name: string) => 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: string[] = [
`# 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");
}

View File

@@ -0,0 +1,184 @@
import { existsSync, readFileSync, readdirSync, statSync } from "fs";
import { join, extname, basename } from "path";
export const projectScanTool = {
name: "project_scan",
description:
"扫描分析项目结构。返回项目类型、技术栈、文件结构、依赖列表、可用脚本、配置文件等全局信息,帮助快速理解项目。",
inputSchema: {
type: "object" as const,
properties: {
project_path: {
type: "string",
description: "项目根目录(绝对路径)",
},
max_depth: {
type: "number",
description: "目录扫描最大深度(默认 3",
},
},
required: ["project_path"],
},
};
function scanDir(dir: string, depth: number, maxDepth: number, prefix: string = ""): string[] {
if (depth > maxDepth) return [];
const lines: string[] = [];
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: string, ext: string, depth = 0): number {
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: {
project_path: string;
max_depth?: number;
}): Promise<string> {
const { project_path, max_depth = 3 } = args;
const hasFile = (name: string) => existsSync(join(project_path, name));
const readJson = (name: string) => {
try { return JSON.parse(readFileSync(join(project_path, name), "utf-8")); } catch { return null; }
};
const output: string[] = [
`# 项目分析报告`,
``,
`📂 路径: ${project_path}`,
``,
];
// 项目类型和技术栈检测
const techStack: string[] = [];
const configs: string[] = [];
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: Record<string, number> = {};
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");
}

View File

@@ -0,0 +1,151 @@
import { exec } from "child_process";
import { promisify } from "util";
import { existsSync } from "fs";
import { join } from "path";
const execAsync = promisify(exec);
export const runTestsTool = {
name: "run_tests",
description:
"运行项目测试。自动检测测试框架Jest、Mocha、Pytest 等),执行测试并返回结构化的结果(通过/失败/跳过数量及失败详情)。",
inputSchema: {
type: "object" as const,
properties: {
project_path: {
type: "string",
description: "项目根目录(绝对路径)",
},
test_file: {
type: "string",
description: "指定测试文件(可选,默认运行全部测试)",
},
test_name: {
type: "string",
description: "指定测试名称或模式(可选)",
},
},
required: ["project_path"],
},
};
async function runCommand(cmd: string, cwd: string): Promise<{ stdout: string; stderr: string; code: number }> {
try {
const { stdout, stderr } = await execAsync(cmd, {
cwd,
timeout: 120000,
maxBuffer: 1024 * 1024 * 10,
shell: process.platform === "win32" ? "powershell.exe" : "/bin/bash",
});
return { stdout, stderr, code: 0 };
} catch (error: any) {
return {
stdout: error.stdout || "",
stderr: error.stderr || "",
code: error.code ?? 1,
};
}
}
export async function executeRunTests(args: {
project_path: string;
test_file?: string;
test_name?: string;
}): Promise<string> {
const { project_path, test_file, test_name } = args;
const hasFile = (name: string) => existsSync(join(project_path, name));
let testCmd = "";
let framework = "";
// 检测测试框架
if (hasFile("jest.config.js") || hasFile("jest.config.ts") || hasFile("jest.config.mjs")) {
framework = "Jest";
testCmd = "npx jest --verbose --no-coverage";
if (test_file) testCmd += ` "${test_file}"`;
if (test_name) testCmd += ` -t "${test_name}"`;
} else if (hasFile("vitest.config.ts") || hasFile("vitest.config.js")) {
framework = "Vitest";
testCmd = "npx vitest run --reporter verbose";
if (test_file) testCmd += ` "${test_file}"`;
} else if (hasFile("pytest.ini") || hasFile("pyproject.toml") || hasFile("setup.cfg")) {
framework = "Pytest";
testCmd = "python -m pytest -v";
if (test_file) testCmd += ` "${test_file}"`;
if (test_name) testCmd += ` -k "${test_name}"`;
} else if (hasFile("package.json")) {
// 尝试 package.json 中的 test 脚本
framework = "npm test";
testCmd = "npm test 2>&1";
} else if (hasFile("requirements.txt")) {
framework = "Pytest (default)";
testCmd = "python -m pytest -v";
if (test_file) testCmd += ` "${test_file}"`;
}
if (!testCmd) {
return `# 测试结果\n\n⚠ 未检测到测试框架\n项目路径: ${project_path}\n\n建议\n- JS/TS: npm install -D jest 或 vitest\n- Python: pip install pytest`;
}
const result = await runCommand(testCmd, project_path);
const fullOutput = (result.stdout + "\n" + result.stderr).trim();
// 解析测试结果
let passed = 0, failed = 0, skipped = 0;
// Jest / Vitest 格式
const jestMatch = fullOutput.match(/Tests:\s+(?:(\d+) failed,?\s*)?(?:(\d+) skipped,?\s*)?(?:(\d+) passed)?/);
if (jestMatch) {
failed = parseInt(jestMatch[1] || "0");
skipped = parseInt(jestMatch[2] || "0");
passed = parseInt(jestMatch[3] || "0");
}
// Pytest 格式
const pytestMatch = fullOutput.match(/(\d+) passed(?:.*?(\d+) failed)?(?:.*?(\d+) skipped)?/);
if (pytestMatch) {
passed = parseInt(pytestMatch[1] || "0");
failed = parseInt(pytestMatch[2] || "0");
skipped = parseInt(pytestMatch[3] || "0");
}
const total = passed + failed + skipped;
const allPassed = failed === 0 && result.code === 0;
const output: string[] = [
`# 测试报告`,
``,
`📂 项目: ${project_path}`,
`🔧 框架: ${framework}`,
``,
`## 结果`,
allPassed ? "✅ **全部通过**" : "❌ **存在失败**",
``,
`| 状态 | 数量 |`,
`|------|------|`,
`| ✅ 通过 | ${passed} |`,
`| ❌ 失败 | ${failed} |`,
`| ⏭️ 跳过 | ${skipped} |`,
`| 📊 总计 | ${total} |`,
``,
`## 命令`,
`\`${testCmd}\``,
``,
`## 完整输出`,
"```",
fullOutput.slice(0, 5000),
"```",
];
if (failed > 0) {
output.push(
``,
`## 建议`,
`1. 查看上方失败的测试用例详情`,
`2. 使用 code_debug 工具分析失败原因`,
`3. 修复后重新运行 run_tests 验证`,
);
}
return output.join("\n");
}

View File

@@ -0,0 +1,206 @@
import { exec } from "child_process";
import { promisify } from "util";
import { readdirSync, readFileSync, statSync } from "fs";
import { join, extname, relative } from "path";
const execAsync = promisify(exec);
export const searchCodeTool = {
name: "search_code",
description:
"在项目中搜索代码。支持正则表达式、文本搜索、文件名搜索。返回匹配的文件、行号和上下文。类似 grep/ripgrep。",
inputSchema: {
type: "object" as const,
properties: {
project_path: {
type: "string",
description: "项目根目录(绝对路径)",
},
query: {
type: "string",
description: "搜索内容(支持正则表达式)",
},
mode: {
type: "string",
description: "搜索模式content=代码内容默认、filename=文件名、symbol=函数/类名",
enum: ["content", "filename", "symbol"],
},
includes: {
type: "string",
description: "文件过滤 glob如 *.ts, *.py",
},
case_sensitive: {
type: "boolean",
description: "是否区分大小写(默认 false",
},
max_results: {
type: "number",
description: "最大结果数(默认 50",
},
},
required: ["project_path", "query"],
},
};
const SKIP_DIRS = new Set([
"node_modules", ".git", "__pycache__", "dist", "build", ".next",
".nuxt", "coverage", ".cache", ".tsbuildinfo", "vendor",
]);
const TEXT_EXTS = new Set([
".ts", ".tsx", ".js", ".jsx", ".py", ".java", ".go", ".rs", ".c", ".cpp", ".h",
".css", ".scss", ".less", ".html", ".vue", ".svelte", ".json", ".yaml", ".yml",
".toml", ".md", ".txt", ".sh", ".bat", ".ps1", ".sql", ".graphql", ".prisma",
".env", ".gitignore", ".eslintrc", ".prettierrc",
]);
interface SearchMatch {
file: string;
line: number;
content: string;
context_before?: string;
context_after?: string;
}
function searchInDir(
dir: string,
rootDir: string,
regex: RegExp,
mode: string,
includeExt: string | null,
results: SearchMatch[],
maxResults: number,
depth = 0
): void {
if (depth > 10 || results.length >= maxResults) return;
try {
const entries = readdirSync(dir);
for (const entry of entries) {
if (results.length >= maxResults) break;
if (SKIP_DIRS.has(entry) || entry.startsWith(".")) continue;
const fullPath = join(dir, entry);
try {
const stat = statSync(fullPath);
if (stat.isDirectory()) {
searchInDir(fullPath, rootDir, regex, mode, includeExt, results, maxResults, depth + 1);
} else if (stat.isFile()) {
const ext = extname(entry).toLowerCase();
// 文件名搜索
if (mode === "filename") {
if (regex.test(entry)) {
results.push({ file: relative(rootDir, fullPath), line: 0, content: entry });
}
continue;
}
// 内容搜索 - 只搜索文本文件
if (!TEXT_EXTS.has(ext) && ext !== "") continue;
if (includeExt && !entry.endsWith(includeExt.replace("*", ""))) continue;
if (stat.size > 1024 * 512) continue; // 跳过 >512KB 的文件
const content = readFileSync(fullPath, "utf-8");
const lines = content.split("\n");
for (let i = 0; i < lines.length; i++) {
if (results.length >= maxResults) break;
if (mode === "symbol") {
// 只匹配函数/类/接口定义
const line = lines[i];
if (/(?:function|class|interface|def|const|let|var|export)\s/.test(line) && regex.test(line)) {
results.push({
file: relative(rootDir, fullPath),
line: i + 1,
content: line.trim(),
context_before: i > 0 ? lines[i - 1].trim() : undefined,
context_after: i < lines.length - 1 ? lines[i + 1].trim() : undefined,
});
}
} else {
if (regex.test(lines[i])) {
results.push({
file: relative(rootDir, fullPath),
line: i + 1,
content: lines[i].trim(),
context_before: i > 0 ? lines[i - 1].trim() : undefined,
context_after: i < lines.length - 1 ? lines[i + 1].trim() : undefined,
});
}
}
}
}
} catch {}
}
} catch {}
}
export async function executeSearchCode(args: {
project_path: string;
query: string;
mode?: string;
includes?: string;
case_sensitive?: boolean;
max_results?: number;
}): Promise<string> {
const { project_path, query, mode = "content", includes, case_sensitive = false, max_results = 50 } = args;
const flags = case_sensitive ? "" : "i";
let regex: RegExp;
try {
regex = new RegExp(query, flags);
} catch {
regex = new RegExp(query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), flags);
}
const results: SearchMatch[] = [];
const includeExt = includes ? includes.replace("*", "") : null;
searchInDir(project_path, project_path, regex, mode, includeExt, results, max_results);
if (results.length === 0) {
return `# 搜索结果\n\n🔍 "${query}" (${mode})\n📂 ${project_path}\n\n_未找到匹配结果_`;
}
// 按文件分组
const grouped: Record<string, SearchMatch[]> = {};
for (const r of results) {
(grouped[r.file] || (grouped[r.file] = [])).push(r);
}
const fileCount = Object.keys(grouped).length;
const output: string[] = [
`# 搜索结果`,
``,
`🔍 "${query}" | 模式: ${mode}${includes ? ` | 过滤: ${includes}` : ""}`,
`📂 ${project_path}`,
`📊 ${results.length} 个匹配,${fileCount} 个文件`,
``,
];
for (const [file, matches] of Object.entries(grouped)) {
output.push(`## 📄 ${file} (${matches.length} 处)`);
for (const m of matches) {
if (mode === "filename") {
output.push(`- ${m.content}`);
} else {
output.push(`**行 ${m.line}:**`);
if (m.context_before) output.push(`\`\`\`\n ${m.context_before}`);
else output.push("```");
output.push(`${m.content}`);
if (m.context_after) output.push(` ${m.context_after}\n\`\`\``);
else output.push("```");
}
}
output.push(``);
}
if (results.length >= max_results) {
output.push(`\n⚠ 结果已截断(最大 ${max_results} 条),可增大 max_results 或缩小搜索范围`);
}
return output.join("\n");
}

View File

@@ -0,0 +1,241 @@
import { executeProjectScan } from "./projectScan.js";
import { executeLintCheck } from "./lintCheck.js";
import { executeAutoFix } from "./autoFix.js";
import { executeBuildProject } from "./buildProject.js";
import { executeRunTests } from "./runTests.js";
import { executeGitOps } from "./gitOps.js";
import { executeDepManage } from "./depManage.js";
export const workflowTool = {
name: "workflow",
description:
"自动化工作流编排。串联多个工具形成流水线一键执行完整开发流程。内置多种预设流程全量检查、CI 模拟、项目初始化等),也支持自定义步骤。",
inputSchema: {
type: "object" as const,
properties: {
project_path: {
type: "string",
description: "项目根目录(绝对路径)",
},
preset: {
type: "string",
description: "预设工作流",
enum: [
"full_check", // 全量检查scan → lint → build → test
"fix_and_verify", // 修复验证auto_fix → lint → build → test
"pre_commit", // 提交前检查lint → build → test → git status
"ci_simulate", // CI 模拟install → lint → build → test → audit
"quick_scan", // 快速扫描scan → lint
"deploy_prep", // 部署准备lint → build → test → git status
],
},
steps: {
type: "string",
description: "自定义步骤,逗号分隔(如 scan,lint,fix,build,test,git_status,audit。与 preset 二选一。",
},
stop_on_error: {
type: "boolean",
description: "遇到错误是否停止(默认 true",
},
},
required: ["project_path"],
},
};
interface StepResult {
name: string;
success: boolean;
duration: number;
output: string;
}
type StepFn = (projectPath: string) => Promise<string>;
const STEP_MAP: Record<string, { name: string; fn: StepFn }> = {
scan: {
name: "📋 项目扫描",
fn: (p) => executeProjectScan({ project_path: p }),
},
lint: {
name: "🔍 代码检查",
fn: (p) => executeLintCheck({ project_path: p }),
},
fix: {
name: "🔧 自动修复",
fn: (p) => executeAutoFix({ project_path: p }),
},
build: {
name: "🏗️ 构建项目",
fn: (p) => executeBuildProject({ project_path: p }),
},
test: {
name: "🧪 运行测试",
fn: (p) => executeRunTests({ project_path: p }),
},
git_status: {
name: "📊 Git 状态",
fn: (p) => executeGitOps({ project_path: p, action: "status" }),
},
git_diff: {
name: "📝 Git Diff",
fn: (p) => executeGitOps({ project_path: p, action: "diff" }),
},
audit: {
name: "🛡️ 安全审计",
fn: (p) => executeDepManage({ project_path: p, action: "audit" }),
},
outdated: {
name: "📦 过时依赖检查",
fn: (p) => executeDepManage({ project_path: p, action: "outdated" }),
},
install: {
name: "📥 安装依赖",
fn: (p) => executeDepManage({ project_path: p, action: "install" }),
},
};
const PRESETS: Record<string, string[]> = {
full_check: ["scan", "lint", "build", "test"],
fix_and_verify: ["fix", "lint", "build", "test"],
pre_commit: ["lint", "build", "test", "git_status"],
ci_simulate: ["install", "lint", "build", "test", "audit"],
quick_scan: ["scan", "lint"],
deploy_prep: ["lint", "build", "test", "git_status"],
};
export async function executeWorkflow(args: {
project_path: string;
preset?: string;
steps?: string;
stop_on_error?: boolean;
}): Promise<string> {
const { project_path, preset, steps, stop_on_error = true } = args;
// 确定步骤列表
let stepKeys: string[];
let workflowName: string;
if (steps) {
stepKeys = steps.split(",").map((s) => s.trim());
workflowName = "自定义工作流";
} else if (preset && PRESETS[preset]) {
stepKeys = PRESETS[preset];
workflowName = `预设: ${preset}`;
} else {
// 默认全量检查
stepKeys = PRESETS.full_check;
workflowName = "预设: full_check全量检查";
}
// 验证步骤
const invalidSteps = stepKeys.filter((k) => !STEP_MAP[k]);
if (invalidSteps.length > 0) {
return `❌ 未知步骤: ${invalidSteps.join(", ")}\n\n可用步骤: ${Object.keys(STEP_MAP).join(", ")}`;
}
const totalSteps = stepKeys.length;
const startTime = Date.now();
const results: StepResult[] = [];
const output: string[] = [
`# 🚀 工作流执行`,
``,
`📂 ${project_path}`,
`📋 ${workflowName}`,
`📊 共 ${totalSteps} 个步骤`,
`${stop_on_error ? "⛔ 遇错停止" : "⏭️ 遇错继续"}`,
``,
`---`,
``,
];
let hasError = false;
for (let i = 0; i < stepKeys.length; i++) {
const key = stepKeys[i];
const step = STEP_MAP[key];
output.push(`## [${i + 1}/${totalSteps}] ${step.name}`);
const stepStart = Date.now();
try {
const result = await step.fn(project_path);
const duration = Date.now() - stepStart;
// 判断步骤是否成功(检查输出中的错误标志)
const isError = result.includes("❌") && !result.includes("✅");
const stepSuccess = !isError;
results.push({
name: step.name,
success: stepSuccess,
duration,
output: result,
});
output.push(
`${stepSuccess ? "✅ 通过" : "❌ 失败"} (${(duration / 1000).toFixed(1)}s)`,
``,
`<details>`,
`<summary>查看详情</summary>`,
``,
result.slice(0, 3000),
``,
`</details>`,
``
);
if (!stepSuccess) {
hasError = true;
if (stop_on_error) {
output.push(`\n⛔ **遇到错误,工作流停止。** 后续步骤: ${stepKeys.slice(i + 1).join(" → ")}`);
break;
}
}
} catch (error: any) {
const duration = Date.now() - stepStart;
results.push({ name: step.name, success: false, duration, output: error.message });
output.push(`❌ 异常 (${(duration / 1000).toFixed(1)}s): ${error.message}`, ``);
hasError = true;
if (stop_on_error) {
output.push(`\n⛔ **工作流中断**`);
break;
}
}
}
// 总结报告
const totalDuration = Date.now() - startTime;
const passed = results.filter((r) => r.success).length;
const failed = results.filter((r) => !r.success).length;
output.push(
``,
`---`,
``,
`# 📊 工作流总结`,
``,
`| 项目 | 值 |`,
`|------|------|`,
`| 总耗时 | ${(totalDuration / 1000).toFixed(1)}s |`,
`| 已执行 | ${results.length}/${totalSteps} |`,
`| ✅ 通过 | ${passed} |`,
`| ❌ 失败 | ${failed} |`,
`| 最终结果 | ${hasError ? "❌ 有问题需要处理" : "✅ 全部通过"} |`,
``
);
if (hasError) {
output.push(
`## 💡 建议`,
`1. 查看失败步骤的详情`,
`2. 使用 \`auto_fix\` 自动修复格式问题`,
`3. 使用 \`code_debug\` 分析编译错误`,
`4. 修复后运行 \`workflow preset=fix_and_verify\` 验证`,
);
} else {
output.push(`✨ **所有检查通过,代码质量良好!**`);
}
return output.join("\n");
}