Metadata-Version: 2.4
Name: ill-be-backspace
Version: 0.1.1
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Rust
Classifier: Topic :: Software Development :: Quality Assurance
License-File: LICENSE
Summary: Flags comment blocks that have outgrown the code they describe.
Keywords: linter,comments,pre-commit,static-analysis
Home-Page: https://github.com/phin-tech/ill-be-backspace
Requires-Python: >=3.8
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Homepage, https://github.com/phin-tech/ill-be-backspace
Project-URL: Issues, https://github.com/phin-tech/ill-be-backspace/issues
Project-URL: Repository, https://github.com/phin-tech/ill-be-backspace

<p align="center">
  <img src="docs/img/100-percent-vibed.png" alt="100% vibed" width="640">
</p>

<h1 align="center">I'll Be Backspace</h1>

<p align="center">
  <em>The comment linter that says what everybody's thinking.</em>
</p>

---

## Friend, Can We Talk About Your Comments?

Let me paint you a picture. You open a file. You're a busy person. And there,
sitting on top of a two-line function like a wedding cake on a bicycle, is
*this*:

```python
# Sync pulls files; it does NOT recreate containers. That's enough for
# config a service reads live (Traefik file-watches dynamic/, Dashy
# re-reads conf.yml per request), but a compose change needs an `up` to
# take effect. Verified 2026-07-29: fixing postgres's data mount and
# force-syncing left the same container id running the old mount, so the
# crash loop continued. Hence the two-step below.
sync(service)
compose_up(service)
```

Six lines of comment. Two lines of code. Somewhere in there is one useful fact,
and it is doing hard time.

Now — what if I told you there was a **better way**?

```console
$ backspace deploy.py
deploy.py:2:5: error: comment-code-ratio: 6 comment lines describe 2 lines of code (ratio 3.0, max 1.5)
    2 | Sync pulls files; it does NOT recreate containers. That's enough for
    3 | config a service reads live (Traefik file-watches dynamic/, Dashy
      | ... 3 more lines
    7 | crash loop continued. Hence the two-step below.
      = help: A comment longer than the code it describes usually restates the
             code. Say what the code cannot.

1 file checked, 1 violation
```

That's it. That's the product. And folks, it is **fast**.

## What's In The Box

- **Nineteen languages out of the box.** Python, Rust, Go, TypeScript,
  JavaScript, Bash, C/C++, Java, Kotlin, Swift, Ruby, PHP, Lua, SQL, YAML,
  TOML, HCL, Dockerfile, Makefile. Adding another is *one TOML file*.
- **It knows a string from a comment.** `s = "# not a comment"` does not fool
  it. Neither do raw strings, template literals, nested block comments, or
  JavaScript regex literals. This is the part everyone else gets wrong.
- **Diff-aware.** Point it at a mature repo and it won't hand you four hundred
  findings on code you didn't write. It checks what *you* touched.
- **Docstrings are safe.** Your API documentation is supposed to be long. We
  leave it alone unless you ask.
- **A `--json` mode built for robots.** Ships the offending comment text right
  in the payload, so your agent can fix it without opening the file again.
- **Configurable six ways from Sunday**, and — here's the kicker — a
  `config show` command that tells you *which layer* set every single value.
  No guessing. Never guessing.

## Get It Installed. Right Now. Today.

**Pre-commit** — and this is where it really sings:

```yaml
repos:
  - repo: https://github.com/phin-tech/ill-be-backspace
    rev: v0.1.0
    hooks:
      - id: backspace
```

**Or any of these**, whichever suits your operation:

```console
$ uvx --from ill-be-backspace backspace   # try before you buy
$ pipx install ill-be-backspace           # a wheel, no Rust required
$ brew install phin-tech/tap/backspace
```

The package is `ill-be-backspace`; the command it installs is `backspace`.

## Kick The Tires

```console
$ backspace                       # current directory, changed lines only
$ backspace src/ --all            # the whole lot
$ backspace --diff=main           # everything your branch changed
$ backspace src/ --json           # for machines
$ backspace --stats               # who's the worst offender?
```

Exit codes are `0` clean, `1` violations, `2` you've configured something
strange. Your CI will know exactly what to do.

## Three Rules. That's All It Takes.

| Rule | What it catches |
|---|---|
| `block-too-long` | More than `max_lines` (5) consecutive lines of comment. |
| `comment-code-ratio` | A comment longer than the code beneath it. **This is the one that finds the real offenders.** |
| `banned-phrase` | Regexes you pick. There's an `llm-tells` preset for `Verified 2026-…`, `Note that`, `it does NOT`, and friends. Opt-in — we're not here to preach. |

`backspace explain <rule>` if you want the long version.

## Make It Yours

It reads config from wherever you already keep it — `.backspace.toml`,
`pyproject.toml`, `package.json`, or `Cargo.toml`. No new file unless you want
one.

```toml
max_lines = 5
exclude = ["**/vendor/**"]

[rules.banned-phrase]
preset = "llm-tells"

[languages.go]
max_lines = 8          # licence headers, we understand

[[overrides]]
paths = ["tests/**"]
max_lines = 15         # tests get a little room to breathe
```

And when you can't remember why a value is what it is:

```console
$ backspace config show src/api/handlers.py
src/api/handlers.py  (python, config: .backspace.toml)
  max_lines                  = 15            overrides[0]
  max_ratio                  = 1.50          default
  severity                   = error         command line
```

Full schema in [`skill/reference.md`](skill/reference.md).

## Sometimes The Long Comment Is Right

Wire formats. Protocol quirks. We're reasonable people:

```python
# backspace: ignore[block-too-long] — frame layout is fixed by RFC 9114 §4.2
```

`backspace: ignore` for everything, `backspace: ignore-file` near the top of a
file for the whole thing. Set `require_suppression_reason = true` and it'll
insist you explain yourself — which, frankly, you should.

## For The Robots

There's a Claude Code skill in [`skill/`](skill/). Drop it in and your agent
writes shorter comments *in the first place*, which beats catching them at
commit time every day of the week:

```console
$ cp -r skill ~/.claude/skills/backspace
```

## Under The Hood

A clanker-rolled character state machine, one pass, no parser generators, no
grammar downloads. Language definitions are plain TOML embedded at compile time
— and you can add your own at *runtime*, same schema, no recompile.

Two things it's honest about: JavaScript regex-literal detection and Python
docstring position are heuristics, not a parser. They're tested against the
cases that matter and documented where they aren't perfect.

197 tests. It passes its own lint. We wouldn't dare ship it otherwise.

## Contributing

New language? One file in `languages/`, one line in `src/lang/mod.rs`, a
`good_` and a `bad_` fixture in `tests/fixtures/`. That's the whole ceremony.

```console
$ cargo test
$ cargo run -- --all .
```

## License

MIT. Go build something.

