Commit Staged Files with Conventional Commits
You are helping the user commit their currently staged (indexed) git files using the
Conventional Commits specification and commit message best practices.
Pre-flight Checks
Before crafting a commit message, always:
- Run
git status to see what files are staged
- Run
git diff --cached to review the actual staged changes
- If nothing is staged, inform the user and stop — do not create an empty commit
Conventional Commit Format
<type>[optional scope]: <subject>
[optional body]
[optional footer(s)]
Types
| Type |
When to use |
feat |
A new feature or capability |
fix |
A bug fix |
docs |
Documentation-only changes |
style |
Formatting, whitespace, semicolons — no logic change |
refactor |
Code restructuring without behavior change |
perf |
Performance improvement |
test |
Adding or updating tests |
build |
Build system or external dependency changes |
ci |
CI/CD configuration changes |
chore |
Maintenance tasks (deps update, tooling, config) |
revert |
Reverting a previous commit |
Note: Comment-only changes (adding, updating, or removing code comments) should use style or
chore — never feat or fix. Keep commit messages concise; do not describe individual comments.
Scope
- Optional, but recommended when the change targets a specific module, component, or area
- Use lowercase, kebab-case:
feat(auth):, fix(api-client):
- Keep consistent with the project's existing scope conventions
- Check recent git log (
git log --oneline -50 or more) for scope patterns already used in the project
- Also note whether the project actually uses conventional commits — if not, adapt to the project's style
Subject Line Rules
- Imperative mood: "add feature" not "added feature" or "adds feature"
- Lowercase first letter: "add feature" not "Add feature"
- No period at the end
- 50 characters or less — hard limit at 72
- Complete the sentence: "If applied, this commit will <subject>"
Body Rules
- Separate from subject with a blank line
- Wrap at 72 characters
- Explain what and why, not how (the diff shows how)
- Use when the subject alone is not sufficient to understand the change
- Use bullet points for multiple related changes
Footer Rules
BREAKING CHANGE: <description> for breaking changes (triggers major version bump)
Refs: #123 or Closes #456 for issue references
Co-authored-by: Name <email> for co-authors
Signed-off-by: Name <email> when the project requires a Developer Certificate of Origin (DCO)
Decision Process
Follow this process to determine the commit message:
Step 1: Analyze the Staged Changes
Read the diff carefully and identify:
- What files changed and their purpose
- Whether this is a single logical change or multiple unrelated changes
- The primary intent: new feature, bug fix, refactor, etc.
Step 2: Check for Multiple Logical Changes
If the staged changes contain multiple unrelated changes:
- Inform the user: "The staged changes contain multiple unrelated changes.
Consider splitting them into separate commits for a cleaner history."
- Classify changes into categories to suggest logical groupings:
- Tidying — formatting, renaming, dead code removal (no behavior change)
- Infrastructure/build — dependencies, tooling, configuration
- Feature implementation — new capabilities
- Bug fixes — correcting incorrect behavior
- Documentation — docs-only changes
- Keep dependency manifests with their lock files (e.g.,
package.json + package-lock.json,
go.mod + go.sum, Cargo.toml + Cargo.lock, pyproject.toml + lock files)
- Suggest a commit order that tells a clear story:
- Tidying/structural changes first (separate from behavior changes)
- Documentation before related code changes
- Infrastructure/build before features that depend on them
- Feature or fix commits last
- Let the user decide whether to proceed with a single commit or split
Step 3: Determine the Type
- Ask yourself: "What is the primary intent of this change?"
- If a feature includes tests, the type is
feat (not test)
- If a bug fix includes a refactor, the type is
fix (not refactor)
- The type reflects the reason for the change, not every file touched
- Exception — scope-inherent types: When all changed files belong to a single domain that has
its own type, use that type directly without a scope. For example, if a commit only touches CI/CD
files (e.g.,
.github/workflows/), use ci: — not fix(ci): or feat(ci):. The same applies
to docs: (only documentation files), test: (only test files), and build: (only build config).
These types already convey the scope, so adding it as a parenthetical is redundant.
Step 4: Determine the Scope
- Look at what area of the codebase is affected
- Check
git log --oneline -50 for existing scope conventions
- If the change touches multiple areas, either omit the scope or use the primary area
Step 5: Write the Subject
- Describe the change concisely in imperative mood
- Focus on the user-facing or developer-facing impact
- Bad:
fix(api): fixed the bug in the login endpoint
- Good:
fix(api): return 401 on expired token instead of 500
Step 6: Write the Body (if needed)
Add a body when:
- The subject does not fully explain the change
- There is important context (why this approach, what was considered)
- The change has side effects or non-obvious consequences
- There is a breaking change to document
Step 7: Present and Confirm
- Present the complete commit message to the user
- Wait for approval before executing the commit
- If the user wants changes, adjust accordingly
Commit Execution
When executing the commit:
- Use
git commit -m with a HEREDOC for multi-line messages
- Never use
--no-verify — respect pre-commit hooks
- Never use
--amend unless the user explicitly requests it
- If a pre-commit hook fails, investigate and fix the issue, then create a new commit
- After committing, run
git status to confirm success
Single-line Commit
git commit -m "feat(auth): add JWT token refresh endpoint"
Multi-line Commit
git commit -m "$(cat <<'EOF'
feat(auth): add JWT token refresh endpoint
Implement automatic token refresh when the access token expires.
The refresh endpoint validates the refresh token and issues a new
access token with a 15-minute expiry.
Closes #234
EOF
)"
Examples
Simple Feature
feat: add dark mode toggle to settings page
Bug Fix with Context
fix(parser): handle empty input without panic
The YAML parser panicked on empty strings because it attempted
to access the first character without a length check. Now returns
an empty document instead.
Closes #89
Breaking Change
feat(api)!: require authentication for all endpoints
All API endpoints now require a valid Bearer token. Previously,
read-only endpoints were publicly accessible.
BREAKING CHANGE: unauthenticated requests to /api/* now return 401.
Clients must include an Authorization header with a valid token.
Refs: #156
Documentation Update
docs: add API rate limiting guide
Refactor
refactor(db): extract connection pooling into dedicated module
Move connection pool logic from the monolithic database module into
its own module to improve testability and separation of concerns.
No behavior change.
Anti-patterns to Avoid
| Anti-pattern |
Why it is wrong |
Better alternative |
fix: fix bug |
Says nothing useful |
fix(cart): prevent negative quantities |
update code |
Not a conventional commit |
refactor(utils): simplify date parsing |
feat: Added new feature and fixes |
Past tense, vague, mixed scope |
Split into separate commits |
WIP |
Not meaningful in history |
Use a descriptive message or --fixup |
misc changes |
Uninformative |
Describe what actually changed |
fix: fix |
Redundant and meaningless |
Describe the actual fix |
| Subject longer than 72 characters |
Breaks tooling and readability |
Keep it concise, use body for details |
Fixup Commits
When the user indicates a change is a fixup (e.g., "this is a fixup", "fixup change", "attach to previous commit"),
the commit should be created as a fixup! commit targeting the original commit that introduced the issue.
Fixup Process
Determine the branch boundary — before anything else, identify which commits belong to the current branch:
git log --oneline $(git merge-base HEAD origin/main)..HEAD
This is the safe rebase range. Only commits in this range may be targeted for fixup or autosquash.
Identify the target commit — search the git log for the commit that introduced the code being fixed:
- Use
git log --oneline $(git merge-base HEAD origin/main)..HEAD -- <changed-files> to find
commits on the current branch that touched the same files
- Pick the commit whose subject best matches the change being fixed
- CRITICAL guardrail: if the target commit is not in the branch range (i.e., it is on
main
or before the branch point), do not create a fixup commit. Instead, inform the user and
create a normal commit with the appropriate type (e.g., fix, ci)
Create the fixup commit — use git commit --fixup <target-sha>:
git commit --fixup abc1234
This produces a commit with the message fixup! <original subject>.
Ask the user if they want to autosquash — after the fixup commit is created, ask:
"Fixup commit created. Do you want to autosquash it into the target commit now
(git rebase --autosquash)?"
If the user accepts, run the interactive rebase with autosquash scoped to the branch:
GIT_SEQUENCE_EDITOR=true git rebase --autosquash $(git merge-base HEAD origin/main)
Using GIT_SEQUENCE_EDITOR=true auto-confirms the rebase editor so it runs non-interactively.
Never rebase beyond the merge-base — this would rewrite commits shared with main.
If the user declines, leave the fixup commit as-is — it will be squashed during a future rebase.
Fixup Example
# Original commit:
a1b2c3d feat(auth): add OAuth2 login flow
# Fixup commit (auto-generated message):
fixup! feat(auth): add OAuth2 login flow
Important Guidelines
- Always review the diff before writing the commit message — do not guess
- Never commit secrets (.env, API keys, credentials) — warn the user if staged
- Respect the project's conventions — check recent history for patterns
- One logical change per commit — suggest splitting when appropriate
- Ask before committing — always present the message for approval first
1---2name: conventional-commit3description: Guides committing staged (indexed) git files using the Conventional Commits specification and commit message best practices. Use when user mentions commit, git commit, conventional commit, commit message, staged files, indexed files, fixup, or fixup commit. Helps craft well-structured, meaningful commit messages including fixup commits with optional autosquash.4---56# Commit Staged Files with Conventional Commits78You are helping the user commit their currently staged (indexed) git files using the9[Conventional Commits](https://www.conventionalcommits.org/) specification and commit message best practices.1011## Pre-flight Checks1213Before crafting a commit message, always:14151. **Run `git status`** to see what files are staged162. **Run `git diff --cached`** to review the actual staged changes173. **If nothing is staged**, inform the user and stop — do not create an empty commit1819## Conventional Commit Format2021```text22<type>[optional scope]: <subject>2324[optional body]2526[optional footer(s)]27```2829### Types3031| Type | When to use |32| ------------ | ----------------------------------------------------------- |33| `feat` | A new feature or capability |34| `fix` | A bug fix |35| `docs` | Documentation-only changes |36| `style` | Formatting, whitespace, semicolons — no logic change |37| `refactor` | Code restructuring without behavior change |38| `perf` | Performance improvement |39| `test` | Adding or updating tests |40| `build` | Build system or external dependency changes |41| `ci` | CI/CD configuration changes |42| `chore` | Maintenance tasks (deps update, tooling, config) |43| `revert` | Reverting a previous commit |4445**Note**: Comment-only changes (adding, updating, or removing code comments) should use `style` or46`chore` — never `feat` or `fix`. Keep commit messages concise; do not describe individual comments.4748### Scope4950- Optional, but recommended when the change targets a specific module, component, or area51- Use lowercase, kebab-case: `feat(auth):`, `fix(api-client):`52- Keep consistent with the project's existing scope conventions53- **Check recent git log** (`git log --oneline -50` or more) for scope patterns already used in the project54- Also note whether the project actually uses conventional commits — if not, adapt to the project's style5556### Subject Line Rules5758- **Imperative mood**: "add feature" not "added feature" or "adds feature"59- **Lowercase first letter**: "add feature" not "Add feature"60- **No period at the end**61- **50 characters or less** — hard limit at 7262- **Complete the sentence**: "If applied, this commit will _\<subject\>_"6364### Body Rules6566- Separate from subject with a blank line67- Wrap at 72 characters68- Explain **what** and **why**, not **how** (the diff shows how)69- Use when the subject alone is not sufficient to understand the change70- Use bullet points for multiple related changes7172### Footer Rules7374- `BREAKING CHANGE: <description>` for breaking changes (triggers major version bump)75- `Refs: #123` or `Closes #456` for issue references76- `Co-authored-by: Name <email>` for co-authors77- `Signed-off-by: Name <email>` when the project requires a Developer Certificate of Origin (DCO)7879## Decision Process8081Follow this process to determine the commit message:8283### Step 1: Analyze the Staged Changes8485Read the diff carefully and identify:8687- What files changed and their purpose88- Whether this is a single logical change or multiple unrelated changes89- The primary intent: new feature, bug fix, refactor, etc.9091### Step 2: Check for Multiple Logical Changes9293If the staged changes contain **multiple unrelated changes**:9495- Inform the user: "The staged changes contain multiple unrelated changes.96 Consider splitting them into separate commits for a cleaner history."97- Classify changes into categories to suggest logical groupings:98 - **Tidying** — formatting, renaming, dead code removal (no behavior change)99 - **Infrastructure/build** — dependencies, tooling, configuration100 - **Feature implementation** — new capabilities101 - **Bug fixes** — correcting incorrect behavior102 - **Documentation** — docs-only changes103- Keep dependency manifests with their lock files (e.g., `package.json` + `package-lock.json`,104 `go.mod` + `go.sum`, `Cargo.toml` + `Cargo.lock`, `pyproject.toml` + lock files)105- Suggest a commit order that tells a clear story:106 1. Tidying/structural changes first (separate from behavior changes)107 2. Documentation before related code changes108 3. Infrastructure/build before features that depend on them109 4. Feature or fix commits last110- Let the user decide whether to proceed with a single commit or split111112### Step 3: Determine the Type113114- Ask yourself: "What is the **primary intent** of this change?"115- If a feature includes tests, the type is `feat` (not `test`)116- If a bug fix includes a refactor, the type is `fix` (not `refactor`)117- The type reflects the reason for the change, not every file touched118- **Exception — scope-inherent types**: When all changed files belong to a single domain that has119 its own type, use that type directly without a scope. For example, if a commit only touches CI/CD120 files (e.g., `.github/workflows/`), use `ci:` — not `fix(ci):` or `feat(ci):`. The same applies121 to `docs:` (only documentation files), `test:` (only test files), and `build:` (only build config).122 These types already convey the scope, so adding it as a parenthetical is redundant.123124### Step 4: Determine the Scope125126- Look at what area of the codebase is affected127- Check `git log --oneline -50` for existing scope conventions128- If the change touches multiple areas, either omit the scope or use the primary area129130### Step 5: Write the Subject131132- Describe the change concisely in imperative mood133- Focus on the user-facing or developer-facing impact134- Bad: `fix(api): fixed the bug in the login endpoint`135- Good: `fix(api): return 401 on expired token instead of 500`136137### Step 6: Write the Body (if needed)138139Add a body when:140141- The subject does not fully explain the change142- There is important context (why this approach, what was considered)143- The change has side effects or non-obvious consequences144- There is a breaking change to document145146### Step 7: Present and Confirm147148- Present the complete commit message to the user149- Wait for approval before executing the commit150- If the user wants changes, adjust accordingly151152## Commit Execution153154When executing the commit:155156- Use `git commit -m` with a HEREDOC for multi-line messages157- **Never** use `--no-verify` — respect pre-commit hooks158- **Never** use `--amend` unless the user explicitly requests it159- If a pre-commit hook fails, investigate and fix the issue, then create a new commit160- After committing, run `git status` to confirm success161162### Single-line Commit163164```bash165git commit -m "feat(auth): add JWT token refresh endpoint"166```167168### Multi-line Commit169170```bash171git commit -m "$(cat <<'EOF'172feat(auth): add JWT token refresh endpoint173174Implement automatic token refresh when the access token expires.175The refresh endpoint validates the refresh token and issues a new176access token with a 15-minute expiry.177178Closes #234179EOF180)"181```182183## Examples184185### Simple Feature186187```text188feat: add dark mode toggle to settings page189```190191### Bug Fix with Context192193```text194fix(parser): handle empty input without panic195196The YAML parser panicked on empty strings because it attempted197to access the first character without a length check. Now returns198an empty document instead.199200Closes #89201```202203### Breaking Change204205```text206feat(api)!: require authentication for all endpoints207208All API endpoints now require a valid Bearer token. Previously,209read-only endpoints were publicly accessible.210211BREAKING CHANGE: unauthenticated requests to /api/* now return 401.212Clients must include an Authorization header with a valid token.213214Refs: #156215```216217### Documentation Update218219```text220docs: add API rate limiting guide221```222223### Refactor224225```text226refactor(db): extract connection pooling into dedicated module227228Move connection pool logic from the monolithic database module into229its own module to improve testability and separation of concerns.230No behavior change.231```232233## Anti-patterns to Avoid234235| Anti-pattern | Why it is wrong | Better alternative |236| ------------------------------------- | ------------------------------ | ---------------------------------------- |237| `fix: fix bug` | Says nothing useful | `fix(cart): prevent negative quantities` |238| `update code` | Not a conventional commit | `refactor(utils): simplify date parsing` |239| `feat: Added new feature and fixes` | Past tense, vague, mixed scope | Split into separate commits |240| `WIP` | Not meaningful in history | Use a descriptive message or `--fixup` |241| `misc changes` | Uninformative | Describe what actually changed |242| `fix: fix` | Redundant and meaningless | Describe the actual fix |243| Subject longer than 72 characters | Breaks tooling and readability | Keep it concise, use body for details |244245## Fixup Commits246247When the user indicates a change is a **fixup** (e.g., "this is a fixup", "fixup change", "attach to previous commit"),248the commit should be created as a `fixup!` commit targeting the original commit that introduced the issue.249250### Fixup Process2512521. **Determine the branch boundary** — before anything else, identify which commits belong to the current branch:253254 ```bash255 git log --oneline $(git merge-base HEAD origin/main)..HEAD256 ```257258 This is the **safe rebase range**. Only commits in this range may be targeted for fixup or autosquash.2592. **Identify the target commit** — search the git log for the commit that introduced the code being fixed:260 - Use `git log --oneline $(git merge-base HEAD origin/main)..HEAD -- <changed-files>` to find261 commits on the current branch that touched the same files262 - Pick the commit whose subject best matches the change being fixed263 - **CRITICAL guardrail**: if the target commit is **not** in the branch range (i.e., it is on `main`264 or before the branch point), **do not create a fixup commit**. Instead, inform the user and265 create a normal commit with the appropriate type (e.g., `fix`, `ci`)2663. **Create the fixup commit** — use `git commit --fixup <target-sha>`:267268 ```bash269 git commit --fixup abc1234270 ```271272 This produces a commit with the message `fixup! <original subject>`.2734. **Ask the user if they want to autosquash** — after the fixup commit is created, ask:274 > "Fixup commit created. Do you want to autosquash it into the target commit now275 > (`git rebase --autosquash`)?"2765. **If the user accepts**, run the interactive rebase with autosquash **scoped to the branch**:277278 ```bash279 GIT_SEQUENCE_EDITOR=true git rebase --autosquash $(git merge-base HEAD origin/main)280 ```281282 Using `GIT_SEQUENCE_EDITOR=true` auto-confirms the rebase editor so it runs non-interactively.283 **Never** rebase beyond the merge-base — this would rewrite commits shared with `main`.2846. **If the user declines**, leave the fixup commit as-is — it will be squashed during a future rebase.285286### Fixup Example287288```text289# Original commit:290a1b2c3d feat(auth): add OAuth2 login flow291292# Fixup commit (auto-generated message):293fixup! feat(auth): add OAuth2 login flow294```295296## Important Guidelines297298- **Always review the diff** before writing the commit message — do not guess299- **Never commit secrets** (.env, API keys, credentials) — warn the user if staged300- **Respect the project's conventions** — check recent history for patterns301- **One logical change per commit** — suggest splitting when appropriate302- **Ask before committing** — always present the message for approval first