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 {
|
export function mergeCodexConfig(existingContent: string, mcpToml: string | null): string | null {
|
||||||
// Strip current and previous managed blocks
|
// Strip current and previous managed blocks
|
||||||
let stripped = existingContent
|
let stripped = existingContent
|
||||||
|
let removedManagedBlock = false
|
||||||
for (const [start, end] of [[MANAGED_START_MARKER, MANAGED_END_MARKER], [PREV_START_MARKER, PREV_END_MARKER]]) {
|
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"),
|
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()
|
stripped = stripped.trimEnd()
|
||||||
|
|
||||||
// Strip from legacy markers to end of content (old formats wrote everything after the marker)
|
// 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 = [
|
const managedBlock = [
|
||||||
MANAGED_START_MARKER,
|
MANAGED_START_MARKER,
|
||||||
mcpToml.trim(),
|
mcpToml.trim(),
|
||||||
|
|||||||
@@ -524,6 +524,53 @@ describe("mergeCodexConfig", () => {
|
|||||||
expect(result).toContain("[mcp_servers.new]")
|
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", () => {
|
test("preserves user config before unmarked legacy format", () => {
|
||||||
const existing = [
|
const existing = [
|
||||||
"[user]",
|
"[user]",
|
||||||
|
|||||||
Reference in New Issue
Block a user