Write Release Notes
Generate compelling, high-energy release notes by pulling live data from GitHub and synthesizing every PR into a cohesive narrative.
Workflow
1. Fetch Release Data
Run the bundled script to pull the release metadata and all linked PR details in one shot:
bash .forge/skills/write-release-notes/scripts/fetch-release-data.sh <version> [owner/repo]
<version>: The release tag (e.g.v1.32.0)[owner/repo]: Optional. Defaults to the current repo detected viagh repo view.
The script outputs two sections:
### RELEASE METADATA ###— tag name, publish date, release name, raw body### PR DETAILS ###— one JSON object per PR with:number,title,body,labels,author,mergedAt,url
2. Categorize Changes
Group PRs by their conventional commit prefix or label:
| Category | Prefixes / Labels |
|---|---|
| Features | feat, type: feature |
| Bug Fixes | fix, type: fix |
| Performance | perf |
| Refactors | refactor |
| Maintenance | chore, docs, ci, build, deps |
Dependency bumps (e.g. Dependabot PRs) go into Maintenance. Skip PRs with error: "not found".
3. Write the Release Notes
Produce a Markdown document with the following structure. Keep the tone exciting, punchy, and developer-friendly — celebrate wins, highlight impact, and make readers feel the momentum.
# [Product Name] [Version] — [Punchy Tagline]
> One-sentence hook that captures the spirit of this release.
## What's New
[2-4 sentence narrative that weaves together the biggest features and fixes.
Speak to impact, not implementation. Use active voice. Be enthusiastic.]
## Highlights
### [Feature/Fix Category]
**[PR Title rephrased as user benefit]**
[1-2 sentences expanding on the PR description. Focus on what the user gains.
If the PR body has useful context, distill it. If empty, infer from the title.]
[Repeat for each significant PR — skip pure chores/dep bumps unless noteworthy]
## Bug Fixes & Reliability
[Bullet list of fixes, each with a brief impact statement]
## Under the Hood
[Brief paragraph or bullet list covering refactors, maintenance, and dep updates —
keep it light, acknowledge the work without boring the reader]
## Contributors
A huge thank you to everyone who made this release happen: [list @handles — exclude bots like @dependabot]
---
**Full changelog**: [GitHub Release link]
4. Tone & Style Guidelines
- Lead with value: "You can now..." beats "We added..."
- Be specific: Name the feature, not just the category
- Use energy words: "blazing", "seamless", "rock-solid", "powerful", "finally"
- Short paragraphs: Max 3 sentences per block
- Skip internal jargon: Translate crate names and internal concepts into plain language
- Celebrate contributors: Name them enthusiastically
- Tagline formula:
[Version] — [Adjective] [Theme](e.g. "v1.32.0 — Smarter Config, Smoother Workflows") - No implementation details: Do not mention internal module names, struct names, function names, crate names, or how something was implemented. Focus purely on what the user experiences or gains.
- No PR/issue references: Do not include PR numbers, issue numbers, or links to GitHub PRs/issues in the release notes. Focus on the changes themselves, not their tracking identifiers.
5. Contributors Filter
Only include external contributors in the Contributors section — exclude the core team:
@tusharmath@amitksingh1490@laststylebender14- Bots (e.g.
@dependabot)
If no external contributors exist, omit the Contributors section entirely.
6. Validate Length
After writing the release notes, run the bundled validation script to confirm the output is under 2000 characters:
echo "<release notes>" | bash .forge/skills/write-release-notes/scripts/validate-release-notes.sh
If it prints FAIL, trim the draft and re-run until it prints PASS:
- Remove the Under the Hood section first
- Consolidate Bug Fixes into a shorter bullet list
- Shorten individual PR descriptions to one tight sentence
- Remove the least impactful Highlights entries
7. Output
Print the final release notes directly in the chat. Do not write to a file unless the user explicitly asks.
Notes
- The script handles ANSI color codes injected by
ghCLI automatically. - PRs not found (closed without merge, private, etc.) are silently skipped.
- If the release has no linked PRs in its body, fall back to listing commits between tags:
gh api repos/<owner>/<repo>/compare/<prev_tag>...<version> --jq '.commits[].commit.message'