decomment
Scope
Default: comments this branch added, nothing else.
base=$(git merge-base HEAD origin/HEAD 2>/dev/null || git merge-base HEAD origin/main)
git diff "$base"...HEAD # committed work on the branch
git diff; git diff --staged # plus anything still uncommitted
Only added (+) comment lines are in scope. Pre-existing comments stay unless the user
names them — someone thought about those, and you are not reading their context.
An argument narrows or moves the scope (a ref, a path); honour it and say what you covered.
Cut
- Restates the code.
// increment the counter over counter++; a docstring that
lists the parameters and their types and says nothing else.
- Narrates the change. "previously this used X", "now returns null instead of throwing",
"moved here from utils", "added after the incident". That is a commit message living in
the wrong file.
git log -S finds it forever; the reader of this line does not need it.
- Announces the obvious.
// constructor, // imports, // helper functions,
// early return, banner bars separating two-line blocks.
- Explains the language. What
??, useMemo, a context manager or a try/finally does.
- Reassures. "this is safe because we checked above" three lines under the check.
Trim
A comment is not atomic. The common shape after an agent writes it is one useful sentence
welded to one worthless one:
/**
* A session `uid` is `userId_projectId_sessionHash`. <- the domain format, unguessable: keep
* Returns the project id, or `undefined` when the uid <- the six lines below say this: cut
* does not have that shape.
*/
Cut the dead sentence, keep the live one, and delete only — never rewrite the words that
stay, never merge two sentences into a better one. If what survives no longer reads as a
sentence, keep the whole thing and say so in the report.
Two recurring dead openers worth naming:
- The restated signature. The first line of a doc block that says what the symbol's own
name says (
Temp session data is gone over classifyMissingSessionData).
- Diff voice. "say why here", "note that we now…", "this addresses the review comment".
A comment talks to whoever opens the file in a year, not to the reviewer of this PR.
Keep
- Why, not what — the reason a non-obvious choice was made, an invariant the types
cannot express, an ordering that looks arbitrary and is not.
- A trap. "looks idempotent, is not", "the API returns 200 on failure", a workaround
with the upstream issue or ticket it waits on.
- Anything the toolchain reads:
eslint-disable, @ts-expect-error, # type: ignore,
# noqa, #pragma, codegen markers, license headers. These are code wearing a comment's
clothes — deleting one changes behaviour.
- Public API docs in a codebase that documents its public surface, even when they read
as obvious. Follow the file's neighbours — including their length: a one-line convention
makes a six-line block on the symbol next door the thing that looks wrong.
- A measured number. "batching over 500 doubled p99" earns its line; "for performance"
does not.
- Anything you are unsure about. The user can always ask for a second, harsher pass.
How
- Read the diff, then read each candidate in its file, not in the hunk — a comment can
read as redundant in a diff and carry the file's only warning in place.
- List what you propose, grouped by file, each with the comment verbatim and one clause of
reasoning: cut whole, trim to the surviving sentences (quote what goes and what
stays), keep. Anything you nearly cut and kept goes in a short second list — that is
where the user corrects your taste.
- Apply after the user agrees: delete whole comment lines, trailing comments, and — for a
trim — the dead sentences inside a block. Deletion only: never reword what stays, never
touch code, never reflow a line you did not cut into. Mind comment-shaped text inside
strings and regexes.
- Verify: the repo's typecheck or lint on the touched files, then
git diff — every hunk
must be a deletion of comment lines and nothing else.
- Report the count per file and leave the commit to the user unless they asked otherwise.
Two agents chattering at each other produce the worst of this: each documents its own edit
for the other to read. When the diff is mostly such comments, say so plainly — the fix is a
line in that repo's CLAUDE.md, not a sweep every week.
1---2name: decomment3description: Strip the noise comments an AI agent left behind on a branch — the ones that restate the code line by line or narrate the change as a story ("was X, now Y", "added a guard here"). Git holds the history and the code holds the present; only genuinely non-obvious comments survive.4---56# decomment78## Scope910Default: comments **this branch added**, nothing else.1112```bash13base=$(git merge-base HEAD origin/HEAD 2>/dev/null || git merge-base HEAD origin/main)14git diff "$base"...HEAD # committed work on the branch15git diff; git diff --staged # plus anything still uncommitted16```1718Only added (`+`) comment lines are in scope. Pre-existing comments stay unless the user19names them — someone thought about those, and you are not reading their context.20An argument narrows or moves the scope (a ref, a path); honour it and say what you covered.2122## Cut2324- **Restates the code.** `// increment the counter` over `counter++`; a docstring that25 lists the parameters and their types and says nothing else.26- **Narrates the change.** "previously this used X", "now returns null instead of throwing",27 "moved here from utils", "added after the incident". That is a commit message living in28 the wrong file. `git log -S` finds it forever; the reader of this line does not need it.29- **Announces the obvious.** `// constructor`, `// imports`, `// helper functions`,30 `// early return`, banner bars separating two-line blocks.31- **Explains the language.** What `??`, `useMemo`, a context manager or a try/finally does.32- **Reassures.** "this is safe because we checked above" three lines under the check.3334## Trim3536A comment is not atomic. The common shape after an agent writes it is one useful sentence37welded to one worthless one:3839```40/**41 * A session `uid` is `userId_projectId_sessionHash`. <- the domain format, unguessable: keep42 * Returns the project id, or `undefined` when the uid <- the six lines below say this: cut43 * does not have that shape.44 */45```4647Cut the dead sentence, keep the live one, and **delete only** — never rewrite the words that48stay, never merge two sentences into a better one. If what survives no longer reads as a49sentence, keep the whole thing and say so in the report.5051Two recurring dead openers worth naming:5253- **The restated signature.** The first line of a doc block that says what the symbol's own54 name says (`Temp session data is gone` over `classifyMissingSessionData`).55- **Diff voice.** "say *why* here", "note that we now…", "this addresses the review comment".56 A comment talks to whoever opens the file in a year, not to the reviewer of this PR.5758## Keep5960- **Why, not what** — the reason a non-obvious choice was made, an invariant the types61 cannot express, an ordering that looks arbitrary and is not.62- **A trap.** "looks idempotent, is not", "the API returns 200 on failure", a workaround63 with the upstream issue or ticket it waits on.64- **Anything the toolchain reads**: `eslint-disable`, `@ts-expect-error`, `# type: ignore`,65 `# noqa`, `#pragma`, codegen markers, license headers. These are code wearing a comment's66 clothes — deleting one changes behaviour.67- **Public API docs** in a codebase that documents its public surface, even when they read68 as obvious. Follow the file's neighbours — including their length: a one-line convention69 makes a six-line block on the symbol next door the thing that looks wrong.70- **A measured number.** "batching over 500 doubled p99" earns its line; "for performance"71 does not.72- Anything you are unsure about. The user can always ask for a second, harsher pass.7374## How75761. Read the diff, then read each candidate **in its file**, not in the hunk — a comment can77 read as redundant in a diff and carry the file's only warning in place.782. List what you propose, grouped by file, each with the comment verbatim and one clause of79 reasoning: **cut** whole, **trim** to the surviving sentences (quote what goes and what80 stays), **keep**. Anything you nearly cut and kept goes in a short second list — that is81 where the user corrects your taste.823. Apply after the user agrees: delete whole comment lines, trailing comments, and — for a83 trim — the dead sentences inside a block. Deletion only: never reword what stays, never84 touch code, never reflow a line you did not cut into. Mind comment-shaped text inside85 strings and regexes.864. Verify: the repo's typecheck or lint on the touched files, then `git diff` — every hunk87 must be a deletion of comment lines and nothing else.885. Report the count per file and leave the commit to the user unless they asked otherwise.8990Two agents chattering at each other produce the worst of this: each documents its own edit91for the other to read. When the diff is mostly such comments, say so plainly — the fix is a92line in that repo's CLAUDE.md, not a sweep every week.