Changelog Writer
Turns git commits into changelog entries humans want to read. Covers the gap between "git log --oneline" (too technical) and "nothing" (too common).
When to Use
- Before a release, version bump, or deployment
- When a CHANGELOG.md needs updating
- When you need release notes for a blog post, email, or app store update
- When commits describe implementation but not user impact
When NOT to Use
- For internal-only changes with no user-visible effect
- For very first releases (no "since last release" baseline)
Phase 1: Determine the Scope
Ask or infer:
- Git range — "since last release" (git tags), a specific SHA, or a branch diff. Default:
git log $(git describe --tags --abbrev=0)..HEAD if tags exist, else git log HEAD~20..HEAD.
- Audiences — which sections to generate. Default: all three (Users, Developers, Operators). Skip any that aren't relevant.
- Format — CHANGELOG.md (Keep a Changelog format), Markdown for a blog post, or plain text for an email.
- Version — if known, use it. Otherwise leave as
[NEXT VERSION].
Detect git range automatically
# Check if tags exist
git describe --tags --abbrev=0 2>/dev/null && \
echo "Use: git log $(git describe --tags --abbrev=0)..HEAD --pretty=format:'%H %s' --no-merges" || \
echo "No tags found — use: git log HEAD~20..HEAD --pretty=format:'%H %s' --no-merges"
Phase 2: Read the Git History
Run:
git log <range> --pretty=format:"%H %s" --no-merges
For each commit, also read the full diff summary:
git show --stat <SHA>
Group commits by type if using conventional commits (feat, fix, docs, chore, refactor, perf, test, build, ci). Otherwise, read the commit message and classify by likely intent.
Phase 3: Classify and Draft
For each commit, decide:
Affects users? → Include in "Users" section
- New features, UI changes, behavior changes, bug fixes visible to users
- Write in plain English: "You can now do X" / "Fixed: X no longer happens"
- NEVER include: commit SHA, file names, function names, internal terminology
Affects developers integrating or extending? → Include in "Developers" section
- API changes, new hooks/events, SDK changes, breaking changes, new endpoints
- Can include technical terms, but explain impact: "Breaking: X now returns Y instead of Z"
Affects deployment, configuration, or operations? → Include in "Operators" section
- New environment variables, config changes, migration steps, infra changes
- Be specific: "New required env var: FEATURE_X_ENABLED (default: false)"
Internal only? → SKIP (refactors, test changes, CI changes, code style)
Transformation examples
| Raw commit |
Translated entry |
fix: resolve null pointer in UserService.processPayment |
Fixed a bug that caused payment processing to fail for some users. |
feat: add dark mode toggle to settings panel |
You can now switch to dark mode in Settings. |
perf: replace O(n²) sort in feed ranking |
Feeds with many items now load significantly faster. |
chore: upgrade postgres driver to 16.x |
(Operators) Updated database driver — no action required unless pinning the old version. |
feat: add /v2/webhooks endpoint with retry logic |
(Developers) New: /v2/webhooks endpoint with automatic retry on failure. See API docs. |
refactor: extract PaymentGateway interface |
SKIP — internal only. |
Phase 4: Write the Entries
Rules for writing:
- Users section: Plain English. No jargon. Lead with the user benefit, not the implementation. Max 2 sentences per entry. Use "You can now...", "Fixed...", "Improved..."
- Developers section: Technical but clear. Always state the migration path for breaking changes.
- Operators section: Imperative and specific. "Set X to Y before deploying." "Run migration Z."
- No filler: Don't write "Various improvements and bug fixes" — if you can't describe it specifically, skip it.
- Group related items: Multiple commits fixing the same area → one entry.
Phase 5: Format Output
Keep a Changelog format (default)
## [NEXT VERSION] - YYYY-MM-DD
### For Users
- **New:** [feature description]
- **Fixed:** [bug fix description]
- **Changed:** [behavior change and why]
### For Developers
- **Breaking:** [what changed, how to migrate]
- **New:** [new API/hook/event]
### For Operators
- **Action required:** [what to do before deploying]
- **New config:** [variable name and purpose]
Blog post / email format
Omit the markdown headers. Write short prose paragraphs instead of bullet lists. Friendly tone, no technical filler.
If the user wants to update CHANGELOG.md
- Read the existing file.
- Identify the position of the
# Changelog header (or top of file).
- Prepend the new section immediately after the header, before any existing entries.
- Preserve all existing content verbatim.
- Offer to write it back.
Phase 6: Review Pass
Before presenting, check:
- Are all user-visible changes covered?
- Is there any internal jargon in the Users section?
- Are breaking changes clearly labeled?
- Would a non-technical user understand the Users section?
If any check fails, revise before presenting.
Anti-Patterns
| Don't |
Do instead |
| Include commit SHAs or branch names in the Users section |
Write about what the user experiences, not what the developer did |
| Write "Various improvements and bug fixes" |
Name the specific fix or improvement, or skip it |
| Put refactors and test changes in any audience section |
Internal changes have no changelog entry |
| Use function names, file names, or class names in the Users section |
Translate to user experience: "payment processing" not "UserService.processPayment" |
| Write passive vague entries ("Performance has been improved") |
Name the scenario: "Large dashboards now load in under 2 seconds" |
| Produce a Developers section entry without migration guidance for breaking changes |
Always include "Before: X. After: Y. To migrate: Z." |
| Produce an Operators section entry without the exact variable name or command |
Be specific: REDIS_MAX_CONNECTIONS=50 not "configure Redis appropriately" |
| Cover 0 commits in a section when none qualify |
Omit the section entirely if it has no entries |
The changelog is complete when:
1---2name: changelog-writer3description: Draft plain-language changelog entries from Git history, optionally updating CHANGELOG.md.4---56# Changelog Writer78Turns git commits into changelog entries humans want to read. Covers the gap between "git log --oneline" (too technical) and "nothing" (too common).910## When to Use1112- Before a release, version bump, or deployment13- When a CHANGELOG.md needs updating14- When you need release notes for a blog post, email, or app store update15- When commits describe implementation but not user impact1617## When NOT to Use1819- For internal-only changes with no user-visible effect20- For very first releases (no "since last release" baseline)2122---2324<process>2526## Phase 1: Determine the Scope2728Ask or infer:29301. **Git range** — "since last release" (git tags), a specific SHA, or a branch diff. Default: `git log $(git describe --tags --abbrev=0)..HEAD` if tags exist, else `git log HEAD~20..HEAD`.312. **Audiences** — which sections to generate. Default: all three (Users, Developers, Operators). Skip any that aren't relevant.323. **Format** — CHANGELOG.md (Keep a Changelog format), Markdown for a blog post, or plain text for an email.334. **Version** — if known, use it. Otherwise leave as `[NEXT VERSION]`.3435### Detect git range automatically3637```bash38# Check if tags exist39git describe --tags --abbrev=0 2>/dev/null && \40 echo "Use: git log $(git describe --tags --abbrev=0)..HEAD --pretty=format:'%H %s' --no-merges" || \41 echo "No tags found — use: git log HEAD~20..HEAD --pretty=format:'%H %s' --no-merges"42```4344## Phase 2: Read the Git History4546Run:47```bash48git log <range> --pretty=format:"%H %s" --no-merges49```5051For each commit, also read the full diff summary:52```bash53git show --stat <SHA>54```5556Group commits by type if using conventional commits (feat, fix, docs, chore, refactor, perf, test, build, ci). Otherwise, read the commit message and classify by likely intent.5758## Phase 3: Classify and Draft5960For each commit, decide:6162**Affects users?** → Include in "Users" section63- New features, UI changes, behavior changes, bug fixes visible to users64- Write in plain English: "You can now do X" / "Fixed: X no longer happens"65- NEVER include: commit SHA, file names, function names, internal terminology6667**Affects developers integrating or extending?** → Include in "Developers" section68- API changes, new hooks/events, SDK changes, breaking changes, new endpoints69- Can include technical terms, but explain impact: "Breaking: X now returns Y instead of Z"7071**Affects deployment, configuration, or operations?** → Include in "Operators" section72- New environment variables, config changes, migration steps, infra changes73- Be specific: "New required env var: FEATURE_X_ENABLED (default: false)"7475**Internal only?** → SKIP (refactors, test changes, CI changes, code style)7677### Transformation examples7879| Raw commit | Translated entry |80|---|---|81| `fix: resolve null pointer in UserService.processPayment` | Fixed a bug that caused payment processing to fail for some users. |82| `feat: add dark mode toggle to settings panel` | You can now switch to dark mode in Settings. |83| `perf: replace O(n²) sort in feed ranking` | Feeds with many items now load significantly faster. |84| `chore: upgrade postgres driver to 16.x` | (Operators) Updated database driver — no action required unless pinning the old version. |85| `feat: add /v2/webhooks endpoint with retry logic` | (Developers) New: `/v2/webhooks` endpoint with automatic retry on failure. See API docs. |86| `refactor: extract PaymentGateway interface` | SKIP — internal only. |8788## Phase 4: Write the Entries8990Rules for writing:91- **Users section**: Plain English. No jargon. Lead with the user benefit, not the implementation. Max 2 sentences per entry. Use "You can now...", "Fixed...", "Improved..."92- **Developers section**: Technical but clear. Always state the migration path for breaking changes.93- **Operators section**: Imperative and specific. "Set X to Y before deploying." "Run migration Z."94- **No filler**: Don't write "Various improvements and bug fixes" — if you can't describe it specifically, skip it.95- **Group related items**: Multiple commits fixing the same area → one entry.9697## Phase 5: Format Output9899### Keep a Changelog format (default)100```markdown101## [NEXT VERSION] - YYYY-MM-DD102103### For Users104105- **New:** [feature description]106- **Fixed:** [bug fix description]107- **Changed:** [behavior change and why]108109### For Developers110111- **Breaking:** [what changed, how to migrate]112- **New:** [new API/hook/event]113114### For Operators115116- **Action required:** [what to do before deploying]117- **New config:** [variable name and purpose]118```119120### Blog post / email format121122Omit the markdown headers. Write short prose paragraphs instead of bullet lists. Friendly tone, no technical filler.123124### If the user wants to update CHANGELOG.md1251261. Read the existing file.1272. Identify the position of the `# Changelog` header (or top of file).1283. Prepend the new section immediately after the header, before any existing entries.1294. Preserve all existing content verbatim.1305. Offer to write it back.131132## Phase 6: Review Pass133134Before presenting, check:135- Are all user-visible changes covered?136- Is there any internal jargon in the Users section?137- Are breaking changes clearly labeled?138- Would a non-technical user understand the Users section?139140If any check fails, revise before presenting.141142</process>143144<anti_patterns>145146## Anti-Patterns147148| Don't | Do instead |149|---|---|150| Include commit SHAs or branch names in the Users section | Write about what the user experiences, not what the developer did |151| Write "Various improvements and bug fixes" | Name the specific fix or improvement, or skip it |152| Put refactors and test changes in any audience section | Internal changes have no changelog entry |153| Use function names, file names, or class names in the Users section | Translate to user experience: "payment processing" not "UserService.processPayment" |154| Write passive vague entries ("Performance has been improved") | Name the scenario: "Large dashboards now load in under 2 seconds" |155| Produce a Developers section entry without migration guidance for breaking changes | Always include "Before: X. After: Y. To migrate: Z." |156| Produce an Operators section entry without the exact variable name or command | Be specific: `REDIS_MAX_CONNECTIONS=50` not "configure Redis appropriately" |157| Cover 0 commits in a section when none qualify | Omit the section entirely if it has no entries |158159</anti_patterns>160161<success_criteria>162163The changelog is complete when:164- [ ] Every user-visible change has an entry in plain English165- [ ] No commit SHAs, file names, or function names appear in the Users section166- [ ] Breaking changes are labeled and include a migration path167- [ ] Operator actions are imperative and specific168- [ ] Internal-only commits (refactors, CI, tests) are excluded169- [ ] Related commits are grouped, not listed individually170- [ ] The user has been offered to write/update CHANGELOG.md if applicable171172</success_criteria>