Merge pull request #3875 from jollyxenon/fix/3846-opencode-config-dir-additive

fix(config): align OPENCODE_CONFIG_DIR with additive OpenCode semantics
This commit is contained in:
YeonGyu-Kim
2026-05-21 00:48:00 +09:00
committed by GitHub
9 changed files with 273 additions and 75 deletions
@@ -55,12 +55,32 @@ const NO_FRONTMATTER_AGENT = `Just a prompt with no frontmatter.`;
describe("claude-code-agent-loader", () => {
const dirs: string[] = [];
const originalEnv = {
CLAUDE_CONFIG_DIR: process.env.CLAUDE_CONFIG_DIR,
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.CLAUDE_CONFIG_DIR === undefined) {
delete process.env.CLAUDE_CONFIG_DIR
} else {
process.env.CLAUDE_CONFIG_DIR = originalEnv.CLAUDE_CONFIG_DIR
}
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 +224,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", () => {
@@ -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<string, ClaudeCode
}
export function loadOpencodeGlobalAgents(): Record<string, ClaudeCodeAgentConfig> {
const configDir = getOpenCodeConfigDir({ binary: "opencode" })
const opencodeAgentsDir = join(configDir, "agents")
const agents = loadAgentsFromDir(opencodeAgentsDir, "opencode")
const result: Record<string, ClaudeCodeAgentConfig> = 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
}