Skip to content

Commit 6987017

Browse files
authored
chore(forge): add write-release-notes skill (#2535)
1 parent fc62f49 commit 6987017

2 files changed

Lines changed: 152 additions & 0 deletions

File tree

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
---
2+
name: write-release-notes
3+
description: Generate engaging, high-energy release notes for a given version tag. Fetches the release from GitHub, retrieves every linked PR's title and description, then synthesizes all changes into a polished, user-facing release note with an enthusiastic tone. Use when the user asks to write, generate, or create release notes for a version (e.g. "write release notes for v1.32.0", "generate release notes for the latest release", "create changelog for v2.0").
4+
---
5+
6+
# Write Release Notes
7+
8+
Generate compelling, high-energy release notes by pulling live data from GitHub and synthesizing every PR into a cohesive narrative.
9+
10+
## Workflow
11+
12+
### 1. Fetch Release Data
13+
14+
Run the bundled script to pull the release metadata and all linked PR details in one shot:
15+
16+
```bash
17+
bash .forge/skills/write-release-notes/scripts/fetch-release-data.sh <version> [owner/repo]
18+
```
19+
20+
- `<version>`: The release tag (e.g. `v1.32.0`)
21+
- `[owner/repo]`: Optional. Defaults to the current repo detected via `gh repo view`.
22+
23+
The script outputs two sections:
24+
- `### RELEASE METADATA ###` — tag name, publish date, release name, raw body
25+
- `### PR DETAILS ###` — one JSON object per PR with: `number`, `title`, `body`, `labels`, `author`, `mergedAt`, `url`
26+
27+
### 2. Categorize Changes
28+
29+
Group PRs by their conventional commit prefix or label:
30+
31+
| Category | Prefixes / Labels |
32+
|---|---|
33+
| Features | `feat`, `type: feature` |
34+
| Bug Fixes | `fix`, `type: fix` |
35+
| Performance | `perf` |
36+
| Refactors | `refactor` |
37+
| Maintenance | `chore`, `docs`, `ci`, `build`, `deps` |
38+
39+
Dependency bumps (e.g. Dependabot PRs) go into Maintenance. Skip PRs with `error: "not found"`.
40+
41+
### 3. Write the Release Notes
42+
43+
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.
44+
45+
```markdown
46+
# [Product Name] [Version][Punchy Tagline]
47+
48+
> One-sentence hook that captures the spirit of this release.
49+
50+
## What's New
51+
52+
[2-4 sentence narrative that weaves together the biggest features and fixes.
53+
Speak to impact, not implementation. Use active voice. Be enthusiastic.]
54+
55+
## Highlights
56+
57+
### [Feature/Fix Category]
58+
**[PR Title rephrased as user benefit]** ([#NNN](url))
59+
[1-2 sentences expanding on the PR description. Focus on what the user gains.
60+
If the PR body has useful context, distill it. If empty, infer from the title.]
61+
62+
[Repeat for each significant PR — skip pure chores/dep bumps unless noteworthy]
63+
64+
## Bug Fixes & Reliability
65+
66+
[Bullet list of fixes, each with a brief impact statement and PR link]
67+
68+
## Under the Hood
69+
70+
[Brief paragraph or bullet list covering refactors, maintenance, and dep updates —
71+
keep it light, acknowledge the work without boring the reader]
72+
73+
## Contributors
74+
75+
A huge thank you to everyone who made this release happen: [list @handles]
76+
77+
---
78+
**Full changelog**: [GitHub Release link]
79+
```
80+
81+
### 4. Tone & Style Guidelines
82+
83+
- **Lead with value**: "You can now..." beats "We added..."
84+
- **Be specific**: Name the feature, not just the category
85+
- **Use energy words**: "blazing", "seamless", "rock-solid", "powerful", "finally"
86+
- **Short paragraphs**: Max 3 sentences per block
87+
- **Skip internal jargon**: Translate crate names and internal concepts into plain language
88+
- **Celebrate contributors**: Name them enthusiastically
89+
- **Tagline formula**: `[Version] — [Adjective] [Theme]` (e.g. "v1.32.0 — Smarter Config, Smoother Workflows")
90+
91+
### 5. Output
92+
93+
Print the final release notes directly in the chat. Do not write to a file unless the user explicitly asks.
94+
95+
## Notes
96+
97+
- The script handles ANSI color codes injected by `gh` CLI automatically.
98+
- PRs not found (closed without merge, private, etc.) are silently skipped.
99+
- If the release has no linked PRs in its body, fall back to listing commits between tags:
100+
```bash
101+
gh api repos/<owner>/<repo>/compare/<prev_tag>...<version> --jq '.commits[].commit.message'
102+
```
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#!/usr/bin/env bash
2+
# Fetches all PR numbers from a GitHub release and outputs their details.
3+
# Usage: ./fetch-release-data.sh <version> [repo]
4+
# Example: ./fetch-release-data.sh v1.32.0 antinomyhq/forge
5+
6+
set -euo pipefail
7+
8+
VERSION="${1:?Usage: $0 <version> [repo]}"
9+
REPO="${2:-$(gh repo view --json nameWithOwner -q '.nameWithOwner' 2>/dev/null)}"
10+
11+
if [[ -z "$REPO" ]]; then
12+
echo "ERROR: Could not determine repository. Pass it as second argument." >&2
13+
exit 1
14+
fi
15+
16+
# Use a temp file to avoid large variable issues with bash subshells
17+
TMPFILE=$(mktemp)
18+
trap 'rm -f "$TMPFILE"' EXIT
19+
20+
# Fetch release metadata (strip ANSI color codes that gh CLI may inject)
21+
gh api "repos/$REPO/releases/tags/$VERSION" | sed 's/\x1b\[[0-9;]*m//g' > "$TMPFILE"
22+
23+
if [[ ! -s "$TMPFILE" ]]; then
24+
echo "ERROR: Release $VERSION not found in $REPO" >&2
25+
exit 1
26+
fi
27+
28+
echo "### RELEASE METADATA ###"
29+
jq '{tagName: .tag_name, publishedAt: .published_at, releaseName: .name, body: .body}' < "$TMPFILE"
30+
31+
# Extract PR numbers from the release body (handle \r\n line endings)
32+
PR_NUMBERS=$(jq -r '.body // ""' < "$TMPFILE" | tr -d '\r' | grep --color=never -oE '#[0-9]+' | tr -d '#' | sort -un)
33+
34+
if [[ -z "$PR_NUMBERS" ]]; then
35+
echo "WARNING: No PR numbers found in release body." >&2
36+
exit 0
37+
fi
38+
39+
echo "### PR DETAILS ###"
40+
for PR_NUM in $PR_NUMBERS; do
41+
PR_DATA=$(gh pr view "$PR_NUM" \
42+
--repo "$REPO" \
43+
--json number,title,body,labels,author,mergedAt,url \
44+
2>/dev/null | sed 's/\x1b\[[0-9;]*m//g') || true
45+
if [[ -n "$PR_DATA" ]]; then
46+
echo "$PR_DATA"
47+
else
48+
echo "{\"number\": $PR_NUM, \"error\": \"not found\"}"
49+
fi
50+
done

0 commit comments

Comments
 (0)