Structured code reviews with severity-ranked findings and deep multi-agent mode. Use when performing a code review, auditing code quality, or critiquing PRs, MRs, or diffs.
文档
ia-simplifying-code
试用Simplifies, polishes, and declutters code without changing behavior. Use when asked to simplify, clean up, refactor, declutter, remove dead code or AI slop, or improve readability. For analysis-only reports without code changes, use code-simplicity-reviewer agent.
它能做什么
Simplifies, polishes, and declutters code without changing behavior. Use when asked to simplify, clean up, refactor, declutter, remove dead code or AI slop, or improve readability. For analysis-only reports without code changes, use code-simplicity-reviewer agent.
技能文档
Simplifying Code
Principles
| Principle | Rule |
|---|---|
| Preserve behavior | Output must do exactly what the input did -- no silent feature additions or removals. Specifically preserve: async/sync boundaries (do not convert sync to async or reverse), error propagation paths (do not alter strategy), logging/telemetry/guards/retries that encode operational intent, and domain-specific steps (do not collapse into generic helpers that hide intent). One carve-out: a shape that existed only in an earlier iteration of the current unshipped scope is not protected behavior. Verify it has no deployed, persisted, public, external, dependent-branch, or in-repo caller outside the resolved scope, and that every required caller update fits inside the edit boundary; otherwise keep the compatibility path. This narrow exemption is about shapes with provably zero consumers -- it never licenses removing a guard, which is governed by the evidence bar under AI Slop Removal |
| Explicit over clever | Prefer explicit variables over nested expressions. Readable beats compact |
| Simplicity over cleanliness | Prefer straightforward code over pattern-heavy "clean" code. Three similar lines beat a premature abstraction |
| Surgical changes | Touch only what needs simplifying. Match existing style, naming conventions, and formatting of the surrounding code |
| Surface assumptions | Before changing a block, identify what imports it, what it imports, and what tests cover it. Edit dependents in the same pass |
Changing an interface, exported name, persisted format, or path reaches past the import graph. Enumerate the producers, consumers, schemas, fixtures, generators, manifests, scripts and CI recipes, config references, and documents that carry the old identifier, and migrate them in the same pass. Close out by searching for the old identifier: zero hits, or one line accounting for each intentional remainder. Renames rot in the fixture holding the old key and the .env.example entry, neither of which any import graph contains. When the identifier is a public or exported API, Stop Conditions applies first -- confirm with the user, then enumerate; the sweep runs unprompted only for internal identifiers.
Process
- Read first -- understand the full file and its dependents before changing anything. Apply Chesterton's Fence: if you see code that looks unnecessary but don't understand why it's there, check
git blamebefore removing it. First understand the reason, then decide if the reason still applies. - Identify invariants -- what must stay the same? Public API, return types, side effects, error behavior
- Identify targets -- find the highest-impact simplification opportunities. Impact = readability and maintainability; prioritize: control flow -> naming -> duplication -> types (see Smell -> Fix table)
- Apply in order -- control flow → naming → duplication → data shaping → types. Structural changes first, cosmetic last
- Verify -- confirm no behavior change: tests pass, types check, imports resolve
- Pre-submit scope audit -- walk every changed line and ask "does the requested task explicitly require this line?" If no, revert it and list it as a follow-up under Residual Risks. Drive-by edits belong in a separate change, not the current patch. For the pre-edit complement on ambiguous-scope requests ("simplify my project"), see
ia-verification-before-completion's Scope Confirmation gate.
Smell → Fix
| Smell | Fix |
|---|---|
| Deep nesting (>2 levels) | Guard clauses with early returns |
| Long function (>20 lines) | Extract into named functions by responsibility |
| Too many parameters (>3) | Group into an options/config object |
| Duplicated block (3+ occurrences) | Extract shared function. Two copies = leave inline; wait for the third |
| Magic numbers/strings | Named constants |
| Complex conditional | Extract to descriptively-named boolean or function |
Boolean-returning if/else (each branch returns a literal True/False) | Collapse to the boolean expression itself: return a and b, not a branch per literal |
| Dense transform chain (3+ chained methods) | Break into named intermediates for debuggability |
| Dead code / unreachable branches | Delete entirely -- no commented-out code |
Unnecessary else after return | Remove else, dedent |
AI Slop Removal
When simplifying AI-generated code, specifically target:
- Redundant comments that restate the code (
// increment counterabovecounter++) -- delete them - Unnecessary defensive checks for conditions that cannot occur in context -- remove the guard. Where the guard, retry, workaround, or flag counters an external hazard (a harness default, an upstream bug, a race, a platform quirk), "cannot occur" needs evidence: demonstrate the hazard's precondition is present and handled. A green suite is not that evidence when the run may never have triggered the hazard at all -- absence of failure and absence of the hazard look identical from the outside. If the precondition cannot be reproduced, keep the code and record the gap. Guards against conditions the type system already excludes need no such proof, provided the type is enforced at that boundary rather than merely declared -- deserialized payloads, unchecked API responses, and anything reached through a cast or assertion do not qualify
- Gratuitous type casts (
as any,as unknown as T) -- fix the actual type or use a proper generic - Over-abstraction (factory for 2 objects, wrapper around a single call, util file with 1 function) -- inline the code
- Inconsistent style that drifts from the file's existing conventions -- match the file
- Placeholder stubs (
// ...,// rest of code,// similar to above,// continue pattern,// add more as needed) -- leave unsimplified code as-is rather than replacing it with stubs - Redundant error wrapping (
catch(e) { throw e; },catch(e) { throw new Error(e.message); }) that strips the original stack for no reason -- remove the try/catch entirely and let errors propagate - Verbose stdlib reimplementations (hand-rolled loops that replicate
array_filter,Array.from,Collection::pluck(),itertools) -- replace with the stdlib/framework one-liner, but verify edge-case parity first: empty input, null/None guard, no-match default, zero-value path. The one-liner can silently differ from the loop (an empty-input crash, a missing no-match default, lost ordering) -- a structurally cleaner version that changes behavior on an edge case is not a simplification - Hand-maintained guarantees the platform, framework, or a downstream layer already enforces (a manual retry wrapping a client that already retries, a hand-rolled TTL cache the ORM/query layer already provides, manual null-coalescing on a value the contract guarantees non-null) -- name the layer that owns the guarantee and what the code collapses to without it. Remove only when it preserves every output, error, side-effect, and ordering; cite the test or a direct comparison proving equivalence, since "it's already guaranteed" over-fires easily
- Copy-paste with variation -- before proposing a shared abstraction, check whether the duplicated construct can be eliminated by deriving it from an existing source of truth (a constant, an existing map, a generated value). Consolidate into a helper only when elimination isn't behavior-preserving and the duplication has already cleared the 3-occurrence gate (Smell → Fix); below that, leave it inline per Constraints
Stop Conditions
Stop and ask before proceeding when:
- Simplification requires changing a public API (function signatures, return types, exports)
- Behavior parity cannot be verified (no tests exist and behavior is non-obvious)
- Code is intentionally complex for domain reasons (performance-critical, protocol compliance)
- Scope implies a redesign rather than a simplification
Constraints
- Only simplify what was requested -- do not add features, expand scope, introduce new dependencies, or add speculative configurability or flexibility the request did not ask for
- Leave unchanged code untouched -- do not add comments, docstrings, or type annotations to lines that were not simplified
- Do not bundle unrelated cleanups into one patch -- each simplification should be a coherent, reviewable unit
- Do not introduce framework-wide patterns while simplifying a small local change
- Do not replace understandable duplication with opaque utility layers -- three similar lines are better than a premature abstraction
- Keep comments that explain intent, invariants, or non-obvious constraints. Remove comments that restate obvious code behavior.
- If a simplification would make the code harder to understand, skip it
- Watch for over-simplification: inlining too aggressively removes names that gave concepts meaning; combining unrelated logic into one function hides distinct responsibilities; removing abstractions that exist for testability breaks the test suite
- When unsure whether a block is dead code, ask instead of deleting
Verify
- Tests pass and types check after changes
- No behavior change (same inputs produce same outputs)
- Scope limited to requested files -- no drive-by cleanups
- Match test scope to the importer count surfaced in step 1 (Surface assumptions). Zero external importers: scoped tests on the changed paths. One or more external importers, or shared/utility code edited: run tests covering each importer. Run the full suite when the test runner has no path-scoping mechanism.
Orchestrator Mode (When Chained With Other Skills)
When this skill is invoked by an orchestrator that also runs ia-code-review, ia-writing-tests, or ia-verification-before-completion on the same scope, each sub-skill re-resolving scope independently wastes tokens and risks drift. Avoid this by resolving scope exactly once and passing a canonical block to every sub-skill.
Resolved scope format — the orchestrator builds this once, before dispatching any sub-skill:
## Resolved scope
Files:
- path/to/file-a.ts
- path/to/file-b.ts
Commit range: HEAD~3..HEAD (or "uncommitted")
Intent: [one-sentence description pulled from the user request or PR description]
Constraints:
- Preserve public API
- No behavior change
- [other constraints specific to this run]
Every chained sub-skill receives this block verbatim in its prompt and uses it as the source of truth — no re-running git diff --name-only, no re-parsing the user request, no independent scope resolution. Sub-skills accept --no-verify --no-report flags when chained so verification and reporting happen once at the end of the chain, not per-skill. The last sub-skill in the chain runs verification; the orchestrator trusts that result rather than re-verifying.
This prevents two failure modes: scope drift (sub-skill A simplifies one set of files, sub-skill B reviews a different set) and double work (every sub-skill rediscovers the same facts).
Integration
ia-code-simplicity-revieweragent -- analysis-only pass producing a simplification report (no code changes). Use before refactoring to identify targets.
Output
After simplifying, report:
- Scope touched: files and functions modified
- Key simplifications: what changed and why (one line each)
- Verification: tests pass, types check, no behavior change
- Residual risks: assumptions made, areas not touched that may need attention
相关技能
Manages project documentation: CLAUDE.md, AGENTS.md, README.md, CONTRIBUTING.md, DOCS.md. Use when asked to update, create, or init these context files. Not for general markdown editing.
Structured reasoning modifiers (/think, /verify, /adversarial, /edge, /confidence, /assumptions, etc.) to stress-test decisions, surface assumptions, or enum...
Coordinate multi-agent swarms for parallel and pipeline workflows. Use when coordinating multiple agents, running parallel reviews, building pipeline workflows, or implementing divide-and-conquer patterns with subagents.
Pre-implementation exploration: deep interview, approach comparison, design doc. Use when exploring a vague feature idea, clarifying ambiguous requirements, or comparing approaches before coding. For the full workflow, use the ia-brainstorm command (Claude Code).
审计并改写文本,去除其中的 AI 生成写作痕迹。