Skip to main content

A cli tool to enforce documentation for code suppressions

Project description

shamefile — force to document silenced linter

shamefile logo

 

Turn silent tech debt into reviewable and documented decisions.

Tests Lint CodeQL Coverage Quality Gate License Rust shamefile


How it works  •  Why use it  •  Installation  •  Supported languages  •  FAQ  •  Roadmap  •  Contributing

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.

How it works

Five real suppressions across five languages:

Python suppression JavaScript suppression

C# suppression Java suppression

Go suppression

Init empty shamefile with shame me .

CI failed: found undocumented suppressions

shamefile.yaml with empty why fields

Fill empty why with shame next

shamefile.yaml with documented why fields

Run again via CI with shame me . --dry-run

CI passed: every suppression documented

Reviewer rejects a weak justification on a shamefile entry

Why you should use it


Instantly improves AI agents

Instantly improves AI agents

Ignoring warnings? Not so easy anymore. Triggers AI reflection — "is this really unfixable?". Observed across Claude, GPT, and Cursor agents. Safer vibe coding by default.



Single source of truth

Single source of truth

"Where to document?", "where to look?", "what to do?" — same answer to every question: shamefile.yaml.



Reviewable by design

Reviewable by design

Make suppression review a ritual, not goodwill.



Shamefile doesn't quit with ex-employees

Shamefile doesn't quit with ex-employees

Knowledge stays in the YAML and you can refactor with confidence.


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

Commands

Command What it does
shame me . Scan project, sync shamefile.yaml; fail if any entry lacks why
shame me . --dry-run CI mode — read-only validation, never writes to disk
shame next Show first undocumented entry with the source line highlighted
shame next "<reason>" Document the first undocumented entry inline
shame fix <location> <token> --why "<reason>" Document a specific entry
shame remove <location> <token> (alias shame rm) Delete a stale entry without editing the YAML by hand

Run shame --help for the full reference.

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-17
  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 languages

shamefile is language-agnostic by design. The list below reflects what has been wired up and tested so far — not a limit on what the engine can do. If your language is missing and there's no open issue for it, please open one so we know there's interest.

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

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

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
  • Native exclusion config — first-class exclude: patterns in shamefile.yaml for checked-in vendored or generated code that bypasses the default .gitignore discovery

FAQ

Why not just write the reason inline, like # noqa: F401 # legacy import?
  • Reviewers don't see it. A # noqa buried in one of seven changed files rarely gets pushback. shamefile.yaml puts every suppression in the PR into one diff — the reviewer sees the full cost as a single list, with author and why per entry.
  • Nothing forces a reason. Linters accept any string after the token, or none. shame me . --dry-run fails the build until every entry has a non-empty why. This matters most for AI coding agents, which lose the suppression's context the moment the session ends — the registry forces them to write the reason to disk while it still exists.
  • Inline is a bad trade-off. A short reason carries no information; a useful one drowns the line of code it is attached to. The registry keeps source readable and justifications detailed.

What stops developers from writing why: 'TODO' and moving on?

The tool guarantees a string is written; the reviewer judges whether it is a real reason. If why: 'TODO' passes review, that is an organisational gap, not a tool gap — but the registry makes the gap visible: every lazy entry is one grep away, by author and date. Before shamefile, the same shortcut was hidden inside whichever file it lived in.


Won't shamefile.yaml become a merge conflict magnet on parallel PRs?

This is the same trade-off every shared-file tool (lockfiles, changelogs, schema migrations) has accepted in exchange for single-source-of-truth visibility. A custom git merge driver that resolves automatically is on the Roadmap.

The registry is sorted by (location, token), so suppressions added in unrelated parts of the codebase land in different regions of the file — most parallel PRs do not collide. When they do, shame me is idempotent: after a merge, running it on the resolved tree deterministically reconciles entries from source, so git checkout --theirs shamefile.yaml && shame me . is the escape hatch.


What about generated, vendored, or third-party code?

A repo's typical generated/vendored content is excluded for free:

  • .gitignore and .ignore files are respected (handled by the same engine ripgrep uses), so node_modules/, target/, dist/, __pycache__/ etc. are skipped without configuration.
  • Only .py / .js / .jsx / .mjs / .cjs / .ts / .tsx are scanned, so vendored content in any other language is silently ignored.

