Release notes
Generate release notes from git historyFrom its SKILL.md
npx -y skills add jhlee0409/all-for-claudecode --skill release-notesAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 7 stars7 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
8.5 KB, as published. Nobody here has run it
/afc:release-notes — Generate Release Notes
Rewrites commit/PR history into user-facing release notes and optionally publishes to GitHub Releases. This is a standalone utility — not part of the auto pipeline. Does NOT modify local files (CHANGELOG updates are handled by
/afc:launch).
Arguments
$ARGUMENTS— (optional) Version range and flagsv2.3.0..v2.4.0— specific tag rangev2.4.0— from that tag to HEAD- Not specified — auto-detect last tag to HEAD
--post— publish to GitHub Releases after preview (can combine with any range)
Parse the arguments:
- Extract
--postflag if present - Parse remaining as version range:
- If contains
..: split into{from_tag}..{to_tag} - If single version: use
{version}..HEAD - If empty: auto-detect with
git describe --tags --abbrev=0
- If contains
Git Context (auto-loaded)
!git describe --tags --abbrev=0 2>/dev/null || echo "[NO_TAGS]"
!git log $(git describe --tags --abbrev=0 2>/dev/null || echo "")..HEAD --pretty=format:"%H %s" --no-merges 2>/dev/null || git log --pretty=format:"%H %s" --no-merges
Execution Steps
1. Determine Range
- If a tag is found, set
from_tagto that tag,to_tagto HEAD - If no tags exist, inform the user: "No tags found. Include all commits? (y/n)"
- If a
to_tagis specified (not HEAD), verify it exists:git rev-parse --verify {to_tag} 2>/dev/null - Determine the version label for the notes header:
- If
to_tagis a version tag: use it (e.g.,v2.4.0) - If
to_tagis HEAD: use "Unreleased" or thefrom_tagbumped (ask user)
- If
2. Collect Raw Data
Run these commands to gather change context:
# Commit history (no merges)
git log {from_tag}..{to_tag} --pretty=format:"%H %s" --no-merges
# Merged PRs since the from_tag date
from_date=$(git log -1 --format=%aI {from_tag})
gh pr list --state merged --search "merged:>$from_date" --json number,title,author,labels,body --limit 100
If gh is not available or the repo has no remote, skip PR collection — proceed with git-only data.
3. Detect Breaking Changes
Search commit messages and PR titles for breaking change indicators:
- Patterns:
BREAKING,BREAKING CHANGE,!:(conventional commitsfeat!:,fix!:) - Also check PR labels for:
breaking,breaking-change,semver-major
Flag any matches for the Breaking Changes section.
4. Categorize and Rewrite
Categorize each commit/PR into one of:
| Category | Conventional Commit Prefixes | Fallback (no prefix) |
|---|---|---|
| Breaking Changes | !: suffix, BREAKING | Label: breaking |
| New Features | feat: | Commit introduces new user-facing functionality |
| Bug Fixes | fix: | Commit fixes broken or incorrect behavior |
| Other Changes | chore:, docs:, ci:, refactor:, perf:, test:, style:, build: | Everything else |
Fallback classification rule — For commits without conventional prefixes, read the commit message semantically. Classify based on what the commit actually does: did it introduce new user-facing functionality (New Features)? Did it fix broken behavior (Bug Fixes)? Or is it a maintenance/improvement change (Other Changes)? Do not match individual words — understand the commit's purpose.
The word "add" in "add a new API endpoint" indicates a feature. The same word in "add missing test coverage" indicates a test (Other Changes). Context determines classification.
Rewriting rules — transform each entry from developer-speak to user-facing language:
- Remove conventional commit prefixes (
feat:,fix(scope):, etc.) - Rewrite in terms of what the user experiences, not what the developer changed
- Bad: "Refactor ThemeProvider to use context API"
- Good: "Improved theme switching reliability"
- Bad: "Fix race condition in afc-state.sh"
- Good: "Fixed an issue where pipeline state could become corrupted during concurrent execution"
- Merge related commits into a single entry when they address the same feature/fix
- Include PR number references where available:
(#42) - For breaking changes, add a brief migration note after the description
5. Collect Contributor Info
Build a contributors section:
# Get commit authors with their commit counts
git log {from_tag}..{to_tag} --format="%aN" | sort | uniq -c | sort -rn
# Resolve repo identity for GitHub username lookup
gh repo view --json owner,name --jq '"\(.owner.login)/\(.name)"'
# Map git authors to GitHub usernames via commit SHAs
git log {from_tag}..{to_tag} --format="%H" | head -100 | while read sha; do
gh api "repos/{owner}/{repo}/commits/$sha" --jq '.author.login // empty' 2>/dev/null
done | sort -u
For each contributor:
- Try to resolve GitHub username via the commit SHA lookup above, or from PR author data collected in step 2
- List their PR numbers if available
- Fall back to git author name if no GitHub username found
- If
ghis not available, skip username resolution entirely — use git author names
6. Compose Release Notes
Assemble the final release notes in this format:
# {version}
{2-3 sentence summary: what is the most important thing in this release? Written for end users.}
## Breaking Changes
- {description + migration guide}
## New Features
- {user-facing description} (#{pr_number})
## Bug Fixes
- {user-facing description} (#{pr_number})
## Other Changes
- {description} (#{pr_number})
## Contributors
{contributor list with @mentions and PR numbers}
Format rules:
- Omit empty sections entirely (if no breaking changes, skip that section)
- Breaking Changes section always comes first when present
- Each entry is a single bullet point, max 2 sentences
- Summary paragraph should highlight the top 1-2 changes
- Contributors section:
@username (#42, #45)format, orName (#42)if no GitHub username
7. Preview Output
Display the complete release notes to the user in the console.
Print a summary:
Release notes generated
├─ Version: {version}
├─ Range: {from_tag}..{to_tag}
├─ Commits: {N}
├─ PRs referenced: {N}
├─ Breaking changes: {count or "none"}
├─ Contributors: {N}
└─ --post: {will publish / preview only}
8. Publish to GitHub Releases (if --post)
If --post flag is present:
-
Determine the tag name:
- If
to_tagis a specific tag: use it - If
to_tagis HEAD: ask the user what tag to create (suggest next version)
- If
-
Ask user to confirm using AskUserQuestion:
- Post as-is — Publish the GitHub Release immediately
- Post as draft — Create as draft release (can review and publish later from GitHub)
- Edit first — Let me modify the notes before posting
- Cancel — Do not post
-
On approval, write to a temp file and publish:
tmp_file=$(mktemp) cat > "$tmp_file" << 'NOTES_EOF' {release notes content} NOTES_EOF # Add --draft if user chose "Post as draft" gh release create {tag} --notes-file "$tmp_file" --title "{version}" [--draft] rm -f "$tmp_file" -
Print result:
GitHub Release published ├─ Tag: {tag} ├─ Title: {version} ├─ Status: {published / draft} └─ URL: {release URL from gh output}
If --post is not present, print:
To publish these notes as a GitHub Release, run again with --post flag.
Notes
- Read-only by default: Without
--post, this command only outputs to console. No files are created or modified. - Complements
/afc:launch: Uselaunchfor local artifact generation (CHANGELOG, README). Userelease-notesfor GitHub Release publishing. - Conventional Commits aware: Projects using conventional commits get better categorization. Projects without them still get reasonable results via heuristic matching.
- Contributor attribution: Best effort. GitHub username resolution requires
ghCLI and repository access. - User confirmation required:
--postalways asks for explicit approval before publishing to GitHub. - Verify after publishing: After
gh release createcompletes, verify success by checking the exit code and output URL. If it fails, report the error and suggest manual creation. - Idempotent: Running without
--postis safe to repeat. With--post, GitHub Releases for existing tags will fail (GitHub does not allow duplicate release tags).