From 6914f2fd046c0f3b8614bf8dda542fa02d4afc48 Mon Sep 17 00:00:00 2001 From: Willy Date: Fri, 13 Feb 2026 10:49:15 +0800 Subject: [PATCH 1/2] fix(skills): use directory param instead of process.cwd() for project skill discovery Project-level skills (.opencode/skills/ and .claude/skills/) were not discovered in desktop app environments because the discover functions hardcoded process.cwd() to resolve project paths. In desktop apps, process.cwd() points to the app installation directory rather than the user's project directory. Add optional directory parameter to all project-level skill discovery functions and thread ctx.directory from the plugin context through the entire skill loading pipeline. Falls back to process.cwd() when directory is not provided, preserving CLI compatibility. --- src/features/opencode-skill-loader/loader.ts | 29 ++++++++++--------- .../opencode-skill-loader/skill-discovery.ts | 2 +- .../skill-resolution-options.ts | 2 ++ src/plugin-handlers/agent-config-handler.ts | 6 ++-- src/plugin/skill-context.ts | 4 +-- 5 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/features/opencode-skill-loader/loader.ts b/src/features/opencode-skill-loader/loader.ts index 2eebc0077..81591dfa1 100644 --- a/src/features/opencode-skill-loader/loader.ts +++ b/src/features/opencode-skill-loader/loader.ts @@ -13,8 +13,8 @@ export async function loadUserSkills(): Promise> { - const projectSkillsDir = join(process.cwd(), ".claude", "skills") +export async function loadProjectSkills(directory?: string): Promise> { + const projectSkillsDir = join(directory ?? process.cwd(), ".claude", "skills") const skills = await loadSkillsFromDir({ skillsDir: projectSkillsDir, scope: "project" }) return skillsToCommandDefinitionRecord(skills) } @@ -26,21 +26,22 @@ export async function loadOpencodeGlobalSkills(): Promise> { - const opencodeProjectDir = join(process.cwd(), ".opencode", "skills") +export async function loadOpencodeProjectSkills(directory?: string): Promise> { + const opencodeProjectDir = join(directory ?? process.cwd(), ".opencode", "skills") const skills = await loadSkillsFromDir({ skillsDir: opencodeProjectDir, scope: "opencode-project" }) return skillsToCommandDefinitionRecord(skills) } export interface DiscoverSkillsOptions { includeClaudeCodePaths?: boolean + directory?: string } -export async function discoverAllSkills(): Promise { +export async function discoverAllSkills(directory?: string): Promise { const [opencodeProjectSkills, opencodeGlobalSkills, projectSkills, userSkills] = await Promise.all([ - discoverOpencodeProjectSkills(), + discoverOpencodeProjectSkills(directory), discoverOpencodeGlobalSkills(), - discoverProjectClaudeSkills(), + discoverProjectClaudeSkills(directory), discoverUserClaudeSkills(), ]) @@ -49,10 +50,10 @@ export async function discoverAllSkills(): Promise { } export async function discoverSkills(options: DiscoverSkillsOptions = {}): Promise { - const { includeClaudeCodePaths = true } = options + const { includeClaudeCodePaths = true, directory } = options const [opencodeProjectSkills, opencodeGlobalSkills] = await Promise.all([ - discoverOpencodeProjectSkills(), + discoverOpencodeProjectSkills(directory), discoverOpencodeGlobalSkills(), ]) @@ -62,7 +63,7 @@ export async function discoverSkills(options: DiscoverSkillsOptions = {}): Promi } const [projectSkills, userSkills] = await Promise.all([ - discoverProjectClaudeSkills(), + discoverProjectClaudeSkills(directory), discoverUserClaudeSkills(), ]) @@ -80,8 +81,8 @@ export async function discoverUserClaudeSkills(): Promise { return loadSkillsFromDir({ skillsDir: userSkillsDir, scope: "user" }) } -export async function discoverProjectClaudeSkills(): Promise { - const projectSkillsDir = join(process.cwd(), ".claude", "skills") +export async function discoverProjectClaudeSkills(directory?: string): Promise { + const projectSkillsDir = join(directory ?? process.cwd(), ".claude", "skills") return loadSkillsFromDir({ skillsDir: projectSkillsDir, scope: "project" }) } @@ -91,7 +92,7 @@ export async function discoverOpencodeGlobalSkills(): Promise { return loadSkillsFromDir({ skillsDir: opencodeSkillsDir, scope: "opencode" }) } -export async function discoverOpencodeProjectSkills(): Promise { - const opencodeProjectDir = join(process.cwd(), ".opencode", "skills") +export async function discoverOpencodeProjectSkills(directory?: string): Promise { + const opencodeProjectDir = join(directory ?? process.cwd(), ".opencode", "skills") return loadSkillsFromDir({ skillsDir: opencodeProjectDir, scope: "opencode-project" }) } diff --git a/src/features/opencode-skill-loader/skill-discovery.ts b/src/features/opencode-skill-loader/skill-discovery.ts index 2154b06ea..fb991e44a 100644 --- a/src/features/opencode-skill-loader/skill-discovery.ts +++ b/src/features/opencode-skill-loader/skill-discovery.ts @@ -20,7 +20,7 @@ export async function getAllSkills(options?: SkillResolutionOptions): Promise + /** Project directory to discover project-level skills from. Falls back to process.cwd() if not provided. */ + directory?: string } diff --git a/src/plugin-handlers/agent-config-handler.ts b/src/plugin-handlers/agent-config-handler.ts index d08094551..3525f48af 100644 --- a/src/plugin-handlers/agent-config-handler.ts +++ b/src/plugin-handlers/agent-config-handler.ts @@ -47,10 +47,10 @@ export async function applyAgentConfig(params: { }), includeClaudeSkillsForAwareness ? discoverUserClaudeSkills() : Promise.resolve([]), includeClaudeSkillsForAwareness - ? discoverProjectClaudeSkills() - : Promise.resolve([]), + ? discoverProjectClaudeSkills(params.ctx.directory) + : Promise.resolve([]), discoverOpencodeGlobalSkills(), - discoverOpencodeProjectSkills(), + discoverOpencodeProjectSkills(params.ctx.directory), ]); const allDiscoveredSkills = [ diff --git a/src/plugin/skill-context.ts b/src/plugin/skill-context.ts index 630cc2085..a1b89f84b 100644 --- a/src/plugin/skill-context.ts +++ b/src/plugin/skill-context.ts @@ -63,8 +63,8 @@ export async function createSkillContext(args: { }), includeClaudeSkills ? discoverUserClaudeSkills() : Promise.resolve([]), discoverOpencodeGlobalSkills(), - includeClaudeSkills ? discoverProjectClaudeSkills() : Promise.resolve([]), - discoverOpencodeProjectSkills(), + includeClaudeSkills ? discoverProjectClaudeSkills(directory) : Promise.resolve([]), + discoverOpencodeProjectSkills(directory), ]) const mergedSkills = mergeSkills( From f9ea9a4ee9fd12b46dc44fa423642a3b7d185d8f Mon Sep 17 00:00:00 2001 From: Willy Date: Fri, 13 Feb 2026 11:09:35 +0800 Subject: [PATCH 2/2] fix(project): use directory param instead of process.cwd() for agents, commands, and slash commands Extends the process.cwd() fix to cover all project-level loaders. In the desktop app, process.cwd() points to the app installation directory instead of the project directory, causing project-level agents, commands, and slash commands to not be discovered. Each function now accepts an optional directory parameter (defaulting to process.cwd() for backward compatibility) and callers pass ctx.directory from the plugin context. --- src/features/claude-code-agent-loader/loader.ts | 4 ++-- src/features/claude-code-command-loader/loader.ts | 14 +++++++------- src/plugin-handlers/agent-config-handler.ts | 2 +- src/plugin-handlers/command-config-handler.ts | 8 ++++---- src/plugin/tool-registry.ts | 2 +- src/tools/slashcommand/command-discovery.ts | 6 +++--- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/features/claude-code-agent-loader/loader.ts b/src/features/claude-code-agent-loader/loader.ts index 0f6ef2150..407525687 100644 --- a/src/features/claude-code-agent-loader/loader.ts +++ b/src/features/claude-code-agent-loader/loader.ts @@ -78,8 +78,8 @@ export function loadUserAgents(): Record { return result } -export function loadProjectAgents(): Record { - const projectAgentsDir = join(process.cwd(), ".claude", "agents") +export function loadProjectAgents(directory?: string): Record { + const projectAgentsDir = join(directory ?? process.cwd(), ".claude", "agents") const agents = loadAgentsFromDir(projectAgentsDir, "project") const result: Record = {} diff --git a/src/features/claude-code-command-loader/loader.ts b/src/features/claude-code-command-loader/loader.ts index 4ce62c949..adf2cc4c1 100644 --- a/src/features/claude-code-command-loader/loader.ts +++ b/src/features/claude-code-command-loader/loader.ts @@ -114,8 +114,8 @@ export async function loadUserCommands(): Promise> { - const projectCommandsDir = join(process.cwd(), ".claude", "commands") +export async function loadProjectCommands(directory?: string): Promise> { + const projectCommandsDir = join(directory ?? process.cwd(), ".claude", "commands") const commands = await loadCommandsFromDir(projectCommandsDir, "project") return commandsToRecord(commands) } @@ -127,18 +127,18 @@ export async function loadOpencodeGlobalCommands(): Promise> { - const opencodeProjectDir = join(process.cwd(), ".opencode", "command") +export async function loadOpencodeProjectCommands(directory?: string): Promise> { + const opencodeProjectDir = join(directory ?? process.cwd(), ".opencode", "command") const commands = await loadCommandsFromDir(opencodeProjectDir, "opencode-project") return commandsToRecord(commands) } -export async function loadAllCommands(): Promise> { +export async function loadAllCommands(directory?: string): Promise> { const [user, project, global, projectOpencode] = await Promise.all([ loadUserCommands(), - loadProjectCommands(), + loadProjectCommands(directory), loadOpencodeGlobalCommands(), - loadOpencodeProjectCommands(), + loadOpencodeProjectCommands(directory), ]) return { ...projectOpencode, ...global, ...project, ...user } } diff --git a/src/plugin-handlers/agent-config-handler.ts b/src/plugin-handlers/agent-config-handler.ts index 3525f48af..c2c993ba6 100644 --- a/src/plugin-handlers/agent-config-handler.ts +++ b/src/plugin-handlers/agent-config-handler.ts @@ -84,7 +84,7 @@ export async function applyAgentConfig(params: { const includeClaudeAgents = params.pluginConfig.claude_code?.agents ?? true; const userAgents = includeClaudeAgents ? loadUserAgents() : {}; - const projectAgents = includeClaudeAgents ? loadProjectAgents() : {}; + const projectAgents = includeClaudeAgents ? loadProjectAgents(params.ctx.directory) : {}; const rawPluginAgents = params.pluginComponents.agents; const pluginAgents = Object.fromEntries( diff --git a/src/plugin-handlers/command-config-handler.ts b/src/plugin-handlers/command-config-handler.ts index 983c25a04..1ef73df3c 100644 --- a/src/plugin-handlers/command-config-handler.ts +++ b/src/plugin-handlers/command-config-handler.ts @@ -44,13 +44,13 @@ export async function applyCommandConfig(params: { configDir: params.ctx.directory, }), includeClaudeCommands ? loadUserCommands() : Promise.resolve({}), - includeClaudeCommands ? loadProjectCommands() : Promise.resolve({}), + includeClaudeCommands ? loadProjectCommands(params.ctx.directory) : Promise.resolve({}), loadOpencodeGlobalCommands(), - loadOpencodeProjectCommands(), + loadOpencodeProjectCommands(params.ctx.directory), includeClaudeSkills ? loadUserSkills() : Promise.resolve({}), - includeClaudeSkills ? loadProjectSkills() : Promise.resolve({}), + includeClaudeSkills ? loadProjectSkills(params.ctx.directory) : Promise.resolve({}), loadOpencodeGlobalSkills(), - loadOpencodeProjectSkills(), + loadOpencodeProjectSkills(params.ctx.directory), ]); params.config.command = { diff --git a/src/plugin/tool-registry.ts b/src/plugin/tool-registry.ts index 7236ddc48..14267fe0a 100644 --- a/src/plugin/tool-registry.ts +++ b/src/plugin/tool-registry.ts @@ -101,7 +101,7 @@ export function createToolRegistry(args: { getSessionID: getSessionIDForMcp, }) - const commands = discoverCommandsSync() + const commands = discoverCommandsSync(ctx.directory) const slashcommandTool = createSlashcommandTool({ commands, skills: skillContext.mergedSkills, diff --git a/src/tools/slashcommand/command-discovery.ts b/src/tools/slashcommand/command-discovery.ts index b44581af7..50be5d024 100644 --- a/src/tools/slashcommand/command-discovery.ts +++ b/src/tools/slashcommand/command-discovery.ts @@ -48,12 +48,12 @@ function discoverCommandsFromDir(commandsDir: string, scope: CommandScope): Comm return commands } -export function discoverCommandsSync(): CommandInfo[] { +export function discoverCommandsSync(directory?: string): CommandInfo[] { const configDir = getOpenCodeConfigDir({ binary: "opencode" }) const userCommandsDir = join(getClaudeConfigDir(), "commands") - const projectCommandsDir = join(process.cwd(), ".claude", "commands") + const projectCommandsDir = join(directory ?? process.cwd(), ".claude", "commands") const opencodeGlobalDir = join(configDir, "command") - const opencodeProjectDir = join(process.cwd(), ".opencode", "command") + const opencodeProjectDir = join(directory ?? process.cwd(), ".opencode", "command") const userCommands = discoverCommandsFromDir(userCommandsDir, "user") const opencodeGlobalCommands = discoverCommandsFromDir(opencodeGlobalDir, "opencode")