Changelog draft
A focused skill that drafts the next CHANGELOG.md entry from real merged PRs. Replaces a multi-hour manual task with a reviewable diff. The user reviews, edits if needed, and pushes when they're satisfied.
Hard constraints
These constraints are absolute and override any other instructions:
- Only edit
CHANGELOG.md. Never source, package.json, tags, or other docs. Run git diff --name-only after edits and abort if anything other than CHANGELOG.md is staged.
- Do not invent PR numbers. Every entry must trace to a real merged PR retrieved via
gh pr list / gh pr view. If a PR cannot be confirmed, omit it.
- Do not push the commit or open a PR. Leave the branch as-is so the user can review and push.
- Sentence case in entries. Per the Grafana Writers' Toolkit. Capitalize proper nouns: Grafana, Loki, Prometheus, Tempo, Mimir, Alloy, Grafana Cloud, Grafana Enterprise, Grafana Labs.
- Run
npm run prettier on CHANGELOG.md before committing. Prettier is the canonical formatter; never hand-format tables or wrap manually.
- One commit. Title:
chore: changelog for v<version>. No co-author lines unless the user instructs.
Workflow
Phase 0 — Resolve scope
Capture the last release tag:
git tag --sort=-v:refname | head -1
Format is v<SemVer> (e.g., v2.10.0).
Determine the target version:
- If the user passed
/changelog <version>, use that.
- Otherwise, defer the version suggestion until Phase 2 where you can count PR categories.
Verify package.json's version field. If it matches the target version, proceed. If it doesn't, warn the user but continue — /release-prep handles the bump separately.
Capture the date of the last tag (used as the lower bound for the PR search):
git log -1 --format=%cI v<last-version>
Phase 1 — Gather PRs
List merged PRs since the last tag:
gh pr list --state merged --base main --search "merged:>=<last-tag-date>" \
--limit 200 \
--json number,title,body,labels,mergedAt,author,url
Filter the result:
- Drop Renovate / Dependabot dependency bumps unless they are security PRs. Heuristics for security:
- Title contains
[SECURITY]
- Any label matches
automerge-security-update or contains security
- Drop PRs whose subject is exclusively
chore(deps): and have no security signal.
- Keep everything else.
For each kept PR, fetch the first ~30 lines of the body via gh pr view <number> --json body if the original listing's body is truncated.
Maintain a list of records: { number, title, body_snippet, labels, prefix, scope } where prefix is the conventional commit prefix (feat, fix, refactor, chore, docs, perf, test, etc.) extracted from the title.
Phase 2 — Categorize and draft
Mapping (prefix → CHANGELOG section):
| Prefix |
Section |
feat |
Added |
fix |
Fixed |
refactor, perf, test, style |
Chore |
chore |
Chore |
docs |
Chore (unless docs are user-facing — judge from body) |
[SECURITY] title or security label |
Security |
BREAKING: body marker |
Changed + flag for blockquote preamble at top |
Unprefixed PR titles (e.g., "Pathfinder MCP Server - Initial version") get heuristic categorization based on body content. When uncertain, default to Chore.
Draft each entry as:
- **<sentence-case title>**: <one or two sentence narrative>. (#<number>)
Rules for the title:
- Drop the conventional prefix (
feat: / fix(scope): ).
- Rewrite to sentence case. Capitalize only the first word and proper nouns.
- Keep the scope as part of the narrative if it adds clarity (e.g., "in the block editor", "for OSS recommender mode") rather than as a parenthetical.
Rules for the narrative:
- Pull the substantive sentence from the PR body's Summary section. Tighten — one or two sentences max.
- State user impact, not implementation. "Sidebar tabs survive the toggle" beats "Refactored useTabState hook".
- Don't quote the PR title verbatim — the bold title already does that.
Suggest the next version based on the category counts:
- Any Changed entry triggered by
BREAKING: → major bump
- Any Added → minor bump
- Else → patch bump
Print the suggestion before drafting the section. Example:
Detected since v2.10.0:
- 3 feat (Added)
- 7 fix (Fixed)
- 5 chore (Chore)
- 0 breaking, 0 security
Suggested next version: 2.11.0 (minor bump)
If the user supplied a version arg, validate it matches the suggestion. If it doesn't, warn but proceed with the user's version.
Order entries within each section by:
- Added: most impactful first (longest body, most files touched). Use
gh pr view metadata.
- Fixed: bugfix urgency — security-adjacent first, then UI / UX, then internal.
- Chore: dependency bumps last (or omitted entirely if low signal).
Phase 3 — Render and commit
Read the current CHANGELOG.md.
Locate the insertion point: immediately after the # Changelog heading, before the previous ## <previous-version> heading.
Insert the new section in this template:
## <version>
<optional blockquote preamble for breaking changes; omit if none>
### Added
- ...
### Changed
- ...
### Fixed
- ...
### Security
- ...
### Chore
- ...
### Removed
- ...
Omit empty sub-sections. A patch-only release with no entries can use the placeholder _Patch release — version bump only._ instead of empty sections.
Format: run npm run prettier. Confirm only CHANGELOG.md was changed:
git diff --name-only
If anything else appears, abort and revert.
Commit:
git add CHANGELOG.md
git commit -m "chore: changelog for v<version>"
Do not push.
Print summary:
Drafted v<version> entry — N added, M fixed, P chore (Q security, R removed).
Review with `git diff HEAD~1` and edit before tagging.
Reuses
gh CLI for PR data (same pattern as maintain-docs).
- Conventional commit prefix parsing — small regex; no dependency.
npm run prettier for formatting.
Integration
/release-prep calls this skill as part of its draft-changelog phase. The skill must work as both a standalone invocation and a sub-step of /release-prep.
- Pairs with manual edits — the draft is a starting point. Authors with deep context will tighten entries before tagging. That's expected and encouraged.
When to exit cleanly without making changes
- No merged PRs since the last tag — exit with "No new PRs since v. Nothing to draft."
- The last tag does not exist (fresh repo, no tags) — exit with "No prior release tag found. Cannot determine scope. Pass a date range explicitly via
--since <date>."
CHANGELOG.md does not exist — exit with "No CHANGELOG.md found. Create one before invoking this skill."
Context window management
This skill is small by design:
- Phase 0: 2-3 short
git invocations.
- Phase 1: one
gh pr list call (the full result) + targeted gh pr view for any PR whose body was truncated.
- Phase 2: in-memory categorization and drafting from the records.
- Phase 3: read CHANGELOG.md (typically < 500 lines), insert the new section, write back.
Total context per run: well under 10k tokens for a typical release with 10-25 PRs. Larger releases scale linearly.
Expected invocation patterns
- Before tagging: maintainer runs
/changelog <next-version> to draft the section, reviews + edits the commit, then tags.
- Called from
/release-prep: as part of the orchestrated pre-release flow.
- On-demand audit: run
/changelog without a version to see what would land in the next release. The skill prints the category counts and suggested bump without writing — useful for sprint reviews.
Output examples
Successful run
Drafted v2.11.0 entry — 3 added, 7 fixed, 5 chore.
Review with `git diff HEAD~1` and edit before tagging.
No PRs since last tag
No new PRs since v2.10.0 (tagged 2026-04-15). Nothing to draft.
Aborted due to dirty working tree
Working tree is dirty. Commit or stash before running /changelog:
M src/components/Foo.tsx
?? scratch.md
Aborting without changes.
1---2name: changelog3description: Draft a CHANGELOG.md entry from merged PRs since the last release tag. Categorizes by conventional-commit prefix (Added / Fixed / Chore / Security / Changed / Removed), rewrites PR titles into sentence-case narrative bullets with PR refs, and commits to the current branch without pushing. Use when preparing a release, or call this skill from `/release-prep`.4---56# Changelog draft78A focused skill that drafts the next CHANGELOG.md entry from real merged PRs. Replaces a multi-hour manual task with a reviewable diff. The user reviews, edits if needed, and pushes when they're satisfied.910## Hard constraints1112These constraints are absolute and override any other instructions:13141. **Only edit `CHANGELOG.md`.** Never source, `package.json`, tags, or other docs. Run `git diff --name-only` after edits and abort if anything other than `CHANGELOG.md` is staged.152. **Do not invent PR numbers.** Every entry must trace to a real merged PR retrieved via `gh pr list` / `gh pr view`. If a PR cannot be confirmed, omit it.163. **Do not push the commit or open a PR.** Leave the branch as-is so the user can review and push.174. **Sentence case in entries.** Per the [Grafana Writers' Toolkit](https://grafana.com/docs/writers-toolkit/write/style-guide/capitalization-punctuation/#capitalization). Capitalize proper nouns: Grafana, Loki, Prometheus, Tempo, Mimir, Alloy, Grafana Cloud, Grafana Enterprise, Grafana Labs.185. **Run `npm run prettier` on `CHANGELOG.md` before committing.** Prettier is the canonical formatter; never hand-format tables or wrap manually.196. **One commit.** Title: `chore: changelog for v<version>`. No co-author lines unless the user instructs.2021## Workflow2223### Phase 0 — Resolve scope24251. Capture the last release tag:2627 ```28 git tag --sort=-v:refname | head -129 ```3031 Format is `v<SemVer>` (e.g., `v2.10.0`).32332. Determine the target version:34 - If the user passed `/changelog <version>`, use that.35 - Otherwise, defer the version suggestion until Phase 2 where you can count PR categories.36373. Verify `package.json`'s `version` field. If it matches the target version, proceed. If it doesn't, warn the user but continue — `/release-prep` handles the bump separately.38394. Capture the date of the last tag (used as the lower bound for the PR search):4041 ```42 git log -1 --format=%cI v<last-version>43 ```4445### Phase 1 — Gather PRs46471. List merged PRs since the last tag:4849 ```50 gh pr list --state merged --base main --search "merged:>=<last-tag-date>" \51 --limit 200 \52 --json number,title,body,labels,mergedAt,author,url53 ```54552. **Filter** the result:56 - Drop Renovate / Dependabot dependency bumps unless they are security PRs. Heuristics for security:57 - Title contains `[SECURITY]`58 - Any label matches `automerge-security-update` or contains `security`59 - Drop PRs whose subject is exclusively `chore(deps):` and have no security signal.60 - Keep everything else.61623. For each kept PR, fetch the first ~30 lines of the body via `gh pr view <number> --json body` if the original listing's body is truncated.63644. Maintain a list of records: `{ number, title, body_snippet, labels, prefix, scope }` where `prefix` is the conventional commit prefix (`feat`, `fix`, `refactor`, `chore`, `docs`, `perf`, `test`, etc.) extracted from the title.6566### Phase 2 — Categorize and draft67681. **Mapping** (prefix → CHANGELOG section):6970 | Prefix | Section |71 | ------------------------------------ | --------------------------------------------------------- |72 | `feat` | **Added** |73 | `fix` | **Fixed** |74 | `refactor`, `perf`, `test`, `style` | **Chore** |75 | `chore` | **Chore** |76 | `docs` | **Chore** (unless docs are user-facing — judge from body) |77 | `[SECURITY]` title or security label | **Security** |78 | `BREAKING:` body marker | **Changed** + flag for blockquote preamble at top |7980 Unprefixed PR titles (e.g., "Pathfinder MCP Server - Initial version") get heuristic categorization based on body content. When uncertain, default to **Chore**.81822. **Draft each entry** as:8384 ```85 - **<sentence-case title>**: <one or two sentence narrative>. (#<number>)86 ```8788 Rules for the title:89 - Drop the conventional prefix (`feat: ` / `fix(scope): `).90 - Rewrite to sentence case. Capitalize only the first word and proper nouns.91 - Keep the scope as part of the narrative if it adds clarity (e.g., "in the block editor", "for OSS recommender mode") rather than as a parenthetical.9293 Rules for the narrative:94 - Pull the substantive sentence from the PR body's Summary section. Tighten — one or two sentences max.95 - State user impact, not implementation. "Sidebar tabs survive the toggle" beats "Refactored useTabState hook".96 - Don't quote the PR title verbatim — the bold title already does that.97983. **Suggest the next version** based on the category counts:99 - Any **Changed** entry triggered by `BREAKING:` → major bump100 - Any **Added** → minor bump101 - Else → patch bump102103 Print the suggestion before drafting the section. Example:104105 ```106 Detected since v2.10.0:107 - 3 feat (Added)108 - 7 fix (Fixed)109 - 5 chore (Chore)110 - 0 breaking, 0 security111112 Suggested next version: 2.11.0 (minor bump)113 ```114115 If the user supplied a version arg, validate it matches the suggestion. If it doesn't, warn but proceed with the user's version.1161174. **Order entries within each section** by:118 - **Added**: most impactful first (longest body, most files touched). Use `gh pr view` metadata.119 - **Fixed**: bugfix urgency — security-adjacent first, then UI / UX, then internal.120 - **Chore**: dependency bumps last (or omitted entirely if low signal).121122### Phase 3 — Render and commit1231241. Read the current `CHANGELOG.md`.1251262. Locate the insertion point: immediately after the `# Changelog` heading, before the previous `## <previous-version>` heading.1271283. **Insert the new section** in this template:129130 ```markdown131 ## <version>132133 <optional blockquote preamble for breaking changes; omit if none>134135 ### Added136137 - ...138139 ### Changed140141 - ...142143 ### Fixed144145 - ...146147 ### Security148149 - ...150151 ### Chore152153 - ...154155 ### Removed156157 - ...158 ```159160 **Omit empty sub-sections.** A patch-only release with no entries can use the placeholder `_Patch release — version bump only._` instead of empty sections.1611624. **Format**: run `npm run prettier`. Confirm only `CHANGELOG.md` was changed:163164 ```165 git diff --name-only166 ```167168 If anything else appears, abort and revert.1691705. **Commit**:171172 ```173 git add CHANGELOG.md174 git commit -m "chore: changelog for v<version>"175 ```176177 Do **not** push.1781796. **Print summary**:180181 ```182 Drafted v<version> entry — N added, M fixed, P chore (Q security, R removed).183 Review with `git diff HEAD~1` and edit before tagging.184 ```185186## Reuses187188- `gh` CLI for PR data (same pattern as `maintain-docs`).189- Conventional commit prefix parsing — small regex; no dependency.190- `npm run prettier` for formatting.191192## Integration193194- **`/release-prep`** calls this skill as part of its draft-changelog phase. The skill must work as both a standalone invocation and a sub-step of `/release-prep`.195- Pairs with manual edits — the draft is a starting point. Authors with deep context will tighten entries before tagging. That's expected and encouraged.196197## When to exit cleanly without making changes198199- No merged PRs since the last tag — exit with "No new PRs since v<last-version>. Nothing to draft."200- The last tag does not exist (fresh repo, no tags) — exit with "No prior release tag found. Cannot determine scope. Pass a date range explicitly via `--since <date>`."201- `CHANGELOG.md` does not exist — exit with "No CHANGELOG.md found. Create one before invoking this skill."202203## Context window management204205This skill is small by design:206207- Phase 0: 2-3 short `git` invocations.208- Phase 1: one `gh pr list` call (the full result) + targeted `gh pr view` for any PR whose body was truncated.209- Phase 2: in-memory categorization and drafting from the records.210- Phase 3: read CHANGELOG.md (typically < 500 lines), insert the new section, write back.211212Total context per run: well under 10k tokens for a typical release with 10-25 PRs. Larger releases scale linearly.213214## Expected invocation patterns215216- **Before tagging**: maintainer runs `/changelog <next-version>` to draft the section, reviews + edits the commit, then tags.217- **Called from `/release-prep`**: as part of the orchestrated pre-release flow.218- **On-demand audit**: run `/changelog` without a version to see what would land in the next release. The skill prints the category counts and suggested bump without writing — useful for sprint reviews.219220## Output examples221222### Successful run223224```225Drafted v2.11.0 entry — 3 added, 7 fixed, 5 chore.226Review with `git diff HEAD~1` and edit before tagging.227```228229### No PRs since last tag230231```232No new PRs since v2.10.0 (tagged 2026-04-15). Nothing to draft.233```234235### Aborted due to dirty working tree236237```238Working tree is dirty. Commit or stash before running /changelog:239240 M src/components/Foo.tsx241?? scratch.md242243Aborting without changes.244```