Personal and opinionated Python linter rules aimed at coding agents.
Project description
garuff
Personal, opinionated Python linter rules aimed at coding agents.
garuff enforces a curated set of conventions that ruff and ty can't express — and it explains every one of them in terms a coding assistant can act on.
⚠️ Early days. The core linter runs —
garuff checkandgaruff rulework today — but the ruleset is still small and there's no packaged release yet. This README describes the vision; the full rule reference and install instructions will land as the implementation does.
Why
Coding agents write a lot of code, fast. Keeping that code consistent means
enforcing conventions — but enforcement alone isn't enough for an agent. A
terse remove this tells a human who already knows the house style exactly what
to do; an agent needs to know why the convention exists, or it will "fix" the
symptom and reintroduce the cause a moment later.
ruff and ty are indispensable, and garuff is not a replacement for either. It fills the gap they leave:
- Conventions they can't express. Repo-specific taste — "return
Self, never a stringized forward reference to the enclosing class", "every function carries a docstring", "keep agent-facing docs like ADRs consistently numbered." - Messages written for agents. Every rule ships a rationale (why the convention exists) and a prescribed fix (the correct form), not just a one-line complaint.
What makes it different
- Agent-first by design. Each violation carries a terse summary to locate it, a rationale to justify it, and a fix to resolve it. Output stays scannable: the reasoning for each triggered rule is shown once, not repeated on every hit.
- Opinionated, not configurable-to-taste. garuff is a point of view, not a style engine. The rules encode one set of preferences; you don't tune them into something else — you take them or turn them off.
- Enforcement you control. Every rule is on by default. You can ignore individual rules, silence them per file, or suppress a single line inline — while the ruleset itself stays fixed.
- Zero dependencies. garuff is a tool you install into every project's dev and CI environment, so it leans on nothing but the standard library and drops in without dependency friction.
Using it
garuff check [paths] lints (no paths → the whole project root). Each violation
is a terse locator line; once all findings are listed, an appendix explains
each rule that fired — once, no matter how many times it tripped:
$ garuff check src
src/config.py:1:1: GAC001 no `from __future__ import annotations`
src/build.py:9:1: GAC008 `build` takes 3 positional parameters (at most 1)
GAC001 no `from __future__ import annotations`
why Python 3.14 evaluates annotations lazily (PEP 649), so the import is
dead weight — it buys nothing and every module has to carry it.
fix Delete the import:
- from __future__ import annotations
GAC008 keep positional parameters to at most 1
why Positional parameters past the first make a call site ambiguous — the
reader has to count arguments and match them against the signature to
know what each one means.
fix The limit is 1; move every parameter past it behind
a bare `*` so callers must name them:
def build(name, kind, size): ... # before
def build(name, *, kind, size): ... # after
...
Findings go to stdout, one per line at column 0; the appendix is indented
beneath them, so garuff check | grep '^[^ ]' filters the findings alone.
garuff rule <CODE> prints that same explanation on demand — reading the
project's configuration, so a tuned option (say a raised max-positional-args)
shows up in the text — and garuff rule --all prints the whole ruleset. A rule
you've turned off still explains itself, and says that it's ignored.
Two kinds of rules
- Code rules — how you write Python and its prose: types, docstrings, naming, model and dataclass conventions.
- Agent-file rules — how a repository's agent-facing scaffolding is structured: ADRs today, more of the agent surface over time.
Status & direction
The domain model, the key architectural decisions, and the build plan are written down:
CONTEXT.md— the project's glossary.docs/adr/— the decisions and why they were made.docs/structure-plan.md— how the tool is put together.
Implementation is tracked as a sequence of end-to-end issues. This page will grow into proper documentation as those land.
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 Distribution
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 garuff-2026.7.0.tar.gz.
File metadata
- Download URL: garuff-2026.7.0.tar.gz
- Upload date:
- Size: 32.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9e7639ee24a834c9ff131bc393239e126350891c2b6503cf77d988abec9104f7
|
|
| MD5 |
88d49e7c4b01d4c12405bf540c86b706
|
|
| BLAKE2b-256 |
23c11ed64967c3d3b81bb6671afd5ec4f7b13a457723a6df0d3159acd4c1b793
|
Provenance
The following attestation bundles were made for garuff-2026.7.0.tar.gz:
Publisher:
release.yml on gahjelle/garuff
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
garuff-2026.7.0.tar.gz -
Subject digest:
9e7639ee24a834c9ff131bc393239e126350891c2b6503cf77d988abec9104f7 - Sigstore transparency entry: 2191700281
- Sigstore integration time:
-
Permalink:
gahjelle/garuff@d40429fa7f99de0fa0578b74623965395d9892a6 -
Branch / Tag:
refs/tags/v2026.7.0 - Owner: https://github.com/gahjelle
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d40429fa7f99de0fa0578b74623965395d9892a6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file garuff-2026.7.0-py3-none-any.whl.
File metadata
- Download URL: garuff-2026.7.0-py3-none-any.whl
- Upload date:
- Size: 46.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
188efb978875b2f6df1b55341d8ecf10138409daa4d73435e4592f2e3380de17
|
|
| MD5 |
0c8b8d5e33fba977bf06123013ce6fc8
|
|
| BLAKE2b-256 |
c4927d7b702f0c5a50d08c7ed2b773c8396e3f55f060d25ba8adc8a62a21fa4b
|
Provenance
The following attestation bundles were made for garuff-2026.7.0-py3-none-any.whl:
Publisher:
release.yml on gahjelle/garuff
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
garuff-2026.7.0-py3-none-any.whl -
Subject digest:
188efb978875b2f6df1b55341d8ecf10138409daa4d73435e4592f2e3380de17 - Sigstore transparency entry: 2191700291
- Sigstore integration time:
-
Permalink:
gahjelle/garuff@d40429fa7f99de0fa0578b74623965395d9892a6 -
Branch / Tag:
refs/tags/v2026.7.0 - Owner: https://github.com/gahjelle
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d40429fa7f99de0fa0578b74623965395d9892a6 -
Trigger Event:
push
-
Statement type: