Merge pull request #1142 from medhatgalal/AI/fix-codex-markerless-gsd-merge
Fix duplicate Codex GSD agent blocks without marker
This commit is contained in:
@@ -1002,6 +1002,10 @@ function generateCodexConfigBlock(agents) {
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
function stripCodexGsdAgentSections(content) {
|
||||
return content.replace(/^\[agents\.gsd-[^\]]+\]\n(?:(?!\[)[^\n]*\n?)*/gm, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip GSD sections from Codex config.toml content.
|
||||
* Returns cleaned content, or null if file would be empty.
|
||||
@@ -1027,7 +1031,7 @@ function stripGsdFromCodexConfig(content) {
|
||||
cleaned = cleaned.replace(/^default_mode_request_user_input\s*=\s*true\s*\n?/m, '');
|
||||
|
||||
// Remove [agents.gsd-*] sections (from header to next section or EOF)
|
||||
cleaned = cleaned.replace(/^\[agents\.gsd-[^\]]+\]\n(?:(?!\[)[^\n]*\n?)*/gm, '');
|
||||
cleaned = stripCodexGsdAgentSections(cleaned);
|
||||
|
||||
// Remove [features] section if now empty (only header, no keys before next section)
|
||||
cleaned = cleaned.replace(/^\[features\]\s*\n(?=\[|$)/m, '');
|
||||
@@ -1061,7 +1065,7 @@ function mergeCodexConfig(configPath, gsdBlock) {
|
||||
let before = existing.substring(0, markerIndex).trimEnd();
|
||||
if (before) {
|
||||
// Strip any GSD-managed sections that leaked above the marker from previous installs
|
||||
before = before.replace(/^\[agents\.gsd-[^\]]+\]\n(?:(?!\[)[^\n]*\n?)*/gm, '');
|
||||
before = stripCodexGsdAgentSections(before);
|
||||
before = before.replace(/^\[agents\]\n(?:(?!\[)[^\n]*\n?)*/m, '');
|
||||
before = before.replace(/\n{3,}/g, '\n\n').trimEnd();
|
||||
|
||||
@@ -1074,7 +1078,14 @@ function mergeCodexConfig(configPath, gsdBlock) {
|
||||
|
||||
// Case 3: No marker — append GSD block
|
||||
let content = existing;
|
||||
content = content.trimEnd() + '\n\n' + gsdBlock + '\n';
|
||||
content = stripCodexGsdAgentSections(content);
|
||||
content = content.replace(/\n{3,}/g, '\n\n').trimEnd();
|
||||
|
||||
if (content) {
|
||||
content = content + '\n\n' + gsdBlock + '\n';
|
||||
} else {
|
||||
content = gsdBlock + '\n';
|
||||
}
|
||||
|
||||
fs.writeFileSync(configPath, content);
|
||||
}
|
||||
|
||||
@@ -354,6 +354,36 @@ describe('mergeCodexConfig', () => {
|
||||
assert.ok(content.includes('[agents.gsd-executor]'), 'has agent');
|
||||
});
|
||||
|
||||
test('case 3 strips existing [agents.gsd-*] sections before appending fresh block', () => {
|
||||
const configPath = path.join(tmpDir, 'config.toml');
|
||||
const existing = [
|
||||
'[model]',
|
||||
'name = "o3"',
|
||||
'',
|
||||
'[agents.custom-agent]',
|
||||
'description = "user agent"',
|
||||
'',
|
||||
'',
|
||||
'[agents.gsd-executor]',
|
||||
'description = "old"',
|
||||
'config_file = "agents/gsd-executor.toml"',
|
||||
'',
|
||||
].join('\n');
|
||||
fs.writeFileSync(configPath, existing);
|
||||
|
||||
mergeCodexConfig(configPath, sampleBlock);
|
||||
|
||||
const content = fs.readFileSync(configPath, 'utf8');
|
||||
const gsdAgentCount = (content.match(/^\[agents\.gsd-executor\]\s*$/gm) || []).length;
|
||||
const markerCount = (content.match(new RegExp(GSD_CODEX_MARKER.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')) || []).length;
|
||||
|
||||
assert.ok(content.includes('[model]'), 'preserves user content');
|
||||
assert.ok(content.includes('[agents.custom-agent]'), 'preserves non-GSD agent section');
|
||||
assert.strictEqual(gsdAgentCount, 1, 'keeps exactly one GSD agent section');
|
||||
assert.strictEqual(markerCount, 1, 'adds exactly one marker block');
|
||||
assert.ok(!/\n{3,}# GSD Agent Configuration/.test(content), 'does not leave extra blank lines before marker block');
|
||||
});
|
||||
|
||||
test('idempotent: re-merge produces same result', () => {
|
||||
const configPath = path.join(tmpDir, 'config.toml');
|
||||
mergeCodexConfig(configPath, sampleBlock);
|
||||
|
||||
Reference in New Issue
Block a user