Building a Smart Commit Skill: Step-by-Step
Learn to build a smart commit skill that analyzes git diffs and generates meaningful commit messages. A complete tutorial from concept to production.
Building a Smart Commit Skill: Step-by-Step
Every developer knows the struggle: you have made changes to your code, you know what you did, but writing a clear, helpful commit message feels like a chore. The result? Messages like "fix bug" or "update stuff" that help no one, least of all future-you trying to understand what changed and why.
A smart commit skill solves this problem by analyzing your actual code changes and generating meaningful commit messages automatically. In this tutorial, we will build one from scratch, progressing from a simple single-shot skill to a complete, production-ready solution.
What We Are Building
Our smart commit skill will:
- Analyze git diff output to understand changes
- Categorize the type of change (feat, fix, refactor, etc.)
- Identify the scope of changes
- Generate a conventional commit message
- Include relevant details in the commit body
- Handle edge cases like large diffs or multiple change types
By the end, you will have a skill that turns this:
--- a/src/utils/format.ts
+++ b/src/utils/format.ts
@@ -10,6 +10,15 @@ export function formatDate(date: Date): string {
return date.toISOString().split('T')[0];
}
+export function formatCurrency(amount: number, currency = 'USD'): string {
+ return new Intl.NumberFormat('en-US', {
+ style: 'currency',
+ currency,
+ }).format(amount);
+}
Into this:
feat(utils): add currency formatting function
- Add formatCurrency function with locale-aware number formatting
- Support configurable currency with USD default
- Use Intl.NumberFormat for proper internationalization
Prerequisites
Before we begin, you should have:
- Basic understanding of git and commit conventions
- Familiarity with markdown skill files
- Claude Code installed and configured
Part 1: The Basic Skill
Let us start with a simple single-shot skill that generates commit messages.
Step 1: Create the Skill File
Create a new file called smart-commit.md:
---
name: smart-commit
description: Generates commit messages from git diffs
version: 1.0.0
---
# Smart Commit Generator
Generate meaningful commit messages from git diffs.
## Input
A git diff showing the changes to commit.
## Instructions
Analyze the provided git diff and generate a commit message following these rules:
### Message Format
Use the Conventional Commits format:
<type>(<scope>): <description>
[optional body]
[optional footer]
### Types
Choose the most appropriate type:
- **feat**: A new feature
- **fix**: A bug fix
- **docs**: Documentation changes
- **style**: Formatting, missing semicolons, etc.
- **refactor**: Code change that neither fixes a bug nor adds a feature
- **test**: Adding or updating tests
- **chore**: Maintenance tasks, dependency updates
### Scope
Identify the scope from the file paths:
- Use the most specific common directory
- If multiple unrelated scopes, use the primary one
- Common scopes: api, ui, auth, utils, config, docs
### Description
Write a concise description:
- Use imperative mood ("add" not "added")
- Maximum 50 characters
- No period at the end
- Focus on WHAT changed
### Body (when needed)
Add a body for complex changes:
- Explain WHY the change was made
- List key changes with bullet points
- Wrap at 72 characters
## Output
Return only the commit message, no additional explanation.
Step 2: Test the Basic Skill
Test with a simple diff:
# Get your current diff
git diff --staged
# Use the skill (in Claude Code)
/smart-commit
The skill should generate a reasonable commit message. But we can do better.
Part 2: Adding Intelligence
Let us enhance the skill to handle more complex scenarios.
Step 3: Add Change Analysis
Improve the analysis section:
## Analysis Process
### Step 1: Categorize Files
Group changed files by type:
- Source code: .ts, .js, .py, .go, etc.
- Configuration: .json, .yaml, .toml, etc.
- Documentation: .md, README, etc.
- Tests: *.test.*, *.spec.*, *_test.*
### Step 2: Identify Change Patterns
Look for patterns in the diff:
**Addition Patterns:**
- New functions or classes
- New files
- New exports
- New dependencies
**Modification Patterns:**
- Bug fixes (error handling, null checks)
- Refactoring (renaming, restructuring)
- Performance improvements
- Feature enhancements
**Deletion Patterns:**
- Removed dead code
- Cleaned up comments
- Removed deprecated functions
### Step 3: Determine Primary Change
If multiple change types present:
1. Features take priority over fixes
2. Fixes take priority over refactors
3. Refactors take priority over style changes
Note secondary changes in the body.
Step 4: Add Scope Detection
Add intelligent scope detection:
## Scope Detection
### Path Analysis
Extract scope from file paths:
src/api/users.ts → api src/components/Button.tsx → components or ui lib/utils/format.ts → utils tests/api/users.test.ts → api (from main file)
### Scope Rules
1. If all changes in one directory: use that directory
2. If changes span directories with common parent: use parent
3. If changes span unrelated areas: use primary affected area
4. If global/config changes: omit scope or use 'config'
### Common Scope Mappings
```yaml
src/api/: api
src/components/: ui
src/hooks/: hooks
src/utils/: utils
src/services/: services
src/types/: types
lib/: lib
config/: config
docs/: docs
tests/: skip (use scope from tested file)
### Step 5: Improve Message Quality
Add quality guidelines:
```markdown
## Message Quality Guidelines
### Subject Line
**Good Examples:**
- feat(auth): add password reset functionality
- fix(api): handle null response from user service
- refactor(utils): extract date formatting logic
**Bad Examples:**
- feat: add stuff (too vague)
- fix(api): Fixed the bug with the user thing (past tense, too long)
- refactor: refactored code (redundant, no details)
### Body Guidelines
**When to Include Body:**
- Changes affect multiple files
- Breaking changes
- Non-obvious reasoning
- Important implementation details
**Body Format:**
[blank line after subject] Why this change was made.
- Specific change 1
- Specific change 2
- Specific change 3
[blank line] [optional footer: BREAKING CHANGE, Closes #123, etc.]
### Special Cases
**Breaking Changes:**
feat(api)!: change authentication endpoint structure
BREAKING CHANGE: The /auth endpoint now returns a different response format. Update client code to handle new structure.
**Multiple Types:**
Choose primary type, mention others in body:
feat(api): add user preferences endpoint
Also includes:
- Fix for session timeout handling
- Refactored request validation
Part 3: Adding Tool Integration
Now let us make the skill actually read git data.
Step 6: Add Git Integration
Update the skill to use tools:
---
name: smart-commit
description: Generates commit messages from git diffs
version: 2.0.0
tools:
- Bash
---
# Smart Commit Generator
Generate meaningful commit messages by analyzing git state.
## Workflow
### Step 1: Gather Git Information
Run these commands to understand the changes:
```bash
# Get staged diff
git diff --staged
# Get list of staged files
git diff --staged --name-only
# Get recent commit messages for style reference
git log --oneline -5
Step 2: Analyze Changes
[Previous analysis content]
Step 3: Generate Message
[Previous message generation content]
Step 4: Verify and Output
Before outputting, verify:
- Subject line under 50 characters
- Type is appropriate for changes
- Scope reflects actual file paths
- Body explains non-obvious changes
Output the final commit message.
### Step 7: Add Context Awareness
Make the skill aware of project conventions:
```markdown
## Project Context
### Detect Project Type
Check for project indicators:
- package.json → Node.js project
- Cargo.toml → Rust project
- go.mod → Go project
- pyproject.toml → Python project
### Adapt to Conventions
Look for commit conventions:
- Check for .commitlintrc
- Check for CONTRIBUTING.md
- Analyze recent commit style
### Example Project Adaptations
**Monorepo:**
- Include package name in scope
- Example: feat(packages/auth): add token refresh
**Framework-Specific:**
- React: feat(components/Button): add loading state
- API: feat(endpoints/users): add pagination
Part 4: Production Hardening
Make the skill robust for real-world use.
Step 8: Add Error Handling
## Error Handling
### Empty Diff
If no staged changes:
No staged changes found. Stage your changes with 'git add' first.
### Very Large Diff
If diff exceeds 5000 lines:
1. Summarize by file rather than by line
2. Focus on file-level patterns
3. Note that detailed analysis was limited
### Binary Files
Skip binary files, note them separately:
feat(assets): add new logo images
Added:
- logo.png (binary)
- icon.svg (binary)
### Unrecognized Patterns
If changes don't fit standard types:
- Use 'chore' as fallback
- Be explicit about uncertainty
- Suggest human review
Step 9: Add Customization Options
## Configuration
### Optional Arguments
```yaml
arguments:
- name: style
type: string
default: conventional
options: [conventional, simple, detailed]
description: Commit message style
- name: emoji
type: boolean
default: false
description: Include emoji prefixes
- name: scope
type: string
default: auto
description: Override auto-detected scope
- name: type
type: string
description: Override auto-detected type
Style Variations
Conventional (default):
feat(api): add user preferences endpoint
Simple:
Add user preferences endpoint
Detailed:
feat(api): add user preferences endpoint
This commit introduces a new endpoint for managing user preferences.
Changes:
- Add GET /api/preferences for retrieving preferences
- Add PUT /api/preferences for updating preferences
- Add preference validation middleware
- Add integration tests for new endpoints
Testing: All new tests pass. Run npm test to verify.
With Emoji:
✨ feat(api): add user preferences endpoint
### Step 10: Add Interactive Mode
```markdown
## Interactive Mode
When invoked with --interactive flag:
### Step 1: Show Analysis
Analyzing changes...
Files changed: 5 Primary type: feat (new function added) Detected scope: api
Suggested message: feat(api): add user preferences endpoint
### Step 2: Offer Options
Options:
- Accept this message
- Edit type (current: feat)
- Edit scope (current: api)
- Edit description
- Add/edit body
- Cancel
### Step 3: Finalize
Final message:
feat(api): add user preferences endpoint
- Add GET endpoint for retrieving preferences
- Add PUT endpoint for updating preferences
Proceed with commit? (y/n)
Part 5: Complete Skill
Here is the complete, production-ready skill:
---
name: smart-commit
description: Intelligently generates commit messages from git changes
version: 3.0.0
tools:
- Bash
arguments:
- name: style
type: string
default: conventional
description: Message style (conventional, simple, detailed)
- name: interactive
type: boolean
default: false
description: Interactive mode for message refinement
---
# Smart Commit Generator
Generate meaningful, conventional commit messages by analyzing git changes.
## Workflow
### Phase 1: Gather Information
Use Bash to collect git data:
1. **Get staged diff:**
```bash
git diff --staged
-
Get changed files:
git diff --staged --name-only -
Get recent commits for style:
git log --oneline -5 -
Check for commit conventions:
cat .commitlintrc.json 2>/dev/null || cat .commitlintrc 2>/dev/null || echo "no config"
Phase 2: Analyze Changes
File Categorization
Group files by type and purpose:
- Source code changes
- Test changes
- Configuration changes
- Documentation changes
Change Type Detection
Identify the primary change type:
- feat: New exports, new functions, new files
- fix: Error handling, null checks, bug patches
- refactor: Renames, restructures, no behavior change
- style: Formatting only
- docs: Comments, documentation files
- test: Test files only
- chore: Dependencies, configs, tooling
Scope Detection
Determine scope from paths:
- Most specific common ancestor directory
- Or explicit module/package name
- Or primary affected area
Phase 3: Generate Message
Subject Line
<type>(<scope>): <imperative description under 50 chars>
Requirements:
- Imperative mood ("add" not "added")
- No period at end
- Lowercase start
- Under 50 characters
Body (if needed)
Include body when:
- Multiple related changes
- Non-obvious reasoning
- Breaking changes
Format:
[blank line]
Brief explanation of why and what.
- Bullet points for specific changes
- Each point is a complete thought
- Wrap at 72 characters
Footer (if applicable)
- BREAKING CHANGE: description
- Closes #issue-number
- Co-authored-by: name
Phase 4: Validate
Before output, verify:
- Subject under 50 characters
- Type matches change content
- Scope reflects file paths
- Description is clear and specific
- Body explains complex changes
Phase 5: Output
Output only the commit message, ready to use.
Error Handling
No Staged Changes
If git diff --staged is empty:
"No staged changes found. Use 'git add' to stage changes first."
Very Large Diff
If diff > 5000 lines:
- Analyze at file level only
- Note "Large changeset, file-level analysis only"
Undetectable Type
If uncertain about change type:
- Default to 'chore'
- Add note suggesting review
Examples
Example 1: Simple Feature
Diff:
+export function calculateTax(amount: number, rate: number): number {
+ return amount * rate;
+}
Output:
feat(utils): add tax calculation function
Example 2: Bug Fix
Diff:
- return user.name;
+ return user?.name ?? 'Unknown';
Output:
fix(user): handle null user in name display
Example 3: Complex Change
Diff shows: new API endpoint, tests, and documentation
Output:
feat(api): add user preferences endpoint
- Add GET /preferences for retrieving user settings
- Add PUT /preferences for updating settings
- Add validation middleware for preference values
- Add comprehensive test coverage
Quality Commitment
Every generated message will:
- Follow conventional commit format
- Use appropriate type and scope
- Be concise but descriptive
- Be ready to commit without editing
## Testing Your Skill
Verify the skill works correctly:
```markdown
## Test Cases
### Test 1: Simple Addition
Make a small change, stage it, run skill.
Expected: Clear feat/fix message.
### Test 2: Multiple Files
Change files in different directories.
Expected: Appropriate scope, bullet points in body.
### Test 3: No Changes
Run with nothing staged.
Expected: Helpful error message.
### Test 4: Large Diff
Stage many files.
Expected: Summarized message, file-level analysis.
### Test 5: Style Variations
Test each style option.
Expected: Correct format for each.
Conclusion
You have built a smart commit skill that:
- Analyzes diffs to understand what changed
- Categorizes changes into conventional commit types
- Identifies scope from file paths
- Generates messages following best practices
- Handles edge cases gracefully
- Supports customization for different workflows
This skill demonstrates the progression from a simple prompt enhancement (Level 1) to a tool-enabled skill (Level 3) that provides real value in daily development work.
Key takeaways:
- Start simple and add complexity iteratively
- Use tools to gather real context
- Handle errors gracefully
- Provide customization options
- Test with various scenarios
Your smart commit skill is ready for production. Happy committing!