Skip to content

Automatic code review

Deliverable: every time Claude Code finishes a turn with uncommitted changes, a Stop hook runs a Codex review of those changes and prints its verdict.

Prerequisites: Claude Code and Codex CLI both configured with the same MIRAPI_API_KEY (see Claude Code and Codex CLI); a git repository.

Create ~/.claude/review.sh:

#!/usr/bin/env bash
# Run a Codex review of the uncommitted changes.
set -euo pipefail
DIFF_STAT=$(git diff HEAD --stat || true)
# Exit silently when there is nothing to review.
if [[ -z "$DIFF_STAT" ]]; then
exit 0
fi
codex exec "Review the changes below for bugs, security issues, and broken invariants. Be concise.
$DIFF_STAT" 2>/dev/null || true

Make it executable:

Terminal window
chmod +x ~/.claude/review.sh

The script captures a one-line summary of the staged and unstaged changes (git diff HEAD --stat), hands it to Codex, and lets Codex print the review to the hook’s output. The trailing || true keeps a failed Codex call from surfacing as a hook error.

Add the hook to your project’s .claude/settings.json:

{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "bash ~/.claude/review.sh",
"timeout": 120
}
]
}
]
}
}

The Stop event fires whenever Claude Code pauses for your input — exactly the moment a review is useful. The hook fields are:

Field Purpose
type "command" runs a shell command
command The command to execute — here bash ~/.claude/review.sh
timeout Maximum seconds the hook may run before being killed (120)

To keep the hook out of git, use .claude/settings.local.json instead; global hooks live in ~/.claude/settings.json.

First test the script on its own: change a tracked file, then run it directly.

Terminal window
bash ~/.claude/review.sh

You should see the Codex review in the terminal. Then ask Claude Code to make a change and wait for the hook output — the verdict appears in the hook’s output section once Claude Code pauses.

Claude Code and Codex CLI speak different protocols, so they use different base URLs on MirAPI:

Agent Protocol Base URL Authentication
Claude Code Anthropic Messages (POST /v1/messages) https://api.mirapi.ai (no /v1) ANTHROPIC_AUTH_TOKEN=$MIRAPI_API_KEY
Codex CLI OpenAI Responses (POST /v1/responses) https://api.mirapi.ai/v1 env_key = "MIRAPI_API_KEY"

Point the Anthropic client at the bare origin and pass the key through the auth-token variable:

Terminal window
export ANTHROPIC_BASE_URL="https://api.mirapi.ai"
export ANTHROPIC_AUTH_TOKEN="$MIRAPI_API_KEY"
export ANTHROPIC_MODEL="deepseek-chat"

Do not append /v1 — Claude Code builds the /v1/messages path itself. Full setup in Claude Code.

Register MirAPI as a provider in ~/.codex/config.toml:

model = "deepseek-chat"
model_provider = "mirapi"
[model_providers.mirapi]
name = "MirAPI"
base_url = "https://api.mirapi.ai/v1"
env_key = "MIRAPI_API_KEY"
wire_api = "responses"

Codex appends /responses to the base URL, so it needs the /v1 suffix. Full reference in Codex CLI.

Because both agents use the same key, they draw from the same prepaid balance. Watch the spend:

Terminal window
curl https://api.mirapi.ai/api/log/token -H "Authorization: Bearer $MIRAPI_API_KEY"
curl https://api.mirapi.ai/api/usage/token -H "Authorization: Bearer $MIRAPI_API_KEY"

/api/log/token lists per-request rows; /api/usage/token returns account-level totals. Text is metered per token (input / output / cache read), reasoning tokens bill as output, and prompt caching is enabled automatically on OpenAI-compatible models. Reconcile against the console billing log (https://console.mirapi.ai).

Status Meaning What to do
401 Key missing, invalid, or unknown Check the exported key
403 Key valid but refused — balance, whitelist, quota, or IP Fix the cause, then retry
413 Payload too large Trim the diff sent to the model
429 Rate limited Jittered exponential backoff (1s→2s→4s, cap ~30s); no Retry-After header
500 Gateway or upstream error Idempotent requests can be retried safely

The gateway does not retry for you. Every error message ends with a MirAPI request ID — include it in support tickets.

  • Hook stdout is shown to Claude Code — keep the script silent except for the verdict.
  • The review runs inside the hook, so a long diff delays the next turn; keep the timeout tight and the prompt concise.
  • git diff HEAD --stat covers staged and unstaged changes but not untracked files; add git add -N . (or switch to git status --porcelain) if new files matter.
  • If Codex is not logged in or misconfigured, the script exits quietly (|| true) — test it standalone first.

Each review is one short Codex turn — typically a few thousand tokens, so a fraction of a cent to a few cents depending on the diff size and the model. Exact amounts show up in the console usage log and GET /api/log/token.