Skip to main content

Claude Code plugin for automatic review after Claude edits files

Project description

coverage PyPI version

Claude Auto Review

Claude Code plugin for automatic review after Claude edits files.

After each file edit (Write/Edit/MultiEdit/Delete), the plugin tracks the file hash. When Claude tries to stop, the plugin blocks until the changes have been reviewed — either manually in-session or automatically via a reviewer CLI backend.

Features

  • Tracks file edits through Claude Code hooks and blocks stop until changes are reviewed.
  • Supports reviewer CLI backends via reviewerBackend (claude or codex).
  • Uses a last-message classifier to skip review generation when Claude should keep working.
  • Enforces a stop circuit breaker with maxStopPasses.
  • Live-reloads .claude/settings.json without a restart.
  • Dependency-free Python (standard library only).

Installation

pip install claude-auto-review          # Install from PyPI
claude-auto-review install              # One-time init in project root
# or: car install

Creates .claude/claude-auto-review/, copies rules, and updates .claude/settings.json.

Then configure:

claude-auto-review config               # Interactive setup wizard
# or: car config

Prompts for the key settings — after that, Claude Code sessions use the plugin automatically.

See INSTALL.md for full details.

Architecture

The implementation is split into small modules instead of one monolith. Below are focused diagrams covering each subsystem.


1. Hook Wiring

Claude Code calls three lifecycle hooks, each mapped to a plugin hook handler via hooks.json:

flowchart LR
    subgraph "Hooks.json Registration"
        direction TB
        PTU["PostToolUse"]
        STP["Stop ★"]
        SEND["SessionEnd"]
    end

    subgraph "Plugin Hook Handlers"
        direction TB
        PTU_H["post_tool_use.py<br/>Tracks file hashes<br/>for review workflow"]
        STP_H["stop_hook.py<br/>🚦 Decision engine → review<br/>generation → block / allow"]
        SEND_H["session_end.py<br/>Cleans up stale state"]
    end

    PTU -.-> PTU_H
    STP == "★ Reviews happen here ★" ===> STP_H
    SEND -.-> SEND_H

    style PTU fill:#e3f2fd,stroke:#1565c0,stroke-dasharray: 3 3
    style PTU_H fill:#e3f2fd,stroke:#1565c0,stroke-dasharray: 3 3
    style STP fill:#fff3e0,stroke:#e65100,stroke-width:4px
    style STP_H fill:#fff3e0,stroke:#e65100,stroke-width:4px
    style SEND fill:#f3e5f5,stroke:#7b1fa2,stroke-dasharray: 3 3
    style SEND_H fill:#f3e5f5,stroke:#7b1fa2,stroke-dasharray: 3 3

2. File Edit Tracking (PostToolUse)

Every time Claude edits a file, the PostToolUse hook captures the file hash and stores an event for later review:

flowchart TD
    EDIT["Claude edits file<br/>(Write / Edit / MultiEdit / Delete)"] --> EXTRACT["Extract file paths from hook input"]
    EXTRACT --> FILTER{"Should skip?"}
    FILTER -- "Runtime path or excluded pattern" --> SKIP["Log skipped file, continue"]
    FILTER -- "Tracked path" --> HASH["Get file hash from disk"]
    HASH -- "File missing (deleted)" --> DEL["Append EditRecord(deleted=True)"]
    HASH -- "Hash obtained" --> REVIEWED{"Already reviewed?"}
    REVIEWED -- "Yes" --> APPEND_R["Append EditRecord(reviewed=True)"]
    REVIEWED -- "No" --> APPEND_U["Append EditRecord(reviewed=False)"]
    APPEND_R --> DONE["Continue Claude session"]
    APPEND_U --> DONE
    DEL --> DONE
    SKIP --> DONE

    style EDIT fill:#e3f2fd,stroke:#1565c0
    style HASH fill:#e8f5e9,stroke:#2e7d32
    style APPEND_U fill:#ffebee,stroke:#c62828
    style APPEND_R fill:#e8f5e9,stroke:#2e7d32
    style DONE fill:#f5f5f5,stroke:#9e9e9e

3. Stop Flow — Decision Engine

When Claude attempts to stop, the Stop hook runs the stop-flow service through staged checks. Each stage decides whether to allow the stop, continue without review, or block for review:

flowchart TD
    STOP["Claude attempts stop"] --> CTX["Build RuntimeContext<br/>(project, client, settings)"]
    CTX --> ENABLED{"Plugin enabled?"}
    ENABLED -- "No" --> ALLOW_DISABLED["Allow stop (disabled)"]
    ENABLED -- "Yes" --> STATE["Load state snapshot"]
    STATE --> UNREVIEWED{"Unreviewed files?"}
    UNREVIEWED -- "No" --> ALLOW_CLEAN["Allow stop (clean)"]
    UNREVIEWED -- "Yes" --> BREAKER{"Circuit breaker hit?"}
    BREAKER -- "Yes (≥ maxStopPasses)" --> ALLOW_BREAKER["Allow stop (circuit breaker)"]
    BREAKER -- "No" --> CLASSIFIER{"Classifier status?"}
    CLASSIFIER -- "incomplete" --> ALLOW_INCOMPLETE["Allow stop —<br/>Claude still working"]
    CLASSIFIER -- "complete / unknown / error / skipped" --> PENDING{"Pending review reuse?"}
    PENDING -- "Yes (unexpired)" --> FINALIZE_OLD["Finalize with cached review"]
    PENDING -- "No" --> PROMPT["Generate review prompt"]
    PROMPT --> SUCCESS{"Review generated?"}
    SUCCESS -- "No" --> BLOCK["Block stop"]
    SUCCESS -- "Yes" --> BLOCK2{"Would stop be blocked?"}
    FINALIZE_OLD --> BLOCK2
    BLOCK2 -- "No" --> ALLOW_REVIEWED["Allow stop (reviewed)"]
    BLOCK2 -- "Yes" --> BLOCK
    BLOCK --> RETURN_BLOCKED["Return blocked stop response"]

    style STOP fill:#fff3e0,stroke:#e65100
    style ALLOW_DISABLED fill:#e8f5e9,stroke:#2e7d32
    style ALLOW_CLEAN fill:#e8f5e9,stroke:#2e7d32
    style ALLOW_BREAKER fill:#e8f5e9,stroke:#2e7d32
    style ALLOW_INCOMPLETE fill:#e8f5e9,stroke:#2e7d32
    style ALLOW_REVIEWED fill:#e8f5e9,stroke:#2e7d32
    style BLOCK fill:#ffebee,stroke:#c62828
    style RETURN_BLOCKED fill:#ffebee,stroke:#c62828

