agentleFS
getstackitgetstackit/stackit/.claude/skills/create-release/SKILL.md

create-release

Skill32 starsChanged 14 days agoMITView on GitHub

Draft and publish a new GitHub release for stackit. Compares main against the latest release tag, generates release notes in the established format (Highlights, Features, Fixes, Performance, Refactors, Docs, Chore), pushes the tag (triggers goreleaser to build assets), then overwrites the release body with the curated notes. Keywords release, tag, changelog, ship.

  • Commits and pushes

17 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use create-issue, create-pr and 1 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • sahinSkill

    create-issue

    Also in 4 projects that use this

  • SentrySkill

    create-pr

    Create pull requests following Sentry conventions. Use when opening PRs, writing PR descriptions, or preparing changes for review. Follows Sentry's code review guidelines.

    Also in 5 projects that use this

  • DockerSkill

    review-pr

    Review one or more incoming Docker documentation pull requests as a maintainer. Independently validate technical claims, assess editorial fit and information architecture, choose a verdict, and draft exact inline or PR-wide feedback behind a confirmation gate. Use for requests such as "review PR 123", "is this PR correct?", "does this information belong here?", "validate this PR", or "help review backlog PRs". Do not use to maintain or fix a PR you own; use maintain-pr for that.

    Also in 4 projects that use this

The file

We split this skill into the rules it gives an agent

