A Claude Code plugin that runs an automatic code review with OpenAI Codex CLI after every git commit made via the Bash tool. On [P1]/[P2] findings, Claude is blocked and asked to fix and re-commit; on a clean review, you're prompted to push.
This is an independent community project, not affiliated with or endorsed by OpenAI or Anthropic. "Codex" is a product of OpenAI; this plugin drives the Codex CLI you install and authenticate yourself.
- OpenAI Codex CLI on
$PATH— install vianpm install -g @openai/codex(see https://github.com/openai/codex). Verify withcommand -v codex. - Codex authenticated — run
codex login(interactive) or exportOPENAI_API_KEY. Verify withcodex exec 'hello'. jq,git,bashon$PATH(standard on macOS/Linux).- Claude Code with plugins enabled.
/plugin marketplace add andreidavid/codex-review
/plugin install codex-review@andreidavid
/reload-plugins
The @andreidavid suffix is the marketplace name, not a typo: codex-review@andreidavid reads as "the codex-review plugin from the andreidavid marketplace". /reload-plugins is required after install — Claude Code won't pick up the hooks until you run it.
Pull updates later with:
/plugin marketplace update andreidavid
/reload-plugins
The marketplace was renamed from codex-review to andreidavid in an early iteration. Claude Code keys marketplaces by the name they were first added under, so if you installed before the rename your local ID is still codex-review, and the management commands above won't find it. Remove the old registration and re-add fresh:
/plugin uninstall codex-review@codex-review
/plugin marketplace remove codex-review
/plugin marketplace add andreidavid/codex-review
/plugin install codex-review@andreidavid
/reload-plugins
- Slash command
/codex-review— on-demand review of a specific commit, uncommitted changes, or a branch diff. Arguments:[--commit <sha>] [--uncommitted] [--base <branch>]. - Slash command
/codex-review-plan— run a Codex review on a Claude Code plan file in~/.claude/plans/before committing to implementation. Arguments:[path-to-plan.md]. - Slash command
/codex-review-waive— suppress a disputed finding so it stops blocking commits (see Waiving findings). - Skill
codex-review— invoked when you ask Claude to "review my changes", "run a codex review", etc. - PostToolUse hook — after every successful
git commitmade via the Bash tool, Codex reviews the new commit.[P1]/[P2]findings block Claude and instruct it to fix and re-commit. Codex errors and timeouts do not block — only findings do. Consecutive FAIL rounds on one commit saga are capped atCODEX_REVIEW_MAX_ROUNDS(default 8): at the cap, findings demote to advisory context and stop blocking. - Stop hook — keeps Claude iterating through the fix/re-commit cycle until the review passes, capped at
CODEX_REVIEW_MAX_LOOPSiterations (default 5). The loop holds only the session that made the commit — a second Claude Code session sharing the repo is never told to fix or amend a commit it didn't make. While the reviewed commit is unpushed, the loop folds fixes in withgit commit --amend, so broken intermediate versions never survive in history. - SessionStart hook — warns at session start if reviews are not going to run (codex missing or unauthenticated, kill switch active). Silent when everything is healthy.
Scope: only commits that Claude itself makes via the Bash tool trigger the review. Commits you run in your own terminal (outside a Claude Code session) are not reviewed — the hook has no visibility into them. Use the
/codex-reviewslash command or the skill to review those on demand.The PostToolUse hook is registered for every Bash tool call; a fast filter inside the script lets unrelated calls fall through in milliseconds, and only successful
git commitcommands escalate to running Codex.
Claude Code's plan mode writes an implementation plan to ~/.claude/plans/<name>.md before you approve execution. /codex-review-plan asks Codex to critique that plan (missing steps, design flaws, scope/risk, verification gaps) so you catch problems before they ship as code.
# Review the most recently modified plan in ~/.claude/plans/
/codex-review-plan
# Review a specific file (absolute or relative path)
/codex-review-plan ~/.claude/plans/my-plan.md
/codex-review-plan my-plan.md # resolves against ~/.claude/plans/
Unlike the commit review, this uses codex exec (ad-hoc prose review) rather than codex exec review (which is diff-oriented). No git state is required. Findings use the same [P1]/[P2]/[P3] priority scheme; [P1] / [P2] issues suggest revising the plan before implementation.
If you disagree with a finding (false positive, accepted trade-off), waive it instead of fighting the loop:
/codex-review-waive # one blocking finding -> waives it
/codex-review-waive 2 # waive finding #2 from the last failed review
/codex-review-waive all # waive every blocking finding
Waivers live in .git/codex-review-waived, one normalized key per line (# comments allowed). They match on title with case, whitespace, and line numbers stripped, so a finding re-reported at a shifted line stays waived; a substantially re-worded finding needs waiving again. Waivers suppress blocking only — waived findings still appear in the history log marked "waived": true, and PASS/FAIL messages note how many findings were suppressed. Un-waive by editing or deleting lines from the file.
Each triggered review is one Codex API call billed to your OpenAI account. A blocked fix loop re-reviews after every fix commit or amend; consecutive FAIL rounds are capped at CODEX_REVIEW_MAX_ROUNDS (default 8), which also bounds worst-case review spend on one commit saga. The Stop hook separately caps turn-end continuations at CODEX_REVIEW_MAX_LOOPS (default 5). Reviews run under a 600-second timeout (CODEX_REVIEW_TIMEOUT, raisable up to the 900s hook ceiling); a review that exceeds it is recorded as TIMEOUT and does not block — use /codex-review on smaller slices for very large commits.
If you're about to do a run of experimental or throwaway commits, bypass the plugin with touch .git/codex-review-skip in the repo (delete the file to re-enable). Setting CODEX_REVIEW_SKIP=1 does the same, but only if exported before launching Claude Code — hooks inherit the launch environment, so exporting it mid-session has no effect.
Plugin option:
sandbox_mode—workspace-write(default: sandboxed, today's behavior),danger-full-access(no filesystem sandbox; usually the right fix when Codex's own sandbox fails inside an already-sandboxed Claude session), orbypass(--dangerously-bypass-approvals-and-sandbox: unrestricted filesystem, command, and network access, and it also skips Codex's trusted-directory check). Reviews run unattended on untrusted input. Withbypass, a prompt-injection payload in a reviewed change could execute arbitrary commands, so only use it inside a container or VM you consider disposable. Applies to automatic post-commit reviews only; manual/codex-reviewand/codex-review-planalways use the sandboxed default.
This command is provided as a convenience:
/codex-review-sandbox-mode [workspace-write|danger-full-access|bypass|status]— Set the mode or show the saved setting and effective Codex arguments.
No need to run /reload-plugins after changing the setting.
Environment variables:
| Variable | Default | Effect |
|---|---|---|
CODEX_REVIEW_SKIP |
unset | If set to any non-empty value at Claude Code launch, both hooks no-op. For a mid-session switch, use the kill-switch file below. |
CODEX_REVIEW_MAX_LOOPS |
5 |
Max iterations of the fix-and-recommit loop before the Stop hook lets Claude end the turn. |
CODEX_REVIEW_MAX_ROUNDS |
8 |
Max consecutive FAIL review rounds (across amends) since the last PASS. At the cap the review stops blocking: findings are surfaced as advisory context and the counters reset. |
CODEX_REVIEW_TIMEOUT |
600 |
Seconds before an in-flight review is killed and recorded as TIMEOUT (non-blocking). Values above 880 are clamped to keep headroom under the 900s hook ceiling in hooks.json, so the script always observes the kill itself. |
CODEX_BIN |
$(command -v codex) |
Override path to the Codex binary. |
CODEX_REVIEW_MAX_OUTPUT |
8000 |
Max characters of review output surfaced back to Claude. |
Per-repo kill switch: touch .git/codex-review-skip disables both hooks for that repo until the file is removed. To disable the plugin everywhere without uninstalling, use /plugin disable codex-review@andreidavid.
The hooks keep their working state inside the repo's .git directory — never the working tree, so nothing can be accidentally committed and no .gitignore edits are needed:
.git/codex-review-state[.<session-id>]— current verdict (RUNNING/FAIL/ERROR/TIMEOUT/INCONCLUSIVE) plus the commit SHA, a timestamp used for staleness detection, and — onFAIL— the id of the session that made the commit (only that session is held in the fix loop).git/codex-review-loop-count[.<session-id>]— fix-loop counter.git/codex-review-round-count[.<session-id>]— consecutive-FAIL round counter, stamped with the epoch of the last failed review (<count> <epoch>)
State and counters are keyed by the committing session (the .<session-id> suffix), so parallel sessions and linked worktrees keep independent fix loops. The bare names are the legacy/anonymous bucket: commits made without a session id, and state written by pre-1.7.0 versions.
.git/codex-review-skip— create this file to disable the hooks for the repo.git/codex-review-waived— waived finding keys (see Waiving findings)
Stale state heals itself: the Stop hook clears a FAIL whose commit no longer matches HEAD or that is over an hour old, and clears RUNNING markers older than 16 minutes — past the 900-second hook ceiling, a marker that old can only be the fossil of a killed review, never a live one. A round counter whose last FAIL is over an hour old (or that predates the epoch stamp) reads as a fresh saga. Every turn end also garbage-collects any session's state that has outlived those windows, so abandoned sessions clean up after themselves no matter which session stops next.
Upgrading from a pre-1.4.0 install: the old working-tree markers (.codex-review-state, .codex-review-loop-count) are removed automatically the first time a hook runs, and the .gitignore entries they required can be deleted.
Every review (pass or fail) appends one JSON line to .git/codex-reviews.jsonl in the repo where the commit was made. The file lives inside .git/ so it is never committed and is scoped per-repo.
Each entry contains:
timestamp(ISO-8601 UTC),sha/short_sha,branch,author_name,author_emailverdict—PASS/FAIL/ERROR/TIMEOUT/INCONCLUSIVE(exit-zero, no findings, but the review's event stream shows no successful execution activity at all — nothing was inspected, typically a nested-sandbox failure), pluscapped: trueon a FAIL that was demoted to advisory at the round cap (it did not block)findings[]— structured{priority, title, waive_key, waived}per[P1]/[P2]/[P3]line Codex reported, plusfinding_count,blocking_count(unwaived P1/P2 only), andwaived_countreview_prose— the full Codex review text (truncated to 2000 chars)codex_exit,duration_seconds,plugin_version
Inspect with jq:
# Last 5 verdicts + finding counts in the current repo
tail -5 .git/codex-reviews.jsonl | jq -c '{short_sha, verdict, blocking_count}'
# Find when a specific commit was reviewed
jq -c 'select(.short_sha == "abc12345")' .git/codex-reviews.jsonl
# All FAIL verdicts with their blocking titles
jq 'select(.verdict == "FAIL") | {short_sha, titles: [.findings[].title]}' .git/codex-reviews.jsonlTo reset history, rm .git/codex-reviews.jsonl. Nothing in the plugin relies on this file — it's write-only from the hook's perspective.
Under the hood, the post-commit hook invokes codex exec review --json and parses the agent_message event from Codex's JSONL event stream. This replaces the earlier sed/grep text-scraping that produced false positives. If your Codex CLI is older and ignores --json, the hook falls back to the legacy text parser and still works — upgrade when convenient.
bats tests runs the test suite (35 tests, stub Codex CLI — no API calls). CI runs shellcheck plus the suite on Linux and macOS.
- Stuck in a fix loop / Stop hook keeps blocking — interrupt Claude (Ctrl-C), then
rm -f .git/codex-review-state* .git/codex-review-loop-count* .git/codex-review-round-count*in the repo, ortouch .git/codex-review-skip. (Stale state from an abandoned loop also clears itself: see State files.) - Review timed out — Codex exceeded
CODEX_REVIEW_TIMEOUT(600s). The commit is not blocked; the timeout is logged to the history file. Re-check with/codex-review, break the commit up, or raise the variable (up to the 900s hook ceiling). - Codex not found —
command -v codexreturns empty. Install withnpm install -g @openai/codex, or setCODEX_BIN=/path/to/codex. - Codex auth failure on review — run
codex loginor exportOPENAI_API_KEY. Auth failures don't block commits; they're logged asERROR. - Review keeps blocking on a finding you disagree with —
/codex-review-waivesuppresses it permanently for the repo (see Waiving findings). For a systematic false-positive pattern, file an issue with the verbatim Codex output. - Review keeps failing on obviously clean commits — file an issue with the verbatim Codex output. As a workaround,
touch .git/codex-review-skip. - Every review times out — check for a misbehaving Codex MCP server. Run
codex exec review --json --commit HEAD --full-auto 2>/dev/null | jq -c 'select(((.type // "") + "/" + (.item.type // "")) | test("mcp"; "i"))'— the filter scopes to event/item type fields (ignores prose or diff text that merely mentions MCP) and tolerates schema variation across Codex versions. Look for an event that starts but never completes. If you spot one, temporarily comment out the offending[mcp_servers.<name>]block in~/.codex/config.tomland retry. - Reset review history —
rm .git/codex-reviews.jsonlin the affected repo.
/plugin uninstall codex-review@andreidavid
/plugin marketplace remove andreidavid
If any stale state markers remain in projects you used the plugin in:
rm -f .git/codex-review-state* .git/codex-review-loop-count* .git/codex-review-round-count* .git/codex-review-skip .git/codex-review-waived
rm -f .codex-review-state .codex-review-loop-count # pre-1.4.0 locations