Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions .agents/skills/clean-comments/PERMISSIONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Permissions

What `clean-comments` touches, and what it never touches.

## Filesystem

**Reads**

- Source files inside the scope the user asked for, to find and judge comments.
- `.beads/config.yaml`, for the local issue prefix, so tracker IDs can be
recognised. Read only, never written.
- Agent instruction files (`CLAUDE.md`, `AGENTS.md`, `.cursorrules`, and the
rest of the list in `references/install.md`), during `install` only.

**Writes**

- Comment text in files inside the requested scope. Executable code is never
changed, and `scripts/verify.mjs` exists to check it.
- The fenced guidance block in agent instruction files — only under
`install-guidance.sh --write`, which is never the default. A dry run prints a
diff and writes nothing.
- Temporary files from `mktemp`, removed on the same run.

**Never**

- Files outside the requested scope, including vendored trees, build output,
and any file carrying a generated-code banner.
- `~/.ssh`, `~/.aws`, `~/.config`, keychains, environment files, or any path
outside the repository other than `mktemp` output.
- Its own files. The skill does not modify itself.

## Network

None. No script makes a network call, and none is needed: every check is local
and static. The GitHub Actions snippet in `references/check.md` runs in the
user's CI under their own credentials and is not invoked by the skill.

## Subprocesses

`git`, always through `execFileSync` with an argument array or a quoted shell
call, so no user value reaches a shell for interpretation; plus standard POSIX
text utilities (`grep`, `sed`, `awk`, `head`, `diff`, `cmp`, `mktemp`) in the
bash helpers.

Git commands used: `rev-parse`, `ls-files`, `diff`, `show`, `cat-file`,
`symbolic-ref`. All are read-only. The skill never commits, stages, pushes,
checks out, or resets, and it does not install hooks.

## Secrets

None read, none written, none needed.

## Tools required

`git` and `node` (18 or newer). Both are used locally.

## Blast radius

Worst case is a bad comment edit inside the scope the user asked for, which
`git diff` shows and `git checkout` reverts. The skill takes no action that
leaves the working tree, so nothing it does can reach a remote, a registry, or
another machine.
60 changes: 60 additions & 0 deletions .agents/skills/clean-comments/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
name: clean-comments
description: "Deletes agent commentary and rewrites what survives as one plain line. Use when cleaning comments in a diff or repo."
---

# Clean Comments

Agents comment too much. They restate the code, narrate their own edits, carry
issue IDs no reader can resolve, and spend three lines where none were needed.
This skill removes that layer and rewrites what survives as one plain line.

**Edit comment text only. Never change executable code.** If a comment can only
be fixed by changing the code, report it instead — see
[references/rules.md](references/rules.md).

```text
/clean-comments [command | path]
```

| Command | Use for | Read |
| --- | --- | --- |
| `[path]` | Clean the current diff, or a file or directory when given a path | [references/cleanup.md](references/cleanup.md) |
| `all` | Clean every source file in the repository | [references/cleanup.md](references/cleanup.md) |
| `check [scope]` | Report violations without editing, for CI or PR review | [references/check.md](references/check.md) |
| `install` | Write the comment rules into the project's agent files | [references/install.md](references/install.md) |

With no argument, clean the current diff. Treat an unrecognized argument as a
path. Never widen the scope the user asked for.

## Triage

Judge every comment in scope against this ladder and stop at the first match.
[references/rules.md](references/rules.md) holds the full test and examples for
each rung.

| # | The comment… | Do |
|---|---|---|
| 1 | Is a **directive** the toolchain reads — `eslint-disable`, `# noqa`, `//go:generate`, `# type: ignore` — or a license header, shebang, or generated-file marker | **Keep exactly as written** |
| 2 | Is a **doc comment on a public interface** stating a contract | **Keep the contract**, drop padding that repeats the signature. The one-line rule does not apply |
| 3 | Is **commented-out code** | **Delete** — version control already has it |
| 4 | **Restates the code** the next line or the function name already says | **Delete** |
| 5 | **Narrates an edit** — "added", "updated", "now handles", "refactored", "as requested" | **Delete**, keeping any reason it carried |
| 6 | **Names the agent or session** — "I've", "as an AI", "Claude", "per your request" | **Delete the reference**, keep the technical content |
| 7 | **Cites an unshared tracker** a reader of this repo cannot resolve | **Delete the ID**, keep the substance |
| 8 | **Marks unfinished work** | **Rewrite** to `TODO:`, `FIXME:`, `HACK:`, or `XXX:` so tooling can find it |
| 9 | **Explains** a constraint, bug fix, unidiomatic code, or business rule | **Keep**, rewritten to one line in the style of [references/ste.md](references/ste.md) |
| 10 | **You cannot tell what it means** | **Flag it, do not delete it.** An unclear comment usually marks unclear code, which is the human's call |

