Skip to main content

Blast radius analyzer for AI-generated code changes. Catches the butterfly effect before you commit.

Project description

diff-guard

PyPI version

Stop AI coding agents from breaking things they weren't asked to touch.

diff-guard is a blast radius analyzer for AI-generated code changes. It detects unintended modifications, measures their downstream impact, and blocks dangerous commits before they reach production.


The Problem

AI coding agents are fast, but they create 1.7x more post-release issues than human-written code. An agent asked to fix a login bug might also refactor the payment module, tweak a cache layer, or silently change error handling three files away.

The butterfly effect is real: one stray change in a utility function can cascade through import chains, breaking features nobody asked it to touch. Traditional linters and tests catch syntax errors and known patterns, but they don't answer the question: "Did the AI change more than it was supposed to?"

diff-guard answers that question. Every commit.


30-Second Demo

$ diffguard-cli check --scope "fix login form validation"

╭──────────────────────────────────────────────────────────╮
│                 diff-guard blast radius                  │
├──────────────────────────────────────────────────────────┤
│                                                          │
│  Scope: "fix login form validation"                      │
│         Files changed: 7  |  Lines: +142 / -38           │
╰──────────────────────────────────────────────────────────╯

Risk level: ⚠️  REVIEW (score: 0.54)
  [████████████░░░░░░░░░░] 54%

✅ In scope (4 files):
   src/auth/login.py           +45 / -12   validate_email(), validate_password()
   src/templates/login.html    +28 / -8    form layout, error messages
   src/forms/login_form.py     +15 / -3    field validators
   tests/test_login.py         +30 / -0    new test cases

⚠️  Phantom changes (2 files):
   src/auth/session.py         +18 / -9    [confidence: 72%]
      Not mentioned in scope, but 1 hop from login.py via imports
      Might be intentional  review recommended

   src/middleware/rate_limit.py +6 / -6     [confidence: 94%]
      0 import-chain connection to any in-scope file
      Completely unrelated to "fix login form validation"
      LIKELY UNINTENDED

💥 Downstream blast radius:
   src/api/users.py            imports session.py  may break
   src/api/payments.py         imports session.py  may break
   src/api/dashboard.py        imports session.py, rate_limit.py  may break
   ... and 22 more files

🧪 Suggested tests to run:
   pytest tests/test_login.py tests/test_session.py tests/test_rate_limit.py -v

💡 Recommendation: Review changes to session.py and rate_limit.py manually.

Install

pip install diffguard-cli

View on PyPI

Then set up the pre-commit hook:

diffguard-cli install

Every commit will now be automatically checked. Use --no-verify to skip when needed.


How It Works — The Pipeline

