Skip to main content

A cli tool to enforce documentation for code suppressions

Project description

shamefile logo

Tests Lint CodeQL Coverage Quality Gate License Rust

Turn linter suppressions from silent technical debt into reviewable, documented decisions.

shamefile is a CLI that scans your codebase for linter suppressions (# noqa, // eslint-disable, // @ts-ignore, and many more), consolidates them into a single registry, and fails the build until every one of them carries a written justification. Human authors and AI coding agents operate through the same interface.

Why shamefile

A mysterious # noqa with no explanation, left by a developer who moved on years ago. Nobody remembers why. Nobody wants to touch it. This is how legacy code accumulates — silently, one linter suppression at a time.

shamefile interrupts that pattern. Every suppression is tracked in a single shamefile.yaml — one file, one purpose. When it changes in a pull request, a reviewer sees the full cost of a shortcut in a single diff. And as AI coding agents become routine PR authors, the registry acts as a consistent gate: whether a suppression was introduced by a human or a model, it ships with a written justification or it doesn't ship at all.

How it works

shamefile exposes two stages, one command each.

Scanshame me . walks your project, finds every suppression token, and syncs the central shamefile.yaml. New suppressions are registered with auto-filled metadata (owner from git blame, timestamp, source line). Stale entries are removed. The command fails if any entry lacks a why.

Documentshame next shows the first undocumented suppression, with the exact source line highlighted. Provide the reason inline (shame next "<reason>"), or target a specific entry with shame fix <location> <token> --why "<reason>".

The same interface works for a developer opening a PR and for an AI agent iterating through gaps one at a time — without having to read the full registry into context.

Workflow

1. Developer writes code with a suppression:

result = parse_legacy_api(raw)  # type: ignore

2. Pre-commit (or manual) run surfaces the gap:

$ shame me .
Scanning . for suppressions...
Added 1 new entries to /home/user/myproject/shamefile.yaml
1 suppressions need documentation (why).
Run `shame next` to see the first one, or `shame next "<reason>"` to fill its why.

...

3. Developer documents it — one entry at a time:

$ shame next
./src/api.py:42
    |
  42| result = parse_legacy_api(raw)  # type: ignore

Fix with:
  shame next "<reason>"
  shame fix "./src/api.py:42" "# type: ignore" --why "<reason>"

$ shame next "legacy API returns untyped dict; types module in progress"
Documented: # type: ignore at ./src/api.py:42
All entries documented. No shame today!

4. Developer commits both api.py and shamefile.yaml. The shortcut and its justification land in the same PR, reviewable in a single diff.

CI/CD integration

On the CI side, shame me . --dry-run is read-only and deterministic. It validates three contracts:

Check Meaning
Coverage Every suppression in code is registered in shamefile.yaml
Staleness Every registered entry still points at a live suppression in code
Justification Every entry has a non-empty why

A failure on any of the three exits non-zero.

# .github/workflows/ci.yml
- name: Check suppressions
  run: shame me . --dry-run
Flag Description
--dry-run (-n) Read-only validation for CI/CD — never writes to disk
--hidden Also scan hidden files and directories (dotfiles)

Registry format

shamefile.yaml lives at the project root (git root if available, otherwise the working directory). Every entry is human-readable and stable under git diff:

---
config: {}
entries:

- location: ./src/api.py:42
  token: '# type: ignore'
  content: 'result = parse_legacy_api(raw)  # type: ignore'
  created_at: 2026-04-17T21:15:05Z
  owner: Anna Nowak <anna@example.com>
  why: 'legacy API returns untyped dict; types module in progress'
  • location and token form the entry's identity.
  • content is the verbatim source line — used for reconciliation when code moves.
  • owner and created_at are populated automatically on first run via git blame.
  • why is the only field that requires a written justification — from a developer or an AI agent. The PR reviewer decides whether the reason is good enough.

Cascade matching

A registry that breaks every time you refactor is worse than no registry. shamefile reconciles entries against source code in two passes:

  1. Location match — exact file:line + token.
  2. Content match — same source line + token (handles line shifts, with rename detection limited to the most recent commit via git diff HEAD~1..HEAD -M).

Reformatting a function or inserting imports above a suppression preserves the entry — owner, created_at, and why stay intact. Entries are only removed when the token itself is gone from the code.

Supported tokens

Token Tool Language
# noqa Flake8 / Ruff Python
# pylint: disable Pylint Python
# type: ignore Mypy Python
# pyright: ignore Pyright Python
# pytype: disable Pytype Python
# pyre-ignore / # pyre-fixme Pyre Python
nosec Bandit Python
# pragma: no cover Coverage.py Python
# fmt: off / # fmt: skip Black / Ruff Python
# isort: skip isort Python
# lint-fixme / # lint-ignore Fixit Python
# autopep8: off autopep8 Python
// eslint-disable, /* eslint-disable ESLint JS / TS / TSX
// tslint:disable, /* tslint:disable TSLint TS / TSX
// @ts-ignore, /* @ts-ignore TypeScript JS / TS / TSX
// @ts-expect-error, /* @ts-expect-error TypeScript JS / TS / TSX

Experimental tokens

New languages added via the Add a language template land here first. They have passed the existing test suite and a sanity run on a real codebase, but no real-world showcase has been contributed yet. Promotion to Supported tokens happens through the Verify and release a language flow.

Language

Supported file extensions: .py, .js, .jsx, .mjs, .cjs, .ts, .tsx.

Installation

Source Command
npm npm install -g shamefile
PyPI pip install shamefile
crates.io cargo install shamefile
From source cargo install --git https://github.com/BKDDFS/shamefile
Homebrew coming soon

All channels install the shame CLI. Run shame --help to verify.

Or as a pre-commit hook:

# .pre-commit-config.yaml
- repo: https://github.com/BKDDFS/shamefile
  rev: main
  hooks:
    - id: shamefile

Roadmap

  • MCP server — native integration for LLM-based PR authors (avoids loading the full registry into agent context)
  • Custom git merge driver — auto-resolve shamefile.yaml conflicts on parallel PRs
  • Additional language grammars — Rust, Go, Java, Kotlin, C# via tree-sitter
  • Custom entry fields — attach ticket, reviewer, or deadline metadata to suppressions

Contributing

Contributions are welcome. Where you start depends on what you have:

  • Found a bug? Open an issue with a minimal repro.
  • Idea or design question? Open a Discussion under Ideas so direction can be agreed before any code is written.
  • Usage question or trouble setting things up? Ask in Q&A.
  • Want to send a PR? Read CONTRIBUTING.md first — dev setup, build/test/lint commands, commit format.
  • Security vulnerability? Use the private advisory form — see SECURITY.md. Do not open a public issue.

By participating you agree to the Code of Conduct.

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

shamefile-0.1.1.tar.gz (98.9 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

shamefile-0.1.1-py3-none-win_arm64.whl (1.7 MB view details)

Uploaded Python 3Windows ARM64

shamefile-0.1.1-py3-none-win_amd64.whl (1.8 MB view details)

Uploaded Python 3Windows x86-64

shamefile-0.1.1-py3-none-musllinux_1_2_x86_64.whl (1.9 MB view details)

Uploaded Python 3musllinux: musl 1.2+ x86-64

shamefile-0.1.1-py3-none-musllinux_1_2_aarch64.whl (1.8 MB view details)

Uploaded Python 3musllinux: musl 1.2+ ARM64

shamefile-0.1.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.9 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

shamefile-0.1.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.8 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARM64

shamefile-0.1.1-py3-none-macosx_11_0_arm64.whl (1.8 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

shamefile-0.1.1-py3-none-macosx_10_12_x86_64.whl (1.8 MB view details)

Uploaded Python 3macOS 10.12+ x86-64

File details

Details for the file shamefile-0.1.1.tar.gz.

File metadata

  • Download URL: shamefile-0.1.1.tar.gz
  • Upload date:
  • Size: 98.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for shamefile-0.1.1.tar.gz
Algorithm Hash digest
SHA256 f6d7550f060ceafc3db56a1b7c3e804b18bd672e58a62b5f36145cb4b44352da
MD5 d6f551dc4288d43ddd5be1f4e3b9d07f
BLAKE2b-256 274178996d0f22216de57ab31fe5b2a51031e4b25ae8fbdd2877d785debba29e

See more details on using hashes here.

Provenance

The following attestation bundles were made for shamefile-0.1.1.tar.gz:

Publisher: publish-pypi.yml on BKDDFS/shamefile

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file shamefile-0.1.1-py3-none-win_arm64.whl.

File metadata

  • Download URL: shamefile-0.1.1-py3-none-win_arm64.whl
  • Upload date:
  • Size: 1.7 MB
  • Tags: Python 3, Windows ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for shamefile-0.1.1-py3-none-win_arm64.whl
Algorithm Hash digest
SHA256 8f4f3b033a2f090e3a32a5b6d3760d76d490c031e3bc41a9477574b08c6572e0
MD5 2838703146db3e0cf121510b96ec7b31
BLAKE2b-256 379eb1c45964b5745bfba07293fede84b1158d3ecdcf31a66dbea725840f8eca

See more details on using hashes here.

Provenance

The following attestation bundles were made for shamefile-0.1.1-py3-none-win_arm64.whl:

Publisher: publish-pypi.yml on BKDDFS/shamefile

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file shamefile-0.1.1-py3-none-win_amd64.whl.

File metadata

  • Download URL: shamefile-0.1.1-py3-none-win_amd64.whl
  • Upload date:
  • Size: 1.8 MB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for shamefile-0.1.1-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 535bbda0fd71b653da1aa33a26f9aad594a56fa8b890e474ff2b507a5792722c
MD5 ec5370d191c558c64350ab0f270c184c
BLAKE2b-256 2f32e11cc7836cd497087d861a606640cbf9bb56dcc607d3693cc7804402e60c

See more details on using hashes here.

Provenance

The following attestation bundles were made for shamefile-0.1.1-py3-none-win_amd64.whl:

Publisher: publish-pypi.yml on BKDDFS/shamefile

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file shamefile-0.1.1-py3-none-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for shamefile-0.1.1-py3-none-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 56a4edc737d8ed2f76448ac8fd072d48d36300733ec5d03913e5b3ea01f9a502
MD5 890ad51386fb2f3fefe7d3d0734cc7cb
BLAKE2b-256 aff3d6e28a4f8092e2f6a7d01bcdb8dac930914f5d16f4ff4380d1d1635de086

See more details on using hashes here.

Provenance

The following attestation bundles were made for shamefile-0.1.1-py3-none-musllinux_1_2_x86_64.whl:

Publisher: publish-pypi.yml on BKDDFS/shamefile

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file shamefile-0.1.1-py3-none-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for shamefile-0.1.1-py3-none-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 c75dbd4c2a7a2f2998d7055ead6c7e74387e4e6bca6ca38a5b062c7164a3813d
MD5 31b3c074ee27f59b4543f1b44a668f1b
BLAKE2b-256 2ffd7493cff38789990db42fc83037276216960edcc013c1e98049e68fe1afea

See more details on using hashes here.

Provenance

The following attestation bundles were made for shamefile-0.1.1-py3-none-musllinux_1_2_aarch64.whl:

Publisher: publish-pypi.yml on BKDDFS/shamefile

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file shamefile-0.1.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for shamefile-0.1.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 001e089e5b83a33cb75ce7abe2e87958c4806b176d22d55cca39ccfa5018c287
MD5 7ad2d0b085ffe926d860b96ff128842e
BLAKE2b-256 ae790f6dcc4b5c40ebabe5f9926c41d471f6ef06601abff60f3f7529ce1474e8

See more details on using hashes here.

Provenance

The following attestation bundles were made for shamefile-0.1.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: publish-pypi.yml on BKDDFS/shamefile

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file shamefile-0.1.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for shamefile-0.1.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 3fa4459f23f248142b45de396b201639a2251e756a6564098432b73b54261889
MD5 ec005b4e0de55f57f0669643453770b8
BLAKE2b-256 0bcbd17abcc8650d847baf5771c3dacacd6d77886a992124e9aad7d83a5815bc

See more details on using hashes here.

Provenance

The following attestation bundles were made for shamefile-0.1.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: publish-pypi.yml on BKDDFS/shamefile

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file shamefile-0.1.1-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for shamefile-0.1.1-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 adaabb80446b0a1af5b505fc16b175ae001797f8c488e073405af91a584bb465
MD5 e8fc5e619232598802aad950c7632d77
BLAKE2b-256 24e392fc68ba77b3997c377abfa6f0fff0d780cb56745fee10060b009df342c2

See more details on using hashes here.

Provenance

The following attestation bundles were made for shamefile-0.1.1-py3-none-macosx_11_0_arm64.whl:

Publisher: publish-pypi.yml on BKDDFS/shamefile

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file shamefile-0.1.1-py3-none-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for shamefile-0.1.1-py3-none-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 4d5e8c5a79e926f0f848bdcf97410a86bba8523f8fe39b411145b0444f68f477
MD5 3d113f403fdff3b3e3fc9fd3c439d81c
BLAKE2b-256 c7ead9c976306af417de6ad20855b99f1b86abbcd3c13ee83370aa8a5345712c

See more details on using hashes here.

Provenance

The following attestation bundles were made for shamefile-0.1.1-py3-none-macosx_10_12_x86_64.whl:

Publisher: publish-pypi.yml on BKDDFS/shamefile

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page