Skill for creating structured, semantic commit messages following the Conventional Commits specification
71
89%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Conventional Commits exist to make commit history machine-readable. The type prefix is a contract: feat triggers a MINOR version bump, fix triggers PATCH, and ! or BREAKING CHANGE: triggers MAJOR. Every commit is a semantic event, not just a description of code changes.
Write the header for the person reviewing a CHANGELOG six months from now, not for yourself today. The body explains why — the code already shows what.
A less obvious implication: scope consistency drives changelog quality more than type correctness. A perfectly typed feat with an ad-hoc scope like feat(getUserById): produces a one-line changelog group with no neighbours. Establish a fixed scope vocabulary early — see Scope Naming and Commit Strategy.
Apply when the user asks for help writing commit messages, wants to format staged changes as a commit, needs a CHANGELOG entry, or mentions conventional commits, semantic versioning, or version bumps.
When not to use: skip this skill if the project explicitly avoids Conventional Commits (e.g. uses Angular-style without the spec, or enforces a custom pattern via a project-specific hook). Do not override confirmed project conventions.
refactor vs feat: does the user gain new capability? If yes → featfix vs refactor: does the change correct wrong behaviour? If yes → fixperf vs refactor: is there a measurable latency/throughput improvement? If yes → perfchore vs anything else: if another type fits, use it — chore is the last resortauth, checkout, api). Omit scope when the change is truly cross-cutting. Use a fixed vocabulary if the project defines one. See scope naming for conventions.<header>."Closes: #N for issue references; BREAKING CHANGE: <description> whenever the public API surface changes. Always pair ! with a BREAKING CHANGE: footer.git commit --dry-run or your local commitlint hook to catch formatting violations before they enter history.Agent output format: Present the commit message in a fenced code block with no extra commentary, ready to paste:
feat(auth): add OAuth2 login support
Allows users to sign in with Google and GitHub. Replaces the
username/password flow for external accounts.
Closes: #412<type>[optional scope]: <description>
[optional body]
[optional footer(s)]Rules:
add not added), lowercase, no trailing period, ≤ 72 charactersToken: value format (e.g. Closes: #123, BREAKING CHANGE: description)| Type | SemVer Impact | Use When |
|---|---|---|
feat | MINOR (1.0.0 → 1.1.0) | New feature or user-facing capability |
fix | PATCH (1.0.0 → 1.0.1) | Bug fix or incorrect behavior |
build | None | Build system or dependency changes |
ci | None | CI/CD configuration changes |
docs | None | Documentation only |
style | None | Code formatting, no logic change |
refactor | None | Restructuring, same behavior |
perf | None | Measurable performance improvement |
test | None | Adding or updating tests |
chore | None | Maintenance not covered above |
revert | Varies | Reverting a previous commit |
Breaking changes: append ! after type/scope (feat!:) and/or add a BREAKING CHANGE: footer — triggers a MAJOR version bump.
Simple feature:
feat(auth): add OAuth2 login supportBug fix with context:
fix(checkout): prevent duplicate order submissions
Race condition when users double-clicked submit during network
latency. Implemented debouncing with a 2-second window.
Fixes: #234Breaking change with migration info:
refactor!: consolidate API endpoints under /api/v2
BREAKING CHANGE: All endpoints moved from /endpoints/* to /api/v2/*
Migration:
- /endpoints/users → /api/v2/users
- /endpoints/auth → /api/v2/auth
See docs/migration-v2.md for the full migration guide.Performance improvement:
perf(search): cache query results with Redis
Cache TTL set to 5 minutes. Reduces average query time from 500ms to 50ms.
Benchmark (1000 req/s sustained):
- Before: 500ms avg, 2s p99
- After: 50ms avg, 200ms p99fix(auth): fixed token expiration bug — GOOD: fix(auth): prevent token expiration on refreshchore as a catch-allchore is for maintenance tasks not covered by other types (e.g., bumping lock files). Using it for features, refactors, or docs hides semantic meaning from changelog generators.chore: update auth module, chore: fix login bug — GOOD: refactor(auth): extract token refresh logic, fix(auth): resolve null pointer on logoutfeat(checkout): add new payment processing integration with Stripe for recurring subscriptions — GOOD: feat(checkout): add Stripe recurring subscription supportBREAKING CHANGE: footer when the API surface changes! suffix alone is not sufficient for all tooling. BREAKING CHANGE: in the footer carries the migration description that changelog generators parse for release notes.feat!: remove /v1 endpoints (no footer) — GOOD: feat!: remove /v1 endpoints + footer BREAKING CHANGE: /v1/* routes deleted; migrate to /api/v2/*git log --oneline. Implementation details belong in the body.fix(db): change SQL query from SELECT * to SELECT id,name in getUserById — GOOD: fix(db): reduce getUserById query to required columns (details in body)fix(auth): prevent token expiration and update README and bump deps — GOOD: Three commits: fix(auth): prevent token expiration, docs(auth): document token refresh flow, chore(deps): bump jsonwebtoken to 9.0.2Validate a message ad-hoc:
echo "feat(auth): add OAuth2 login" | npx commitlintScan recent history:
git log --oneline -10
git log --format="%s" HEAD~5..For full commitlint setup, CHANGELOG generation, and semantic-release, see Team Tooling.