diff --git a/src/agents/pi-tools.read.ts b/src/agents/pi-tools.read.ts index 93abd66f2d5..a5fb9a1ccd0 100644 --- a/src/agents/pi-tools.read.ts +++ b/src/agents/pi-tools.read.ts @@ -353,6 +353,7 @@ export const CLAUDE_PARAM_GROUPS = { { keys: ["newText", "new_string"], label: "newText (newText or new_string)", + allowEmpty: true, }, ], } as const; diff --git a/src/agents/pi-tools.workspace-paths.test.ts b/src/agents/pi-tools.workspace-paths.test.ts index 625c04227d3..969bc448caf 100644 --- a/src/agents/pi-tools.workspace-paths.test.ts +++ b/src/agents/pi-tools.workspace-paths.test.ts @@ -60,6 +60,31 @@ describe("workspace path resolution", () => { }); }); + it("allows deletion edits with empty newText", async () => { + await withTempDir("openclaw-ws-", async (workspaceDir) => { + await withTempDir("openclaw-cwd-", async (otherDir) => { + const testFile = "delete.txt"; + await fs.writeFile(path.join(workspaceDir, testFile), "hello world", "utf8"); + + const cwdSpy = vi.spyOn(process, "cwd").mockReturnValue(otherDir); + try { + const tools = createOpenClawCodingTools({ workspaceDir }); + const { editTool } = expectReadWriteEditTools(tools); + + await editTool.execute("ws-edit-delete", { + path: testFile, + oldText: " world", + newText: "", + }); + + expect(await fs.readFile(path.join(workspaceDir, testFile), "utf8")).toBe("hello"); + } finally { + cwdSpy.mockRestore(); + } + }); + }); + }); + it("defaults exec cwd to workspaceDir when workdir is omitted", async () => { await withTempDir("openclaw-ws-", async (workspaceDir) => { const tools = createOpenClawCodingTools({