Skip to main content

AI-powered git diff analysis and code review with RAG context

Project description

diffrag

AI-powered code reviewer for git repositories. Point it at two branches and get an LLM-generated review that understands the broader codebase through RAG (Retrieval-Augmented Generation).

Features

  • AI code review — analyzes the diff between any two branches and reports on breaking changes, API surface changes, SOLID violations, code smells, security concerns, missing documentation, and more.
  • RAG context — the full repository is indexed into a ChromaDB vector store so the LLM can reference relevant surrounding code, not just the changed lines.
  • Smart index caching — the vector index is rebuilt only when the HEAD commit changes; repeated runs on the same commit reuse the cached index instantly.
  • Interactive Q&A — after a review you can open an interactive REPL and ask follow-up questions about the diff or the codebase; the session remembers prior exchanges so follow-up questions work naturally.
  • Flexible AI backends — works with any OpenAI-compatible endpoint (OpenWebUI, Ollama /v1, OpenAI) as well as Ollama's native API.
  • Fully configurable — settings can be set once in a user-level global config or overridden per-project; sane defaults work out of the box. Indexes are stored in the OS user cache directory — your project directories stay clean.
  • .gitignore-aware indexing — the repository's .gitignore is parsed automatically and its patterns are respected during indexing; extra excludes can be added via config.

Installation

uv add diffrag          # add to a project
uv tool install diffrag # install as a standalone CLI tool

Requires Python ≥ 3.12.

Setup

1. Create a config file

diffrag looks for configuration in the following order (first match wins):

  1. --config <path> — explicit path passed on the command line
  2. .diffrag.toml — in the current working directory (project-level override)
  3. $(diffrag config-path) — user-level global config (set once, used everywhere)
  4. Built-in defaults — everything works without any config file

Recommended: set up a global config once so every repository uses the same model and endpoint without needing its own .diffrag.toml:

# Create the global config (creates the directory too)
diffrag create-config

# Open it in your editor
$EDITOR "$(diffrag config-path)"

For a project-specific override (e.g. a different base_branch or verbosity):

diffrag create-config --out .diffrag.toml

Per-project .diffrag.toml files take precedence over the global config.

Minimal config — Ollama for both LLM and embeddings:

[ai]
base_url = "http://localhost:11434"
model    = "llama3.2"

[embedding]
base_url = "http://localhost:11434"
model    = "nomic-embed-text"

OpenWebUI for the LLM, Ollama for embeddings:

[ai]
base_url = "http://localhost:3000"
model    = "gpt-4o"
api_key  = "your-openwebui-token"

[embedding]
base_url = "http://localhost:11434"
model    = "nomic-embed-text"

The [ai] and [embedding] sections are independent — you can mix and match any OpenAI-compatible endpoint for each role.


CLI usage

review — generate a code review

# Review current branch (HEAD) against main
diffrag review --base main

# Review a specific branch against main
diffrag review --base main --head feature/my-feature

# Review against a tag or commit SHA
diffrag review --base v1.2.0 --head HEAD

# Review a repo at a different path
diffrag review --repo /path/to/repo --base main

# Use a non-default config file
diffrag review --base main --config /path/to/my.toml

Index caching: on the first run the repository is indexed and the result is persisted to the OS user cache directory (run diffrag db-path to see the exact path for a given repo). On subsequent runs the tool compares the current HEAD SHA against the stored one — if they match the index is reused immediately; if they differ (new commit or branch switch) the index is rebuilt automatically. Your project directories are never modified.

ask — interactive Q&A about the diff

diffrag ask --base main
diffrag ask --base main --head feature/my-feature

Opens an interactive prompt backed by the same RAG index. Run review at least once beforehand so the index is populated. Type exit or press Ctrl-D to quit.

Each answer is informed by the repository context and the diff. The session accumulates conversation history, so follow-up questions can refer back to earlier answers:

> What does the change in auth.py affect downstream?
> Can you give a concrete example of how that breaks existing callers?
> Which tests cover the modified functions?

show-config — inspect effective configuration

