Classify PR diffs by reviewer attention: review, skim, or skip
Project description
garbelour
Classify every hunk in a git diff (or a GitHub pull request) by how much reviewer attention it deserves: review, skim, or skip.
What it does
Garbelour walks the diff between two git refs, runs each hunk through a pipeline of deterministic classifiers, and (optionally) sends whatever the heuristics didn't classify to an LLM for enrichment. The output is a single report in one of three formats:
- human (terminal): grouped by level, color-coded, with file:line pointers to the lines that matter.
- markdown (GitHub sticky comment): collapsible Skim/Skip sections and deep links to the exact lines of each Review item.
- json (machine-readable): one record per classified hunk plus a summary; suitable for piping into other tools.
The format defaults to auto: markdown when posting to GitHub, human in
a TTY, json otherwise.
Built-in classifiers
| Classifier | Level | What it catches |
|---|---|---|
generated |
Skip | Files matching globs (*.lock, dist/**, *.min.js, …) or .gitattributes linguist-generated |
lockfile |
Skip | Cargo.lock, package-lock.json, yarn.lock, pnpm-lock.yaml, go.sum, … |
comment_only |
Skip | Hunks where every changed line is inside a comment / docstring node |
import_reorder |
Skip | Same set of imports, different order |
public_api |
Review | pub items in Rust, export in TS/JS, module-level def/class in Python |
control_flow |
Review | Added / removed / modified if, match/switch, for, while, loop, return |
error_handling |
Review | Removed ?, try/except, try/catch, except clauses |
size_threshold |
Review | Hunks > 150 changed lines (configurable) |
AST-based classifiers use tree-sitter for Rust, Python, TypeScript, and JavaScript. Files in unsupported languages still go through the path-based classifiers (generated, lockfile, size).
Install
From source:
cargo install --path .
The binary is garbelour.
Usage
Locally, against any two refs
garbelour review --base main
Pretty terminal output by default. Pipe to jq to get JSON instead:
garbelour review --base main | jq '.summary'
Force a specific format:
garbelour review --base main --format markdown
garbelour review --base main --format json
Force color on through a pager:
garbelour review --base main --color always | less -R
In a GitHub Action
Garbelour reads the PR event payload from GITHUB_EVENT_PATH and posts a
single sticky comment, updating it on each push.
# .github/workflows/garbelour.yml
on:
pull_request:
types: [opened, synchronize, reopened]
permissions:
contents: read
pull-requests: write
jobs:
review-map:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: cargo install garbelour
- run: garbelour review --post-comment --llm
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
fetch-depth: 0 matters — git diff needs both base and head refs in
the local repo.
LLM triage (optional)
--llm sends hunks no heuristic claimed to an LLM, which assigns each a
level (review/skim/skip), a focus line range, and a one-line rationale.
The provider is auto-detected from whichever API-key env var is present:
| Env var | Provider | Default model |
|---|---|---|
ANTHROPIC_API_KEY |
Anthropic | claude-haiku-4-5 |
OPENAI_API_KEY |
OpenAI | gpt-4o-mini |
OLLAMA_API_KEY |
Ollama | llama3.2 |
Override with --llm-provider, --llm-model, or --llm-base-url.
Without --llm, hunks no heuristic claimed default to review — the
safe option, since the tool hasn't actually evaluated them.
Configuration
garbelour.toml at the repository root is loaded automatically. All
fields are optional.
[classify]
generated_globs = ["generated/**", "*.auto.ts"] # merged with defaults
lockfile_names = ["shrinkwrap.json"] # merged with defaults
size_threshold = 150
[llm]
provider = "anthropic"
model = "claude-haiku-4-5"
[github]
base_url = "https://github.example.com/api/v3" # GitHub Enterprise
CLI flags override config; config overrides built-in defaults.
.gitattributes is also consulted: any path marked linguist-generated
(or linguist-generated=true) is treated as generated. Glob patterns in
.gitattributes are not interpreted — fall back to
[classify].generated_globs for those.
Output examples
Human:
garbelour: 3 of 47 hunks need review, 5 worth skimming, 39 mechanical
Review (3)
src/engine.rs:145–148 public fn signature changed
src/engine.rs:201–208 new branch in retry loop
src/store.rs:88–95 (old) removed try/except block
Skim (5)
…
Skip (39)
generated (15) proto/*.pb.go, dist/bundle.js, ...
lockfile (1) Cargo.lock
comment-only (3) src/engine.rs, src/store.rs, README.md
import-reorder (8) src/engine.rs, src/store.rs, ...
test-fixture (12) tests/fixtures/*.snap
JSON:
{
"base_sha": "...",
"head_sha": "...",
"hunks": [
{
"hunk_id": "src/engine.rs:142",
"file": "src/engine.rs",
"line": 142,
"level": "review",
"category": "public_api_change",
"rationale": "public fn signature changed at lines 145–148",
"focus_lines": { "start": 145, "end": 148, "side": "new" },
"source": { "heuristic": { "name": "public_api" } }
}
],
"summary": { "total": 47, "review": 3, "skim": 5, "skip": 39 }
}
Library use
Garbelour is dual-target: a garbelour binary and a garbelour library
crate. The pipeline, classifiers, and renderers are public API.
use garbelour::{diff, classify::{Pipeline, PipelineConfig}};
let mut d = diff::extract(std::path::Path::new("."), "main", "HEAD")?;
let pipeline = Pipeline::standard(&PipelineConfig::default())?;
let (classified, unclassified) = pipeline.run(&mut d);
Design notes
- One subprocess to
git diff --raw -z -M -Cfor file statuses (rename detection), one togit diff -U3 -M -Cfor hunks (parsed via thepatchcrate). File content for AST classifiers is loaded lazily viagit show. - Heuristics emit only
SkiporReview.Skimrequires positive evidence — currently only the LLM emits it. focus_linespinpoints the specific line range that triggered each classification, so the markdown deep link lands on the right line, not just the start of the hunk.
What is New in Garbelour
0.1.0
Initial release.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file garbelour-0.2.0.tar.gz.
File metadata
- Download URL: garbelour-0.2.0.tar.gz
- Upload date:
- Size: 21.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
273ea16e2614231912742789964fd0333e991e552561b256f0f8cd78cff3c6b3
|
|
| MD5 |
557f8b5043dfa53e5d0cbb8e62f0a837
|
|
| BLAKE2b-256 |
e02c601da7a6b585cc0f10795cc5c78e20671bcdf64bde165f68b33cbee3eb38
|
File details
Details for the file garbelour-0.2.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: garbelour-0.2.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 3.2 MB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ce7cfbd883824c6ff17ad16d02496feecca2aa36eb66f1c5019a1832f2206f02
|
|
| MD5 |
5cb2cdc1fe9d181bf74204e56e6ca8ac
|
|
| BLAKE2b-256 |
5b050c21b5de8b9f5c6bd923e763a95c6b87fa456b5cad18389414caf3e8c400
|
File details
Details for the file garbelour-0.2.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl.
File metadata
- Download URL: garbelour-0.2.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl
- Upload date:
- Size: 3.2 MB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ s390x
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
adca60aacf85a7823c95cbc243ac683a8f1486f31960a96bb7d85b58e69a503e
|
|
| MD5 |
d7a1708349b4e20953fdc9d3e1998eba
|
|
| BLAKE2b-256 |
f6b1ab5e4c4df6e9224f5c765569cc7276b2da9c2690f0518d4ef9daff880462
|
File details
Details for the file garbelour-0.2.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl.
File metadata
- Download URL: garbelour-0.2.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl
- Upload date:
- Size: 3.6 MB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ ppc64le
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
174567bea5690b8aafa5e0839bddb6c62b5df2c2c4f853b672dd9152ac59b9bf
|
|
| MD5 |
ddc942072035f779c03d06a07fb41397
|
|
| BLAKE2b-256 |
f3fddeec6396c2f119cdbb3744b5892f177a38b8f77d5f00dd20c6c43bef1ed9
|
File details
Details for the file garbelour-0.2.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl.
File metadata
- Download URL: garbelour-0.2.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl
- Upload date:
- Size: 3.2 MB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ i686
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b3e45ac32779096e27b486ca159f1b0d5714d84eb6ecc51271e419ad0a7224e6
|
|
| MD5 |
14a89e485a52139abb61b06b0af1e1cf
|
|
| BLAKE2b-256 |
76deb638ec0aac7b4926cd6045476ad82c065000b9131aac5124f1bcb6e4415c
|
File details
Details for the file garbelour-0.2.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl.
File metadata
- Download URL: garbelour-0.2.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl
- Upload date:
- Size: 3.0 MB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ ARMv7l
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
98556a2f6c6853fc9a5097963b3b12c1c2f40634e89ac12d5a30a5b0557b1c51
|
|
| MD5 |
4287f036212dba9a6ba4ed03a30878dd
|
|
| BLAKE2b-256 |
ebf495a2a1e32253c8e86868c40b2f7a24d33b6ccc2d9f9236a3d4210189dfd4
|
File details
Details for the file garbelour-0.2.0-cp39-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: garbelour-0.2.0-cp39-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 2.9 MB
- Tags: CPython 3.9+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d3e5275e7159f02391e6f05285261f8877a2d024e54cca9e8e072a1e45b5d0bc
|
|
| MD5 |
57c3cfd6afa854696cc3492a0f2d13bb
|
|
| BLAKE2b-256 |
cf31fb9e1f77c5149fd6539b3ada975cf4bb476423d1d4835f3df034525f6f85
|
File details
Details for the file garbelour-0.2.0-cp39-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: garbelour-0.2.0-cp39-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 3.0 MB
- Tags: CPython 3.9+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6e2b96a05f2d4db395755d21a47668da67439d7c9f2389b554834adb1b451fca
|
|
| MD5 |
7a88d6cfa1079342ea4eccc5447f0f63
|
|
| BLAKE2b-256 |
7a282d4a2bd5daac6faec671b1bb13ba928a1a814f4a3550cdd5b03ceae04266
|