From b20a34bfa7fe7badf48df239b3a178e25ffb8e10 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Mar 2026 12:15:47 +0900 Subject: [PATCH] fix(slashcommand): discover nested opencode commands Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../slashcommand/command-discovery.test.ts | 20 +++++++++++++++++++ src/tools/slashcommand/command-discovery.ts | 18 +++++++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/tools/slashcommand/command-discovery.test.ts b/src/tools/slashcommand/command-discovery.test.ts index 232515a33..1bf6ce016 100644 --- a/src/tools/slashcommand/command-discovery.test.ts +++ b/src/tools/slashcommand/command-discovery.test.ts @@ -235,4 +235,24 @@ Use plural command. expect(duplicates).toHaveLength(1) expect(duplicates[0]?.content).toContain("Use plural command.") }) + + it("discovers nested opencode project commands", () => { + const commandsDir = join(projectDir, ".opencode", "commands", "refactor") + + mkdirSync(commandsDir, { recursive: true }) + writeFileSync( + join(commandsDir, "code.md"), + `--- +description: Nested command +--- +Use nested command. +`, + ) + + const commands = discoverCommandsSync(projectDir) + const nestedCommand = commands.find((command) => command.name === "refactor:code") + + expect(nestedCommand?.content).toContain("Use nested command.") + expect(nestedCommand?.scope).toBe("opencode-project") + }) }) diff --git a/src/tools/slashcommand/command-discovery.ts b/src/tools/slashcommand/command-discovery.ts index 7567e74dd..574c0cb65 100644 --- a/src/tools/slashcommand/command-discovery.ts +++ b/src/tools/slashcommand/command-discovery.ts @@ -18,17 +18,31 @@ export interface CommandDiscoveryOptions { enabledPluginsOverride?: Record } -function discoverCommandsFromDir(commandsDir: string, scope: CommandScope): CommandInfo[] { +function discoverCommandsFromDir( + commandsDir: string, + scope: CommandScope, + prefix = "", +): CommandInfo[] { if (!existsSync(commandsDir)) return [] const entries = readdirSync(commandsDir, { withFileTypes: true }) const commands: CommandInfo[] = [] for (const entry of entries) { + if (entry.isDirectory()) { + if (entry.name.startsWith(".")) continue + const nestedPrefix = prefix ? `${prefix}:${entry.name}` : entry.name + commands.push( + ...discoverCommandsFromDir(join(commandsDir, entry.name), scope, nestedPrefix), + ) + continue + } + if (!isMarkdownFile(entry)) continue const commandPath = join(commandsDir, entry.name) - const commandName = basename(entry.name, ".md") + const baseCommandName = basename(entry.name, ".md") + const commandName = prefix ? `${prefix}:${baseCommandName}` : baseCommandName try { const content = readFileSync(commandPath, "utf-8")