diff --git a/src/core/tools/applyDiffTool.ts b/src/core/tools/applyDiffTool.ts index 903e3c846e..39ca4eeaaa 100644 --- a/src/core/tools/applyDiffTool.ts +++ b/src/core/tools/applyDiffTool.ts @@ -25,6 +25,8 @@ export async function applyDiffToolLegacy( const relPath: string | undefined = block.params.path let diffContent: string | undefined = block.params.diff + // Unescape HTML entities for non-Claude models (e.g., Gemini, DeepSeek, Llama) + // These models may return content with escaped characters that need to be unescaped if (diffContent && !cline.api.getModel().id.includes("claude")) { diffContent = unescapeHtmlEntities(diffContent) } diff --git a/src/core/tools/writeToFileTool.ts b/src/core/tools/writeToFileTool.ts index e82eab92bc..43701d6f95 100644 --- a/src/core/tools/writeToFileTool.ts +++ b/src/core/tools/writeToFileTool.ts @@ -73,9 +73,11 @@ export async function writeToFileTool( cline.diffViewProvider.editType = fileExists ? "modify" : "create" } - // pre-processing newContent for cases where weaker models might add artifacts like markdown codeblock markers (deepseek/llama) or extra escape characters (gemini) + // Pre-processing newContent for cases where models might add artifacts + // Some models (DeepSeek/Llama) add markdown codeblock markers + // Others (Gemini) return content with HTML-escaped characters if (newContent.startsWith("```")) { - // cline handles cases where it includes language specifiers like ```python ```js + // Handle cases where it includes language specifiers like ```python ```js newContent = newContent.split("\n").slice(1).join("\n") } @@ -83,6 +85,8 @@ export async function writeToFileTool( newContent = newContent.split("\n").slice(0, -1).join("\n") } + // Unescape HTML entities for non-Claude models (e.g., Gemini, DeepSeek, Llama) + // These models may return content with escaped characters that need to be unescaped if (!cline.api.getModel().id.includes("claude")) { newContent = unescapeHtmlEntities(newContent) } diff --git a/src/utils/__tests__/text-normalization-extended.spec.ts b/src/utils/__tests__/text-normalization-extended.spec.ts new file mode 100644 index 0000000000..06f6328a2c --- /dev/null +++ b/src/utils/__tests__/text-normalization-extended.spec.ts @@ -0,0 +1,57 @@ +import { describe, it, expect } from "vitest" +import { unescapeHtmlEntities } from "../text-normalization" + +describe("Extended HTML entity unescaping", () => { + describe("unescapeHtmlEntities", () => { + it("unescapes alternative apostrophe encoding", () => { + const input = "It's working" + const expected = "It's working" + expect(unescapeHtmlEntities(input)).toBe(expected) + }) + + it("unescapes forward slash", () => { + const input = "path/to/file" + const expected = "path/to/file" + expect(unescapeHtmlEntities(input)).toBe(expected) + }) + + it("unescapes backslash", () => { + const input = "C:\Users\file" + const expected = "C:\\Users\\file" + expect(unescapeHtmlEntities(input)).toBe(expected) + }) + + it("unescapes backtick", () => { + const input = "`code`" + const expected = "`code`" + expect(unescapeHtmlEntities(input)).toBe(expected) + }) + + it("unescapes non-breaking space", () => { + const input = "Hello World" + const expected = "Hello World" + expect(unescapeHtmlEntities(input)).toBe(expected) + }) + + it("handles complex mixed content with all entity types", () => { + const input = + "<div class="test">It's a test/path\file with `code` & more</div>" + const expected = '