Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/core/task/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ import { RepoPerTaskCheckpointService } from "../../services/checkpoints"

// integrations
import { DiffViewProvider } from "../../integrations/editor/DiffViewProvider"
import { findToolName, formatContentBlockToMarkdown } from "../../integrations/misc/export-markdown"
import { findToolName } from "../../integrations/misc/export-markdown"
import { formatContentBlockForUi } from "../../shared/formatContentBlockForUi"
import { RooTerminalProcess } from "../../integrations/terminal/types"
import { TerminalRegistry } from "../../integrations/terminal/TerminalRegistry"

Expand Down Expand Up @@ -1794,7 +1795,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
"api_req_started",
JSON.stringify({
request:
currentUserContent.map((block) => formatContentBlockToMarkdown(block)).join("\n\n") +
currentUserContent.map((block) => formatContentBlockForUi(block)).join("\n\n") +
"\n\nLoading...",
apiProtocol,
}),
Expand Down Expand Up @@ -1835,7 +1836,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
const lastApiReqIndex = findLastIndex(this.clineMessages, (m) => m.say === "api_req_started")

this.clineMessages[lastApiReqIndex].text = JSON.stringify({
request: finalUserContent.map((block) => formatContentBlockToMarkdown(block)).join("\n\n"),
request: finalUserContent.map((block) => formatContentBlockForUi(block)).join("\n\n"),
apiProtocol,
} satisfies ClineApiReqInfo)

Expand Down
179 changes: 179 additions & 0 deletions src/shared/formatContentBlockForUi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import { Anthropic } from "@anthropic-ai/sdk"

/**
* UI-safe formatter for ContentBlockParam that avoids including large or sensitive payloads
* such as full file contents, diffs, or oversized text in UI messages (ui_messages.json).
*
* IMPORTANT:
* - This is ONLY for rendering content into UI messages (e.g., api_req_started.request).
* - Do NOT use this for API conversation history or export; those need full fidelity.
*/
export function formatContentBlockForUi(block: Anthropic.Messages.ContentBlockParam): string {
switch (block.type) {
case "text":
return sanitizeText(block.text ?? "")
case "image":
return "[Image]"
case "tool_use":
return summarizeToolUse(block)
case "tool_result":
if (typeof block.content === "string") {
return sanitizeText(block.content)
} else if (Array.isArray(block.content)) {
// Recursively sanitize nested blocks
return block.content.map(formatContentBlockForUi).join("\n")
} else {
return "[Tool Result]"
}
default:
return `[${block.type}]`
}
}

/**
* Summarize tool_use without dumping large params (like diff/content).
*/
function summarizeToolUse(block: Anthropic.Messages.ToolUseBlockParam): string {
const name = block.name
// Try to extract relevant lightweight params for display
try {
const params = (block as any)?.input ?? (block as any)?.params ?? {}
// Prefer path if present
const directPath = params?.path as string | undefined

// For XML args (e.g., read_file, apply_diff multi-file), collect a small summary of paths
const xmlArgs = typeof params?.args === "string" ? params.args : undefined
const pathsFromXml = xmlArgs ? extractPathsFromXml(xmlArgs) : []

if (name === "read_file") {
const paths = directPath ? [directPath] : pathsFromXml
if (paths.length === 0) return `[Tool Use: ${name}]`
if (paths.length === 1) return `[Tool Use: ${name}] ${paths[0]}`
return `[Tool Use: ${name}] ${paths[0]} (+${paths.length - 1} more)`
}

if (
name === "apply_diff" ||
name === "insert_content" ||
name === "search_and_replace" ||
name === "write_to_file"
) {
const paths = directPath ? [directPath] : pathsFromXml
if (paths.length === 0) return `[Tool Use: ${name}]`
if (paths.length === 1) return `[Tool Use: ${name}] ${paths[0]}`
return `[Tool Use: ${name}] ${paths[0]} (+${paths.length - 1} more)`
}

if (name === "search_files") {
const regex = params?.regex ? ` regex="${String(params.regex)}"` : ""
const fp = params?.file_pattern ? ` file_pattern="${String(params.file_pattern)}"` : ""
const p = params?.path ? ` ${String(params.path)}` : ""
return `[Tool Use: ${name}]${p}${regex}${fp}`
}

// Default: show name only
return `[Tool Use: ${name}]`
} catch {
return `[Tool Use: ${block.name}]`
}
}

