SlopSniff
A lightweight CLI for catching code-quality drift ("slop") before it hardens into team-wide tech debt.
Quick Start
| JavaScript / TypeScript (app repo) | Python (app repo) |
|---|---|
# One-off run
npx slopsniff-cli .
# or install locally
npm i -D slopsniff-cli
{
"scripts": {
"slopsniff": "slopsniff .",
"slopsniff:branch": "slopsniff . --branch",
"slopsniff:strict": "slopsniff . --fail-threshold 0 --format json"
}
}
|
# install in your Python project
uv add --dev slopsniff
# or: pip install slopsniff
# run in your project
slopsniff .
# only files changed vs main (git diff)
slopsniff . --branch
# optional strict mode
slopsniff . --fail-threshold 0 --format json
|
Notes:
- The npm package is a wrapper around the Python CLI.
- It runs
slopsniffviauv tool run --from slopsniff .... - On macOS/Linux, it will attempt to install
uvautomatically if missing. - Commit a
slopsniff.jsonin your project root to configure rules and exclusions.
What it catches
| Pattern | Slop | Better |
|---|---|---|
| Fallback defaults Silent primitives that mask missing config |
timeout = os.getenv("TIMEOUT", 0)
const retries = process.env.RETRIES || 0;
|
timeout = require_env("TIMEOUT")
const retries = requireEnv("RETRIES");
|
| Catch-all primitive returns Flattens every failure into one silent shape |
except Exception:
return []
catch (e) { return null; }
|
except TimeoutError:
logger.warning("upstream timeout")
raise
catch (e) {
if (e instanceof RateLimitError) { ... }
throw e;
}
|
| Exposed secrets Credentials committed in source or docs |
API_KEY = "sk-proj-abc123..."
|
API_KEY = os.environ["API_KEY"]
|
| Large files & functions Monoliths that resist review and testing |
|
|
| Duplicate functions Copy-pasted logic across files |
|
|
| Helper sprawl Vague catch-all files and versioned copies |
|
|
Local Setup (Open Source Dev)
git clone https://github.com/joshuagilley/slopsniff
cd slopsniff
uv sync --dev
pre-commit install
Quick sanity run:
uv run pytest
uv run ruff check .
env PYTHONPATH=src uv run python -m slopsniff.cli . --fail-threshold 30
Notes:
- Pre-commit runs
ruff,ruff-format,slopsniff, andpytest. - Default terminal output is compact and Rich-colored: one summary line (files, issue count, score, status), then findings grouped by directory and file (paths relative to the current working directory when possible). Use
--format jsonfor machine output. - For local runs, prefer
env PYTHONPATH=src uv run python -m slopsniff.cli ....
Basic Usage
# Scan current directory
env PYTHONPATH=src uv run python -m slopsniff.cli .
# Scan a specific path
env PYTHONPATH=src uv run python -m slopsniff.cli ./src
# JSON output for CI/machines
env PYTHONPATH=src uv run python -m slopsniff.cli . --format json
# Override thresholds ad hoc
env PYTHONPATH=src uv run python -m slopsniff.cli . --max-file-lines 300 --max-function-lines 40
# Only files changed vs main (git diff main --name-only --diff-filter=ACMR, then normal include/exclude rules)
env PYTHONPATH=src uv run python -m slopsniff.cli . --branch
env PYTHONPATH=src uv run python -m slopsniff.cli . --changed-since origin/main
--branch is a shortcut for --changed-since main. The scan root must sit inside a git work tree. Untracked files are not included until they are committed or staged in a way that shows up in that diff (same as plain git diff <ref>).
Terminal vs JSON: default output is human-oriented and grouped as above; pass --format json for CI or tooling. Use --verbose to print each finding’s score contribution.
Configuration (slopsniff.json)
SlopSniff auto-loads slopsniff.json from the scan root (the path you pass to slopsniff).
You can tune scoring thresholds, file selection, and enabled rules in one place.
Example:
{
"fail-threshold": 20,
"max-file-lines-warning": 400,
"max-file-lines-high": 800,
"max-function-lines-warning": 50,
"max-function-lines-high": 100,
"verbose": false,
"include-extensions": [".py", ".js", ".ts", ".tsx", ".jsx", ".vue", ".html"],
"large-file-extensions": [".py", ".js", ".ts", ".tsx", ".jsx", ".vue"],
"exclude-files": ["temp_slop_examples.py", "src/fixtures/example.py"],
"exclude-severities": ["low"],
"exclude-dirs": [".git", "node_modules", ".venv", "tests", "dist", "build"],
"include": [
"fallback-defaults",
"exposed-secrets",
"large-function",
"large-file",
"duplicate-functions",
"helper-sprawl"
]
}
Rule IDs for include:
fallback-defaultsexposed-secretslarge-functionlarge-fileduplicate-functionshelper-sprawl
File and function size (line counts)
These keys control the large-file and large-function rules (line counts include the whole file or function body).
| Key | Default | Effect |
|---|---|---|
max-file-lines-warning |
400 |
At or above this line count → medium large-file finding (only for extensions in large-file-extensions). |
max-file-lines-high |
800 |
At or above this line count → high large-file finding. |
max-function-lines-warning |
50 |
At or above this line count → medium large-function finding. |
max-function-lines-high |
100 |
At or above this line count → high large-function finding. |
Raise the warning thresholds when large components or test files are normal for your repo (for example "max-file-lines-warning": 750 so a 592-line file is below the warning cutoff). CLI flags --max-file-lines and --max-function-lines still override only the warning thresholds for a one-off run.
Notes:
- CLI flags still work and override file values (for example,
--fail-threshold). - If
includeis omitted, all rules run. exclude-filesaccepts either bare filenames or scan-root-relative paths.exclude-severitiesis an array oflow,medium, and/orhigh; matching findings are dropped from output and from the total score (case-insensitive).- Unknown keys and unknown rule IDs fail fast with clear errors.
Contributing and Commits
Standard flow:
- Create a feature branch from
main. - Make changes.
- Run checks locally:
uv run pytest uv run ruff check . env PYTHONPATH=src uv run python -m slopsniff.cli . --fail-threshold 30
- Commit with a clear message.
- Open a PR and merge after CI passes.
Release Process
Publishing is handled by .github/workflows/publish.yml.
It runs on GitHub Release published (not on tag push alone).
Recommended: one-command release script
From repo root, on main, with a clean working tree:
./scripts/release.py 0.1.9
# or:
uv run python scripts/release.py v0.1.9
What it does:
- Pulls
main. - Bumps version in
pyproject.tomlandsrc/slopsniff/__init__.py. - Runs
uv lock. - Commits
chore: release X.Y.Z. - Pushes
main. - Tags
vX.Y.Zand pushes the tag. - Creates/publishes a GitHub release with
gh release create.
Useful flags:
--dry-run--no-pull--allow-dirty--notes-file PATH--expect-repo OWNER/REPO
Optional guard:
- Set
SLOPSNIFF_RELEASE_EXPECT_REPO=joshuagilley/slopsniffto prevent accidental release from a fork clone.
If release/publish fails
HTTP 400from PyPI: that version already exists; bump to a new version and release again.HTTP 422fromgh release create: release already exists for that tag. Re-run workflow for that release if needed.- Re-running old releases uses the same tagged commit; it does not pick up newer
main.
Minimal Architecture Notes
Pipeline:
- Walk repo (or, with
--changed-since/--branch, only paths fromgit diff) and collect included files. - Parse functions (
astfor Python, heuristic parser for JS/TS/TSX/JSX/Vue). - Run per-file rules.
- Run cross-file rules.
- Aggregate findings and score.
- Report (compact Rich terminal or
json) and exit non-zero on threshold fail.
Key paths:
src/slopsniff/cli.py— CLI entrypointsrc/slopsniff/scanner.py— orchestrationsrc/slopsniff/rules/— rule implementationssrc/slopsniff/reporters/— terminal/json outputscripts/release.py— scripted release flow
Metadata
Release files for slopsniff 0.1.19
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| slopsniff-0.1.19.tar.gz | 27.7 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| slopsniff-0.1.19-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 52.2 kB
Release files / slopsniff-0.1.19.tar.gz
| Download URL | slopsniff-0.1.19.tar.gz |
|---|---|
| Size | 27.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d0990d49648d680a8de8c863704c417fb0e609584045af491b7c5ac9dd48e713
|
|
BLAKE2b-256 checksum How to use checksums |
4a4ad64cfa4f32421d628dd2997d6909942b2db87f2930894ebbbee3fb52ea3a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 24, 2026.
Transparency logRelease files / slopsniff-0.1.19-py3-none-any.whl
| Download URL | slopsniff-0.1.19-py3-none-any.whl |
|---|---|
| Size | 24.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b066b0e2d43b2ffce3a8ea859439c9d038823156d7c129ca0b9f5f9a4e37c65c
|
|
BLAKE2b-256 checksum How to use checksums |
1e108661494a397065fd2997d7c5a92992a6e18cb8d0b9f03028d0453f7a3918
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 24, 2026.
Transparency logSecurity for slopsniff 0.1.19
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for slopsniff-0.1.19.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 24, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.