fix(converters): preserve Codex config on no-MCP install (#564)

This commit is contained in:
alexph-dev
2026-04-16 00:06:42 +07:00
committed by GitHub
parent ee8e402897
commit ed778e62f1
2 changed files with 65 additions and 7 deletions

View File

@@ -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(),

View File

@@ -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]",