Metadata-Version: 2.5
Name: nocomment-linter
Version: 1.0.0
Summary: Linter that bans comments in Python code, with optional docstring banning. Built for coding agents.
Keywords: agents,cli,comments,docstrings,linter
Requires-Python: >=3.11
Description-Content-Type: text/markdown

# nocomment-linter

`nocomment-linter` bans comments in Python code and is designed for coding agents:
compact, stable, one-line-per-violation output, safe autofix.

## What it does

- checks only `*.py` files (never `.pyi`);
- reports regular Python comments; `#!`, coding cookies, `# noqa`,
  `# type: ignore`, `# pragma: no cover`, `# fmt: off/on`, `# ruff: noqa`,
  `# pyright: ignore` are preserved (tooling comments);
- optionally bans and removes docstrings (module, class, function, async
  function) — detected via AST, exactly what Python treats as a docstring;
- `--fix` removes violations in place and never writes a file that would not
  compile;
- configured through `pyproject.toml`; no `.gitignore` support.

## Installation

```bash
uv sync
```

Requires Python 3.11+.

## Check

```bash
nocomment-linter .
nocomment-linter src tests
```

## Autofix

```bash
nocomment-linter --fix .
```

Exit codes: `0` — no violations (with `--fix`: everything fixed), `1` —
violations found or unfixable violations remain, `2` — execution error (file
read, configuration, tokenizing or parsing).

## Configuration

```toml
[tool.nocomment-linter]
check-docstrings = false
exclude = [
    "migrations/**",
    "generated/**",
]
```

- `check-docstrings` (default `false`): `true` also enables the `DOC001-DOC004`
  docstring rules.
- `exclude`: glob patterns (`fnmatch` semantics) relative to the directory
  containing `pyproject.toml`. A pattern containing `/` is matched against the
  whole relative path (`migrations/**`); a pattern without `/` matches any
  path component, so `generated` excludes the `generated/` directory.
- The config is the first `pyproject.toml` with a `[tool.nocomment-linter]`
  section found walking up from the first `PATH` argument, then from the
  current directory; with no configuration anywhere, defaults apply.

Always skipped directories: `.vscode/`, `.idea/`, `.git/`, `.venv/`, `venv/`,
`__pycache__/`, `.mypy_cache/`, `.pytest_cache/`, `.ruff_cache/`, `build/`,
`dist/`.

## Rule codes

| Code | Violation |
| --- | --- |
| `CMT001` | standalone comment (only whitespace before `#`) |
| `CMT002` | inline comment (code before `#` on the same line) |
| `DOC001` | module docstring |
| `DOC002` | class docstring |
| `DOC003` | function docstring |
| `DOC004` | async function docstring |

## Output format

One self-sufficient line per violation:

```text
<path>:<start-line>:<start-column>-<end-line>:<end-column> <RULE> <FIX> "<content>"
```

Coordinates are 1-based; the range is half-open: `start` points at the first
character of the violation, `end` points one past its last character. `<FIX>`
is `delete-line` (whole physical line) or `delete-range` (the violation span).
`<content>` is the exact source text, escaped (`\n`, `\t`, `\"`, `\\`).

```text
src/foo.py:10:5-10:23 CMT001 delete-line "# explanation"
src/foo.py:17:20-17:31 CMT002 delete-range "# legacy"
SUMMARY violations=2 files=1
```

With no violations:

```text
OK violations=0 files=27
```

After `--fix` (all violations fixed):

```text
FIXED violations=4 files=2
```

`files` counts the files successfully checked; files with read/parse errors are
reported as `<path>: error: <message>` lines and make the run exit `2`.
Unfixable violations (autofix would break the file) are printed as ordinary
violation lines, the file is left untouched, and the run exits `1`.
