fix(install): merge config instead of overwriting on opencode target

The sync path's mergeJsonConfigAtKey had incoming entries overwriting
existing user entries on conflict. Reverse the spread order so user
config wins, matching the install path's existing behavior.

Also add merge feedback logging and a test for the sync merge path.

Fixes #125

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matt Van Horn
2026-03-08 21:43:39 -07:00
parent 69f2a96e66
commit 1db76800f9
3 changed files with 41 additions and 1 deletions

View File

@@ -3,6 +3,7 @@ import { promises as fs } from "fs"
import path from "path"
import os from "os"
import { writeOpenCodeBundle } from "../src/targets/opencode"
import { mergeJsonConfigAtKey } from "../src/sync/json-config"
import type { OpenCodeBundle } from "../src/types/opencode"
async function exists(filePath: string): Promise<boolean> {
@@ -254,3 +255,38 @@ describe("writeOpenCodeBundle", () => {
expect(backupContent).toBe("old content\n")
})
})
describe("mergeJsonConfigAtKey", () => {
test("preserves existing user entries on conflict", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "json-merge-"))
const configPath = path.join(tempDir, "opencode.json")
// User has an existing MCP server config
const existingConfig = {
model: "my-model",
mcp: {
"user-server": { type: "local", command: ["uvx", "user-srv"] },
},
}
await fs.writeFile(configPath, JSON.stringify(existingConfig, null, 2))
// Plugin tries to add its own server and override user-server
await mergeJsonConfigAtKey({
configPath,
key: "mcp",
incoming: {
"plugin-server": { type: "local", command: ["uvx", "plugin-srv"] },
"user-server": { type: "local", command: ["uvx", "plugin-override"] },
},
})
const merged = JSON.parse(await fs.readFile(configPath, "utf8"))
// User's top-level keys preserved
expect(merged.model).toBe("my-model")
// Plugin server added
expect(merged.mcp["plugin-server"]).toBeDefined()
// User's server NOT overwritten by plugin
expect(merged.mcp["user-server"].command[1]).toBe("user-srv")
})
})