diffrag show-config
diffrag show-config --config /path/to/my.toml

Prints a table of every setting that is in effect, including defaults for keys not present in your config file.

db-path — print the database path for a repository

diffrag db-path
diffrag db-path --repo /path/to/repo

Prints the path where the vector index for that repository is (or will be) stored. Useful for inspecting or deleting a stale index.

create-config — create a starter config file

diffrag create-config                       # write to user-level global config
diffrag create-config --out .diffrag.toml  # write a project-level override
diffrag create-config --force               # overwrite if the file already exists

Creates a config file pre-populated with all available settings and their defaults. The destination directory is created automatically.

config-path — print the user-level config path

diffrag config-path

Prints the OS-appropriate path for the user-level config file (it may not exist yet). Useful for opening the config in an editor: $EDITOR $(diffrag config-path).

Global options

Option Description
-v / --verbose Enable debug-level logging
-c / --config PATH Path to a .toml config file (overrides auto-discovery)

Configuration reference

All keys are optional. Unset keys fall back to the defaults shown below. Place the file at $(diffrag config-path) for global defaults, or at .diffrag.toml in a project root to override per-repository.

[ai]
# Base URL of the OpenAI-compatible or Ollama endpoint used for review generation.
# Do NOT include a /v1 suffix — it is appended automatically.
base_url = "http://localhost:11434"

# Model identifier sent with every completion request.
model = "llama3.2"

# Bearer token for the Authorization header. Leave empty for local servers.
api_key = ""

# Per-read timeout in seconds. Requests use streaming, so this is the maximum
# time allowed between consecutive chunks — not the total generation time.
# Increase only if the model stalls mid-generation for unusually long periods.
timeout = 120.0


[embedding]
# Endpoint used for computing text embeddings. Can be the same server as [ai]
# or a different one (e.g. a dedicated embedding service).
base_url = "http://localhost:11434"

# Embedding model. nomic-embed-text works well with Ollama.
model = "nomic-embed-text"

api_key = ""
timeout = 60.0


[indexing]
# The index is always stored in the OS user cache directory, keyed by repo.
# Run "diffrag db-path" to see the exact path. This cannot be configured.

# Maximum number of characters per text chunk when splitting files.
chunk_size = 1024

# Character overlap between consecutive chunks (helps preserve context at boundaries).
chunk_overlap = 128

# The repository's .gitignore is parsed automatically and its patterns are
# applied first. The list below provides additional excludes on top of .gitignore.
# Any path component matching one of these strings is also skipped.
excluded_patterns = [
    ".git", ".venv", "__pycache__", "node_modules",
    ".idea", ".mypy_cache", ".ruff_cache", "dist", "build"
]

# Files larger than this (in bytes) are silently skipped.
# Binary files are always skipped regardless of this setting.
max_file_size = 1000000


[review]
# Default base branch when --base is not specified on the command line.
base_branch = "main"

# Number of context chunks retrieved from the vector index per diff chunk.
# Higher values give the LLM more surrounding context but increase prompt size.
similarity_top_k = 5

# Approximate token budget per diff chunk before it is split into individual hunks.
# Reduce this if your model has a small context window.
max_prompt_tokens = 6000

# Detail level of generated reviews. Can be overridden per-run with --verbosity.
# "brief"    — top 3 critical issues only, under 200 words
# "standard" — balanced default
# "detailed" — exhaustive, includes minor nits and suggested fixes per issue
verbosity = "standard"

Library usage

diffrag is also usable as a Python library:

import asyncio
from pathlib import Path
from diffrag import (
    Settings,
    GitRepository,
    OpenAICompatClient,
    RepoIndexer,
    CodeReviewer,
)

settings = Settings.from_toml(Path(".diffrag.toml"))

ai_client = OpenAICompatClient(
    base_url=settings.ai.base_url,
    model=settings.ai.model,
    api_key=settings.ai.api_key,
)
embed_client = OpenAICompatClient(
    base_url=settings.embedding.base_url,
    model=settings.embedding.model,
    api_key=settings.embedding.api_key,
)
indexer = RepoIndexer(
    db_path=settings.indexing.db_path,
    embedding_client=embed_client,
)
reviewer = CodeReviewer(ai_client=ai_client, indexer=indexer)
repo = GitRepository(Path("/path/to/repo"))


