Compare commits

..

1 Commits

Author SHA1 Message Date
YeonGyu-Kim
f6ca8bc934 fix(config): support oh-my-openagent.jsonc as alternative config file name (fixes #2624)
The project was renamed from oh-my-opencode to oh-my-openagent.
Users creating oh-my-openagent.jsonc expect it to be loaded.

Now all config file discovery paths check for oh-my-openagent as
a fallback when oh-my-opencode is not found:
- plugin-config.ts (user-level and project-level)
- doctor config check
- LSP server config loader
- opencode-config-dir paths

oh-my-opencode remains the primary name for backward compatibility.

🤖 Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 12:08:47 +09:00
8 changed files with 57 additions and 147 deletions

View File

@@ -9,8 +9,11 @@ import { loadAvailableModelsFromCache } from "./model-resolution-cache"
import { getModelResolutionInfoWithOverrides } from "./model-resolution"
import type { OmoConfig } from "./model-resolution-types"
const PACKAGE_NAME_ALT = "oh-my-openagent"
const USER_CONFIG_BASE = join(getOpenCodeConfigDir({ binary: "opencode" }), PACKAGE_NAME)
const USER_CONFIG_BASE_ALT = join(getOpenCodeConfigDir({ binary: "opencode" }), PACKAGE_NAME_ALT)
const PROJECT_CONFIG_BASE = join(process.cwd(), ".opencode", PACKAGE_NAME)
const PROJECT_CONFIG_BASE_ALT = join(process.cwd(), ".opencode", PACKAGE_NAME_ALT)
interface ConfigValidationResult {
exists: boolean
@@ -24,9 +27,15 @@ function findConfigPath(): string | null {
const projectConfig = detectConfigFile(PROJECT_CONFIG_BASE)
if (projectConfig.format !== "none") return projectConfig.path
const projectConfigAlt = detectConfigFile(PROJECT_CONFIG_BASE_ALT)
if (projectConfigAlt.format !== "none") return projectConfigAlt.path
const userConfig = detectConfigFile(USER_CONFIG_BASE)
if (userConfig.format !== "none") return userConfig.path
const userConfigAlt = detectConfigFile(USER_CONFIG_BASE_ALT)
if (userConfigAlt.format !== "none") return userConfigAlt.path
return null
}

View File

@@ -101,13 +101,7 @@ export function createAutoRetryHelpers(deps: HookDeps) {
return
}
const agentSettings = resolvedAgent
? pluginConfig?.agents?.[resolvedAgent as keyof typeof pluginConfig.agents]
: undefined
const retryModelPayload = buildRetryModelPayload(newModel, agentSettings ? {
variant: agentSettings.variant,
reasoningEffort: agentSettings.reasoningEffort,
} : undefined)
const retryModelPayload = buildRetryModelPayload(newModel)
if (!retryModelPayload) {
log(`[${HOOK_NAME}] Invalid model format (missing provider prefix): ${newModel}`)
const state = sessionStates.get(sessionID)

View File

@@ -1,114 +0,0 @@
import { describe, test, expect } from "bun:test"
import { buildRetryModelPayload } from "./retry-model-payload"
describe("buildRetryModelPayload", () => {
test("should return undefined for empty model string", () => {
// given
const model = ""
// when
const result = buildRetryModelPayload(model)
// then
expect(result).toBeUndefined()
})
test("should return undefined for model without provider prefix", () => {
// given
const model = "kimi-k2.5"
// when
const result = buildRetryModelPayload(model)
// then
expect(result).toBeUndefined()
})
test("should parse provider and model ID", () => {
// given
const model = "chutes/kimi-k2.5"
// when
const result = buildRetryModelPayload(model)
// then
expect(result).toEqual({
model: { providerID: "chutes", modelID: "kimi-k2.5" },
})
})
test("should include variant from model string", () => {
// given
const model = "anthropic/claude-sonnet-4-5 high"
// when
const result = buildRetryModelPayload(model)
// then
expect(result).toEqual({
model: { providerID: "anthropic", modelID: "claude-sonnet-4-5" },
variant: "high",
})
})
test("should use agent variant when model string has no variant", () => {
// given
const model = "chutes/kimi-k2.5"
const agentSettings = { variant: "max" }
// when
const result = buildRetryModelPayload(model, agentSettings)
// then
expect(result).toEqual({
model: { providerID: "chutes", modelID: "kimi-k2.5" },
variant: "max",
})
})
test("should prefer model string variant over agent variant", () => {
// given
const model = "anthropic/claude-sonnet-4-5 high"
const agentSettings = { variant: "max" }
// when
const result = buildRetryModelPayload(model, agentSettings)
// then
expect(result).toEqual({
model: { providerID: "anthropic", modelID: "claude-sonnet-4-5" },
variant: "high",
})
})
test("should include reasoningEffort from agent settings", () => {
// given
const model = "openai/gpt-5.4"
const agentSettings = { variant: "high", reasoningEffort: "xhigh" }
// when
const result = buildRetryModelPayload(model, agentSettings)
// then
expect(result).toEqual({
model: { providerID: "openai", modelID: "gpt-5.4" },
variant: "high",
reasoningEffort: "xhigh",
})
})
test("should not include reasoningEffort when agent settings has none", () => {
// given
const model = "chutes/kimi-k2.5"
const agentSettings = { variant: "medium" }
// when
const result = buildRetryModelPayload(model, agentSettings)
// then
expect(result).toEqual({
model: { providerID: "chutes", modelID: "kimi-k2.5" },
variant: "medium",
})
})
})

View File

@@ -2,29 +2,24 @@ import { parseModelString } from "../../tools/delegate-task/model-string-parser"
export function buildRetryModelPayload(
model: string,
agentSettings?: { variant?: string; reasoningEffort?: string },
): { model: { providerID: string; modelID: string }; variant?: string; reasoningEffort?: string } | undefined {
): { model: { providerID: string; modelID: string }; variant?: string } | undefined {
const parsedModel = parseModelString(model)
if (!parsedModel) {
return undefined
}
const variant = parsedModel.variant ?? agentSettings?.variant
const reasoningEffort = agentSettings?.reasoningEffort
const payload: { model: { providerID: string; modelID: string }; variant?: string; reasoningEffort?: string } = {
model: {
providerID: parsedModel.providerID,
modelID: parsedModel.modelID,
},
}
if (variant) {
payload.variant = variant
}
if (reasoningEffort) {
payload.reasoningEffort = reasoningEffort
}
return payload
return parsedModel.variant
? {
model: {
providerID: parsedModel.providerID,
modelID: parsedModel.modelID,
},
variant: parsedModel.variant,
}
: {
model: {
providerID: parsedModel.providerID,
modelID: parsedModel.modelID,
},
}
}

View File

@@ -160,18 +160,32 @@ export function loadPluginConfig(
directory: string,
ctx: unknown
): OhMyOpenCodeConfig {
// User-level config path - prefer .jsonc over .json
// User-level config path - prefer .jsonc over .json, try oh-my-openagent as fallback
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
const userBasePath = path.join(configDir, "oh-my-opencode");
const userDetected = detectConfigFile(userBasePath);
let userDetected = detectConfigFile(userBasePath);
if (userDetected.format === "none") {
const altUserBasePath = path.join(configDir, "oh-my-openagent");
const altDetected = detectConfigFile(altUserBasePath);
if (altDetected.format !== "none") {
userDetected = altDetected;
}
}
const userConfigPath =
userDetected.format !== "none"
? userDetected.path
: userBasePath + ".json";
// Project-level config path - prefer .jsonc over .json
// Project-level config path - prefer .jsonc over .json, try oh-my-openagent as fallback
const projectBasePath = path.join(directory, ".opencode", "oh-my-opencode");
const projectDetected = detectConfigFile(projectBasePath);
let projectDetected = detectConfigFile(projectBasePath);
if (projectDetected.format === "none") {
const altProjectBasePath = path.join(directory, ".opencode", "oh-my-openagent");
const altDetected = detectConfigFile(altProjectBasePath);
if (altDetected.format !== "none") {
projectDetected = altDetected;
}
}
const projectConfigPath =
projectDetected.format !== "none"
? projectDetected.path

View File

@@ -12,4 +12,5 @@ export type OpenCodeConfigPaths = {
configJsonc: string
packageJson: string
omoConfig: string
omoConfigAlt: string
}

View File

@@ -84,6 +84,7 @@ export function getOpenCodeConfigPaths(options: OpenCodeConfigDirOptions): OpenC
configJsonc: join(configDir, "opencode.jsonc"),
packageJson: join(configDir, "package.json"),
omoConfig: join(configDir, "oh-my-opencode.json"),
omoConfigAlt: join(configDir, "oh-my-openagent.json"),
}
}

View File

@@ -37,9 +37,19 @@ export function loadJsonFile<T>(path: string): T | null {
export function getConfigPaths(): { project: string; user: string; opencode: string } {
const cwd = process.cwd()
const configDir = getOpenCodeConfigDir({ binary: "opencode" })
const projectDetected = detectConfigFile(join(cwd, ".opencode", "oh-my-opencode"))
const projectPath = projectDetected.format !== "none"
? projectDetected.path
: detectConfigFile(join(cwd, ".opencode", "oh-my-openagent")).path
const userDetected = detectConfigFile(join(configDir, "oh-my-opencode"))
const userPath = userDetected.format !== "none"
? userDetected.path
: detectConfigFile(join(configDir, "oh-my-openagent")).path
return {
project: detectConfigFile(join(cwd, ".opencode", "oh-my-opencode")).path,
user: detectConfigFile(join(configDir, "oh-my-opencode")).path,
project: projectPath,
user: userPath,
opencode: detectConfigFile(join(configDir, "opencode")).path,
}
}