4. Review Prompt Generation

When a new review is needed, the plugin assembles context from rules and session-scoped diffs into a prompt for the reviewer backend:

flowchart LR
    subgraph inputs["① Review Inputs"]
        direction TB
        RULES["Review rules"]
        SNAPSHOTS["Session-scoped diff<br/>(captured before first edit)"]
        FILES["Unreviewed files"]
    end

    subgraph assembly["② Prompt Assembly"]
        direction TB
        TIMESTAMP("Gen ID + timestamp")
        BUILD["Build prompt"]
        WRITE_PROMPT["Write prompt file"]
        WRITE_REVIEW["Write review stub"]
    end

    subgraph backend["③ Reviewer Backend"]
        direction TB
        CLAUDE["Claude Code"]
        CODEX["Codex CLI"]
    end

    FILES --> TIMESTAMP
    RULES --> BUILD
    SNAPSHOTS --> BUILD
    TIMESTAMP --> BUILD
    BUILD --> WRITE_PROMPT
    BUILD --> WRITE_REVIEW
    WRITE_PROMPT --> CLAUDE
    WRITE_PROMPT --> CODEX

    style TIMESTAMP fill:#f3e5f5,stroke:#7b1fa2
    style inputs fill:#e3f2fd,stroke:#1565c0
    style assembly fill:#f3e5f5,stroke:#7b1fa2
    style backend fill:#e8f5e9,stroke:#2e7d32

5. State Management

All session events are recorded as JSONL entries. A snapshot is computed on each stop attempt for consistent queries:

flowchart LR
    subgraph writers["① Event Writers"]
        direction TB
        PTU_W["PostToolUse writes<br/>EditRecord"]
        STOP_W["Stop hook writes<br/>ReviewMetadata / StopBlocked"]
        SEND_W["SessionEnd writes<br/>cleanup events"]
    end

    subgraph storage["② State Storage"]
        direction TB
        CLI_STATE["Per-client state.jsonl"]
        GLOBAL_LOG["Project lifecycle log"]
    end

    subgraph reader["③ State Reader"]
        direction TB
        SNAPSHOT["StateSnapshot<br/>from events"]
        UNREVIEWED["Unreviewed files"]
        BLOCKS["Stop blocks count"]
        REVIEW_MATCH["Pending review match"]
    end

    PTU_W --> CLI_STATE
    STOP_W --> CLI_STATE
    SEND_W --> CLI_STATE
    PTU_W -.-> GLOBAL_LOG
    STOP_W -.-> GLOBAL_LOG
    SEND_W -.-> GLOBAL_LOG

    CLI_STATE --> SNAPSHOT
    SNAPSHOT --> UNREVIEWED
    SNAPSHOT --> BLOCKS
    SNAPSHOT --> REVIEW_MATCH

    style writers fill:#e3f2fd,stroke:#1565c0
    style storage fill:#fff3e0,stroke:#e65100
    style reader fill:#e8f5e9,stroke:#2e7d32

The classifier runs before pending-review resolution on unreviewed stop paths: incomplete lets Claude continue working without invoking review generation, while complete, unknown, error, and skipped continue into the normal review/block flow.

The stop flow reads the current client state into a snapshot once per stop attempt so lifecycle queries share one view of the session.

State events are written through a single semantic append path so the per-client state.jsonl log and the project-level lifecycle log stay aligned.

Related Projects

This plugin was inspired by:

Thanks to both projects for the ideas and patterns that influenced this plugin.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

claude_auto_review-1.10.7.tar.gz (79.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

claude_auto_review-1.10.7-py3-none-any.whl (122.0 kB view details)

Uploaded Python 3

File details

Details for the file claude_auto_review-1.10.7.tar.gz.

File metadata

  • Download URL: claude_auto_review-1.10.7.tar.gz
  • Upload date:
  • Size: 79.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.8

File hashes

Hashes for claude_auto_review-1.10.7.tar.gz
Algorithm Hash digest
SHA256 5dc53c7a0fd5b1c2f646bc2c3be156b682a86c25de048d5d592fe4e9369be478
MD5 b898ab3b571ff3c8746b1565ef259385
BLAKE2b-256 e294226d6e8611c6ab7c6a7cc58c2f3183565e6ee0ff0512495e2d2c8949c135

See more details on using hashes here.

File details

Details for the file claude_auto_review-1.10.7-py3-none-any.whl.

File metadata

File hashes

Hashes for claude_auto_review-1.10.7-py3-none-any.whl
Algorithm Hash digest
SHA256 c7edbbd1321359673f883d7b804f72d184b24831e0d4095ff680b423215005d3
MD5 61df934360fff05b0eb96c898daf198e
BLAKE2b-256 5576c7f1e04114e849c4453ecc0949afd0f7fe06b6e86639c74e8448cb8c28f2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page