Migrating from Claude Code to Codex is not a search-replace
By Codcompass Team··10 min read
Engineering Reliable Transitions Between AI Coding Agents: A Claude Code to Codex Migration Framework
Current Situation Analysis
AI-powered coding assistants have rapidly evolved from simple prompt interfaces into complex operational environments. When engineering teams decide to transition from one agent CLI to another—specifically from Claude Code to Codex—the migration is frequently treated as a configuration file swap. Developers copy instruction files, adjust JSON keys, and assume the workflow will function identically. This approach consistently fails in production because it confuses documentation with infrastructure.
The core pain point is behavioral drift. A functional agent setup is not defined by its visible markdown files or basic configuration blocks. It is defined by the execution layer: event-driven hooks that enforce safety policies, permission boundaries that control filesystem access, MCP server scopes that dictate tool availability, plugin bundles that package automation logic, and session archives that preserve conversational state. When these components are migrated using naive string replacement or direct file duplication, the agent either operates with broken policies, loses critical tool access, or silently escalates permissions.
This problem is routinely overlooked because the surface-level artifacts are trivial to move. CLAUDE.md becomes AGENTS.md. .mcp.json becomes .codex/config.toml. The migration appears complete until a developer runs a complex workflow and discovers that hooks are firing incorrectly, permission prompts are bypassed, or session context has been lost. The misunderstanding stems from treating agent CLIs as text editors rather than runtime environments. They require the same rigor as migrating CI/CD pipelines, infrastructure-as-code, or service mesh configurations.
Production evidence confirms this pattern. A mature Claude Code environment typically spans multiple directories and scopes: .claude/settings.json for runtime flags, .claude/commands/ for custom slash workflows, .claude/agents/ for subagent definitions, project-level .mcp.json manifests, user-scoped session archives under ~/.claude/projects/, and plugin bundles that bundle hooks, scripts, and LSP configurations. Codex expects a different topology: AGENTS.md for instructions, .codex/config.toml for core settings, .codex/hooks.json for event routing, skill registries for automation, and a distinct session handoff protocol. Mapping file-to-file ignores execution context, leading to silent hook failures, permission drift, and broken tool chains.
WOW Moment: Key Findings
The critical insight emerges when we measure migration approaches across operational dimensions rather than file counts. Teams that treat migration as a behavioral translation exercise consistently outperform those that rely on configuration duplication.
Approach
Security Boundary
Context Preservation
Hook Fidelity
Rollback Complexity
Naive File Swap
High drift risk
Session corruption
Syntax-only mapping
Manual file restoration
Structured Behavior Mapping
Intent-aligned policies
State serialization
Semantic event routing
Version-controlled config diff
This finding matters because it reclassifies agent migration from a documentation task to an infrastructure engineering problem. Structured mapping ensures that safety policies, tool scopes, and automation logic are translated rather than copied. It enables teams to maintain audit trails, enforce conservative permission defaults, and verify workflows against actual tool execution rather than configuration syntax. The result is a predictable transition with measurable security posture and zero silent failures.
Core Solution
Migrating between agent CLIs requires a phased, intent-driven approach. The goal is not to replicate files but to preserve behavior. Below is a production-tested implementation strategy.
Before modifying any configuration, generate a complete inventory of the source environment. This inventory must explicitly exclude secret values. Reading or exporting tokens, API keys, or credentials during migration is a primary vector for credential leakage into version control.
// inventory-scanner.ts
import { readFileSync, readdirSync, statSync } from 'fs';
import { join } from 'path';
interface InventoryReport {
instructions: string[];
h
// Traverse .claude/ directory structure
const claudeDir = join(rootDir, '.claude');
if (statSync(claudeDir, { throwIfNoEntry: false })) {
// Parse settings.json for hooks & permissions
// Parse commands/ for custom slash workflows
// Parse agents/ for subagent definitions
// Extract secret variable names from env references
}
return report;
}
**Architecture Rationale:** Separating secret names from values forces explicit credential rotation in the target environment. It prevents automated migration scripts from accidentally committing sensitive data to Git history. The inventory acts as a contract between source and target, ensuring no component is silently dropped.
### Phase 2: Policy & Hook Translation
Hooks encode the safety model. They intercept tool execution, enforce filesystem boundaries, and trigger post-action validations. Copying hook JSON directly fails because event names, payload structures, and execution contexts differ between CLIs.
Map hooks by intent, not syntax:
| Source Event | Target Event | Translation Strategy |
|--------------|--------------|----------------------|
| `PreToolUse.Bash` | `PreToolUse.Bash` | Direct mapping; validate command allowlists |
| `PreToolUse.Edit` | `FileWrite` | Convert patch logic to target diff format |
| `PermissionRequest` | `ApprovalPolicy` | Map to on-request or sandbox mode |
| `SessionStart` | `Lifecycle.Init` | Trigger environment warmup scripts |
| `Stop` | `Lifecycle.Terminate` | Run cleanup or state serialization |
```json
// codex-hooks.json
{
"version": "2.1",
"routing": {
"PreToolUse": {
"bash": {
"handler": "scripts/validate-bash.sh",
"timeout_ms": 5000,
"fail_policy": "block"
},
"file_write": {
"handler": "scripts/check-diff-scope.js",
"allowed_paths": ["src/", "lib/"],
"blocked_patterns": ["*.env", "node_modules/"]
}
},
"Lifecycle": {
"init": {
"handler": "scripts/warm-cache.sh",
"async": true
}
}
}
}
Architecture Rationale: Hooks should be idempotent and fail-safe. Blocking on validation errors prevents unsafe tool execution. Async lifecycle hooks prevent startup latency from blocking the main agent loop. Explicit path allowlists reduce the blast radius of file operations.
Phase 3: MCP Server Scoping & Environment Forwarding
MCP servers expose external tools to the agent. Claude Code and Codex handle server discovery differently. Crucially, desktop MCP connectors must never be migrated to CLI environments. They operate under different threat models, consume different context windows, and often expose GUI automation tools irrelevant to terminal workflows.
Extract only CLI-scoped servers:
# Source environment extraction
claude mcp list --scope cli > cli-servers.txt
claude mcp get <server-name> --format json > server-def.json
Translate to target configuration with environment forwarding:
Architecture Rationale: Forwarding environment variables instead of hardcoding credentials ensures secrets remain in the host environment or secret manager. Sandbox modes restrict server capabilities by default. Timeout and retry policies prevent runaway tool calls from consuming context windows or rate limits.
Phase 4: Plugin Decomposition & Skill Mapping
Plugins in Claude Code are monolithic bundles. They may contain skills, commands, subagents, hooks, MCP definitions, scripts, output themes, and LSP configurations. Codex expects modular skills and explicit configuration. Decompose plugins before migration.
Architecture Rationale: Monolithic plugins obscure dependencies and make rollback difficult. Decomposition forces explicit mapping of automation logic to target skills, hooks to policy files, and scripts to executable handlers. Themes and LSP configs are typically UI/IDE concerns and should be handled separately or discarded if irrelevant to CLI execution.
Phase 5: Session State Handoff & Verification
Raw session files contain serialized conversation state, tool outputs, and internal agent metadata. Directly copying them between CLIs causes parsing errors, context corruption, or policy violations. Use structured handoff documents instead.
Repository state (branch, uncommitted changes, build status)
Executed commands and their outputs
Decisions made and rationale
Failed attempts and error logs
Pending tasks and next action
Architecture Rationale: Handoff documents decouple session state from agent internals. They are human-readable, version-controllable, and immune to parser mismatches. Long-running agent sessions degrade in quality over time; handoff forces context pruning and objective realignment before resuming in the target environment.
Pitfall Guide
1. Silent Permission Escalation
Explanation: Copying allow/deny rules without semantic mapping often results in broader access than intended. Source CLIs may use prefix matching while target CLIs use glob patterns or path hierarchies.
Fix: Default to on-request approval and workspace-write sandbox mode. Explicitly add known-safe paths. Never migrate bypassPermissions flags without manual review.
2. Desktop MCP Leakage into CLI
Explanation: Desktop MCP servers often include GUI automation, clipboard managers, or system monitors. Migrating them to a CLI agent introduces unnecessary context consumption and security surface area.
Fix: Filter servers by scope during inventory. Only migrate servers explicitly registered for CLI execution. Validate each server's tool list against terminal workflow requirements.
3. Hook Syntax Copying Without Semantic Mapping
Explanation: Hook payloads, event names, and execution contexts differ between CLIs. Direct JSON duplication causes silent failures or misfired policies.
Fix: Map events by intent. Rewrite handlers to match target payload structures. Implement explicit fail policies (block vs warn) and test hooks against dry-run tool calls.
4. Raw Session File Duplication
Explanation: Session files contain internal agent state, memory buffers, and parser-specific metadata. Copying them directly causes deserialization errors and context pollution.
Fix: Use structured handoff documents. Serialize objective, state, decisions, and pending tasks. Verify handoff readability before resuming in the target environment.
5. Plugin Bundle Monolith Migration
Explanation: Treating plugins as single units obscures dependencies, makes rollback difficult, and carries unnecessary assets (themes, LSP configs) into the target environment.
Fix: Decompose plugins into skills, commands, hooks, and scripts. Map each component explicitly. Discard UI/IDE-specific assets unless required for CLI output formatting.
6. Secret Value Extraction During Inventory
Explanation: Migration scripts that read .env files or settings JSON often extract actual token values. These values frequently end up in Git history, migration logs, or temporary files.
Fix: Extract only variable names. Require explicit credential rotation in the target environment. Use secret managers or environment injection rather than file-based storage.
7. Hook Idempotency Neglect
Explanation: Hooks that modify state or trigger external services without idempotency checks cause duplicate executions, rate limit exhaustion, or inconsistent filesystem states.
Fix: Design hooks to be idempotent. Use checksums, state files, or atomic operations. Implement timeout limits and explicit retry policies. Log execution traces for auditability.
Run inventory scan: Execute the environment scanner against your .claude/ directory. Export the report to migration-inventory.json. Verify that only secret names are captured.
Generate handoff document: Use the session handoff tool to extract your current workflow state. Review the markdown report for accuracy and completeness.
Apply conservative config: Copy the provided .codex/config.toml and .codex/hooks.json templates. Adjust allowed paths and MCP servers to match your project structure.
Validate with dry-run: Execute a test workflow using --dry-run or --simulate flags. Verify that hooks trigger correctly, permissions prompt as expected, and MCP servers respond within timeout limits.
Commit and monitor: Version control your new configuration. Monitor the first production session for permission prompts, hook failures, or context drift. Adjust allowlists and timeout policies based on observed behavior.
🎉 Mid-Year Sale — Unlock Full Article
Base plan from just $4.99/mo or $49/yr
Sign in to read the full article and unlock all 635+ tutorials.