From ad44af9d15a904fdcbf76d2c6e5db8241e9778b5 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Wed, 7 Jan 2026 01:41:42 +0900 Subject: [PATCH] fix: load skill content via lazyContentLoader in slashcommand tool - Fix #542: slashcommand tool returns empty content for skills - Add lazyContentLoader to CommandInfo type - Load skill content in formatLoadedCommand when content is empty - Filter non-ultrawork keywords in subagent sessions --- src/hooks/keyword-detector/index.ts | 27 ++++++++++++++++++++++++++- src/tools/slashcommand/tools.ts | 8 +++++++- src/tools/slashcommand/types.ts | 3 ++- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/hooks/keyword-detector/index.ts b/src/hooks/keyword-detector/index.ts index 462fd39..27e08b5 100644 --- a/src/hooks/keyword-detector/index.ts +++ b/src/hooks/keyword-detector/index.ts @@ -21,12 +21,37 @@ export function createKeywordDetectorHook(ctx: PluginInput) { } ): Promise => { const promptText = extractPromptText(output.parts) - const detectedKeywords = detectKeywordsWithType(removeCodeBlocks(promptText), input.agent) + let detectedKeywords = detectKeywordsWithType(removeCodeBlocks(promptText), input.agent) if (detectedKeywords.length === 0) { return } + // Check if this is a subagent session (has parent) + // Only ultrawork keywords work in subagent sessions + // Other keywords (search, analyze, etc.) only work in main sessions + try { + const sessionInfo = await ctx.client.session.get({ path: { id: input.sessionID } }) + const isSubagentSession = !!sessionInfo.data?.parentID + + if (isSubagentSession) { + // Filter to only ultrawork keywords in subagent sessions + detectedKeywords = detectedKeywords.filter((k) => k.type === "ultrawork") + if (detectedKeywords.length === 0) { + log(`[keyword-detector] Skipping non-ultrawork keywords in subagent session`, { + sessionID: input.sessionID, + parentID: sessionInfo.data?.parentID, + }) + return + } + } + } catch (err) { + log(`[keyword-detector] Failed to get session info, proceeding with all keywords`, { + error: err, + sessionID: input.sessionID, + }) + } + const hasUltrawork = detectedKeywords.some((k) => k.type === "ultrawork") if (hasUltrawork) { log(`[keyword-detector] Ultrawork mode activated`, { sessionID: input.sessionID }) diff --git a/src/tools/slashcommand/tools.ts b/src/tools/slashcommand/tools.ts index 0cee32d..335d442 100644 --- a/src/tools/slashcommand/tools.ts +++ b/src/tools/slashcommand/tools.ts @@ -80,6 +80,7 @@ function skillToCommandInfo(skill: LoadedSkill): CommandInfo { }, content: skill.definition.template, scope: skill.scope, + lazyContentLoader: skill.lazyContent, } } @@ -112,8 +113,13 @@ async function formatLoadedCommand(cmd: CommandInfo): Promise { sections.push("---\n") sections.push("## Command Instructions\n") + let content = cmd.content || "" + if (!content && cmd.lazyContentLoader) { + content = await cmd.lazyContentLoader.load() + } + const commandDir = cmd.path ? dirname(cmd.path) : process.cwd() - const withFileRefs = await resolveFileReferencesInText(cmd.content || "", commandDir) + const withFileRefs = await resolveFileReferencesInText(content, commandDir) const resolvedContent = await resolveCommandsInText(withFileRefs) sections.push(resolvedContent.trim()) diff --git a/src/tools/slashcommand/types.ts b/src/tools/slashcommand/types.ts index fa51268..2cacdd0 100644 --- a/src/tools/slashcommand/types.ts +++ b/src/tools/slashcommand/types.ts @@ -1,4 +1,4 @@ -import type { LoadedSkill } from "../../features/opencode-skill-loader" +import type { LoadedSkill, LazyContentLoader } from "../../features/opencode-skill-loader" export type CommandScope = "builtin" | "config" | "user" | "project" | "opencode" | "opencode-project" @@ -17,6 +17,7 @@ export interface CommandInfo { metadata: CommandMetadata content?: string scope: CommandScope + lazyContentLoader?: LazyContentLoader } export interface SlashcommandToolOptions {