Comment Cleanup
Sweep a codebase's comments and bring them to one standard: short, sorted, placed, marked.
This skill edits by default — it is not a report generator. Because it edits by default and sweeps the whole
repository by default, every run passes through a preview gate first, and it never touches a line of executable
code.
The standard and its sources are in references/COMMENT_STANDARD.md. The
per-language docblock spellings are in
references/LANGUAGE_CONVENTIONS.md. The catalogue every finding maps to is
references/ANTIPATTERNS.md.
Two helpers do the mechanical work; resolve <skill-directory> from the loaded skill path:
<skill-directory>/scripts/scan-comments.py — inventories every comment and flags candidates by rule ID.
<skill-directory>/scripts/detect-stack.sh — summarizes the languages and tooling in play.
Non-negotiable output
- Comments only. Never executable code. Not a rename, not a reorder, not a "while I was in there" fix. When a
comment is only bad because the code is unclear, report it and move on — that is an
ESCALATE, not an edit.
Crossing this line turns a comment sweep into an unreviewed refactor. git diff at the end must contain no
change to a non-comment line.
- Refuse to start on a dirty working tree. Run
git status --porcelain. If it is not empty, stop and say so —
the user must be able to git diff the whole result and git checkout . to undo it. Proceed only if they
explicitly accept mixing the sweep into existing changes.
- Preview before editing. No file is modified before the user approves the plan (Phase 4). Never skip this,
even when the scope is a single file.
- Never delete information that is not recoverable from the code — a licence header, a bug/spec link, a
workaround rationale, or a pragma (
@ts-ignore, eslint-disable, # noqa, //nolint, //go:build,
# frozen_string_literal, …). Pragmas are code wearing a comment's syntax; deleting one changes behavior.
- Never invent a tracking reference. An un-owned
TODO is reported, not rewritten into a fake issue number.
- Never reproduce a suspected secret. Report the file, line, and class of credential, redacted. Say plainly
that deletion does not remediate it — the value must be rotated and the git history handled.
- Preserve the repository's language. Do not translate existing comments into English.
- The repository's own configuration wins. If a linter already encodes a comment rule, follow it over the house
rule and say which one you deferred to.
- Report faithfully. State what was changed, what was deliberately left alone and why, and what was escalated.
Never describe an edit you did not make.
Phase 0: Resolve inputs
Determine these before doing anything else, then echo them back:
- Scope. Default: the whole repository. Honor explicit paths, directories, or globs. Generated, vendored,
minified, and lockfile paths are always excluded —
scan-comments.py does this for you.
- Stance. Default minimalist: assume most comments should not exist; keep the why; require a docblock
only on non-obvious public API. The alternative, on request, is conventional: document every exported symbol.
- Line cap. Default 3 for inline comments (
--cap N to change).
- Apply or preview only. Default: apply, after the Phase 4 gate.
preview only stops after the plan.
- Clean tree. Run
git status --porcelain and apply the rule above.
Phase 1: Detect the stack and the local rules
- Run
<skill-directory>/scripts/detect-stack.sh for the languages and tooling.
- Read repository guidance when present:
AGENTS.md, CLAUDE.md, CONTRIBUTING.md, README.md.
- Find any comment rule the project already enforces, and defer to it:
- JS/TS —
eslint jsdoc/*, require-jsdoc, valid-jsdoc, spaced-comment, no-inline-comments;
.prettierrc print width.
- PHP —
phpcs.xml / .php-cs-fixer.php (Squiz.Commenting.*, phpdoc_* fixers).
- Python —
ruff D rules, pydocstyle convention (google / numpy / pep257), flake8-docstrings.
- Go —
golangci-lint godot, revive exported, godoclint.
- Rust —
#![warn(missing_docs)], clippy::missing_docs_in_private_items.
Never fight a configured rule. If the house standard and the project's config disagree, the config wins and you
say so in the summary.
- Note the dominant existing convention so rewritten comments do not look foreign beside their neighbours.
Phase 2: Inventory
python3 <skill-directory>/scripts/scan-comments.py [paths...] [--cap N] [--json]
The scan reports, it never gates — it always exits 0. Every line is a candidate, not a verdict: the heuristics
cannot read intent. Useful flags: --rules DUP,DEAD to focus a pass, --summary-only for a baseline,
--json for structured input, --max-per-file N to bound output.
Rule IDs and their default dispositions:
| ID |
Means |
Default |
DUP |
restates the code beside it |
DELETE |
LEN |
inline comment over the cap |
SHORTEN |
ORD |
docblock tags out of canonical order |
REORDER |
POS |
detached from its declaration, or a trailing essay |
REPOSITION |
TODO |
marker with no tracked issue |
ESCALATE |
DEAD |
commented-out code |
DELETE |
BANNER |
divider or section art |
DELETE (labelled dividers: SHORTEN) |
CHANGELOG |
version history that belongs in git |
DELETE |
GENERATED |
assistant attribution or filler |
DELETE |
EMPTY |
docblock adding nothing beyond the signature |
DELETE |
SECRET |
possible credential in a comment |
ESCALATE, redacted |
Three antipatterns are not scanner-detectable and are found only by reading: STALE (the comment contradicts
the code), CONTAMINATE (implementation detail in an interface comment), and MISSING (undocumented non-obvious
public API). Look for these in the files you open — a large scan result is not a substitute for reading.
Phase 3: Classify
Open each flagged file and assign a disposition per candidate, using
references/ANTIPATTERNS.md:
DELETE · SHORTEN · REORDER · REPOSITION · ADD · KEEP · ESCALATE
Confirm the scanner's guess against the actual code — it does not know that a "duplicate" comment carries a why
clause, that a long comment encodes an algorithm's invariants, or that a commented-out block is a documented
example. Downgrade to KEEP freely and record the reason so the next run does not re-litigate it.
Bias hard toward KEEP for: config-file comments (often the only documentation of an option), test files (a
comment naming the scenario earns its place), and anything referencing an external system you cannot verify.
Phase 4: Preview gate
No file is modified before this passes. Present:
- Files to be touched and the count per rule ID.
- The net effect — comments deleted, shortened, reordered, repositioned, added.
- Two or three representative before/after pairs, including the most aggressive edit planned.
- Everything being escalated rather than fixed (
TODO, STALE, SECRET, unclear code).
- Anything skipped and why (generated, vendored, linter-governed).
For a scope over ~20 files, write this to COMMENT_CLEANUP_PLAN.md at the repository root and give the path
instead of flooding the chat. Then get explicit confirmation. The user may narrow the scope, drop a rule, or
change the stance — re-plan rather than proceeding partially.
Phase 5: Apply
Work in batches of related files, not one sweeping pass, so a bad batch can be reverted without losing the good
ones. After each batch:
- Re-read what you changed and confirm every hunk is comment-only.
- Keep the file's existing docblock dialect and alignment — you are normalizing order and length, not converting
JSDoc to TSDoc or reflowing aligned PHPDoc columns.
- Never leave a file mid-standard. If a docblock cannot be fixed without touching the signature,
ESCALATE it and
leave it untouched.
Then verify the batch (Phase 6) before starting the next.
Phase 6: Verify
After each batch, and again at the end:
git diff --stat # scope check
git diff -U0 | grep -E '^[+-]' | grep -vE '^(\+\+\+|---)' # every changed line
python3 <skill-directory>/scripts/scan-comments.py [paths...] --summary-only
- Read every changed line. Each one must be either (a) wholly a comment or a blank line, or (b) a line whose
code portion is byte-identical to its counterpart, differing only in a trailing comment that was removed or
rewritten. Anything else — a renamed variable, a moved statement, a reformatted expression — violates the first
non-negotiable rule. Revert it immediately.
- Re-scan and confirm the counts moved in the expected direction, and that no new rule fired.
- Run whatever the project already provides and is cheap: the linter, the type checker, and the focused tests for
the touched files. A docblock edit can break a build — malformed C# XML docs, an unparseable rustdoc fence, a
Go build constraint that lost its exact spacing. Do not claim a check passed if you did not run it.
- If the project generates documentation (
typedoc, phpDocumentor, sphinx, cargo doc, godoc), build it
when that is cheap and confirm it still succeeds.
Phase 7: Summarize
In chat, report concisely:
- Files changed and the counts by disposition.
- What was escalated and needs a human: un-owned
TODOs, comments that contradict their code, suspected
secrets (redacted), and any place where the comment was only bad because the code is unclear.
- What was deliberately kept, and which project linter configuration you deferred to.
- The verification commands you ran and their results.
Remind the user the whole sweep is one reviewable diff: git diff to inspect, git checkout . to undo.
1---2name: comment-cleanup3description: Sweeps a codebase and rewrites its code comments in place to one standard — short (1-line default, 3-line cap), sorted (canonical docblock tag order), correctly placed (attached to the declaration, no detached or trailing essays), and consistently marked (TODO with a tracked issue). Deletes comments that restate the code, banner art, commented-out blocks, changelog-in-comments, and assistant filler; adds a docblock only to non-obvious public API. Use when asked to clean up, shorten, sort, standardize, or fix code comments and docblocks — "my comments are too long", "sort the docblocks", "remove the useless comments", "normalize the JSDoc/PHPDoc/docstrings". Edits comments only, never executable code, and always previews the plan before touching a file. Do not use for writing prose documentation, README files, PR review comments, or commenting out code.4license: MIT5---67# Comment Cleanup89Sweep a codebase's **comments** and bring them to one standard: **short**, **sorted**, **placed**, **marked**.10This skill **edits by default** — it is not a report generator. Because it edits by default and sweeps the whole11repository by default, every run passes through a preview gate first, and it never touches a line of executable12code.1314The standard and its sources are in [references/COMMENT_STANDARD.md](references/COMMENT_STANDARD.md). The15per-language docblock spellings are in16[references/LANGUAGE_CONVENTIONS.md](references/LANGUAGE_CONVENTIONS.md). The catalogue every finding maps to is17[references/ANTIPATTERNS.md](references/ANTIPATTERNS.md).1819Two helpers do the mechanical work; resolve `<skill-directory>` from the loaded skill path:2021- `<skill-directory>/scripts/scan-comments.py` — inventories every comment and flags candidates by rule ID.22- `<skill-directory>/scripts/detect-stack.sh` — summarizes the languages and tooling in play.2324## Non-negotiable output2526- **Comments only. Never executable code.** Not a rename, not a reorder, not a "while I was in there" fix. When a27 comment is only bad because the code is unclear, **report it and move on** — that is an `ESCALATE`, not an edit.28 Crossing this line turns a comment sweep into an unreviewed refactor. `git diff` at the end must contain no29 change to a non-comment line.30- **Refuse to start on a dirty working tree.** Run `git status --porcelain`. If it is not empty, stop and say so —31 the user must be able to `git diff` the whole result and `git checkout .` to undo it. Proceed only if they32 explicitly accept mixing the sweep into existing changes.33- **Preview before editing.** No file is modified before the user approves the plan (Phase 4). Never skip this,34 even when the scope is a single file.35- **Never delete information that is not recoverable from the code** — a licence header, a bug/spec link, a36 workaround rationale, or a **pragma** (`@ts-ignore`, `eslint-disable`, `# noqa`, `//nolint`, `//go:build`,37 `# frozen_string_literal`, …). Pragmas are code wearing a comment's syntax; deleting one changes behavior.38- **Never invent a tracking reference.** An un-owned `TODO` is reported, not rewritten into a fake issue number.39- **Never reproduce a suspected secret.** Report the file, line, and class of credential, redacted. Say plainly40 that deletion does not remediate it — the value must be rotated and the git history handled.41- **Preserve the repository's language.** Do not translate existing comments into English.42- **The repository's own configuration wins.** If a linter already encodes a comment rule, follow it over the house43 rule and say which one you deferred to.44- **Report faithfully.** State what was changed, what was deliberately left alone and why, and what was escalated.45 Never describe an edit you did not make.4647## Phase 0: Resolve inputs4849Determine these before doing anything else, then echo them back:50511. **Scope.** Default: **the whole repository**. Honor explicit paths, directories, or globs. Generated, vendored,52 minified, and lockfile paths are always excluded — `scan-comments.py` does this for you.532. **Stance.** Default **minimalist**: assume most comments should not exist; keep the *why*; require a docblock54 only on non-obvious public API. The alternative, on request, is **conventional**: document every exported symbol.553. **Line cap.** Default 3 for inline comments (`--cap N` to change).564. **Apply or preview only.** Default: apply, after the Phase 4 gate. `preview only` stops after the plan.575. **Clean tree.** Run `git status --porcelain` and apply the rule above.5859## Phase 1: Detect the stack and the local rules60611. Run `<skill-directory>/scripts/detect-stack.sh` for the languages and tooling.622. Read repository guidance when present: `AGENTS.md`, `CLAUDE.md`, `CONTRIBUTING.md`, `README.md`.633. **Find any comment rule the project already enforces**, and defer to it:64 - JS/TS — `eslint` `jsdoc/*`, `require-jsdoc`, `valid-jsdoc`, `spaced-comment`, `no-inline-comments`;65 `.prettierrc` print width.66 - PHP — `phpcs.xml` / `.php-cs-fixer.php` (`Squiz.Commenting.*`, `phpdoc_*` fixers).67 - Python — `ruff` `D` rules, `pydocstyle` convention (`google` / `numpy` / `pep257`), `flake8-docstrings`.68 - Go — `golangci-lint` `godot`, `revive` `exported`, `godoclint`.69 - Rust — `#![warn(missing_docs)]`, `clippy::missing_docs_in_private_items`.70 Never fight a configured rule. If the house standard and the project's config disagree, the config wins and you71 say so in the summary.724. Note the **dominant existing convention** so rewritten comments do not look foreign beside their neighbours.7374## Phase 2: Inventory7576```bash77python3 <skill-directory>/scripts/scan-comments.py [paths...] [--cap N] [--json]78```7980The scan reports, it never gates — it always exits 0. Every line is a **candidate, not a verdict**: the heuristics81cannot read intent. Useful flags: `--rules DUP,DEAD` to focus a pass, `--summary-only` for a baseline,82`--json` for structured input, `--max-per-file N` to bound output.8384Rule IDs and their default dispositions:8586| ID | Means | Default |87|---|---|---|88| `DUP` | restates the code beside it | `DELETE` |89| `LEN` | inline comment over the cap | `SHORTEN` |90| `ORD` | docblock tags out of canonical order | `REORDER` |91| `POS` | detached from its declaration, or a trailing essay | `REPOSITION` |92| `TODO` | marker with no tracked issue | `ESCALATE` |93| `DEAD` | commented-out code | `DELETE` |94| `BANNER` | divider or section art | `DELETE` (labelled dividers: `SHORTEN`) |95| `CHANGELOG` | version history that belongs in git | `DELETE` |96| `GENERATED` | assistant attribution or filler | `DELETE` |97| `EMPTY` | docblock adding nothing beyond the signature | `DELETE` |98| `SECRET` | possible credential in a comment | `ESCALATE`, redacted |99100Three antipatterns are **not** scanner-detectable and are found only by reading: `STALE` (the comment contradicts101the code), `CONTAMINATE` (implementation detail in an interface comment), and `MISSING` (undocumented non-obvious102public API). Look for these in the files you open — a large scan result is not a substitute for reading.103104## Phase 3: Classify105106Open each flagged file and assign a disposition per candidate, using107[references/ANTIPATTERNS.md](references/ANTIPATTERNS.md):108109`DELETE` · `SHORTEN` · `REORDER` · `REPOSITION` · `ADD` · `KEEP` · `ESCALATE`110111Confirm the scanner's guess against the actual code — it does not know that a "duplicate" comment carries a *why*112clause, that a long comment encodes an algorithm's invariants, or that a commented-out block is a documented113example. Downgrade to `KEEP` freely and record the reason so the next run does not re-litigate it.114115Bias hard toward `KEEP` for: config-file comments (often the only documentation of an option), test files (a116comment naming the scenario earns its place), and anything referencing an external system you cannot verify.117118## Phase 4: Preview gate119120**No file is modified before this passes.** Present:1211221. Files to be touched and the count per rule ID.1232. The net effect — comments deleted, shortened, reordered, repositioned, added.1243. Two or three representative **before/after** pairs, including the most aggressive edit planned.1254. Everything being escalated rather than fixed (`TODO`, `STALE`, `SECRET`, unclear code).1265. Anything skipped and why (generated, vendored, linter-governed).127128For a scope over ~20 files, write this to `COMMENT_CLEANUP_PLAN.md` at the repository root and give the path129instead of flooding the chat. Then get explicit confirmation. The user may narrow the scope, drop a rule, or130change the stance — re-plan rather than proceeding partially.131132## Phase 5: Apply133134Work in **batches of related files**, not one sweeping pass, so a bad batch can be reverted without losing the good135ones. After each batch:1361371. Re-read what you changed and confirm every hunk is comment-only.1382. Keep the file's existing docblock dialect and alignment — you are normalizing order and length, not converting139 JSDoc to TSDoc or reflowing aligned PHPDoc columns.1403. Never leave a file mid-standard. If a docblock cannot be fixed without touching the signature, `ESCALATE` it and141 leave it untouched.142143Then verify the batch (Phase 6) before starting the next.144145## Phase 6: Verify146147After each batch, and again at the end:148149```bash150git diff --stat # scope check151git diff -U0 | grep -E '^[+-]' | grep -vE '^(\+\+\+|---)' # every changed line152python3 <skill-directory>/scripts/scan-comments.py [paths...] --summary-only153```1541551. **Read every changed line.** Each one must be either (a) wholly a comment or a blank line, or (b) a line whose156 **code portion is byte-identical** to its counterpart, differing only in a trailing comment that was removed or157 rewritten. Anything else — a renamed variable, a moved statement, a reformatted expression — violates the first158 non-negotiable rule. Revert it immediately.1592. Re-scan and confirm the counts moved in the expected direction, and that no new rule fired.1603. Run whatever the project already provides and is cheap: the linter, the type checker, and the focused tests for161 the touched files. A docblock edit can break a build — malformed C# XML docs, an unparseable rustdoc fence, a162 Go build constraint that lost its exact spacing. Do not claim a check passed if you did not run it.1634. If the project generates documentation (`typedoc`, `phpDocumentor`, `sphinx`, `cargo doc`, `godoc`), build it164 when that is cheap and confirm it still succeeds.165166## Phase 7: Summarize167168In chat, report concisely:169170- Files changed and the counts by disposition.171- What was **escalated** and needs a human: un-owned `TODO`s, comments that contradict their code, suspected172 secrets (redacted), and any place where the comment was only bad because the code is unclear.173- What was deliberately kept, and which project linter configuration you deferred to.174- The verification commands you ran and their results.175176Remind the user the whole sweep is one reviewable diff: `git diff` to inspect, `git checkout .` to undo.