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
+46 -32
View File
@@ -6,7 +6,7 @@ import {
log,
containsPath,
deepMerge,
getOpenCodeConfigDir,
getOpenCodeConfigDirs,
addConfigLoadError,
parseJsonc,
detectPluginConfigFile,
@@ -281,25 +281,23 @@ export function loadPluginConfig(
directory: string,
ctx: unknown
): OhMyOpenCodeConfig {
// User-level config path - prefer .jsonc over .json
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
const userDetected = detectPluginConfigFile(configDir);
let userConfigPath =
userDetected.format !== "none"
? userDetected.path
: path.join(configDir, `${CONFIG_BASENAME}.json`);
const userConfigDirs = [...getOpenCodeConfigDirs({ binary: "opencode" })].reverse()
const userConfigLayers = userConfigDirs.map((configDir) => {
const detected = detectPluginConfigFile(configDir)
if (userDetected.legacyPath) {
log("Canonical plugin config detected alongside legacy config. Remove the legacy file to avoid confusion.", {
canonicalPath: userDetected.path,
legacyPath: userDetected.legacyPath,
});
}
if (detected.legacyPath) {
log("Canonical plugin config detected alongside legacy config. Remove the legacy file to avoid confusion.", {
canonicalPath: detected.path,
legacyPath: detected.legacyPath,
})
}
// Auto-copy legacy config file to canonical name if needed
if (userDetected.format !== "none") {
userConfigPath = resolveConfigPathAfterLegacyMigration(userConfigPath)
}
const configPath = detected.format !== "none"
? resolveConfigPathAfterLegacyMigration(detected.path)
: null
return { configDir, configPath }
})
// Pin the walk to $HOME only when the start directory is inside it. Outside
// $HOME the walker would otherwise reach FS root and surface unrelated configs
@@ -331,20 +329,36 @@ export function loadPluginConfig(
},
)
// Load user config first (base). Parse empty config through Zod to apply field defaults.
const userConfig = loadConfigFromPath(userConfigPath, ctx)
const userGitMasterOverrides = loadExplicitGitMasterOverrides(userConfigPath)
let config: OhMyOpenCodeConfig = OhMyOpenCodeConfigSchema.parse({})
let mergedUserGitMasterOverrides: Record<string, unknown> | null = null
if (userConfig?.agent_definitions) {
userConfig.agent_definitions = resolveAgentDefinitionPaths(
userConfig.agent_definitions,
configDir,
null
)
for (const userLayer of userConfigLayers) {
if (!userLayer.configPath) continue
const userConfig = loadConfigFromPath(userLayer.configPath, ctx)
const userGitMasterOverrides = loadExplicitGitMasterOverrides(userLayer.configPath)
if (userConfig?.agent_definitions) {
userConfig.agent_definitions = resolveAgentDefinitionPaths(
userConfig.agent_definitions,
userLayer.configDir,
null,
)
}
if (userConfig) {
config = mergeConfigs(config, userConfig)
}
if (userGitMasterOverrides) {
mergedUserGitMasterOverrides = {
...(mergedUserGitMasterOverrides ?? {}),
...userGitMasterOverrides,
}
}
}
let config: OhMyOpenCodeConfig =
userConfig ?? OhMyOpenCodeConfigSchema.parse({});
const userMcpEnvAllowlist = config.mcp_env_allowlist ?? []
const canonicalAncestorPathsFarthestFirst = [...canonicalAncestorPathsNearestFirst].reverse()
const defaultGitMaster = OhMyOpenCodeConfigSchema.parse({}).git_master
@@ -374,7 +388,7 @@ export function loadPluginConfig(
}
}
if (userGitMasterOverrides || ancestorGitMasterOverridesFarthestFirst.length > 0) {
if (mergedUserGitMasterOverrides || ancestorGitMasterOverridesFarthestFirst.length > 0) {
const mergedAncestorGitMaster: Record<string, unknown> = {}
for (const override of ancestorGitMasterOverridesFarthestFirst) {
Object.assign(mergedAncestorGitMaster, override)
@@ -383,7 +397,7 @@ export function loadPluginConfig(
...config,
git_master: {
...defaultGitMaster,
...(userGitMasterOverrides ?? {}),
...(mergedUserGitMasterOverrides ?? {}),
...mergedAncestorGitMaster,
},
}
@@ -395,7 +409,7 @@ export function loadPluginConfig(
// expansion in .mcp.json files. See commit 316d2504 for context.
config = {
...config,
mcp_env_allowlist: userConfig?.mcp_env_allowlist ?? [],
mcp_env_allowlist: userMcpEnvAllowlist,
};
log("Final merged config", {