12 rules. 0 are common, 3 are distinctive, and 9 are very specific to this project.

  • 0

    Common

    Most of the 16 edited versions we read keep these.

  • 3

    Distinctive

    This author's own choices. Worth a look.

  • 9

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Do not create the GitHub release yourself with gh release create — goreleaser owns release creation.

    line 20
  2. Very specific:

    All scripts use set -euo pipefail and fail loudly with diagnostic messages.

    line 38
  3. Distinctive:

    If it fails, stop and surface the message.

    line 48
  4. Very specific:

    Read the JSON. If .commits is empty, stop — nothing to release.

    line 67
  5. Very specific:

    Write each PR as a past-tense one-liner ending with (#NNN).

    line 102
  6. Very specific:

    Write the notes via the Write tool to $NOTES_FILE.

    line 133
  7. Distinctive:

    Default: include the footer.

    line 177
  8. Very specific:

    Read the notes file back and show the user both $NEXT_TAG and the full notes.

    line 181
  9. Distinctive:

    Do not push the tag without explicit approval at this step.

    line 188
  10. Very specific:

    If gh run list --workflow=release.yml --limit 1 shows the run failed, surface that — the tag exists but assets and Homebrew may be missing.

    line 206
  11. Very specific:

    Run one command per Bash call — don't chain with &&.

    line 212
  12. Very specific:

    Don't append 2>&1.

    line 214

“Common” is measured: kept by at least 6 in 10 of the 16 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

/create-release

Cut a new GitHub release for stackit by comparing main against the latest release tag and drafting curated notes.

Mental Model

The repo is wired so that pushing a vX.Y.Z tag triggers .github/workflows/release.yml, which runs goreleaser to build cross-platform binaries, publish the Homebrew tap, and create the GitHub release with an auto-generated body. Our job in this skill is to:

  1. Determine the next version.
  2. Draft a curated release body in the project's established format.
  3. Tag and push so goreleaser can do its thing.
  4. Once goreleaser has created the release, overwrite the body with the curated notes.

Do not create the GitHub release yourself with gh release create — goreleaser owns release creation. We only edit the body after the fact.

Scripts

The deterministic steps live in .claude/skills/create-release/scripts/. Use them instead of inlining commands — they encode the right error checks and keep invocations identical across runs (which keeps the permission allowlist small).

ScriptPurpose
preflight.shVerify on main, clean tree, in sync with origin
last-tag.shPrint the latest release tag (or git describe fallback)
gather-changelog.sh <tag>Build stackit from source and print the stack-aware changelog since <tag> as JSON ({commits: [...]}) — collapses consolidated stacks into one entry listing their constituent PRs
gather-commits.sh <tag>Direct-push backstop: list commits since <tag> to spot pushes that bypassed PR review
bump-version.sh <current> <patch|minor|major>Compute next semver tag
tag-and-push.sh <tag>Create annotated tag + push to origin
wait-for-release.sh <tag> [timeout]Poll until goreleaser materializes the release (default 1200s; goreleaser typically takes 9-11 minutes)
apply-notes.sh <tag> <file>Overwrite release body with notes file, print URL
append-github-footer.sh <new> <prev> <file>Append GitHub's auto-generated "What's Changed" footer

All scripts use set -euo pipefail and fail loudly with diagnostic messages. Treat their exit codes as authoritative.

Execution Steps

Step 1: Preflight

bash .claude/skills/create-release/scripts/preflight.sh

If it fails, stop and surface the message. Do not attempt to auto-fix (checkout, stash, pull). The user owns those decisions.

Step 2: Last Tag

LAST_TAG=$(bash .claude/skills/create-release/scripts/last-tag.sh)

Step 3: Gather Changes

bash .claude/skills/create-release/scripts/gather-changelog.sh "$LAST_TAG" > /tmp/stackit-release-changelog.json
bash .claude/skills/create-release/scripts/gather-commits.sh "$LAST_TAG"

gather-changelog.sh builds stackit from the current source (≈30s) and runs stackit log --json, so it works even when the installed stackit predates the new log command.

Read the JSON. If .commits is empty, stop — nothing to release. Use gather-commits.sh only as a sanity check: if it shows non-merge commits with no corresponding PR (and not part of a stack), flag those as possible direct pushes.

Step 4: Derive the PR List and Categorize (Model Judgment)

Each entry in .commits is either a regular commit or a stack-merge (a consolidated stack). Flatten them into a deduplicated list of merged PRs:

  • kind: "stack-merge" — emit each PR in stackPRs, titled from stackPRTitles (keyed by PR number). These are the real shipped PRs. Do not emit the entry's own prNumber — that's the "Merging N PRs" consolidation rollup, not a changelog item. Note stackScope: PRs sharing a scope shipped together as one stack.
  • kind: "regular" with prNumber set — emit (prNumber, message).
  • kind: "regular" with no prNumber — skip. These are raw squashed commits already represented by a stack-merge's constituents (or a direct push, which gather-commits.sh surfaces separately).
  • Dedup by PR number — the same PR can appear via both a merge commit and a consolidation commit.

Flattening stack-merges into stackPRs handles rollup-dropping automatically: the "Merging N PRs" PR is the stack-merge's own prNumber, which we never emit.

Titles follow Conventional Commits. Bucket each PR by its title prefix:

BucketConventional types
Featuresfeat
Fixesfix
Performanceperf
Refactorsrefactor
Docsdocs
Chorechore, ci, test, style

Write each PR as a past-tense one-liner ending with (#NNN). — matching v0.17.11 / v0.17.13 style. Combine related PRs onto one line when natural (e.g., dependency bumps → (#NNN, #MMM)). When several PRs share a stackScope, prefer grouping them adjacently so the stack reads as one feature.

Step 5: Pick Highlights (Model Judgment)

Pick 2–5 Highlights. A highlight is something a user will notice or care about — a new flag, a workflow improvement, a major refactor with visible impact. Skip cosmetic refactors and dependency bumps. Each highlight gets a 1–3 sentence bullet explaining what changed and why it matters to the user.

Step 6: Confirm the Next Version

Propose a default based on the categorized PRs:

  • Patch — only fixes, perf, refactor, docs, chore.
  • Minor — any user-visible new features, new flags, new commands.
  • Major — explicit breaking changes (rare pre-1.0).

Always ask the user via AskUserQuestion before computing the tag, even when the bump looks obvious. Tagging is durable and triggers a release pipeline; an unconfirmed bump is not recoverable without deleting a published tag.

Once the user picks a kind:

NEXT_TAG=$(bash .claude/skills/create-release/scripts/bump-version.sh "$LAST_TAG" "$BUMP_KIND")

Step 7: Assemble Notes

NOTES_FILE=$(mktemp -t stackit-release-notes)

Write the notes via the Write tool to $NOTES_FILE. Structure:

<Optional 1–2 sentence opener describing the theme of the release. Omit if
the release has no unifying theme.>

### Highlights

- <highlight 1>
- <highlight 2>

### Features

- <feature> (#NNN).

### Fixes

- <fix> (#NNN).

### Performance

- <perf change> (#NNN).

### Refactors

- <refactor> (#NNN).

### Docs

- <docs change> (#NNN).

### Chore

- <chore> (#NNN).

Omit empty sections entirely.

Optionally append GitHub's auto-generated footer (the "What's Changed" PR list + "Full Changelog" diff link) so the body matches the historical shape:

bash .claude/skills/create-release/scripts/append-github-footer.sh "$NEXT_TAG" "$LAST_TAG" "$NOTES_FILE"

Default: include the footer. Skip only if the user explicitly asks for a minimal body.

Step 8: Final Confirmation Before Tagging

Read the notes file back and show the user both $NEXT_TAG and the full notes. Use AskUserQuestion:

  • Approve and tag — proceeds to Step 9.
  • Edit notes — rewrite the file with the user's corrections, then re-confirm.
  • Change version — loop back to Step 6.
  • Cancel — stop without tagging.

Do not push the tag without explicit approval at this step.

Step 9: Tag, Wait, Apply Notes

bash .claude/skills/create-release/scripts/tag-and-push.sh "$NEXT_TAG"
bash .claude/skills/create-release/scripts/wait-for-release.sh "$NEXT_TAG"
bash .claude/skills/create-release/scripts/apply-notes.sh "$NEXT_TAG" "$NOTES_FILE"

apply-notes.sh prints the release URL on success.

Step 10: Report

Print to the user:

  • The new tag and release URL.
  • Reminder that the Homebrew tap will update once goreleaser finishes.
  • If gh run list --workflow=release.yml --limit 1 shows the run failed, surface that — the tag exists but assets and Homebrew may be missing.

Permission Stability

Per .claude/rules/stackit-workflow.md:

  • Run one command per Bash call — don't chain with &&.
  • Pass release notes via a file path, never inline with -m/--notes.
  • Don't append 2>&1.

Calling the scripts (rather than inlining gh/git commands) keeps the command shape bash .claude/skills/create-release/scripts/<name>.sh ... across every invocation, so a single permission rule covers all of them.

Common Pitfalls

MistakeFix
Tagging from a non-main branchpreflight.sh aborts
Tagging with a dirty treepreflight.sh aborts
Using gh release create directlyGoreleaser owns release creation; we only edit after
Forgetting to push the tagtag-and-push.sh does both atomically
Overwriting goreleaser's release body before it's createdwait-for-release.sh polls until ready
Patch bump when adding a user-visible featureRe-check Step 6 — feat-level changes warrant a minor bump
Emitting a stack-merge's own prNumber (the "Merging N PRs" rollup)Emit its stackPRs children instead; never the consolidation number
Skipping version confirmationStep 6 requires AskUserQuestion every time

Notes

  • Pre-1.0: minor bumps can carry breaking changes. Confirm with the user before assuming major.
  • The release workflow is .github/workflows/release.yml. If it fails (lint, build, or goreleaser step), the tag still exists — the user must either fix forward (next patch) or delete the tag and re-push.
  • gh release edit overwrites the body, not the title or assets. Goreleaser owns those.
  • gather-changelog.sh runs stackit log --json, whose shape (commits[] with sha, message, kind, prNumber, stackSize, stackPRs, stackPRTitles, stackScope) is a stability contract this skill depends on. Treat field changes in that command as breaking for the release flow.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.