'use strict'; const fs = require('fs'); const path = require('path'); const { structuredPatch } = require('diff'); const { getContainerContext, resolveContainerPath, execContainerAction } = require('./container_bridge'); function resolvePath(workspace, p) { if (path.isAbsolute(p)) return p; return path.join(workspace, p); } function diffSummary(oldText, newText) { const patch = structuredPatch('old', 'new', oldText, newText, '', '', { context: 1 }); let added = 0; let removed = 0; const hunks = patch.hunks.map((hunk) => { let oldLine = hunk.oldStart; let newLine = hunk.newStart; const lines = hunk.lines.map((line) => { const prefix = line[0]; const content = line.slice(1); let lineNum = null; if (prefix === ' ') { lineNum = oldLine; oldLine += 1; newLine += 1; } else if (prefix === '-') { lineNum = oldLine; removed += 1; oldLine += 1; } else if (prefix === '+') { lineNum = newLine; added += 1; newLine += 1; } return { prefix, lineNum, content }; }); return lines; }); return { added, removed, hunks }; } function editFileTool(workspace, args) { const target = resolvePath(workspace, args.file_path); const oldString = args.old_string ?? ''; const newString = args.new_string ?? ''; const ctx = getContainerContext(); if (ctx) { const resolved = resolveContainerPath(workspace, args.file_path || '.', ctx); if (resolved.error) return { success: false, error: resolved.error }; const rel = resolved.relativePath; if (!oldString) { const writeResp = execContainerAction(ctx, 'write_file', { path: rel, content: newString || '', mode: 'w', }); if (!writeResp.success) return { success: false, error: writeResp.error || '写入失败' }; return { success: true, path: resolved.containerPath, replacements: newString ? 1 : 0, }; } const readResp = execContainerAction(ctx, 'read_file', { path: rel }); if (!readResp.success) return { success: false, error: readResp.error || '读取失败' }; const original = readResp.content || ''; if (!original.includes(oldString)) { return { success: false, error: 'old_string 未匹配到内容' }; } const updated = original.split(oldString).join(newString); const replacements = original.split(oldString).length - 1; const writeResp = execContainerAction(ctx, 'write_file', { path: rel, content: updated, mode: 'w', }); if (!writeResp.success) return { success: false, error: writeResp.error || '写入失败' }; const diff = diffSummary(original, updated); return { success: true, path: resolved.containerPath, replacements, diff, }; } try { let creating = false; if (!fs.existsSync(target)) { creating = true; fs.mkdirSync(path.dirname(target), { recursive: true }); fs.writeFileSync(target, '', 'utf8'); } const stat = fs.statSync(target); if (!stat.isFile()) { return { success: false, error: '目标不是文件' }; } const original = fs.readFileSync(target, 'utf8'); if (!creating && oldString === '') { return { success: false, error: 'old_string 不能为空,请从 read_file 内容中精确复制' }; } if (!creating && !original.includes(oldString)) { return { success: false, error: 'old_string 未匹配到内容' }; } const updated = creating ? newString : original.split(oldString).join(newString); let replacements = creating ? 0 : original.split(oldString).length - 1; if (creating && newString) replacements = 1; fs.writeFileSync(target, updated, 'utf8'); const diff = diffSummary(original, updated); return { success: true, path: target, replacements, diff, }; } catch (err) { return { success: false, error: err.message || String(err) }; } } module.exports = { editFileTool, resolvePath };