From clean-commits
Organizes all local git changes into clean, logical conventional commits grouped by concern. This skill should be used when there are uncommitted local changes that need to be committed following the project's conventional commit format. Dynamically reads commit scopes and types from config files, groups changes by purpose, and creates commits in dependency order using hunk-level staging when needed.
How this skill is triggered — by the user, by Claude, or both
Slash command
/clean-commits:clean-commitsThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Analyze all local git changes, group them into logical conventional commits by concern, and create them in the right order — respecting the project's commit types, scopes, and pre-commit hooks. Use hunk-level staging when a single file contains changes belonging to different commit groups.
Analyze all local git changes, group them into logical conventional commits by concern, and create them in the right order — respecting the project's commit types, scopes, and pre-commit hooks. Use hunk-level staging when a single file contains changes belonging to different commit groups.
--no-verify — pre-commit hooks must run and pass for every commitgit add . or git add -A — always stage specific files or hunksgit status after all commits to confirm nothing was missedRead the project's conventional commit configuration to extract the allowed types and scopes. Check these sources in priority order — stop at the first one that provides scopes:
# Check for conventional-commit-scopes.cjs (exports a plain string array)
Format: module.exports = ['app', 'providers', 'auth', 'ui', ...]
Read the file directly — the exported array IS the scope list.
# Check for .cz-config.cjs
Format: scopes: [{ name: 'app' }, { name: 'ui' }, ...]
Extract the name values from the scopes array. Also check the types array for allowed commit types. Note: some repos import scopes from conventional-commit-scopes.cjs via require() — in that case, read the referenced file instead.
# Check for commitlint.config.ts, commitlint.config.js, or commitlint.config.cjs
Format: 'scope-enum': [2, 'always', ['app', 'ui', 'deps', ...]]
Extract the string array from the scope-enum rule (third element of the tuple). Note: some configs import scopes via require('./conventional-commit-scopes.cjs') — in that case, read the referenced file instead.
If no config is found, use standard conventional commit types (feat, fix, chore, refactor, docs, test, perf, build, ci, style, revert) with no scope restriction.
After extraction, print the discovered scopes to confirm before proceeding:
Discovered scopes: app, providers, auth, tray, ui, settings, cli, ci, deps, docs, config
Use ONLY these scopes in commit messages. If a change doesn't fit any configured scope, omit the scope rather than inventing one (unless allowCustomScopes: true is set in .cz-config.cjs).
Run these commands in parallel to understand the full picture:
git status # overview of staged, unstaged, untracked
git diff --stat # summary of unstaged changes
git diff --cached --stat # summary of staged changes
git log --oneline -5 # recent commit style reference
Then inspect actual diffs to understand the nature of changes. For large changesets, use git diff --stat to prioritize, then read diffs selectively.
Key question for each changed file: What is the purpose of this change — new feature, bug fix, formatting, config, CI, docs, deps, refactoring?
Key question for mixed-purpose files: Does this file contain hunks belonging to different commit groups? If yes, mark it for hunk-level staging.
Assign each changed file (or hunk) to a logical commit group. Common groupings:
| Group | Type | Scope | Examples |
|---|---|---|---|
| Build/tooling config | chore | config | package.json scripts, tsconfig, eslint, vite, vitest, .gitignore, hooks |
| Dependencies | chore or fix | deps | package.json version bumps, lock files |
| CI/CD | ci | ci | .github/workflows/, .github/actions/ |
| Code formatting | style | varies | cargo fmt, prettier-only changes |
| Refactoring | refactor | varies | structural improvements, let chains, deduplication |
| Bug fixes | fix | varies | behavioral corrections |
| New features | feat | varies | new functionality |
| Tests | test | varies | new or updated test files |
| Documentation | docs | docs | README, DEVELOPMENT.md, CLAUDE.md, AGENTS.md |
Grouping principles:
Order commits to avoid pre-commit hook failures and maintain logical progression:
If a commit would fail the pre-commit hook because a later commit fixes the issue, reorder to commit the fix first.
For each group, in the determined order:
git reset HEAD (only if prior staging exists)git commit -m "$(cat <<'EOF'
type(scope): concise imperative description
Optional body explaining why, not what. Keep to 1-2 sentences
when the diff isn't self-explanatory.
Co-Authored-By: Claude <[email protected]>
EOF
)"
When all changes in a file belong to the same commit group, stage the entire file:
git add file1.rs file2.ts file3.json
When a file contains changes belonging to different commit groups, use patch-based staging to split them precisely. This avoids including unrelated changes in a commit.
Method: Extract and apply specific hunks
# 1. Extract the full diff for the file
git diff path/to/file.ts > /tmp/full.patch
# 2. Edit the patch to keep only the hunks for this commit group
# Remove unwanted @@ hunk sections, keeping the file header intact
# 3. Stage only those hunks
git apply --cached /tmp/partial.patch
# 4. Verify what was staged vs what remains
git diff --cached -- path/to/file.ts # staged hunks
git diff -- path/to/file.ts # remaining hunks
When to use hunk staging:
When NOT to use hunk staging (prefer whole-file):
Practical shortcut: If only a few lines need exclusion, consider staging the whole file and noting the mixed nature in the commit body rather than splitting a trivial diff.
type(scope): imperative description (max ~72 chars)If a commit fails due to the pre-commit hook:
After all commits are created:
git status # must show clean working tree
git log --oneline -N # show the N new commits for review
Report the final commit list to the user with a summary table.
git add for both the old and new paths so git detects the rename. Group with the commit that explains why the rename happened.git reset HEAD first to start from a clean staging area.See references/commit-examples.md for real-world examples.
npx claudepluginhub dsebastien/claude-code-clean-commitsGroups unstaged changes into atomic commits by logical concern, matching repo commit style. Invoked via /commit or when user asks to commit.
Executes git commits with conventional commit message analysis, intelligent staging, and message generation. Use when asked to commit changes or when /commit is invoked.
Groups uncommitted git changes (staged/unstaged/untracked) into atomic commits by logical purpose and generates conventional commit messages with bodies. Use for splitting multiple changes or 'smart commit' requests.