From a26117d944b4cdf27653e5853c1301c2cacb23e5 Mon Sep 17 00:00:00 2001 From: Biem Date: Mon, 4 May 2026 19:47:08 +0800 Subject: [PATCH] fix: resolve ${CLAUDE_PLUGIN_ROOT} in plugin commands, agents, and skills When loading Claude Code plugins, the hooks JSON config already had ${CLAUDE_PLUGIN_ROOT} replaced via resolvePluginPath. However, command markdown bodies, agent prompts, and skill content were passed through as-is, leaving the variable unresolved. This caused plugin commands (e.g. codex:review) to fail when executed in OpenCode because the shell had no CLAUDE_PLUGIN_ROOT environment variable set. Apply resolvePluginPath to: - command-loader.ts: command body before wrapping in template - agent-loader.ts: agent prompt body - skill-loader.ts: skill body after @path resolution --- src/features/claude-code-plugin-loader/agent-loader.ts | 3 ++- src/features/claude-code-plugin-loader/command-loader.ts | 4 +++- src/features/claude-code-plugin-loader/skill-loader.ts | 4 +++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/features/claude-code-plugin-loader/agent-loader.ts b/src/features/claude-code-plugin-loader/agent-loader.ts index 128b9f229..a2184fcfb 100644 --- a/src/features/claude-code-plugin-loader/agent-loader.ts +++ b/src/features/claude-code-plugin-loader/agent-loader.ts @@ -4,6 +4,7 @@ import { parseFrontmatter } from "../../shared/frontmatter" import { isMarkdownFile } from "../../shared/file-utils" import { log } from "../../shared/logger" import { parseToolsConfig } from "../../shared/parse-tools-config" +import { resolvePluginPath } from "./plugin-path-resolver" import type { AgentFrontmatter, ClaudeCodeAgentConfig } from "../claude-code-agent-loader/types" import { mapClaudeModelToOpenCode } from "../claude-code-agent-loader/claude-model-mapper" import type { LoadedPlugin } from "./types" @@ -38,7 +39,7 @@ export function loadPluginAgents(plugins: LoadedPlugin[]): Record(content) - const wrappedTemplate = `\n${body.trim()}\n\n\n\n$ARGUMENTS\n` + const resolvedBody = resolvePluginPath(body, plugin.installPath) + const wrappedTemplate = `\n${resolvedBody.trim()}\n\n\n\n$ARGUMENTS\n` const formattedDescription = `(plugin: ${plugin.name}) ${data.description || ""}` const definition = { diff --git a/src/features/claude-code-plugin-loader/skill-loader.ts b/src/features/claude-code-plugin-loader/skill-loader.ts index 1649055ef..b91f6855d 100644 --- a/src/features/claude-code-plugin-loader/skill-loader.ts +++ b/src/features/claude-code-plugin-loader/skill-loader.ts @@ -4,6 +4,7 @@ import { parseFrontmatter } from "../../shared/frontmatter" import { resolveSymlink } from "../../shared/file-utils" import { sanitizeModelField } from "../../shared/model-sanitizer" import { resolveSkillPathReferences } from "../../shared/skill-path-resolver" +import { resolvePluginPath } from "./plugin-path-resolver" import { log } from "../../shared/logger" import type { CommandDefinition } from "../claude-code-command-loader/types" import type { SkillMetadata } from "../opencode-skill-loader/types" @@ -39,7 +40,8 @@ export function loadPluginSkillsAsCommands( const formattedDescription = `(plugin: ${plugin.name} - Skill) ${originalDescription}` const resolvedBody = resolveSkillPathReferences(body.trim(), resolvedPath) - const wrappedTemplate = `\nBase directory for this skill: ${resolvedPath}/\nFile references (@path) in this skill are relative to this directory.\n\n${resolvedBody}\n\n\n\n$ARGUMENTS\n` + const pluginResolvedBody = resolvePluginPath(resolvedBody, plugin.installPath) + const wrappedTemplate = `\nBase directory for this skill: ${resolvedPath}/\nFile references (@path) in this skill are relative to this directory.\n\n${pluginResolvedBody}\n\n\n\n$ARGUMENTS\n` const definition = { name: namespacedName,