| name | kookr-codex-claude-compatibility |
| description | Make Codex CLI behave more like Claude Code for Kookr by preserving the known fork, branch, build, deploy, daily upstream sync, and live-verification workflow for Claude-compatible skills, agents, settings, hooks, and related UX. |
| keywords | codex, claude, compatibility, codex cli, claude code, skills, agents, subagents, settings, permissions, hooks, costs, kookr, deploy, rebuild, app-server, daily upstream sync, feat/claude-compat, upstream/main, gh pr create, pre-pr-review |
| related | claude-code-permissions, claude-code-hooks, self-reflect, token-efficiency, pre-pr-review |
Codex Claude Compatibility
When to Use
- The user asks to make Codex CLI more similar to Claude Code
- The task touches Claude-compatible
.claude/skills or .claude/agents behavior in Codex
- The task involves Codex settings, permission semantics, hooks, cost surfacing, or Kookr integration
- You need to rebuild or redeploy the custom Codex binary used by Kookr
- You need to sync
feat/claude-compat with upstream/main
- You need the shortest path back into the Codex fork without rediscovering repo state
Reference Principle
Claude Code is the reference behavior. Treat Codex CLI as the implementation being adapted toward Claude compatibility, not the other way around.
For parity work, prefer:
- Confirm how Claude Code behaves
- Find the corresponding Codex implementation point
- Patch Codex to match the Claude-facing workflow and file formats where practical
- Verify in a real Kookr-adjacent process, not just with unit-level reasoning
Known Codex Fork Layout
Single Claude-compat branch
All Claude-Code compatibility work lives on feat/claude-compat in the fork (jeanibarz/codex). The main fork checkout is configured with KOOKR_CODEX_CHECKOUT and defaults to $HOME/git/codex; it should remain the only persistent worktree.
Branch state (as of 2026-04-05 consolidation):
main — fork's sync point with openai/codex upstream
feat/claude-compat — the active Claude-compat branch, contains:
- Claude skill loader (
.claude/skills, frontmatter tolerance)
- Claude agent loader (
.claude/agents)
PermissionRequest, Notification, PostToolUseFailure hook events
- Claude paths in docs/prompts/fixtures (replaces
.agents/.codex defaults)
.claude/plugins/marketplace.json primary path with .agents/ legacy fallback
- the hook-parity compatibility batch including commit
af0c99a14
Work directly on feat/claude-compat.
Operational rule:
- Do not create long-lived compat branches such as
feat/codex-hook-parity
- Do not keep separate Codex build worktrees around after the task
- If you temporarily need a throwaway checkout for validation or merge recovery, merge the result back into
feat/claude-compat, push it, and delete the temporary worktree before concluding the task
Compatibility Already Implemented
Claude skills
- Codex now loads repo and user skills from
.claude/skills
- It no longer treats
.agents as a skill root
- The skill loader tolerates Claude-style frontmatter extras that Codex does not model directly
- This specifically fixed failures caused by lines like
related: [[foo]], [[bar]]
Claude agents
- Codex now discovers markdown agents from
.claude/agents
- It parses Claude-style markdown agent files with YAML frontmatter
- Imported Kookr roles were verified in a real live spawned subagent flow
spawn_agent skill-workflow authorization (v1)
The v1 spawn_agent tool description originally restricted invocation strictly to "the user explicitly asks for sub-agents", which blocked legitimate skill-driven workflows. Kookr's pre-PR reviewer-specialist spawn was the canonical failure case — codex sessions hitting the .hooks/pre-push review-marker gate would refuse to call spawn_agent and fall back to fabricating the marker via shell instead, defeating the gate.
The fork's multi_agents_spec.rs now exposes two explicit authorization clauses:
- (a) the user explicitly asks for sub-agents, delegation, or parallel agent work, OR
- (b) a skill or workflow you are currently executing explicitly instructs you to spawn sub-agents — e.g. a pre-PR review skill that prescribes parallel reviewer-specialist sub-agents, or an agent role definition that names specific sub-agents to invoke.
Clause (a) preserves the original safety intent against reflexive delegation on free-form research tasks. Clause (b) authorizes the loaded-skill case. The pair is pinned by spawn_agent_tool_v1_description_authorizes_skill_workflow_spawns in multi_agents_spec_tests.rs so a future tightening cannot silently regress this.
This applies to v1 only (Feature::Collab, default enabled). The v2 description (Feature::MultiAgentV2, default disabled) does not carry the original restriction.
Claude model alias mapping
Map Claude agent aliases to the Kookr Codex fork's Luna model and role effort:
opus -> gpt-5.6-luna, model_reasoning_effort = "max"
sonnet -> gpt-5.6-luna, model_reasoning_effort = "high"
haiku -> gpt-5.6-luna, model_reasoning_effort = "medium"
Kookr's top-level Codex launches default to gpt-5.6-sol with no
model_reasoning_effort override (model-native default). Override the model
with KOOKR_CODEX_MODEL (e.g. gpt-5.6-luna) and/or set agentEffort.codex-cli
in settings / per-task --effort. An explicit top-level ultra effort always
selects gpt-5.6-sol, because Luna's advertised reasoning ceiling is max.
Build and Deploy Workflow
Versioning rule for Kookr Codex builds
Do not leave the fork at 0.0.0, and do not hardcode the commit SHA in Cargo.toml.
Use this scheme:
- Base version in
codex-rs/Cargo.toml tracks upstream, e.g. 0.118.0
- Build metadata is injected at build time from git
- Final deployed version format:
<upstream-version>+kookr.<short-sha>
- If the worktree is dirty at build time, append
.dirty
Examples:
0.118.0+kookr.4dfed7ea3
0.118.0+kookr.4dfed7ea3.dirty
Operational rule:
- Prefer a shared build-script implementation that derives the SHA from
${KOOKR_CODEX_CHECKOUT:-$HOME/git/codex}
- Make
codex --version the primary sanity check after install
- Keep runtime/UI/app-server version surfaces aligned with the same build version where practical
- After changing versioning, rebuild and reinstall
${KOOKR_CODEX_BIN:-$HOME/bin/codex} before concluding the task
Build from the single fork checkout, on feat/claude-compat. Use the
toolchain pinned in codex-rs/rust-toolchain.toml (1.95.0 as of
2026-06-12; upstream syncs bump it — a stale +<version> pin fails with
"rustc X is not supported by the following packages"; scripts/rebuild-codex.sh
auto-detects it):
KOOKR_CODEX_CHECKOUT="${KOOKR_CODEX_CHECKOUT:-$HOME/git/codex}"
cd "$KOOKR_CODEX_CHECKOUT" && git checkout feat/claude-compat && git pull
cargo +1.95.0 build \
--manifest-path "$KOOKR_CODEX_CHECKOUT/codex-rs/Cargo.toml" \
-p codex-cli \
--release
Or use the Kookr helper: pnpm codex:rebuild (runs scripts/rebuild-codex.sh, uses CODEX_SRC=$HOME/git/codex).
Install the built binary for Kookr use:
CODEX_SRC="${CODEX_SRC:-${KOOKR_CODEX_CHECKOUT:-$HOME/git/codex}}"
CODEX_INSTALL_DIR="${CODEX_INSTALL_DIR:-$HOME/bin}"
MANIFEST="$CODEX_SRC/codex-rs/Cargo.toml"
TARGET_DIR="$(cargo +1.95.0 metadata --manifest-path "$MANIFEST" --no-deps --format-version 1 | node -e 'let input = ""; process.stdin.on("data", chunk => input += chunk); process.stdin.on("end", () => process.stdout.write(JSON.parse(input).target_directory));')"
install -m 755 "$TARGET_DIR/release/codex" "$CODEX_INSTALL_DIR/codex"
Do not assume the binary lives under codex-rs/target/release. Some machines set Cargo's target directory outside the repo, for example /mnt/d/cargo-target. Use cargo metadata's target_directory value when locating the built binary.
Sanity check:
"${CODEX_INSTALL_DIR:-$HOME/bin}/codex" --version
Kookr should use ${KOOKR_CODEX_BIN:-${CODEX_INSTALL_DIR:-$HOME/bin}/codex} as the deployed custom Codex binary.
If you had to use an alternate checkout or target dir for a one-off rebuild, do not leave that as the operating model. The final committed source of truth must still be feat/claude-compat in ${KOOKR_CODEX_CHECKOUT:-$HOME/git/codex}.
Daily Upstream Sync Gotchas
When keeping feat/claude-compat current with upstream/main, use the auditable sync-PR workflow rather than rebasing or force-pushing the integration branch.
Pre-PR review must happen before gh pr create
The local pr-workflow-gate hook blocks gh pr create unless the pre-pr-review skill has created its gate marker. Run the pre-PR review after the sync branch passes local verification and before the first gh pr create attempt.
For a pure upstream sync, focus reviewer specialists on the merge-resolution and sync fix-up commits, not on re-reviewing every upstream commit. Ask them to inspect:
- conflicted files resolved in the merge commit
- generated schema reconciliation
- any post-merge fix-up commits
- release/version/install behavior touched by the sync
Fix blocking reviewer findings, rerun the affected local verification, and push the updated sync branch before creating the PR.
Use a literal --head branch in gh pr create
Do not pass the sync branch to gh pr create through a shell variable such as --head "$SYNC_BRANCH". The local pr-workflow-gate parses the raw shell command text before shell expansion, so a variable can make it look for the wrong gate marker.
Use a literal head branch in the actual gh pr create command:
gh pr create \
--repo jeanibarz/codex \
--base feat/claude-compat \
--head sync/upstream-main-YYYYMMDD \
--title "sync: merge upstream/main into feat/claude-compat (YYYY-MM-DD)" \
--body "..."
If you need shell variables to assemble text, expand them before the command or write the final literal command explicitly.
Treat gh pr merge local git failures as ambiguous
gh pr merge --merge --delete-branch can merge the PR remotely and still exit nonzero while doing local git cleanup. One known failure is:
fatal: 'main' is already checked out at '...'
If gh pr merge exits nonzero after contacting GitHub, do not retry blindly and do not stop for user intervention. First inspect the remote PR:
gh pr view "$PR_URL" --json state,mergedAt,mergeCommit,url
git ls-remote --heads origin feat/claude-compat "$SYNC_BRANCH"
If the PR state is MERGED, continue with Phase 6 from the remote merge commit:
git fetch origin
- Fast-forward the main checkout's
feat/claude-compat
- Build and install from the final integration branch
- Delete the remote sync branch manually if
--delete-branch did not do it:
git push origin --delete "$SYNC_BRANCH"
Only stop if the PR is still open or the base branch did not advance to the reported merge commit.
Real Verification Workflow
Do not stop at static inspection when the change affects runtime compatibility.
For skills
- Start Codex against the target repo/workspace
- Confirm the expected
.claude/skills entries are present in the available skill list
- If skills fail to load, inspect the Codex loader before blaming the skill files
For agents and subagents
Use a real spawned imported role, not a generic agent with copied instructions.
- Start app-server from the deployed binary:
KOOKR_CODEX_BIN="${KOOKR_CODEX_BIN:-$(command -v codex)}"
"$KOOKR_CODEX_BIN" app-server --listen ws://127.0.0.1:4222
- Drive it with the Codex app-server test client:
KOOKR_CODEX_CHECKOUT="${KOOKR_CODEX_CHECKOUT:-$HOME/git/codex}"
cargo +1.95.0 run \
--manifest-path "$KOOKR_CODEX_CHECKOUT/codex-rs/Cargo.toml" \
-p codex-app-server-test-client \
-- \
--url ws://127.0.0.1:4222 \
send-message-v2 --experimental-api 'Spawn the `failure-mode-analyst` subagent. Ask it to reply with exactly one short sentence identifying itself as the failure-mode-analyst role. Then report back with that sentence and nothing else.'
- Verify the spawned child thread with
thread-resume
The proof surface is the child thread metadata, not just the parent response. A real imported role should show:
agentRole set to the imported Claude role name
source set to SubAgent(...)
- child turn history containing the role-specific reply
Known live verification already succeeded with Kookr:
- parent thread:
019d57b4-4557-7b30-96aa-31e617bdcbfa
- child thread:
019d57b4-6731-7d61-9f10-ffd45f1f9145
- imported role:
failure-mode-analyst
- child reply:
I am the failure-mode analyst.
This proved Codex was using the imported .claude/agents role metadata in a real process.
Known Gaps — Empirically Mapped
Before starting any new compatibility work, read docs/poc/003-codex-compatibility-gaps.md.
That PoC runs both CLIs with identical hook settings and documents 10 concrete gaps with their exact fix locations (fork vs Kookr) and severity. Each gap is tracked as a GitHub issue on kookr-ai/kookr for one-at-a-time execution in the recommended sequence. Search issues with label codex-compat for the current list.
Top findings to know before editing the adapter or the fork:
--settings FILE was previously silently broken in the installed binary. The hook-parity batch fixed the root CLI plumbing and added regressions; if this regresses again, treat it as a critical blocker because Kookr loses hook coverage.
- Settings parsing regressed again on 2026-06-12 via upstream sync — strict
HooksFile. Upstream parses settings JSON with #[serde(deny_unknown_fields)] and only a hooks member; Kookr settings files carry permissions alongside hooks, so the whole file was rejected (warning only: unknown field "permissions", expected "hooks") and every codex session launched after the binary swap ran with zero hooks. Fixed in fork commit 8f21230e8d (tolerant ClaudeSettingsHooks struct in codex-rs/hooks/src/engine/discovery.rs, used for --settings files and .claude/settings.json layers; codex-native hooks.json stays strict). Regression tests: settings_file_with_claude_top_level_keys_still_loads_hooks, claude_settings_with_top_level_permissions_still_load_hooks. After every upstream sync, smoke-test hooks with a production-shaped settings file (one that includes permissions), in TUI mode, not just exec.
- Hook-coverage debugging gotchas (from the 2026-06-12 incident): a missing
~/.kookr/hooks/<session>.jsonl has at least three distinct causes — (1) settings file rejected by the strict parser (above); (2) untrusted project directory: the TUI blocks on the trust onboarding screen before any session starts, so headless dtach launches in a never-seen cwd produce no session and no hooks (codex exec does not show this screen); (3) running sessions keep their in-memory binary — swapping ~/bin/codex only fixes new launches, existing hook-silent sessions must be restarted. Also note codex exec --dangerously-bypass-approvals-and-sandbox persists a [projects."<cwd>"] trust entry in ~/.codex/config.toml, which can silently change repro behavior between runs in the same scratch dir.
codex_hooks feature flag graduated. The key is now hooks, stable and enabled by default; codex_hooks is a deprecated legacy alias that prints a startup deprecation warning. The adapter's -c features.codex_hooks=true is harmless but obsolete and can be dropped.
- Codex emits 8/12 hook events. Missing:
SessionEnd, StopFailure, SubagentStart, SubagentStop.
- Payload format is ~95% compatible. Same event names, same field names, same PascalCase, same
tool_name: "Bash", tool_input.command is a string (not an array as earlier RFCs guessed).
When patching the fork, patch feat/claude-compat directly. Do not resurrect older compatibility branches or use a sync branch as the primary place where the work lives.
Areas Still Worth Pursuing
When the user asks for more Claude compatibility, check these next:
- permission semantics and config parity with Claude settings
- hook and settings compatibility, especially
.claude/settings.json behavior
- Claude-style UX around permission prompting and default modes
- cost and usage surfacing so Kookr can show Codex spend similarly to Claude sessions
- any file-format or frontmatter differences that Claude accepts but Codex still rejects
Working Pattern for New Compatibility Fixes
- Reproduce the gap using the real deployed binary or the main
${KOOKR_CODEX_CHECKOUT:-$HOME/git/codex} checkout on feat/claude-compat
- Inspect the Codex implementation in
${KOOKR_CODEX_CHECKOUT:-$HOME/git/codex}
- Patch the minimal compatibility layer instead of rewriting Claude assets
- Add a regression test in the Codex fork whenever the issue is deterministic
- Build and deploy
${KOOKR_CODEX_BIN:-$HOME/bin/codex}
- Re-verify in a live Kookr-adjacent process
- Commit, push, and record the new behavior here if it is reusable
Notes
${KOOKR_CODEX_CHECKOUT:-$HOME/git/codex} on feat/claude-compat is the only persistent Codex checkout to rely on
- If
${KOOKR_CODEX_CHECKOUT:-$HOME/git/codex} is dirty, either work carefully with those changes or stop and resolve the ownership conflict before proceeding
- Before ending a task, make sure
git worktree list for ${KOOKR_CODEX_CHECKOUT:-$HOME/git/codex} only shows the main checkout unless the user explicitly asked to keep an extra worktree
- If a future compatibility fix produces reusable operational knowledge, update this skill rather than leaving the knowledge in chat history