async def main() -> None:
    # review() checks the HEAD commit hash and only rebuilds the index when needed
    result = await reviewer.review(repo, base="main", head="feature/my-feature")
    print(result.summary)

    # ask follow-up questions about the same diff; pass history for multi-turn
    diff = repo.get_diff("main", "feature/my-feature")
    history: list[tuple[str, str]] = []
    q1 = "Are there any breaking API changes?"
    a1 = await reviewer.ask(q1, diff)
    print(a1)
    history.append((q1, a1))
    a2 = await reviewer.ask("Which callers are affected?", diff, history=history)
    print(a2)


asyncio.run(main())

For Ollama's native API (supports num_ctx and other Ollama-specific options):

from diffrag import OllamaClient

embed_client = OllamaClient(
    base_url="http://localhost:11434",
    model="nomic-embed-text",
)

How the review pipeline works

  1. Diffgit diff base...head is run for each changed file; results are parsed into typed FileDiff / Hunk objects.
  2. Index — every non-binary file in the repository is split into overlapping character chunks and embedded into ChromaDB. The HEAD commit hash is saved alongside the index; on future runs this hash is compared and the rebuild is skipped if nothing has changed.
  3. Per-file review — for each changed file:
    • The LLM summarises the diff in plain English (used as a targeted search query).
    • The summary is embedded and the top-k nearest chunks are retrieved from the index.
    • The LLM reviews the raw diff in light of the retrieved context.
    • Large diffs are split into individual git hunks and reviewed separately.
  4. Consolidation — all per-file reviews are sent to the LLM in a final prompt that produces a single, cross-cutting summary.

Development

# Install with dev dependencies
uv sync --group dev

# Run tests
uv run pytest

# Lint and format checks
uv run ruff check src tests
uv run ruff format --check src tests

# Type checking
uv run ty check src

# Run all checks via tox
uv run tox

# Build Sphinx docs
uv run sphinx-build docs docs/_build/html

Project structure

src/diffrag/
├── config/       # Settings dataclasses + TOML loading (settings.py)
├── diff/         # GitRepository, FileDiff, Hunk models (git.py, models.py)
├── ai/           # AIClient / EmbeddingClient protocols, OpenAI-compat + Ollama clients
├── indexing/     # ChromaDB-backed RepoIndexer with staleness detection
├── review/       # CodeReviewer orchestrator + prompt templates
└── cli/          # Click CLI: review / ask / show-config / create-config / db-path / config-path

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

diffrag-0.2.3.tar.gz (31.1 kB view details)

Uploaded Source

Built Distribution

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

diffrag-0.2.3-py3-none-any.whl (40.9 kB view details)

Uploaded Python 3

File details

Details for the file diffrag-0.2.3.tar.gz.

File metadata

  • Download URL: diffrag-0.2.3.tar.gz
  • Upload date:
  • Size: 31.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for diffrag-0.2.3.tar.gz
Algorithm Hash digest
SHA256 0633000030164aedc2b7c5bd117de42130d2fcecef4717b2369333241c81bbf6
MD5 5d66aca59ce8cf4a025c9e7e7ebf27b8
BLAKE2b-256 902220cf27d8c6b969f028349d761dfc2d99c036ae40088d4e563258cdddf407

See more details on using hashes here.

File details

Details for the file diffrag-0.2.3-py3-none-any.whl.

File metadata

  • Download URL: diffrag-0.2.3-py3-none-any.whl
  • Upload date:
  • Size: 40.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for diffrag-0.2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 20b66773baf6168610dd7f33339fe48f6bfb84ceb0c370401f83ec255e05f171
MD5 2dfb1c8c661e540206906cb10167bb1f
BLAKE2b-256 f482457a8cbfb30404d037850ab2230ae8f422a3597bba196374aa568cacee5d

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