From 491a2adf8d61cb52aa5d8a6c3b3e5fe0ef073a73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Krzywa=C5=BCnia?= <1815898+ribelo@users.noreply.github.com> Date: Wed, 12 Nov 2025 02:38:50 +0100 Subject: [PATCH] fix: resolve @file references in slash commands with subagents (#4221) --- packages/opencode/src/session/prompt.ts | 94 +++++++++++++------------ packages/opencode/src/tool/task.ts | 9 +-- 2 files changed, 51 insertions(+), 52 deletions(-) diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 10a9727a..f4dc84be 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -145,6 +145,54 @@ export namespace SessionPrompt { ), }) export type PromptInput = z.infer + + export async function resolvePromptParts(template: string): Promise { + const parts: PromptInput["parts"] = [ + { + type: "text", + text: template, + }, + ] + const files = ConfigMarkdown.files(template) + await Promise.all( + files.map(async (match) => { + const name = match[1] + const filepath = name.startsWith("~/") + ? path.join(os.homedir(), name.slice(2)) + : path.resolve(Instance.worktree, name) + + const stats = await fs.stat(filepath).catch(() => undefined) + if (!stats) { + const agent = await Agent.get(name) + if (agent) { + parts.push({ + type: "agent", + name: agent.name, + }) + } + return + } + + if (stats.isDirectory()) { + parts.push({ + type: "file", + url: `file://${filepath}`, + filename: name, + mime: "application/x-directory", + }) + return + } + + parts.push({ + type: "file", + url: `file://${filepath}`, + filename: name, + mime: "text/plain", + }) + }), + ) + return parts + } export async function prompt(input: PromptInput): Promise { const l = log.clone().tag("session", input.sessionID) l.info("prompt") @@ -1605,51 +1653,7 @@ export namespace SessionPrompt { } template = template.trim() - const parts = [ - { - type: "text", - text: template, - }, - ] as PromptInput["parts"] - - const files = ConfigMarkdown.files(template) - await Promise.all( - files.map(async (match) => { - const name = match[1] - const filepath = name.startsWith("~/") - ? path.join(os.homedir(), name.slice(2)) - : path.resolve(Instance.worktree, name) - - const stats = await fs.stat(filepath).catch(() => undefined) - if (!stats) { - const agent = await Agent.get(name) - if (agent) { - parts.push({ - type: "agent", - name: agent.name, - }) - } - return - } - - if (stats.isDirectory()) { - parts.push({ - type: "file", - url: `file://${filepath}`, - filename: name, - mime: "application/x-directory", - }) - return - } - - parts.push({ - type: "file", - url: `file://${filepath}`, - filename: name, - mime: "text/plain", - }) - }), - ) + const parts = await resolvePromptParts(template) const model = await (async () => { if (command.model) { diff --git a/packages/opencode/src/tool/task.ts b/packages/opencode/src/tool/task.ts index 845a0e8c..a5369d33 100644 --- a/packages/opencode/src/tool/task.ts +++ b/packages/opencode/src/tool/task.ts @@ -65,6 +65,7 @@ export const TaskTool = Tool.define("task", async () => { ctx.abort.addEventListener("abort", () => { SessionLock.abort(session.id) }) + const promptParts = await SessionPrompt.resolvePromptParts(params.prompt) const result = await SessionPrompt.prompt({ messageID, sessionID: session.id, @@ -79,13 +80,7 @@ export const TaskTool = Tool.define("task", async () => { task: false, ...agent.tools, }, - parts: [ - { - id: Identifier.ascending("part"), - type: "text", - text: params.prompt, - }, - ], + parts: promptParts, }) unsub() let all