summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src/components
diff options
context:
space:
mode:
authorShoubhit Dash <[email protected]>2026-04-01 16:11:57 +0530
committerGitHub <[email protected]>2026-04-01 16:11:57 +0530
commita3a6cf1c075c40c87980dda181d586a1d06ea304 (patch)
tree71b3c3d7144f7c38eb73d7e173852858313fc4bf /packages/app/src/components
parent47a676111a3532aebed01110494742e536b7e5b4 (diff)
downloadopencode-a3a6cf1c075c40c87980dda181d586a1d06ea304.tar.gz
opencode-a3a6cf1c075c40c87980dda181d586a1d06ea304.zip
feat(comments): support file mentions (#20447)
Diffstat (limited to 'packages/app/src/components')
-rw-r--r--packages/app/src/components/prompt-input/build-request-parts.test.ts24
-rw-r--r--packages/app/src/components/prompt-input/build-request-parts.ts26
2 files changed, 50 insertions, 0 deletions
diff --git a/packages/app/src/components/prompt-input/build-request-parts.test.ts b/packages/app/src/components/prompt-input/build-request-parts.test.ts
index ce09ae921..06c377331 100644
--- a/packages/app/src/components/prompt-input/build-request-parts.test.ts
+++ b/packages/app/src/components/prompt-input/build-request-parts.test.ts
@@ -100,6 +100,30 @@ describe("buildRequestParts", () => {
expect(synthetic).toHaveLength(1)
})
+ test("adds file parts for @mentions inside comment text", () => {
+ const result = buildRequestParts({
+ prompt: [{ type: "text", content: "look", start: 0, end: 4 }],
+ context: [
+ {
+ key: "ctx:comment-mention",
+ type: "file",
+ path: "src/review.ts",
+ comment: "Compare with @src/shared.ts and @src/review.ts.",
+ },
+ ],
+ images: [],
+ text: "look",
+ messageID: "msg_comment_mentions",
+ sessionID: "ses_comment_mentions",
+ sessionDirectory: "/repo",
+ })
+
+ const files = result.requestParts.filter((part) => part.type === "file")
+ expect(files).toHaveLength(2)
+ expect(files.some((part) => part.type === "file" && part.url === "file:///repo/src/review.ts")).toBe(true)
+ expect(files.some((part) => part.type === "file" && part.url === "file:///repo/src/shared.ts")).toBe(true)
+ })
+
test("handles Windows paths correctly (simulated on macOS)", () => {
const prompt: Prompt = [{ type: "file", path: "src\\foo.ts", content: "@src\\foo.ts", start: 0, end: 11 }]
diff --git a/packages/app/src/components/prompt-input/build-request-parts.ts b/packages/app/src/components/prompt-input/build-request-parts.ts
index 4146fb484..a1076e60c 100644
--- a/packages/app/src/components/prompt-input/build-request-parts.ts
+++ b/packages/app/src/components/prompt-input/build-request-parts.ts
@@ -39,6 +39,16 @@ const absolute = (directory: string, path: string) => {
const fileQuery = (selection: FileSelection | undefined) =>
selection ? `?start=${selection.startLine}&end=${selection.endLine}` : ""
+const mention = /(^|[\s([{"'])@(\S+)/g
+
+const parseCommentMentions = (comment: string) => {
+ return Array.from(comment.matchAll(mention)).flatMap((match) => {
+ const path = (match[2] ?? "").replace(/[.,!?;:)}\]"']+$/, "")
+ if (!path) return []
+ return [path]
+ })
+}
+
const isFileAttachment = (part: Prompt[number]): part is FileAttachmentPart => part.type === "file"
const isAgentAttachment = (part: Prompt[number]): part is AgentPart => part.type === "agent"
@@ -138,6 +148,21 @@ export function buildRequestParts(input: BuildRequestPartsInput) {
if (!comment) return [filePart]
+ const mentions = parseCommentMentions(comment).flatMap((path) => {
+ const url = `file://${encodeFilePath(absolute(input.sessionDirectory, path))}`
+ if (used.has(url)) return []
+ used.add(url)
+ return [
+ {
+ id: Identifier.ascending("part"),
+ type: "file",
+ mime: "text/plain",
+ url,
+ filename: getFilename(path),
+ } satisfies PromptRequestPart,
+ ]
+ })
+
return [
{
id: Identifier.ascending("part"),
@@ -153,6 +178,7 @@ export function buildRequestParts(input: BuildRequestPartsInput) {
}),
} satisfies PromptRequestPart,
filePart,
+ ...mentions,
]
})