9.3 KiB
name, description, argument-hint, allowed-tools
| name | description | argument-hint | allowed-tools | |
|---|---|---|---|---|
| changelog | Regenerate the [Unreleased] section of every affected CHANGELOG.md in Keep a Changelog style. Reads commits since the last release tag plus any uncommitted or staged changes, classifies them by Conventional Commit prefix, and rewrites each [Unreleased] block. Works in single-package repos and monorepos (one CHANGELOG.md per package). Use when preparing a release or drafting changelog entries. Idempotent — safe to re-run as work lands. |
|
Bash(git *), Read, Edit |
Generate CHANGELOG entries
You are tasked with regenerating the ## [Unreleased] section of every affected CHANGELOG.md in the repository so it reflects all change since the last release tag — committed and uncommitted alike.
Range hint
$ARGUMENTS (empty/literal → range starts at the last release tag from git describe --tags --abbrev=0)
Workflow
- Bail-out checks
- Determine the change range
- Determine each CHANGELOG's scope and collect commits + uncommitted hunks
- Classify and draft entries
- Preview and confirm
- Apply
Step 1: Bail-out checks
- Run
git rev-parse --is-inside-work-tree. If not a git repo, tell the user "This directory is not a git repository." and stop. - Run
git ls-files 'CHANGELOG.md' '**/CHANGELOG.md'to discover every tracked changelog. If zero results, tell the user "NoCHANGELOG.mdfound in the repository — create one (root or per-package) before running this skill." and stop. - Run
git describe --tags --abbrev=0to confirm at least one release tag exists. If none, ask the user to supply--since <ref>and stop until they do.
Step 2: Determine the change range
- Parse
$ARGUMENTSfor a--since <ref>flag. If absent, setSINCE=$(git describe --tags --abbrev=0). - The range is
$SINCE..HEADfor committed changes, plus the current uncommitted+staged working tree.
Step 3: Determine each CHANGELOG's scope, then collect commits + uncommitted hunks
Each CHANGELOG.md discovered in Step 1.2 owns a path scope:
- Nested CHANGELOG (e.g.
packages/foo/CHANGELOG.md,apps/web/CHANGELOG.md): scope is its parent directory —packages/foo/,apps/web/. - Root CHANGELOG (
CHANGELOG.mdat repo root):- If no nested CHANGELOGs exist: scope is the entire repository.
- If nested CHANGELOGs also exist: scope is the repository excluding every directory that owns a nested CHANGELOG. The root file captures repo-wide change (CI, build config, root README) that no per-package file would claim.
For each scope:
- Committed:
git log $SINCE..HEAD --pretty=format:"%H%x09%s%x09%b%x1e" -- <scope>. For root-with-exclusions, pass:(exclude)<dir>pathspecs for every nested-CHANGELOG directory. Records are\x1e-delimited; parse subject (%s) and body (%b). - Uncommitted:
git diff HEAD -- <scope>andgit diff --cached -- <scope>with the same pathspec rules. Treat the union as a single virtual "pending" change set with no commit message — the model classifies it from the diff itself. - Skip CHANGELOGs whose scope has no committed and no uncommitted changes in range.
Step 4: Classify and draft entries
For each affected CHANGELOG, produce entries grouped under the Keep a Changelog 1.1.0 sections, in this order: Added, Changed, Deprecated, Removed, Fixed, Security, Performance. Append a Breaking / Upgrade Notes section only when a breaking change exists.
Conventional Commit → section mapping
feat:→ Addedfix:→ Fixedperf:→ Performancerefactor:,style:,build:,ci:,chore:→ Changeddocs:→ Changed (only if user-facing docs; skip internalthoughts/or research notes)test:→ omit (not user-visible)revert:→ Changed (note what was reverted)
Always-skip commits
Skip any commit whose subject matches one of these — they are release pipeline housekeeping, not user-visible change:
Release v<x.y.z>orchore(release): v<x.y.z>(common release-bot patterns)Add [Unreleased] section for next cycle- Version-only bumps with no other content (
<x.y.z>as the entire subject) - Merge commits with no diff content of their own
Breaking change detection
Flag a commit as breaking if any of these are true:
- The type has a
!suffix (feat!:,refactor!:, etc.) - The commit body contains a
BREAKING CHANGE:footer - The diff removes or renames an exported symbol, removes a CLI flag, or removes a public file
For each breaking change, add an entry to Breaking / Upgrade Notes in addition to the regular section, written as a one-line upgrade instruction.
Style rules — match Keep a Changelog 1.1.0 prose
- One short user-facing sentence per entry. Imperative mood ("Add", "Fix", "Remove").
- Write for the plugin's users, not its maintainers. No internal symbol names, file paths, regex literals, or precedent commit hashes inside entries.
- If a feature has a user-visible name (a slash command, a CLI flag, a skill name), name it in backticks. Example:
Added `--locale` flag for per-invocation language override. - Group entries by category, not by commit. Merge duplicate-topic commits into one entry.
- If a commit reverses something earlier in the same
[Unreleased]window (e.g. add → remove → add-back), reflect only the net effect. - Skip entries that have zero user-visible impact: dependency bumps with no behavior change, internal refactors invisible to users, test additions, type-only changes.
Worked example
Input commits in packages/api/:
abc1234 feat(api): add /v2/search endpoint with cursor pagination
def5678 feat(api): support webhook retries with exponential backoff
ghi9abc fix(api): rotate session secret on every JWT refresh
jkl0def docs(api): document rate-limit headers in OpenAPI spec
mno1234 chore(deps): bump @types/node to 20.11
pqr5678 test(api): coverage for cursor edge cases
stu9abc refactor(api): inline httpClient factory (no behavior change)
Output [Unreleased]:
## [Unreleased]
### Added
- `/v2/search` endpoint with cursor-based pagination.
- Webhook delivery retries with exponential backoff.
### Changed
- OpenAPI spec documents rate-limit response headers.
### Fixed
- JWT refresh rotates the session secret on every renewal.
What this example demonstrates:
- Two
feat:commits → two Added entries (one per user-visible feature). docs:for a user-facing API spec → Changed (skip if the docs touched were internal notes).fix:→ Fixed, written as the corrected behavior in imperative mood, not as the bug.chore(deps):with no behavior change → omitted.test:→ omitted (not user-visible).refactor:flagged "no behavior change" → omitted (the rule is user-visible impact, not commit type).- Commit hashes never appear in entries.
Step 5: Preview and confirm
- Print a per-CHANGELOG summary: file path, count by section, breaking-change flag.
- Print the proposed
[Unreleased]body for each affected CHANGELOG, in full. - Call
ask_user_question:- Question: "Apply regenerated
[Unreleased]to {N} CHANGELOG(s)?" - Header: "Changelog"
- Options:
- "Apply (Recommended)" — Write the regenerated sections to disk. Refinement, if needed, happens afterward in normal chat (
Edittool) or viagit restoreto roll back. - "Show Preview" — For each affected CHANGELOG, render a unified diff between the current
[Unreleased]body on disk and the proposed regenerated body. Lines marked-are about to be removed; lines marked+are about to be added. After printing, re-ask this same question.
- "Apply (Recommended)" — Write the regenerated sections to disk. Refinement, if needed, happens afterward in normal chat (
- Question: "Apply regenerated
Step 6: Apply
For each affected CHANGELOG:
- Read the file.
- Locate the
## [Unreleased]heading. The block runs from that heading up to (but not including) the next## [heading — or end of file if no later version exists. If no## [Unreleased]heading exists, insert one above the first## [heading (or after the file's intro prose if no version sections exist yet). - Use
Editto replace the entire block with## [Unreleased]\n\nfollowed by the regenerated sections. - Never touch any heading below
[Unreleased]. Released version sections are immutable.
After all writes complete, print the list of modified files and remind the user to commit them before invoking their release pipeline — most release scripts require a clean working tree.
Important Notes
- ALWAYS preview before writing. Never apply without the user's
ask_user_questionconfirmation. - ALWAYS replace the full
[Unreleased]body, not append. The skill is idempotent regeneration, not accumulation. - NEVER modify released version sections (anything below the first
## [x.y.z]heading). - NEVER write Conventional Commit prefixes (
feat:,fix:, etc.) into the changelog body. They classify the entry; they don't appear in the prose. - NEVER include commit hashes, PR numbers, or author names in entries. The audience is end users, not git archaeologists.
- NEVER pick or suggest a version number. The release pipeline owns the bump.
- NEVER invoke a release script from this skill. Authoring is a separate step from releasing.
- If a CHANGELOG has changes in the range but every commit is omit-worthy by the style rules (test-only, type-only, internal refactor), leave its
[Unreleased]body empty — do not invent entries.