/**
* Sanitize a text chunk for UI:
* - Collapse <files> XML to a per-file summary (hide <content> bodies)
* - Truncate very long text
* - Redact obvious diff blobs
*/
function sanitizeText(text: string): string {
if (!text) return ""

// If this looks like a files XML, summarize paths/errors/notices and drop content bodies
if (text.includes("<files") || text.includes("<files")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate condition found in sanitizeText: checking text.includes('<files') twice. Remove the redundant check.

Suggested change
if (text.includes("<files") || text.includes("<files")) {
if (text.includes("<files")) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition checks for the unescaped <files tag twice. The second check should be for the escaped version &lt;files to properly detect escaped XML in text content. Without this, escaped XML tags won't be summarized and could bloat the UI messages.

return summarizeFilesXml(text)
}

// If this contains diff markers, replace with a short placeholder
if (looksLikeDiff(text)) {
return "[diff content omitted]"
}

// Generic truncation to keep UI light-weight
const MAX = 2000
if (text.length > MAX) {
const omitted = text.length - MAX
return `${text.slice(0, MAX)}\n[omitted ${omitted} chars]`
}

return text
}

function looksLikeDiff(s: string): boolean {
return (
s.includes("<<<<<<< SEARCH") ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant diff marker checks in looksLikeDiff: the same strings are checked twice. Consider removing the duplicate includes for '<<<<<<< SEARCH' and '>>>>>>> REPLACE'.

s.includes(">>>>>>> REPLACE") ||
s.includes("<<<<<<< SEARCH") ||
Comment on lines +112 to +114
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These lines check for the same unescaped diff markers twice. To properly handle both escaped and unescaped content, the second set should check for escaped versions (e.g., &lt;&lt;&lt;&lt;&lt;&lt;&lt; SEARCH and &gt;&gt;&gt;&gt;&gt;&gt;&gt; REPLACE) to detect diff content that may have been HTML-escaped in the text.

s.includes(">>>>>>> REPLACE") ||
/^diff --git/m.test(s)
)
}

/**
* Summarize a <files> XML payload by listing file paths and high-level status,
* but never including <content> bodies.
*/
function summarizeFilesXml(xmlLike: string): string {
// Support both escaped and unescaped tags
const decode = (s: string) => s.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The decode function does not actually decode HTML entities. It replaces '<' with '<', etc. Consider using proper replacement (e.g. replacing '&lt;' with '<', '&gt;' with '>', '&amp;' with '&').

Suggested change
const decode = (s: string) => s.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")
const decode = (s: string) => s.replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&')

Check warning

Code scanning / CodeQL

Replacement of a substring with itself Medium

This replaces '<' with itself.

Copilot Autofix

AI 3 months ago

To resolve this, we should properly decode XML/HTML entities in the string: replace &lt; with <, &gt; with >, and &amp; with & (and possibly more, but these cover the standard cases given the context of parsing XML-like content). This can be done by chaining .replace() calls, or (for broader correctness) by using a well-known library such as he for HTML entity decoding. Since only standard entities are mentioned and only the shown code can be changed, the best fix is to replace the identity replacements with proper entity replacements in the decode helper function, on line 126.

Suggested changeset 1
src/shared/formatContentBlockForUi.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/shared/formatContentBlockForUi.ts b/src/shared/formatContentBlockForUi.ts
--- a/src/shared/formatContentBlockForUi.ts
+++ b/src/shared/formatContentBlockForUi.ts
@@ -123,7 +123,11 @@
  */
 function summarizeFilesXml(xmlLike: string): string {
 	// Support both escaped and unescaped tags
-	const decode = (s: string) => s.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")
+	const decode = (s: string) =>
+		s
+			.replace(/&lt;/g, "<")
+			.replace(/&gt;/g, ">")
+			.replace(/&amp;/g, "&");
 
 	const raw = decode(xmlLike)
 	const fileRegex = /<file>([\s\S]*?)<\/file>/g
EOF
@@ -123,7 +123,11 @@
*/
function summarizeFilesXml(xmlLike: string): string {
// Support both escaped and unescaped tags
const decode = (s: string) => s.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")
const decode = (s: string) =>
s
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&amp;/g, "&");

const raw = decode(xmlLike)
const fileRegex = /<file>([\s\S]*?)<\/file>/g
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated

Check warning

Code scanning / CodeQL

Replacement of a substring with itself Medium

This replaces '>' with itself.

Copilot Autofix

AI 3 months ago

To fix this problem, update the decode function (on line 126) to properly decode common XML entities. Specifically, replace all occurrences of &lt; with <, &gt; with >, and &amp; with &. This ensures that both escaped and unescaped XML tags are handled as described in the comment, and the decoder correctly converts XML entity-encoded tags to their character equivalents for further processing. The changes are localized to the decode function, so edit line 126 in the file src/shared/formatContentBlockForUi.ts to swap the current replacement calls for the correct ones.

No additional imports or dependencies are required, and the fix is self-contained.


Suggested changeset 1
src/shared/formatContentBlockForUi.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/shared/formatContentBlockForUi.ts b/src/shared/formatContentBlockForUi.ts
--- a/src/shared/formatContentBlockForUi.ts
+++ b/src/shared/formatContentBlockForUi.ts
@@ -123,7 +123,10 @@
  */
 function summarizeFilesXml(xmlLike: string): string {
 	// Support both escaped and unescaped tags
-	const decode = (s: string) => s.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")
+	const decode = (s: string) =>
+		s.replace(/&lt;/g, "<")
+		 .replace(/&gt;/g, ">")
+		 .replace(/&amp;/g, "&")
 
 	const raw = decode(xmlLike)
 	const fileRegex = /<file>([\s\S]*?)<\/file>/g
EOF
@@ -123,7 +123,10 @@
*/
function summarizeFilesXml(xmlLike: string): string {
// Support both escaped and unescaped tags
const decode = (s: string) => s.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")
const decode = (s: string) =>
s.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&amp;/g, "&")

const raw = decode(xmlLike)
const fileRegex = /<file>([\s\S]*?)<\/file>/g
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated

Check warning

Code scanning / CodeQL

Replacement of a substring with itself Medium

This replaces '&' with itself.

Copilot Autofix

AI 3 months ago

The best fix is to replace the decode function with one that properly decodes XML/HTML entities, converting sequences like &lt; to <, &gt; to >, and &amp; to &, as well as any other encountered entities. Since TypeScript/JavaScript does not provide a standard, reliable method for entity decoding, but the well-known library he (html-entities is also popular) can accomplish this robustly. In the context of this file, we only need to decode a string, so importing and using a decoder from a widely used library is the best way to meet the requirement. Therefore, replace the ad-hoc decode function with an implementation using he.decode. This change is limited to src/shared/formatContentBlockForUi.ts. If importing is not possible, you may use a minimum viable manual decoding for the three most common entities (&lt;, &gt;, &amp;).

Suggested changeset 2
src/shared/formatContentBlockForUi.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/shared/formatContentBlockForUi.ts b/src/shared/formatContentBlockForUi.ts
--- a/src/shared/formatContentBlockForUi.ts
+++ b/src/shared/formatContentBlockForUi.ts
@@ -123,7 +123,8 @@
  */
 function summarizeFilesXml(xmlLike: string): string {
 	// Support both escaped and unescaped tags
-	const decode = (s: string) => s.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")
+	import { decode as heDecode } from "he";
+	const decode = (s: string) => heDecode(s);
 
 	const raw = decode(xmlLike)
 	const fileRegex = /<file>([\s\S]*?)<\/file>/g
EOF
@@ -123,7 +123,8 @@
*/
function summarizeFilesXml(xmlLike: string): string {
// Support both escaped and unescaped tags
const decode = (s: string) => s.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")
import { decode as heDecode } from "he";
const decode = (s: string) => heDecode(s);

const raw = decode(xmlLike)
const fileRegex = /<file>([\s\S]*?)<\/file>/g
src/package.json
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/package.json b/src/package.json
--- a/src/package.json
+++ b/src/package.json
@@ -521,7 +521,8 @@
 		"web-tree-sitter": "^0.25.6",
 		"workerpool": "^9.2.0",
 		"yaml": "^2.8.0",
-		"zod": "^3.25.61"
+		"zod": "^3.25.61",
+		"he": "^1.2.0"
 	},
 	"devDependencies": {
 		"@roo-code/build": "workspace:^",
EOF
@@ -521,7 +521,8 @@
"web-tree-sitter": "^0.25.6",
"workerpool": "^9.2.0",
"yaml": "^2.8.0",
"zod": "^3.25.61"
"zod": "^3.25.61",
"he": "^1.2.0"
},
"devDependencies": {
"@roo-code/build": "workspace:^",
This fix introduces these dependencies
Package Version Security advisories
he (npm) 1.2.0 None
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The entity decoding order is incorrect. When decoding HTML entities, &amp; must be decoded LAST, otherwise double-encoded entities won't decode properly. For example, with the current order, &amp;lt; becomes &lt; (still escaped) instead of < (correct). The correct order should be: .replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&'). This same issue exists in the decode function at line 161.


const raw = decode(xmlLike)
const fileRegex = /<file>([\s\S]*?)<\/file>/g
const items: string[] = []
let match: RegExpExecArray | null

while ((match = fileRegex.exec(raw)) !== null) {
const fileBlock = match[1]
const path = matchOne(fileBlock, /<path>([\s\S]*?)<\/path>/)
const error = matchOne(fileBlock, /<error>([\s\S]*?)<\/error>/)
const notice = matchOne(fileBlock, /<notice>([\s\S]*?)<\/notice>/)
const binary = matchOne(fileBlock, /<binary_file(?:[^>]*)>([\s\S]*?)<\/binary_file>/)

let line = path ? `- ${path}` : "- [unknown path]"
if (error) line += ` [error: ${singleLine(error)}]`
if (!error && binary) line += " [binary file]"
if (!error && !binary && notice) line += ` [${singleLine(notice)}]`

items.push(line)
}

if (items.length === 0) {
return "[files omitted]"
}

const MAX_ITEMS = 20
let output = items.slice(0, MAX_ITEMS).join("\n")
if (items.length > MAX_ITEMS) {
output += `\n[+${items.length - MAX_ITEMS} more files]`
}
return output
}

function extractPathsFromXml(xml: string): string[] {
const decode = (s: string) => s.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")

Check warning

Code scanning / CodeQL

Replacement of a substring with itself Medium

This replaces '<' with itself.

Copilot Autofix

AI 3 months ago

The main issue is that the decode function is defined as follows:

const decode = (s: string) => s.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")

This does nothing, because it replaces "<" with "<", and so on. The function should decode XML/HTML entity escapes such as &lt;, &gt;, and &amp; to their respective literal characters, so that subsequent regex matches against tags like <file> work whether the tags are escaped or not.

General repair:
Replace the decode function definition with one that actually decodes &lt;, &gt;, and &amp; to <, >, and &, respectively.

Best way (detailed):

  • Update the decode function in summarizeFilesXml (line 126) and in extractPathsFromXml (line 161) so they replace escaped XML/HTML entities (&lt;, &gt;, &amp;) with the corresponding literal characters (<, >, &).
  • Do not change other code or functionality, only fix the decoding.
  • No new import is needed; simple string replace suffices for these cases.

Suggested changeset 1
src/shared/formatContentBlockForUi.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/shared/formatContentBlockForUi.ts b/src/shared/formatContentBlockForUi.ts
--- a/src/shared/formatContentBlockForUi.ts
+++ b/src/shared/formatContentBlockForUi.ts
@@ -123,7 +123,8 @@
  */
 function summarizeFilesXml(xmlLike: string): string {
 	// Support both escaped and unescaped tags
-	const decode = (s: string) => s.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")
+	const decode = (s: string) =>
+		s.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&")
 
 	const raw = decode(xmlLike)
 	const fileRegex = /<file>([\s\S]*?)<\/file>/g
@@ -158,7 +159,8 @@
 }
 
 function extractPathsFromXml(xml: string): string[] {
-	const decode = (s: string) => s.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")
+	const decode = (s: string) =>
+		s.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&")
 	const raw = decode(xml)
 	const pathRegex = /<path>([\s\S]*?)<\/path>/g
 	const paths: string[] = []
EOF
@@ -123,7 +123,8 @@
*/
function summarizeFilesXml(xmlLike: string): string {
// Support both escaped and unescaped tags
const decode = (s: string) => s.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")
const decode = (s: string) =>
s.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&")

const raw = decode(xmlLike)
const fileRegex = /<file>([\s\S]*?)<\/file>/g
@@ -158,7 +159,8 @@
}

function extractPathsFromXml(xml: string): string[] {
const decode = (s: string) => s.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")
const decode = (s: string) =>
s.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&")
const raw = decode(xml)
const pathRegex = /<path>([\s\S]*?)<\/path>/g
const paths: string[] = []
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated

Check warning

Code scanning / CodeQL

Replacement of a substring with itself Medium

This replaces '>' with itself.

Copilot Autofix

AI 3 months ago

The bug exists in the decode functions (lines 126 and 161) where .replace(/</g, "<").replace(/>/g, ">") replaces characters with themselves — a useless operation. The intention appears to be decoding the XML-escaped forms (&lt;, &gt;, and &amp;), converting XML character entities back to their original characters. The correct replacements should therefore be:

  • .replace(/&lt;/g, "<")
  • .replace(/&gt;/g, ">")
  • .replace(/&amp;/g, "&")

To fix this:

  • On lines 126 and 161, update both decode functions to replace &lt;, &gt;, and &amp; with <, >, and & respectively, instead of attempting to replace literal <, >, and & with themselves.
  • No imports are required.
  • Only change the decode lambda body in both places, not other code.

Suggested changeset 1
src/shared/formatContentBlockForUi.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/shared/formatContentBlockForUi.ts b/src/shared/formatContentBlockForUi.ts
--- a/src/shared/formatContentBlockForUi.ts
+++ b/src/shared/formatContentBlockForUi.ts
@@ -123,7 +123,10 @@
  */
 function summarizeFilesXml(xmlLike: string): string {
 	// Support both escaped and unescaped tags
-	const decode = (s: string) => s.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")
+	const decode = (s: string) => s
+		.replace(/&lt;/g, "<")
+		.replace(/&gt;/g, ">")
+		.replace(/&amp;/g, "&")
 
 	const raw = decode(xmlLike)
 	const fileRegex = /<file>([\s\S]*?)<\/file>/g
@@ -158,7 +161,10 @@
 }
 
 function extractPathsFromXml(xml: string): string[] {
-	const decode = (s: string) => s.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")
+	const decode = (s: string) => s
+		.replace(/&lt;/g, "<")
+		.replace(/&gt;/g, ">")
+		.replace(/&amp;/g, "&")
 	const raw = decode(xml)
 	const pathRegex = /<path>([\s\S]*?)<\/path>/g
 	const paths: string[] = []
EOF
@@ -123,7 +123,10 @@
*/
function summarizeFilesXml(xmlLike: string): string {
// Support both escaped and unescaped tags
const decode = (s: string) => s.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")
const decode = (s: string) => s
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&amp;/g, "&")

const raw = decode(xmlLike)
const fileRegex = /<file>([\s\S]*?)<\/file>/g
@@ -158,7 +161,10 @@
}

function extractPathsFromXml(xml: string): string[] {
const decode = (s: string) => s.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")
const decode = (s: string) => s
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&amp;/g, "&")
const raw = decode(xml)
const pathRegex = /<path>([\s\S]*?)<\/path>/g
const paths: string[] = []
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated

Check warning

Code scanning / CodeQL

Replacement of a substring with itself Medium

This replaces '&' with itself.

Copilot Autofix

AI 3 months ago

To fix the problem, the decode function should replace entity-encoded representations (such as &amp;, &lt;, &gt;) with their corresponding normal characters (&, <, >). Therefore, within the decode function, update the replacement logic to convert both < and > entities, as well as &amp; to &. Specifically, change the replacements so that .replace(/&/g, "&") becomes .replace(/&amp;/g, "&"), and (optionally) update < and > handling for XML entities if necessary. Make changes only in the code region defining the decode function within summarizeFilesXml and extractPathsFromXml (lines 126 and 161).

Suggested changeset 1
src/shared/formatContentBlockForUi.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/shared/formatContentBlockForUi.ts b/src/shared/formatContentBlockForUi.ts
--- a/src/shared/formatContentBlockForUi.ts
+++ b/src/shared/formatContentBlockForUi.ts
@@ -123,7 +123,7 @@
  */
 function summarizeFilesXml(xmlLike: string): string {
 	// Support both escaped and unescaped tags
-	const decode = (s: string) => s.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")
+	const decode = (s: string) => s.replace(/&amp;/g, "&").replace(/</g, "<").replace(/>/g, ">")
 
 	const raw = decode(xmlLike)
 	const fileRegex = /<file>([\s\S]*?)<\/file>/g
@@ -158,7 +158,7 @@
 }
 
 function extractPathsFromXml(xml: string): string[] {
-	const decode = (s: string) => s.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")
+	const decode = (s: string) => s.replace(/&amp;/g, "&").replace(/</g, "<").replace(/>/g, ">")
 	const raw = decode(xml)
 	const pathRegex = /<path>([\s\S]*?)<\/path>/g
 	const paths: string[] = []
EOF
@@ -123,7 +123,7 @@
*/
function summarizeFilesXml(xmlLike: string): string {
// Support both escaped and unescaped tags
const decode = (s: string) => s.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")
const decode = (s: string) => s.replace(/&amp;/g, "&").replace(/</g, "<").replace(/>/g, ">")

const raw = decode(xmlLike)
const fileRegex = /<file>([\s\S]*?)<\/file>/g
@@ -158,7 +158,7 @@
}

function extractPathsFromXml(xml: string): string[] {
const decode = (s: string) => s.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")
const decode = (s: string) => s.replace(/&amp;/g, "&").replace(/</g, "<").replace(/>/g, ">")
const raw = decode(xml)
const pathRegex = /<path>([\s\S]*?)<\/path>/g
const paths: string[] = []
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
const raw = decode(xml)
const pathRegex = /<path>([\s\S]*?)<\/path>/g
const paths: string[] = []
let m: RegExpExecArray | null
while ((m = pathRegex.exec(raw)) !== null) {
paths.push(m[1])
}
return paths
}

function matchOne(source: string, re: RegExp): string | undefined {
const m = re.exec(source)
return m ? m[1] : undefined
}

function singleLine(s: string): string {
return s.replace(/\s+/g, " ").trim()
}
Loading