From smart-commit
Use when committing changes to Git. Triggers when the user says "commit", "save my changes", "create a commit", uses /commit, or any variation of wanting to commit code to the repository. Also use this skill after completing a coding task when the user asks to commit the result. When changes are already staged, it commits them directly. When nothing is staged, it analyzes all tracked unstaged changes, decomposes them into logical groups if needed, and commits each group separately with user approval. This skill does not amend commits, push, or perform any other git operations beyond staging and committing.
How this skill is triggered — by the user, by Claude, or both
Slash command
/smart-commit:commitThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Create well-formed conventional commit messages and commit changes after user approval. Supports two workflows depending on whether changes are already staged.
Create well-formed conventional commit messages and commit changes after user approval. Supports two workflows depending on whether changes are already staged.
Before analyzing changes, check the current conversation context for a previously planned but uncommitted group list (from a prior /commit invocation in the same conversation where only some groups were committed).
If a previous plan with remaining groups exists:
git diff --name-only and git diff --cached --name-only to get the current set of modified files.This avoids redundant re-analysis when the user commits groups incrementally across multiple /commit invocations in the same conversation.
Run git diff --cached --stat and git diff --stat to check for both staged and unstaged changes.
The user has intentionally staged specific files. Respect that intent and commit only what's staged — but large changesets still need decomposition.
Run these commands in parallel:
git diff --cached — the staged changesgit diff --cached --stat — summary of files changedgit branch --show-current — current branch name (for issue number)After gathering context, check the size of the staged changeset. If the staged changes exceed 200 total diff lines or touch more than 10 files, the changeset likely serves multiple purposes and should be decomposed into smaller commits — even though the user staged everything together.
When this threshold is exceeded:
When the changeset is small enough for a single commit, proceed to A.2.
The branch name contains the issue/ticket number. Extract it:
task/AM-4256-some-feature → issue #AM-4256feat/PROJ-123-thing → issue #PROJ-123fix/456-bug → issue #456Look for a Jira-style key first ([A-Z]+-\d+), then fall back to the first numeric segment. Prefix the result with #. If nothing is found, ask the user for the issue reference or omit the footer.
Follow the Conventional Commits format described in the Commit Message Format section below.
Present the full commit message inside a box-drawing frame (see Commit Message Presentation) and ask for confirmation. Wait for explicit approval before committing.
Once approved, commit using a heredoc:
git commit -m "$(cat <<'EOF'
<the approved message>
EOF
)"
After the commit succeeds, show the resulting commit hash with git log --oneline -1.
Both staged and unstaged tracked changes exist. Treat them as a single combined pool and decompose into logical groups. The fact that some files were pre-staged does not give them special treatment — grouping is based purely on logical purpose.
Run in parallel:
git diff --cached — staged diff contentgit diff --cached --name-only — staged file listgit diff — unstaged diff contentgit diff --name-only — unstaged tracked file listgit branch --show-current — for issue number extractiongit status --short — to detect untracked filesIf untracked files exist, mention them once: "Note: N untracked file(s) were excluded from analysis." Do not include untracked files in any analysis or staging.
Same as Path A — parse branch name for the first numeric segment.
Combine all changes (staged + unstaged) into a single pool. Apply the same decomposition logic as Path B (B.3) — group by purpose, check size, split if needed. Mark each group's files with their current git state (staged vs unstaged) so the execution step knows what to do.
Same format as Path B (B.4), including file count and line stats per group, the option to commit only specific groups with automatic re-staging of remaining groups' files. Additionally, annotate each group to indicate which files are currently staged vs unstaged, so the user understands the staging area will be adjusted.
Critical: reset the staging area before each group. Because some files may already be staged from a previous group or from the user's original staging, always start each group with a clean slate:
git reset HEAD (soft reset, unstages everything without losing changes)git add <file1> <file2> ...git log --oneline -1If the user chooses to commit only specific groups (e.g., "commit Group 2 for now"), still clear the staging area first so that files from other groups don't leak into the commit.
For stub handling and hunk-level splitting, follow the same rules as Path B (B.5).
Hook failure handling: Same as Path B — report failure, help fix, re-stage, create a new commit (never amend).
Same as Path B (B.5a) — after committing fewer than all groups, collect file lists from uncommitted groups, re-stage them with git add, and confirm to the user.
Same as Path B (B.6), including the partial commit summary format when not all groups are committed.
The user invoked /commit but hasn't staged anything. Analyze all tracked unstaged changes and propose how to commit them.
Run in parallel:
git diff --name-only — unstaged tracked file listgit diff — full unstaged diff contentgit branch --show-current — for issue number extractiongit status --short — to detect untracked filesIf no tracked changes exist either → tell the user there are no changes and stop.
If untracked files exist, mention them once: "Note: N untracked file(s) were excluded from analysis." Do not include untracked files in any analysis or staging.
Same as Path A — parse branch name for the first numeric segment.
Read the diffs and determine whether all unstaged tracked changes serve a single logical purpose or multiple distinct purposes.
Pass 1 — By purpose: Separate changes that serve different logical goals into distinct groups. A bug fix and a new feature are always separate commits. Unrelated style/cleanup changes get their own group when they are not trivially small relative to the main change.
Watch for "introduce vs. consume" — a common pattern where changes look like one purpose but are actually two sequential steps. If the changeset both (a) adds a new capability to a contract/interface and its implementations, and (b) uses that capability in a higher layer, those are distinct purposes: "enrich the domain model" and "use the enriched model." The enrichment is independently reviewable and mergeable; the consumer depends on it, not vice versa. Treat them as separate groups even when they serve the same feature.
This pattern appears in multiple forms — catch all of them during Pass 1, not as an afterthought during size checks:
Pass 2 — By size: Evaluate each group's diff size.
| Size | Action |
|---|---|
| ≤200 lines | Target. Keep as one commit. |
| 201–400 lines | Split if a clean boundary exists; justify keeping together if not. |
| >400 lines | Must split — see exception below for uniform changes. |
The >400-line rule is strict. Do not present a group at or above 400 lines and wait for the user to request a split — apply the split proactively during analysis. If a group lands in the 380–400 range after Pass 1, look for a clean split boundary and prefer splitting. The goal is that every group presented to the user is already within limits.
Soft file-count limit: Aim for ~10 files per commit. When a group exceeds this, look for a clean split boundary. Waive the limit when files are tightly coupled and splitting would break CI (e.g., a required parameter change that touches the command, its handler, and all callers).
Pass 3 — CI safety: After grouping, validate that each commit can pass CI independently (static analysis, type checks, tests). When a split would break CI, resolve it using one of these strategies (in order of preference):
Introduce without consuming. The preferred approach. In commit N, add the new class/method/parameter AND update all call sites to pass it — but don't use it internally yet. In commit N+1, implement the logic that consumes it. This preserves the clean split while keeping CI green. Examples:
Merge groups. When "introduce without consuming" isn't practical (e.g., the split boundary doesn't allow it, or the groups are small enough that merging stays within file/line limits), merge the dependent groups into one commit.
The goal is: every commit in the sequence must pass CI on its own. Never leave a commit where a required parameter has no callers, a type reference is unresolved, or a test fails.
The >400-line split rule does not apply when every file in the group receives the same structural transformation — a single repetitive operation applied uniformly across the codebase.
Valid examples:
Requirements to qualify:
refactor(shared): rename MapId::getValue to MapId::toString)When these conditions are met, the entire change stays in one commit regardless of line count.
Follow the hexagonal architecture's dependency direction. Each sub-commit builds on the previous:
Tests go with the layer they test.
When a size-driven split creates a dependency gap between commits, temporary stubs bridge the gap:
// @TODO: To be implemented comment. The next commit removes the stub and wires in the real code.No file modification without user approval. The decomposition plan must detail exactly what stubs will be inserted and where. The user must explicitly approve the plan before any source file is touched.
Stubs are transient. After the full commit sequence completes, no stubs remain — the final commit restores all real code.
Do not split for the sake of architectural purity:
However, do split when the domain change is independently meaningful even if small:
When a single file contains changes of different categories (e.g., domain logic + unrelated style fixes), the analysis separates them. A file may contribute changes to multiple commit groups.
When this happens: temporarily revert the unrelated changes in the file (keeping only the changes for the current group), stage the file, commit, then restore all changes for subsequent groups. This uses the same stub/restore mechanism.
Unrelated improvements, style fixes, or minor cleanups must not be bundled into a feature or bugfix commit unless they are trivially small relative to the main change.
Single group: Present the commit message inside a box-drawing frame (see Commit Message Presentation) along with the file list. Ask to proceed.
Multiple groups: Present a numbered plan where each group shows file count, line stats (from git diff --stat for the group's files), and commit message inside a box-drawing frame:
I've identified N logical groups in your changes:
Group 1 (2 files, +142 lines):
╔════════════════════════════════════════════════════════╗
║ feature(map): add polygon validation service ║
╚════════════════════════════════════════════════════════╝
Files:
- src/Module/Map/Domain/Service/PolygonValidator.php
- tests/Module/Map/Domain/Service/PolygonValidatorTest.php
Group 2 (1 file, +12 -8 lines):
╔════════════════════════════════════════════════════════╗
║ fix(websocket): correct heartbeat interval ║
╚════════════════════════════════════════════════════════╝
Files:
- src/Module/Websocket/Infrastructure/Server.php
Group 3 (2 files, +3 -1 lines):
╔════════════════════════════════════════════════════════╗
║ chore(docker): update PHP base image tag ║
╚════════════════════════════════════════════════════════╝
Files:
- devops/images/settings
- .gitlab-ci.yml
Options: approve as-is, merge groups, move files between groups,
change a commit message, commit only specific groups (remaining
groups' files will be re-staged automatically), or drop a group
(leaves files uncommitted).
Stats format: Use git diff --stat -- <files> for each group to get line counts. Show as (N files, +A -D lines) for new+deleted, or (N files, +A ~M -D lines) when modifications are present. For new-only files, (N files, +A lines) suffices.
If any group requires stubs, detail exactly what stubs will be inserted and where.
Wait for explicit user approval before proceeding.
For single-group plans (no stubs needed):
git diff --cached --stat to check for pre-existing staged changes. If any files are staged that do not belong to this group, run git reset HEAD first to unstage everything. This prevents unrelated staged files from leaking into the commit.git add <file1> <file2> ...git log --oneline -1For multi-group plans requiring stubs:
For each group in order:
git reset HEAD to unstage everything, so only this group's files end up in the commit.git add <file1> <file2> ...git log --oneline -1The final group in the sequence should have no stubs remaining — all real code is committed.
For multi-group plans with hunk-level splitting:
When a file contributes changes to multiple groups, for each group:
Hook failure handling: Report which group (N of M) failed, help fix the issue, re-stage the same files, create a new commit (never amend), then continue with remaining groups.
After committing fewer than all groups (user chose to commit only specific groups, or stopped mid-sequence):
git add <file1> <file2> ... for every file in the remaining groups/commit again to continue."This ensures the user doesn't lose their staging state and can resume committing the remaining groups without manually re-staging.
All groups committed: Show a complete summary:
All commits complete:
abc1234 feature(map): add polygon validation service
def5678 fix(websocket): correct heartbeat interval
ghi9012 chore(docker): update PHP base image tag
Partial commit (some groups remaining): Show committed groups with hashes, then list remaining groups with their planned messages and files:
Committed:
abc1234 feature(map): add polygon validation service
Remaining (files re-staged):
Group 2: fix(websocket): correct heartbeat interval
- src/Module/Websocket/Infrastructure/Server.php
Group 3: chore(docker): update PHP base image tag
- devops/images/settings
- .gitlab-ci.yml
Run /commit again to continue with the remaining groups.
When presenting a proposed commit message for user approval, always wrap it in a Unicode box-drawing frame. This creates a clear visual boundary in the terminal.
Template:
╔══════════════════════════════════════════════════╗
║ fix(map): correct polygon z-index calculation ║
║ ║
║ The polygon overlay was rendering behind ║
║ markers due to incorrect z-index assignment ║
║ during layer initialization. ║
║ ║
║ Refs: #AM-342 ║
╚══════════════════════════════════════════════════╝
Rules:
╔ + ═ repeated + ╗║ on each side╚ + ═ repeated + ╝║ + spaces + ║Display-only wrapping: The box-drawing frame may wrap long body lines to fit a reasonable display width. These visual line breaks are for presentation only. When passing the message to git commit, use the unwrapped text — each paragraph in the body should be a single continuous line. Only preserve intentional line breaks (blank lines between paragraphs, footer separation).
Compose commit messages using two sources:
General structure and rules — read references/conventional-commits.md for the Conventional Commits message structure, subject/body/footer rules, and breaking change syntax.
Project-specific types, scopes, and examples — read the project's CONTRIBUTING.md at the repository root for allowed commit types, scope conventions, and example messages.
If the project has no CONTRIBUTING.md, fall back to standard Conventional Commits types: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert.
If changes span multiple scopes, use the most dominant one. If truly cross-cutting, omit the scope.
Co-Authored-By lines for any AI model (Claude, GPT, etc.)--no-verify to skip hooksgit commit message must use one continuous line per paragraph (git and hosting tools handle wrapping):<space> separator in footer tokens (e.g., Refs: #AM-1234, not Refs #AM-1234)Guides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.
npx claudepluginhub jazithedev/ai-marketplace --plugin smart-commit