write.ts 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. import z from "zod/v4"
  2. import * as path from "path"
  3. import { Tool } from "./tool"
  4. import { LSP } from "../lsp"
  5. import { Permission } from "../permission"
  6. import DESCRIPTION from "./write.txt"
  7. import { Bus } from "../bus"
  8. import { File } from "../file"
  9. import { FileTime } from "../file/time"
  10. import { Filesystem } from "../util/filesystem"
  11. import { Instance } from "../project/instance"
  12. import { Agent } from "../agent/agent"
  13. export const WriteTool = Tool.define("write", {
  14. description: DESCRIPTION,
  15. parameters: z.object({
  16. filePath: z.string().describe("The absolute path to the file to write (must be absolute, not relative)"),
  17. content: z.string().describe("The content to write to the file"),
  18. }),
  19. async execute(params, ctx) {
  20. const filepath = path.isAbsolute(params.filePath) ? params.filePath : path.join(Instance.directory, params.filePath)
  21. if (!Filesystem.contains(Instance.directory, filepath)) {
  22. throw new Error(`File ${filepath} is not in the current working directory`)
  23. }
  24. const file = Bun.file(filepath)
  25. const exists = await file.exists()
  26. if (exists) await FileTime.assert(ctx.sessionID, filepath)
  27. const agent = await Agent.get(ctx.agent)
  28. if (agent.permission.edit === "ask")
  29. await Permission.ask({
  30. type: "write",
  31. sessionID: ctx.sessionID,
  32. messageID: ctx.messageID,
  33. callID: ctx.callID,
  34. title: exists ? "Overwrite this file: " + filepath : "Create new file: " + filepath,
  35. metadata: {
  36. filePath: filepath,
  37. content: params.content,
  38. exists,
  39. },
  40. })
  41. await Bun.write(filepath, params.content)
  42. await Bus.publish(File.Event.Edited, {
  43. file: filepath,
  44. })
  45. FileTime.read(ctx.sessionID, filepath)
  46. let output = ""
  47. await LSP.touchFile(filepath, true)
  48. const diagnostics = await LSP.diagnostics()
  49. for (const [file, issues] of Object.entries(diagnostics)) {
  50. if (issues.length === 0) continue
  51. if (file === filepath) {
  52. output += `\nThis file has errors, please fix\n<file_diagnostics>\n${issues.map(LSP.Diagnostic.pretty).join("\n")}\n</file_diagnostics>\n`
  53. continue
  54. }
  55. output += `\n<project_diagnostics>\n${file}\n${issues.map(LSP.Diagnostic.pretty).join("\n")}\n</project_diagnostics>\n`
  56. }
  57. return {
  58. title: path.relative(Instance.worktree, filepath),
  59. metadata: {
  60. diagnostics,
  61. filepath,
  62. exists: exists,
  63. },
  64. output,
  65. }
  66. },
  67. })