Anti-slop linter for AI-assisted codebases
Project description
grain
Anti-slop linter for AI-assisted codebases. Detects AI-generated code and documentation patterns before they land in version control.
What it does
AI code has tells. grain flags them so a human can decide whether to keep, rewrite, or suppress. It does not auto-fix -- fixing requires judgment.
Quick start
pip install grain-lint # from PyPI
pip install -e . # from source
Usage
grain check [files...] # check specific files
grain check --all # check entire repo
grain install # install git hooks into .git/hooks/
grain status # show current config and enabled checks
grain suppress FILE:LINE RULE # add inline suppression comment
Checks
Python
| Rule | Severity | Description |
|---|---|---|
| OBVIOUS_COMMENT | error | comment restates the following line |
| NAKED_EXCEPT | error | broad except clause with no re-raise |
| RESTATED_DOCSTRING | warn | docstring just expands the function/class name |
| VAGUE_TODO | error | TODO without specific approach or reason |
| SINGLE_IMPL_ABC | warn | ABC with exactly one concrete implementation |
| GENERIC_VARNAME | error | function named with AI filler (process_data, etc.) |
Markdown
| Rule | Severity | Description |
|---|---|---|
| HEDGE_WORD | error | AI filler words -- see hedge_words in config |
| THANKS_OPENER | error | README/CONTRIBUTING opens with "Thanks for contributing" |
| OBVIOUS_HEADER | warn | header content fully restated in following paragraph |
| BULLET_PROSE | warn | short bullet list that reads better as a sentence |
| TABLE_OVERKILL | warn | table with 1 row or constant column |
Commit messages
| Rule | Severity | Description |
|---|---|---|
| VAGUE_COMMIT | error | subject too generic (update, fix bug, wip...) |
| AND_COMMIT | error | subject contains "and" -- do one thing per commit |
| NO_CONTEXT | error | fix/feat with no description of what changed |
Config
Create .grain.toml in your repo root:
[grain]
fail_on = ["OBVIOUS_COMMENT", "NAKED_EXCEPT", "HEDGE_WORD", "VAGUE_TODO", "VAGUE_COMMIT"]
warn_only = ["RESTATED_DOCSTRING", "SINGLE_IMPL_ABC", "BULLET_PROSE"]
ignore = []
[grain.python]
generic_varnames = ["process_data", "handle_response", "get_result", "do_thing"]
[grain.markdown]
hedge_words = ["robust", "seamless", "leverage", "cutting-edge", "powerful",
"you might want to", "consider using", "it's worth noting", "note that"]
Suppression
Add # grain: ignore RULE_NAME to the offending line:
except Exception as e: # grain: ignore NAKED_EXCEPT
pass # intentional -- this is a top-level catch
Or use the CLI:
grain suppress src/main.py:42 NAKED_EXCEPT
pre-commit framework
# .pre-commit-config.yaml
repos:
- repo: https://github.com/mmartoccia/grain
rev: v0.1.1
hooks:
- id: grain
Output format
path/to/file.py:42 [FAIL] OBVIOUS_COMMENT "# return result" restates the following line
path/to/README.md:7 [FAIL] HEDGE_WORD "robust" signals AI-generated prose
Exit 0 = clean. Exit 1 = errors found (pre-commit blocks the commit).
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
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 grain_lint-0.1.2.tar.gz.
File metadata
- Download URL: grain_lint-0.1.2.tar.gz
- Upload date:
- Size: 19.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b74839ab65c47349480517dd7ca31e39757af2a83b919a81bffad972294e0e71
|
|
| MD5 |
aab92a4902ae697fce7be74579232685
|
|
| BLAKE2b-256 |
26d1d58a675874ab0a17e57b1305519b359d3468abf710297c1dc06308d570e1
|
File details
Details for the file grain_lint-0.1.2-py3-none-any.whl.
File metadata
- Download URL: grain_lint-0.1.2-py3-none-any.whl
- Upload date:
- Size: 18.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
802a94876ea771928d2f99287eede7a0b6fb9fd8882603231f2854fb2b0880f1
|
|
| MD5 |
d4a465c40d19f51011b30bb0fe4e6b44
|
|
| BLAKE2b-256 |
d906a9c120a04630f2783fdd1a21609b7c4bfcb780ed05430cdcc0d59a8a135b
|