From c38c2a59c72504a72a537123dc4b6026875cdfac Mon Sep 17 00:00:00 2001 From: Luci <22126563+LuciNyan@users.noreply.github.com> Date: Fri, 25 Apr 2025 08:32:33 +0800 Subject: [PATCH] fix(utils): save config (#578) ## Description When `saveConfig` is called, the project doc is incorrectly saved into user instructions. This change ensures that only user instructions are saved to `instructions.md` during saveConfig, preventing data corruption. close: #576 --------- Co-authored-by: Thibault Sottiaux --- codex-cli/src/utils/config.ts | 9 ++++++-- codex-cli/tests/config.test.tsx | 41 +++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/codex-cli/src/utils/config.ts b/codex-cli/src/utils/config.ts index e837c3a5..d2b59680 100644 --- a/codex-cli/src/utils/config.ts +++ b/codex-cli/src/utils/config.ts @@ -157,6 +157,7 @@ export const PRETTY_PRINT = Boolean(process.env["PRETTY_PRINT"] || ""); export const PROJECT_DOC_MAX_BYTES = 32 * 1024; // 32 kB const PROJECT_DOC_FILENAMES = ["codex.md", ".codex.md", "CODEX.md"]; +const PROJECT_DOC_SEPARATOR = "\n\n--- project-doc ---\n\n"; export function discoverProjectDocPath(startDir: string): string | null { const cwd = resolvePath(startDir); @@ -311,7 +312,7 @@ export const loadConfig = ( const combinedInstructions = [userInstructions, projectDoc] .filter((s) => s && s.trim() !== "") - .join("\n\n--- project-doc ---\n\n"); + .join(PROJECT_DOC_SEPARATOR); // Treat empty string ("" or whitespace) as absence so we can fall back to // the latest DEFAULT_MODEL. @@ -462,5 +463,9 @@ export const saveConfig = ( writeFileSync(targetPath, JSON.stringify(configToSave, null, 2), "utf-8"); } - writeFileSync(instructionsPath, config.instructions, "utf-8"); + // Take everything before the first PROJECT_DOC_SEPARATOR (or the whole string if none). + const [userInstructions = ""] = config.instructions.split( + PROJECT_DOC_SEPARATOR, + ); + writeFileSync(instructionsPath, userInstructions, "utf-8"); }; diff --git a/codex-cli/tests/config.test.tsx b/codex-cli/tests/config.test.tsx index e94b5b29..831208a1 100644 --- a/codex-cli/tests/config.test.tsx +++ b/codex-cli/tests/config.test.tsx @@ -234,3 +234,44 @@ test("loads and saves providers correctly", () => { expect(mergedConfig.providers["openai"]).toBeDefined(); } }); + +test("saves and loads instructions with project doc separator correctly", () => { + const userInstructions = "user specific instructions"; + const projectDoc = "project specific documentation"; + const combinedInstructions = `${userInstructions}\n\n--- project-doc ---\n\n${projectDoc}`; + + const testConfig = { + model: "test-model", + instructions: combinedInstructions, + notify: false, + }; + + saveConfig(testConfig, testConfigPath, testInstructionsPath); + + expect(memfs[testInstructionsPath]).toBe(userInstructions); + + const loadedConfig = loadConfig(testConfigPath, testInstructionsPath, { + disableProjectDoc: true, + }); + expect(loadedConfig.instructions).toBe(userInstructions); +}); + +test("handles empty user instructions when saving with project doc separator", () => { + const projectDoc = "project specific documentation"; + const combinedInstructions = `\n\n--- project-doc ---\n\n${projectDoc}`; + + const testConfig = { + model: "test-model", + instructions: combinedInstructions, + notify: false, + }; + + saveConfig(testConfig, testConfigPath, testInstructionsPath); + + expect(memfs[testInstructionsPath]).toBe(""); + + const loadedConfig = loadConfig(testConfigPath, testInstructionsPath, { + disableProjectDoc: true, + }); + expect(loadedConfig.instructions).toBe(""); +});