Skip to main content

A cli tool to enforce documentation for code suppressions

Project description

shamefile logo

 

Turn silent tech debt into reviewable and documented decisions.

Tests Lint CodeQL Coverage Quality Gate License Rust


shamefile won't let anyone silence a linter warning in your code without writing down why.

People are lazy. Both committer and code reviewer.

  • The committer slaps a // NOLINT comment when there's no easy fix. They don't justify it — in most languages there's no good place for that.
  • The code reviewer focuses on more important things: security, bugs, design. There's no dedicated time for checking new suppression arrivals.

Shamefile adds shamefile.yaml for the code reviewer and the shame CLI for the committer to give them tools to react before tech debt gets out of control.

Why it's important

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:

$ 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.2.tar.gz (137.6 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.2-py3-none-win_arm64.whl (1.7 MB view details)

Uploaded Python 3Windows ARM64

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

Uploaded Python 3Windows x86-64

shamefile-0.1.2-py3-none-musllinux_1_2_x86_64.whl (2.0 MB view details)

Uploaded Python 3musllinux: musl 1.2+ x86-64

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

Uploaded Python 3musllinux: musl 1.2+ ARM64

shamefile-0.1.2-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.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.8 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARM64

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

Uploaded Python 3macOS 11.0+ ARM64

shamefile-0.1.2-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.2.tar.gz.

File metadata

  • Download URL: shamefile-0.1.2.tar.gz
  • Upload date:
  • Size: 137.6 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.2.tar.gz
Algorithm Hash digest
SHA256 151a80c29d33398e83e49969a9b495e95161f24a34af0aa2a63a86e41d992b74
MD5 63fb8bddbb19fc469505c0e38c0cf6b0
BLAKE2b-256 aa1be2cbc3a5ea5af1fda7b4009b144ef83d62c0221031a86162caacd99befc8

See more details on using hashes here.

Provenance

The following attestation bundles were made for shamefile-0.1.2.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.2-py3-none-win_arm64.whl.

File metadata

  • Download URL: shamefile-0.1.2-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.2-py3-none-win_arm64.whl
Algorithm Hash digest
SHA256 af08a7588e8f57d8a56eb9505f98de100d4713e8abc06a063a8dbcd2c6b12bed
MD5 0d9be3f322e767d3c98be903f03795bd
BLAKE2b-256 33014b84250a272610a7e6c302d736d0e534f241b4896c2b765845f0aea5a523

See more details on using hashes here.

Provenance

The following attestation bundles were made for shamefile-0.1.2-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.2-py3-none-win_amd64.whl.

File metadata

  • Download URL: shamefile-0.1.2-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.2-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 b1f3e96157d5eddfee54c96f94bd5abb714376b596f8153a5230568713e7deec
MD5 ce651129aa38d638e49790c5558c24ea
BLAKE2b-256 c40f0653202001984190a861eb795b006fd40f4f6b4ec815b82134d11b5f16e9

See more details on using hashes here.

Provenance

The following attestation bundles were made for shamefile-0.1.2-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.2-py3-none-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for shamefile-0.1.2-py3-none-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 0453070d51199c2b3d083ea09c8932d78cc70132476ec7e1ffea9a9795b4a947
MD5 1601a62222224193a2b3f586e22784e6
BLAKE2b-256 a7d6599f071d20e1c1c0089acfde27c49730d091c352ec13186713c074bb1210

See more details on using hashes here.

Provenance

The following attestation bundles were made for shamefile-0.1.2-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.2-py3-none-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for shamefile-0.1.2-py3-none-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 8e5a19366684a233b1bfb7e4f43735b6bdeebb0ea903f53d78ceca7133bf5a4c
MD5 390b615c246de4889e1ba05f8678e9c3
BLAKE2b-256 99ec2db020e485cfddd59e3b7f97a051e121fce650d3a460163245c47f263f7a

See more details on using hashes here.

Provenance

The following attestation bundles were made for shamefile-0.1.2-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.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for shamefile-0.1.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 53ff1cb0feeb2e426d376273e15799a4580690dffcdb4a0c5d98c012c754acd9
MD5 ea1ba00670e99bbdf595ecad7b1a6df2
BLAKE2b-256 c10af7923689d71090e02e692583866c085fb6d99528ff939a8fea8574e8c40d

See more details on using hashes here.

Provenance

The following attestation bundles were made for shamefile-0.1.2-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.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for shamefile-0.1.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 e6b6d605698ca035537ebbc429614f646b3c086c126dc522526020f3e63df6b4
MD5 5f0f3c3f22b70fde0220bea1a7729ff2
BLAKE2b-256 ebcae7f5abc7bd3a6a782fff313eb79b1fac093d9a17705c4befedf3a3ac01e7

See more details on using hashes here.

Provenance

The following attestation bundles were made for shamefile-0.1.2-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.2-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for shamefile-0.1.2-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 f22b986e127bfd90013a230b2d5a1fd1f113310c9dc98b540c716a3b3d81982b
MD5 32772f00e6d3ed2ac8176c81a11228d3
BLAKE2b-256 7fe2e5ee18a45a91e1f9d13e03e76458bc206ff0c9fee223ad07dea6833b7695

See more details on using hashes here.

Provenance

The following attestation bundles were made for shamefile-0.1.2-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.2-py3-none-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for shamefile-0.1.2-py3-none-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 38782d806a115534e5df8c754a207d0c06f8edd8fa2e40553bfd30e2df16f463
MD5 4ed2e3d1628a30821c33d23126d292dc
BLAKE2b-256 8bceedf5774ec2c288c2aed24e59c4c03f4d0c0511c8e313062b64e6680fb8ab

See more details on using hashes here.

Provenance

The following attestation bundles were made for shamefile-0.1.2-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