code-cleanup
Skill2 starsChanged 12 days agoMITView on GitHub
Clean up correct code after implementing: prune comments, apply the project's conventions, and simplify.
8 other projects use a version of this.
Adopted by
Most recently taken up by
- TidalPaladin/skillstheir own version, first seen Nov 8, 2026
- snappedly/skillstheir own version, first seen Oct 13, 2026
- CorySchulz/skillstheir own version, first seen Oct 13, 2026
- hempq/symfony-claude-codetheir own version, first seen Oct 9, 2026
- looties-io/looties-skillstheir own version, first seen Sep 24, 2026
- evanpersinger/claude-skillstheir own version, first seen Aug 16, 2026
Other versions8 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 8
- jenreh/appkit★ 4same name
- CorySchulz/skills★ 2same name
- snappedly/skills★ 2same name
- hempq/symfony-claude-code★ 1same name
- looties-io/looties-skills★ 1same name
- TidalPaladin/skills★ 1same name
- evanpersinger/claude-skills★ 0same name
- szmidtpiotr/ClaudeCode-Tools★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository40 other files it gives its agents.
- AGENTS.mdAGENTS.md
- skills/outsider/references/agents.mdAGENTS.md
- broSkill
- changes-reviewSkill
- ci-auditSkill
- code-to-specSkill
- commit-summarySkill
- consiliumSkill
- doSkill
- doubtSkill
- editor-configSkill
- explainSkill
- handoffSkill
- issue-analyzeSkill
- issue-flowSkill
- issue-triageSkill
- issue-writerSkill
- labels-syncSkill
- live-probeSkill
- markdown-writingSkill
- next-issueSkill
- npm-releaseSkill
- orchestrateSkill
- outsiderSkill
- package-auditSkill
- pr-reviewSkill
- pr-triageSkill
- react-auditSkill
- reflectSkill
- repo-hardeningSkill
- resolve-conflictsSkill
- security-auditSkill
- session-cleanupSkill
- sharpenSkill
- skill-auditSkill
- solve-issueSkill
- tests-auditSkill
- three-auditSkill
- tsconfig-auditSkill
- working-docsSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
14 rules. 0 are common, 12 are distinctive, and 2 are very specific to this project.
0
Common
Most of the 8 edited versions we read keep these.
12
Distinctive
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 42
Only the last row survives contact with the source.
- Very specific: line 64
Every question in this skill is written as AskUserQuestion options.
- Distinctive: line 76
Step 1: Find project guidelines: Check for comment/doc conventions in:
- Distinctive: line 108
Leave unrelated pre-existing comments elsewhere in the file alone — surgical.
- Distinctive: line 136
The project's own instructions outrank all of this: If CLAUDE.md, AGENTS.md, or a rules file found in Step 1 contradicts a reference, …
- Distinctive: line 142
Run every in-scope comment through this table.
- Distinctive: line 160
The test for the top two classes: read the code *without* the comment.
- Distinctive: line 217
Order: remove restatement → pull rationale and wider context out (collect for the report by destination) → strip external references → …
- Distinctive: line 238
When a cheap, local rename makes a comment unnecessary, do it and remove the comment.
- Very specific: line 249
Never remove @ts-ignore / @ts-expect-error without understanding why it's there.
- Distinctive: line 250
Never remove license headers or copyright notices.
- Distinctive: line 265
Not in scope: anything that changes behavior, anything that needs a test to prove it is safe, and anything the code is doing deliberately …
- Distinctive: line 280
Ask per Asking the User:
- Distinctive: line 357
Write it as prose that can carry that weight — the reason, not a label for it.
“Common” is measured: kept by at least 6 in 10 of the 8 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Code Cleanup
Post-implementation cleanup of code that is already correct. Prunes the comment noise an AI tends to add, applies the project's conventions, and simplifies what is needlessly complicated.
Local writes only. It edits files in the working tree and reports; it never stages, commits, or pushes, and it opens no issue and edits no PR.
Correctness is not this skill's job. It does not hunt for bugs, and it must not "fix" behavior
it thinks is wrong — that is /changes-review. Everything here preserves behavior.
Core Principle
A comment stays only if the code cannot carry it and no other artifact can either. The default for every comment in scope is delete. Being true is not the bar; a comment survives by earning its line, not by being defensible.
Ask where the sentence belongs before asking whether it is true. A comment competes with the code itself and with three other artifacts, and it loses to all four:
| What the sentence says | Where it belongs |
| What the code does | The code. Delete the comment. |
| Why it is built this way — the decision, the alternative rejected, the history | The commit message |
| What changed and what a reviewer should look at | The PR body |
| Work still owed, or context that outlives this change | An issue |
| What a reader of this file would get wrong without it | Here. Keep it, at one or two lines. |
Only the last row survives contact with the source. The rows above it are usually real content filed in the wrong place — surface the text in the report under the destination it belongs to, then take it out of the code.
Delete what does not earn its place; do not shorten to save space. A surviving comment must still read well and still be correct, and a compaction that drops a fact or leaves a half-sentence is a worse outcome than the verbose original it replaced.
Read Intent First
- A request about comments ("clean up the comments") → comment work is the whole job. Code changes are allowed only where a cheap rename removes the need for a comment.
- A general "clean this up" → run the full pass: comments, artifacts, conventions, simplify.
--comments-onlyforces the comment-focused mode regardless of phrasing, and never touches code. It disables, without exception: convention loading and fixes, the simplifier pass, rename-to-kill, and quick wins. Comments and docs only. It may still report what it would otherwise have done.--no-simplifyruns everything except applying the simplifier pass.--dry-runruns every phase and edits nothing — Phase 4 reports what would change.
Asking the User
Every question in this skill is written as AskUserQuestion options. Use that tool where
the host offers it, or the host's nearest structured-choice equivalent. Where the host has
neither, ask the same question in normal chat as a numbered list of 2–5 options —
recommended first, one short line of description each — and wait for the user to reply
with a number.
Workflow
Done means: every file in scope has been through Phase 3.5 and the Phase 4 report is printed.
Phase 1: Scope
Step 1: Find project guidelines. Check for comment/doc conventions in:
CLAUDE.md AGENTS.md .cursor/rules/*.md(c) docs/CONTRIBUTING.md docs/STYLE_GUIDE.md .github/CONTRIBUTING.mdHonor anything they say about comment style, JSDoc requirements, or section markers.
Step 2: Resolve the target. In priority order:
- Explicit argument — files, a directory, or a commit ref passed in → use exactly that.
- Uncommitted changes (default) — staged + unstaged:
git diff --name-only HEAD # tracked modifications (staged + unstaged) git ls-files --others --exclude-standard # new untracked files - Last commit — fall back here only when the working tree is clean:
git diff --name-only HEAD~1..HEAD
Announce which scope resolved (e.g. "Cleaning uncommitted changes: 4 files" or "Tree clean —
cleaning last commit 0cccf9d: 3 files").
Filter to code files (exclude *.md, *.json, *.lock, *.yaml, *.yml, dist/, build/,
*.min.js, *.map).
Step 3: Decide which comments are in scope. For diff/commit modes, pull the actual hunks
(git diff HEAD / git show) and consider:
- every comment added or modified by the change, plus
- pre-existing comments in changed files that relate to the changed code (e.g. a doc comment on a function whose body you just edited).
Leave unrelated pre-existing comments elsewhere in the file alone — surgical. For an explicitly named file, the whole file is in scope.
Open every file in the resolved list, in one batch, before classifying anything. A comment judged from a hunk alone is judged without the declaration it serves, which is exactly what Phase 2.5 needs.
Step 4: Load the conventions that apply. Skip entirely under --comments-only.
Always load references/conventions.md. It is the repo-adaptive half of the pass: read what
the project itself documents, detect the stack, and audit against both. It is what makes this work
on a repo the stack files below do not describe.
The seven stack files carry fixed conventions for one specific frontend stack. Each is gated on two conditions, and both must hold:
| Reference | Paths | Only when |
references/react.md | *.tsx | react in deps and React 19+ |
references/typescript.md | *.ts, *.tsx | typescript in deps, or tsconfig.json exists |
references/tailwind.md | *.tsx | tailwindcss or @tailwindcss/* in deps |
references/radix.md | *.tsx | radix-ui or @radix-ui/* in deps |
references/storybook.md | *.stories.tsx, .storybook/** | @storybook/* in deps, or .storybook/ exists |
references/frontend-structure.md | src/** (JS/TS only — never src/main/kotlin/**) | react or preact in deps |
references/kotlin.md | *.kt | .kt files present, or a kotlin(...) plugin in build.gradle.kts |
Check package.json before loading a stack file; if the dependency is absent, its rules are
not violations — a glob alone would rewrite h-4 w-4 to size-4 in a project with no Tailwind.
The project's own instructions outrank all of this. If CLAUDE.md, AGENTS.md, or a rules
file found in Step 1 contradicts a reference, follow the project. These are defaults for repos
that have not said otherwise — not a house style to write into someone else's codebase.
Phase 2: Classify Each Comment
Run every in-scope comment through this table. The first five classes are the AI-noise the skill exists to remove; a comment that matches none of them still has to earn its line under the Core Principle before it stays.
| Class | What it looks like | Action |
| Restates the code | One-liner narration (// increment counter) or a multi-line block describing how the mechanism works, step by step, that a reader gets from the code itself | Remove |
| Design rationale / history | "We do it this way because…", "Captured here because by the time X runs…", "chose this over Y", the story of the decision aimed at a PR reader | Remove from code; surface the text for the commit message |
| Wider context | Describes the change rather than the file: what a reviewer should check, what the surrounding refactor is for, what is deliberately out of scope | Remove from code; surface it for the PR body |
| Duplicate | Restates a mechanism a nearby comment already carries, from a second angle | Remove — keep the one authoritative statement (Phase 2.5) |
| External-context reference | // see #123, // added for PR #45, // per the linked issue, // fixes JIRA-88, // see STAGE3.md | Strip the reference. If the fact matters, state it inline so the file reads on its own; if it does not, delete the comment. Keep a link only where the context is genuinely unreachable otherwise — an upstream bug tracker for a workaround, a spec section, an RFC |
| Non-obvious behavior / gotcha / hack | Timing dependency, surprising side effect, workaround for an external bug, a constraint you cannot infer from the code | Keep — compact to 1–2 lines, drop the narration |
| Comment compensating for a bad name | The comment exists mainly to explain what a vague symbol means | Rename the symbol (see Phase 3); delete the comment. Under --comments-only, leave both and report the rename as a suggestion |
| Documentation (JSDoc / docstring / public API) | API-level doc humans rely on | Gentle, not exempt: correct inaccuracies and tighten — cut prose that re-spells the signature or the parameter names. Never gut what a caller cannot get from the type: what it does, what it throws, what it returns |
| AI implementation artifacts | // TODO: implement on done code, // Claude:/// AI:, // Add your code here, leftover console.log('DEBUG…'), commented-out alternatives with AI reasoning | Remove |
| Misleading / stale | Describes behavior the code no longer has, wrong names, outdated references | Fix if obvious, else flag |
| Markers | HACK FIXME XXX BUG TODO @ts-expect-error eslint-disable // region, license headers | Preserve (flag FIXME/HACK/TODO for review, don't delete) |
The test for the top two classes: read the code without the comment. If you still understand what it does, the comment was restating — remove it. If what you lose is why a decision was made rather than what the code does, that belongs in the commit message, not the source.
Prefix markers — // ! warning, // ? open question, // * divider — belong to the human,
never to you. Never author one; the user places them by hand. What to do with one that already
exists turns on whose line it is:
- In pre-existing code — preserve the prefix. The comment text is still judged like any other, but the marker is not yours to remove.
- On a line this change added — drop the prefix and judge the bare comment on its merits.
Exception: keep and match it where the project genuinely uses the convention —
CLAUDE.md,AGENTS.md, or a rules file documents it, or the surrounding untouched code is visibly written that way.
A // TODO: left on work this change just finished is an AI implementation artifact, and goes.
One the project or the user placed is a marker, and stays.
A kept comment stating a constraint the code cannot show — "keep in sync with X", "do not reorder" — stays; the report names the cheapest check that would enforce it (a type, a test, a lint rule) and never adds it. A suppression this change added that silences a correctness rule is reported, never edited.
Test files: a step-label comment that just narrates the next call (// focus the field) is
restatement — remove it. Keep one only where it conveys intent the calls don't (e.g. "most recent,
not stale"). The test name and assertions should carry the rest.
Print one line with the counts: Classified 41 comments: 26 remove, 4 compact, 2 rename, 9 keep.
Phase 2.5: Read the survivors together
Phase 2 judges one comment at a time, which is exactly how three individually defensible comments end up telling one story between them. Before applying anything, read the surviving set as a whole.
- One mechanism, one home. Where two or three comments each explain a part of the same mechanism, keep the explanation once — on the thing all of them exist to serve, usually the field or the declaration rather than any of the functions touching it — and delete the others. The remaining one may grow a line to absorb what was real in them.
- Contradiction. The same rule stated in opposite directions in two places means at least one is stale. Resolve it against the code; never keep both.
- Density is a signal, not a quota. Count added comment lines against added code lines. Past roughly a fifth, look again — not to hit a number, but because in practice that ratio has meant duplication rather than thoroughness. Report both counts either way.
Wording-level trimming cannot reach this: three shortened comments still leave three places to look and update, the failure this skill is most often called back to fix.
Phase 3: Apply
Auto-apply. Edit directly — uncommitted changes are the review surface, and git diff shows
exactly what changed. With --dry-run, produce the Phase 4 report and stop without editing.
Apply targeted edits per hunk; never rewrite a whole file. Removing six comments from a 300-line file by rewriting it picks up trailing-whitespace, quote-style, and line-ending churn nobody asked for, and buries the six real deletions in a diff no reviewer will read. One edit per comment, or per adjacent run of comments.
Order: remove restatement → pull rationale and wider context out (collect for the report by destination) → strip external references → de-duplicate per Phase 2.5 → compact gotchas → rename-to-kill → docs polish → AI artifacts & dead code → quick wins → conventions → flag the rest. The simplifier runs after all of it, in Phase 3.5.
Convention fixes. Apply only the references that passed both gates in Step 4. Fix what is
mechanical, local, and behavior-preserving — h-4 w-4 that should be size-4, an
interface X extends Y that should be an intersection type, a flat story name missing its group
prefix, a forwardRef that should be ref-as-prop if the project is on React 19.
Two things are never auto-applied:
- Anything that ripples past the files in scope — a rename with external callers, a file move
to satisfy the structure rules, a dependency swap like
@radix-ui/react-tabs→radix-ui. - Anything a reference marks report-only, and anything that changes behavior. Some rules in
references/conventions.mdandreferences/kotlin.mdare correctness or security checks; they produce findings, never edits.
Skipped entirely under --comments-only.
Renaming to kill a comment. Skipped under --comments-only — a rename is a code change.
When a cheap, local rename makes a comment unnecessary, do it and remove the comment. "Cheap" = a single symbol whose every reference you can update in the in-scope
files (a local variable, a private function). Anything heavier — splitting a function into
utilities, reshaping control flow, renaming an exported symbol with external callers — is not
applied; record it as a suggestion in the report instead.
Quick wins (auto-fix if truly trivial). Skipped under --comments-only. An obvious missing
return type TS already infers, or an unused import your comment removal orphaned. Leave an
eslint-disable alone unless you can actually run the linter and confirm the rule passes —
guessing that a suppression is stale is how a suppression becomes a build failure.
Safety rules:
- Never remove
@ts-ignore/@ts-expect-errorwithout understanding why it's there. - Never remove license headers or copyright notices.
Print one line with the counts: Applied across 4 files: 26 comments removed, 4 compacted, 2 renames,
3 convention fixes.
Phase 3.5: Simplifier pass
Skipped entirely under --comments-only. Under --no-simplify and --dry-run the analysis still
runs — the suggestions go in the report and nothing is written.
Simplify code that is more complicated than it needs to be, preserving behavior exactly: collapse a nested conditional into a guard clause, replace a hand-rolled loop with the obvious built-in, drop an indirection with one caller, delete a branch that cannot be reached, unwrap a wrapper that only forwards. Small, local, provable.
Not in scope: anything that changes behavior, anything that needs a test to prove it is safe, and anything the code is doing deliberately for a reason you cannot see. When in doubt, it is a suggestion, not an edit.
Whether to ask first turns on one observable signal: did the invocation carry anything beyond the bare skill name?
- Any argument, flag, or instruction — apply without asking. A scope, a flag, "simplify the parser", "clean this up and tidy the helpers".
- Nothing at all — ask before applying. A bare
/code-cleanup, or a bare "clean up the changes" with no further direction.
A skill calling this one must pass at least one argument — a scope, or --no-simplify to turn
the pass off — or it is treated as a bare invocation and may block on a prompt.
Ask per Asking the User:
Apply the simplifications(Recommended) — N refactors, all behavior-preservingReport them only— list them under Suggested refactors, change nothing
Declined, or the host cannot prompt at all → apply nothing and list them under Suggested
refactors (not applied). Under --comments-only that section carries only the renames and
convention fixes it would otherwise have applied.
Print one line with the counts: Simplifier: 5 candidates, 3 applied, 2 suggested.
Phase 4: Report
## Cleanup: [N files, M comments touched]
Scope: uncommitted changes (4 files)
Comment lines: 64 → 28 · added code lines: 201
### Removed
- [count] restated / how-it-works comments
- [count] duplicate explanations, folded into the one that stays
- [count] external references (issue / PR / doc numbers) stripped
- [count] AI artifacts
- [count] dead commented code
### Compacted
- [count] gotcha comments shortened
### Renamed (to remove a comment)
- `activeDataContext` → `focusedDataContext` (file.ts)
### Docs
- [count] doc comments corrected / tightened
### Conventions applied
- `Button.tsx:12` forwardRef → ref-as-prop (react.md)
- `Card.tsx:30` `h-4 w-4` → `size-4` (tailwind.md)
### Simplified
- `parse.ts:44` nested conditional → guard clause
- `index.ts:8` removed a wrapper that only forwarded to `format()`
### Belongs in the commit message
> The seed is captured at the toggle's pointer-down because the click blurs the
> field before the open handler runs. Clearing context on close is the plugin's job.
### Belongs in the PR body
- The change is scoped to the `Map` branch of the converter; the surrounding conversion path
is deliberately untouched.
### Belongs in an issue
- `parse.ts` still handles the legacy `v1` envelope and nothing in this change needs it —
worth an issue, not a comment.
### Quick wins
- `util.ts:3` removed an import orphaned by a comment deletion
### Suggested refactors (not applied)
Everything analysed but not edited lands here: declined simplifications, renames with callers
outside scope, convention fixes that ripple past the files in scope, and file moves the structure
rules imply.
- `file.ts:120` handleDataActivePath does two things — consider splitting focus-tracking from context-push
- `Card.tsx` structure rules put this in `components/card/` — a move, so not applied
### Flagged for review
- `file.ts:456` FIXME left in place — needs attention
- `file.ts:88` comment claims X but code does Y
- `status.ts:9` "keep in sync with LABELS" — a `Record<Status, string>` type would enforce it
- `api.ts:31` added `eslint-disable no-floating-promises` — silences a correctness ruleThe three Belongs in sections carry the text taken out of the source, filed where it should have gone. They are inputs to those artifacts, not blocks to be pasted at the end of one.
Commit message is the important one: it answers why the code is built the way it is, which is the first thing a body has to establish, so the commit writer folds it into that opening paragraph. Write it as prose that can carry that weight — the reason, not a label for it.
PR body and an issue are handed to whoever writes them. Omit a section that has nothing in it rather than printing an empty heading.
Then this skill stops; a calling skill goes on. Do not stage, do not commit, do not open the issue you just filed text for, and do not start a second cleanup pass over the same files.
Examples
One mechanism in three places → one home
Each of these is defensible alone, which is why a per-comment pass keeps all three. Together they are one story told in fragments, and the reader has three places to look and three to update.
// Before — three adjacent declarations, three angles on the same mechanism
// Set when a modifier rebuilds its own output, so the target's render is dead work.
suppressRender: boolean;
// A modifier rebuilds from `expression` plus its own `renderDice`, so rendering
// the target first produces a string nobody reads.
function createDiscardedRenderContext() { ... }
// Every pool render routes through here, including the suppressed ones, which is
// why the guard lives at the call site rather than in each wrapper.
function renderedPool() { ... }
// After — the whole mechanism sits once, on the field both functions serve
// Set when a modifier rebuilds its own output; the target's render is then dead work.
// Every pool render routes through `renderedPool`, including suppressed ones.
suppressRender: boolean;Design rationale → commit message
// Before
// CS pushes the focused data field to the operator as its mention target — but
// only while the dialog is open. While closed the field is remembered so the
// toolbar can seed the dialog on open. The seed is captured at pointer-down
// because clicking the toggle blurs the field before the open handler runs.
// Clearing context on close is the plugin's responsibility.
// After (a short orienting note stays; the decision story goes to the commit)
// Focused field is pushed as context only while the dialog is open;
// while closed it is remembered as a seed for the next open.Report → Belongs in the commit message: "Seed captured at pointer-down because the click blurs the field before the open handler runs; clearing on close is the plugin's job."
Five more — restated mechanism, gotcha compaction, rename-to-kill, a transformation carried by its
example, and an external reference — are in references/examples.md.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

