|
| 1 | +--- |
| 2 | +name: release |
| 3 | +description: "Plan and publish a GitHub Release in a tag-driven repository. Use when a user asks to cut, prepare, or publish a software release, propose the next vX.Y.Z tag, draft better release notes from PRs and direct commits since the last release, update CHANGELOG.md, create the tag pinned to an exact commit, and watch the publish workflow." |
| 4 | +--- |
| 5 | + |
| 6 | +# Release |
| 7 | + |
| 8 | +Use this skill for repos that publish from GitHub Releases and want human-written notes instead of GitHub's generated summary. |
| 9 | + |
| 10 | +## Guardrails |
| 11 | + |
| 12 | +- Prefer the repo's default branch from `gh repo view`; do not guess. |
| 13 | +- Start from a clean working tree. If tracked files are dirty, stop and ask before continuing. |
| 14 | +- If the current branch is not the default branch, stop and ask before switching. |
| 15 | +- Fetch before planning: `git fetch origin --tags`. |
| 16 | +- Fast-forward the default branch before editing: `git pull --ff-only origin <default-branch>`. |
| 17 | +- Never force-push the default branch. |
| 18 | +- Never use GitHub generated release notes for this workflow. |
| 19 | +- Always create the release tag with a leading `v`, for example `v0.1.0`. |
| 20 | +- Always pin the release to the exact changelog commit SHA with `gh release create --target <sha>`. |
| 21 | +- If `origin/<default-branch>` moves after planning or before pushing, stop and regenerate the release plan. |
| 22 | + |
| 23 | +## Helper Script |
| 24 | + |
| 25 | +Use the bundled planner to gather release facts and raw note inputs: |
| 26 | + |
| 27 | +```bash |
| 28 | +python3 .agents/skills/release/scripts/release_plan.py --output-dir .local/release |
| 29 | +``` |
| 30 | + |
| 31 | +It writes: |
| 32 | + |
| 33 | +- `.local/release/release-plan.json` |
| 34 | +- `.local/release/release-plan.md` |
| 35 | + |
| 36 | +The planner: |
| 37 | + |
| 38 | +- finds the latest published semver release |
| 39 | +- counts first-parent commits on the default branch since that release |
| 40 | +- filters leaked release-housekeeping commits such as changelog-only commits |
| 41 | +- proposes the next tag |
| 42 | +- groups PR-backed changes separately from direct commits on `main` |
| 43 | +- captures contributor mentions for PR-backed items |
| 44 | + |
| 45 | +## Approval Prompt |
| 46 | + |
| 47 | +Before making any changelog edit, commit, push, tag, or release, show the user: |
| 48 | + |
| 49 | +- the last release tag |
| 50 | +- the raw and meaningful commit counts since that release |
| 51 | +- the suggested new tag and why |
| 52 | +- whether the change looks like a minor release or a small emergency patch |
| 53 | +- the exact commit SHA currently at `origin/<default-branch>` |
| 54 | + |
| 55 | +If the meaningful commit count is less than `3`, explicitly warn that there are not many changes in this release and ask whether they still want to proceed. |
| 56 | + |
| 57 | +## Notes And Changelog Rules |
| 58 | + |
| 59 | +- Do not copy PR titles verbatim into release notes. |
| 60 | +- Rewrite each PR-backed item into a clearer user-facing bullet. |
| 61 | +- For direct commits on `main` with no PR, use the commit subject and body as raw input and rewrite those too. |
| 62 | +- Add the PR author mention on the same line for PR-backed entries. |
| 63 | +- Keep the same substance in `CHANGELOG.md` and the GitHub release notes. |
| 64 | +- Prefer grouped sections such as `Highlights`, `Fixes`, `Performance`, `Docs`, and `Internal` when they fit the release. |
| 65 | +- If `CHANGELOG.md` does not exist, create it with a `# Changelog` header. |
| 66 | +- Insert the new release section at the top, directly under the file header if there is one. |
| 67 | +- Use a heading in this shape: |
| 68 | + |
| 69 | +```md |
| 70 | +## v0.1.0 - 2026-03-15 |
| 71 | +``` |
| 72 | + |
| 73 | +- If you make a dedicated changelog commit, use a subject like: |
| 74 | + |
| 75 | +```bash |
| 76 | +docs: add changelog for v0.1.0 |
| 77 | +``` |
| 78 | + |
| 79 | +## Versioning Heuristic |
| 80 | + |
| 81 | +Use the planner's suggestion unless the user overrides it. |
| 82 | + |
| 83 | +- Default to a minor bump: `v0.1.0` -> `v0.2.0`. |
| 84 | +- Use a patch bump only for a small hotfix shortly after the previous release. |
| 85 | +- Treat `v0.9.0` -> `v0.10.0` as the normal next minor bump. |
| 86 | +- Do not jump from `v0.9.0` to `v1.0.0` unless the user explicitly asks. |
| 87 | + |
| 88 | +The bundled planner treats a patch release as the default only when all of these are true: |
| 89 | + |
| 90 | +- the last release is recent |
| 91 | +- there are only a few meaningful commits |
| 92 | +- the included changes are patch-sized fix/docs/ci/chore/deps style work |
| 93 | +- there is no obvious feature or larger performance/restructure change |
| 94 | + |
| 95 | +## Execution Flow |
| 96 | + |
| 97 | +1. Prepare the repo. |
| 98 | + |
| 99 | +```bash |
| 100 | +git fetch origin --tags |
| 101 | +default_branch=$(gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name') |
| 102 | +current_branch=$(git branch --show-current) |
| 103 | +test "$current_branch" = "$default_branch" |
| 104 | +git pull --ff-only origin "$default_branch" |
| 105 | +python3 .agents/skills/release/scripts/release_plan.py --output-dir .local/release |
| 106 | +``` |
| 107 | + |
| 108 | +2. Read `.local/release/release-plan.md` and summarize the proposed release for approval. |
| 109 | + |
| 110 | +3. After approval, write: |
| 111 | + |
| 112 | +- `.local/release/release-notes.md` |
| 113 | +- `CHANGELOG.md` |
| 114 | + |
| 115 | +4. Commit and push the changelog commit on top of the planned branch tip. |
| 116 | + |
| 117 | +```bash |
| 118 | +git add CHANGELOG.md |
| 119 | +git commit -m "docs: add changelog for <tag>" |
| 120 | +git push origin HEAD:"$default_branch" |
| 121 | +release_sha=$(git rev-parse HEAD) |
| 122 | +``` |
| 123 | + |
| 124 | +5. Create the release from that exact commit. |
| 125 | + |
| 126 | +```bash |
| 127 | +gh release create "<tag>" \ |
| 128 | + --target "$release_sha" \ |
| 129 | + --title "<tag>" \ |
| 130 | + --notes-file .local/release/release-notes.md |
| 131 | +``` |
| 132 | + |
| 133 | +6. Verify the release and watch the publish workflow. |
| 134 | + |
| 135 | +```bash |
| 136 | +gh release view "<tag>" |
| 137 | +run_id=$(gh run list --workflow publish.yml --event release --limit 10 --json databaseId,headSha,status,conclusion \ |
| 138 | + --jq '.[] | select(.headSha == "'"$release_sha"'") | .databaseId' | head -n1) |
| 139 | +gh run watch "$run_id" |
| 140 | +``` |
| 141 | + |
| 142 | +If the publish workflow fails, inspect it yourself: |
| 143 | + |
| 144 | +```bash |
| 145 | +gh run view "$run_id" --log-failed |
| 146 | +``` |
| 147 | + |
| 148 | +## Best Practices |
| 149 | + |
| 150 | +- Re-read the generated notes before publishing; fix awkward wording instead of shipping raw commit text. |
| 151 | +- Keep release bullets user-facing and outcome-oriented, not implementation-jargon heavy. |
| 152 | +- Mention direct-to-main commits that would otherwise be invisible to GitHub's PR-based notes. |
| 153 | +- If the approval gap was long, rerun the planner immediately before editing `CHANGELOG.md`. |
| 154 | +- If the push to the default branch is rejected, stop and regenerate notes from the new branch tip instead of rebasing blindly. |
0 commit comments