engineering / Review & improve
lean-diff
Keep the net diff as small as possible whenever code is written, edited, or refactored — minimal targeted changes over rewrites, no drive-by reformatting or renaming, no unrequested abstractions, and a git diff review before calling any code change done.
Put it to work.
Ask your agent to use lean-diff, or let it load the skill when your task matches the description.
Use the lean-diff skill [path-or-diff-range]Invocation syntax varies by agent. Use the skill name in your prompt, or your agent’s supported slash command.
About this skill
Portable /lean-diff skill for keeping the net diff as small as possible on
every code change: targeted edits over rewrites, no drive-by reformatting or
renaming, no unrequested abstractions, and a git diff read-through before
calling any change done.
It applies automatically to any task that touches code, and is also directly invocable to audit and trim whatever is currently uncommitted.
Install
npx skills@latest add smeltery/skillsOr copy just this skill:
mkdir -p ~/.claude/skills/lean-diff
curl -fsSL https://raw.githubusercontent.com/smeltery/skills/main/skills/engineering/lean-diff/SKILL.md \
-o ~/.claude/skills/lean-diff/SKILL.mdUsage
/lean-diffNo arguments needed to audit the current uncommitted diff. As a standing discipline it applies to any coding task without being invoked explicitly.
The hook
This skill ships a PostToolUse hook (scripts/diff-stat.sh) that runs after
every Edit, Write, MultiEdit, or NotebookEdit and echoes the current
git diff --shortstat. It's a passive nudge, not a gate — it never blocks, it
just keeps the running diff size visible so scope creep gets caught early
instead of at final review.
Requirements
git
Files
SKILL.md— canonical skill definition consumed by the agent.hooks.json—PostToolUsehook spec wiringscripts/diff-stat.shto code-touching tool calls.scripts/diff-stat.sh— echoes the runninggit diff --shortstat; best-effort, never blocks.
Read the full SKILL.md
Lean Diff
Every extra line changed is a line a reviewer has to read, a line that can hide a regression, and a line that makes a revert harder. The smallest diff that fully satisfies the requirement is the correct diff — this skill is the habit of checking that before calling a change done.
This skill ships a PostToolUse hook (scripts/diff-stat.sh) that echoes the running git diff --shortstat after every Edit/Write/MultiEdit/NotebookEdit, so the running total stays visible without waiting for a final review. Use that number as a prompt to re-check scope, not as the goal itself — a genuinely large change should still have a large diff.
Before editing
- Identify the minimal set of lines that must change to satisfy the requirement. Don't rewrite a whole function or file when a targeted edit does the job.
- Reuse existing functions, types, and helpers instead of duplicating logic nearby.
- If the correct fix genuinely requires a large diff (a real migration, a wide rename across call sites, a requested refactor), say so up front and confirm scope rather than let it grow silently.
While editing
- Prefer patch-style edits over full-file rewrites — untouched lines should never enter the diff.
- Don't reformat, reflow, reorder imports/members, or rename anything outside what the task asked for, even if it looks nicer. That's a separate change.
- Don't add comments, config flags, defensive checks, tests, or abstractions the task doesn't call for.
- Delete dead code outright rather than commenting it out.
- Match the style of the surrounding code in the lines you do touch instead of imposing a new one.
Before calling it done
- Run
git diff --statfor the shape, thengit diffto read every changed hunk. - For each hunk, ask: is this line required by the task? If not, revert it.
- Watch specifically for: whitespace-only changes, files touched that aren't part of the task, variables renamed without need, added blank lines, reordered members.
- Report the net diff (files changed / insertions / deletions) in your summary so the size is visible at a glance.
Audit mode — invoked directly with no code change in flight
Review the current uncommitted diff end to end:
git statusandgit diff(staged and unstaged) to see the full scope.- Walk every hunk and flag lines that aren't load-bearing for the change's stated purpose — leftover debug output, unrelated formatting, unused variables, duplicated logic that could reuse something existing.
- Apply the trims directly, then re-run
git diff --statand report the before/after line counts.
When a larger diff is actually right
Don't fight the requirement. A genuine feature, a real cross-cutting rename, or an explicitly requested refactor will have a proportionally larger diff — the goal is zero unnecessary lines, not an arbitrarily small line count. If everything non-essential has been trimmed and the diff is still big, say so and explain why.
Related skills
simplify— a different axis, not a duplicate:simplifyruns after the diff is settled and improves the readability of the surviving lines (naming, comments, structure), and can add lines (a clarifying comment, a split function) if that's what readability needs.lean-diffruns throughout editing and cares only about size — whether a line needed to exist in the diff at all. Runlean-difffirst to keep the diff honest, thensimplifyto polish what's left.loc-budget— a repo-wide, standing concern (are existing files too big, is the directory structure too flat) enforced via CI gates.lean-diffis a per-change, in-the-moment discipline (is this diff bigger than it needs to be). A change can pass one and fail the other.workon— §3.5 implementation step and §4.3 reviewer-comment fixesworkon-event—create-prhandlerpr-monitor— review-thread fix handling
workon and workon-event also carry a numeric scope-budget gate (§3.4b) — a hard halt when a diff blows past a multiplier or absolute LOC floor set by /groom. That gate is a backstop against gross overrun, not a substitute for this skill: a diff can sail well under the gate and still carry drive-by reformatting, dead code, or an unrequested abstraction. Apply lean-diff throughout implementation; let the gate catch what it's there to catch.