| name | changelog-generator |
| description | Parse git history and produce or update a CHANGELOG.md following the Keep a Changelog convention. Supports Conventional Commits, basic prefix conventions, and unstructured commit messages. Intelligently categorizes changes, detects breaking changes, links to PRs/issues, and handles both initial generation and incremental updates.
**Triggers — use this skill when:** - User asks to "generate", "create", "update", or "write" a changelog - User mentions "CHANGELOG", "changelog", "release notes" - User says "document changes", "what changed since last release" - User wants to "prepare a release" and needs a changelog entry - User asks to "clean up" or "reformat" an existing changelog
**Covers:** Any git-based project. Handles Conventional Commits (feat/fix/chore), Angular convention, basic prefixes (Add/Fix/Remove), and freeform commit messages. Outputs Keep a Changelog format with optional Common Changelog enhancements.
|
Changelog Generator
Parse a project's git history and produce a high-quality CHANGELOG.md following
the Keep a Changelog convention.
Philosophy
A changelog is for humans, not machines. It answers: "What meaningful changes
happened between version X and version Y?" Raw commit logs fail at this because
they contain noise — typo fixes, merge commits, CI tweaks, and refactors that
don't affect users. This skill filters signal from noise and writes entries that
a user or contributor can actually understand.
Workflow
Phase 1 — Repository Analysis
Before generating anything, understand the project's commit and versioning patterns.
git rev-parse --is-inside-work-tree 2>/dev/null
git tag --sort=-creatordate | head -30
git tag --sort=-creatordate | head -10
git log --oneline -30
cat CHANGELOG.md 2>/dev/null
cat HISTORY.md 2>/dev/null
cat CHANGES.md 2>/dev/null
cat .commitlintrc* commitlint.config.* 2>/dev/null
cat .czrc .cz.toml 2>/dev/null
cat cliff.toml 2>/dev/null
cat .versionrc* 2>/dev/null
git remote get-url origin 2>/dev/null
cat package.json 2>/dev/null | grep '"version"'
cat pyproject.toml 2>/dev/null | grep 'version'
cat Cargo.toml 2>/dev/null | grep '^version'
Extract from the analysis:
| Fact | How to determine |
|---|
| Commit convention | Pattern match on recent commits (see detection rules below) |
| Versioning scheme | Tag format analysis |
| Latest version/tag | Most recent tag |
| Remote URL | git remote get-url origin — needed for PR/issue links |
| Existing changelog | Check for CHANGELOG.md or variants |
| Unreleased commits | Commits since the latest tag |
Phase 2 — Commit Convention Detection
Analyze the last 30–50 commits to detect which convention is in use. Apply these rules in order:
Conventional Commits / Angular
Pattern: ^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?!?:
Example: feat(auth): add OAuth2 support
Example: fix!: resolve race condition in queue
→ If >50% of commits match this pattern, use Conventional Commits parsing.
Basic Prefix
Pattern: ^(Add|Fix|Remove|Update|Change|Deprecate|Security)[: ]
Example: Add user export feature
Example: Fix login redirect loop
→ If >50% of commits match this pattern, use basic prefix parsing.
Freeform / No Convention
→ If neither pattern dominates, use AI-assisted categorization (see Phase 3).
Report the detected convention to the user before proceeding.
Phase 3 — Commit Extraction & Categorization
Extract commits for the target range and categorize them.
git log $(git describe --tags --abbrev=0 2>/dev/null)..HEAD \
--pretty=format:"%H|%s|%b|%an|%ae|%aI" 2>/dev/null
git log v1.1.0..v1.2.0 \
--pretty=format:"%H|%s|%b|%an|%ae|%aI"
git log --pretty=format:"%H|%s|%b|%an|%ae|%aI"
git log --merges --oneline -20
Category Mapping
Map commits to Keep a Changelog categories:
| Keep a Changelog Category | Conventional Commits types | Basic prefixes | Description |
|---|
| Added | feat | Add, Create, Implement | New features |
| Changed | refactor, perf, style | Change, Update, Refactor, Improve | Changes to existing functionality |
| Deprecated | — (detect from message) | Deprecate | Soon-to-be removed features |
| Removed | — (detect from message) | Remove, Delete, Drop | Removed features |
| Fixed | fix | Fix, Resolve, Correct | Bug fixes |
| Security | — (detect from message) | Security | Vulnerability fixes |
Entries to SKIP (noise filtering)
Do NOT include these in the changelog unless they have user-facing impact:
- Merge commits (
Merge branch..., Merge pull request...) — extract the PR title instead
- CI/CD changes (
ci:, build:, changes only to .github/workflows/)
- Chore commits (
chore:, dependency bumps without breaking changes)
- Typo fixes in code comments
- Formatting/linting-only changes (
style: that don't affect behavior)
- Version bump commits (
chore: bump version to...)
- Commits that only touch test files (unless adding test coverage is noteworthy)
Freeform Commit Categorization
When commits don't follow a convention, categorize by analyzing the message content:
- Contains "add", "new", "create", "implement", "introduce" → Added
- Contains "fix", "resolve", "correct", "patch", "bug" → Fixed
- Contains "remove", "delete", "drop" → Removed
- Contains "deprecate" → Deprecated
- Contains "security", "vulnerability", "CVE" → Security
- Contains "update", "change", "refactor", "improve", "optimize" → Changed
- If unclear, use the diff to determine: new files = Added, deleted files = Removed, modified = Changed
Breaking Change Detection
Flag breaking changes regardless of convention:
- Conventional:
! after type/scope, or BREAKING CHANGE: in footer
- Message content: "breaking", "incompatible", "migration required"
- SemVer major bump between tags
- Removal of public API functions/endpoints
Mark breaking entries with BREAKING: prefix in the changelog.
Phase 4 — Entry Writing
Transform raw commit data into human-readable changelog entries.
Writing Rules
- Write for the user, not the developer. "Add dark mode support" not "feat(ui): implement dark-mode toggle via CSS custom properties in ThemeProvider"
- Use imperative mood. "Add", "Fix", "Remove" — not "Added", "Fixed", "Removed"
- One entry per meaningful change. Squash related commits into a single entry
- Include references. Link to PR numbers and issues:
(#123), ([#123](url))
- Credit authors for external contributions.
(@username) for non-core contributors
- Lead with impact. Put the most important changes first within each category
- Be specific. "Fix crash when uploading files larger than 10MB" not "Fix upload bug"
- Keep entries to one line. If you need more, the change probably needs multiple entries or a migration guide
Entry Format
- Add dark mode support with automatic system preference detection (#142)
- **BREAKING:** Remove deprecated `v1/users` endpoint; use `v2/users` instead (#138)
- Fix memory leak in WebSocket connection handler (#145)
Phase 5 — Assemble the Changelog
File Format (Keep a Changelog)
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- Description of new feature (#PR)
### Fixed
- Description of bug fix (#PR)
## [1.2.0] - 2025-09-15
### Added
- Add OAuth2 authentication support (#89)
- Add CSV export for user data (#92)
### Changed
- Improve query performance for large datasets (#91)
### Fixed
- Fix timezone handling in scheduled reports (#88)
- Fix incorrect pagination count on filtered results (#90)
### Removed
- **BREAKING:** Remove legacy XML API endpoint (#87)
## [1.1.0] - 2025-06-01
### Added
- ...
[Unreleased]: https://github.com/user/repo/compare/v1.2.0...HEAD
[1.2.0]: https://github.com/user/repo/compare/v1.1.0...v1.2.0
[1.1.0]: https://github.com/user/repo/compare/v1.0.0...v1.1.0
Critical Format Rules
- H1 for title.
# Changelog — only one H1
- H2 for versions.
## [1.2.0] - 2025-09-15 — square brackets around version, ISO date
- H3 for categories.
### Added, ### Changed, etc. — only use categories that have entries
- Comparison links at the bottom. Every version heading must have a corresponding comparison link
- Unreleased section at top. Always present, even if empty (remove if the project prefers not to have it)
- Newest version first. Reverse chronological order
- ISO 8601 dates.
YYYY-MM-DD — no exceptions
- Empty categories. Do NOT include a category heading if there are no entries under it
Operating Modes
Mode A: Generate from Scratch
No existing changelog. Generate the full history.
- Get all tags sorted chronologically
- For each tag pair (oldest to newest), extract and categorize commits
- Assemble full changelog
- If there are many tags (>10), ask the user if they want all history or just recent versions
git tag --sort=creatordate
git log -1 --format=%aI v1.0.0
Mode B: Update Existing Changelog
A CHANGELOG.md already exists. Add new entries.
- Parse the existing changelog to find the latest documented version
- Determine the commit range: latest documented version → HEAD (or → new tag)
- Generate entries only for the new range
- Insert the new version section after
## [Unreleased] (or at the top if no Unreleased)
- Update comparison links at the bottom
- Preserve all existing content exactly as-is
Never modify existing entries unless the user explicitly asks for a reformat.
Mode C: Reformat / Clean Up
User has a messy or inconsistent changelog. Rewrite to match the standard.
- Parse all existing entries
- Re-categorize under Keep a Changelog headings
- Fix date formats to ISO 8601
- Add missing comparison links
- Fix heading hierarchy
- Show a diff/summary of changes to the user before writing
Mode D: Prepare Release
User wants to create a changelog entry for an upcoming release.
- Extract commits since the last tag
- Categorize and write entries
- Ask the user for the new version number (or suggest one based on changes: major if breaking, minor if features, patch if only fixes)
- Move entries from
## [Unreleased] to the new version section
- Update comparison links
- Optionally suggest a SemVer bump
Handling Edge Cases
No Tags
If the project has no tags, treat all commits as unreleased. Ask the user if they want to:
- Generate a single
## [Unreleased] section
- Create a
## [0.1.0] retroactively from the initial commit
Monorepo
If the project is a monorepo with multiple packages:
- Ask which package to generate for
- Filter commits by path:
git log -- packages/package-name/
- Consider separate changelogs per package
Squash Merges
If the project uses squash merges, the PR title becomes the commit message. These are often well-written and can be used directly as changelog entries.
Very Large History
If there are >500 commits to process:
- Process in batches by tag range
- Ask the user if they want full history or just the last N versions
- For initial generation, suggest starting from the latest 3-5 versions
Pre-1.0 Projects
For projects that haven't reached 1.0:
- Minor bumps may contain breaking changes (per SemVer spec)
- Still flag breaking changes but note the pre-1.0 context
Quality Checklist
After generating, verify:
Format
Content
Accuracy
Anti-Patterns to Avoid
- Dump of commit messages — A changelog is not
git log --oneline. Curate and rewrite.
- "Bug fixes and improvements" — This tells the user nothing. Be specific.
- Including every single commit — Filter noise. Only user-facing changes matter.
- Inconsistent tense — Always imperative mood: "Add", "Fix", "Remove"
- Missing links — Always include comparison links at the bottom
- Invented version numbers — Only use versions that correspond to actual tags
- Reformatting existing entries — When updating, never touch historical entries unless asked
- Skipping the preamble — Always include the "format is based on" reference lines
Output
The final output should be:
- Convention report: What commit convention was detected, how many commits processed
- The CHANGELOG.md file: Written to the project root, ready to commit
- Summary: Number of versions documented, total entries, any commits that were ambiguous or skipped
Always write the changelog as a file so the user can review and commit it directly.