Commit
Create smart, context-aware git commits with conventional commit messages.
Core Principle: Small Logical Chunks
Always prefer committing in the smallest logical chunks that are appropriate. Each commit should represent a single coherent change: one bug fix, one new function, one refactor, one documentation update, etc. Do not batch unrelated changes into a single commit.
Why this matters:
- Small commits are easier to review, revert, cherry-pick, and bisect.
- Each commit tells a clear story about one thing that changed and why.
- When something breaks, small commits make it straightforward to identify the cause.
What constitutes a "logical chunk":
- A single bug fix (even if it touches multiple files)
- A single new feature or capability
- A refactor that moves or renames code without changing behavior
- A documentation update
- A configuration or tooling change
- Test additions or updates for a specific feature
What does NOT belong in the same commit:
- A bug fix mixed with an unrelated refactor
- A new feature combined with formatting changes to unrelated files
- Documentation updates bundled with code changes they do not describe
Options
The user may provide these options inline:
- --push: Commit and push to remote after committing
- --staged: Commit only staged changes (ignore unstaged changes)
- --plan: Commit only the plan file
- --all: Stage and commit all changes (including untracked files)
Workflow
1. Check for Changes
Run these commands in parallel to understand the current state:
# See all changed and untracked files
git status
# See staged changes
git diff --cached
# See unstaged changes
git diff
# See recent commit messages for style reference
git log --oneline -10
If there are no changes at all (no staged, unstaged, or untracked files), report that there is nothing to commit and stop.
2. Determine What to Commit
First, resolve which files are in scope using these rules in order:
- If
--stagedwas specified: Only staged files are in scope. If nothing is staged, report that and stop. - If
--planwas specified: See the Plan-Aware Commits section. - If
--allwas specified: All changes (staged, unstaged, and untracked) are in scope, but still exclude likely secret files (see below). - If there are staged changes and no unstaged changes or untracked files: Staged files are in scope.
- If there are only unstaged changes and/or untracked files: All of them are in scope.
- If there are both staged changes and either unstaged changes or untracked files: Ask the user whether to use only staged changes or include everything.
Never stage files that likely contain secrets (.env, credentials.json, *.pem, *.key, etc.). If such files are detected among untracked or unstaged changes, warn the user and exclude them.
3. Analyze Changes and Identify Logical Chunks
Review the in-scope changes and group them into the smallest logical chunks. Each chunk should be a self-contained, coherent change that makes sense on its own.
How to identify chunks:
- Examine the diff: Look at all in-scope file changes and understand what each change accomplishes.
- Group by purpose: Changes that serve the same purpose belong together. For example, a new function and its tests are one chunk, but an unrelated formatting fix is a separate chunk.
- Check for independence: If a change can be committed on its own without leaving the codebase in a broken or inconsistent state, it is a candidate for its own chunk.
- Respect dependencies: If change B depends on change A, commit A first. They may still be separate chunks if they represent distinct logical steps.
Common chunk patterns:
- Adding a new file and updating an index or registry that references it: one chunk.
- Fixing a bug in one module and reformatting an unrelated module: two chunks.
- Renaming a function across multiple call sites: one chunk.
- Adding a feature, adding its tests, and updating documentation: one to three chunks depending on whether the docs describe only the new feature or also cover unrelated material.
When there is only one logical chunk: Proceed directly to step 4 with all in-scope changes.
When there are multiple logical chunks: Process each chunk sequentially, repeating steps 4 through 6 for each one. Stage only the files belonging to the current chunk before committing. Report the plan (number of commits and a brief description of each) before creating the first commit.
4. Analyze Recent Commit Style
Before generating a message, examine the output from git log --oneline -10 to understand the repository's commit message conventions:
- Do commits use conventional commits format (
type: description)? - What types are used (
feat,fix,chore,docs,refactor,test, etc.)? - What is the typical length and style?
- Are issue numbers referenced?
Match the repository's existing style. If there is no clear convention, default to conventional commits format.
Defer to a project-enforced convention
The type list and length limit in the next step are this skill's default, not an override. A project that enforces its own commit convention wins. Check these signals in order and stop at the first match:
A commitlint config exists. Look for
.commitlintrc*,commitlint.config.*, or acommitlintkey inpackage.json. Unlike the PR-title case, a commitlint config on its own is authoritative here, because linting commit messages is what commitlint does by default. Read the config it resolves and follow itstype-enum,scope-enum,subject-case, andheader-max-length. Where the config only extends a preset, the preset supplies those values;@commitlint/config-conventionalsetsheader-max-lengthto 100, not 72.A commit-msg hook enforces a format. Check
.husky/commit-msg,.git/hooks/commit-msg, and anycore.hooksPathdirectory. Follow whatever it runs.Project agent config states a commit message format. Read whichever of these exist:
CLAUDE.mdandAGENTS.mdin the repository root, andcopilot-instructions.mdunder.github/. Any of them may be absent, which is normal, andCLAUDE.mdis often a symlink toAGENTS.md, so read the target rather than treating it as a second source. Also honor any user-level instructions already in context.Recent commits are consistent. As a fallback, the
git log --oneline -10output already gathered in step 1: if most subjects match^[a-z]+(\([^)]+\))?!?:\s, the project uses conventional commits. Match that style, including its apparent type vocabulary.
5. Generate Commit Message
Analyze the diff to generate a commit message:
- Determine the type: Based on the nature of the changes:
feat: New functionalityfix: Bug fixdocs: Documentation changes onlyrefactor: Code restructuring without behavior changetest: Adding or updating testschore: Build, tooling, or maintenance changesstyle: Formatting, whitespace, or cosmetic changes
- Write the description: A concise summary (under 72 characters, unless a project convention detected above sets a different limit) focused on why the change was made, not what files changed.
- Add context when relevant: Reference issue numbers if they appear in branch names (e.g., branch
fix/issue-42-login-bugsuggestsfixes #42).
6. Create the Commit
All commits must be GPG signed. Use a HEREDOC for the commit message to ensure proper formatting:
git commit -S -m "$(
cat << 'EOF'
type: description here
EOF
)"
CRITICAL: Never use git commit --amend. Always create a new commit. If a pre-commit hook fails, fix the issue, re-stage, and create a new commit.
7. Post-Commit
After a successful commit:
- Run
git statusto verify success. - Report the commit hash and message.
- If
--pushwas specified: Push to the remote.
git push
If the branch has no upstream, use:
git push -u origin HEAD
Plan-Aware Commits
When the user mentions "commit the plan" or --plan is specified, or when plan files are detected among the changes, apply the rules in this section.
Detecting Plan Files
Plan files live under docs/plans/ and its subdirectories (todo/, done/). Look for Markdown files in these directories among the changed or untracked files.
Plan Name Cleanup
Well-named plans follow the pattern YYYY-MM-DD-meaningful-description.md. Auto-generated names are nonsensical word combinations with no datestamp (e.g., wandering-copper-lantern.md, quizzical-amber-turnstile.md).
Every time a plan file is part of a commit, check its filename. If the name lacks a datestamp prefix or uses a nonsensical auto-generated name:
- Read the plan file to understand its content.
- Choose a meaningful slug derived from the plan's title or purpose (e.g.,
consolidate-ci-workflows,add-user-authentication). - Rename the file to
YYYY-MM-DD-<slug>.md, using the current date for new plans or the date from the plan's title/content if one is stated. - Stage both the deletion of the old path and the addition of the new path.
If the filename already has a datestamp prefix and a meaningful description, leave it as-is.
Moving Completed Plans
When committing code changes alongside a plan file, or when all the work described in a plan is being committed:
- Check whether the plan's work is complete. Indicators: the branch is being finalized, the user says the work is done, or the commit represents the last piece of the plan.
- If the work is complete, apply Plan Name Cleanup first (if needed), so any rename happens before the move.
- Move the (possibly renamed) plan file to
docs/plans/done/(creating the directory if it does not exist). If both a rename and a move apply, perform a singlegit mvfrom the original path directly to the final destination (e.g.,docs/plans/done/YYYY-MM-DD-slug.md). - If the plan is currently in
docs/plans/todo/, the move goes fromtodo/todone/. - If the plan is in the
docs/plans/root, the move goes from there todone/. - Stage the rename (if any) and the file move together as part of the commit.
Do not move a plan to done/ if the work is only partially complete. Plans for work still in progress should stay in docs/plans/todo/ or the docs/plans/ root.
"Commit the plan"
Commit only the plan file(s):
- Apply Plan Name Cleanup first.
- Apply Moving Completed Plans if the plan's work is fully complete.
- Stage only the plan file(s), including any renames or moves.
- Generate a message like
docs: add plan for <meaningful-description>where the description is derived from the plan's content or title.
"Give the plan a meaningful name and commit"
- Apply Plan Name Cleanup (this request explicitly calls for it; the cleanup procedure handles staging).
- Commit with an appropriate message.
"Commit the plan as one commit, then the changes as another"
Create two sequential commits:
- First commit: Apply Plan Name Cleanup and Moving Completed Plans. If both apply to the same plan file, perform a single
git mvfrom the original path directly to the finaldone/destination (e.g.,docs/plans/done/YYYY-MM-DD-slug.md). Stage and commit the plan file with adocs:message. - Second commit: Stage and commit the remaining changes with an appropriate message.
Compound Commands
When the user gives compound instructions like "commit, then push" or "commit and push":
- Treat "and push" or "then push" as equivalent to
--push. - For other compound commands (e.g., "commit, then open a PR"), complete the commit first, then proceed with the next action.
Error Handling
- Nothing to commit: Report cleanly, do not create an empty commit.
- Pre-commit hook failure: Read the hook output, fix the issue if possible, re-stage, and create a new commit (never amend).
- Push failure: Report the error. If it's a rejected push due to remote changes, suggest
git pull --rebasefirst. Never force push unless the user explicitly requests it.