fix(config): fall back to legacy path when migration fails and use canonical basename everywhere (#3133)

Root cause: loadPluginConfig() unconditionally switched userConfigPath to the
canonical name after calling migrateLegacyConfigFile(), even when migration
failed (e.g. file lock on Windows, permission denied). This left the config
path pointing to a non-existent file, so the plugin config silently loaded
as empty defaults.

Additionally, several fallback/default paths were hardcoded to the legacy
'oh-my-opencode' basename instead of using CONFIG_BASENAME ('oh-my-openagent'),
causing CLI config commands (writeOmoConfig, detectCurrentConfig) to write to
the wrong filename.

Changes:
- plugin-config.ts: check migrateLegacyConfigFile() return value; only switch
  to canonical path if migration succeeded OR the canonical file already exists
- opencode-config-dir.ts: use CONFIG_BASENAME for omoConfig path in
  getOpenCodeConfigPaths()
- config-context.ts: getOmoConfigPath() now uses detectPluginConfigFile() to
  find whichever name variant actually exists on disk
- plugin-config.ts: default fallback paths use CONFIG_BASENAME instead of
  hardcoded legacy name
- Added test: loadPluginConfig still loads config when migration fails
  (read-only directory simulation)
This commit is contained in:
YeonGyu-Kim
2026-04-05 14:26:54 +09:00
parent 2ae9a757fb
commit d7c2b6249b
5 changed files with 62 additions and 11 deletions
+37 -1
View File
@@ -1,5 +1,5 @@
import { afterEach, describe, expect, it, mock, spyOn } from "bun:test";
import { existsSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"
import { chmodSync, existsSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"
import { tmpdir } from "node:os"
import { join } from "node:path"
import * as shared from "./shared"
@@ -355,6 +355,42 @@ describe("loadPluginConfig", () => {
expect(reloadedConfig.agents?.oracle?.model).toBe("openai/gpt-5.4")
})
it("should still load config from legacy path when migration fails", () => {
// given - legacy config exists but canonical path is not writable
const rootDir = mkdtempSync(join(tmpdir(), "omo-plugin-config-fail-"))
const userConfigDir = join(rootDir, "user-config")
const projectDir = join(rootDir, "project")
const projectConfigDir = join(projectDir, ".opencode")
const legacyConfigPath = join(projectConfigDir, "oh-my-opencode.json")
tempDirs.push(rootDir)
mkdirSync(userConfigDir, { recursive: true })
mkdirSync(projectConfigDir, { recursive: true })
writeFileSync(legacyConfigPath, JSON.stringify({ agents: { oracle: { model: "openai/gpt-5.4" } } }))
// Make the directory read-only so migration write fails
// (simulates Windows file lock / permission issues)
if (process.platform !== "win32") {
chmodSync(projectConfigDir, 0o555)
}
spyOn(shared, "getOpenCodeConfigDir").mockReturnValue(userConfigDir)
// when
let config: OhMyOpenCodeConfig
try {
config = loadPluginConfig(projectDir, {})
} finally {
// Restore permissions for cleanup
if (process.platform !== "win32") {
chmodSync(projectConfigDir, 0o755)
}
}
// then - should still load the config from legacy path
expect(config.agents?.oracle?.model).toBe("openai/gpt-5.4")
})
it("should load migrated legacy project config on the first load", () => {
// given
const rootDir = mkdtempSync(join(tmpdir(), "omo-plugin-config-first-load-"))