Install
$ agentstack add skill-atypical-consulting-githubskills-ghs-release ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Release Management
Automate GitHub Release creation: generate a changelog from commits and merged PRs since the last release, determine the version bump, create a Git tag and GitHub Release with structured release notes.
References:
- ../shared/references/gh-cli-patterns.md
- ../shared/references/output-conventions.md
- ../shared/references/ui-brand.md
- ../shared/references/argument-parsing.md
- ../shared/references/checkpoint-patterns.md
Purpose: Create GitHub Releases with auto-generated changelogs, semver version detection, and conventional commit classification.
Roles:
- Releaser (you) — detects current version, gathers changes, classifies them, determines version bump, generates changelog, checks CI, creates the release
This skill does not spawn sub-agents — the release workflow is a single sequential pipeline.
Shared references:
| Reference | Purpose | |-----------|---------| | ../shared/references/gh-cli-patterns.md | Authentication, repo detection, error handling, PR operations | | ../shared/references/output-conventions.md | Status indicators, table formats, summary blocks |
| Do NOT | Do Instead | Why | |--------|-----------|-----| | Create a release without checking CI status on the default branch | Run pre-flight CI check and warn if failing | Releasing broken code erodes trust and may require a hotfix release | | Skip changelog generation and use a bare tag | Always generate a grouped changelog from PRs and commits | Empty release notes provide no value to users or contributors | | Guess the next version number | Derive from conventional commits analysis or use user-provided explicit version | Wrong versions break semver contracts and confuse dependency consumers | | Create a release on a non-default branch without user confirmation | Detect the target branch and confirm with user if it differs from default | Releasing from feature branches can ship incomplete or experimental work | | Include unmerged PRs in the changelog | Filter strictly by merged state and merge date after last release | Unmerged PRs are not part of the release and mislead users | | Skip draft mode for major releases | Default to --draft for major version bumps and confirm before publishing | Major releases deserve extra review since they signal breaking changes | | Create a release when no changes exist since the last one | Warn the user and abort unless --force is specified | Empty releases add noise and confuse the version timeline | | Delete or overwrite existing tags | Warn if the computed tag already exists and ask the user | Overwriting tags breaks anyone who already pulled the previous tag |
Scope Boundary
This skill creates tags and releases only — it never modifies code, merges PRs, deletes branches, or changes repository settings. The sole write actions are:
- Creating a Git tag via
gh release create(which tags automatically) - Creating a GitHub Release with generated notes
Everything else is read-only investigation.
Create a GitHub Release with a structured changelog grouped by change type.
Outputs:
- GitHub Release URL (or dry-run preview in terminal)
- Terminal changelog preview with version summary
Next routing (see output-conventions.md @ Routing Suggestions):
- Suggest
ghs-backlog-boardto see updated dashboard — "To review status:/ghs-backlog-board" - If there are open PRs remaining, suggest
ghs-merge-prs— "To merge remaining PRs:/ghs-merge-prs {owner}/{repo}"
Read git tags and merged PRs since last release.
Input
- Explicit version:
release v2.0.0,create release v1.3.0 - Bump type:
patch release,minor release,major release - Auto-detect:
create release,what would the next release look like - Dry-run:
prepare release,changelog,what would the next release look like— show preview without creating - Pre-release:
pre-release,release v1.0.0-beta.1,alpha release - Draft:
draft release— creates release as draft
Input Parsing Rules
| Rule | Trigger | Example | |------|---------|---------| | Explicit version overrides auto-detect | User provides a full semver string | release v2.0.0 — use v2.0.0 regardless of commit analysis | | Bump type narrows auto-detect | User specifies major/minor/patch | patch release — force patch bump even if commits suggest minor | | Dry-run by default for informational queries | User asks "what would" or "changelog" or "prepare" | what would the next release look like — show preview, do not create | | Pre-release suffix detection | Version contains -alpha, -beta, -rc | release v1.0.0-beta.1 — set --prerelease flag | | Draft flag detection | User says "draft" | draft release — set --draft flag | | Repo detection | No explicit repo | Detect from gh repo view (see gh-cli-patterns.md @ Repo Detection) |
Dry-Run Mode
When --dry-run is present in $ARGUMENTS:
- Calculate next version and generate changelog
- Display the full release notes that would be published
- Do not create any tags, releases, or commits
- Display the dry-run indicator box from ui-brand.md
Phase 1 — Detect Current Version
Determine the latest released version using multiple sources:
| Source | Command | Fallback | |--------|---------|----------| | Latest GitHub release | gh release list --limit 1 --json tagName,publishedAt | If empty, try git tags | | Latest git tag | git tag --sort=-v:refname \| head -1 | If empty, treat as first release | | No previous release | N/A | Default to v0.1.0 or user-specified version |
# Preferred: GitHub release (includes date for changelog range)
LAST_RELEASE=$(gh release list --repo {owner}/{repo} --limit 1 --json tagName,publishedAt -q '.[0]')
# Fallback: git tag
if [ -z "$LAST_RELEASE" ]; then
LAST_TAG=$(git tag --sort=-v:refname | head -1)
fi
Extract the release date for the changelog query range. If using a git tag without a release, use the tag's commit date:
git log -1 --format=%aI {tag}
Phase 2 — Gather Changes Since Last Release
Merged PRs
gh pr list --repo {owner}/{repo} --state merged \
--search "merged:>={last_release_date}" \
--json number,title,labels,author,mergedAt \
--limit 200
Commits (for repos that don't use PRs)
git log {last_tag}..HEAD --oneline --format="%h %s" --no-merges
Guard rail: If both lists are empty, warn the user and abort:
[WARN] No changes found since {last_tag} ({last_release_date}).
Nothing to release. Use --force to create an empty release.
Phase 3 — Classify Changes
Conventional Commits Mapping
| Prefix | Version Bump | Changelog Group | |--------|-------------|-----------------| | feat: / feat(scope): | minor | Features | | fix: / fix(scope): | patch | Bug Fixes | | BREAKING CHANGE: / !: (e.g., feat!:) | major | Breaking Changes | | docs: / docs(scope): | patch | Documentation | | chore(deps): / build(deps): / deps: | patch | Dependencies | | ci: / test: / refactor: / perf: / style: / chore: / build: | patch | Other |
Classification Rules
| Rule | Trigger | Example | |------|---------|---------| | PR title takes precedence over individual commits | PR has a conventional prefix in its title | PR #42 "feat: add dark mode" — classified as Features | | Label-based fallback if no conventional prefix | PR has labels like bug, enhancement, dependencies | PR #43 labeled bug — classified as Bug Fixes | | Commits without PRs are classified individually | Commit not associated with any PR | fix: correct typo — classified as Bug Fixes | | Unknown prefix defaults to Other | No recognized prefix or label | Update README — classified as Other | | BREAKING CHANGE in commit body also triggers major | Body contains BREAKING CHANGE: | feat: new API\n\nBREAKING CHANGE: removed v1 endpoints — major + Breaking Changes |
Label-to-Group Mapping (Fallback)
| Label | Changelog Group | |-------|-----------------| | bug, fix, type:bug | Bug Fixes | | enhancement, feature, type:feature | Features | | dependencies, deps, renovate, dependabot | Dependencies | | documentation, docs, type:docs | Documentation | | breaking, breaking-change | Breaking Changes |
Phase 4 — Determine Version Bump
If the user provided an explicit version, use it. Otherwise, compute from classified changes:
| Highest Change Type | Bump | Example | |--------------------|------|---------| | Breaking Changes present | major | v1.2.3 -> v2.0.0 | | Features present (no breaking) | minor | v1.2.3 -> v1.3.0 | | Only fixes/deps/docs/other | patch | v1.2.3 -> v1.2.4 |
Pre-release Version Handling
| Current Version | Bump Type | Next Version | |----------------|-----------|--------------| | v1.0.0 | pre-release (alpha) | v1.1.0-alpha.1 (or user-specified) | | v1.1.0-alpha.1 | pre-release (alpha) | v1.1.0-alpha.2 | | v1.1.0-alpha.2 | pre-release (beta) | v1.1.0-beta.1 | | v1.1.0-rc.1 | release | v1.1.0 |
Guard Rails
| Rule | Trigger | Example | |------|---------|---------| | Warn if major bump detected | Conventional commits show breaking changes | [WARN] Breaking changes detected — this will be a MAJOR release (v1.x -> v2.0.0). Proceed? | | Warn if tag exists | Computed tag already exists in git | [WARN] Tag v1.3.0 already exists. Use a different version or --force to overwrite. | | Validate semver format | User-provided version doesn't match semver | [FAIL] "v1.2" is not valid semver. Use format: v{major}.{minor}.{patch}[-prerelease] |
Phase 5 — Generate Changelog
Group classified changes into the changelog template:
## What's Changed
### Breaking Changes
- {PR title or commit description} by @{author} in #{number}
### Features
- {PR title or commit description} by @{author} in #{number}
### Bug Fixes
- {PR title or commit description} by @{author} in #{number}
### Dependencies
- {PR title or commit description} by @{author} in #{number}
### Documentation
- {PR title or commit description} by @{author} in #{number}
### Other
- {PR title or commit description} by @{author} in #{number}
**Full Changelog**: https://github.com/{owner}/{repo}/compare/{prev_tag}...{next_tag}
Rules:
- Omit empty sections (if no breaking changes, skip that heading)
- Strip conventional commit prefixes from titles (e.g.,
feat: add dark modebecomesAdd dark mode) - Capitalize the first letter of each entry
- For commits without PRs, use the commit hash instead of
#{number} - Sort entries within each group by merge date (newest first)
Phase 6 — Pre-flight Checks
Before creating the release, verify:
| Check | Command | Action on Failure | |-------|---------|-------------------| | CI status on default branch | gh run list --repo {owner}/{repo} --branch {default_branch} --limit 1 --json status,conclusion | Warn user; require --force to proceed | | Target branch is default | gh repo view --json defaultBranchRef -q '.defaultBranchRef.name' | Warn and ask for confirmation if different | | Tag does not already exist | git tag -l {next_tag} | Abort and ask user for a different version | | Authenticated user has write access | gh repo view --json viewerPermission | Abort if insufficient permissions |
[PASS] CI passing on main (run #456)
[PASS] Targeting default branch: main
[PASS] Tag v1.3.0 does not exist
[PASS] Write access confirmed
If CI is failing:
[WARN] CI is FAILING on main (run #456 — conclusion: failure)
Creating a release with failing CI is not recommended.
Use --force to override, or wait for CI to pass.
Phase 7 — Create Release (or Dry-Run Preview)
Dry-Run Mode
If dry-run, display the changelog and version summary in the terminal without creating anything:
## Dry-Run Release Preview: {owner}/{repo}
Version: v1.2.3 -> v1.3.0 (minor bump)
Tag: v1.3.0
Target: main
Mode: dry-run (no release created)
{changelog}
---
Summary:
Changes: 12 (3 features, 5 fixes, 2 deps, 2 other)
Authors: @alice, @bob, @charlie
Status: DRY-RUN — no release created
To create this release: `/ghs-release v1.3.0`
Create Mode
gh release create {next_tag} \
--repo {owner}/{repo} \
--title "{next_tag}" \
--notes "$(cat v1.3.0
Tag: v1.3.0
Target: main
Type: {release | draft | pre-release}
URL: https://github.com/{owner}/{repo}/releases/tag/{next_tag}
### Changelog
{changelog}
---
Summary:
Changes: 12 (3 features, 5 fixes, 2 deps, 2 other)
Authors: @alice, @bob, @charlie
PRs: #40, #41, #42, #43, #44, #45, #46, #47, #48, #49, #50, #51
Status: [PASS] Release created
Failed Release
## Release Failed: {owner}/{repo}
Version: v1.3.0
Error: {error message}
Status: [FAIL] Release not created
Troubleshooting:
- Check `gh auth status` for authentication
- Verify write access to the repository
- Ensure the tag does not already exist
Edge Cases
| Scenario | Handling | |----------|----------| | No previous releases (first release) | Default to v0.1.0; gather all commits on default branch; note "Initial release" in changelog header | | Pre-release versions | Detect -alpha, -beta, -rc suffixes; increment pre-release counter; set --prerelease flag | | No changes since last release | Warn user and abort; suggest checking if PRs were merged to a different branch | | Failed CI on default branch | Warn with run URL; require --force or wait for CI to pass | | Monorepo | Note limitation — this skill creates a single release per repo; suggest manual scoping for monorepos | | Tag already exists | Abort with error; suggest using a different version number | | Non-default branch target | Warn and require explicit confirmation before creating | | Very large changelog (50+ PRs) | Group normally but add a count summary; truncate individual entries if the body would exceed 10,000 characters | | Archived repository | Detect via gh repo view --json isArchived; abort with warning | | No conventional commits | Fall back to label-based classification; if no labels either, list all as "Other" |
Examples
Good Release Notes
## What's Changed
### Features
- Add dark mode support for dashboard by @alice in #42
- Implement webhook retry logic by @bob in #38
### Bug Fixes
- Fix login timeout on slow connections by @charlie in #41
- Correct pagination offset in API response by @alice in #40
### Dependencies
- Update actions/checkout action to v6 by @renovate in #39
- Update dependency node to v24 by @renovate in #37
**Full Changelog**: https://github.com/owner/repo/compare/v1.2.3...v1.3.0
Bad Release Notes
## What's Changed
- feat: dark mode
- fix stuff
- bump deps
- misc changes
- Update README.md
(Missing: author attribution, PR links, grouping by type, full changelog link, capitalization)
Example 1: Auto-detect Release
User says: create release Result: Detects current version v1.2.3, analyzes commits, determines minor bump (features found), generates grouped changelog, checks CI, creates release v1.3.0, shows terminal summary with URL.
Example 2: Explicit Version
User says: release v2.0.0 Result: Uses v2.0.0 as-is, generates changelog since v1.2.3, warns about major release, creates as draft, shows terminal summary.
Example 3: Dry-Run Preview
User says: what would the next release look like Result: Detects version, analyzes changes, shows changelog preview in terminal, does NOT create release. Suggests command to create.
Example 4: Pre-release
User says: release v1.3.0-beta.1 Result: Creates pre-release with --prerelease flag, generates changelog, shows terminal summary.
Example 5: No Changes
User says: create release Result: Detects v1.2.3 as current, finds no merged PRs or commits since then, warns user, abo
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Atypical-Consulting
- Source: Atypical-Consulting/GitHubSkills
- License: MIT
- Homepage: https://atypical-consulting.github.io/GitHubSkills/
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.