fix(converters): preserve Codex config on no-MCP install (#564)
This commit is contained in:
@@ -116,12 +116,29 @@ async function readFileSafe(filePath: string): Promise<string> {
|
||||
export function mergeCodexConfig(existingContent: string, mcpToml: string | null): string | null {
|
||||
// Strip current and previous managed blocks
|
||||
let stripped = existingContent
|
||||
let removedManagedBlock = false
|
||||
for (const [start, end] of [[MANAGED_START_MARKER, MANAGED_END_MARKER], [PREV_START_MARKER, PREV_END_MARKER]]) {
|
||||
stripped = stripped.replace(
|
||||
const next = stripped.replace(
|
||||
new RegExp(`${escapeForRegex(start)}[\\s\\S]*?${escapeForRegex(end)}\\n?`, "g"),
|
||||
"",
|
||||
)
|
||||
if (next !== stripped) removedManagedBlock = true
|
||||
stripped = next
|
||||
}
|
||||
|
||||
// No MCP servers to write — only remove bounded managed blocks. Do not strip
|
||||
// unmarked legacy markers here: old Codex config files may contain user
|
||||
// settings after "# Generated by compound-plugin", and there is no safe
|
||||
// boundary for deleting only plugin-owned TOML.
|
||||
if (!mcpToml) {
|
||||
if (!existingContent) return null
|
||||
const legacyMarkerIndex = stripped.indexOf(LEGACY_MARKER)
|
||||
if (legacyMarkerIndex !== -1) {
|
||||
return stripped.slice(0, legacyMarkerIndex).trimEnd()
|
||||
}
|
||||
return removedManagedBlock ? stripped.trimEnd() : existingContent
|
||||
}
|
||||
|
||||
stripped = stripped.trimEnd()
|
||||
|
||||
// Strip from legacy markers to end of content (old formats wrote everything after the marker)
|
||||
@@ -133,12 +150,6 @@ export function mergeCodexConfig(existingContent: string, mcpToml: string | null
|
||||
}
|
||||
}
|
||||
|
||||
// No MCP servers to write — return cleaned content, or null only if there was never a file
|
||||
if (!mcpToml) {
|
||||
if (!existingContent) return null
|
||||
return cleaned
|
||||
}
|
||||
|
||||
const managedBlock = [
|
||||
MANAGED_START_MARKER,
|
||||
mcpToml.trim(),
|
||||
|
||||
@@ -524,6 +524,53 @@ describe("mergeCodexConfig", () => {
|
||||
expect(result).toContain("[mcp_servers.new]")
|
||||
})
|
||||
|
||||
test("preserves unmarked legacy content when no MCP servers are incoming", () => {
|
||||
const existing = [
|
||||
'model = "gpt-5.4"',
|
||||
"",
|
||||
"# Generated by compound-plugin",
|
||||
"",
|
||||
"[projects.example]",
|
||||
'trust_level = "trusted"',
|
||||
].join("\n")
|
||||
|
||||
const result = mergeCodexConfig(existing, null)!
|
||||
expect(result).toContain("# Generated by compound-plugin")
|
||||
expect(result).toContain("[projects.example]")
|
||||
expect(result).toContain('trust_level = "trusted"')
|
||||
})
|
||||
|
||||
test("strips bounded legacy MCP block when no MCP servers are incoming", () => {
|
||||
const existing = [
|
||||
"[user]",
|
||||
'model = "gpt-5.4"',
|
||||
"",
|
||||
"# MCP servers synced from Claude Code",
|
||||
"",
|
||||
"[mcp_servers.old]",
|
||||
'command = "old"',
|
||||
].join("\n")
|
||||
|
||||
const result = mergeCodexConfig(existing, null)!
|
||||
expect(result).toContain("[user]")
|
||||
expect(result).not.toContain("# MCP servers synced from Claude Code")
|
||||
expect(result).not.toContain("[mcp_servers.old]")
|
||||
})
|
||||
|
||||
test("returns existing content byte-for-byte when no MCP servers or managed blocks exist", () => {
|
||||
const existing = [
|
||||
'model = "gpt-5.4"',
|
||||
"",
|
||||
"# Generated by compound-plugin",
|
||||
"",
|
||||
"[projects.example]",
|
||||
'trust_level = "trusted"',
|
||||
"",
|
||||
].join("\n")
|
||||
|
||||
expect(mergeCodexConfig(existing, null)).toBe(existing)
|
||||
})
|
||||
|
||||
test("preserves user config before unmarked legacy format", () => {
|
||||
const existing = [
|
||||
"[user]",
|
||||
|
||||
Reference in New Issue
Block a user