Migrate To Codex
OpenAIOfficialPublished by OpenAI
- Skill
- Workflow
- Automation and integrations
- Apache-2.0
- Low risk
- Has scripts
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
The migrate-to-codex skill provides a structured, automated procedure to migrate existing Claude Code setups into the Codex ecosystem. It handles the conversion of instruction files, slash commands, skills, agents, and MCP server configurations. The tool follows a rigorous process involving scanning, planning, dry-running, and validating the migration to ensure artifacts are correctly mapped while preserving project-specific configurations. It includes a self-healing loop to resolve manual migration requirements and ensures that generated Codex artifacts are validated against target standards.
What it does
- Converts Claude Code instruction files into Codex-compatible formats.
- Migrates slash commands and skills into the Codex skill structure.
- Updates MCP server configurations for use in Codex.
- Generates a migration report detailing added items and manual review requirements.
- Validates generated Codex artifacts to ensure configuration integrity.
- Provides a dry-run mode to preview changes before applying them.
When to use it
- When transitioning a development environment from Claude Code to Codex.
- When you need to port custom skills or agents to a new Codex project.
- When updating global or project-level configurations to align with Codex standards.
- When you need to verify the readiness of existing Claude artifacts for Codex.
Advantages
- Automates complex migration steps, reducing manual effort.
- Provides clear reporting on what was successfully migrated and what requires manual attention.
- Includes a self-healing loop to iteratively resolve migration issues.
- Ensures project-specific configurations are preserved during the transition.
- Validates targets post-migration to prevent configuration errors.
Usage examples
- “Run a scan to see what Claude artifacts are available for migration.”
- The tool lists active and inactive source surfaces from the specified Claude directory.
- “Perform a dry-run migration of my project settings to Codex.”
- The tool generates a plan and reports potential changes without modifying any files.
- “Execute the full migration of my global Claude configuration to Codex.”
- The tool migrates instructions, skills, and MCP configs, creating a report of the results.
- “Validate the Codex target after my migration.”
- The tool checks the generated Codex files for errors and reports any remaining manual fixes.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/openai/migrate-to-codex/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip migrate-to-codex.zip -d .agents/skills
Documentation by OpenAI
Migrate to Codex
Autonomy
Keep going until the selected migration is completely done: run the migrator, inspect the report, fix migrated Codex instructions/skills/agents/MCP config, and re-run checks without stopping to ask for confirmation of the next step. If the user has selected a target, do not ask before creating, editing, replacing, or deleting generated Codex artifacts in that target (AGENTS.md, .codex/, .agents/, or ~/.codex/). Preserve unrelated existing Codex config entries in .codex/config.toml or ~/.codex/config.toml, such as notify, projects, marketplaces, or unrelated MCP servers; do not ask about them unless they fail validation or directly conflict with the migration. Do not edit source Claude Code files (.claude/, ~/.claude/, .mcp.json, or .claude.json), unrelated project code, secrets, or another repository.
Migration Order
Run the migration in this order for each selected global or project source:
Start by using Codex's built-in TODO/task list tool. Do not create
MIGRATION_TODOS.mdor any TODO file unless the user explicitly asks. The TODO list input has aplanarray whose items each havestepandstatus; use statusespending,in_progress, andcompleted. Make the TODOs specific to the selected artifacts. Before finishing, update the TODO list so every finished step is markedcompletedand no step remainsin_progress. Use literal source → Codex target labels, for example:Inspect
.claude/commands→ Codex skills/promptsInspect
.claude/agents→.codex/agentsInspect
.mcp.json→.codex/config.tomlMCP serversInspect
.claude/settings.jsonhooks →.codex/hooks.jsonMigrate safe selected artifacts → Codex files
Validate generated
.codex/config.tomlValidate generated
.codex/agentsReport migrated artifacts and manual-review items
Read
references/differences.md(and refresh Codex docs if itsDocs last checkeddate is old).Scan and inspect before writing:
--scan-onlylists active and inactive source surfaces.--planprints staged Codex artifact paths and report rows.--doctorsummarizes readiness, manual-review work, and validation risks.
Convert surfaces in the same order the CLI uses:
instructions:
CLAUDE.md/AGENTS.mdtoAGENTS.mdplugins: report Claude plugin trees and marketplaces as manual migration work
hooks: rewrite supported Claude hooks into
.codex/hooks.jsonand enable[features].codex_hooks = trueskills and commands: write Codex skills under
.agents/skills/config: write
.codex/config.tomlfrom Claude model/sandbox settings and MCP servers, includingpersonality = "friendly"when config is generatedsubagents: write Codex custom agents under
.codex/agents/
Dry-run, then write the selected target. Use
--replaceonly when orphan generated skills or agents should be deleted.Inspect the terminal output and
.codex/migrate-to-codex-report.txtafter real runs.Review generated artifacts in this order:
AGENTS.md,.agents/skills/,.codex/config.toml,.codex/hooks.json,.codex/agents/, then report-only plugin items.Run
--validate-targetagainst each target after edits.Re-run checks and
--dry-runafter edits.Return the final migration report as one markdown table per scope that has rows. The tables cover only the non-native follow-up migration work you performed, such as skills created from slash commands, subagents, MCP servers, hooks, unsupported/local plugin notes, and manual-review caveats. Include programmatic native import rows for config, instructions, skills, or supported plugins only if you personally migrated them in this follow-up run.
If only one scope has rows, render only the table with no heading. If multiple scopes have rows, render one heading before each table. Use
**User Config**for user-scope rows. For project-scope rows, use the actual project folder name as the heading, for example**northstar-support-portal**; do not useCurrent Projectas the heading. Do not add prose before or after the table output.Use exactly these columns:
northstar-support-portal
Status Item Notes AddedSlash commandpr-reviewConverted into a Codex skill AddedSubagentrelease-leadAdded as a Codex subagent Check before usingHookPreToolUseConverted, but some Claude hook behavior differs in Codex Not AddedHookNotificationCodex does not have an equivalent notification hook Not AddedPluginteam-macrosPlugin needs manual setup Statusmust beAdded,Check before using, orNot Added. UseAddedwhen a Codex-facing artifact was created or changed and needs no special review. UseCheck before usingwhen a Codex-facing artifact was created or changed but the migration changed semantics, inferred behavior, preserved tool rules as guidance, or dropped unsupported behavior. UseNot Addedwhen a source artifact was detected but no Codex-facing artifact was created.Itemcombines the artifact type and concrete item name in one cell. Artifact type must be singular:Skill,Slash command,Subagent,MCP,Hook, orPlugin. Wrap the artifact type in inline code; write the item name as plain text after it.Notesis always required; never leave it empty. Keep notes short, plain, and literal. Avoid internal implementation terms such as runtime expansion. Prefer phrases likeConverted into a Codex skill,Added as a Codex subagent,Added to Codex config,Converted into a Codex hook,Converted, but some Claude hook behavior differs in Codex,Codex does not have an equivalent notification hook,Plugin needs manual setup, orPlugin marketplace needs manual setup.
Self-Healing Loop
Keep looping until the selected migration is complete:
Run
--planor--doctor.Run the migration with
--dry-run.Run the migration for real.
Fix every generated
## MANUAL MIGRATION REQUIREDblock and everymanual_fix_requiredorskippedreport row that can be resolved inside Codex artifacts.Run
--validate-target.Re-run the migrator and validator until the report and validator have no actionable generated-artifact fixes left.
Do not edit source Claude Code files, unrelated project code, secrets, or another repository during this loop. If a report row requires source-provider changes or product judgment, leave the generated Codex artifact with clear manual guidance instead of changing the source.
Commands
Choose the migrator command.
MIGRATE_TO_CODEX='python3 .codex/skills/migrate-to-codex/scripts/migrate-to-codex.py'Inspect the migration before writing.
$MIGRATE_TO_CODEX --source ~/.claude/ --scan-only
$MIGRATE_TO_CODEX --source ~/.claude/ --target ~/.codex/ --plan
$MIGRATE_TO_CODEX --source ~/.claude/ --target ~/.codex/ --doctorDry-run, then run without --dry-run, for global and project.
$MIGRATE_TO_CODEX --source ~/.claude/ --target ~/.codex/ --dry-run
$MIGRATE_TO_CODEX --source ~/.claude/ --target ~/.codex/
$MIGRATE_TO_CODEX --source ./.claude/ --target ./.codex/ --dry-run
$MIGRATE_TO_CODEX --source ./.claude/ --target ./.codex/Run the post-migration validator against each target after edits.
$MIGRATE_TO_CODEX --validate-target ~/.codex/
$MIGRATE_TO_CODEX --validate-target ./.codex/Run $MIGRATE_TO_CODEX --help for flags (--scan-only, --plan, --doctor, --validate-target, defaults, and so on). Deep tables and more links are in references/differences.md.
Files
- ATTRIBUTION.mdAdded by AutoGineer890 B
- LICENSE.txt11.1 KiB
- SKILL.md7.8 KiB
- agents/openai.yaml144 B
- references/differences.md13.4 KiB
- scripts/cli.pyScript29.4 KiB
- scripts/migrate-to-codex.pyScript208 B
- scripts/migrate/__init__.pyScript285 B
- scripts/migrate/agents.pyScript9.8 KiB
- scripts/migrate/codex_config.pyScript3.9 KiB
- scripts/migrate/common.pyScript11.2 KiB
- scripts/migrate/hooks.pyScript8.0 KiB
- scripts/migrate/instructions.pyScript2.4 KiB
- scripts/migrate/mcps.pyScript6.9 KiB
- scripts/migrate/plugins.pyScript660 B
- scripts/migrate/settings.pyScript1.0 KiB
- scripts/migrate/skills.pyScript12.3 KiB
- scripts/utils/__init__.pyScript35 B
- scripts/utils/scan.pyScript4.3 KiB
- scripts/utils/util.pyScript9.2 KiB
scripts/migrate/agents.py
"""Convert Claude Code subagents into Codex custom-agent TOML.
Reads `.claude/agents/*.md` files, parses their frontmatter/body, and emits
`.codex/agents/<name>.toml` artifacts. Partially mapped metadata such as
skills, tool allowlists, and unknown permission modes is preserved as prompt
guidance plus a manual-review report row.
"""
from __future__ import annotations
import tomllib
from pathlib import Path
from migrate.common import (
CODEX_AGENTS_ROOT,
ConversionResult,
MigrationReportItem,
ParsedDocument,
PlannedArtifact,
append_report_item,
format_bullets,
format_manual_migration_block,
map_model_effort,
map_model_name,
map_permission_mode,
unsupported_frontmatter_fields,
)
from utils.util import (
TomlMultilineString,
first_markdown_heading,
render_toml_document,
slugify_name,
)
AGENT_SOURCE_ROOTS = (
Path(".claude") / "agents",
)
SUPPORTED_AGENT_FRONTMATTER = (
"name",
"description",
"model",
"permissionMode",
"skills",
"tools",
"disallowedTools",
"effort",
)
def iter_agent_files(source_root: Path) -> tuple[Path, ...]:
if not source_root.exists():
return ()
return tuple(
source_file
for source_file in sorted(source_root.glob("*.md"))
if source_file.stem != "README"
)
def agent_metadata(source_file: Path, document: ParsedDocument) -> dict[str, object]:
inferred_fields: list[str] = []
name = document.frontmatter.optional_string("name")
if not name:
name = slugify_name(source_file.stem)
inferred_fields.append("name")
description = document.frontmatter.optional_string("description")
if not description:
heading = first_markdown_heading(document.body)
if heading:
description = f"Migrated Claude subagent inferred from heading `{heading}`."
else:
description = f"Migrated Claude subagent inferred from `{source_file.name}`."
inferred_fields.append("description")
return {
"name": name,
"description": description,
"model": document.frontmatter.optional_string("model"),
"permission_mode": document.frontmatter.optional_string("permissionMode"),
"skills": document.frontmatter.string_tuple("skills"),
"tools": document.frontmatter.string_tuple("tools"),
"disallowed_tools": document.frontmatter.string_tuple("disallowedTools"),
"effort": document.frontmatter.optional_string("effort"),
"unsupported_fields": unsupported_frontmatter_fields(
document.frontmatter.to_dict(),
SUPPORTED_AGENT_FRONTMATTER,
)
+ tuple(inferred_fields),
}
def convert_agent_file(source_file: Path) -> tuple[PlannedArtifact, MigrationReportItem]:
document = ParsedDocument.from_file(source_file)
metadata = agent_metadata(source_file, document)
artifact = PlannedArtifact.for_agent(
source_file,
render_agent_toml(document.body, **metadata),
)
return artifact, agent_report_item(source_file, **metadata)
def render_agent_toml(
body: str,
*,
name: str,
description: str,
model: str | None,
permission_mode: str | None,
skills: tuple[str, ...],
tools: tuple[str, ...],
disallowed_tools: tuple[str, ...],
effort: str | None,
unsupported_fields: tuple[str, ...],
) -> str:
document = {
"name": name,
"description": description,
}
if model:
document["model"] = map_model_name(model)
if effort:
document["model_reasoning_effort"] = map_model_effort(model, effort)
sandbox_mode = map_permission_mode(permission_mode)
if sandbox_mode:
document["sandbox_mode"] = sandbox_mode
document["developer_instructions"] = TomlMultilineString(
render_agent_body(
body,
permission_mode=permission_mode,
skills=skills,
tools=tools,
disallowed_tools=disallowed_tools,
unsupported_fields=unsupported_fields,
).strip()
)
return render_toml_document(document)
def render_agent_body(
body: str,
*,
permission_mode: str | None,
skills: tuple[str, ...],
tools: tuple[str, ...],
disallowed_tools: tuple[str, ...],
unsupported_fields: tuple[str, ...],
) -> str:
sections = []
manual_notes: list[str] = []
sandbox_mode = map_permission_mode(permission_mode)
if permission_mode and not sandbox_mode:
manual_notes.append(
f"Claude `permissionMode: {permission_mode}` has no direct Codex mapping. "
"Manually choose `sandbox_mode`, `[permissions]`, MCP tool filters, or app tool filters before relying on this agent."
)
if skills:
sections.append(
"## Skills\n\n"
"You're allowed to use these skills when working on this task:\n\n"
f"{format_bullets(skills, '$')}"
)
manual_notes.append(
"Claude `skills` preload semantics were preserved as prompt guidance. Verify this agent still discovers the intended skills at runtime."
)
if tools or disallowed_tools:
tool_section_lines = [
"## Tools",
"",
"Claude tool allow/deny lists were preserved as prompt guidance, not Codex permissions.",
]
if tools:
tool_section_lines.extend(
[
"",
"You're allowed to use these tools:",
"",
format_bullets(tools),
]
)
if disallowed_tools:
tool_section_lines.extend(
[
"",
"Don't use these tools:",
"",
format_bullets(disallowed_tools),
]
)
sections.append("\n".join(tool_section_lines))
manual_notes.append(
"Rebuild Claude `tools` / `disallowedTools` intent with Codex sandbox, MCP tool filters, or app tool filters if you need hard enforcement."
)
if unsupported_fields:
manual_notes.append(
"Review unsupported Claude subagent fields manually: "
f"{', '.join(f'`{field_name}`' for field_name in unsupported_fields)}."
)
if manual_notes:
sections.append(format_manual_migration_block(manual_notes))
if not sections:
return body
joined_sections = "\n\n".join(sections)
return f"{body.rstrip()}\n\n{joined_sections}\n"
def agent_report_detail(
*,
permission_mode: str | None,
skills: tuple[str, ...],
tools: tuple[str, ...],
disallowed_tools: tuple[str, ...],
unsupported_fields: tuple[str, ...],
**_: object,
) -> str:
caveats: list[str] = []
if skills:
caveats.append("skills")
if tools:
caveats.append("tools")
if disallowed_tools:
caveats.append("disallowedTools")
if permission_mode and not map_permission_mode(permission_mode):
caveats.append("permissionMode")
caveats.extend(unsupported_fields)
if not caveats:
return "Converted Claude subagent."
return (
"Manual review required for Claude subagent fields: "
+ ", ".join(f"`{field_name}`" for field_name in caveats)
+ "."
)
def agent_report_item(
source_file: Path,
*,
permission_mode: str | None,
skills: tuple[str, ...],
tools: tuple[str, ...],
disallowed_tools: tuple[str, ...],
unsupported_fields: tuple[str, ...],
**metadata: object,
) -> MigrationReportItem:
report_items: list[MigrationReportItem] = []
detail = agent_report_detail(
permission_mode=permission_mode,
skills=skills,
tools=tools,
disallowed_tools=disallowed_tools,
unsupported_fields=unsupported_fields,
**metadata,
)
append_report_item(
report_items,
skills
or tools
or disallowed_tools
or (permission_mode and not map_permission_mode(permission_mode))
or unsupported_fields,
CODEX_AGENTS_ROOT / f"{source_file.stem}.toml",
detail,
detail,
)
return report_items[0]
def convert_agents(source_root: Path) -> ConversionResult:
return convert_agent_files(source_root / ".claude" / "agents")
def convert_agent_files(source_root: Path) -> ConversionResult:
result = ConversionResult()
for source_file in iter_agent_files(source_root):
artifact, report_item = convert_agent_file(source_file)
result.artifacts.append(artifact)
result.summary.subagents += 1
result.report_items.append(report_item)
return result
def validate_agent_files(target_root: Path) -> list[MigrationReportItem]:
agents_root = target_root / CODEX_AGENTS_ROOT
if not agents_root.exists():
return []
report_items: list[MigrationReportItem] = []
for agent_file in sorted(agents_root.glob("*.toml")):
relative_path = agent_file.relative_to(target_root)
try:
parsed = tomllib.loads(agent_file.read_text())
except tomllib.TOMLDecodeError as exc:
report_items.append(
MigrationReportItem("error", relative_path, f"invalid TOML: {exc}.")
)
continue
missing = [
key
for key in ("name", "description", "developer_instructions")
if not parsed.get(key)
]
if missing:
report_items.append(
MigrationReportItem(
"error",
relative_path,
"agent TOML missing " + ", ".join(missing) + ".",
)
)
continue
report_items.append(
MigrationReportItem("ok", relative_path, "agent TOML has required fields.")
)
return report_items
Requirements and compatibility
Written for: OpenAI Codex
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs an MCP server, Needs Python
Security
What our scanner noticed· Low risk
- Scripts that your agent may run
This skill includes scripts that your agent may run. Read them before you use it.
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- OpenAI
- Publisher
- OpenAI (Official)
- Source
- openai/skills · skills/.curated/migrate-to-codex · commit 49f948f
- Licence
- Apache License 2.0 (redistribution allowed)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by OpenAI.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download migrate-to-codex.zip