Merge pull request #4279 from MoerAI/fix/migrate-orphan-lsp-config-key
fix(migration): drop orphan 'lsp' config key so users see LSP moved to .opencode/lsp.json (fixes #4225)
This commit is contained in:
@@ -199,3 +199,48 @@ describe("migrateConfigFile backup skipping", () => {
|
||||
expect(backupFiles.length).toBe(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe("migrateConfigFile orphan lsp key", () => {
|
||||
test("removes the obsolete 'lsp' key from rawConfig and from the persisted file", () => {
|
||||
// given - a v3-era config with a populated lsp block that the v4 schema silently strips
|
||||
const workdir = createWorkdir()
|
||||
const configPath = join(workdir, "oh-my-opencode.json")
|
||||
const rawConfig: Record<string, unknown> = {
|
||||
lsp: {
|
||||
typescript: { command: ["typescript-language-server", "--stdio"] },
|
||||
rust: { command: ["rust-analyzer"] },
|
||||
},
|
||||
}
|
||||
writeFileSync(configPath, JSON.stringify(rawConfig, null, 2) + "\n")
|
||||
|
||||
// when
|
||||
const needsWrite = migrateConfigFile(configPath, rawConfig)
|
||||
|
||||
// then - the in-memory config and the persisted file have both lost the lsp key
|
||||
expect(needsWrite).toBe(true)
|
||||
expect(rawConfig.lsp).toBeUndefined()
|
||||
const persistedConfig = JSON.parse(readFileSync(configPath, "utf-8")) as Record<string, unknown>
|
||||
expect(persistedConfig.lsp).toBeUndefined()
|
||||
})
|
||||
|
||||
test("leaves the config alone when no 'lsp' key is present", () => {
|
||||
// given - a config that never had an lsp block
|
||||
const workdir = createWorkdir()
|
||||
const configPath = join(workdir, "oh-my-opencode.json")
|
||||
const rawConfig: Record<string, unknown> = {
|
||||
agents: {
|
||||
sisyphus: { model: "anthropic/claude-opus-4-7" },
|
||||
},
|
||||
}
|
||||
writeFileSync(configPath, JSON.stringify(rawConfig, null, 2) + "\n")
|
||||
|
||||
// when
|
||||
const needsWrite = migrateConfigFile(configPath, rawConfig)
|
||||
|
||||
// then - no rewrite triggered by the lsp migrator, agents block untouched
|
||||
expect(needsWrite).toBe(false)
|
||||
expect((rawConfig.agents as Record<string, Record<string, unknown>>).sisyphus.model).toBe(
|
||||
"anthropic/claude-opus-4-7",
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -105,6 +105,26 @@ export function migrateConfigFile(
|
||||
needsWrite = true
|
||||
}
|
||||
|
||||
// The legacy `lsp` config key was retired when LSP moved from native plugin
|
||||
// tools to the `lsp` MCP server backed by `packages/lsp-tools-mcp`. The
|
||||
// server now reads its server map from `.opencode/lsp.json` in the project
|
||||
// root (path is hard-coded in `src/mcp/lsp.ts` via the
|
||||
// `LSP_TOOLS_MCP_PROJECT_CONFIG` env var passed to the stdio MCP). The Zod
|
||||
// schema strips unknown keys silently, so without this migration a stale
|
||||
// `lsp` block lingers in the user's config file with no signal that it has
|
||||
// stopped doing anything.
|
||||
if (copy.lsp !== undefined) {
|
||||
const droppedServers = copy.lsp && typeof copy.lsp === "object"
|
||||
? Object.keys(copy.lsp as Record<string, unknown>)
|
||||
: []
|
||||
log(
|
||||
"Removed obsolete 'lsp' config key from oh-my-opencode config. Custom LSP servers are now configured in .opencode/lsp.json at the project root (consumed by the 'lsp' MCP server). Move any server definitions there to restore them.",
|
||||
{ configPath, droppedServers },
|
||||
)
|
||||
delete copy.lsp
|
||||
needsWrite = true
|
||||
}
|
||||
|
||||
if (copy.experimental && typeof copy.experimental === "object") {
|
||||
const experimental = copy.experimental as Record<string, unknown>
|
||||
if ("hashline_edit" in experimental) {
|
||||
|
||||
Reference in New Issue
Block a user