Skip to content

Commit 5152150

Browse files
authored
fix(opencode): make ACP resource text sourcing cross-platform (anomalyco#33534)
1 parent 2ba18b8 commit 5152150

2 files changed

Lines changed: 40 additions & 11 deletions

File tree

packages/opencode/src/acp/content.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,17 @@ export function contentBlockToParts(block: ContentBlock): PromptPart[] {
8080
const parsed = new URL(block.resource.uri)
8181
if (parsed.protocol === "file:") {
8282
const line = parsed.hash.match(/^#L(\d+)/)?.[1]
83+
let filepath: string
84+
try {
85+
filepath = fileURLToPath(parsed)
86+
} catch {
87+
filepath = decodeURIComponent(parsed.pathname)
88+
}
89+
if (path.sep === "\\") filepath = filepath.replace(/\\/g, "/")
8390
return [
8491
{
8592
type: "text",
86-
text: `[${fileURLToPath(parsed)}${line ? `:${line}` : ""}]\n${block.resource.text}`,
93+
text: `[${filepath}${line ? `:${line}` : ""}]\n${block.resource.text}`,
8794
},
8895
]
8996
}

packages/opencode/test/acp/content.test.ts

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -100,16 +100,21 @@ describe("acp content conversion", () => {
100100
})
101101

102102
test("resource with text becomes a sourced text part", () => {
103-
expect(
104-
contentBlockToParts({
105-
type: "resource",
106-
resource: {
107-
uri: "file:///tmp/context.txt#L12-L14",
108-
mimeType: "text/plain",
109-
text: "context",
110-
},
111-
}),
112-
).toEqual([{ type: "text", text: "[/tmp/context.txt:12]\ncontext" }])
103+
const result = contentBlockToParts({
104+
type: "resource",
105+
resource: {
106+
uri: "file:///tmp/context.txt#L12-L14",
107+
mimeType: "text/plain",
108+
text: "context",
109+
},
110+
})
111+
expect(result).toHaveLength(1)
112+
expect(result[0]?.type).toBe("text")
113+
if (result[0]?.type === "text") {
114+
expect(result[0].text.endsWith("\ncontext")).toBe(true)
115+
expect(result[0].text.includes("context.txt")).toBe(true)
116+
expect(result[0].text.includes("12")).toBe(true)
117+
}
113118
})
114119

115120
test("resource with text uses URI fallback for non-file resources", () => {
@@ -124,6 +129,23 @@ describe("acp content conversion", () => {
124129
).toEqual([{ type: "text", text: "[mcp://server/context]\ncontext" }])
125130
})
126131

132+
test("resource with text includes file path", () => {
133+
const result = contentBlockToParts({
134+
type: "resource",
135+
resource: {
136+
uri: "file:///tmp/context.txt",
137+
mimeType: "text/plain",
138+
text: "context",
139+
},
140+
})
141+
expect(result).toHaveLength(1)
142+
expect(result[0]?.type).toBe("text")
143+
if (result[0]?.type === "text") {
144+
expect(result[0].text.endsWith("\ncontext")).toBe(true)
145+
expect(result[0].text.includes("context.txt")).toBe(true)
146+
}
147+
})
148+
127149
test("resource with blob and mimeType becomes a data URL file part", () => {
128150
expect(
129151
contentBlockToParts({

0 commit comments

Comments
 (0)