Automatic Code Review
Automatic code review for Claude Code using hooks and OpenRouter — async, non-blocking reviews from a second model while you keep working
Automatic code review for Claude Code using hooks and OpenRouter — async, non-blocking reviews from a second model while you keep working
A reference implementation is available at redline. Key features:
redline loginEvery time Claude Code finishes a response and there are uncommitted changes, a hook automatically triggers a background Codex code review — without blocking your workflow. You keep working while the review runs. When it finishes, Claude reads the output and presents the findings.
No background processes, no daemons, no filesystem protocols. The hook is the trigger, and Claude Code’s own background task system handles async execution.
claude on PATH)codex on PATH)Both agents route inference through OpenRouter, but they use different API skins with different base URLs.
Set these environment variables in your shell profile
(~/.zshrc, ~/.bashrc). Do not use a .env
file — Claude Code doesn’t read them.
The base URL is https://openrouter.ai/api — no
/v1 suffix. This is OpenRouter’s Anthropic Skin,
which speaks the native Anthropic protocol. Using
/v1 causes model-not-found errors.
ANTHROPIC_API_KEY must be explicitly empty to
prevent Claude Code from authenticating directly
with Anthropic.
Verify by running /status in a Claude Code session.
See the full
Claude Code integration guide
for details.
Create or edit ~/.codex/config.toml:
Then set the API key:
At runtime, pass
-c 'model_provider="openrouter"' to select the
OpenRouter provider.
Common pitfalls:
--provider flag —
use -c for all runtime config overrides[model_providers.openrouter],
not [provider.openrouter]https://openrouter.ai/api/v1 (with
/v1), while Claude uses
https://openrouter.ai/api (without /v1) —
they use different protocol skinsSee the full Codex CLI integration guide for details.
Claude Code has a hook system configured in
settings.json. The key hook for this use case is
Stop, which fires every time Claude finishes a
response cycle.
decision: "block" works"decision": "block" with a
"reason" string, Claude Code:
reason text is injected into Claude’s
context as new informationThis is the key mechanism: the check command uses
decision: "block" to inject a diff stat summary
and review instructions into Claude’s context. Claude
sees the summary, decides whether the changes warrant
a review, and if so spawns the review command via its
Bash tool. The background task appears in Claude’s
task list — visible, monitorable, and killable.
In order of precedence:
~/.claude/settings.json — global (all projects).claude/settings.json — project (shareable,
committed).claude/settings.local.json — project (local,
gitignored)Use .claude/settings.local.json for the review
hook so it doesn’t affect other collaborators.
The timeout is 10 seconds — the check is fast. This is much lower than the 300s you’d need for a synchronous review.
A simpler synchronous approach — running the full review inside the Stop hook — has three problems:
codex exec review can take minutes.
A synchronous Stop hook blocks Claude Code the
entire time — no progress visibility, no way to
cancel.The async approach solves all three: the check is fast (<1s), only fires when changes exist, hashes the diff stat to skip when nothing has changed since the last check, and the reason text tells Claude to skip if a review is already running. The actual review runs as a Claude Code background task that the user can monitor and kill.
The tool splits into two commands: check (the fast gate, called by the hook) and review (the actual work, spawned by Claude as a background task).
The check command runs on every Stop event and must complete in under a second.
The check uses git diff --stat HEAD for a concise
summary of what changed, falling back to
git status --porcelain for untracked files. It
hashes the diff stat and stores it in
.git/redline-last-diff — if the diff hasn’t changed
since the last check, the hook exits silently. This
prevents the same diff from repeatedly firing the
hook. The diff stat is included in the reason text so
Claude can decide whether the changes warrant a
review.
The review command is spawned by Claude as a background task. It streams Codex output in real-time for progress visibility, then prints a final summary.
Key details:
codex exec review --uncommitted — reviews all
staged, unstaged, and untracked changesstdio: "inherit" — streams Codex output in
real-time so the background task shows progress-o <file> — writes the last agent message to a
file for reliable output capture-c 'model_provider="openrouter"' — routes
through OpenRouter-c 'model="openai/gpt-5.4-pro"' for
model overrideThe check command’s reason field includes the diff
stat and lets Claude decide whether to review:
Claude sees the summary, judges whether the changes are substantive, and either spawns the review as a background task or skips it. When the review completes, Claude reads the streamed output and presents the findings.
The tool should provide commands to programmatically
install and remove the hook from
.claude/settings.local.json.
Read .claude/settings.local.json, deep-merge the
Stop hook entry, and write back. Create the .claude/
directory if needed. Be idempotent — if the hook
already exists with the same config, skip. If it
exists with a different model, update it. Identify
your hooks by command prefix (commands starting with
"redline").
The resulting file:
Filter out hook entries whose command starts with
"redline". Clean up empty arrays and objects
(remove Stop: [] if empty, remove hooks: {} if
empty).
The full CLI has four commands:
You should see the Stop hook entry with the
redline check command and a 10-second timeout.
Ensure your working tree is clean, then:
No output — the check exits silently when there are no uncommitted changes.
Make a small change to any file, then:
You should see JSON with "decision": "block" and a
"reason" containing the diff stat summary and
review instructions.
You should see streamed Codex output followed by a review summary.
redline installclaudeThis pattern relies on Claude Code’s
decision: "block" hook output, which injects
instructions directly into the agent’s conversation.
Codex CLI’s hook system (as of early 2026) is more
limited — its Stop hook is fire-and-forget and
cannot feed structured output back into the model’s
context. Native hooks ([[hooks]] in config.toml)
support several events, but only SessionStart can
feed stdout into the model. There is no equivalent of
decision: "block" + reason for injecting
mid-session instructions. When Codex adds full
structured hook output support, this pattern can be
extended.
The review covers everything uncommitted — all staged, unstaged, and untracked changes. It does not distinguish between changes Claude just made and pre-existing uncommitted work. Future versions could use smarter change detection (e.g., diffing against a baseline snapshot taken before Claude’s response).