Diffmind is a free, open-source AI code review tool that runs entirely on your machine — no cloud, no API keys, no subscription. It analyzes your git diff using a local Qwen2.5-Coder model and reports security issues, bugs, and code quality problems directly in your terminal.
Your source code never leaves your environment. Works offline. Ships as a single self-contained binary for Linux, macOS, and Windows.
The only AI code reviewer that keeps your code 100% private.
| Diffmind | Cloud AI review (Copilot, CodeRabbit, etc.) | |
|---|---|---|
| Privacy | Code stays on your machine | Code sent to third-party servers |
| Cost | Free — one-time model download | Per-token billing or subscription |
| Offline | Works with no internet after setup | Requires connectivity |
| CI/CD | Single binary, no runtime deps | Needs API key management and secrets |
| Compliance | No data residency concerns | Data may cross jurisdictions |
- Deterministic detectors — commented-out code, removed-but-still-used declarations, and your own regex rules. These run before the model, cost nothing, and are the findings you can trust unconditionally.
- Security, bug, performance and maintainability review by a local model
- Ticket-aware review — check the diff actually implements the acceptance criteria (
--ticket) - Suppressions — inline
// diffmind-ignorecomments and a project baseline, so one false positive doesn't get the whole gate deleted - SARIF output — inline PR annotations via GitHub Code Scanning, no bot account or token
- Pluggable backends — the bundled GGUF, or your own Ollama / vLLM / LM Studio endpoint
- Daemon mode — keep the model resident so reviews are near-instant
- Local RAG — feeds the model the enclosing function of each hunk, not just the diff
- Reproducible — greedy decoding with a fixed seed: the same diff always reviews the same way
- Interactive TUI (
--tui), JSON / Markdown output, and a proper CI gate
curl -fsSL https://github.com/thinkgrid-labs/diffmind/releases/latest/download/install.sh | bashAuto-detects your OS and CPU architecture, verifies the SHA-256 checksum, and installs to /usr/local/bin.
Pin a specific version — note that the variable goes on the bash side of the pipe, not the curl side:
curl -fsSL https://github.com/thinkgrid-labs/diffmind/releases/latest/download/install.sh | VERSION=v0.8.0 bashDownload diffmind-x86_64-pc-windows-msvc.zip from Releases, extract it, and put diffmind.exe on your PATH.
npx @diffmind/cli --help
npm install -g @diffmind/cli@diffmind/cli is a launcher — the binary ships in a per-platform package declared as an optional dependency, so npm downloads only the one matching your machine. Linux binaries are glibc-linked; on musl (Alpine) use a glibc base image or build from source.
git clone https://github.com/thinkgrid-labs/diffmind
cd diffmind
cargo install --path apps/tui-clidiffmind download # one-time model download (~1.1 GB)
diffmind index # optional: index symbols for context-aware reviews
diffmind # review this branch against the repo's default branch--branch is no longer assumed to be main: diffmind reads the repository's default branch from origin/HEAD and falls back to whichever of main/master/develop/trunk actually exists.
diffmind --last # just the last commit
diffmind --staged # just what's staged
diffmind src/auth/ # just these paths
diffmind --tui # interactive browserModels download to ~/.diffmind/models/. All are Qwen2.5-Coder, Q4_K_M quantised. Inference runs on the Apple Silicon GPU via Metal where available, and on CPU (with Accelerate/BLAS) everywhere else.
diffmind download # interactive picker with a hardware check
diffmind download --model 3b
diffmind download --model 1.5b --force # re-download
diffmind download --model 1.5b --verify # check an existing download's checksum| Model | Size | Min RAM | Notes |
|---|---|---|---|
| Qwen2.5-Coder-0.5B | 0.4 GB | 2 GB | Fastest — lint-style, CI, low-end hardware |
| Qwen2.5-Coder-1.5B ★ | 1.1 GB | 4 GB | Recommended — balanced |
| Qwen2.5-Coder-3B | 2.1 GB | 6 GB | Deeper reasoning |
| Qwen2.5-Coder-7B | 4.7 GB | 10 GB | Strong security analysis |
| Qwen2.5-Coder-14B | 9.0 GB | 18 GB | Expert |
| Qwen2.5-Coder-32B | 20.0 GB | 40 GB | Maximum |
Downloads are atomic and checksummed: an interrupted download can no longer leave a truncated file that looks valid.
The local GGUF is the default and the point of the tool, but "local" doesn't have to mean "in this process". If you already run Ollama, vLLM, or LM Studio, point diffmind at it — your code still never leaves your machine or your network, and you get a model class diffmind will never ship as a 20 GB download.
# Ollama (default URL http://localhost:11434)
diffmind --backend ollama --backend-model qwen2.5-coder:14b
# Anything speaking the OpenAI chat API — vLLM, LM Studio, llama.cpp server, LiteLLM
diffmind --backend openai-compatible \
--backend-url http://localhost:8000/v1 \
--backend-model my-modelAPI keys are read from the environment only (DIFFMIND_API_KEY by default, override with --backend-api-key-env) — never from the config file, which gets committed.
Findings carry a stable rule ID, shown in the output, so you can silence exactly one thing.
| Rule ID | Meaning |
|---|---|
DM001 |
A code block was commented out instead of deleted |
DM002 |
A declaration was removed but is still referenced |
DM900.<category> |
A model-authored finding of that category |
custom.<slug> |
One of your .diffmind/rules.toml rules |
// diffmind-ignore-next-line DM001
// const legacy = oldPath();
const x = 1; // diffmind-ignore
/* diffmind-ignore-file DM900.maintainability */Listing no rule IDs suppresses everything at that location. DM900 suppresses every model category without listing each one.
Adopting diffmind on an existing codebase shouldn't mean fixing everything first.
diffmind baseline create # record today's findings as accepted
diffmind baseline show
diffmind baseline clearCommit .diffmind/baseline.json. Future runs report only new issues. The baseline keys on a content fingerprint rather than a line number, so it survives unrelated edits above the finding.
- uses: thinkgrid-labs/diffmind@v0.8.0
with:
model: 1.5b
fail-on: highThat caches the model, installs the binary, reviews the PR diff, and uploads SARIF so findings appear inline on the diff. For a PR comment instead:
- uses: thinkgrid-labs/diffmind@v0.8.0
with:
format: markdown
comment: true
fail-on: nonepermissions: { security-events: write } is required for the SARIF upload, pull-requests: write for comments.
git diff origin/main...HEAD | diffmind --stdin --format sarif --output diffmind.sarif --fail-on high| Code | Meaning |
|---|---|
0 |
Clean, or nothing at or above --fail-on |
1 |
Findings at or above --fail-on |
2 |
diffmind itself failed (bad flag, missing model) |
1 and 2 used to be indistinguishable, so a crashed binary looked exactly like a failed review.
diffmind install-hooks --hook pre-push --min-severity high
diffmind install-hooks --hook pre-commitThe generated hook exits 0 when diffmind isn't installed, so it never blocks a teammate who hasn't set it up. It refuses to overwrite a hook it didn't write unless you pass --force.
Or via pre-commit:
repos:
- repo: https://github.com/thinkgrid-labs/diffmind
rev: v0.8.0
hooks:
- id: diffmindEvery invocation otherwise pays the model-load cost — seconds, every time.
diffmind serve # loads the model, unloads after 10 idle minutes
diffmind serve --status
diffmind serve --stopReviews automatically use a running daemon whose model and device match; --no-daemon opts out. It listens on 127.0.0.1 only and requires a per-instance token stored in a 0600 file, so no other user on the machine can submit work to it.
.diffmind/config.toml — precedence is CLI flag > config file > default.
[review]
branch = "develop"
model = "3b"
min_severity = "low" # what gets reported
fail_on = "high" # what fails the build
min_confidence = 0.0 # detectors score 0.9+; unscored model findings 0.5
triage = "auto" # two-pass triage on large diffs
cache = true
temperature = 0.0 # 0 = greedy and reproducible
max_tokens = 1024
[backend]
kind = "local" # or "ollama" / "openai-compatible"
url = "http://localhost:11434"
model = "qwen2.5-coder:14b"
api_key_env = "DIFFMIND_API_KEY"Unknown keys are reported rather than silently ignored.
Regex rules run before the model: instant, deterministic, zero inference cost.
[[rule]]
id = "no-console"
pattern = "console\\.log"
message = "Remove debug logging before merging"
fix = "Use the structured logger"
severity = "medium"
category = "quality"
files = ["*.ts", "*.tsx"]
[[rule]]
id = "no-hardcoded-password"
pattern = "password\\s*=\\s*[\"'][^\"']+[\"']"
message = "Hardcoded password — use a secrets manager"
severity = "high"
category = "security"| Field | Required | Description |
|---|---|---|
pattern |
✓ | Regex matched against added lines |
message |
✓ | Finding description |
id |
Stable ID for suppression and SARIF. Defaults to a slug of the message. | |
fix |
Remediation hint | |
severity |
high, medium, low (default medium) |
|
category |
security, quality, performance, maintainability |
|
files |
Globs: *.ts, **/generated/**, or an exact path |
diffmind describe # PR title, summary, and test plan
diffmind commit # conventional commit message for staged changes
diffmind commit --apply
diffmind index # build the symbol index
diffmind cache show # cache location and size
diffmind cache clearUsage: diffmind [OPTIONS] [FILES]... [COMMAND]
Commands:
download Download or refresh the local AI model files
index Build a symbol index for context-aware reviews
describe Generate a PR title and description
commit Suggest a conventional commit message
baseline Record current findings as accepted
install-hooks Install git hooks
serve Keep the model resident between runs
cache Inspect or clear the review cache
Options:
-b, --branch <BRANCH> Base branch [default: the repo's default branch]
-m, --model <MODEL> 0.5b, 1.5b, 3b, 7b, 14b, 32b [default: 1.5b]
-l, --last Review the last commit only
--staged Review staged changes only
--stdin Read the diff from stdin
-t, --tui Launch the interactive TUI
--ticket <FILE_OR_TEXT> Acceptance criteria to check against
--min-severity <LEVEL> Minimum severity to report [default: low]
--min-confidence <N> Minimum confidence to report, 0.0-1.0
--fail-on <LEVEL> Severity causing exit 1 [default: --min-severity]
-f, --format <FORMAT> text, json, sarif, markdown [default: text]
-o, --output <FILE> Write the report to a file
--max-tokens <N> Output tokens per chunk [default: 1024]
--triage <MODE> auto, on, off [default: auto]
--temperature <N> 0 is greedy and reproducible [default: 0]
--seed <N> Sampling seed
--no-cache Skip the result cache
--no-baseline Ignore .diffmind/baseline.json
--no-daemon Don't use a running daemon
--device <DEVICE> auto, cpu, metal [default: auto]
--backend <KIND> local, ollama, openai-compatible
--backend-url <URL> Remote backend base URL
--backend-model <NAME> Model name on the remote backend
--debug Print raw model output to stderr
- Parse — the diff is parsed once into typed per-file hunks with real pre/post-image line numbers.
- Deterministic detectors — commented-out code, removed-but-used declarations, and your regex rules. No model involved.
- Context — the enclosing function of each hunk (plus definitions of referenced symbols) is pulled from
.diffmind/symbols.json. - Triage — on large diffs, a cheap first pass decides which files carry real risk.
- Chunked inference — chunks are sized to the backend's actual context window, read from the GGUF metadata.
- Constrained decoding — the sampler consults a JSON state machine before committing each token, so the model cannot emit a preamble, an unbalanced brace, or a truncated string. Output that hits the token cap is repaired rather than discarded.
- Anchoring — findings pointing at a file not in the diff are dropped; off-by-N line numbers snap to the nearest changed line.
- Suppression — inline directives, the baseline, and
--min-confidenceare applied, then results are deduplicated and sorted.
diffmind/
├── action.yml # GitHub Action
├── .pre-commit-hooks.yaml # pre-commit framework integration
├── install.sh # one-line installer (checksum-verified)
├── packages/core-engine/src/
│ ├── analyzer.rs # orchestration: chunking, triage, cache, anchoring
│ ├── backend/ # candle (local GGUF) and remote (Ollama / OpenAI)
│ ├── detectors.rs # deterministic rules
│ ├── diff.rs # unified-diff parser, chunking, finding anchoring
│ ├── json_guard.rs # constrained-decoding state machine
│ ├── prompt.rs # prompt construction
│ ├── sarif.rs # SARIF 2.1.0 output
│ └── suppression.rs # inline directives and baselines
└── apps/tui-cli/src/
├── main.rs # entry point and review pipeline
├── daemon.rs # diffmind serve
├── settings.rs # CLI + config resolution
├── output.rs # text / JSON / SARIF / Markdown rendering
├── indexer.rs # symbol indexer
└── tui.rs # interactive browser
- Auto-fix patches (
diffmind fix) — gated on remote backends landing, since a 1.5B's patches are not trustworthy enough to apply - Cross-file impact analysis — find callers of deleted or renamed functions
-
diffmind stats— findings over time - VS Code / JetBrains extensions, talking to the daemon
- Homebrew tap and scoop manifest
- Fine-tuned review model
Issues and pull requests welcome at github.com/thinkgrid-labs/diffmind.
cargo test --workspace --all-targets
cargo clippy --workspace --all-targets -- -D warnings
cargo fmt --allMIT — see LICENSE.
Diffmind — AI-powered local code review. Private by design. Free forever.
Built with ❤️ by Tech Lead, for Tech Leads.