OpenAI Codex · Plugin marketplace

Migration only keeps fields that map

Week one on Codex, the fear is whether last year’s hooks, MCP, and plugins are still there. Detect prints a list. Import only keeps the slice that maps.

Course goalAfter this you can name three things. Migration sources are only Claude Code and Cursor. Each field takes one of three exits: copy as-is, rewrite, or drop. Plugins install from the user level only; remote marketplaces and sessions wait on the background.
Try it first · Field by field: keep or drop
One set of belongings through the allowlist: as-is, rewrite, cannot move
Source
memory
Session
Click a card to pull it out of the belongings. Changing source, the switch, or session age reruns. Cursor has no memory; repo-scoped plugins are dropped.
Detect Map Rewrite Import
As-is 0 Rewrite 0 Cannot move 0 Background 0
Source fields
Codex landing
Logic trail · each animation step maps to a source span
  1. Pick Cla or Cur by string; anything else falls to Claude Codemigration_source.rs L59
  2. Scan user-level config first; repo scope skips plugin detectdetect/mod.rs L330
  3. Sessions filter at 30 days and 50 items; older ones dropsessions/common.rs L43
  4. memory needs the source to support it, plus its own feature flagdetect/mod.rs L59
  5. hooks keep only sync command; a prompt is skipped wholehooks_cla.rs L135
  6. If an MCP command contains ${, drop the whole servermcp.rs L208
  7. Marketplace sources keep the git family and local dirs; npm is droppedsource_cla.rs L270
  8. Docs are renamed; product names rewrite to Codex on word boundariesrewrite.rs L39
  9. Sync import returns success for Sessions immediatelyservice.rs L437
  10. Background writes the thread later and installs remote plugins later; a closed flag refuses memoryprocessor.rs L184
Hit Play. Watch each field copy as-is, rewrite, or drop.
Three exitsFields that map copy or rewrite. prompt hooks, npm marketplaces, and MCP with placeholders never appear on the success list.
Plugins install from home onlyA repo enable list is not install authority. Remote marketplaces enter a pending-install queue; they do not finish in the sync phase.
Sync first returns a receiptA session can appear on the detect list. Sync import writes nothing; the thread is left to the background.
Teaching sketch:Belongings are course samples; hooks and MCP use only harmless commands. Line numbers on the logic trail map to openai/codex commit 4f39251a01.
Idea 1 · Three exits: as-is, rewrite, drop
What problem it solves

Someone said it would migrate from Claude Code automatically. You thought that meant copying the whole config directory. Next day one hook is missing, one MCP server is missing, and memory is not on the list. The missing hook used type: prompt. The missing MCP command wrote ${API_KEY}.

Swap to another teammate. He uses Cursor. Codex can see his skills and hooks, not his memory. Cursor plugin config in the repo is dropped outright.

What the idea is

The migration crate only reads external config in. How plugins run lives next door in core-plugins. The source type has two variants: Cla is Claude Code, Cur is Cursor. A string that is not cursor falls to Claude Code. If the caller omits the source, detect goes looking in ~/.claude.

Source:codex-rs/external-agent-migration/src/migration_source.rs lines 51–67

One detect run produces at most ten kinds of entries. Each kind’s import must have a place to go. Fields pass an allowlist first. Codex recognizes 11 hook events; Claude Code emits 27. A group whose names do not map disappears whole. A single hook’s type defaults to command; a mismatch is skipped. If an MCP command or url contains ${, drop the whole server. Marketplace sources keep github, git, and local dirs. file, url, npm, settings are dropped outright.

Source:codex-rs/external-agent-migration/src/model.rs lines 53–64 · codex-rs/hooks/src/lib.rs lines 23–35 · restored-src/src/entrypoints/sdk/coreSchemas.ts lines 355–383 · codex-rs/external-agent-migration/src/hooks_cla.rs lines 131–137 · codex-rs/external-agent-migration/src/mcp.rs lines 204–210 · codex-rs/external-agent-migration/src/source_cla.rs lines 270–274

Then the docs are renamed. CLAUDE.md becomes AGENTS.md. Product names rewrite to Codex on word boundaries. Cursor only matches case-sensitive Cursor, so ordinary English cursor is left alone.

Source:codex-rs/external-agent-migration/src/rewrite.rs lines 38–49

Source fields hook / MCP / marketplace Allowlist Name, type, source Copy as-is Sync command hook Rewrite Rename CLAUDE.md, rewrite MCP to TOML Drop prompt hook, npm marketplace, ${} MCP Input is one competitor field. What happens is an allowlist judgment. Output is copy, rewrite, or drop.
Teaching diagram:Migration is not a whole-directory copy. Each field takes its own exit.
Why it lasts

An importer’s usual shape is an allowlist plus three exits. Fields that do not map are dropped — do not rewrite them into a look-alike. A prompt hook is not stuffed into command. MCP with placeholders is not blindly expanded. Rewrite it in another language and this table still works.

Idea 2 · Plugins install from the user level; slow work goes to the background
What problem it solves

Claude Code lets repo settings carry an enabled-plugin list, so a teammate clone gets the same plugins automatically. For Codex, a repo enable list is not install authority. Installing would write user config. An untrusted repo that wants to put executables on your machine has to have that road cut first.

What the idea is

Detect only scans plugins in home. A comment is hardcoded: repo-controlled settings cannot be install authority. The moment Cursor sees a repo root, plugin detect returns empty. Import seeing a nonempty working directory reports repository-scoped plugin migration is not allowed.

Source:codex-rs/external-agent-migration/src/detect/mod.rs lines 330–332 · codex-rs/external-agent-migration/src/migration_source.rs lines 116–125 · codex-rs/external-agent-migration/src/plugins.rs lines 26–36

Local plugins install on the spot. Remote marketplaces enter a pending-install queue. Sessions are two cuts too: they can sit on the detect list, and sync import() returns success for Sessions without writing anything. What actually becomes a Codex thread is a background task in app-server. The caller gets an import_id first.

Source:codex-rs/external-agent-migration/src/service.rs line 437 · codex-rs/app-server/src/external_agent_migration/session_importer.rs lines 100–111

When Codex recognizes a marketplace list, it looks up the first existing file on a fixed set of relative paths. Two of the four paths are its own; two are the competitor’s. The same lookup serves both “user added a marketplace” and “migrate a marketplace from a competitor.”

Source:codex-rs/core-plugins/src/marketplace.rs lines 20–25

Detect home plus repo Sync import Config, hooks, local plugins import_id Hand back a receipt first Background writes the thread Sessions sync is a no-op Background installs remote plugins git clone may take tens of seconds Input is the detect list. What happens: finish what can land on disk now. Output is a receipt plus background work.
Teaching sequence:The sync phase admits “we saw it.” Slow work is left to the background.
Why it lasts

Install authority for executables must sit at the user level. For a product that faces arbitrary git clones, home-only is safer. Sync first returns a receipt and slow work goes to the background — that is the usual shape of a long-job API.

Idea 3 · A nonempty target is not overwritten
What problem it solves

The scariest product switch is overwriting config you already edited. If the target hooks.json already has content, a whole-file replace would wash away handwritten hooks.

What the idea is

If the target hook file is nonempty, the whole item is cancelled. An existing config.toml only fills missing keys. Same-named MCP servers keep the old one. The first migration feels like filling blanks. Click migrate a second time and most entries stay off the list because they already exist.

Source:codex-rs/external-agent-migration/src/hooks_common.rs lines 13–19

memory has another door. The feature flag is off by default; the stage is UnderDevelopment. Detect does not look at this flag; the pre-import processor does. Off returns external agent memory import is disabled. Import copies by bytes and does not redact.

Source:codex-rs/features/src/lib.rs lines 998–1003 · codex-rs/app-server/src/external_agent_migration/processor.rs lines 180–185

Fields that map copy or rewrite. Fields that do not map are dropped.
Why it lasts

Import fills blanks. Files the user already wrote, migration must not touch. memory is off by default because another agent’s project memory would land on disk in the clear.

Side-by-side · The same batch of extensions: should they be read in

DeepSeek Harness: no migration, install again

DSH’s plugin door is a thin pnpm forwarder. It initializes a profile, runs pnpm in the profile directory, then reconciles the list from the install result. Plugins are npm packages. There is no Claude Code detect and no Cursor hook import. Switching products means you reinstall. The cost is a user tax. The gain is not keeping permanent compatibility with a settings schema that keeps growing fields.

Checked apps/cli/src/plugin.ts lines 120–133 · 2026-08-22

Grok: its own marketplace, no competitor migration

Grok copies from the marketplace root plus a relative path into its own install registry, and writes provenance. Relative paths ban .., absolute paths, and drive letters. If there is no plugin.json but a SKILL.md at the root, it synthesizes a list. The marketplace object is its own directory plus a source record. It does not read plugins the user enabled in another home.

Checked crates/codegen/xai-grok-plugin-marketplace/src/installer.rs lines 36–45 · 2026-08-22 · Grok · Marketplace discovery and trust
Classroom Exercise
01

Which fields enter the list

Four Claude Code assets sit on the table: a PreToolUse type: command hook, a same-event type: prompt hook, an MCP whose command contains ${API_KEY}, and a session from 40 days ago. Using the allowlist above, list what should and should not appear on the detect list.

One more: when does the caller get an import_id, and when does this session become a thread. If memory with the flag off is sent to import, which door turns it back.

Takeaway:Migration is detect plus map plus rewrite. Fields that map copy or rewrite; the rest drop. Plugins install from the user level; sessions and remote marketplaces go to the background. A nonempty target is not overwritten.