A first-class exclude: config in shamefile.yaml is on the Roadmap.

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.

License

shamefile is licensed under the Apache License 2.0. See the LICENSE file for more information.

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

Uploaded Python 3Windows ARM64

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

Uploaded Python 3Windows x86-64

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

Uploaded Python 3musllinux: musl 1.2+ x86-64

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

Uploaded Python 3musllinux: musl 1.2+ ARM64

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

Uploaded Python 3manylinux: glibc 2.17+ ARM64

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

Uploaded Python 3macOS 11.0+ ARM64

shamefile-0.1.6-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.6.tar.gz.

File metadata

  • Download URL: shamefile-0.1.6.tar.gz
  • Upload date:
  • Size: 790.3 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.6.tar.gz
Algorithm Hash digest
SHA256 e735c43daec628ddda44d6244251e7d252aeea4dc8ff00d8bb069552b6eefde1
MD5 dc2746d5681325827c1cc430768779c7
BLAKE2b-256 d062fe6acd943afe43be4fbf879ce9be7787c9ef98851dd9e4db73c43354f869

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: shamefile-0.1.6-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.6-py3-none-win_arm64.whl
Algorithm Hash digest
SHA256 eb11130958e3149d5d01f0775091dbd47ce8795e7e052264ac7765d05ebe4533
MD5 477916f4f2156ee5bda69a6ec03da7cd
BLAKE2b-256 1c799319e504e7c6ba3563725a5819ca87eb07ff434145c6c53501b93fa02b2d

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: shamefile-0.1.6-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.6-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 ab5b1d86446e4c6e16f713a7c023e7937f621db66e01a099ee4072b1053d3ed8
MD5 f1b313372b64d04b1d0860720d89ffc8
BLAKE2b-256 eb4d9a1295b5fe72bfd40f32d71d57a398e8aa05a97df9cc8a17b589823a242c

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for shamefile-0.1.6-py3-none-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 7e9fd1fa29f9440cbe42ded0ec03ca412b19e361fffb68f98c084eb513d22aca
MD5 4ce6150d2d58a17de5a23855a75ae3c4
BLAKE2b-256 0c8b533ab9123cf65d4f4421181b8cdba1326a16d7dba0d07eff267f0b29f06e

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for shamefile-0.1.6-py3-none-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 37ded954dfb5fb2a060e02df78aa9d215664e58d0a3e5b7e1bf1bf4b4e22f6c5
MD5 075089a9d00ccaab714aba28edcfbdb5
BLAKE2b-256 6eea85d56504961d36aa330053c6cf0525161d989f875d799ca33f6f8d34b8e9

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for shamefile-0.1.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 6adf4c90cce8eb5e90d2f98269638fff9162ebd66e327a0a51d954df502637fb
MD5 facf85d497dbb786bda96aa52c2b17d0
BLAKE2b-256 edba19a142ab1e3835d93865c4394816df11fbe5fb9355b47be5d3fe6bfcded0

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for shamefile-0.1.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 0de5713e2ce930a1509530144a92d7d43cc7266887ac99b10cf25676f9664e47
MD5 81025f5a5f3dae6831ba3dfb76faa3e2
BLAKE2b-256 4a9f27095d496d168427d424f01e4a95793a8da122ecb66e2ff077cfbe17de07

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for shamefile-0.1.6-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 5fe45cb51c1fd480cc6656272c29341bdf46b86a9820ea36fe53fefa3f2d01ee
MD5 a47cf7c5499676d7249d9e0336c62319
BLAKE2b-256 4e8b61f8bbe69f034eaa22185e08d30c28b7df06bd2188389c2ea22f69b63bd0

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for shamefile-0.1.6-py3-none-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 99de3466cf74865f89c15c70c3f4125671604374a4d0a61e9699ca4d3773a263
MD5 e021300be3535890122a7f5ae4a85f66
BLAKE2b-256 38251a87116a4f73a1382c4c47792973a064f036bd5ea4c2575aee661ce4b4c5

See more details on using hashes here.

Provenance

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