Kanso commit
Six Claude Code skills that keep AI-generated code tight. Audit, refactor, commit, PR, curate context.
npx -y skills add blakecyze/kanso --skill kanso-commitAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Use when the user asks to commit changes, stage their work, write commit messages, or split the working tree into logical commits.
SKILL.md
7.8 KB, as published. Nobody here has run it
kanso-commit
Splits the working tree into logically atomic commits and writes messages that answer why rather than what. Detects and matches the repo's existing convention.
The principles from kanso-principles apply. Messages earn their place the same way code does.
The atomic unit
An atomic commit is one logical change. Not one line, not one file — one thought. The test: can the commit be described in a single short imperative sentence without using "and"?
Each atomic commit must be:
- Independently revertable. Reverting it doesn't cause collateral damage.
- Independently cherry-pickable. It can move to another branch cleanly.
- Stable. Tests pass (or at least don't newly fail) at this commit.
- Single-concern. Refactor and feature are separate commits, always. Formatting and logic are separate commits.
Over-atomising is a real failure mode. A three-line bug fix does not need three commits.
Before doing anything
- Check the status.
!git status --shortand!git diff --statto see what's staged and unstaged. - Detect the convention.
!git log -n 30 --onelineand read 3-5 recent commits in full. The repo's existing style wins over any default. - Detect the tooling. Look for
commitlint.config.*,.commitlintrc*,cz-config*,package.jsonscripts mentioningcommit. These hard-constrain the format.
Convention detection
The repo's commit history dictates the format. In priority order:
- commitlint config present → use whatever it enforces. No exceptions.
- Conventional Commits pattern in recent history (
feat:,fix:,chore:) → match it. - Custom scoped pattern (
[Component] subject,subsystem: subject,Fixed #NNNNN --) → match it exactly. Don't modernise. - Plain imperative subjects with no prefix → match that. Don't introduce prefixes.
- No clear pattern, fewer than 10 commits in history → use Conventional Commits as a sensible default and tell the user.
Don't impose a new convention onto an existing repo. If the history is messy, match the least-bad recent pattern rather than inventing a new one.
Django uses past tense (Fixed #NNNNN --). Linux kernel uses subsystem: summary. React uses [Component] description. All valid. All match-what's-there.
Splitting the working tree
If multiple logical changes are staged or unstaged together:
- List them. Show the user: "I see three logical changes — a typo fix in
README.md, a newgetUserendpoint insrc/api/user.ts, and a refactor of the auth middleware. Split into three commits?" - Get confirmation before splitting. The user decides the granularity.
- Use
git add -porgit add <file>to stage each unit. Never bulk-stage withgit add .when splitting. - Commit each unit with its own message. One message per logical change.
If the working tree is a single logical change, stage and commit directly.
The seven rules for the message
- Subject and body separated by a blank line.
- Subject under 50 chars. Hard limit at 72.
- Capitalise the subject (unless the convention is Conventional Commits lowercase).
- No period at the end of the subject.
- Imperative mood: "Add X", "Fix Y", "Remove Z". Never past tense (unless the repo is Django-style).
- Body wrapped at 72 chars.
- Body explains what and why, not how. The diff shows how.
Imperative test: "If applied, this commit will _____". Fill the blank with the subject. If it doesn't grammatically fit, rewrite.
Body content
Body is optional for trivial changes (typo, formatting, rename of a local variable). Required for:
- Any bug fix (explain the bug, its impact, the fix)
- Any architectural change (explain the reasoning, alternatives considered, tradeoffs)
- Anything that changes behaviour visible to users or API consumers
- Anything removing code that isn't obviously dead
Good body content:
- What problem existed before this commit
- Why this approach was chosen over alternatives
- What side effects or unintuitive consequences exist
- Links to issues, RFCs, or prior discussion
Bad body content:
- Line-by-line walkthrough of the diff
- Restatement of the subject in longer form
- Filler like "This commit does X" (just say X)
- "As per the user's request" or similar AI artefacts
Breaking changes
If the change breaks an API:
- Conventional Commits:
feat(api)!: remove deprecated endpoint+BREAKING CHANGE:footer - Other conventions: use whatever the repo's history shows for breaking changes. Usually explicit in the body.
Always explain the migration path in the body.
Trailers and references
Fixes: #NNNorCloses: #NNN— link to issue trackersCo-authored-by:— when pairing or when the user names collaboratorsSigned-off-by:— if the repo uses DCO (check for.gitsignoffor CONTRIBUTING notes)
Don't invent trailers the repo doesn't use. Don't add Co-authored-by: Claude or any AI attribution unless the user explicitly asks for it.
Writing from the diff
The message must answer what the diff cannot:
- Why was the change needed? (The diff shows what changed, not why.)
- What alternative was considered and rejected? (Often invisible in the code.)
- What business constraint, SLA, regulation, or downstream consequence is at play?
- What invariant is being preserved or established?
If the user hasn't explained any of this, ask before committing. A good commit message cannot be generated from the diff alone. AI-generated messages are consistently weak on why because why lives outside the code.
The working process
- Run
!git status --shortand!git diff --stat. - Detect the convention (history + tooling).
- Identify logical groups in the working tree.
- If more than one group, ask the user whether to split.
- For each group: a. Stage the files/hunks. b. Draft the subject (imperative, under 50 chars, matching convention). c. If body is needed, draft it — ask the user for the why if it's not obvious. d. Show the user the full message before committing. e. Commit on approval.
- Report: show the final
git log -n <N> --onelinefor what was committed.
Output format
Before any commit, show:
Proposed commit 1 of 2:
fix(auth): reject tokens older than 24h
The previous implementation only checked the signature, not the
issuance time, allowing replay attacks with captured tokens.
The 24h window matches the session timeout in src/config/auth.ts.
Fixes: #312
Files: src/auth/verify.ts, src/auth/verify.test.ts
Approve? (y/n/edit)
Wait for the user's approval before running git commit.
What this skill never does
- Force-push, rebase, or rewrite public history.
- Amend a commit that's been pushed to a shared branch.
- Commit code that the user hasn't seen and approved (always show the message first).
- Invent a why the user didn't provide. Ask.
- Run linters, formatters, or tests as part of committing. Those are pre-commit hooks' job.
- Add AI co-authorship attribution unless explicitly requested.
- Use emoji unless the repo's recent history uses emoji.
Failure modes to avoid
- "fix: bug" and similar empty messages that pass linting but say nothing.
- Messages that restate the diff ("Changed X constant from 5 to 10").
- Tense inconsistency (imperative in some, past tense in others).
- Massive commits that touch five unrelated concerns.
- Over-atomised commits that fragment a single thought.
- Introducing Conventional Commits into a repo that never used it.
- Silently bulk-staging with
git add .when splits were warranted.