From 850dc3a4cf0be4ced7adde008e012d53996c7fea Mon Sep 17 00:00:00 2001 From: jollyxenon <1378319314@qq.com> Date: Sat, 9 May 2026 00:26:45 +0800 Subject: [PATCH] fix(agent-loader): load opencode global agents from both config roots --- .../claude-code-agent-loader/loader.test.ts | 37 +++++++++++++++++++ .../claude-code-agent-loader/loader.ts | 20 ++++++---- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/src/features/claude-code-agent-loader/loader.test.ts b/src/features/claude-code-agent-loader/loader.test.ts index 672ac76eb..06ad2b2e6 100644 --- a/src/features/claude-code-agent-loader/loader.test.ts +++ b/src/features/claude-code-agent-loader/loader.test.ts @@ -55,12 +55,26 @@ const NO_FRONTMATTER_AGENT = `Just a prompt with no frontmatter.`; describe("claude-code-agent-loader", () => { const dirs: string[] = []; + const originalEnv = { + OPENCODE_CONFIG_DIR: process.env.OPENCODE_CONFIG_DIR, + XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME, + } afterEach(() => { for (const dir of dirs) { rmSync(dir, { recursive: true, force: true }); } dirs.length = 0; + if (originalEnv.OPENCODE_CONFIG_DIR === undefined) { + delete process.env.OPENCODE_CONFIG_DIR + } else { + process.env.OPENCODE_CONFIG_DIR = originalEnv.OPENCODE_CONFIG_DIR + } + if (originalEnv.XDG_CONFIG_HOME === undefined) { + delete process.env.XDG_CONFIG_HOME + } else { + process.env.XDG_CONFIG_HOME = originalEnv.XDG_CONFIG_HOME + } }); function trackDir(dir: string): string { @@ -204,6 +218,29 @@ describe("claude-code-agent-loader", () => { const result = loadOpencodeGlobalAgents() expect(result).toEqual({}) }) + + test("loads agents from both the custom and default opencode config directories", () => { + const root = trackDir(mkdtempSync(join(tmpdir(), "agent-loader-opencode-global-"))) + const defaultAgentsDir = join(root, "xdg", "opencode", "agents") + const customAgentsDir = join(root, "custom-opencode", "agents") + + mkdirSync(defaultAgentsDir, { recursive: true }) + mkdirSync(customAgentsDir, { recursive: true }) + + writeFileSync(join(defaultAgentsDir, "default-agent.md"), BASIC_AGENT, "utf-8") + writeFileSync( + join(customAgentsDir, "custom-agent.md"), + `---\nname: custom-agent\ndescription: Custom agent\n---\nFrom custom config.`, + "utf-8", + ) + + process.env.XDG_CONFIG_HOME = join(root, "xdg") + process.env.OPENCODE_CONFIG_DIR = join(root, "custom-opencode") + + const result = loadOpencodeGlobalAgents() + + expect(Object.keys(result).sort()).toEqual(["custom-agent", "test-agent"]) + }) }) describe("tools parsing", () => { diff --git a/src/features/claude-code-agent-loader/loader.ts b/src/features/claude-code-agent-loader/loader.ts index 380f479eb..d6bd166c0 100644 --- a/src/features/claude-code-agent-loader/loader.ts +++ b/src/features/claude-code-agent-loader/loader.ts @@ -3,7 +3,7 @@ import { join } from "path" import { isMarkdownFile } from "../../shared/file-utils" import { getClaudeConfigDir } from "../../shared" import type { AgentScope, ClaudeCodeAgentConfig, LoadedAgent } from "./types" -import { getOpenCodeConfigDir } from "../../shared/opencode-config-dir" +import { getOpenCodeConfigDirs } from "../../shared/opencode-config-dir" import { parseMarkdownAgentFile } from "./agent-definitions-loader" function loadAgentsFromDir(agentsDir: string, scope: AgentScope): LoadedAgent[] { @@ -51,14 +51,20 @@ export function loadProjectAgents(directory?: string): Record { - const configDir = getOpenCodeConfigDir({ binary: "opencode" }) - const opencodeAgentsDir = join(configDir, "agents") - const agents = loadAgentsFromDir(opencodeAgentsDir, "opencode") - const result: Record = Object.create(null) - for (const agent of agents) { - result[agent.name] = agent.config + const configDirs = getOpenCodeConfigDirs({ binary: "opencode" }) + + for (const configDir of configDirs) { + const opencodeAgentsDir = join(configDir, "agents") + const agents = loadAgentsFromDir(opencodeAgentsDir, "opencode") + + for (const agent of agents) { + if (!(agent.name in result)) { + result[agent.name] = agent.config + } + } } + return result }