slopcheck-lint 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- slopcheck_lint-0.1.0/.agents/skills/slopcheck/SKILL.md +1 -0
- slopcheck_lint-0.1.0/.claude/skills/slopcheck/SKILL.md +1 -0
- slopcheck_lint-0.1.0/.cursor/skills/slopcheck/SKILL.md +1 -0
- slopcheck_lint-0.1.0/.github/ISSUE_TEMPLATE/bug.yml +34 -0
- slopcheck_lint-0.1.0/.github/ISSUE_TEMPLATE/config.yml +1 -0
- slopcheck_lint-0.1.0/.github/ISSUE_TEMPLATE/rule.yml +29 -0
- slopcheck_lint-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +9 -0
- slopcheck_lint-0.1.0/.github/workflows/ci.yml +24 -0
- slopcheck_lint-0.1.0/.github/workflows/release.yml +44 -0
- slopcheck_lint-0.1.0/.gitignore +18 -0
- slopcheck_lint-0.1.0/.pre-commit-hooks.yaml +9 -0
- slopcheck_lint-0.1.0/CHANGELOG.md +12 -0
- slopcheck_lint-0.1.0/CONTRIBUTING.md +48 -0
- slopcheck_lint-0.1.0/LICENSE +21 -0
- slopcheck_lint-0.1.0/PKG-INFO +250 -0
- slopcheck_lint-0.1.0/README.md +218 -0
- slopcheck_lint-0.1.0/README.zh-CN.md +218 -0
- slopcheck_lint-0.1.0/action.yml +73 -0
- slopcheck_lint-0.1.0/demo/taskflow/__init__.py +1 -0
- slopcheck_lint-0.1.0/demo/taskflow/client.py +53 -0
- slopcheck_lint-0.1.0/demo/taskflow/imports_hallucinated.py +9 -0
- slopcheck_lint-0.1.0/demo/taskflow/ok.py +41 -0
- slopcheck_lint-0.1.0/demo/taskflow/utils.py +87 -0
- slopcheck_lint-0.1.0/docs/before-after.png +0 -0
- slopcheck_lint-0.1.0/docs/demo-report.html +181 -0
- slopcheck_lint-0.1.0/docs/demo.gif +0 -0
- slopcheck_lint-0.1.0/docs/demo.svg +281 -0
- slopcheck_lint-0.1.0/docs/demo.txt +65 -0
- slopcheck_lint-0.1.0/docs/hero-dark.png +0 -0
- slopcheck_lint-0.1.0/docs/hero-light.png +0 -0
- slopcheck_lint-0.1.0/docs/report.png +0 -0
- slopcheck_lint-0.1.0/docs/slop-score.svg +12 -0
- slopcheck_lint-0.1.0/docs/social-preview.png +0 -0
- slopcheck_lint-0.1.0/examples/hooks/claude-code.settings.json +16 -0
- slopcheck_lint-0.1.0/examples/hooks/slopcheck.sh +35 -0
- slopcheck_lint-0.1.0/examples/pre-commit-config.yaml +5 -0
- slopcheck_lint-0.1.0/examples/workflows/slopcheck.yml +19 -0
- slopcheck_lint-0.1.0/pyproject.toml +76 -0
- slopcheck_lint-0.1.0/scripts/render_assets.py +114 -0
- slopcheck_lint-0.1.0/scripts/render_launch.py +682 -0
- slopcheck_lint-0.1.0/skills/slopcheck/SKILL.md +78 -0
- slopcheck_lint-0.1.0/src/slopcheck/__init__.py +7 -0
- slopcheck_lint-0.1.0/src/slopcheck/__main__.py +6 -0
- slopcheck_lint-0.1.0/src/slopcheck/cli.py +306 -0
- slopcheck_lint-0.1.0/src/slopcheck/config.py +189 -0
- slopcheck_lint-0.1.0/src/slopcheck/discover.py +172 -0
- slopcheck_lint-0.1.0/src/slopcheck/engine.py +188 -0
- slopcheck_lint-0.1.0/src/slopcheck/formatters/__init__.py +9 -0
- slopcheck_lint-0.1.0/src/slopcheck/formatters/badge.py +47 -0
- slopcheck_lint-0.1.0/src/slopcheck/formatters/html.py +211 -0
- slopcheck_lint-0.1.0/src/slopcheck/formatters/jsonfmt.py +62 -0
- slopcheck_lint-0.1.0/src/slopcheck/formatters/prompt.py +60 -0
- slopcheck_lint-0.1.0/src/slopcheck/formatters/sarif.py +83 -0
- slopcheck_lint-0.1.0/src/slopcheck/formatters/text.py +135 -0
- slopcheck_lint-0.1.0/src/slopcheck/globs.py +42 -0
- slopcheck_lint-0.1.0/src/slopcheck/ignore.py +113 -0
- slopcheck_lint-0.1.0/src/slopcheck/models.py +60 -0
- slopcheck_lint-0.1.0/src/slopcheck/parse.py +82 -0
- slopcheck_lint-0.1.0/src/slopcheck/resolver.py +134 -0
- slopcheck_lint-0.1.0/src/slopcheck/rules/__init__.py +43 -0
- slopcheck_lint-0.1.0/src/slopcheck/rules/comments.py +228 -0
- slopcheck_lint-0.1.0/src/slopcheck/rules/defensive.py +282 -0
- slopcheck_lint-0.1.0/src/slopcheck/rules/exceptions.py +262 -0
- slopcheck_lint-0.1.0/src/slopcheck/rules/hygiene.py +514 -0
- slopcheck_lint-0.1.0/src/slopcheck/rules/imports.py +135 -0
- slopcheck_lint-0.1.0/src/slopcheck/rules/support.py +244 -0
- slopcheck_lint-0.1.0/src/slopcheck/score.py +55 -0
- slopcheck_lint-0.1.0/src/slopcheck/version.py +3 -0
- slopcheck_lint-0.1.0/tests/__init__.py +1 -0
- slopcheck_lint-0.1.0/tests/conftest.py +20 -0
- slopcheck_lint-0.1.0/tests/fixtures/clean/ok.py +41 -0
- slopcheck_lint-0.1.0/tests/fixtures/ignored/bare_ok.py +5 -0
- slopcheck_lint-0.1.0/tests/fixtures/sloppy/SLP001_bare_except.py +5 -0
- slopcheck_lint-0.1.0/tests/fixtures/sloppy/SLP002_broad_except.py +5 -0
- slopcheck_lint-0.1.0/tests/fixtures/sloppy/SLP003_fake_fallback.py +5 -0
- slopcheck_lint-0.1.0/tests/fixtures/sloppy/SLP004_narrating_comment.py +4 -0
- slopcheck_lint-0.1.0/tests/fixtures/sloppy/SLP005_debug_print.py +5 -0
- slopcheck_lint-0.1.0/tests/fixtures/sloppy/SLP006_unused_private.py +6 -0
- slopcheck_lint-0.1.0/tests/fixtures/sloppy/SLP007_near_duplicate.py +38 -0
- slopcheck_lint-0.1.0/tests/fixtures/sloppy/SLP008_over_defensive.py +5 -0
- slopcheck_lint-0.1.0/tests/fixtures/sloppy/SLP009_placeholder.py +3 -0
- slopcheck_lint-0.1.0/tests/fixtures/sloppy/SLP010_hallucinated_import.py +3 -0
- slopcheck_lint-0.1.0/tests/fixtures/sloppy/SLP011_try_nesting.py +11 -0
- slopcheck_lint-0.1.0/tests/fixtures/sloppy/SLP012_verbose_docstring.py +13 -0
- slopcheck_lint-0.1.0/tests/fixtures/sloppy/SLP013_versioned_name.py +2 -0
- slopcheck_lint-0.1.0/tests/fixtures/sloppy/SLP014_commented_code.py +4 -0
- slopcheck_lint-0.1.0/tests/fixtures/sloppy/SLP015_error_string.py +5 -0
- slopcheck_lint-0.1.0/tests/test_cli.py +109 -0
- slopcheck_lint-0.1.0/tests/test_config.py +73 -0
- slopcheck_lint-0.1.0/tests/test_demo.py +18 -0
- slopcheck_lint-0.1.0/tests/test_discover.py +51 -0
- slopcheck_lint-0.1.0/tests/test_formatters.py +56 -0
- slopcheck_lint-0.1.0/tests/test_ignore.py +80 -0
- slopcheck_lint-0.1.0/tests/test_readme.py +87 -0
- slopcheck_lint-0.1.0/tests/test_rules.py +236 -0
- slopcheck_lint-0.1.0/tests/test_score.py +48 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
../../../skills/slopcheck/SKILL.md
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
../../../skills/slopcheck/SKILL.md
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
../../../skills/slopcheck/SKILL.md
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: Bug
|
|
2
|
+
description: A rule misfired, a score looks wrong, or a command failed
|
|
3
|
+
title: "SLPxxx: "
|
|
4
|
+
labels: ["bug"]
|
|
5
|
+
body:
|
|
6
|
+
- type: textarea
|
|
7
|
+
id: what
|
|
8
|
+
attributes:
|
|
9
|
+
label: What happened
|
|
10
|
+
description: Include the rule id, the file, and the score if you have one.
|
|
11
|
+
validations:
|
|
12
|
+
required: true
|
|
13
|
+
- type: textarea
|
|
14
|
+
id: code
|
|
15
|
+
attributes:
|
|
16
|
+
label: Smallest Python snippet
|
|
17
|
+
description: Ten lines that reproduce it. Strip secrets.
|
|
18
|
+
render: python
|
|
19
|
+
validations:
|
|
20
|
+
required: true
|
|
21
|
+
- type: input
|
|
22
|
+
id: version
|
|
23
|
+
attributes:
|
|
24
|
+
label: slopcheck version
|
|
25
|
+
placeholder: "0.1.0"
|
|
26
|
+
validations:
|
|
27
|
+
required: true
|
|
28
|
+
- type: input
|
|
29
|
+
id: python
|
|
30
|
+
attributes:
|
|
31
|
+
label: Python version
|
|
32
|
+
placeholder: "3.12"
|
|
33
|
+
validations:
|
|
34
|
+
required: true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
blank_issues_enabled: false
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Rule idea
|
|
2
|
+
description: A pattern agents keep leaving that slopcheck should flag
|
|
3
|
+
title: "Rule: "
|
|
4
|
+
labels: ["rule"]
|
|
5
|
+
body:
|
|
6
|
+
- type: textarea
|
|
7
|
+
id: pattern
|
|
8
|
+
attributes:
|
|
9
|
+
label: The pattern
|
|
10
|
+
description: Show the messy version and the version you want to keep.
|
|
11
|
+
render: python
|
|
12
|
+
validations:
|
|
13
|
+
required: true
|
|
14
|
+
- type: textarea
|
|
15
|
+
id: why
|
|
16
|
+
attributes:
|
|
17
|
+
label: Why Ruff or Pylint does not already cover it
|
|
18
|
+
validations:
|
|
19
|
+
required: true
|
|
20
|
+
- type: dropdown
|
|
21
|
+
id: severity
|
|
22
|
+
attributes:
|
|
23
|
+
label: Suggested severity
|
|
24
|
+
options:
|
|
25
|
+
- error
|
|
26
|
+
- warning
|
|
27
|
+
- note
|
|
28
|
+
validations:
|
|
29
|
+
required: true
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
strategy:
|
|
11
|
+
fail-fast: false
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: ${{ matrix.python-version }}
|
|
19
|
+
- name: Install
|
|
20
|
+
run: python -m pip install -e ".[dev]"
|
|
21
|
+
- name: Test
|
|
22
|
+
run: pytest
|
|
23
|
+
- name: Dogfood
|
|
24
|
+
run: slopcheck .
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
pypi:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
environment:
|
|
15
|
+
name: pypi
|
|
16
|
+
url: https://pypi.org/p/slopcheck-lint
|
|
17
|
+
permissions:
|
|
18
|
+
id-token: write
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: "3.12"
|
|
24
|
+
- name: Check tag matches package version
|
|
25
|
+
run: |
|
|
26
|
+
python - << 'PY'
|
|
27
|
+
import os
|
|
28
|
+
import pathlib
|
|
29
|
+
import re
|
|
30
|
+
import sys
|
|
31
|
+
|
|
32
|
+
text = pathlib.Path("src/slopcheck/version.py").read_text(encoding="utf-8")
|
|
33
|
+
match = re.search(r'__version__ = "([^"]+)"', text)
|
|
34
|
+
version = match.group(1) if match else ""
|
|
35
|
+
tag = os.environ["GITHUB_REF_NAME"].removeprefix("v")
|
|
36
|
+
if version != tag:
|
|
37
|
+
sys.exit(f"tag {tag} does not match package {version}")
|
|
38
|
+
PY
|
|
39
|
+
- name: Build
|
|
40
|
+
run: |
|
|
41
|
+
python -m pip install build
|
|
42
|
+
python -m build
|
|
43
|
+
- name: Publish
|
|
44
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 — 2026-10-26
|
|
4
|
+
|
|
5
|
+
First release.
|
|
6
|
+
|
|
7
|
+
- Published on PyPI as `slopcheck-lint`, because `slopcheck` on PyPI belongs to another project. The CLI command and the import package are `slopcheck`: `pip install slopcheck-lint`, then run `slopcheck`.
|
|
8
|
+
- Fifteen rules, `SLP001` through `SLP015`, and a Slop Score from 0 to 100.
|
|
9
|
+
- Terminal, JSON, SARIF, HTML, SVG badge, and an agent fix prompt.
|
|
10
|
+
- Config in `[tool.slopcheck]`, inline `# slopcheck: ignore`.
|
|
11
|
+
- pre-commit hook, GitHub Action, and a skill for Claude Code, Codex, and Cursor.
|
|
12
|
+
- Python 3.10 through 3.13.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Issues and pull requests go to [yungcs/slopcheck](https://github.com/yungcs/slopcheck).
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install -e ".[dev]"
|
|
9
|
+
pytest
|
|
10
|
+
slopcheck .
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`slopcheck .` has to score 100. The demo and the test fixtures are excluded on purpose. Scan them by passing the path: `slopcheck demo`.
|
|
14
|
+
|
|
15
|
+
Python 3.10 through 3.13. CI runs that matrix.
|
|
16
|
+
|
|
17
|
+
## A new rule
|
|
18
|
+
|
|
19
|
+
1. Add `SLP0xx` in `src/slopcheck/rules/`, and register it in `src/slopcheck/rules/__init__.py`.
|
|
20
|
+
2. Add `tests/fixtures/sloppy/SLP0xx_*.py` that the rule flags, and a case in `tests/test_rules.py` for something it must keep.
|
|
21
|
+
3. Put the id in both `README.md` and `README.zh-CN.md`.
|
|
22
|
+
4. Keep the tool's own tree clean. Do not silence a rule to hide a hit in `src/`.
|
|
23
|
+
|
|
24
|
+
Rule ids are stable. Do not renumber one that has shipped.
|
|
25
|
+
|
|
26
|
+
## Pictures
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
python scripts/render_assets.py # SVG terminal, badge, HTML sample
|
|
30
|
+
python scripts/render_launch.py # hero, GIF, report shot, before/after, social card
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`render_launch.py` needs Pillow (installed with `.[dev]`) and ffmpeg. The GIF is built from a real `slopcheck demo` run (score 29) and a fixed tree tuned to score 94. Headless Chrome takes `docs/report.png`. If Chrome stays open after writing the PNG, the script stops it.
|
|
34
|
+
|
|
35
|
+
`docs/social-preview.png` is 1280×640 for the GitHub social preview setting. It is not embedded in the README.
|
|
36
|
+
|
|
37
|
+
## Releasing
|
|
38
|
+
|
|
39
|
+
Tag `vX.Y.Z` only when `src/slopcheck/version.py` has the same version. [`.github/workflows/release.yml`](.github/workflows/release.yml) publishes to PyPI with trusted publishing.
|
|
40
|
+
|
|
41
|
+
The PyPI distribution is `slopcheck-lint` (the CLI command and import name stay `slopcheck`). On PyPI, add this GitHub repo as a trusted publisher for the `slopcheck-lint` project:
|
|
42
|
+
|
|
43
|
+
- Owner: `yungcs`
|
|
44
|
+
- Repository: `slopcheck`
|
|
45
|
+
- Workflow: `release.yml`
|
|
46
|
+
- Environment: `pypi`
|
|
47
|
+
|
|
48
|
+
The GitHub environment named `pypi` needs the `id-token: write` permission the workflow already requests.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 slopcheck contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: slopcheck-lint
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A linter for the mess AI coding agents leave in Python.
|
|
5
|
+
Project-URL: Homepage, https://github.com/yungcs/slopcheck
|
|
6
|
+
Project-URL: Repository, https://github.com/yungcs/slopcheck
|
|
7
|
+
Project-URL: Issues, https://github.com/yungcs/slopcheck/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/yungcs/slopcheck/blob/main/CHANGELOG.md
|
|
9
|
+
Author: slopcheck contributors
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: agents,ai,claude,cli,codex,cursor,linter,python,sarif,slop,static-analysis
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Requires-Dist: rich>=13.7
|
|
26
|
+
Requires-Dist: tomli>=2.0.1; python_version < '3.11'
|
|
27
|
+
Requires-Dist: typer>=0.12
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: pillow>=10; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
<p align="center"><strong>English</strong> · <a href="README.zh-CN.md">简体中文</a></p>
|
|
34
|
+
|
|
35
|
+
<p align="center">
|
|
36
|
+
<picture>
|
|
37
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/hero-dark.png">
|
|
38
|
+
<img src="docs/hero-light.png" alt="slopcheck — A linter for the mess AI agents leave in Python" width="820">
|
|
39
|
+
</picture>
|
|
40
|
+
</p>
|
|
41
|
+
|
|
42
|
+
<p align="center">
|
|
43
|
+
<a href="https://pypi.org/project/slopcheck-lint/"><img alt="PyPI" src="https://img.shields.io/pypi/v/slopcheck-lint?label=pypi"></a>
|
|
44
|
+
<a href="https://github.com/yungcs/slopcheck/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/yungcs/slopcheck/actions/workflows/ci.yml/badge.svg"></a>
|
|
45
|
+
<a href="LICENSE"><img alt="MIT license" src="https://img.shields.io/badge/license-MIT-blue"></a>
|
|
46
|
+
<img alt="Python 3.10 through 3.13" src="https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-3776AB">
|
|
47
|
+
<img alt="Slop Score 100" src="docs/slop-score.svg">
|
|
48
|
+
</p>
|
|
49
|
+
|
|
50
|
+
<p align="center"><strong>One command. A score from 0 to 100. Higher is cleaner.</strong></p>
|
|
51
|
+
|
|
52
|
+
<p align="center">
|
|
53
|
+
<img src="docs/demo.gif" alt="uvx --from slopcheck-lint slopcheck demo scores 29, slopcheck prompt, then a fixed tree scores 94" width="720">
|
|
54
|
+
</p>
|
|
55
|
+
|
|
56
|
+
Ruff and Pylint will not catch this. Agents still ship it: a bare `except`, `import requets`, a `_v2` twin, a `breakpoint()` left in the fetch path. slopcheck flags that residue and hands the findings to Claude Code, Codex, or Cursor.
|
|
57
|
+
|
|
58
|
+
## Install in 5 seconds
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
uvx --from slopcheck-lint slopcheck .
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pipx install slopcheck-lint
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pip install slopcheck-lint
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The PyPI package is `slopcheck-lint`; the command it installs is `slopcheck`, and the import name stays `slopcheck`.
|
|
73
|
+
|
|
74
|
+
Python 3.10 or newer. From a checkout of this repo: `pip install -e ".[dev]"`.
|
|
75
|
+
|
|
76
|
+
## Quick start
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
slopcheck . # terminal report and a Slop Score
|
|
80
|
+
slopcheck . --format sarif -o slopcheck.sarif
|
|
81
|
+
slopcheck prompt . > fix.md # paste into Claude Code, Codex, or Cursor
|
|
82
|
+
slopcheck . --fix-with-agent
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The bundled demo is the messy tree in the GIF. It ends at `20 findings · score 29 · fail`. The plain transcript is [`docs/demo.txt`](docs/demo.txt). `demo/taskflow/ok.py`, scanned alone, scores 100.
|
|
86
|
+
|
|
87
|
+
An error weighs 8, a warning 4, a note 2. `budget = lines / 5`, and the score is `round(100 * budget / (budget + raw))`. No findings is 100. A 200-line project with five errors scores 50.
|
|
88
|
+
|
|
89
|
+
| Score | Label |
|
|
90
|
+
| --- | --- |
|
|
91
|
+
| 90–100 | clean |
|
|
92
|
+
| 75–89 | light residue |
|
|
93
|
+
| 50–74 | needs a wash |
|
|
94
|
+
| 25–49 | sloppy |
|
|
95
|
+
| 0–24 | hazardous sludge |
|
|
96
|
+
|
|
97
|
+
Exit `0` when nothing error-or-warning remains and the score clears `--min-score`. Exit `1` for findings, parse errors, or a score under the minimum. Exit `2` for bad arguments. Notes alone do not fail the process unless `min-score` says they do. `--exit-zero` always exits 0.
|
|
98
|
+
|
|
99
|
+
## Why
|
|
100
|
+
|
|
101
|
+
Agents write code that looks finished and fails quietly. Ruff is the fast linter (it will flag a bare `except` as E722). Pylint has its own score. Neither one is looking for the residue around that `except`.
|
|
102
|
+
|
|
103
|
+
| Pattern | Ruff / Pylint | slopcheck |
|
|
104
|
+
| --- | --- | --- |
|
|
105
|
+
| `except KeyError: return {}` | no | SLP003 |
|
|
106
|
+
| `import requets` | no | SLP010 |
|
|
107
|
+
| `summarize_tasks_v2` next to `summarize_tasks` | no | SLP007, SLP013 |
|
|
108
|
+
| `# increment index` above `index += 1` | no | SLP004 |
|
|
109
|
+
| `print(f"{tasks=}")` and `breakpoint()` | not by default | SLP005 |
|
|
110
|
+
|
|
111
|
+
Run both. Ruff formats and finds bugs. slopcheck is the pass after the agent.
|
|
112
|
+
|
|
113
|
+
<p align="center">
|
|
114
|
+
<img src="docs/before-after.png" alt="Before and after for a bare except, a hallucinated import, and a _v2 function" width="820">
|
|
115
|
+
</p>
|
|
116
|
+
|
|
117
|
+
## Rules
|
|
118
|
+
|
|
119
|
+
| ID | Sev | What a tiny example looks like |
|
|
120
|
+
| --- | --- | --- |
|
|
121
|
+
| SLP001 | error | `except:` then `pass` |
|
|
122
|
+
| SLP002 | error | `except Exception: return None` |
|
|
123
|
+
| SLP003 | error | `except KeyError: return {}` |
|
|
124
|
+
| SLP004 | note | `# increment index` |
|
|
125
|
+
| SLP005 | warning | `print(f"{tasks=}")`, `breakpoint()` |
|
|
126
|
+
| SLP006 | warning | `def _helper(): ...` and nothing calls it |
|
|
127
|
+
| SLP007 | warning | a second function with the same shape |
|
|
128
|
+
| SLP008 | note | `name = "ada"` then `if name is not None` |
|
|
129
|
+
| SLP009 | warning | `def export_tasks(): pass` |
|
|
130
|
+
| SLP010 | error | `import requets` |
|
|
131
|
+
| SLP011 | warning | `try` inside `try` inside `try` |
|
|
132
|
+
| SLP012 | note | an essay docstring on `return len(tasks)` |
|
|
133
|
+
| SLP013 | warning | `def summarize_tasks_v2` |
|
|
134
|
+
| SLP014 | note | `# status = "archived"` |
|
|
135
|
+
| SLP015 | warning | `return "Error: failed to fetch title"` |
|
|
136
|
+
|
|
137
|
+
`slopcheck rules` prints the hint that ships with each finding.
|
|
138
|
+
|
|
139
|
+
Left alone on purpose: a comment that says why, `print(f"tasks={tasks}")` (the flagged form is `f"{tasks=}"`), imports inside `except ImportError`, abstract methods, `Protocol`, `.pyi` files, and `return False` from `__exit__`.
|
|
140
|
+
|
|
141
|
+
Silence a line with `# slopcheck: ignore[SLP003]`. A comment-only line applies to the next line of code. `# slopcheck: disable` covers the rest of the file until `# slopcheck: enable`.
|
|
142
|
+
|
|
143
|
+
```toml
|
|
144
|
+
[tool.slopcheck]
|
|
145
|
+
exclude = ["demo/**", "tests/fixtures/**"]
|
|
146
|
+
ignore = ["SLP004"]
|
|
147
|
+
min-score = 90
|
|
148
|
+
|
|
149
|
+
[tool.slopcheck.rules.SLP010]
|
|
150
|
+
allowed_modules = ["pytest"]
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
`--select` replaces the config selection. `--ignore` is added on top. This repo sets `min-score = 100` and excludes the demo, so `slopcheck .` fails on any finding. `slopcheck demo` opts that path back in.
|
|
154
|
+
|
|
155
|
+
<p align="center">
|
|
156
|
+
<img src="docs/report.png" alt="HTML report for the demo, score 29, with filters and the first findings" width="760">
|
|
157
|
+
</p>
|
|
158
|
+
|
|
159
|
+
`slopcheck . --format html -o report.html` writes one file. No network requests. The same run as JSON, SARIF 2.1.0, or an SVG badge: `slopcheck badge . -o slop-score.svg`.
|
|
160
|
+
|
|
161
|
+
## Use with Claude Code, Codex, and Cursor
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
slopcheck prompt . > fix.md
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
The prompt says: keep behavior, raise instead of returning a dummy, do not silence rules to inflate the score, then run slopcheck again.
|
|
168
|
+
|
|
169
|
+
The skill is [`skills/slopcheck/SKILL.md`](skills/slopcheck/SKILL.md). The same file is linked from `.claude/skills`, `.cursor/skills`, and `.agents/skills`.
|
|
170
|
+
|
|
171
|
+
Claude Code can lint after each edit. Copy [`examples/hooks/slopcheck.sh`](examples/hooks/slopcheck.sh) and the matcher in [`examples/hooks/claude-code.settings.json`](examples/hooks/claude-code.settings.json).
|
|
172
|
+
|
|
173
|
+
## CI, pre-commit, GitHub Action
|
|
174
|
+
|
|
175
|
+
pre-commit, once `v0.1.0` is tagged:
|
|
176
|
+
|
|
177
|
+
```yaml
|
|
178
|
+
repos:
|
|
179
|
+
- repo: https://github.com/yungcs/slopcheck
|
|
180
|
+
rev: v0.1.0
|
|
181
|
+
hooks:
|
|
182
|
+
- id: slopcheck
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
GitHub Action, with SARIF upload for code scanning:
|
|
186
|
+
|
|
187
|
+
```yaml
|
|
188
|
+
- uses: yungcs/slopcheck@v0.1.0
|
|
189
|
+
with:
|
|
190
|
+
path: .
|
|
191
|
+
upload-sarif: "true"
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
The action is [`action.yml`](action.yml). A full workflow is [`examples/workflows/slopcheck.yml`](examples/workflows/slopcheck.yml). CI for this repo runs pytest and `slopcheck .` on Python 3.10–3.13.
|
|
195
|
+
|
|
196
|
+
## Comparison
|
|
197
|
+
|
|
198
|
+
| | slopcheck | Ruff | Pylint | eslint-plugin-slop |
|
|
199
|
+
| --- | --- | --- | --- | --- |
|
|
200
|
+
| Language | Python | Python | Python | TypeScript |
|
|
201
|
+
| Fake fallback `return {}` | yes | no | no | no |
|
|
202
|
+
| Hallucinated import | yes | no | no | no |
|
|
203
|
+
| Near-duplicate `_v2` | yes | no | no | no |
|
|
204
|
+
| Narrating comments | yes | no | no | no |
|
|
205
|
+
| Score meant for agents | 0–100 | no | 0–10, different job | no |
|
|
206
|
+
| Fix prompt | yes | no | no | no |
|
|
207
|
+
| SARIF | yes | yes | no | via ESLint |
|
|
208
|
+
| Replaces the others | no | no | no | different language |
|
|
209
|
+
|
|
210
|
+
[eslint-plugin-slop](https://github.com/antfu/eslint-plugin-slop) is the TypeScript cousin. [no-ai-slop](https://github.com/petergyang/no-ai-slop) edits prose. [hallmark](https://github.com/Nutlope/hallmark) lints interface. This one is for Python.
|
|
211
|
+
|
|
212
|
+
## FAQ
|
|
213
|
+
|
|
214
|
+
**Does this replace Ruff?** No. Turn Ruff on. Add slopcheck for the residue Ruff is not built to name.
|
|
215
|
+
|
|
216
|
+
**Will it call my code AI-written?** No. It flags patterns. Humans write `except: pass` too.
|
|
217
|
+
|
|
218
|
+
**A note failed my build.** Notes weigh 2 points. They fail the process only when the score drops under `--min-score`. This repo uses 100, so a note fails `slopcheck .`.
|
|
219
|
+
|
|
220
|
+
**How do I keep a false positive?** `# slopcheck: ignore[SLP010]` on the line, or `allowed_modules` for an extra that is not installed here.
|
|
221
|
+
|
|
222
|
+
**Which Python?** 3.10, 3.11, 3.12, and 3.13.
|
|
223
|
+
|
|
224
|
+
## Roadmap
|
|
225
|
+
|
|
226
|
+
- Safe edits for narrating comments, commented-out code, and debug leftovers.
|
|
227
|
+
- A baseline file, so a dirty tree can ratchet forward.
|
|
228
|
+
- An LSP, so the score shows up while the agent is still typing.
|
|
229
|
+
- Hallucinated keyword arguments.
|
|
230
|
+
- A plugin API for rules that only make sense in one codebase.
|
|
231
|
+
|
|
232
|
+
## Contributing
|
|
233
|
+
|
|
234
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). `pytest` and `slopcheck .` should both be clean before you open a PR. The changelog is [CHANGELOG.md](CHANGELOG.md).
|
|
235
|
+
|
|
236
|
+
## Star history
|
|
237
|
+
|
|
238
|
+
<p align="center">
|
|
239
|
+
<a href="https://star-history.com/#yungcs/slopcheck&Date">
|
|
240
|
+
<picture>
|
|
241
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=yungcs/slopcheck&type=Date&theme=dark">
|
|
242
|
+
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=yungcs/slopcheck&type=Date">
|
|
243
|
+
<img alt="Star history of yungcs/slopcheck" src="https://api.star-history.com/svg?repos=yungcs/slopcheck&type=Date">
|
|
244
|
+
</picture>
|
|
245
|
+
</a>
|
|
246
|
+
</p>
|
|
247
|
+
|
|
248
|
+
## License
|
|
249
|
+
|
|
250
|
+
[MIT](LICENSE)
|