Committing Changes
When invoked, follow this procedure step by step. Create a TodoWrite task for each step and complete them in order.
Rule Hierarchy
Apply rules in this priority order (highest first):
- Explicit user instruction in the current message — always wins.
- Project conventions file —
CLAUDE.mdorAGENTS.md— sections## Git workflow,## Git conventions,## Commit rules. - Global conventions —
~/.claude/CLAUDE.md(or your harness's global agents file, e.g.~/.codex/AGENTS.md). - This SKILL.md — process defaults.
Step 1: Read conventions
- Read the project conventions file if it exists —
CLAUDE.mdorAGENTS.md. Look for sections:## Git workflow,## Git conventions,## Commit rules. - Read the global conventions file —
~/.claude/CLAUDE.md(or the equivalent for your harness). - Project rules override global rules.
Step 2: Gather state (parallel)
Run these Bash commands in parallel:
git status(do NOT use-uallflag)git diff --stagedgit diff(unstaged)git log -n 10 --oneline
If git status shows the working tree is not a git repo — STOP. Report to user. Do not run git init.
Step 3: Safety scan on staged files
Block the commit and report to user if any of these are in staged files:
Suspicious filenames:
.env*,*credentials*,*secret*,*.pem,*.key,id_rsa*
Suspicious content patterns (grep staged diff):
AWS_[A-Z_]+ ?= ?['\"][A-Za-z0-9/+=]{20,}sk-[a-zA-Z0-9]{20,}(OpenAI/Anthropic)ghp_[A-Za-z0-9]{20,}(GitHub PAT)xox[baprs]-[A-Za-z0-9-]{10,}(Slack)-----BEGIN.*PRIVATE KEY-----
Garbage files:
.DS_Store,Thumbs.db, anything insidenode_modules/,__pycache__/,.next/,dist/,build/
Large binaries: any staged file >1MB (check with git diff --staged --stat).
Tiered response:
- Secrets and credentials (suspicious filenames + content patterns above) — unconditional STOP. NO bypass, even on user request. List offending files/lines and refuse to commit until they are removed from staging.
- Garbage files and large binaries — STOP, list them, ask user whether to proceed despite the warning.
Step 4: Analyze for atomic split
Cross-skill context: If this skill was invoked by another skill (e.g., merging-branches) with context containing "skip atomic-split check" or "squash merge" — skip this entire step and proceed directly to Step 5. The caller has determined a single commit is correct.
Inspect staged changes for "should this be multiple commits" heuristics:
- Changes touch ≥2 unrelated top-level directories (e.g.,
src/auth/*ANDsrc/billing/*) - Mixed categories: code + independent docs + independent config
- Multiple independent fixes in different files
If a split is warranted, present the logical groups to the user:
Found N logical changes:
1. feat(auth): add OAuth provider — src/auth/oauth.ts, src/auth/oauth.test.ts
2. docs(api): update API documentation — docs/api/*.md
3. chore(deps): bump dependencies — package.json, pnpm-lock.yaml
Split into N commits? (yes / no / custom)
Wait for user response.
- "yes" → proceed with N separate commits, each through Steps 5–10
- "no" → proceed as a SINGLE commit with all staged changes (continue to Step 5)
- "custom" → ask user to specify the grouping
- No response → do not commit until user clarifies
If no split was warranted in the first place (single logical group or small changes) — proceed as one commit, no question asked.
Step 5: Compose the message
Format: type(scope): subject
Allowed types (10 common Conventional Commits types):
| type | when |
|---|---|
feat |
new functionality |
fix |
bug fix |
docs |
documentation only |
style |
formatting, whitespace, code style (NOT logic changes) |
refactor |
refactor without feat/fix |
test |
tests only |
chore |
misc (deps, gitignore, build config) |
perf |
performance |
build |
build system |
ci |
CI/CD |
Subject rules:
- imperative mood ("add" not "added")
- lowercase
- no trailing period
- ≤72 chars
- English
Body is REQUIRED when:
- Commit touches files in ≥2 different code areas
- The decision is non-obvious (workaround, unusual approach)
- Breaking change (then include
BREAKING CHANGE:footer)
Body answers "what AND why" — not "how" (the diff shows how).
Step 6: Pre-action validation
Strip / refuse the following:
- ❌
Co-Authored-Bytrailer — REMOVE if present in any draft message - ❌
--amend— only allowed if user explicitly said "amend" - ❌
--no-verify— only allowed if user explicitly asked to skip hooks - ❌
git add -A/git add .— stage only explicit files by path. If the user's request implies "add everything," enumerate files fromgit statusoutput and stage each explicitly:git add file1 file2 file3.
Step 7: Show the plan, wait for approval
Format:
Plan (single commit):
- git add <files>
- git commit -m "<subject>"
<optional body preview>
Plan (split into N commits — if user confirmed split in Step 4):
1. git add <files-group-1>
git commit -m "<subject-1>"
2. git add <files-group-2>
git commit -m "<subject-2>"
... (one block per group)
Confirm? (y/n)
Without y (or equivalent affirmation), do NOT commit.
Step 8: Execute via HEREDOC
For multi-line messages, use HEREDOC to preserve formatting:
git commit -m "$(cat <<'EOF'
<subject>
<body>
EOF
)"
For one-line subject, -m "subject" is fine.
Step 9: Pre-commit hook failure
If the pre-commit hook fails — the commit did NOT happen.
- Do NOT run
--amend(it would modify the previous, unrelated commit). - Fix the underlying problem.
- Re-stage the relevant files.
- Create a NEW commit with the same intended message.
If the hook fails a second time — STOP. Report to user. Do NOT try a third time.
Step 10: Final report
- Run
git statusand confirm it's clean. - Report SHA + subject on one line:
c726baa docs(specs): add git skills design spec. - If multiple commits (from split), list each one.
- Do NOT push. Push is always a separate, explicit user command.
Override phrases
If the user explicitly uses one of these phrases, follow the override without re-asking:
| Phrase | Effect |
|---|---|
| "skip checks" | Skip safety scan, but secret detection still blocks |
| "amend" | Allow --amend |
| "no-verify" | Allow --no-verify |
Absolute prohibitions
Never, even on explicit request:
- Any
git configmodifications.
Requires special confirmation beyond regular y/n:
- Committing files matched by
.gitignoreas explicit paths → warning + confirmation.
Edge cases (handle and report)
- Detached HEAD (
git symbolic-ref HEADfails): report state to user, ask whether to proceed (commit will be unreachable without a branch). - Submodules in staged (
git diff --staged --submodule): warn separately — submodule commits behave differently, confirm intent. - Existing
.git/MERGE_HEAD(unfinished merge): STOP. Tell user a previous merge is in progress, ask how to handle. - Pre-commit hook failing in a loop: if hook fails twice in a row, STOP after the 2nd attempt. Do not retry. Ask user.
- Not a git repository: report and stop. Never run
git init.