Preserve existing Codex agent TOMLs on reinstall#4711
Conversation
|
All contributors have signed the CLA. Thank you! ✅ |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1f981f7b8d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const materialized: string[] = [] | ||
| const entries = await readdir(agentsDir, { withFileTypes: true }) | ||
| for (const entry of entries) { | ||
| if (!entry.name.endsWith(".toml")) continue |
There was a problem hiding this comment.
Limit preservation to OMO-managed agent TOMLs
When a user already has their own CODEX_HOME/agents/custom.toml symlink, this loop treats it the same as an OMO-installed agent and the later rm/writeFile replaces the symlink with a regular copy, breaking dotfiles-managed or otherwise user-managed Codex agents. Both installer paths call this preservation step before linking OMO agents, so it should filter to the bundled/managed agent names or verify the symlink target is an OMO marketplace snapshot before materializing it.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
5 issues found across 10 files
Confidence score: 2/5
- There is a concrete data-loss risk in
src/cli/install-codex/preserve-existing-codex-agents.tsandpackages/omo-codex/scripts/install/preserve-existing-agents.mjs: the existing symlink is removed before the replacement file is safely written, so write failures can erase preserved agent TOML. exists()insrc/cli/install-codex/preserve-existing-codex-agents.tsswallows alllstaterrors, which can hide real filesystem problems and silently skip preservation behavior when users most need it.materializeExistingCodexAgentFilesis currently unguarded insrc/cli/install-codex/install-codex.ts, so normal FS exceptions (permissions/races) can crash install; combined with theLinkedAgent.targetsemantic mismatch insrc/cli/install-codex/link-cached-plugin-agents.ts, this creates meaningful regression risk.- Pay close attention to
src/cli/install-codex/preserve-existing-codex-agents.ts,packages/omo-codex/scripts/install/preserve-existing-agents.mjs,src/cli/install-codex/install-codex.ts,src/cli/install-codex/link-cached-plugin-agents.ts- preservation flow can lose user config, mask FS failures, or fail during install.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/cli/install-codex/preserve-existing-codex-agents.ts">
<violation number="1" location="src/cli/install-codex/preserve-existing-codex-agents.ts:17">
P1: Symlink is removed before replacement file is safely written, risking loss of agent config on write failure</violation>
<violation number="2" location="src/cli/install-codex/preserve-existing-codex-agents.ts:35">
P2: `exists()` helper swallows all lstat errors, masking real filesystem failures and silently skipping preservation logic.</violation>
</file>
<file name="src/cli/install-codex/link-cached-plugin-agents.ts">
<violation number="1" location="src/cli/install-codex/link-cached-plugin-agents.ts:34">
P2: `LinkedAgent.target` has mixed semantics depending on `managed` flag: it is the bundled source path when `managed=true`, but the destination (`linkPath`) when `managed=false`. This silently breaks the contract that `target` represents the source artifact.</violation>
</file>
<file name="src/cli/install-codex/install-codex.ts">
<violation number="1" location="src/cli/install-codex/install-codex.ts:83">
P2: `materializeExistingCodexAgentFiles` call is unguarded: permission errors, race-condition ENOENT on `readdir`/`lstat`, and other FS exceptions can crash the entire install. Wrap it in a try-catch so agent-preservation failures don't hard-block the installer.</violation>
</file>
<file name="packages/omo-codex/scripts/install/preserve-existing-agents.mjs">
<violation number="1" location="packages/omo-codex/scripts/install/preserve-existing-agents.mjs:19">
P2: Don't remove the old agent symlink before the replacement file is safely written; a write failure here can erase the user's preserved TOML.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| if (!stat.isSymbolicLink()) continue | ||
|
|
||
| const content = await readExistingSymlinkContent(agentPath) | ||
| await rm(agentPath, { force: true }) |
There was a problem hiding this comment.
P1: Symlink is removed before replacement file is safely written, risking loss of agent config on write failure
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/cli/install-codex/preserve-existing-codex-agents.ts, line 17:
<comment>Symlink is removed before replacement file is safely written, risking loss of agent config on write failure</comment>
<file context>
@@ -0,0 +1,42 @@
+ if (!stat.isSymbolicLink()) continue
+
+ const content = await readExistingSymlinkContent(agentPath)
+ await rm(agentPath, { force: true })
+ if (content === null) continue
+
</file context>
| } | ||
| } | ||
|
|
||
| async function exists(path: string): Promise<boolean> { |
There was a problem hiding this comment.
P2: exists() helper swallows all lstat errors, masking real filesystem failures and silently skipping preservation logic.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/cli/install-codex/preserve-existing-codex-agents.ts, line 35:
<comment>`exists()` helper swallows all lstat errors, masking real filesystem failures and silently skipping preservation logic.</comment>
<file context>
@@ -0,0 +1,42 @@
+ }
+}
+
+async function exists(path: string): Promise<boolean> {
+ try {
+ await lstat(path)
</file context>
| const installed = platform === "win32" | ||
| ? await copyIfMissing(linkPath, agentPath) | ||
| : await symlinkIfMissing(linkPath, agentPath) | ||
| linked.push({ name: basename(agentPath), path: linkPath, target: installed.target, managed: installed.managed }) |
There was a problem hiding this comment.
P2: LinkedAgent.target has mixed semantics depending on managed flag: it is the bundled source path when managed=true, but the destination (linkPath) when managed=false. This silently breaks the contract that target represents the source artifact.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/cli/install-codex/link-cached-plugin-agents.ts, line 34:
<comment>`LinkedAgent.target` has mixed semantics depending on `managed` flag: it is the bundled source path when `managed=true`, but the destination (`linkPath`) when `managed=false`. This silently breaks the contract that `target` represents the source artifact.</comment>
<file context>
@@ -27,16 +28,14 @@ export async function linkCachedPluginAgents(input: {
+ const installed = platform === "win32"
+ ? await copyIfMissing(linkPath, agentPath)
+ : await symlinkIfMissing(linkPath, agentPath)
+ linked.push({ name: basename(agentPath), path: linkPath, target: installed.target, managed: installed.managed })
}
await writeManifest(
</file context>
| installed.push(plugin) | ||
| } | ||
|
|
||
| await materializeExistingCodexAgentFiles(codexHome) |
There was a problem hiding this comment.
P2: materializeExistingCodexAgentFiles call is unguarded: permission errors, race-condition ENOENT on readdir/lstat, and other FS exceptions can crash the entire install. Wrap it in a try-catch so agent-preservation failures don't hard-block the installer.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/cli/install-codex/install-codex.ts, line 83:
<comment>`materializeExistingCodexAgentFiles` call is unguarded: permission errors, race-condition ENOENT on `readdir`/`lstat`, and other FS exceptions can crash the entire install. Wrap it in a try-catch so agent-preservation failures don't hard-block the installer.</comment>
<file context>
@@ -79,6 +80,7 @@ export async function runCodexInstaller(options: CodexInstallOptions = {}): Prom
installed.push(plugin)
}
+ await materializeExistingCodexAgentFiles(codexHome)
const agentSourceRoots = await agentSourceRootsForInstall({
codexHome,
</file context>
| await materializeExistingCodexAgentFiles(codexHome) | |
| try { | |
| await materializeExistingCodexAgentFiles(codexHome) | |
| } catch (error) { | |
| log(`Warning: could not materialize existing agent files: ${error instanceof Error ? error.message : String(error)}`) | |
| } |
| if (!stat.isSymbolicLink()) continue; | ||
|
|
||
| const content = await readExistingSymlinkContent(agentPath); | ||
| await rm(agentPath, { force: true }); |
There was a problem hiding this comment.
P2: Don't remove the old agent symlink before the replacement file is safely written; a write failure here can erase the user's preserved TOML.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/omo-codex/scripts/install/preserve-existing-agents.mjs, line 19:
<comment>Don't remove the old agent symlink before the replacement file is safely written; a write failure here can erase the user's preserved TOML.</comment>
<file context>
@@ -0,0 +1,35 @@
+ if (!stat.isSymbolicLink()) continue;
+
+ const content = await readExistingSymlinkContent(agentPath);
+ await rm(agentPath, { force: true });
+ if (content === null) continue;
+
</file context>
|
I have read the CLA Document and I hereby sign the CLA |
Summary
CODEX_HOME/agents/*.tomlfiles instead of replacing them during Codex/LazyCodex reinstalls..installed-agents.jsonso cleanup does not delete them, while still registering their[agents.*]config entries.packages/omo-codex/scriptsinstaller path and update ultrawork docs.Refs code-yeongyu/lazycodex#18.
Verification
bun run typecheckbun test src/cli/install-codex/link-cached-plugin-agents.test.ts src/cli/install-codex/preserve-existing-codex-agents.test.tsnode --test packages/omo-codex/scripts/install-agent-links.test.mjsNotes
bun run test:codexcurrently stops before reaching the Codex tests becausebuild:lsp-tools-mcprunsnpm --prefix packages/lsp-tools-mcp ci, but that package has nopackage-lock.jsonin this checkout.Summary by cubic
Preserves existing agent TOMLs in
CODEX_HOME/agentsduring Codex/LazyCodex reinstalls and marketplace updates so local edits aren’t overwritten. Converts old Unix symlinked agents to user-owned files and only installs missing agents.*.tomlinto regular files before refreshing the marketplace snapshot..installed-agents.jsonso preserved user files aren’t deleted on cleanup.packages/omo-codex/scripts; updated ultrawork docs and added tests.Written for commit 1f981f7. Summary will update on new commits.