A reference implementation is available at redline. Key features:
- Claude (or you) decides when a review is necessary
- Claude Code is in full control of the reviewer agent — it runs as an async background process
- Both agents are observable, customizable, and cost-monitored on OpenRouter
- Log into both agents with one command:
redline login - Implemented as a single Claude Code stop hook, providing transparency and customizability on the agent and model(s) used
What This Achieves
Every 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.Prerequisites
- An OpenRouter API key
- Claude Code installed (
claudeon PATH) - Codex CLI installed (
codexon PATH) - Bun or Node.js runtime
Configuring OpenRouter
Both agents route inference through OpenRouter, but they use different API skins with different base URLs.Claude Code
Set these environment variables in your shell profile (~/.zshrc, ~/.bashrc). Do not use a .env file — Claude Code doesn’t read them.
/status in a Claude Code session. See the full Claude Code integration guide for details.
Codex CLI
Create or edit~/.codex/config.toml:
-c 'model_provider="openrouter"' to select the OpenRouter provider.
See the full Codex CLI integration guide for details.
Understanding the Stop Hook
Claude Code has a hook system configured insettings.json. The key hook for this use case is Stop, which fires every time Claude finishes a response cycle.
How decision: "block" works
- The hook command runs and outputs JSON to stdout
- If the JSON contains
"decision": "block"with a"reason"string, Claude Code:- Does not stop — it continues the conversation
- The
reasontext is injected into Claude’s context as new information - Claude processes it and acts on it (e.g., spawning a background task as instructed)
- If the command exits 0 with no JSON output, Claude proceeds normally (no blocking)
- If the command exits non-zero, it’s treated as a non-blocking error
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.
Settings file locations
In order of precedence:~/.claude/settings.json— global (all projects).claude/settings.json— project (shareable, committed).claude/settings.local.json— project (local, gitignored)
.claude/settings.local.json for the review hook so it doesn’t affect other collaborators.
Hook configuration
Why Async Matters
A simpler synchronous approach — running the full review inside the Stop hook — has three problems:- Blocking.
codex exec reviewcan take minutes. A synchronous Stop hook blocks Claude Code the entire time — no progress visibility, no way to cancel. - No filtering. The hook fires after every response, even when Claude just answered a question and made no code changes.
- Duplicate reviews. Nothing prevents the hook from triggering a second review while one is already running.
The Two-Command Architecture
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).Building the check command
The check command runs on every Stop event and must complete in under a second.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.
Building the review command
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.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- Optional:
-c 'model="openai/gpt-5.4-pro"'for model override - Exit code is checked — if Codex fails and produced no output, the tool exits with an error
The reason text that instructs Claude
The check command’sreason field includes the diff stat and lets Claude decide whether to review:
Installing and Removing the Hook
The tool should provide commands to programmatically install and remove the hook from.claude/settings.local.json.
Install
.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:
Remove
command starts with "redline". Clean up empty arrays and objects (remove Stop: [] if empty, remove hooks: {} if empty).
Implementation
Putting It Together
The full CLI has four commands:Full CLI entry point
Testing
Verify hook installation
redline check command and a 10-second timeout.
Test check with no changes
Ensure your working tree is clean, then:Test check with changes
Make a small change to any file, then:"decision": "block" and a "reason" containing the diff stat summary and review instructions.
Test review
Full integration test
- Install the hook:
redline install - Start Claude Code:
claude - Ask Claude to make a small code change
- When Claude finishes, the Stop hook fires the check — if there are uncommitted changes, Claude spawns the review as a background task
- You can keep working while the review runs
- When the review completes, Claude reads the output and presents any findings
Limitations and Future Work
Claude Code only
This pattern relies on Claude Code’sdecision: "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.