Every time you (or an AI agent) run git commit, diff-guard runs a 6-stage pipeline:

 git commit
     │
     ▼
 ┌──────────────────────────────────────────────────┐
 │  STAGE 1: DIFF PARSING                           │
 │  git diff --staged → list of Change objects      │
 │  (files, hunks, functions, imports, line counts) │
 └──────────┬───────────────────────────────────────┘
            │
            ▼
 ┌──────────────────────────────────────────────────┐
 │  STAGE 2: SCOPE RESOLUTION                       │
 │  "What was the developer/AI SUPPOSED to change?" │
 │  Try in priority order:                          │
 │   1. Prompt file (.diff-guard-prompt)  → conf 1.0│
 │   2. CLI argument (--scope)            → conf 0.8│
 │   3. Commit message                    → conf 0.6│
 │   4. Inference from diff structure    → conf 0.3│
 └──────────┬───────────────────────────────────────┘
            │
            ▼
 ┌──────────────────────────────────────────────────┐
 │  STAGE 3: PHANTOM CHANGE DETECTION               │
 │  For each changed file vs. scope:                │
 │   - Auto-ignore lockfiles, generated, migrations │
 │   - Import-chain whitelisting (2-hop)            │
 │   - Dynamic thresholds (low confidence → lenient)│
 │   - Relevance scoring (path + import + proximity)│
 │  "Did the AI change files it wasn't asked to?"   │
 └──────────┬───────────────────────────────────────┘
            │
            ▼
 ┌──────────────────────────────────────────────────┐
 │  STAGE 4: BLAST RADIUS                           │
 │  Build project import graph → BFS from changed   │
 │  files → find downstream dependents (up to 3 hops│
 │  "What ELSE could break?"                        │
 └──────────┬───────────────────────────────────────┘
            │
            ▼
 ┌──────────────────────────────────────────────────┐
 │  STAGE 5: RISK SCORING (5 weighted factors)      │
 │   Scope overflow   (0.30)                        │
 │ + Complexity       (0.20)                        │
 │ + Centrality       (0.20)                        │
 │ + Test coverage    (0.15)                        │
 │ + Deletion risk    (0.15)                        │
 │ ─────────────────────                            │
 │ = SAFE (<0.3) | REVIEW (0.3-0.6) | DANGER (>0.6)│
 │                                                  │
 │ Safety gate: scope confidence < 0.4              │
 │   → never exit non-zero (cannot block commits)   │
 └──────────┬───────────────────────────────────────┘
            │
            ▼
 ┌──────────────────────────────────────────────────┐
 │  STAGE 6: REPORT                                 │
 │  Terminal (ANSI colors) / JSON / Markdown         │
 │  + Test suggestions + Recommendation              │
 └──────────┬───────────────────────────────────────┘
            │
            ▼
      Exit 0 (safe) or Exit 1 (blocked)

Commands

diffguard-cli check

Full blast radius analysis of code changes.

# Check staged changes (default)
diffguard-cli check

# Check with explicit scope
diffguard-cli check --scope "add user authentication"

# Check last commit
diffguard-cli check --last-commit

# Check diff against a branch
diffguard-cli check --diff main

# Output formats
diffguard-cli check --json
diffguard-cli check --markdown

# Fail on specific risk level
diffguard-cli check --fail-on review

diffguard-cli test

Suggest tests for changed files. Zero false-positive risk — purely maps changes to tests.

# Suggest tests for staged changes
diffguard-cli test

# Get just the test command
diffguard-cli test --command-only

# JSON output
diffguard-cli test --json

diffguard-cli install

Install as a git pre-commit hook.

# Install with defaults (fail on 'danger')
diffguard-cli install

# Stricter: fail on 'review'
diffguard-cli install --fail-on review

# Test-only mode (just suggest tests, never block)
diffguard-cli install --mode test-only

# Uninstall
diffguard-cli install --uninstall

diffguard-cli init

Create a .diff-guard.yml config file with sensible defaults.

diffguard-cli init

AI Agent Integration

Claude Code

Add this to your CLAUDE.md:

## Before committing changes

After making changes, write a one-line description of what you changed
to the file `.diff-guard-prompt` before committing. Example:
echo "Refactored login form validation" > .diff-guard-prompt

When .diff-guard-prompt exists, diff-guard uses it as the scope source with confidence 1.0 — the most accurate phantom detection possible.

1. You ask Claude Code: "Fix the login form validation"
2. Claude Code edits files...
3. Claude Code writes scope:
   echo "fix login form validation" > .diff-guard-prompt
4. Claude Code commits:
   git add -A && git commit -m "fix login validation"
5. Hook fires → reads .diff-guard-prompt → detects phantom changes
   → blocks or allows the commit

Cursor

Cursor writes descriptive commit messages. diff-guard extracts scope from the commit message at confidence 0.6:

1. You highlight code: "Fix the payment calculation bug"
2. Cursor edits files (possibly touching unrelated files)...
3. Cursor commits: "Fix payment calculation bug in stripe.py"
4. Hook fires → reads commit message → detects phantom changes

For better accuracy, configure Cursor to write .cursor-prompt before committing (diff-guard reads this file too, at confidence 1.0).

Windsurf / Copilot / Cline / Any Agent

The same pattern works for any AI coding agent that can write to a file:

# In your agent instructions or wrapper script:
echo "$TASK_DESCRIPTION" > .diff-guard-prompt

# Agent does its work...

# Agent commits:
git add -A && git commit -m "changes"
# Hook fires automatically

Scope Confidence Comparison

Source Confidence Phantom Detection Accuracy
.diff-guard-prompt 1.0 Very accurate
--scope CLI arg 0.8 Accurate
Commit message 0.6 Good
Inference from diff 0.3 Lenient (fewer flags)

Configuration

diff-guard reads .diff-guard.yml from your repository root. Generate one with diffguard-cli init.

# diff-guard configuration
version: 1

# Risk thresholds (0.0 - 1.0)
thresholds:
  phantom_relevance: 0.3   # min relevance to flag as phantom
  risk_safe: 0.3            # below this = safe
  risk_danger: 0.6          # above this = danger

# Glob patterns for files to ignore during analysis
ignore:
  - "*.lock"
  - "*.min.js"
  - "*.min.css"
  - "node_modules/"
  - "vendor/"
  - "__pycache__/"
  - ".git/"
  - "migrations/"

# Test discovery settings
tests:
  directories:
    - "tests/"
    - "test/"
    - "__tests__/"
    - "spec/"
  patterns:
    - "test_*.py"
    - "*_test.py"
    - "*.test.ts"
    - "*.test.js"
    - "*.spec.js"
  command: "pytest {files} -v"

# Scope resolution settings
scope:
  prompt_files:
    - ".diff-guard-prompt"
    - ".claude-prompt"
    - ".cursor-prompt"
  # Define code areas for better scope detection
  # areas:
  #   auth:
  #     files:
  #       - "src/auth/"
  #     related:
  #       - "src/redis_cache.py"

# Pre-commit hook settings
hook:
  fail_on: "danger"     # safe | review | danger | never
  auto_test: false       # automatically run suggested tests
  show_report: true      # show full report in hook output
  mode: "full"           # test-only | full | check-only

# Language-specific analyzer settings
languages:
  python:
    analyzer: "ast"
  javascript:
    analyzer: "regex"
  typescript:
    analyzer: "regex"
  go:
    analyzer: "regex"

CI/CD Integration

GitHub Actions

Add .github/workflows/diff-guard.yml:

name: diff-guard
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  blast-radius-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - run: pip install diffguard-cli

      - name: Check blast radius
        run: |
          diffguard-cli check \
            --diff origin/${{ github.base_ref }} \
            --fail-on review \
            --markdown \
            > diff-guard-report.md

      - name: Comment PR with report
        if: always()
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            const report = fs.readFileSync('diff-guard-report.md', 'utf8');
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: report
            });

      - name: Suggest tests
        if: always()
        run: |
          diffguard-cli test \
            --diff origin/${{ github.base_ref }} \
            --json

