From 12115e4cdf2334cde8bc26dbc8b8afc454beb5fc Mon Sep 17 00:00:00 2001 From: can1357 Date: Thu, 14 May 2026 06:40:50 +0200 Subject: [PATCH] fix(utils): excluded ASCII replacements inside HTML comments - Added an HTML comment state tracker to prompt formatting. - Updated ASCII symbol replacement to skip substitutions inside `` comment blocks across lines. - Added tests that preserved comment text while converting symbols outside comments. --- .../coding-agent/test/prompt-format.test.ts | 24 ++++++++ packages/utils/src/prompt.ts | 59 ++++++++++++++++++- 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/packages/coding-agent/test/prompt-format.test.ts b/packages/coding-agent/test/prompt-format.test.ts index 65bc1548f..60ef39628 100644 --- a/packages/coding-agent/test/prompt-format.test.ts +++ b/packages/coding-agent/test/prompt-format.test.ts @@ -54,4 +54,28 @@ describe("prompt.format renderPhase", () => { '|`cat <<\'EOF\' > file`|`write(path="file", content="…")`|\n|`sed -i \'s/old/new/\' file`|`edit(path="file", edits=[…])`|', ); }); + test("does not mutate HTML comment markers when replacing ascii symbols", () => { + const input = ""; + const output = prompt.format(input, { + renderPhase: "pre-render", + replaceAsciiSymbols: true, + }); + expect(output).toBe(input); + }); + test("replaces arrows outside but not inside HTML comments", () => { + const input = "\nvalue -> value"; + const output = prompt.format(input, { + renderPhase: "pre-render", + replaceAsciiSymbols: true, + }); + expect(output).toBe("\nvalue → value"); + }); + test("handles multi-line HTML comments", () => { + const input = ""; + const output = prompt.format(input, { + renderPhase: "pre-render", + replaceAsciiSymbols: true, + }); + expect(output).toBe(""); + }); }); diff --git a/packages/utils/src/prompt.ts b/packages/utils/src/prompt.ts index 32fa9cb6a..ee751174d 100644 --- a/packages/utils/src/prompt.ts +++ b/packages/utils/src/prompt.ts @@ -66,6 +66,13 @@ function compactTableSep(line: string): string { return `|${normalized.join("|")}|`; } +const HTML_COMMENT_OPEN = ""; + +type HtmlCommentState = { + inHtmlComment: boolean; +}; + function replaceCommonAsciiSymbols(line: string): string { return line .replace(/\.{3}/g, "…") @@ -77,6 +84,54 @@ function replaceCommonAsciiSymbols(line: string): string { .replace(/>=/g, "≥"); } +function replaceCommonAsciiSymbolsOutsideHtmlComments( + line: string, + state: HtmlCommentState, +): string { + if ( + !state.inHtmlComment && + !line.includes(HTML_COMMENT_OPEN) && + !line.includes(HTML_COMMENT_CLOSE) + ) { + return replaceCommonAsciiSymbols(line); + } + + let result = ""; + let cursor = 0; + + while (cursor < line.length) { + if (state.inHtmlComment) { + const closeIndex = line.indexOf(HTML_COMMENT_CLOSE, cursor); + if (closeIndex === -1) { + return result + line.slice(cursor); + } + result += line.slice(cursor, closeIndex + HTML_COMMENT_CLOSE.length); + cursor = closeIndex + HTML_COMMENT_CLOSE.length; + state.inHtmlComment = false; + continue; + } + + const openIndex = line.indexOf(HTML_COMMENT_OPEN, cursor); + if (openIndex === -1) { + result += replaceCommonAsciiSymbols(line.slice(cursor)); + return result; + } + + result += replaceCommonAsciiSymbols(line.slice(cursor, openIndex)); + const closeIndex = line.indexOf(HTML_COMMENT_CLOSE, openIndex + HTML_COMMENT_OPEN.length); + if (closeIndex === -1) { + result += line.slice(openIndex); + state.inHtmlComment = true; + return result; + } + + result += line.slice(openIndex, closeIndex + HTML_COMMENT_CLOSE.length); + cursor = closeIndex + HTML_COMMENT_CLOSE.length; + } + + return result; +} + export function format(content: string, options: PromptFormatOptions = {}): string { const { renderPhase = "post-render", @@ -87,6 +142,8 @@ export function format(content: string, options: PromptFormatOptions = {}): stri const lines = content.split("\n"); const result: string[] = []; let inCodeBlock = false; + + const htmlCommentState: HtmlCommentState = { inHtmlComment: false }; const topLevelTags: string[] = []; for (let i = 0; i < lines.length; i++) { @@ -104,7 +161,7 @@ export function format(content: string, options: PromptFormatOptions = {}): stri } if (replaceAsciiSymbols) { - line = replaceCommonAsciiSymbols(line); + line = replaceCommonAsciiSymbolsOutsideHtmlComments(line, htmlCommentState); } trimmedStart = line.trimStart(); const trimmed = line.trim();