## Rules of thumb

- One line, almost always. A comment that needs a paragraph is usually a sign
the code needs work — report that rather than writing the paragraph.
- Say why, never what. The code already says what.
- Delete freely when the code is self-evident, and never when you are unsure.

## Validation

Run `bash tests/selftest.sh` after changing the bundled scripts. After any
cleanup, run `node scripts/verify.mjs`: it checks that only comment text
changed. Its failure is authoritative; its pass is strong evidence, not proof.
7 changes: 7 additions & 0 deletions .agents/skills/clean-comments/agents/openai.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
interface:
display_name: "Clean Comments"
short_description: "Delete agent commentary, keep the comments that earn their place"
default_prompt: "Use /clean-comments to clean comments in this diff, check without editing, or install the comment rules."

policy:
allow_implicit_invocation: true
17 changes: 17 additions & 0 deletions .agents/skills/clean-comments/assets/agent-guidance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
## Code comments

Write a comment only when the code cannot say it. Then write one line.

- Say why, not what. The code already says what.
- No edit narration: not "added", "updated", "now handles", "as requested".
- Never name the agent, the model, or the session. Attribution is git's job.
- No issue ID a reader of this repository cannot resolve — write the substance.
- Mark unfinished work `TODO:`, broken work `FIXME:`, a workaround `HACK:`.
- Keep: constraints, the reason for a bug fix, why unidiomatic code is
deliberate, business rules, and a link to the source of copied code.
- Delete: commented-out code, restatements of the next line, section banners,
and doc padding that repeats the signature.
- Plain English, active voice, twenty words or fewer.

If a comment needs a paragraph, the code usually needs the work instead. Say so
rather than writing the paragraph.
81 changes: 81 additions & 0 deletions .agents/skills/clean-comments/references/check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Checking without editing

`/clean-comments check [scope]` reports violations and changes nothing. Use it
in CI, in PR review, or before deciding whether a cleanup is worth running.

```bash
# tr|xargs -0 keeps filenames with spaces as one argument
bash scripts/scope.sh --branch | tr '\n' '\0' | xargs -0 -r node scripts/scan.mjs
bash scripts/scope.sh --branch | tr '\n' '\0' | xargs -0 -r node scripts/scan.mjs --ci
node scripts/scan.mjs --diff-only --base origin/main src/api/user.ts
```

`--ci` exits 1 only for `commented-code`, `agent-reference`, `edit-history`,
and `tracker-reference` — the four highest-confidence rules. `long-comment`,
`comment-block`, and `restates-name` always report and never fail, because
judging them needs the code around them.

## Why this never rewrites

A check that edits code during a commit or a push is a bad trade, and the skill
does not offer one:

- It rewrites code the author already reviewed, at the moment they have
stopped looking.
- A model in the loop makes it slow and non-deterministic, so the same commit
can produce different files twice.
- A comment that reads as noise sometimes carries the only record of a
constraint. That call needs a human, or at least a session where one is
present.

Flag in CI. Fix with `/clean-comments` while the author is still reading.

## Precision over recall

The scan flags patterns, not judgements. A check nobody trusts gets disabled
within a week, so keep it quiet: prefer missing a bad comment to failing a
build over a good one. If a pattern produces a false positive on your codebase,
narrow it rather than adding an ignore list.

## GitHub Actions

```yaml
name: comments
on: pull_request

jobs:
clean-comments:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Check comments on changed files
env:
SKILL: .claude/skills/clean-comments
run: |
bash "$SKILL/scripts/scope.sh" --base origin/${{ github.base_ref }} --branch \
| tr '\n' '\0' | xargs -0 -r node "$SKILL/scripts/scan.mjs" --ci
```

Set `SKILL` to wherever `npx skills add gpu-cli/skills --skill clean-comments`
put the skill. Drop `--ci` to report without failing the build, which is the
right first step in a repository that has never been cleaned.

## Local pre-push hook

A pre-push hook is the one place a local check belongs: it runs after the
author is done, and it only reports.

```bash
#!/usr/bin/env bash
# .git/hooks/pre-push
SKILL=.claude/skills/clean-comments
bash "$SKILL/scripts/scope.sh" --branch \
| tr '\n' '\0' | xargs -0 -r node "$SKILL/scripts/scan.mjs" || true
```

