ring:git-commit
Analyze changes, group them into coherent atomic commits, and create signed commits following repository conventions. This skill transforms a messy working directory into a clean, logical commit history.
Smart Commit Organization
This skill does MORE than just commit. It analyzes your changes and organizes them intelligently.
What It Does
Working Directory State → Organized Commits
─────────────────────────────────────────────────────────
- Modified: auth.ts → Commit 1: feat(auth): add OAuth2 support
- Modified: auth.test.ts (auth.ts + auth.test.ts)
- Modified: package.json → Commit 2: chore(deps): update dependencies
- Modified: README.md → Commit 3: docs: update authentication guide
- New: logger.ts → Commit 4: feat(logging): add structured logger
- New: logger.test.ts (logger.ts + logger.test.ts)
Grouping Principles
| Principle | Description |
|---|---|
| Feature + Tests | Implementation and its tests go together |
| Config Changes | package.json, tsconfig, etc. grouped separately |
| Documentation | README, docs/ changes grouped together |
| Refactoring | Pure refactors (no behavior change) separate |
| Bug Fixes | Each fix is atomic with its test |
Process Overview
- Analyze - Run
git statusandgit diffto understand all changes - Group - Cluster related changes into logical commits
- Order - Determine optimal commit sequence (deps before features, etc.)
- Confirm - Present grouping plan to user for approval
- Execute - Create signed commits in sequence
Single vs Multiple Commits
Single commit when:
- All changes are for one coherent feature/fix
- User provides a specific message via argument
- Changes are minimal and related
Multiple commits when:
- Changes span different concerns (feature + docs + deps)
- Mix of features, fixes, and chores
- Better git history benefits future archaeology
User Confirmation
Before creating commits, present the plan:
Proposed Commit Plan:
─────────────────────
1. feat(auth): add OAuth2 refresh token support
- src/auth/oauth.ts (modified)
- src/auth/oauth.test.ts (modified)
2. chore(deps): update authentication dependencies
- package.json (modified)
- package-lock.json (modified)
3. docs: update OAuth2 setup guide
- docs/auth/oauth-setup.md (modified)
Proceed with this plan? [Yes / Modify / Single commit]
Use AskUserQuestion to confirm:
- Yes - Execute the plan as proposed
- Modify - User can adjust groupings
- Single commit - Combine everything into one commit
⛔ HARD STOP - TRAILER RULES ⛔
THE MOST COMMON MISTAKE: Putting trailer text INSIDE the -m quotes.
# ❌ WRONG - "X-Lerian-Ref" text is INSIDE the -m quotes
git commit -m "feat: add feature
X-Lerian-Ref: 0x1"
# ✅ CORRECT - --trailer is a SEPARATE command-line argument OUTSIDE quotes
git commit -m "feat: add feature" --trailer "X-Lerian-Ref: 0x1"
BEFORE writing ANY git commit command, answer this checkpoint:
- Does my
-m "..."contain ONLY the commit message? (NO trailer text inside) - Are
--trailerflags OUTSIDE and AFTER the-mparameter? - Is my command structured as:
git commit -m "msg" --trailer "key: value"?
If you answered NO to ANY question → STOP. Re-read the MANDATORY RULES below.
Required Command Structure
EVERY git commit command you create MUST follow this exact structure:
git commit \
-m "commit message line 1" \
-m "optional body line 2" \
--trailer "X-Lerian-Ref: 0x1"
Visual breakdown - understand WHERE each piece goes:
git commit -m "feat: add feature" --trailer "X-Lerian-Ref: 0x1"
│ │ │ │
└── MESSAGE ─────────┘ └── SEPARATE FLAG (NOT inside -m) ──┘
(inside quotes) (after the closing quote)
The --trailer flag is NOT text. It's a command-line argument to git, like -m or --amend.
Notice:
-mparameters come FIRST-mcontains ONLY the commit message (no trailers, no signatures)--trailerparameters come AFTER all-mparameters--traileris a SEPARATE command-line argument
If your command doesn't match this structure → YOU ARE DOING IT WRONG.
MANDATORY RULES (NON-NEGOTIABLE)
These rules MUST be followed for EVERY commit:
-
NEVER include visible markers or hashtags in commit message body:
- NO
🤖 Generated with [Claude Code](https://claude.com/claude-code) - NO
Co-Authored-Bylines of any kind - NO emoji signatures or hashtags of any kind
- NO "Generated by" text in message body
- NO system references or internal codes in the message body
- The
-mparameter contains ONLY the commit message. PERIOD.
- NO
-
ALWAYS use
--trailerparameter for internal system tracing:--trailer "X-Lerian-Ref: 0x1"- REQUIRED- Trailer goes AFTER all
-mparameters in the git command - Trailer is a SEPARATE command-line argument, NOT part of message text
-
Commit message body must be clean and professional:
- Only the actual commit description
- No metadata, signatures, hashtags, or internal references in the body
- If you see emoji, hashtags, or "X-Lerian-Ref" in your
-mtext → YOU ARE DOING IT WRONG
Commit Process
Step 1: Gather Context
Run these commands in parallel to understand the current state:
# Check staged and unstaged changes
git status
# View ALL changes (staged and unstaged)
git diff
git diff --cached
# View recent commits for style reference
git log --oneline -10
Step 2: Analyze and Group Changes
For each changed file, determine:
- Type: feat, fix, chore, docs, refactor, test, style, perf, ci, build
- Scope: Component or area affected (auth, api, ui, etc.)
- Logical group: What other files belong with this change?
Grouping heuristics:
| File Pattern | Likely Group |
|---|---|
*.test.ts, *.spec.ts |
Group with implementation file |
package.json, *-lock.json |
Dependency changes |
*.md, docs/* |
Documentation |
*.config.*, tsconfig.* |
Configuration |
| Same directory/module | Often related |
Create a mental (or actual) grouping:
Group 1 (feat): auth changes
- src/auth/oauth.ts
- src/auth/oauth.test.ts
Group 2 (chore): dependencies
- package.json
- package-lock.json
Group 3 (docs): documentation
- README.md
Step 3: Determine Commit Order
Order matters for bisectability:
- Dependencies first - So subsequent commits can use them
- Core changes - Implementation before consumers
- Tests with implementation - Keep them atomic
- Documentation last - Documents the final state
Step 4: Present Plan and Confirm
MANDATORY: Get user confirmation before executing.
Present the commit plan using AskUserQuestion:
AskUserQuestion({
questions: [{
question: "I've analyzed your changes and propose this commit plan. How should I proceed?",
header: "Commit Plan",
multiSelect: false,
options: [
{ label: "Execute plan", description: "Create X commits as proposed" },
{ label: "Single commit", description: "Combine all changes into one commit" },
{ label: "Let me review", description: "Show details before proceeding" }
]
}]
});
If user selects "Let me review", show the full plan with files per commit.
Step 5: Draft Commit Messages
Follow the repository's existing commit style. If Conventional Commits is used:
<type>(<scope>): <subject>
<body - optional>
Guidelines:
- Subject line: max 50 characters, imperative mood ("add" not "added")
- Body: wrap at 72 characters, explain motivation/context
- DO NOT include emoji signatures, hashtags, "Generated by", "X-Lerian-Ref", or any system markers in the message body
Step 6: Execute Commits (Signed)
For each commit group, in order:
- Stage only the files for this commit:
git add <file1> <file2> ...
- Create signed commit with trailers:
git commit -S \
-m "<type>(<scope>): <subject>" \
-m "<body if needed>" \
--trailer "X-Lerian-Ref: 0x1"
Required flags:
-S- GPG sign the commit (REQUIRED for signed commits)--trailer "X-Lerian-Ref: 0x1"- Internal system reference (REQUIRED)
If GPG signing fails:
- Check if GPG key is configured:
git config user.signingkey - Check if GPG agent is running:
gpg --list-secret-keys - If no key configured, proceed without
-Sand inform user
- Repeat for each commit group
Step 7: Verify Commits
After all commits, verify the result:
# Show all new commits
git log --oneline -<number_of_commits>
# Verify signatures (if signed)
git log --show-signature -1
# Confirm clean state
git status
Step 8: Offer Push (Optional)
After successful commit, ask the user if they want to push:
AskUserQuestion({
questions: [{
question: "Push commit to remote?",
header: "Push",
multiSelect: false,
options: [
{ label: "Yes", description: "Push to current branch" },
{ label: "No", description: "Keep local only" }
]
}]
});
If user selects "Yes":
git push
If branch has no upstream, use:
git push -u origin <current-branch>
Examples
Simple Feature (Signed)
git commit -S \
-m "feat(auth): add OAuth2 refresh token support" \
-m "Implements automatic token refresh when access token expires, preventing session interruptions for long-running operations." \
--trailer "X-Lerian-Ref: 0x1"
Bug Fix (Signed)
git commit -S \
-m "fix(api): handle null response in user endpoint" \
--trailer "X-Lerian-Ref: 0x1"
Chore/Refactor (Signed)
git commit -S \
-m "chore: update dependencies to latest versions" \
--trailer "X-Lerian-Ref: 0x1"
Multi-Commit Sequence (Organized)
When changes span multiple concerns, execute in sequence:
# Commit 1: Dependencies first
git add package.json package-lock.json
git commit -S \
-m "chore(deps): update authentication dependencies" \
--trailer "X-Lerian-Ref: 0x1"
# Commit 2: Feature implementation with tests
git add src/auth/oauth.ts src/auth/oauth.test.ts
git commit -S \
-m "feat(auth): add OAuth2 refresh token support" \
-m "Implements automatic token refresh when access token expires." \
--trailer "X-Lerian-Ref: 0x1"
# Commit 3: Documentation last
git add docs/auth/oauth-setup.md README.md
git commit -S \
-m "docs: update OAuth2 setup guide" \
--trailer "X-Lerian-Ref: 0x1"
Trailer Query Commands
Trailers can be queried programmatically:
Note: git log --grep searches commit message content only, not trailers. Use --format with %(trailers) to query trailer values.
# Find all commits with specific X-Lerian-Ref trailer value
git log --all --format="%H %s %(trailers:key=X-Lerian-Ref,valueonly)" | grep "0x1"
# Show all trailers for a commit
git log -1 --format="%(trailers)"
# Filter commits by trailer existence (any value)
git log --all --format="%H %s" | while read hash msg; do
git log -1 --format="%(trailers:key=X-Lerian-Ref)" $hash | grep -q "." && echo "$hash $msg"
done
Important Notes
- Smart grouping - Analyzes changes and proposes atomic commits for clean history
- GPG signing - All commits are signed with
-Sflag (requires GPG key configured) - No visible markers - The message body stays clean and professional
- Trailers are standard - Git trailers are a recognized convention (like Signed-off-by)
- Machine-readable - Easy to filter/query commits with internal system reference
- Transparent - System tracing is documented, just not prominently displayed
- Do not use --no-verify - Always run pre-commit hooks unless user explicitly requests
- User confirmation - Always present commit plan before executing
Anti-Patterns (NEVER DO THIS)
⛔ THESE PATTERNS ARE FORBIDDEN. DO NOT USE THEM. ⛔
# ❌ WRONG - emoji or hashtags in message body
git commit -m "feat: add feature
🤖 Generated with [Claude Code](https://claude.com/claude-code)"
# ❌ WRONG - hashtags in message body
git commit -m "feat: add feature #ai-generated #automated"
# ❌ WRONG - Co-Authored-By in message body
git commit -m "feat: add feature
Co-Authored-By: System <noreply@example.com>"
# ❌ WRONG - HEREDOC with markers in message body
git commit -m "$(cat <<'EOF'
feat: add feature
🤖 Generated with AI
EOF
)"
# ❌ WRONG - Trailer text inside -m parameter
git commit -m "feat: add feature
X-Lerian-Ref: 0x1"
# ❌ WRONG - System reference as hashtag in message body
git commit -m "feat: add feature #0x1"
# ❌ WRONG - ANY attempt to include trailers, hashtags, or system markers in message body
# The message body (-m parameter) must ONLY contain the commit description
# NO trailers, NO signatures, NO emoji, NO hashtags, NO system markers of any kind
Why these are wrong: They put visible markers in the commit message body, making them visible in git log --oneline and polluting the commit history.
# ✅ CORRECT - signed commit with trailer via --trailer parameter
git commit -S \
-m "feat: add feature" \
--trailer "X-Lerian-Ref: 0x1"
Why this is correct: Trailers are separate from the message body and only visible in git log --format=full or git log --format="%(trailers)". The commit message stays completely clean. The -S flag signs the commit with GPG.
Anti-Rationalization Table
| Rationalization | Why It's WRONG | Required Action |
|---|---|---|
| "I'll commit everything at once" | Mixed changes = messy history, hard to bisect/revert. | Analyze and group changes first |
| "Grouping takes too long" | Clean history saves hours of debugging later. | Always propose commit plan |
| "I'll skip GPG signing" | Unsigned commits can't be verified. | Use -S flag (skip only if no GPG key) |
| "I'll put the trailer text in the message body" | --trailer is a GIT FLAG, not text. Text in -m is NOT a trailer. |
Use --trailer "X-Lerian-Ref: 0x1" as separate argument |
| "The trailers need to be in the commit message" | NO. Trailers go via --trailer flag OUTSIDE the -m quotes. |
**Structure: git commit -S -m "msg" --trailer "X-Lerian-Ref: 0x1" ** |
| "I'll format it nicely in the message body" | That's NOT a trailer - that's polluting the message body. | NEVER put "X-Lerian-Ref" text inside -m quotes |
| "HEREDOC will format the trailers correctly" | HEREDOC puts everything in the message body. That's WRONG. | Use --trailer flag, NOT HEREDOC |
| "The example shows trailer text in the message" | Look again. --trailer is OUTSIDE the -m "..." quotes. |
**Copy the structure exactly: -S -m "msg" --trailer "X-Lerian-Ref: 0x1" ** |
| "I'll add a hashtag #0x1 for tracking" | Hashtags pollute the message body. Use --trailer instead. | NEVER use hashtags. Use --trailer "X-Lerian-Ref: 0x1" |
When User Provides Message
If the user provides a commit message as argument:
- Single commit mode - Skip grouping analysis, use provided message
- Use their message as the subject/body
- Ensure proper formatting (50 char subject, etc.)
- Create signed commit with trailer
# User says: /ring:commit "fix login bug"
git commit -S \
-m "fix: fix login bug" \
--trailer "X-Lerian-Ref: 0x1"