/changelog — Update Changelog from Recent Changes
Adds entries to a CHANGELOG.md file following the Keep a Changelog format by analyzing git history and uncommitted changes.
Tools
- Bash — Run
git log, git tag, git diff, git diff --cached, git status, and git remote commands
- Read — Read the existing
CHANGELOG.md
- Edit — Insert new entries into the changelog (preferred for updates)
- Write — Create
CHANGELOG.md from scratch when it doesn't exist
Arguments
The skill accepts an optional argument string:
- No argument: Analyze commits since the last changelog entry or tag and add entries to
## [Unreleased]
- Version string (e.g.,
1.2.0): Add entries under ## [1.2.0] - YYYY-MM-DD using today's date
unreleased: Explicitly target the ## [Unreleased] section
consolidate: Run housekeeping on the existing changelog — merge duplicates, remove stale entries superseded by newer ones, and tighten wording (see Housekeeping)
Changelog Format
The file MUST follow Keep a Changelog 1.1.0:
# 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
- New feature description
### Changed
- Modified behavior description
### Deprecated
- Soon-to-be removed feature
### Removed
- Deleted feature description
### Fixed
- Bug fix description
### Security
- Vulnerability fix description
## [1.0.0] - 2024-01-15
### Added
- Initial release features
Rules
- Section order is fixed: Added, Changed, Deprecated, Removed, Fixed, Security — never reorder or alphabetize
- Dates use ISO 8601 format:
YYYY-MM-DD
- Latest version appears first, just below
[Unreleased]
- Only include sections that have entries (don't add empty sections)
- Each entry is a bullet point starting with
-
- Entries are for humans: write clear, concise descriptions of what changed and why it matters, not raw commit messages
- Group related commits into a single entry when they address the same change
- Don't duplicate entries already present in the changelog
Commit-to-Section Mapping
| Commit Signal |
Section |
feat:, feat(scope):, new files, new modules, new API endpoints |
Added |
refactor:, perf:, behavior changes, API changes |
Changed |
Deprecation notices, deprecated in message |
Deprecated |
Deleted files, removed features, remove in message |
Removed |
fix:, fix(scope):, bug fixes, error corrections |
Fixed |
security:, vulnerability patches, dependency security updates |
Security |
feat!:, fix!:, BREAKING CHANGE: in footer |
Prefix entry with "BREAKING:" |
When a commit includes a scope (e.g., feat(api): add endpoint), use the scope as component context in the entry (e.g., "Add API endpoint"). When a commit doesn't clearly map to a type, read the diff to determine the appropriate section.
Decision Rules for Ambiguous Cases
- If a commit both adds and removes user-visible functionality, categorize by the primary intent (e.g.,
feat: replace old auth with OAuth is Added, not Removed)
- If a refactor removes user-visible features or endpoints, categorize as Removed, not Changed
- If a performance improvement noticeably changes user experience (e.g., faster page loads), categorize as Changed
- Skip docs-only commits (
README.md, CONTRIBUTING.md) unless they document a user-visible change (e.g., correcting a license is Fixed)
Initial commit messages: skip if the changelog is being created retroactively for an established project; include as Added if this is genuinely a new project with meaningful initial functionality
Execution Steps
Find or create CHANGELOG.md in the repository root.
- If creating, add the standard header.
Determine the target section:
- If the changelog has an
## [Unreleased] section and no version argument was given, use it.
- If a version argument was given, find or create
## [version] - YYYY-MM-DD.
- If no
[Unreleased] exists and no version given, create ## [Unreleased] below the header.
Collect recent changes:
- Determine scope using git tags first: find the most recent tag matching
v* or semver pattern.
- If no tags exist, fall back to parsing the latest
## [x.y.z] heading in the changelog to identify the last documented version, then use git log from that point.
- If neither tags nor changelog versions exist, limit to the last 50 commits to avoid excessive analysis. The user can override scope by providing a commit range or tag as argument.
- Run
git log --no-merges to get commits within the determined scope.
- Run
git diff against the last version tag if available.
- Run
git diff --cached to collect staged but uncommitted changes.
- Run
git diff (no arguments) to collect unstaged changes in the working tree.
- Combine committed and uncommitted changes. When the only source is uncommitted changes (no new commits in scope), still generate entries from those changes.
Analyze and categorize:
- Parse commit messages for type prefixes (
feat:, fix:, etc.).
- Read diffs for commits without clear type prefixes.
- Group related commits into single entries.
- Skip merge commits and changelog-only commits.
- Skip commits that only touch CI config, documentation, or non-user-facing files (e.g.,
.github/, README.md, CHANGELOG.md) unless they represent meaningful user-visible changes.
Write entries:
- Use clear, human-readable language.
- Start each entry with an imperative verb matching the section: Add, Change, Deprecate, Remove, Fix, Secure.
- Include relevant context (what component, what behavior).
- Prefix breaking changes with BREAKING: (detect via
! suffix on commit type, e.g. feat!:, or BREAKING CHANGE: in commit footer).
- Order entries within a section by significance: most impactful changes first.
- Don't include commit hashes or author names.
Insert into the changelog:
- Add section headers only for sections that have entries.
- Place new entries in the target section.
- Preserve all existing content below.
- Don't modify existing entries.
Generate comparison links:
Write to the changelog — auto-apply by default:
- Apply the changes directly using Edit (or Write for new files) without asking for confirmation.
- Exception — malformed changelog: If the existing file cannot be parsed (missing headers, broken markdown structure, unparseable version sections), stop and use AskUserQuestion to show the user what's wrong and ask how to proceed before overwriting.
Housekeeping
When invoked with consolidate (or when the user asks to "clean up the changelog", "consolidate entries", "tidy the changelog"), run a housekeeping pass on the existing CHANGELOG.md:
Scan for duplicates: Find entries across sections that describe the same change in different words (e.g., "Add retry logic to API client" and "Add automatic retries for failed API requests"). Merge them into the strongest single entry, keeping it in the most appropriate section.
Detect superseded entries: When a later entry makes an earlier one obsolete, remove the stale one. Common patterns:
- A feature was added, then rewritten — keep only the rewrite (or the final state).
- A bug was fixed, then the fix was reverted and re-fixed differently — keep only the final fix.
- A deprecation notice followed by actual removal — keep the removal, drop the deprecation (unless they're in different version sections).
Tighten wording: Shorten verbose entries without losing meaning. Each entry should be one concise line.
Preserve version boundaries: Never move entries between versioned sections (## [x.y.z]). Housekeeping within ## [Unreleased] is unrestricted. Within versioned sections, only merge duplicates and tighten wording — do not delete entries.
Apply changes: Apply the consolidated entries directly using Edit without asking for confirmation. The same malformed-changelog exception from step 8 applies here.
Examples
Example 1: Adding to Unreleased
Before:
## [Unreleased]
### Fixed
- Resolve timeout on large file uploads
After running /changelog:
## [Unreleased]
### Added
- Support for WebSocket connections in the API gateway
### Fixed
- Resolve timeout on large file uploads
- Correct off-by-one error in pagination logic
Example 2: Creating a versioned release
Running /changelog 2.1.0:
## [2.1.0] - 2026-04-20
### Added
- Bulk import endpoint for market data
- Rate limiting middleware with configurable thresholds
### Changed
- Upgrade authentication flow to use PKCE
### Fixed
- Memory leak in connection pool during high traffic
Example 3: Creating changelog from scratch
Running /changelog in a repo without CHANGELOG.md:
# 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
- Initial project setup with CLI interface
- Configuration file support
- Logging framework integration
Error Handling
Handle these conditions gracefully:
- Not a git repository: Inform user and stop — do not attempt to create a changelog without git history
- Invalid version argument: Validate that the argument matches semver (
X.Y.Z); reject with a clear message if not
- Malformed changelog: If the existing file cannot be parsed (missing headers, broken markdown structure, unparseable version sections), stop and use AskUserQuestion to show the user what's wrong and ask how to proceed before overwriting
- Git command failures: If
git log or git diff fails, report the error and stop rather than producing incomplete entries
- No changes in scope: If scope detection finds zero commits and no uncommitted changes, inform the user — do not add empty sections
Anti-Patterns
- Raw commit messages: Don't paste commit messages verbatim; rewrite for humans
- Empty sections: Don't include section headers with no entries
- Overwriting: Never modify or delete existing changelog entries during normal operation (adding new entries). The only exception is
consolidate mode, which explicitly merges and cleans up entries
- Implementation details: Focus on user-visible changes, not internal refactoring noise
- Duplicate entries: Check existing entries before adding new ones
- Unbounded history: Don't analyze the entire git history when there are no tags — cap at 50 commits
- CI/docs noise: Don't add entries for commits that only change CI config, linting rules, or documentation unless user-visible
- Future tense: Write entries as completed actions ("Add support for X"), not future plans ("Will add support for X")
- Unmarked breaking changes: Always clearly mark breaking changes (e.g., prefix with "BREAKING:")
- Stale comparison links: When adding a new version, update all comparison link references at the bottom of the file
- Unnecessary confirmation prompts: Don't ask for approval before writing — auto-apply is the default. Only stop and ask when the existing changelog is malformed and can't be parsed safely
- Cross-version consolidation: During housekeeping, never move or delete entries from versioned release sections — only consolidate within
[Unreleased]
1---2name: changelog3description: This skill should be used when the user asks to "update the changelog", "add changelog entries", "generate release notes", "document recent changes", "write a changelog", "summarize recent changes", "prepare a release", "clean up the changelog", "consolidate changelog entries", "tidy the changelog", "create a changelog", "draft release notes", or says /changelog. It adds entries to CHANGELOG.md following Keep a Changelog format by analyzing git history and uncommitted changes, categorizing them into Added, Changed, Deprecated, Removed, Fixed, and Security sections. It can also consolidate existing entries to remove duplicates and superseded items.4license: Apache-2.05---67# /changelog — Update Changelog from Recent Changes89Adds entries to a `CHANGELOG.md` file following the [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format by analyzing git history and uncommitted changes.1011## Tools1213- **Bash** — Run `git log`, `git tag`, `git diff`, `git diff --cached`, `git status`, and `git remote` commands14- **Read** — Read the existing `CHANGELOG.md`15- **Edit** — Insert new entries into the changelog (preferred for updates)16- **Write** — Create `CHANGELOG.md` from scratch when it doesn't exist1718## Arguments1920The skill accepts an optional argument string:2122- **No argument**: Analyze commits since the last changelog entry or tag and add entries to `## [Unreleased]`23- **Version string** (e.g., `1.2.0`): Add entries under `## [1.2.0] - YYYY-MM-DD` using today's date24- **`unreleased`**: Explicitly target the `## [Unreleased]` section25- **`consolidate`**: Run housekeeping on the existing changelog — merge duplicates, remove stale entries superseded by newer ones, and tighten wording (see [Housekeeping](#housekeeping))2627## Changelog Format2829The file MUST follow [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/):3031```markdown32# Changelog3334All notable changes to this project will be documented in this file.3536The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),37and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).3839## [Unreleased]4041### Added42- New feature description4344### Changed45- Modified behavior description4647### Deprecated48- Soon-to-be removed feature4950### Removed51- Deleted feature description5253### Fixed54- Bug fix description5556### Security57- Vulnerability fix description5859## [1.0.0] - 2024-01-156061### Added62- Initial release features63```6465### Rules6667- **Section order** is fixed: Added, Changed, Deprecated, Removed, Fixed, Security — never reorder or alphabetize68- **Dates** use ISO 8601 format: `YYYY-MM-DD`69- **Latest version** appears first, just below `[Unreleased]`70- **Only include sections** that have entries (don't add empty sections)71- **Each entry** is a bullet point starting with `- `72- **Entries are for humans**: write clear, concise descriptions of what changed and why it matters, not raw commit messages73- **Group related commits** into a single entry when they address the same change74- **Don't duplicate** entries already present in the changelog7576## Commit-to-Section Mapping7778| Commit Signal | Section |79|---------------|---------|80| `feat:`, `feat(scope):`, new files, new modules, new API endpoints | **Added** |81| `refactor:`, `perf:`, behavior changes, API changes | **Changed** |82| Deprecation notices, `deprecated` in message | **Deprecated** |83| Deleted files, removed features, `remove` in message | **Removed** |84| `fix:`, `fix(scope):`, bug fixes, error corrections | **Fixed** |85| `security:`, vulnerability patches, dependency security updates | **Security** |86| `feat!:`, `fix!:`, `BREAKING CHANGE:` in footer | **Prefix entry with "BREAKING:"** |8788When a commit includes a scope (e.g., `feat(api): add endpoint`), use the scope as component context in the entry (e.g., "Add API endpoint"). When a commit doesn't clearly map to a type, read the diff to determine the appropriate section.8990### Decision Rules for Ambiguous Cases9192- If a commit both adds and removes user-visible functionality, categorize by the primary intent (e.g., `feat: replace old auth with OAuth` is **Added**, not Removed)93- If a refactor removes user-visible features or endpoints, categorize as **Removed**, not Changed94- If a performance improvement noticeably changes user experience (e.g., faster page loads), categorize as **Changed**95- Skip docs-only commits (`README.md`, `CONTRIBUTING.md`) unless they document a user-visible change (e.g., correcting a license is **Fixed**)96- `Initial commit` messages: skip if the changelog is being created retroactively for an established project; include as **Added** if this is genuinely a new project with meaningful initial functionality9798## Execution Steps991001. **Find or create `CHANGELOG.md`** in the repository root.101 - If creating, add the standard header.1021032. **Determine the target section**:104 - If the changelog has an `## [Unreleased]` section and no version argument was given, use it.105 - If a version argument was given, find or create `## [version] - YYYY-MM-DD`.106 - If no `[Unreleased]` exists and no version given, create `## [Unreleased]` below the header.1071083. **Collect recent changes**:109 - Determine scope using git tags first: find the most recent tag matching `v*` or semver pattern.110 - If no tags exist, fall back to parsing the latest `## [x.y.z]` heading in the changelog to identify the last documented version, then use `git log` from that point.111 - If neither tags nor changelog versions exist, limit to the last 50 commits to avoid excessive analysis. The user can override scope by providing a commit range or tag as argument.112 - Run `git log --no-merges` to get commits within the determined scope.113 - Run `git diff` against the last version tag if available.114 - Run `git diff --cached` to collect staged but uncommitted changes.115 - Run `git diff` (no arguments) to collect unstaged changes in the working tree.116 - Combine committed and uncommitted changes. When the only source is uncommitted changes (no new commits in scope), still generate entries from those changes.1171184. **Analyze and categorize**:119 - Parse commit messages for type prefixes (`feat:`, `fix:`, etc.).120 - Read diffs for commits without clear type prefixes.121 - Group related commits into single entries.122 - Skip merge commits and changelog-only commits.123 - Skip commits that only touch CI config, documentation, or non-user-facing files (e.g., `.github/`, `README.md`, `CHANGELOG.md`) unless they represent meaningful user-visible changes.1241255. **Write entries**:126 - Use clear, human-readable language.127 - Start each entry with an imperative verb matching the section: Add, Change, Deprecate, Remove, Fix, Secure.128 - Include relevant context (what component, what behavior).129 - Prefix breaking changes with **BREAKING:** (detect via `!` suffix on commit type, e.g. `feat!:`, or `BREAKING CHANGE:` in commit footer).130 - Order entries within a section by significance: most impactful changes first.131 - Don't include commit hashes or author names.1321336. **Insert into the changelog**:134 - Add section headers only for sections that have entries.135 - Place new entries in the target section.136 - Preserve all existing content below.137 - Don't modify existing entries.1381397. **Generate comparison links**:140 - Run `git remote get-url origin` to detect the repository URL.141 - Detect the hosting platform from the remote URL hostname and generate platform-appropriate links:142 - **GitHub** (`github.com`): `https://github.com/user/repo/compare/v1.0.0...HEAD`143 - **GitLab** (`gitlab.com` or self-hosted): `https://gitlab.com/user/repo/-/compare/v1.0.0...HEAD`144 - **Bitbucket** (`bitbucket.org`): `https://bitbucket.org/user/repo/branches/compare/HEAD..v1.0.0`145 - Add link references at the bottom of the changelog:146 ```147 [Unreleased]: https://github.com/user/repo/compare/v1.0.0...HEAD148 [1.0.0]: https://github.com/user/repo/compare/v0.9.0...v1.0.0149 ```150 - If there are no prior tags, use the initial commit hash as the base for the `[Unreleased]` link.151 - Update existing link references when adding new versions.152 - Skip this step if the remote URL cannot be parsed or is not available.1531548. **Write to the changelog** — auto-apply by default:155 - Apply the changes directly using Edit (or Write for new files) without asking for confirmation.156 - **Exception — malformed changelog**: If the existing file cannot be parsed (missing headers, broken markdown structure, unparseable version sections), stop and use **AskUserQuestion** to show the user what's wrong and ask how to proceed before overwriting.157158## Housekeeping159160When invoked with `consolidate` (or when the user asks to "clean up the changelog", "consolidate entries", "tidy the changelog"), run a housekeeping pass on the existing `CHANGELOG.md`:1611621. **Scan for duplicates**: Find entries across sections that describe the same change in different words (e.g., "Add retry logic to API client" and "Add automatic retries for failed API requests"). Merge them into the strongest single entry, keeping it in the most appropriate section.1631642. **Detect superseded entries**: When a later entry makes an earlier one obsolete, remove the stale one. Common patterns:165 - A feature was added, then rewritten — keep only the rewrite (or the final state).166 - A bug was fixed, then the fix was reverted and re-fixed differently — keep only the final fix.167 - A deprecation notice followed by actual removal — keep the removal, drop the deprecation (unless they're in different version sections).1681693. **Tighten wording**: Shorten verbose entries without losing meaning. Each entry should be one concise line.1701714. **Preserve version boundaries**: Never move entries between versioned sections (`## [x.y.z]`). Housekeeping within `## [Unreleased]` is unrestricted. Within versioned sections, only merge duplicates and tighten wording — do not delete entries.1721735. **Apply changes**: Apply the consolidated entries directly using Edit without asking for confirmation. The same malformed-changelog exception from step 8 applies here.174175## Examples176177### Example 1: Adding to Unreleased178179Before:180```markdown181## [Unreleased]182183### Fixed184- Resolve timeout on large file uploads185```186187After running `/changelog`:188```markdown189## [Unreleased]190191### Added192- Support for WebSocket connections in the API gateway193194### Fixed195- Resolve timeout on large file uploads196- Correct off-by-one error in pagination logic197```198199### Example 2: Creating a versioned release200201Running `/changelog 2.1.0`:202```markdown203## [2.1.0] - 2026-04-20204205### Added206- Bulk import endpoint for market data207- Rate limiting middleware with configurable thresholds208209### Changed210- Upgrade authentication flow to use PKCE211212### Fixed213- Memory leak in connection pool during high traffic214```215216### Example 3: Creating changelog from scratch217218Running `/changelog` in a repo without `CHANGELOG.md`:219```markdown220# Changelog221222All notable changes to this project will be documented in this file.223224The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),225and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).226227## [Unreleased]228229### Added230- Initial project setup with CLI interface231- Configuration file support232- Logging framework integration233```234235## Error Handling236237Handle these conditions gracefully:238- **Not a git repository**: Inform user and stop — do not attempt to create a changelog without git history239- **Invalid version argument**: Validate that the argument matches semver (`X.Y.Z`); reject with a clear message if not240- **Malformed changelog**: If the existing file cannot be parsed (missing headers, broken markdown structure, unparseable version sections), stop and use **AskUserQuestion** to show the user what's wrong and ask how to proceed before overwriting241- **Git command failures**: If `git log` or `git diff` fails, report the error and stop rather than producing incomplete entries242- **No changes in scope**: If scope detection finds zero commits and no uncommitted changes, inform the user — do not add empty sections243244## Anti-Patterns245246- **Raw commit messages**: Don't paste commit messages verbatim; rewrite for humans247- **Empty sections**: Don't include section headers with no entries248- **Overwriting**: Never modify or delete existing changelog entries during normal operation (adding new entries). The only exception is `consolidate` mode, which explicitly merges and cleans up entries249- **Implementation details**: Focus on user-visible changes, not internal refactoring noise250- **Duplicate entries**: Check existing entries before adding new ones251- **Unbounded history**: Don't analyze the entire git history when there are no tags — cap at 50 commits252- **CI/docs noise**: Don't add entries for commits that only change CI config, linting rules, or documentation unless user-visible253- **Future tense**: Write entries as completed actions ("Add support for X"), not future plans ("Will add support for X")254- **Unmarked breaking changes**: Always clearly mark breaking changes (e.g., prefix with "**BREAKING:**")255- **Stale comparison links**: When adding a new version, update all comparison link references at the bottom of the file256- **Unnecessary confirmation prompts**: Don't ask for approval before writing — auto-apply is the default. Only stop and ask when the existing changelog is malformed and can't be parsed safely257- **Cross-version consolidation**: During housekeeping, never move or delete entries from versioned release sections — only consolidate within `[Unreleased]`