0-4. 学习目标与一句话
文件读写模块是 coding agent 真正改变工程的地方:把 read/edit/write 意图变成受权限保护、可诊断、可反馈的文件系统操作。来源:packages/opencode/src/tool/read.ts:200-260、edit.ts:88-208、write.ts:38-102。
Java 类比
Read/Edit/Write 像 Application Service;AppFileSystem 像 FileRepository;ctx.ask 是审批拦截器;LSP 是 IDE/编译器诊断服务。
5. 最小源码路径
tool/read.ts:29-39、200-260tool/edit.ts:47-65、88-208tool/write.ts:20-30、38-102tool/external-directory.ts:16-45lsp/lsp.ts:346-379
6. 用户输入到 agent 行动的整体链路
model read/edit/write tool-call
-> SessionTools.resolve execute
-> ReadTool/EditTool/WriteTool.execute
-> path resolve + external directory check
-> ctx.ask(read/edit)
-> fs read/write
-> format.file
-> File.Event.Edited / FileWatcher.Event.Updated
-> lsp.touchFile + diagnostics
-> ToolResult output7. 核心源码逐段讲解
ReadTool 权限
if (!path.isAbsolute(filepath)) {
filepath = path.resolve(instance.directory, filepath)
}
yield* assertExternalDirectoryEffect(ctx, filepath, { kind: stat?.type === "Directory" ? "directory" : "file" })
yield* ctx.ask({ permission: "read", patterns: [path.relative(instance.worktree, filepath)], always: ["*"] })路径:packages/opencode/src/tool/read.ts:200-232。
外部目录
if (containsPath(full, ins)) return
const glob = path.join(dir, "*").replaceAll("\\", "/")
yield* ctx.ask({ permission: "external_directory", patterns: [glob], always: [glob] })路径:packages/opencode/src/tool/external-directory.ts:16-45。
EditTool diff + ask + write
diff = trimDiff(createTwoFilesPatch(filePath, filePath, contentOld, contentNew))
yield* ctx.ask({ permission: "edit", patterns: [path.relative(instance.worktree, filePath)], metadata: { filepath: filePath, diff } })
yield* afs.writeWithDirs(filePath, Bom.join(contentNew, desiredBom))
if (yield* format.file(filePath)) {
contentNew = yield* Bom.syncFile(afs, filePath, desiredBom)
}路径:packages/opencode/src/tool/edit.ts:88-160。
LSP 反馈
yield* lsp.touchFile(filePath, "document")
const diagnostics = yield* lsp.diagnostics()
const block = LSP.Diagnostic.report(filePath, diagnostics[normalizedFilePath] ?? [])
if (block) output += `\n\nLSP errors detected in this file, please fix:\n${block}`路径:packages/opencode/src/tool/edit.ts:192-198。
WriteTool 项目诊断
WriteTool 写入后还会报告其他文件的 LSP errors。来源:packages/opencode/src/tool/write.ts:74-90。
8. 关键 TypeScript 语法复习
Schema.Struct定义工具参数。Effect.catchIf恢复 stat not found。- object literal metadata:
{ filepath, diff }。 - template literal 拼 diagnostics。
- 平台分支处理 Windows 路径。
9-10. 架构思想与协作
- Policy enforcement:外部目录 + read/edit 权限。
- Domain Event:文件编辑后发布 File/FileWatcher 事件。
- Feedback Loop:LSP diagnostics 进入 tool output。
- Provider 不直接参与文件工具执行;它主要影响工具 schema 暴露。
11. mini agent 对应代码
async function editFile(args, ctx) {
const filePath = resolveInsideProject(args.filePath, ctx.cwd)
const oldContent = await fs.readFile(filePath, "utf8")
const newContent = oldContent.replace(args.oldString, args.newString)
const diff = makeDiff(oldContent, newContent)
await ctx.ask({ permission: "edit", patterns: [relative(ctx.root, filePath)], metadata: { diff } })
await fs.writeFile(filePath, newContent)
return { output: await diagnostics(filePath) }
}12. 费曼复述区
- ReadTool 执行前有哪些边界检查?
- EditTool 为什么要生成 diff 再 ask?
- LSP diagnostics 为什么要进入 tool output?
13-15. 练习与自测
- 找到 ReadTool 参数。
- 找到 EditTool 申请 edit 权限的代码。
- 实现一个带 diff 和 ask 的 writeFile tool。
- 追踪
ReadTool.execute -> assertExternalDirectoryEffect。 - 回答:如何防止 agent 修改项目外文件?