159 lines
5.7 KiB
JavaScript
159 lines
5.7 KiB
JavaScript
|
|
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
|