diff-explainer
Skill44 starsChanged 4 months agoMITView on GitHub
Generates an interactive HTML report explaining a git diff — compact teaching glosses on the left, clickable diff lines on the right. Use this skill when the user wants to understand, walk through, annotate, or document a diff, commit, changeset, or PR. This is for explaining/teaching the changes, not for finding bugs. Trigger on phrases like "explain this diff", "walk me through this PR", "annotate these changes", "help me understand what changed", or "what's going on in this commit" — even when the user doesn't explicitly ask for an HTML report. Especially useful when onboarding to a new codebase or learning an unfamiliar framework.
2 other projects use a version of this.
Adopted by
Most recently taken up by
- thoughtbot/rails-consultanttheir own version, first seen Nov 8, 2026
- stevepolitodesign/skillstheir own version, first seen Oct 14, 2026
Other versions2 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 2
- thoughtbot/rails-consultant★ 29same name
- stevepolitodesign/skills★ 10same 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 repository7 other files it gives its agents.
- commit-messagesSkill
- defragSkill
- hierarchical-documentationSkill
- method-reviewSkill
- pentestSkill
- retroSkill
- structural-reviewSkill
The file
We split this skill into the rules it gives an agent
4 rules. About 0 are common, 3 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 24
Use --cached for staged changes, main..feature for branch comparisons, etc.
- Distinctive: line 40
Don't infer it from the project's stack, the diff's language, CLAUDE.md, or recent activity — the user might be reading this repo *because* …
- Distinctive: line 42
If you don't have positive background, ask: *"What languages/frameworks are you comfortable with, and which are you newer to?
- Distinctive: line 127
The test: if your gloss disappeared, would the reader miss any information that isn't already in the diff?
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Diff Explainer
Produces a two-column HTML report: compact glosses on the left, the diff on the right, with clickable entries that highlight the relevant lines. Your job is the judgment — what's worth a gloss, how to phrase it, which lines it ties to. A bundled script handles all HTML assembly.
The report is for a specific reader. A pattern that's invisible to a Java dev warrants a gloss for a Ruby dev. Calibrating to the reader (step 2) shapes everything that follows.
Workflow
build.py lives in this skill's scripts/ directory, but the commands below run from the user's working directory — so a relative scripts/build.py won't resolve. In each command, replace <skill-dir> with this skill's absolute base directory (shown to you when the skill loaded), e.g. ~/.claude/skills/diff-explainer/scripts/build.py. Spell out the full path every time rather than using a shell variable — shell state doesn't persist between separate command invocations.
1. Check the diff size
git diff --shortstatCheap up-front so you don't load a giant diff into context. If additions + removals exceed ~1500 lines, ask the user to scope down before going further — a report that big is expensive to generate and too dense to read. Suggest focusing on specific files or themes; the rest can render as panels with no commentary.
Use --cached for staged changes, main..feature for branch comparisons, etc.
2. Calibrate to the reader
Establish the reader's positive background — the languages/frameworks they're already comfortable with. Two things flow from this:
- What gets a gloss (the main thing). A pattern invisible to one reader is obvious to another. Skip constructs the reader already knows; explain the ones they don't.
- How glosses frame (secondary). When something needs explaining, anchor it in something familiar — "like a
Gemfile", "this is .NET's answer touseEffect".
Negative-only signals ("newer to .NET") aren't enough on their own — .NET is huge, and a Java dev needs different cards than a Ruby dev, even if both are new to it.
Where positive background can come from:
- A user memory naming it ("comfortable with Ruby/Elm/TypeScript, learning .NET")
- A direct statement this session
- Asking the user
Don't infer it from the project's stack, the diff's language, CLAUDE.md, or recent activity — the user might be reading this repo because they don't know it.
If you don't have positive background, ask: "What languages/frameworks are you comfortable with, and which are you newer to? I'll tune the commentary." Save the answer to user memory so future invocations don't re-ask.
3. See the diff with line numbers
git diff | python3 <skill-dir>/scripts/build.py showOutput is plain text — one line per diff line, prefixed with its data-n value:
FILE app/models/user.rb (modified)
1: @@ -1,5 +1,7 @@
2: class User < ApplicationRecord
3: + scope :active, -> { where(archived_at: nil) }Reference those numbers in your commentary's lines field. For context, also look at any new untracked files relevant to the change and skim a few comparable existing files to learn project conventions.
4. Write commentary.json
Save to commentary.json in the current directory. Schema:
{
"title": "feature/PR title",
"summary_what": "one-sentence description of what the change does",
"summary_why": "one-sentence description of why",
"files": [
{
"path": "src/Auth.cs",
"tag": "new",
"cards": [
{
"title": "Dependency injection",
"body": "Like Ruby's initializer, but the container instantiates this. Registered in <code>Module.cs</code>.",
"lines": "1-5"
}
]
}
]
}Field reference:
| Field | Values | Notes |
tag | new (green), mod (yellow), infra (purple), deleted (red), renamed (blue), binary (grey) | infra overrides for Terraform, Helm, .csproj, Autofac modules, etc. Omit a file and the script infers the tag from the diff. |
title | short topic name | Appears as the primary gloss title |
body | plain text or HTML | Plain text gets wrapped in <p>; for lists/code use inline HTML (<ul>, <code>) |
lines | "3" or "3,7-12" | Comma-separated data-n values; range syntax with - |
Skip files you have nothing useful to say about — they'll still render with no commentary.
5. Build the report
git diff | python3 <skill-dir>/scripts/build.py render commentary.jsonThe script writes a timestamped HTML file (e.g. diff-report-2026-05-26-143052.html), prints the path, and deletes commentary.json. Pass -o <path> to override.
Open it and share with the user:
open diff-report-<timestamp>.htmlOffer to adjust depth, add more files, or refocus on specific parts.
What makes good commentary
The report teaches a diff to someone unfamiliar with the language or framework. Two kinds of glosses earn their place:
Teaching — when the reader will encounter something they won't recognize. Explain what it is, anchor it in an analogy to something they do know, and name where it comes from: is this the language, the framework, or a pattern specific to this project? A newcomer can't tell these apart without being told, and mistaking a project convention for a universal practice is a common early error.
Contrast — when the code differs from what a newcomer might expect based on tutorials, documentation, or other files in this codebase. State the expectation explicitly before explaining the departure. "A standard Rails controller would call User.create(params) here..." The reader needs to know their expectation was reasonable before they can update it.
Skip a gloss if the construct would be familiar to someone from the reader's background, or if you'd just be narrating what the diff already shows.
A good gloss vs a bad gloss
The test: if your gloss disappeared, would the reader miss any information that isn't already in the diff? If no, don't write it.
(Examples below assume the reader is a JS/TS dev newer to Rails — adjust the cut for your reader.)
Example 1 — teaching a framework idiom
Bad (restates what the diff shows):
Adds an active scope
Defines a new scope on the
Usermodel that filters byarchived_at: nil.
Good (teaches the idiom):
ActiveRecord scopes
A
scopeis Rails-speak for a named, chainable filter on a model. CallingUser.activereturns a query equivalent toUser.where(archived_at: nil)— and because it's a relation, you can keep chaining:User.active.where(role: :admin). Think of it as a method-named query you can compose. You'll see scopes everywhere in this codebase.
The bad gloss just narrates the diff. The good gloss teaches the Rails-specific concept that a JS dev wouldn't pattern-match on, and points to the broader role of scopes in the codebase. That is the skill's job.
Example 2 — contrasting a project convention with the framework default
Bad (notes the change but leaves the reader unsure if it's right):
Uses a form object
The controller calls
UserForm.new(params).saveinstead ofUser.create(params).
Good (grounds the convention so the reader can follow it):
Form objects — this project's convention, not Rails default
A typical Rails controller would write
User.create(user_params)here. This codebase wraps that in a Form Object (UserForminapp/forms/user_form.rb), which combines validation, persistence, and after-save side-effects in one class. The closest JS analogy is a service class or use-case object — a wrapper that bundles validation, DB writes, and side-effects in one place instead of scattering them across the route handler. It's not a bug or a code smell — it's the project's chosen pattern, and you'll see it everywhere records are created. Follow the existing shape when adding new controllers:Form.new(params).savereturns true/false like an ActiveRecord save would.
The bad gloss flags the existence of UserForm without explaining anything. The good gloss tells the newcomer: (1) this isn't what Rails normally does, (2) it's a deliberate project choice and you'll see it everywhere, (3) here's the shape to follow. That's how onboarding diffs earn their keep.
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.

