Agent skill

Release

by nubjs in nubjs/nub

Cut a Nub patch release end-to-end in one invocation. An agent skill from nubjs/nub.

MITAuto-check passedDevelopment

Install Release

skills CLI
$ npx skills add nubjs/nub --skill release -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install nubjs/nub release --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/nubjs/nub.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/release .claude/skills/release && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
release
GitHub stars
4.4k
Token cost
~7.6k tokens
SKILL.md length
3,137 words
Files
1
Skills in repo
31
Repo updated
First seen
Licence
MIT

At a glance

Cut a Nub patch release end-to-end in one invocation. An agent skill from nubjs/nub.

  • Works in 6 steps: Pre-flight: confirm green, pick the… → Version bump → Commit, push, dispatch (the dispatch… → …
  • Tasks that involve OAuth and OpenID Connect
  • SKILL.md covers Step 1 — Pre-flight: confirm…, Step 2 — Version bump, Step 3 — Commit, push,… and Step 4 — Comprehensive release…, plus 4 more sections
  • Calls gh, git and make; reaches github.com; needs HOMEBREW_TAP_TOKEN

What it does

Release is an agent skill from nubjs/nub. Cut a Nub patch release end-to-end in one invocation. Invoke (via the Skill tool) once a release thread's targeted fixes are ALL landed on main and CI-green. Encodes the full runbook: pick the version (patch bump in the 0.0.x/0.1.x pre-release regime), audit @nubjs/types, run make version + make version-check, commit + push to main, write the FACTUAL + NEUTRAL release notes from the full changeset, then dispatch release.yml with publish=true and the notes as its input (that dispatch creates the v<ver tag, runs…

Its SKILL.md is about 7.6k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Development, covering OAuth and OpenID Connect, Changelog and release notes and Runbooks and postmortems. It works with npm and GitHub. The repository describes itself as: The fast all-in-one Node.js toolkit. The licence is MIT.

When your agent uses it

  • Tasks that involve OAuth and OpenID Connect
  • Tasks that involve Changelog and release notes
  • Tasks that involve Runbooks and postmortems

Example prompts

  • “/release”

Requirements

  • Docker

Workflow steps

6 steps, taken from the step headings in SKILL.md.

  1. Pre-flight: confirm green, pick the version, enumerate the changeset
  2. Version bump
  3. Commit, push, dispatch (the dispatch starts CI)
  4. Comprehensive release notes (Opus)
  5. Close the loop on issues + PRs (MANDATORY — always, no matter what)
  6. Post-release verify

What it can do on your machine

Read from SKILL.md and the folder at commit 568e73a. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • gh
    • git
    • make
    • npm
    • brew
    • docker

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Hosts in commands or code, which the agent is likely to contact:

    • github.com

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names these keys or tokens, usually read from environment variables:

    • HOMEBREW_TAP_TOKEN

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Release loads about 7.6k tokens when it runs. Until then it costs about 238 tokens; SKILL.md has 3,137 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~238
When it runs · the whole SKILL.md, loaded when a task matches
~7.6k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from nubjs/nub at commit 568e73a, republished under its MIT licence (© nubjs). 3,137 words, ~7,607 tokens.

Download SKILL.mdSave it as .claude/skills/release/SKILL.md (or your agent's skills folder).
name
release
description
Cut a Nub patch release end-to-end in one invocation. Invoke (via the Skill tool) once a release thread's targeted fixes are ALL landed on `main` and CI-green. Encodes the full runbook: pick the version (patch bump in the 0.0.x/0.1.x pre-release regime), audit `@nubjs/types`, run `make version` + `make version-check`, commit + push to `main`, write the FACTUAL + NEUTRAL release notes from the full changeset, then dispatch release.yml with publish=true and the notes as its input (that dispatch creates the `v<ver>` tag, runs the 8-platform build → glibc and pre-publish native gates → immutable 32-asset DRAFT release carrying the notes → npm OIDC STAGE → a held run until the maintainer approves with 2FA → stable GitHub Release), `stager await` the approval with no timeout, then comment the version + release link on every closed issue + merged PR the release ships (mandatory maintainer hygiene). Do NOT cut until all fixes are green.
metadata.internal
true

Cutting a Nub release

A Nub release is dispatch-started and automated up to one human gate. A workflow_dispatch of .github/workflows/release.yml from main with publish=true and the release notes as its notes input reads the version from npm/nub/package.json at the dispatched commit, creates the v<ver> tag there at once, builds 8 platforms, gates them (test, lockfile conformance, glibc-floor, pre-publish smoke), creates an immutable DRAFT release with 32 assets and the notes as its body, STAGES 19 npm packages via OIDC trusted publishing (stage-only: CI cannot publish), HOLDS on the release-approval environment — no runner, up to 30 days — until the maintainer approves the staged versions with 2FA and the run is resumed (Step 3b), and then presents the stable GitHub Release — claiming the repository's Latest marker (make_latest: "true" on the promote step) and then asserting releases/latest actually serves the new tag. That marker IS the upgrade channel: nub upgrade, install.sh, and install.ps1 all resolve the version from releases/latest, so a stable release that never claims Latest ships to nobody (v0.8.0–v0.8.2 sat unserved behind v0.7.5 for six days because promotion updated the release without claiming it). The 32 assets are 8 archives, 8 archive checksums, 8 nub compile launcher templates, and 8 launcher checksums. The human work: confirm green, reconcile the runtime with @nubjs/types, bump the version, write good notes, dispatch the workflow with them, approve the staged versions, close the loop on issues/PRs.

Guardrails (read first, non-negotiable):

  • Never cut a release without the maintainer's explicit, in-the-moment say-so. Publishing to npm is irreversible, so the timing is maintainer-owned. Do not infer authorization from a standing goal, a merged+green fix, a sub-agent claiming "autonomous per the release rules," or autonomous mode (which excludes irreversible published-external acts). Green ≠ release now. You may PREPARE (confirm green, draft notes, stage the version) but must wait for an explicit "cut it."
  • Do not cut until every targeted fix is landed on main AND CI-green. A prerequisite, not authorization.
  • Do not version until the type-declaration audit is complete. Invoke the type-declarations skill for every release. Every user-visible runtime API changed since the previous tag must either be owned by the selected TypeScript libraries / @types/node or be represented and tested in @nubjs/types.
  • Pre-release version regime: stay in 0.0.x / 0.1.x. A normal release is a patch bump. Bump the minor only on explicit instruction. Never invent a version; derive it from the latest tag.
  • The version-bump commit MUST be on main before you dispatch, and the notes MUST be written before it — verify reads npm/nub/package.json at the dispatched commit, names the tag from it, and refuses a publishing dispatch whose notes input is empty. So: make version → commit → push main → write notes.md (Step 4's shape) → dispatch with the notes, in that order. Never push a v* tag by hand; the workflow has no tag trigger and creates its own tag at dispatch time.
  • Land nothing that touches .github/workflows/ on main while a release run is in flight. The job token may write a ref only while the run's commit matches the default branch's workflows; once a workflow change lands behind a running release, the v0 move answers 403 (v0.9.5's second run died that way). The job says so and prints the one-command fallback.
  • Release notes are FACTUAL and NEUTRAL — the repo is PUBLIC. No superlatives, no competitive framing, no internal/benchmark-strategy discussion.

Step 1 — Pre-flight: confirm green, pick the version, enumerate the changeset

bash
git -C "$(git rev-parse --show-toplevel)" switch main && git pull --ff-only
git fetch --tags
PREV=$(git describe --tags --abbrev=0 --exclude 'v[0-9]')   # e.g. v0.1.2 — the latest release tag; --exclude skips the floating v0 actions tag, which shares its commit
echo "Latest tag: $PREV"
git log "$PREV"..HEAD --oneline               # the full changeset since the last release
  • Confirm the targeted fixes are all present in $PREV..HEAD and each is CI-green on main. If one is red or still converging, STOP and slip it to the next patch.
  • Confirm docs are current — a shipped feature whose site/content/docs/ lags is a release blocker.
  • Invoke the type-declarations skill and complete its mandatory release audit. Reconcile every user-visible runtime change in $PREV..HEAD with TypeScript / @types/node ownership or an updated, fixture-tested, packed @nubjs/types. Missing or unverified declarations are a release blocker.
  • Pick the next version: patch-bump $PREV, dropping the leading v.
  • Keep the git log output — raw material for Steps 4 and 5. For vendor/aube/** changes, note the user-facing effect, not the diff.

Step 2 — Version bump

bash
make version V=<ver>      # sets all 10 npm packages + Cargo.toml + runtime/version.mjs in lockstep
make version-check        # MUST pass: cross-package consistency + @oxc-project/runtime ↔ nub-native oxc pin

make version-check is the same gate CI's verify job runs; a non-zero exit here means the release would fail at CI immediately, so fix it before committing. make version also moves runtime/version.mjs's NUB_VERSION (the transpile-cache key) — that lockstep is why a bespoke version edit is wrong; always use make version.

Step 3 — Commit, push, dispatch (the dispatch starts CI)

The release version-bump commit is a deliberate EXCEPTION to the repo's PR-default flow (AGENTS.md "Default to a PR flow") — it commits DIRECTLY to main. The commit is a version stamp and not a reviewable feature diff, so no PR.

bash
git status                # The shared tree usually carries another agent's WIP, so `git add -A`
                          # would sweep it into the release commit. Path-scope instead:
git commit -m "v<ver>" -- Cargo.lock Cargo.toml \
  crates/nub-core/Cargo.toml \
  crates/nub-native/Cargo.lock crates/nub-native/Cargo.toml \
  crates/nub-launcher/Cargo.lock crates/nub-phantom/Cargo.lock \
  npm/*/package.json runtime/version.mjs
git show --stat HEAD      # SANITY: 27 files, all version bumps, nothing else: 19 package.json
                          # (10 nub + 9 runner), 3 Cargo.toml, 4 Cargo.lock, runtime/version.mjs.
                          # crates/nub-phantom/Cargo.lock is the one that gets missed — its
                          # workspace is excluded from the root, ci.yml checks it `--locked`,
                          # and v0.9.1 shipped without it (main went red, fixed in a follow-up).
                          # `make version` also rewrites site/public/schema/v<major.minor>.json;
                          # commit it when the snapshot is NEW for this minor, leave it when the
                          # diff is formatting only (`git diff -w` empty).

# ONE push, never `git push origin main --tags`. This clone has ~155 local tags against
# ~84 on the remote — v1.x leftovers from the Node fork this repo began as — and `--tags`
# offers every one of them. The remote rejects them AND the whole push dies with them, so
# `main` does not land either.
git push origin main

# Write the release notes FIRST (Step 4 has their shape) — they are an input of the dispatch,
# and verify refuses a publishing dispatch without them. Then THIS is what starts the publish.
# No tag is pushed by hand: release.yml has no tag trigger; verify names the tag from
# npm/nub/package.json at the dispatched commit and creates v<ver> there at dispatch time.
gh workflow run release.yml -R nubjs/nub -f publish=true -f notes="$(cat notes.md)"
gh run list -R nubjs/nub --workflow release.yml --limit 1   # the run id, to watch
# release.yml itself moves the floating v<major> tag (v0) that `uses: nubjs/nub/<name>@v0`
# resolves through, after the promote step. Never push v0 by hand.

Re-running a release that died after its tag was created — a failed build, a run whose held approval expired after 30 days — re-dispatches with publish=true and the same notes, selecting the existing v<ver> tag as the ref. Every other ref is refused by verify, and a tag that points at any commit but the dispatched one is refused there too: release a new version rather than moving a published tag (or, for a version nothing published, disable the Release tags ruleset, delete the draft release with gh release delete v<ver> --cleanup-tag, re-enable it, and dispatch from the fixed main).

Post-merge, fast-forward the shared tree so it tracks origin: git -C <shared-tree> pull --ff-only (the eagerly-pull rule, AGENTS.md "Default to a PR flow" — the shared checkout otherwise drifts behind as PRs land).

The workflow runs, in order: verify (version consistency, notes present, creates the tag), primer, test + conformance + glibc-floor-guard + pre-publish-gate, build (8 platforms), stable-immutable-release (32 assets on a DRAFT release whose body is the notes), publish-npm (19 packages STAGED), await-approval (the run is HELD on the release-approval environment until the maintainer approves), npm-served (confirms npm serves every version and the root package installs and runs), github-release (publishes the draft as the stable release), then the post-publish fan-out — actions-tag, test-install / test-install-musl, docker, bump-homebrew-tap, submit-winget.

Step 3b — Approve the staged versions (the human gate; maintainer only)

publish-npm does not publish. It runs npm stage publish for every package, because the trusted publisher on each @nubjs package is stage-only, and then ends. The run moves to await-approval, a job on the release-approval environment whose required reviewer is the maintainer: GitHub holds the run there with no runner, for up to 30 days. Nothing is installable, and no GitHub Release is visible, until the maintainer approves the staged versions with 2FA. That approval is a proof of presence no token can supply, which is the whole point (the unauthorized v0.9.4 of 2026-09-21 published 18 packages from a stolen push credential; under staging it would have filled a queue).

The approval goes through stager (~/Documents/projects/stager, on PATH). An agent RUNS it; the maintainer supplies the one click on the npm page it prints:

bash
stager approve '@nubjs/*@<ver>'     # prints an npmjs.com URL; the maintainer approves there (tick "skip 2FA for
                                    # 5 minutes" so the other 18 pass without another URL); then stager approves
                                    # the run's pending deployment, which resumes it
stager await '@nubjs/*@<ver>'       # blocks, with no timeout, until npm serves every version — park on this
stager reject '@nubjs/*@<ver>'      # drops the staged versions and fails the held run
stager                              # the interactive picker: every staged version, with its commit and run

stager approves every staged version of a package oldest-first, so two versions staged at once end with latest on the higher one. A version still validating (npm's malware review, up to an hour) is approved once the review finishes; stager waits for it. Without stager: npm stage approve <id> per package (or the Staged Packages tab on npmjs.com), then approve the pending deployment on the run's page. A deployment approved before the npm approval fails npm-served at its registry check; re-run that job after approving.

Park on stager await (a background shell) rather than on the run. It returns when the versions are live; the run's remaining jobs then take a few minutes. The release is not done until stable-immutable-release, publish-npm, github-release and actions-tag are green.

The other distribution channels ride the same run — no manual step, but they are not free

npm is not the only thing a release publishes. Two jobs push OUTSIDE this repo, and neither needs a manual action:

  • bump-homebrew-tap regenerates Formula/nub.rb with .github/scripts/gen-homebrew-formula.sh and pushes it to nubjs/homebrew-tap. It reads the release's own .sha256 sidecars, so it needs github-release to have finished. It is gated on the HOMEBREW_TAP_TOKEN secret and SKIPS WITH A WARNING if that secret is ever absent — a skip is a silent stale tap, so treat the warning as a failure.
  • submit-winget opens a PR against microsoft/winget-pkgs. Gated on WINGET_PAT, which is currently unset, so this job no-ops today.

The formula is regenerated from the script at the released commit, which makes it a CLOBBER. Any hand-edit to the tap is overwritten by the next release. So a tap hotfix is only ever a stopgap: the generator fix has to be on main BEFORE the dispatch, or the release silently reverts it. This is how #676 shipped — the archive layout changed, the generator was not updated with it, and nothing read the formula before it reached users. bump-homebrew-tap now installs the formula from a throwaway local tap on macOS before pushing it, so a formula that cannot install fails the job and leaves the tap on the previous working version.

Step 4 — Comprehensive release notes (Opus)

The notes are the notes input of the dispatch (Step 3): stable-immutable-release creates the draft with them as its body and github-release promotes it unchanged, so they are written BEFORE the dispatch, into notes.md. Hand-written, scannable, factual; never a raw auto-list. Drive this on Opus.

Build the notes from the full git log "$PREV"..HEAD changeset (Step 1), not just the headline fixes — every user-affecting change ships.

Leave out what the maintainer is holding back. Read internal/release-holds.md before drafting. A change listed there ships in the binary but is not announced: it gets no line in the curated notes, its PR comes out of the generated ## What's Changed list, the blog post (Step 4b) does not mention it, and its docs page keeps unpublished: true. Only the maintainer lifts a hold.

Notes must be SCANNABLE, not paragraph-dense. A reader skims headings, tables, and the heads-up callout and gets the whole release at a glance — they should never have to read a run-on paragraph to find what changed. The cross-project prose/tone guide for all public-facing copy — including the release-notes shape — is the prose-writing skill's guide. The concrete rules:

  • One-line intro stating what the release is about (the dominant theme).
  • Themed ## sections, not generic buckets. Group by what the changes touch — e.g. "Lockfile compatibility" / "Performance" / "Runtime fixes" / "Documentation" / "Testing & internals" — not by Fixes/Compatibility/Internal abstractions. Each major change gets a short titled blurb or a table row, never a multi-sentence paragraph.
  • A table for a batch of independent fixes. When several small fixes share a theme (a run of lockfile fixes), put them in a table — | Area | What changed | Commit | — tables read far faster than a bullet wall.
  • A callout for heads-up / migration items. Anything a user should know before upgrading (a cache-schema re-warm, a behavior change) goes in a GitHub-flavored alert: > [!IMPORTANT] (or > [!NOTE]), not buried in a bullet.
  • Per-item links. Every fix/change links to its commit ([abc1234](https://github.com/nubjs/nub/commit/<full-sha>)) and/or PR ([#17](https://github.com/nubjs/nub/pull/17)). Issue refs link too ([#16](https://github.com/nubjs/nub/issues/16)).
  • An auto-generated ## What's Changed section at the BOTTOM (MANDATORY) — this is what makes "lists every change" literally true. GitHub's PR-level breakdown (every merged PR + author + New Contributors) plus the **Full Changelog**: <PREV>...v<ver> compare link, from gh api …/releases/generate-notes (command below). Append it verbatim under a --- separator below the curated narrative — the curated themes stay on top, the exhaustive PR list goes underneath.
  • Tone: factual + neutral. Readability ≠ hype. Each line states what changed. No superlatives, no competitive framing, no editorializing. (Same bar as commit messages — AGENTS.md.) Visual interest comes from structure (sections, tables, callouts), never from marketing language.

Template (adapt the section names to the actual changeset):

markdown
<One-line intro: what this release is about.>

> [!IMPORTANT]
> **<Heads-up title>.** <The one thing to know before upgrading. Omit the callout if there's nothing.>

## <Theme A, e.g. Lockfile compatibility>

<Optional one-line lead.>

| Area | What changed | Commit |
| --- | --- | --- |
| <area> | <what changed, one clause> | [`<sha7>`](https://github.com/nubjs/nub/commit/<full-sha>) |

## <Theme B, e.g. Performance>

<Short blurb with the PR link inline.> ([#17](https://github.com/nubjs/nub/pull/17))

## Testing & internals

- <Bullet> ([`<sha7>`](https://github.com/nubjs/nub/commit/<full-sha>)).

---

## What's Changed

<!-- appended verbatim from `gh api …/releases/generate-notes` — the PR list, New Contributors, and Full Changelog link -->
* <PR title> by @<author> in https://github.com/nubjs/nub/pull/<n>

**Full Changelog**: https://github.com/nubjs/nub/compare/<PREV>...v<ver>

Generate the bottom ## What's Changed breakdown mechanically so every merged PR is listed (the tag does not exist yet, so name the commit):

bash
# PR-level list + New Contributors + Full Changelog compare link — append verbatim below the curated narrative
gh api repos/nubjs/nub/releases/generate-notes \
  -f tag_name=v<ver> -f target_commitish=main -f previous_tag_name=$PREV --jq '.body'

Append that block under a --- separator below the curated sections, save notes.md, and dispatch with it. The curated narrative stays on top; this exhaustive PR list goes underneath.

To correct the notes after the dispatch (a re-dispatch of the same tag rewrites the draft's body from its own notes input; after the promotion, edit in place):

bash
gh release edit v<ver> --notes-file notes.md
gh release view v<ver> --repo nubjs/nub --json body -q .body   # verify it rendered

The v0.1.4 and v0.1.3 release bodies are the reference exemplars of this structure.

Show full SKILL.md (1,131 more words)Show less

Step 4b — Publish the notes as a blog post (MANDATORY — every release)

Every release also ships as a blog post under site/content/blog/. This is a standard release step, done on every version — the same content/presentation-to-main exception as docs (commit directly to main, no PR). Before writing, invoke the prose-writing skill and follow its guide (blog copy: routine patch notes stay factual, neutral, unsigned, scannable — no hype, no personality; a milestone version gets a fuller treatment but the same neutral bar).

  • File: site/content/blog/nub-<major>-<minor>-<patch>.mdx (e.g. nub-0-2-10.mdx) — the filename is the URL slug; fumadocs auto-globs content/blog/*.mdx, so no index/meta wiring is needed.
  • Frontmatter (schema from source.config.ts, all four required): title: "Nub <ver>" (add a : <theme> subtitle only for a milestone), description: a plain sentence with no inline code/backticks (the field renders raw), author: The Nub Team, date: <YYYY-MM-DD> back-dated to the release's publishedAt so the timeline stays chronological.
  • Body: a short lede, then the release's themed sections adapted to blog prose — not a raw changelog dump. Carry over the callouts and per-theme tables. Close with The [full release notes](https://github.com/nubjs/nub/releases/tag/v<ver>) list every change in this release.
  • Structure a feature-carrying release around its features: one top-level ## per major new feature, then ## Breaking changes, then ## Bug fixes. A batch of independent fixes goes in a table whose FIRST column is the PR link — .blog-prose td:not(:last-child) is width:1% + nowrap by design, so a prose column anywhere but last blows the table past the 720px article column.
  • End every post with the get-started block — a final ## Get started heading followed by <GetStarted />, which renders the install tabs plus the pointer at the agent adoption prompt. Every existing post carries it; a new one without it is the odd one out.
  • Catch up a cold reader with <NubIntro /> near the top when the post leads with feature news rather than an introduction. Both components live in site/src/components/ and are registered globally in site/mdx-components.tsx; their copy is maintainer-authored, so edit the component, never a single .mdx.
  • Scale to the release: a small patch gets a short post; a milestone opens with the thing working.
  • Nothing held back appears in the post — the holds in internal/release-holds.md (Step 4) apply here too.

Exemplars: site/content/blog/nub-0-7-0.mdx (feature-carrying, full structure), nub-0-2-0.mdx (milestone), nub-0-2-5.mdx (small patch).

Step 5 — Close the loop on issues + PRs (MANDATORY — always, no matter what)

Comment a brief factual note carrying the version and a link to the release on EVERY closed issue and EVERY merged PR that shipped in this release — not just the headline fixes. This is mandatory maintainer hygiene (AGENTS.md "Git & GitHub maintainer hygiene"); do it on every release without exception. Users see "fixed" the moment an issue closes, but the fix is not on the released binary until the release publishes — this comment closes that credibility gap and gives the reporter a link to the exact release.

The release URL is https://github.com/nubjs/nub/releases/tag/v<ver>. Every comment includes both the version and that link, e.g. Shipped in v<ver>: <release URL>.

Enumerate the targets MECHANICALLY — never a hand-typed list. A hand-enumerated pass silently misses any issue still open at cut time or closed AFTER the cut (this happened on v0.3.0). Drive the set from the union of three queries:

bash
# 1. Every issue a shipped PR auto-closes (closingIssuesReferences) + any Closes/Fixes/Resolves #N in a PR body:
gh pr list --repo nubjs/nub --state merged --search "merged:<PREV-date>..<cut-date>" \
  --json number,body,closingIssuesReferences --limit 200 \
  --jq '.[] | {pr:.number, closes:[.closingIssuesReferences[].number], refs:([.body|scan("(?i)(?:clos|fix|resolv)\\w*\\s+#(\\d+)")]|flatten)}'
# 2. Every issue closed in the release window (catches issues closed without a linked PR):
gh issue list --repo nubjs/nub --state closed --search "closed:<PREV-date>..<cut-date+1>" \
  --json number,title,stateReason --limit 200

For each issue/PR in the union, check whether it ALREADY carries the comment before posting (gh issue view <n> --repo nubjs/nub --json comments --jq '[.comments[].body|select(test("Shipped in v<ver>"))]|length') — skip a NOT_PLANNED issue with no shipped fix. Re-run this pass for any issue closed AFTER the cut — a late-closing issue does not appear in the first sweep.

Then comment (extremely concise — the version and release link, no recap of what was done or how; add a brief thanks when the reporter or PR author is external):

bash
REL="https://github.com/nubjs/nub/releases/tag/v<ver>"
gh issue comment <n> --body "Fixed in v<ver> (now published): $REL"
gh pr comment <n>    --body "Shipped in v<ver>: $REL — thanks for the contribution."

Hit every issue and PR the mechanical union above surfaces — not just the headline fixes. This is non-optional; do not skip an issue because it was "minor," and do not fall back to the release thread's targeted-fix list as the source of truth (it under-counts). Do not comment on issues unrelated to the release.

Step 6 — Post-release verify

Confirm the automated publish actually landed:

bash
gh api repos/nubjs/nub/releases/latest --jq .tag_name   # MUST print v<ver> — this endpoint IS the
                                             # upgrade channel (nub upgrade + both installers);
                                             # anything else means users are served an old release
npm view @nubjs/nub@<ver> version            # the root package is on the registry
npm view @nubjs/nub@<ver> dist.tarball        # sanity: published artifact exists
gh release view v<ver> --json assets --jq '.assets[].name' | sort
# expect these exact 32 assets:
# nub-darwin-arm64.tar.gz
# nub-darwin-arm64.tar.gz.sha256
# nub-darwin-x64.tar.gz
# nub-darwin-x64.tar.gz.sha256
# nub-linux-arm64.tar.gz
# nub-linux-arm64.tar.gz.sha256
# nub-linux-arm64-musl.tar.gz
# nub-linux-arm64-musl.tar.gz.sha256
# nub-linux-x64.tar.gz
# nub-linux-x64.tar.gz.sha256
# nub-linux-x64-musl.tar.gz
# nub-linux-x64-musl.tar.gz.sha256
# nub-win32-arm64.zip
# nub-win32-arm64.zip.sha256
# nub-win32-x64.zip
# nub-win32-x64.zip.sha256
# nub-launcher-darwin-arm64
# nub-launcher-darwin-arm64.sha256
# nub-launcher-darwin-x64
# nub-launcher-darwin-x64.sha256
# nub-launcher-linux-arm64
# nub-launcher-linux-arm64.sha256
# nub-launcher-linux-arm64-musl
# nub-launcher-linux-arm64-musl.sha256
# nub-launcher-linux-x64
# nub-launcher-linux-x64.sha256
# nub-launcher-linux-x64-musl
# nub-launcher-linux-x64-musl.sha256
# nub-launcher-win32-arm64.exe
# nub-launcher-win32-arm64.exe.sha256
# nub-launcher-win32-x64.exe
# nub-launcher-win32-x64.exe.sha256

A complete release has: the 10 npm packages published (@nubjs/nub, @nubjs/nub-<platform> ×8, @nubjs/types), the stable GitHub Release present and marked Latest, and all 32 assets attached. CI's stable-immutable-release job asserts the 32 assets before npm can publish, github-release promotes that same release after npm succeeds, and test-install smokes the published package. This step confirms that the workflow reached green.

The 8 nub-launcher-* assets are what nub compile --platform <foreign> fetches to cross-compile, so a release missing one silently disables cross-compiling to that platform for everyone on that version.

If CI failed partway: Re-run the failed job from the Actions UI. The stable-immutable-release job rebuilds the deterministic archives and re-uploads missing assets before npm starts; publish-npm skips packages already published or already staged during a partial attempt; a run whose approval hold expired (30 days) is re-dispatched selecting its tag (Step 3); github-release only promotes the asset-complete prerelease. Do not re-cut a version for a failed upload or partial npm publish.

Then confirm the Homebrew channel actually moved — the tap lives in another repo, so a green release run here is not evidence that it did:

bash
gh api repos/nubjs/homebrew-tap/contents/Formula/nub.rb --jq '.content' | base64 -d | grep -E 'version|bin\.install'
# expect version "<ver>" and the bin.install lines matching the current archive layout

brew update && brew install nubjs/tap/nub && nub --version && nubx --help | head -1
# or, without touching your own machine:
docker run --rm homebrew/brew brew install nubjs/tap/nub

A complete release has the 10 npm packages published (@nubjs/nub, @nubjs/nub-<platform> ×8, @nubjs/types), the GitHub Release present and marked Latest, all 32 assets attached, and the tap formula bumped to <ver> and installable.

If CI failed partway: publish-npm and github-release are split + idempotent on purpose — re-run the failed job from the Actions UI (npm publish skips already-published packages; the release job re-uploads only missing assets). Never re-cut a version for a flaky asset upload. bump-homebrew-tap is re-runnable too, and failing it is the safe outcome: the tap keeps serving the previous version rather than a broken formula, so fix the generator on main and re-run the job — never hand-edit the tap as the fix, since the next release regenerates it.


Quick reference

StepCommand
Changesetgit log $(git describe --tags --abbrev=0)..HEAD --oneline
TypesInvoke type-declarations; reconcile every runtime API in the changeset before versioning
Bumpmake version V=<ver> → make version-check
Noteswrite notes.md (curated sections + generated ## What's Changed) BEFORE the dispatch
Cutgit commit -m "v<ver>" -- <version files> → git push origin main (never --tags) → gh workflow run release.yml -R nubjs/nub -f publish=true -f notes="$(cat notes.md)"
Approvestager approve '@nubjs/*@<ver>' (maintainer clicks the npm URL) → stager await '@nubjs/*@<ver>'
Blogsite/content/blog/nub-<x>-<y>-<z>.mdx — back-dated to publishedAt (direct to main)
Tapautomatic via bump-homebrew-tap; verify with gh api repos/nubjs/homebrew-tap/contents/Formula/nub.rb --jq .content | base64 -d | head -5
Loopgh issue comment <n> --body "Fixed in v<ver>: <release URL>" (every closed issue + merged PR)
Verifygh api repos/nubjs/nub/releases/latest --jq .tag_name (= v<ver>) · npm view @nubjs/nub@<ver> version · gh release view v<ver> --json assets

Invoked via the Skill tool once a release thread's targeted fixes are all landed on main and CI-green.

© nubjs, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .claude/skills/release of nubjs/nub.

Open the folder on GitHubat commit 568e73a

Compare with similar skills

Release next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Release compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Release this skillnubjs/nub4.4k—~7.6kAutomated safety check: PassMIT
Automate npm Releasejd-solanki/slidev-theme-dracula161—~626Automated safety check: PassNone
Mole CLI Release Flowtw93/Mole69k—~2.5kAutomated safety check: PassGPL-3.0
Hunk Release Workflowmodem-dev/hunk9.5k—~3.8kAutomated safety check: PassMIT
Remotion Bits Releaseav/remotion-bits486—~1.2kAutomated safety check: PassNone
ZCF Release AutomationUfoMiao/zcf6.1k—~3.4kAutomated safety check: PassMIT

Similar skills

  • Automate npm Release

    jd-solanki/slidev-theme-dracula

    Automate npm package publishing via GitHub Actions for single-package repos and independent monorepo packages, including bumpp version tags, GitHub release notes, trusted publishing, provenance, and…

    161 GitHub stars~626 tokensUpdated 3 mo ago
    DevelopmentAuto-check passed
  • Runbook for assessing and executing a Mole CLI release: distribution channels, pre-flight checks, capital-V tags, build artifacts and the handoff to curated release notes.

    69k GitHub stars~2.5k tokensUpdated today
    DevelopmentAuto-check passed
  • Hunk Release Workflow

    modem-dev/hunk

    Maintainer workflow for preparing, publishing, verifying and curating Hunk releases, with confirmation gates before tags, publishes and public edits.

    9.5k GitHub stars~3.8k tokensUpdated yesterday
    DevelopmentAuto-check passed
  • Remotion Bits Release

    av/remotion-bits

    Runs the full release of the remotion-bits package: version bump, changelog, registry build, release commit, GitHub release, docs deploy and npm publish.

    486 GitHub stars~1.2k tokensUpdated 21 days ago
    DevelopmentAuto-check passed
  • Automates a version release with changesets: analyzes code changes, writes a bilingual CHANGELOG, bumps the version and commits through a release branch and pull request.

    6.1k GitHub stars~3.4k tokensUpdated 1 mo ago
    DevelopmentAuto-check passed
  • Walks through releasing the Cline CLI package to npm: release notes, version bump, matching git tag, and either the GitHub workflow or a local publish.

    70k GitHub stars~3.4k tokensUpdated today
    DevelopmentAuto-check: warnings

More from nubjs/nub

All 31 skills in this repo
  • Cpu Reduction

    nubjs/nub

    Diagnose and clear CPU, memory, and disk contention on the maintainer's dev host.

    4.4k GitHub stars~2.8k tokensUpdated today
    Auto-check passed
  • Reclaim disk on the maintainer's Mac when the volume is full or filling — ENOSPC, "no space left on device", a failed build or agent harness, or a routine sweep of Rust build residue.

    4.4k GitHub stars~2.4k tokensUpdated today
    Auto-check passed
  • Nub Charts

    nubjs/nub

    Build a performance chart for nubjs.com — the SVG bar figures in blog posts, docs pages and social posts (a runtime augmentation against plain node, an install or dispatch comparison, a cross-tool…

    4.4k GitHub stars~4.6k tokensUpdated today
    Auto-check passed
  • Audit Thread

    nubjs/nub

    A skill your agent uses when running a compatibility/parity AUDIT — enumerating where nub diverges from a reference it claims parity with (pnpm CLI grammar, a lockfile format, a Node behavior, a…

    4.4k GitHub stars~1.8k tokensUpdated today
    Auto-check passed
  • Linux Vm Test

    nubjs/nub

    Run ad-hoc Nub tests and debugging probes on real local Linux guests.

    4.4k GitHub stars~986 tokensUpdated today
    Auto-check passed
  • Performance-trace Nub package-manager installs using the existing phase timings, structured diagnostics, and sampling-profiler workflow.

    4.4k GitHub stars~1.3k tokensUpdated today
    Auto-check passed

Works with

Questions about Release

What does Release do?

Cut a Nub patch release end-to-end in one invocation. An agent skill from nubjs/nub. Release is an agent skill from nubjs/nub. Cut a Nub patch release end-to-end in one invocation.

When should I use Release?

Release fits situations like: tasks that involve OAuth and OpenID Connect; tasks that involve Changelog and release notes; tasks that involve Runbooks and postmortems.

How do I install Release in Claude Code?

Run `npx skills add nubjs/nub --skill release -a claude-code`. Or copy the skill folder (.claude/skills/release in nubjs/nub) into .claude/skills/release in your project. Claude Code loads it when a task matches its description.

How do I install Release in Codex?

Run `npx skills add nubjs/nub --skill release -a codex`. Or copy the skill folder (.claude/skills/release in nubjs/nub) into .agents/skills/release in your project. Codex loads it when a task matches its description.

Can I use Release in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add nubjs/nub --skill release -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/release, .gemini/skills/release, .github/skills/release and .opencode/skills/release in your project.

What does Release need to run?

Going by SKILL.md and its folder, Release needs the command-line tools its instructions call (gh, git, make, npm, brew and docker) and credentials named HOMEBREW_TAP_TOKEN. Our summary lists: Docker.

Does Release access the network?

SKILL.md names 1 domain. In commands or code: github.com; the agent is likely to contact it when it follows the instructions. This is read from the text; nothing was executed.

Is Release safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Release use?

Release is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Release use?

About 7.6k tokens (SKILL.md is roughly 30k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Release?

Skills that share tags, products or a category with Release: Automate npm Release (jd-solanki/slidev-theme-dracula, 161 stars), Mole CLI Release Flow (tw93/Mole, 69k stars), Hunk Release Workflow (modem-dev/hunk, 9.5k stars) and Remotion Bits Release (av/remotion-bits, 486 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Release?

nubjs (a GitHub organization) maintains it in nubjs/nub, which has 4,370 GitHub stars. The repository holds 31 skills in this directory. The repository was last updated on October 7, 2026.

Source: nubjs/nub on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.