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

159 lines
5.7 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 { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
import { dirname, extname } from "path";
export const readFileTool = {
name: "read_local_file",
description: "读取本地文件内容。支持任意文本文件,返回带行号的内容。",
inputSchema: {
type: "object",
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",
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",
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) {
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) {
return `❌ 读取失败: ${error.message}`;
}
}
export async function executeWriteFile(args) {
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) {
return `❌ 写入失败: ${error.message}`;
}
}
export async function executePatchFile(args) {
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;
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) {
return `❌ 修改失败: ${error.message}`;
}
}
//# sourceMappingURL=fileOps.js.map