Skip to content

Commit b26a02a

Browse files
tmchowclaude
andcommitted
Address PR review feedback (#479)
- Strip unmarked legacy format (# Generated by compound-plugin) during merge to prevent duplicate TOML tables on first upgrade from old writer Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 6bd9e1d commit b26a02a

2 files changed

Lines changed: 76 additions & 4 deletions

File tree

src/targets/codex.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const MANAGED_END_MARKER = "# END Compound Engineering plugin MCP"
1010
const PREV_START_MARKER = "# BEGIN compound-plugin Claude Code MCP"
1111
const PREV_END_MARKER = "# END compound-plugin Claude Code MCP"
1212
const LEGACY_MARKER = "# MCP servers synced from Claude Code"
13+
const UNMARKED_LEGACY_MARKER = "# Generated by compound-plugin"
1314

1415
export async function writeCodexBundle(outputRoot: string, bundle: CodexBundle): Promise<void> {
1516
const codexRoot = resolveCodexRoot(outputRoot)
@@ -119,10 +120,14 @@ export function mergeCodexConfig(existingContent: string, mcpToml: string | null
119120
}
120121
stripped = stripped.trimEnd()
121122

122-
const legacyMarkerIndex = stripped.indexOf(LEGACY_MARKER)
123-
const cleaned = legacyMarkerIndex === -1
124-
? stripped
125-
: stripped.slice(0, legacyMarkerIndex).trimEnd()
123+
// Strip from legacy markers to end of content (old formats wrote everything after the marker)
124+
let cleaned = stripped
125+
for (const marker of [LEGACY_MARKER, UNMARKED_LEGACY_MARKER]) {
126+
const idx = cleaned.indexOf(marker)
127+
if (idx !== -1) {
128+
cleaned = cleaned.slice(0, idx).trimEnd()
129+
}
130+
}
126131

127132
// No MCP servers to write — return cleaned content, or null only if there was never a file
128133
if (!mcpToml) {

tests/codex-writer.test.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,39 @@ describe("writeCodexBundle", () => {
171171
expect(config).toContain("[user]")
172172
})
173173

174+
test("migrates unmarked legacy format (# Generated by compound-plugin)", async () => {
175+
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "codex-unmarked-"))
176+
const codexRoot = path.join(tempRoot, ".codex")
177+
const configPath = path.join(codexRoot, "config.toml")
178+
179+
// Simulate old writer output: entire file was just the generated config
180+
await fs.mkdir(codexRoot, { recursive: true })
181+
await fs.writeFile(configPath, [
182+
"# Generated by compound-plugin",
183+
"",
184+
"[mcp_servers.old]",
185+
'command = "old"',
186+
"",
187+
].join("\n"))
188+
189+
const bundle: CodexBundle = {
190+
prompts: [],
191+
skillDirs: [],
192+
generatedSkills: [],
193+
mcpServers: { fresh: { command: "new" } },
194+
}
195+
196+
await writeCodexBundle(codexRoot, bundle)
197+
198+
const config = await fs.readFile(configPath, "utf8")
199+
expect(config).not.toContain("# Generated by compound-plugin")
200+
expect(config).not.toContain("[mcp_servers.old]")
201+
expect(config).toContain("# BEGIN Compound Engineering plugin MCP")
202+
expect(config).toContain("[mcp_servers.fresh]")
203+
// Should have exactly one BEGIN marker (no duplication)
204+
expect(config.match(/# BEGIN Compound Engineering plugin MCP/g)?.length).toBe(1)
205+
})
206+
174207
test("strips stale managed block when plugin has no MCP servers", async () => {
175208
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "codex-stale-"))
176209
const codexRoot = path.join(tempRoot, ".codex")
@@ -443,6 +476,40 @@ describe("mergeCodexConfig", () => {
443476
expect(result).not.toContain("# BEGIN")
444477
})
445478

479+
test("strips unmarked legacy format (# Generated by compound-plugin)", () => {
480+
const existing = [
481+
"# Generated by compound-plugin",
482+
"",
483+
"[mcp_servers.old]",
484+
'command = "old"',
485+
"",
486+
].join("\n")
487+
488+
const result = mergeCodexConfig(existing, "[mcp_servers.new]\ncommand = \"new\"")!
489+
expect(result).not.toContain("# Generated by compound-plugin")
490+
expect(result).not.toContain("[mcp_servers.old]")
491+
expect(result).toContain("# BEGIN Compound Engineering plugin MCP")
492+
expect(result).toContain("[mcp_servers.new]")
493+
})
494+
495+
test("preserves user config before unmarked legacy format", () => {
496+
const existing = [
497+
"[user]",
498+
'model = "gpt-4.1"',
499+
"",
500+
"# Generated by compound-plugin",
501+
"",
502+
"[mcp_servers.old]",
503+
'command = "old"',
504+
].join("\n")
505+
506+
const result = mergeCodexConfig(existing, "[mcp_servers.new]\ncommand = \"new\"")!
507+
expect(result).toContain("[user]")
508+
expect(result).not.toContain("# Generated by compound-plugin")
509+
expect(result).not.toContain("[mcp_servers.old]")
510+
expect(result).toContain("[mcp_servers.new]")
511+
})
512+
446513
test("returns null when no existing content and no mcpToml", () => {
447514
expect(mergeCodexConfig("", null)).toBeNull()
448515
})

0 commit comments

Comments
 (0)