From 3d2eb6e471f70daa80e78d6b495799f607b7aa79 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Sun, 12 Apr 2026 18:18:11 +0900 Subject: [PATCH] fix(config): preserve explicit git_master overrides during merge Prevent project config defaults from re-enabling git_master fields that users explicitly disabled. Rebuild git_master from explicit user and project keys so include_co_authored_by and commit_footer merge predictably. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- src/plugin-config.test.ts | 88 +++++++++++++++++++++++++++++++++++++++ src/plugin-config.ts | 34 +++++++++++++++ 2 files changed, 122 insertions(+) diff --git a/src/plugin-config.test.ts b/src/plugin-config.test.ts index 8ac0bdee5..70cee2fce 100644 --- a/src/plugin-config.test.ts +++ b/src/plugin-config.test.ts @@ -423,4 +423,92 @@ describe("loadPluginConfig", () => { expect(existsSync(canonicalConfigPath)).toBe(true) expect(config.agents?.oracle?.model).toBe("openai/gpt-5.4") }) + + it("should preserve explicit user git_master settings when project config omits git_master", async () => { + // given + const rootDir = mkdtempSync(join(tmpdir(), "omo-plugin-config-git-master-user-")) + const userConfigDir = join(rootDir, "user-config") + const projectDir = join(rootDir, "project") + const projectConfigDir = join(projectDir, ".opencode") + + tempDirs.push(rootDir) + mkdirSync(userConfigDir, { recursive: true }) + mkdirSync(projectConfigDir, { recursive: true }) + + writeFileSync( + join(userConfigDir, "oh-my-openagent.jsonc"), + JSON.stringify({ + git_master: { + commit_footer: false, + include_co_authored_by: false, + }, + }) + ) + + writeFileSync( + join(projectConfigDir, "oh-my-openagent.jsonc"), + JSON.stringify({ + agents: { + hephaestus: { model: "openai/gpt-5.4" }, + }, + }) + ) + + process.env.OPENCODE_CONFIG_DIR = userConfigDir + + // when + const { loadPluginConfig } = await importFreshPluginConfigModule() + const config = loadPluginConfig(projectDir, {}) + + // then + expect(config.git_master).toEqual({ + commit_footer: false, + include_co_authored_by: false, + git_env_prefix: "GIT_MASTER=1", + }) + }) + + it("should merge explicit git_master keys from user and project configs", async () => { + // given + const rootDir = mkdtempSync(join(tmpdir(), "omo-plugin-config-git-master-merge-")) + const userConfigDir = join(rootDir, "user-config") + const projectDir = join(rootDir, "project") + const projectConfigDir = join(projectDir, ".opencode") + + tempDirs.push(rootDir) + mkdirSync(userConfigDir, { recursive: true }) + mkdirSync(projectConfigDir, { recursive: true }) + + writeFileSync( + join(userConfigDir, "oh-my-openagent.jsonc"), + JSON.stringify({ + git_master: { + commit_footer: false, + include_co_authored_by: false, + }, + }) + ) + + writeFileSync( + join(projectConfigDir, "oh-my-openagent.jsonc"), + JSON.stringify({ + git_master: { + commit_footer: true, + }, + }) + ) + + process.env.OPENCODE_CONFIG_DIR = userConfigDir + + // when + const { loadPluginConfig } = await importFreshPluginConfigModule() + const config = loadPluginConfig(projectDir, {}) + + // then + expect(config.git_master).toEqual({ + commit_footer: true, + include_co_authored_by: false, + git_env_prefix: "GIT_MASTER=1", + }) + }) }) diff --git a/src/plugin-config.ts b/src/plugin-config.ts index 10b9161d3..0b79e96d3 100644 --- a/src/plugin-config.ts +++ b/src/plugin-config.ts @@ -13,6 +13,26 @@ import { import { migrateLegacyConfigFile } from "./shared/migrate-legacy-config-file"; import { CONFIG_BASENAME, LEGACY_CONFIG_BASENAME } from "./shared/plugin-identity"; +function loadExplicitGitMasterOverrides(configPath: string): Record | undefined { + try { + if (!fs.existsSync(configPath)) { + return undefined + } + + const content = fs.readFileSync(configPath, "utf-8") + const rawConfig = parseJsonc>(content) + const gitMaster = rawConfig.git_master + + if (gitMaster && typeof gitMaster === "object" && !Array.isArray(gitMaster)) { + return gitMaster as Record + } + } catch { + return undefined + } + + return undefined +} + const PARTIAL_STRING_ARRAY_KEYS = new Set([ "disabled_mcps", "disabled_agents", @@ -229,15 +249,29 @@ 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 = userConfig ?? OhMyOpenCodeConfigSchema.parse({}); // Override with project config + const defaultGitMaster = OhMyOpenCodeConfigSchema.parse({}).git_master const projectConfig = loadConfigFromPath(projectConfigPath, ctx); + const projectGitMasterOverrides = loadExplicitGitMasterOverrides(projectConfigPath) if (projectConfig) { config = mergeConfigs(config, projectConfig); } + if (userGitMasterOverrides || projectGitMasterOverrides) { + config = { + ...config, + git_master: { + ...defaultGitMaster, + ...(userGitMasterOverrides ?? {}), + ...(projectGitMasterOverrides ?? {}), + }, + } + } + config = { ...config, mcp_env_allowlist: userConfig?.mcp_env_allowlist ?? [],