Leave the `|| true`. A comment finding is not a reason to block a push.
124 changes: 124 additions & 0 deletions .agents/skills/clean-comments/references/cleanup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Cleaning comments

The workflow behind `/clean-comments [path]` and `/clean-comments all`.

## 1. Resolve the scope

```bash
bash scripts/scope.sh # changed files: staged, unstaged, untracked
bash scripts/scope.sh --branch # everything this branch changed vs. its base
bash scripts/scope.sh src/api # a path, file or directory
bash scripts/scope.sh --all # every source file in the repository
```

The script prints one path per line. It excludes vendored trees, build output,
minified bundles, lockfiles, and files carrying a generated-code banner.

Default to the changed-files scope. Widen only when the user asked for it. In
the changed-files scope, judge only comments the diff touched — a comment that
was already in the file and is untouched by the diff is out of scope, however
bad it looks. Report the worst of those in Flagged rather than editing them.

If the scope resolves to nothing, say so and stop.

## 2. Find candidates

```bash
node scripts/scan.mjs <file>... # TSV findings on stdout
node scripts/scan.mjs --json <file>...
node scripts/scan.mjs --diff-only <file>... # only lines the working diff touched
```

Each finding is `file<TAB>line<TAB>rule<TAB>text`. The rules it detects
mechanically are `commented-code`, `agent-reference`, `edit-history`,
`tracker-reference`, `long-comment`, `comment-block`, and `restates-name`.

The scan is a filter, not a verdict. It finds high-signal patterns cheaply so
you read less; it cannot judge whether a comment explains something real. Two
consequences:

- Every finding still needs the triage ladder applied by reading the code.
- A comment the scan missed is still in scope. Read the diff or the file, not
just the scan output.

## 3. Read before editing

For each candidate, read enough surrounding code to answer one question: does
this comment tell the reader something the code does not? You cannot answer it
from the comment alone. Do not skip this for comments that look obviously
disposable — a line that reads like narration sometimes carries the only record
of a constraint.

## 4. Apply the ladder

Walk each comment down the triage ladder in `SKILL.md` and stop at the first
rung that matches. [rules.md](rules.md) has the full test for each rung, and
[ste.md](ste.md) has the style for anything you keep or rewrite.

Edit comment text only. Never change executable code, even to fix something
obvious that you notice on the way — note it in Flagged instead.

Delete a whole comment by deleting its lines, including the now-blank line if
one is left behind. Never leave an empty `//` or a bare `#` where a comment
was.

## 5. Verify

```bash
node scripts/verify.mjs # working tree vs. HEAD
node scripts/verify.mjs --base <ref> # vs. another ref
```

It strips comments from both versions of every changed file and compares what
is left. Identical means only comment text moved.

A failure means an edit changed code. Revert that file and redo it. Never
report a cleanup whose verification failed. Files the check could not compare
are listed as unchecked — say so in the report rather than folding them into
the pass.

The comment stripper is quote-aware but heuristic; it is a backstop against a
slipped edit, not a proof. A clean run does not excuse careless editing, and an
unexpected failure in a language with unusual comment syntax is worth reading
before you dismiss it.

## 6. Report

Show the user what changed before they commit. Group by outcome, not by file,
so the ratio is legible at a glance.

```markdown
## clean-comments — <scope>

Deleted 14 · Rewrote 6 · Kept 31 · Flagged 2

### Deleted
| Location | Comment | Rung |
| --- | --- | --- |
| `src/api/user.ts:42` | `// Loop through the users` | 4 restates the code |

### Rewritten
| Location | Before | After |
| --- | --- | --- |
| `src/cache.py:88` | `# We use a lock here to make sure...` | `# Lock: concurrent writers corrupt the cache.` |

### Flagged
| Location | Finding |
| --- | --- |
| `src/parse.go:19` | Comment contradicts the code: says "returns nil", returns an error. |
| `src/report.rs:204` | Needs four lines of comment to follow; consider splitting the function. |
```

Keep Flagged short and specific. It is the part a human must act on, and it is
where rules 2, 3, and 4 land: comments that excuse unclear code, comments you
could not make clear, and comments that confuse or contradict.

State the counts even when nothing changed. "Reviewed 31 comments in 8 files,
changed none" is a useful result and a common one in a well-kept repository.

## Scope reminders

- Cleaning `all` in a repository nobody has cleaned before produces a large
diff. Say so up front, and offer to go directory by directory instead.
- Never combine a cleanup with any other edit in the same commit. A
comment-only diff is reviewable at a glance; a mixed one is not.
Loading
Loading