Language Support

Language Strategy Notes
Python AST (stdlib) Full import graph, function mapping
JavaScript/TypeScript Generic (regex) Full support with pip install diffguard-cli[js] (tree-sitter)
Go Generic (regex) Import patterns + function detection
Rust Generic (regex) use statements + fn detection
Java Generic (regex) Import + method detection
Ruby Generic (regex) require + def detection
C/C++ Generic (regex) #include detection
Any other Generic fallback Path-based proximity analysis

Quick Reference

What Command
Install hook diffguard-cli install
Stricter hook diffguard-cli install --fail-on review
Test-only hook diffguard-cli install --mode test-only
Uninstall hook diffguard-cli install --uninstall
Check staged diffguard-cli check
Check with scope diffguard-cli check --scope "fix auth"
Check last commit diffguard-cli check --last-commit
Check vs branch diffguard-cli check --diff main
JSON for scripts diffguard-cli check --json
Markdown for PR diffguard-cli check --markdown
Suggest tests diffguard-cli test
Just test command diffguard-cli test --command-only
Create config diffguard-cli init
Skip hook git commit --no-verify

Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b my-feature
  3. Make changes and add tests
  4. Run checks: pytest && mypy src/ --strict && ruff check src/
  5. Commit (diff-guard will check your changes!)
  6. Open a pull request

License

MIT

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

diffguard_cli-0.3.0.tar.gz (91.1 kB view details)

Uploaded Source

Built Distribution

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

diffguard_cli-0.3.0-py3-none-any.whl (61.7 kB view details)

Uploaded Python 3

File details

Details for the file diffguard_cli-0.3.0.tar.gz.

File metadata

  • Download URL: diffguard_cli-0.3.0.tar.gz
  • Upload date:
  • Size: 91.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for diffguard_cli-0.3.0.tar.gz
Algorithm Hash digest
SHA256 7157d4b1c70f6e550dd572b0f0135f60d6129a83f5f500df929a291f5720c983
MD5 9ce99ae493885b1a136a458f81575a06
BLAKE2b-256 653957cde67830fedf2b859910f7012198ef526acefec15b8759c8c113cb1db1

See more details on using hashes here.

File details

Details for the file diffguard_cli-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: diffguard_cli-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 61.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for diffguard_cli-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 20277059c1bfd8ef7b5afd3e399e2c3af8ddeff484fad66f24bebd182a964db2
MD5 e961ada5ebf0502c19097923b36d26df
BLAKE2b-256 84623cd3aaa0faba5c1505471e2c9be130be073471183af269ef9f4135df32a9

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