Changelog Composer
Transforms raw git history and PR descriptions into polished, audience-appropriate
changelogs. Parses conventional commits, classifies changes by impact category, filters
internal-only modifications, and produces structured release notes with PR links —
following Keep a Changelog conventions.
Reference Files
| File |
Contents |
Load When |
references/conventional-commits.md |
Commit type parsing, scope extraction, breaking change indicators |
Repository uses conventional commits |
references/categorization-rules.md |
Change classification logic, audience filtering, severity ordering |
Always |
references/audience-filter.md |
User-facing vs internal change detection, exclusion patterns |
Always |
references/changelog-formats.md |
Keep a Changelog, GitHub Releases, announcement copy templates |
Format selection needed |
Prerequisites
- git — access to the repository history
- gh (optional) — GitHub CLI for PR description extraction
- A tagging strategy (semver tags) for identifying release boundaries
Workflow
Phase 1: Gather Raw Changes
Collect all changes between the previous release and the current state:
- Identify boundaries — Find the last release tag:
git describe --tags --abbrev=0.
If no tags exist, use the initial commit or a user-specified starting point.
- Extract commits —
git log <last-tag>..HEAD --oneline --no-merges
- Extract PR titles —
gh pr list --state merged --base main --search "merged:>YYYY-MM-DD"
or parse merge commit messages.
- Parse conventional commits — If the repository follows conventional commits
(
feat:, fix:, docs:, etc.), extract type, scope, and description. See
references/conventional-commits.md.
- Collect breaking change indicators — Look for
BREAKING CHANGE: in commit
bodies, ! after type (feat!:), or explicit annotations in PR descriptions.
Phase 2: Classify Changes
Categorize each change by its impact:
| Category |
Conventional Commit Type |
Indicators |
| Breaking Changes |
feat!:, BREAKING CHANGE: |
API removal, signature change, behavior change |
| Features |
feat: |
New capability, new endpoint, new command |
| Fixes |
fix: |
Bug correction, error handling improvement |
| Performance |
perf: |
Speed improvement, memory reduction |
| Documentation |
docs: |
README, API docs, guides |
| Internal |
chore:, ci:, refactor:, test:, build: |
No user-facing impact |
For repositories without conventional commits, classify by reading the commit message
and changed files. Code changes to public API → Feature or Fix. Test-only changes → Internal.
Phase 3: Filter for Audience
Exclude internal changes by default:
- CI/CD configuration changes
- Test additions/modifications
- Dependency bumps (unless security-relevant)
- Code refactoring with no behavior change
- Build system changes
Include internal changes only when:
- They represent significant architecture shifts users should know about
- They affect development workflow (contributing guide changes)
- The changelog targets developers, not end-users
Highlight breaking changes prominently — always at the top, always with migration
guidance.
Phase 4: Compose Entries
For each included change, write a human-readable description:
- Lead with the impact — "Users can now..." or "Fixed issue where..."
- Be specific — "Reduced memory usage by 40% for large file processing" not
"Performance improvements"
- Include migration guidance for breaking changes — what the user must change
- Link to source — PR number, issue number, or commit hash
Phase 5: Output
Assemble the changelog in the requested format, ordered by severity:
- Breaking Changes (always first)
- Features
- Fixes
- Performance
- Documentation
Output Format
## [{version}] - {YYYY-MM-DD}
### Breaking Changes
- **`function_name` parameter renamed** — `old_param` is now `new_param`.
Migration: find/replace `old_param=` with `new_param=` in all call sites. ([#{pr}]({url}))
### Features
- **{Feature name}** — {What it enables and why it matters}. ([#{pr}]({url}))
### Fixes
- Fixed {symptom} when {condition}. ([#{pr}]({url}))
### Performance
- {Operation} is now {X}x faster / uses {X}% less memory. ([#{pr}]({url}))
### Documentation
- Added {guide/reference} for {topic}. ([#{pr}]({url}))
Configuring Scope
| Mode |
Input |
Output |
When to Use |
release |
Tag-to-HEAD |
Full changelog entry |
Preparing a versioned release |
sprint |
Date range or commit range |
Summary of changes |
Sprint review, status update |
pr |
Single PR |
One-line changelog entry |
PR description template |
Calibration Rules
- User impact first. Every entry should answer "what does this mean for the user?"
not "what did the developer do?"
- Breaking changes are non-negotiable. Never omit or bury breaking changes. They
go first, with migration guidance.
- Specific over vague. "Fixed login timeout on slow connections" beats "Fixed bug."
"Added CSV export for reports" beats "New feature."
- Link everything. Every entry links to its source PR or issue. Users who want
details can follow the link.
- Exclude noise. Internal refactoring, dependency bumps, and CI changes do not
belong in user-facing changelogs unless they have user-visible impact.
Error Handling
| Problem |
Resolution |
| No tags exist in repository |
Ask for a starting commit or date. Default to the initial commit if the repository is small. |
| Repository doesn't use conventional commits |
Classify by reading commit messages and changed files. Note reduced classification accuracy. |
| PR descriptions are empty or low-quality |
Fall back to commit messages. Flag entries that may need manual review. |
| Ambiguous change classification |
Default to "Features" for additions, "Fixes" for modifications. Mark uncertain entries for review. |
| Too many changes for a single release |
Group by component/module. Consider whether the release should be split. |
| Merge commits obscure individual changes |
Use --no-merges to skip merge commits. Parse individual commits within merged PRs. |
When NOT to Compose
Push back if:
- The user wants to auto-publish release notes without review — changelogs require human judgment
- The repository has no meaningful commit history (single "initial commit" with everything)
- The request is for marketing copy, not technical release notes — different skill
- The changes are not yet merged — changelog is for shipped changes, not in-progress work
1---2name: changelog-composer3description: Generates structured changelogs and release notes from git history and PRs, classifying breaking changes, features, fixes, performance, docs. Triggers on: "generate changelog", "write release notes", "what changed since", "prepare release", "release notes for", "diff since tag".4---56# Changelog Composer78Transforms raw git history and PR descriptions into polished, audience-appropriate9changelogs. Parses conventional commits, classifies changes by impact category, filters10internal-only modifications, and produces structured release notes with PR links —11following Keep a Changelog conventions.1213## Reference Files1415| File | Contents | Load When |16| ------------------------------------ | ------------------------------------------------------------------ | ------------------------------------ |17| `references/conventional-commits.md` | Commit type parsing, scope extraction, breaking change indicators | Repository uses conventional commits |18| `references/categorization-rules.md` | Change classification logic, audience filtering, severity ordering | Always |19| `references/audience-filter.md` | User-facing vs internal change detection, exclusion patterns | Always |20| `references/changelog-formats.md` | Keep a Changelog, GitHub Releases, announcement copy templates | Format selection needed |2122## Prerequisites2324- **git** — access to the repository history25- **gh** (optional) — GitHub CLI for PR description extraction26- A tagging strategy (semver tags) for identifying release boundaries2728## Workflow2930### Phase 1: Gather Raw Changes3132Collect all changes between the previous release and the current state:33341. **Identify boundaries** — Find the last release tag: `git describe --tags --abbrev=0`.35 If no tags exist, use the initial commit or a user-specified starting point.362. **Extract commits** — `git log <last-tag>..HEAD --oneline --no-merges`373. **Extract PR titles** — `gh pr list --state merged --base main --search "merged:>YYYY-MM-DD"`38 or parse merge commit messages.394. **Parse conventional commits** — If the repository follows conventional commits40 (`feat:`, `fix:`, `docs:`, etc.), extract type, scope, and description. See41 `references/conventional-commits.md`.425. **Collect breaking change indicators** — Look for `BREAKING CHANGE:` in commit43 bodies, `!` after type (`feat!:`), or explicit annotations in PR descriptions.4445### Phase 2: Classify Changes4647Categorize each change by its impact:4849| Category | Conventional Commit Type | Indicators |50| ---------------- | ----------------------------------------------- | ---------------------------------------------- |51| Breaking Changes | `feat!:`, `BREAKING CHANGE:` | API removal, signature change, behavior change |52| Features | `feat:` | New capability, new endpoint, new command |53| Fixes | `fix:` | Bug correction, error handling improvement |54| Performance | `perf:` | Speed improvement, memory reduction |55| Documentation | `docs:` | README, API docs, guides |56| Internal | `chore:`, `ci:`, `refactor:`, `test:`, `build:` | No user-facing impact |5758For repositories without conventional commits, classify by reading the commit message59and changed files. Code changes to public API → Feature or Fix. Test-only changes → Internal.6061### Phase 3: Filter for Audience62631. **Exclude internal changes** by default:64 - CI/CD configuration changes65 - Test additions/modifications66 - Dependency bumps (unless security-relevant)67 - Code refactoring with no behavior change68 - Build system changes69702. **Include internal changes** only when:71 - They represent significant architecture shifts users should know about72 - They affect development workflow (contributing guide changes)73 - The changelog targets developers, not end-users74753. **Highlight breaking changes** prominently — always at the top, always with migration76 guidance.7778### Phase 4: Compose Entries7980For each included change, write a human-readable description:81821. **Lead with the impact** — "Users can now..." or "Fixed issue where..."832. **Be specific** — "Reduced memory usage by 40% for large file processing" not84 "Performance improvements"853. **Include migration guidance** for breaking changes — what the user must change864. **Link to source** — PR number, issue number, or commit hash8788### Phase 5: Output8990Assemble the changelog in the requested format, ordered by severity:91921. Breaking Changes (always first)932. Features943. Fixes954. Performance965. Documentation9798## Output Format99100```text101## [{version}] - {YYYY-MM-DD}102103### Breaking Changes104- **`function_name` parameter renamed** — `old_param` is now `new_param`.105 Migration: find/replace `old_param=` with `new_param=` in all call sites. ([#{pr}]({url}))106107### Features108- **{Feature name}** — {What it enables and why it matters}. ([#{pr}]({url}))109110### Fixes111- Fixed {symptom} when {condition}. ([#{pr}]({url}))112113### Performance114- {Operation} is now {X}x faster / uses {X}% less memory. ([#{pr}]({url}))115116### Documentation117- Added {guide/reference} for {topic}. ([#{pr}]({url}))118```119120## Configuring Scope121122| Mode | Input | Output | When to Use |123| --------- | -------------------------- | ------------------------ | ----------------------------- |124| `release` | Tag-to-HEAD | Full changelog entry | Preparing a versioned release |125| `sprint` | Date range or commit range | Summary of changes | Sprint review, status update |126| `pr` | Single PR | One-line changelog entry | PR description template |127128## Calibration Rules1291301. **User impact first.** Every entry should answer "what does this mean for the user?"131 not "what did the developer do?"1322. **Breaking changes are non-negotiable.** Never omit or bury breaking changes. They133 go first, with migration guidance.1343. **Specific over vague.** "Fixed login timeout on slow connections" beats "Fixed bug."135 "Added CSV export for reports" beats "New feature."1364. **Link everything.** Every entry links to its source PR or issue. Users who want137 details can follow the link.1385. **Exclude noise.** Internal refactoring, dependency bumps, and CI changes do not139 belong in user-facing changelogs unless they have user-visible impact.140141## Error Handling142143| Problem | Resolution |144| ------------------------------------------- | -------------------------------------------------------------------------------------------------- |145| No tags exist in repository | Ask for a starting commit or date. Default to the initial commit if the repository is small. |146| Repository doesn't use conventional commits | Classify by reading commit messages and changed files. Note reduced classification accuracy. |147| PR descriptions are empty or low-quality | Fall back to commit messages. Flag entries that may need manual review. |148| Ambiguous change classification | Default to "Features" for additions, "Fixes" for modifications. Mark uncertain entries for review. |149| Too many changes for a single release | Group by component/module. Consider whether the release should be split. |150| Merge commits obscure individual changes | Use `--no-merges` to skip merge commits. Parse individual commits within merged PRs. |151152## When NOT to Compose153154Push back if:155156- The user wants to auto-publish release notes without review — changelogs require human judgment157- The repository has no meaningful commit history (single "initial commit" with everything)158- The request is for marketing copy, not technical release notes — different skill159- The changes are not yet merged — changelog is for shipped changes, not in-progress work