release
Skill1 starsChanged 3 months agoMITView on GitHub
Automates the full release workflow: version bump, git tag, GitHub release, and npm publish.
- Installs packages
- Commits and pushes
196 other projects use a version of this.
Adopted by
Most recently taken up by
- zxcvbn-ts/zxcvbntheir own version, first seen Nov 9, 2026
- TechbeeAT/jtxBoardtheir own version, first seen Nov 9, 2026
- ddnexus/pagytheir own version, first seen Nov 9, 2026
- zerx-lab/FluxDowntheir own version, first seen Nov 9, 2026
- orioledb/orioledbtheir own version, first seen Nov 9, 2026
- AmruthPillai/Reactive-Resumetheir own version, first seen Nov 8, 2026
Used with
Projects with it also use create-agent-adapter, changelog-style and 10 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.
MicrosoftSkillcreate-agent-adapter
Technical guide for creating a new Paperclip agent adapter. Use when building a new adapter package, adding support for a new AI coding tool (e.g. a new CLI agent, API-based agent, or custom process), or when modifying the adapter system. Covers the required interfaces, module structure, registration points, and conventions derived from the existing claude-local and codex-local adapters.
Also in 5 projects that use this
GrinvSkillchangelog-style
Turn a commit range into CHANGELOG.md entries following common-changelog style. Use when finishing a PR or cutting a release, to write/fix the [Unreleased] section's entries — moving [Unreleased] under a new dated version heading is a separate, automated step (see the release skill / scripts/sync-version.ts), not this skill's job.
Also in 4 projects that use this
GrinvSkilldocs-consistency-check
Check README/manifest.json/server.json/CHANGELOG.md/AGENTS.md and docs/*.md for drift against the actual registered tools and source. Use after adding, renaming, or removing a tool, or as part of a live-audit pass.
Also in 4 projects that use this
Microsoft AzureSkillcomponent-release
Prepares a verifiable Agent Landing Zone UI release. Use for release branches, VERSION, changelog entries, tags, release notes, and compatibility evidence; never publish without explicit human approval.
Also in 4 projects that use this
Microsoft AzureSkilldocumentation-consistency
Keeps AI Landing Zone contributor, user, and operator documentation aligned with shipped Bicep behavior. Use for parameters, defaults, outputs, topology, deployment flow, operations, or releases.
Also in 8 projects that use this
rivet-devSkilllaunch-graphics
Generate agentOS marketing/launch graphics (launch & social heroes, code-snippet cards) from the committed generators in docs-internal/graphics. Use when asked to make, update, or render an agentOS launch image, social image, "agentOS Apps" graphic, or technical code-snippet image.
Also in 3 projects that use this
- +3 more from Grinv
Other versions196 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 196
- paperclipai/paperclip★ 112ksame name
- modelcontextprotocol/servers★ 92ksame name
- remotion-dev/remotion★ 72ksame name
- traefik/traefik★ 65ksame name
- usememos/memos★ 64ksame name
- tw93/Pake★ 63ksame name
- AmruthPillai/Reactive-Resume★ 45ksame name
- amruthpillai/reactive-resume★ 45ksame name
- reactive-resume/reactive-resume★ 45ksame name
- Yeachan-Heo/oh-my-claude-sisyphus★ 41ksame name
186 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
The file
Show its rules and text
We split this skill into the rules it gives an agent
30 rules. 0 are common, 9 are distinctive, and 21 are very specific to this project.
0
Common
Most of the 186 edited versions we read keep these.
9
Distinctive
This author's own choices. Worth a look.
21
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 54
Run ALL checks before proceeding.
- Very specific: line 194
Orchestrator/MCP mode: when SEQUANT_ORCHESTRATOR is set, every locks checkout action is a no-op (exit 0, no file touched) and the …
- Distinctive: line 201
If version type not provided as argument, ask the user:
- Very specific: line 254
If CHANGELOG.md exists, use the Edit tool to:
- Very specific: line 283
IMPORTANT: Keep plugin.json in sync with package.json.
- Very specific: line 285
Use the Read tool to read plugin/.claude-plugin/plugin.json, then use the Edit tool to update the version field:
- Very specific: line 295
If plugin/.claude-plugin/plugin.json does not exist, skip this step.
- Very specific: line 301
IMPORTANT: Keep docs/internal/what-weve-built.md version references in sync and document new features.
- Very specific: line 392
If CHANGELOG.md doesn't exist or has no [Unreleased] section, fall back to commit-based detection:
- Distinctive: line 402
Ask the user to review what-weve-built.md before proceeding: if there are new features to document.
- Very specific: line 484
Use the Edit tool on SECURITY.md: in the table under ## Supported versions, replace the supported row's minor (e.g.
- Very specific: line 547
Wait for test and canary on the PR head (poll commits/<sha>/check-runs, not gh pr checks --watch, which can report the previous head), then:
- Very specific: line 611
When the user reports done, verify before continuing: npm view sequant dist-tags must show next: <new_version> and an unchanged latest.
- Very specific: line 624
Do NOT attempt to pass OTP codes programmatically or retry npm publish in a loop.
- Very specific: line 637
Without
--soaked: refuse: Do not run npm dist-tag add ... - Very specific: line 645
If npm returns EOTP, hand off to the user with npm dist-tag add sequant@${new_version} latest --otp=<code>.
- Distinctive: line 651
Verify both platforms show the new version:
- Very specific: line 718
When --dry-run is specified:
- Distinctive: line 720
Run all pre-flight checks
- Very specific: line 812
Use pre-release tags for testing: 1.4.0-beta.1
- Distinctive: line 816
Write for users, not developers
- Distinctive: line 822
Keep package size small (< 500KB ideal)
- Very specific: line 823
Use files in package.json or .npmignore
- Very specific: line 824
Test with npm pack before publishing
- Very specific: line 825
Use --tag for pre-releases to avoid becoming "latest"
- Distinctive: line 828
Use pre-release flag for beta/RC versions
- Distinctive: line 830
Use consistent title format
- Very specific: line 833
Run npm audit before releasing
- Very specific: line 834
Don't include sensitive files (check npm pack --dry-run)
- Very specific: line 835
Verify npm whoami shows correct account
“Common” is measured: kept by at least 6 in 10 of the 186 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.
Files
<!-- sequant:local-override -->Local overrides (read this first). Before following any instruction below, check whether
.claude/.local/skills/release/overrides.mdexists. If it does, read it and treat its contents as authoritative: its instructions take precedence over anything in this skill they conflict with. This is the supported way to tailor/releasewithout forking it —overrides.mdlives under.claude/.local/, whichsequant updateandsyncnever overwrite.
Release Skill
Automates the full release workflow: version bump, git tag, GitHub release, and npm publish.
Usage
/release [patch|minor|major] [--prerelease <tag>] [--soaked] [--dry-run]/release- Interactive, asks for version type/release patch- Patch release (1.3.1 → 1.3.2)/release minor- Minor release (1.3.1 → 1.4.0)/release major- Major release (1.3.1 → 2.0.0)/release minor --prerelease beta- Pre-release (1.3.1 → 1.4.0-beta.0)/release --dry-run- Preview without publishing/release --soaked- Promote an already-publishednextversion tolatest(Step 9b); refused without this flag
Pre-flight Checks
Run ALL checks before proceeding. STOP if any fails.
Git Checks
# 1. On main branch
[ "$(git branch --show-current)" = "main" ] || { echo "Not on main"; exit 1; }
# 2. Clean working tree
[ -z "$(git status --porcelain)" ] || { echo "Uncommitted changes"; exit 1; }
# 3. In sync with remote
git fetch origin
[ -z "$(git log HEAD..origin/main)" ] || { echo "Behind origin - pull first"; exit 1; }
# 4. The latest CI `push` run on main is green (red-main rule, below).
# Filter to the CI workflow: without --workflow the newest push run can be
# Scorecard or another workflow, and a red CI would read as green.
ci_run=$(gh run list --branch main --event push --workflow ci.yml --limit 1 \
--json conclusion,url --jq '.[0] | "\(.conclusion) \(.url)"')
case "$ci_run" in success*) ;; *) echo "Red main: latest CI push run is '$ci_run' - revert first (#1093)"; exit 1;; esacRed main — revert first, debug second (#1093). A failing push run on main
is reverted first and debugged second: the next change that lands is the revert
of the commit that broke it, not a forward fix and not an investigation branch,
and the revert PR references the failing run by URL. Do not release off a red
main, and do not release a forward fix "that will also make it green" — revert,
let CI go green, then release. Investigate afterwards on a branch off a green
main. This mirrors the rule in CLAUDE.md § Red main.
Quality Checks
# 4. Tests pass
npm test
# 5. Build works
npm run build
# 6. No security vulnerabilities (high/critical)
npm audit --audit-level=high || echo "Warning: audit issues found"npm Checks
# 7. Logged into npm
npm whoami || { echo "Not logged in - run: npm login"; exit 1; }
# 8. Version doesn't already exist
pkg_name=$(node -p "require('./package.json').name")
current=$(node -p "require('./package.json').version")
npm view "${pkg_name}@${current}" version 2>/dev/null && { echo "Version already published"; exit 1; }
# 9. Preview package contents
npm pack --dry-run 2>&1 | tail -20GitHub Checks
# 10. Authenticated with GitHub
gh auth status || { echo "Not logged in - run: gh auth login"; exit 1; }Plugin Checks
# 11. Plugin manifest is valid (#1135). Name each manifest explicitly:
# `claude plugin validate .` checks only marketplace.json and skips plugin.json.
# Warnings pass (non-strict); errors, or no `claude` on PATH, stop the release.
claude plugin validate plugin/.claude-plugin/plugin.json || { echo "Plugin manifest invalid - fix plugin/.claude-plugin/plugin.json"; exit 1; }
# 12. Marketplace manifest is valid
claude plugin validate .claude-plugin/marketplace.json || { echo "Marketplace manifest invalid - fix .claude-plugin/marketplace.json"; exit 1; }Documentation Checks
Note: Documentation freshness checks that compare against the version being released run after Step 4 (the version bump), not here in pre-flight:
docs/internal/what-weve-built.mdtitle + ASCII version stamps → Step 4.63CHANGELOG.mdfreshness (version entry present) → Step 4.65README.md"What's new in<minor>" heading freshness → Step 4.66They must compare against the new version, which doesn't exist in
package.jsonuntil Step 4 runs. Running them in pre-flight would compare against the previous release — which the docs already reflect — so the gate would never fire for the release in progress (root cause class of #684 / #687).
Release Steps
Step 0: Acquire the Checkout Lock (REQUIRED)
Everything from here on mutates the main checkout: the version bump and its
commit, tag, and push (Steps 4–7), plus the rollback verbs (git reset --soft,
git checkout -- …) if a step fails. git commit/tag/push are not guarded
by pre-tool.sh, but the git reset in rollback is — and, more importantly,
holding the checkout lock (#901) is what stops a session on another issue from
interleaving a git checkout between the bump and the commit and landing the
release commit on the wrong HEAD. So claim the tree now, before the first
interactive approval pause (Step 2's release-notes review), exactly as
/fullsolve does in its Phase 0.3.
/release has no issue of its own, and the checkout lock proves ownership by a
positive integer — the pre-tool.sh guard and sequant locks checkout both
key on --issue=<N>. So /release claims the tree under a reserved sentinel
issue id, 999999999 (a number that will never be a real issue), and uses it
on the acquire and on every release below. locks list and the CLI's
release-refusal render this sentinel as /release (sentinel) so it does not
read as a real issue. A symbolic holder (e.g. --label=release) would avoid
the reserved-number scheme entirely, but it would require changing the
lock-file schema, the CLI, and the numeric-only pre-tool.sh guard — out of
scope for #911 (tracked there as a follow-up).
# Claim the shared working tree before the first mutation / approval pause.
# Skip for --dry-run: it previews only and mutates nothing, so it needs no lock.
npx sequant locks checkout acquire \
--issue=999999999 \
--command="/release" \
--skip-pid-check || true
export SEQUANT_ISSUE=999999999Acquire after the read-only pre-flight checks above, not before: those checks
(git status/fetch, npm test/build, npm whoami, gh auth) mutate
nothing, so a pre-flight abort holds no lock and needs no release. That is why the
pre-flight exit 1 branches carry no release call — you cannot release a lock you
have not taken.
--issue=999999999 is mandatory on release (#906): it is what proves you are
the holder once the acquiring shell's PID is gone (--skip-pid-check). Stale
recovery is age-based only (the 6h SEQUANT_SKILL_LOCK_TTL_MS and the 24h
SEQUANT_MAX_LOCK_AGE_MS ceiling), not same-host dead-PID recovery.
Release contract: release the checkout lock —
npx sequant locks checkout release --issue=999999999 || true — on every
path that exits after this acquire: happy-path completion (Post-Release
Verification), any mid-release error (Error Handling), and the Rollback
Procedures. Releasing an already-released lock is a harmless no-op, so a failure
that both errors and rolls back may run two of these — that is safe.
Orchestrator/MCP mode: when SEQUANT_ORCHESTRATOR is set, every locks
checkout action is a no-op (exit 0, no file touched) and the pre-tool.sh
checkout guard stands down, so the acquire/release calls are safe to run
unconditionally.
Step 1: Determine Version
If version type not provided as argument, ask the user:
| Type | When to Use | Example |
patch | Bug fixes, maintenance, no new features | 1.3.1 → 1.3.2 |
minor | New features, backwards compatible | 1.3.1 → 1.4.0 |
major | Breaking changes | 1.3.1 → 2.0.0 |
prerelease | Alpha/beta/RC versions | 1.3.1 → 1.4.0-beta.0 |
current=$(node -p "require('./package.json').version")
echo "Current version: ${current}"Step 2: Generate Release Notes
Gather commits since last tag:
last_tag=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
if [ -n "$last_tag" ]; then
git log ${last_tag}..HEAD --oneline --no-merges
fiCategorize by conventional commit prefix:
feat:→ Featuresfix:→ Bug Fixesperf:→ Performancedocs:→ Documentationrefactor:→ Refactoringchore:→ MaintenanceBREAKING CHANGE:→ Breaking Changes (for major releases)
Release notes template:
## What's Changed
### Features
- Feature description (#PR)
### Bug Fixes
- Fix description (#PR)
### Breaking Changes
- Description of breaking change and migration path
**Full Changelog**: https://github.com/sequant-io/sequant/compare/v{old}...v{new}Show draft to user for approval before proceeding.
Step 3: Update CHANGELOG.md (if exists)
If CHANGELOG.md exists, use the Edit tool to:
- Replace
## [Unreleased]with## [{new_version}] - {YYYY-MM-DD} - Insert a fresh
## [Unreleased]section above the newly stamped version:
## [Unreleased]
## [1.4.0] - 2026-01-10
### Added
- New feature X
...This ensures the next development cycle has an [Unreleased] section ready for contributors.
Step 4: Bump Version
# For regular releases
npm version {patch|minor|major} --no-git-tag-version
# For pre-releases
npm version prerelease --preid=beta --no-git-tag-versionStep 4.5: Sync Plugin Version
IMPORTANT: Keep plugin.json in sync with package.json.
Use the Read tool to read plugin/.claude-plugin/plugin.json, then use the Edit tool to update the version field:
Read(file_path="plugin/.claude-plugin/plugin.json")
Edit(file_path="plugin/.claude-plugin/plugin.json",
old_string="\"version\": \"<old_version>\"",
new_string="\"version\": \"<new_version>\"")If plugin/.claude-plugin/plugin.json does not exist, skip this step.
Why sync? Sequant is distributed as both npm package and Claude Code plugin. Both must have matching versions to avoid user confusion and ensure compatibility.
Step 4.6: Update what-weve-built.md
IMPORTANT: Keep docs/internal/what-weve-built.md version references in sync and document new features.
Get the new version, then use the Edit tool to update the file:
new_version=$(node -p "require('./package.json').version")Then use the Edit tool (not sed) to update docs/internal/what-weve-built.md:
# Update title version - use Edit tool with replace_all=true
Edit(file_path="docs/internal/what-weve-built.md",
old_string="Sequant v<old_version>",
new_string="Sequant v${new_version}",
replace_all=true)
# Update ASCII art version - use Edit tool with replace_all=true
Edit(file_path="docs/internal/what-weve-built.md",
old_string="SEQUANT v<old_version>",
new_string="SEQUANT v${new_version}",
replace_all=true)
# Update "Current Version" line - use Edit tool
Edit(file_path="docs/internal/what-weve-built.md",
old_string="**Current Version:** <old_version>",
new_string="**Current Version:** ${new_version}")Auto-generate feature bullets from CHANGELOG:
Extract features from the [Unreleased] section of CHANGELOG.md:
# Extract [Unreleased] entries from CHANGELOG.md
if [ -f "CHANGELOG.md" ]; then
# Get content between [Unreleased] and next version header
unreleased_content=$(sed -n '/^## \[Unreleased\]/,/^## \[/p' CHANGELOG.md | head -n -1)
# Extract Added entries (these become what-weve-built features)
added_entries=$(echo "$unreleased_content" | sed -n '/^### Added/,/^### /p' | grep -E '^\s*-' | head -n -1 || true)
if [ -n "$added_entries" ]; then
echo "Features to add to what-weve-built.md:"
echo "$added_entries"
else
echo "No new features in [Unreleased] section"
fi
fiIf CHANGELOG has features, use the Edit tool to update docs/internal/what-weve-built.md:
- Update the "Recent Additions" section header:
Edit(file_path="docs/internal/what-weve-built.md", old_string="### Recent Additions (v<old_version>)", new_string="### Recent Additions (v${new_version})") Add feature bullets under "Recent Additions": Transform CHANGELOG entries to what-weve-built format:
CHANGELOG Format what-weve-built Format - Feature description (#123)- **Feature Name** - Brief description- Multi-line feature (#123)\n - Sub-detail- **Feature Name** - Brief descriptionExample transformation:
# From CHANGELOG: - CHANGELOG update step in /exec skill (#320) - Instructs /exec to add [Unreleased] entries during feature commits # To what-weve-built: - **CHANGELOG Automation** - Automatic CHANGELOG entry requirements in /exec and /qa- Recount the "At a Glance" table on every minor release — incrementing by hand let it drift to a third of the real numbers (it listed 57 test files when there were 343). Run after
npm run build, write the results into the table, and update its "Counted …" line:echo "Slash Commands: $(ls -d templates/skills/*/ | grep -vc _shared)" echo "CLI Commands: $(node dist/bin/cli.js --help | awk '/^Commands:/{f=1;next} f && /^ [a-z]/ && $(1) == "help" {next} f && /^ [a-z]/ {n++} END{print n}')" echo "Core Library Modules: $(git ls-files 'src/lib/*.ts' 'src/lib/**/*.ts' | grep -vE '\.test\.ts$|__tests__|__fixtures__|\.d\.ts$' | wc -l | tr -d ' ')" echo "Test Files: $(git ls-files '*.test.ts' '*.test.tsx' | wc -l | tr -d ' ')" echo "Documentation Files: $(git ls-files 'docs/*.md' 'docs/**/*.md' | grep -v '^docs/internal/' | wc -l | tr -d ' ')" echo "Stack Configurations: $(node --input-type=module -e 'import("./dist/src/lib/stacks.js").then(m=>console.log(Object.keys(m.STACKS).length))')" echo "Lines of TypeScript: $(git ls-files 'src/*.ts' 'src/**/*.ts' 'bin/*.ts' | grep -vE '\.test\.ts$|__tests__|__fixtures__' | xargs cat | wc -l | tr -d ' ')"
Fallback to commit-based detection:
If CHANGELOG.md doesn't exist or has no [Unreleased] section, fall back to commit-based detection:
last_tag=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
if [ -n "$last_tag" ]; then
echo "New features since ${last_tag}:"
git log ${last_tag}..HEAD --oneline --no-merges | grep -E "^[a-f0-9]+ feat" || echo " (none)"
fiAsk the user to review what-weve-built.md before proceeding if there are new features to document.
Step 4.63: Verify what-weve-built.md Version Stamps
IMPORTANT: This runs after Step 4 (version bump) and Step 4.6 (which updates the stamps), so package.json already holds the new version. Running it earlier (in pre-flight) would compare against the previous release — which the file already reflects — so the gate would never fire for the version actually being released (root cause class of #684 / #687, the same ordering bug fixed for the README check in #685).
Warn-only (like the CHANGELOG freshness check): prompt the releaser if the title or ASCII-art version stamp omits/mismatches the version being released.
# Warn if docs/internal/what-weve-built.md version stamps don't match the version being released
if [ -f "docs/internal/what-weve-built.md" ]; then
new_version=$(node -p "require('./package.json').version")
# Check title version
title_version=$(grep -oE "Sequant v[0-9]+\.[0-9]+\.[0-9]+" docs/internal/what-weve-built.md | head -1 | grep -oE "[0-9]+\.[0-9]+\.[0-9]+" || true)
if [ "$title_version" != "$new_version" ]; then
echo "Warning: docs/internal/what-weve-built.md title shows v${title_version}, expected v${new_version}"
fi
# Check ASCII art version
ascii_version=$(grep -E "SEQUANT v[0-9]+\.[0-9]+\.[0-9]+" docs/internal/what-weve-built.md | grep -oE "[0-9]+\.[0-9]+\.[0-9]+" || true)
if [ "$ascii_version" != "$new_version" ]; then
echo "Warning: docs/internal/what-weve-built.md ASCII art shows v${ascii_version}, expected v${new_version}"
fi
fiStep 4.65: Verify CHANGELOG Freshness
IMPORTANT: This runs after Step 4 (version bump) so package.json already holds the new version. Running it earlier (in pre-flight) would compare against the previous release, which the CHANGELOG already lists — so the gate would never fire for the version actually being released (root cause class of #684).
Note (#701): #694 moved the changelog wall out of README.md into CHANGELOG.md and removed the README "What's new" section, so this gate now checks CHANGELOG.md (the source of truth). Grepping README.md for a now-absent # What's new heading made the warn-only gate fire on every release.
Warn-only (like the what-weve-built checks): prompt the releaser to add a CHANGELOG.md entry if the new version is absent.
# Warn if CHANGELOG.md omits the version being released
if [ -f "CHANGELOG.md" ]; then
new_version=$(node -p "require('./package.json').version")
# Match the Keep-a-Changelog heading for this version, e.g. "## [2.4.0] - 2026-05-30"
if ! grep -qF "[${new_version}]" CHANGELOG.md; then
echo "Warning: CHANGELOG.md omits v${new_version} — add a \"## [${new_version}]\" entry before releasing"
fi
fiStep 4.66: Verify README "What's new" Freshness
IMPORTANT: This runs after Step 4 (version bump) so package.json already holds the new version. Running it earlier (in pre-flight) would compare against the previous release — which the README already reflects — so the gate would never fire for the version actually being released (root cause class of #684 / #687, the same ordering bug fixed for the README check in #685).
Note (#702 / #707): This gate owns the hand-written ### What's new in <major>.<minor> positioning section that #702 re-added to README.md — distinct from the changelog wall that #694 moved to CHANGELOG.md (covered by Step 4.65). It keys on the minor line, not a full semver: "What's new in 2.6" is correct for the entire 2.6.x line, so a patch release (2.6.0 → 2.6.1) does NOT fire against a matching heading. During v2.6.0 the heading still read "What's new in 2.5" and shipped stale (fixed manually in 18c2d42); this gate flags that case.
Warn-only (like the CHANGELOG freshness check): prompt the releaser if the README's latest "What's new" heading lags the minor line being released. No README edit is auto-applied — the section's content and positioning are an editorial decision.
# Warn if README.md's latest "What's new" heading lags the minor line being released.
# Keys on the MINOR line (e.g. "2.6"), so a patch release against a matching heading stays silent.
if [ -f "README.md" ]; then
new_version=$(node -p "require('./package.json').version")
new_minor=$(echo "$new_version" | grep -oE '^[0-9]+\.[0-9]+')
# Anchor to a markdown heading (^#+) so a "What's new in X.Y" mention in prose can't be read as
# the section, and take the numerically-highest minor (sort -t. then tail -1) so the result is
# independent of heading order in the file rather than assuming newest-first.
readme_minor=$(grep -oE "^#+[[:space:]]+What's new in [0-9]+\.[0-9]+" README.md \
| grep -oE '[0-9]+\.[0-9]+' | sort -t. -k1,1n -k2,2n | tail -1 || true)
# The -n guard is load-bearing: if the section is ever removed again (as #694 did),
# this gate stays silent rather than firing on every release (the bug #701 fixed for the old gate).
if [ -n "$readme_minor" ] && [ "$readme_minor" != "$new_minor" ]; then
echo "Warning: README.md 'What's new' section is for $readme_minor, expected $new_minor — add/update the heading before releasing"
fi
fiStep 4.67: Update SECURITY.md Supported Versions
IMPORTANT: Runs after Step 4 so package.json holds the new version.
SECURITY.md states that only the latest minor series receives security fixes and lists it in the ## Supported versions table. That row is typed, so every minor release must move it — otherwise the public policy points at the previous line (QA on #980 flagged exactly this rot).
Use the Edit tool on SECURITY.md: in the table under ## Supported versions, replace the supported row's minor (e.g. 2.14.x) with the new minor (e.g. 2.15.x) and the < 2.14 row with < 2.15. On a patch release the minor is unchanged and this step is a no-op.
# Verify: the supported row names the minor line being released.
new_minor=$(node -p "require('./package.json').version" | grep -oE '^[0-9]+\.[0-9]+')
if [ -f SECURITY.md ] && ! grep -qE "^\| ${new_minor}\.x \|" SECURITY.md; then
echo "SECURITY.md supported-versions table does not list ${new_minor}.x — update it before releasing"
fiStep 4.7: Regenerate Marketplace Artifact
IMPORTANT: The marketplace plugin artifact under dist/marketplace/ is bundled into the published tgz via files: ["dist", ...] in package.json — even though dist/ is gitignored. Regenerate it from current sources before packing/publishing so the bundled README always matches; otherwise a stale artifact ships (root cause of #684 — the v2.4.0 tgz carried a "Node.js 20+" README after the floor moved to 22.12).
This is an action step, so it lives in Release Steps (not the read-only pre-flight checks). It MUST run before Step 5 (npm pack) and Step 9 (npm publish).
# Regenerate the marketplace plugin artifact from current sources
npm run prepare:marketplace
# Freshness sanity check: generated README's Node.js floor should match package.json engines.
# Warn-only (regeneration above already fixes the artifact; this is a belt-and-suspenders signal).
gen_readme="dist/marketplace/external_plugins/sequant/README.md"
if [ -f "$gen_readme" ]; then
engines_floor=$(node -p "require('./package.json').engines.node.replace(/[^0-9.]/g,'')") # e.g. 22.13.0
readme_floor=$(grep -oE "Node\.js [0-9]+\.[0-9]+" "$gen_readme" | head -1 | grep -oE "[0-9]+\.[0-9]+" || true)
if [ -n "$readme_floor" ] && [ "${engines_floor%.*}" != "$readme_floor" ]; then
echo "Warning: generated marketplace README shows Node.js ${readme_floor}, package.json engines floor is ${engines_floor}"
fi
fiStep 5: Verify Package Size
# Check what will be published
npm pack --dry-run
# Verify size is reasonable (warn if > 500KB)
size=$(npm pack --dry-run 2>&1 | grep "total files" -A1 | tail -1 || true)
echo "Package size: ${size}"Step 6: Commit and Land Through a PR
main is protected by a ruleset (#1109): it requires the test and canary
checks, strict up-to-date, and rejects every direct push with GH013. The
release commit lands the same way every other commit does — through a PR that
passes the same checks (#1131). Do not push main; do not add a bypass.
new_version=$(node -p "require('./package.json').version")
git switch -c "chore/release-v${new_version}"
git add package.json package-lock.json CHANGELOG.md plugin/.claude-plugin/plugin.json .claude-plugin/marketplace.json docs/internal/what-weve-built.md SECURITY.md README.md
# The shipped MCP config is gitignored-but-tracked; Step 4.7 re-pinned it to this version (#988), so force-add it into the release commit.
git add -f .mcp.json
git commit -m "chore: release v${new_version}"
git push -u origin "chore/release-v${new_version}"
gh pr create --base main --head "chore/release-v${new_version}" \
--title "chore: release v${new_version}" \
--body "Release v${new_version}. Lands through a PR because the main ruleset rejects direct pushes (#1109, #1131)."Wait for test and canary on the PR head (poll commits/<sha>/check-runs,
not gh pr checks --watch, which can report the previous head), then:
gh pr merge --squash --delete-branch --subject "chore: release v${new_version}"
git switch main && git pull --ff-only origin mainStep 7 tags the squash commit on main; tags are outside the branch rule.
Step 7: Create and Push Tag
git tag -a "v${new_version}" -m "Release v${new_version}"
git push origin "v${new_version}"Step 8: Create GitHub Release
# Regular release
gh release create "v${new_version}" \
--title "v${new_version} - {title}" \
--notes "{release_notes}"
# Pre-release (alpha/beta/rc)
gh release create "v${new_version}" \
--title "v${new_version}" \
--notes "{release_notes}" \
--prereleaseFor the title, use a short summary:
v1.4.0 - New Feature Namev1.3.2 - Bug Fix Releasev2.0.0 - Breaking Changes
Step 9: Publish to npm
<!-- BEGIN: next-tag-publish (#1098) -->A regular release publishes to the next dist-tag, never straight to latest (#1098). latest moves only in Step 9b, after the canary job has passed against the published tarball and a manual soak. Downstream projects run npx sequant@latest, so a defect that only shows up in a real install must be found on next first.
# Regular release: `next` first
npm publish --tag next
# Pre-release with its own tag (also never becomes "latest")
npm publish --tag betaIf npm returns EOTP (2FA required):
Non-interactive environments cannot handle the OTP prompt. Hand the user this exact
line. package.json sets "publishConfig": { "tag": "next" } (#1184), so a bare
npm publish also lands on next — before that, a bare publish moved latest
immediately, on both 2.17.0 and 2.18.0. Keep --tag next in the handoff line anyway:
it is explicit and harmless. An explicit --tag still overrides publishConfig
(pre-releases use --tag beta). With npm web auth the --otp part may be omitted:
npm publish --tag next --otp=<code>When the user reports done, verify before continuing: npm view sequant dist-tags
must show next: <new_version> and an unchanged latest. If latest moved, run the
Step 9b soak checklist immediately and keep npm dist-tag add sequant@<previous> latest
ready as the rollback.
A publish that npm accepted can take several minutes to appear: the registry answers
PUT … 202 ("being processed"), and until then npm view sequant@<new_version> returns
404 and dist-tags looks unchanged. That is lag, not failure (seen on 2.18.0). Before
concluding the publish failed, read the newest ~/.npm/_logs/*-debug-0.log: find the
argv (was it a bare npm publish?), the Publishing to … with tag … line, and the
PUT status. Then poll the registry directly, bypassing npm's cache:
curl -s https://registry.npmjs.org/sequant | jq '."dist-tags"'.
Do NOT attempt to pass OTP codes programmatically or retry npm publish in a loop. Hand off to the user and continue with post-release verification once they confirm.
Step 9b: Promote next to latest (requires --soaked)
Print this soak checklist, then stop unless --soaked was passed:
Soak checklist for {new_version} on the next tag
[ ] CI `canary` job green against the published tarball
[ ] npx sequant@next sync --dry-run, run in each local repo of the maintainer's own projects, lists only expected decisions
[ ] npx sequant@next doctor exits 0 in those reposWithout --soaked: refuse. Do not run npm dist-tag add ... latest. Report "Published to next; latest unchanged. Re-run /release --soaked after the soak checklist passes." and finish. Never infer --soaked from context or pass it yourself.
With --soaked:
npm dist-tag add sequant@${new_version} latestIf npm returns EOTP, hand off to the user with npm dist-tag add sequant@${new_version} latest --otp=<code>; same no-retry rule as Step 9.
<!-- END: next-tag-publish (#1098) -->Post-Release Verification
Verify both platforms show the new version:
# npm verification — a 202 publish can take several minutes to appear (see Step 9);
# poll the registry directly rather than trusting one `npm view`
curl -s https://registry.npmjs.org/sequant | jq '."dist-tags"' # next = new version; latest only after Step 9b
# GitHub verification
gh release view "v${new_version}"
# Test install
npx sequant@${new_version} --versionThe release is complete — hand the working tree back (#901). This is the counterpart to the acquire in Step 0.
npx sequant locks checkout release --issue=999999999 || trueOutput Summary
Release v{version} Complete
Version: {old} → {new}
Commit: {hash}
Tag: v{new}
GitHub: https://github.com/sequant-io/sequant/releases/tag/v{new}
npm: https://www.npmjs.com/package/sequant/v/{new}
Plugin: Version synced in plugin/.claude-plugin/plugin.json
Docs: Version synced in docs/internal/what-weve-built.md
Install (npm):
npm install sequant@{new}
npx sequant@{new}
Install (plugin):
/plugin marketplace update sequant-io/sequant # refreshes the marketplace LISTING only
/plugin install sequant # new installs
Update an existing plugin install:
claude plugin update sequant@sequant # then restart Claude Code
Verification:
[x] npm view shows correct version
[x] GitHub release created
[x] Tag pushed
[x] plugin.json version synced
[x] what-weve-built.md version synced
[x] New features documented (if any)
Next steps:
- Announce release (if major/minor)
- Update dependent projects
- Monitor for issues
- Installed plugins do NOT pick up this release automatically: Claude Code
pins each install at its installed version, and `/plugin marketplace
update` refreshes only the marketplace listing. Each existing install
updates only via `claude plugin update sequant@sequant` + restart
(the pre-tool hook nags stale installs once a day — #784)Dry Run Mode
When --dry-run is specified:
- Run all pre-flight checks
- Show what version would be created
- Show draft release notes
- Show
npm pack --dry-runoutput - Do NOT execute any publish commands
Dry Run Summary
Would release: 1.3.1 → 1.4.0
Package contents:
- 176 files
- 195.9 kB packed
Release notes:
[draft notes here]
To execute: /release minorRollback Procedures
Before npm publish
# Undo version bump (if not committed)
git checkout -- package.json package-lock.json
# Undo commit (if not pushed)
git reset --soft HEAD~1
# Delete local tag
git tag -d v{version}
# Rollback aborts the release — hand the working tree back (#901/#906).
# The git reset above is a guarded verb; it runs unrefused only because Step 0
# made this session the checkout holder.
npx sequant locks checkout release --issue=999999999 || trueAfter git push, before npm publish
# Delete remote tag
git push origin :refs/tags/v{version}
# Delete GitHub release
gh release delete v{version} --yes
# Revert commit
git revert HEAD
git push origin main
# Rollback aborts the release — hand the working tree back (#901/#906).
npx sequant locks checkout release --issue=999999999 || trueAfter npm publish
Do NOT use npm unpublish - it breaks dependent projects.
Instead:
- Publish a patch fix:
npm version patch && npm publish - Deprecate bad version:
npm deprecate sequant@{bad} "Use {good} instead"
Error Handling
If a release step fails and you halt without completing, release the checkout lock first (#901/#906) — Step 0 acquired it and every abort after that must hand the working tree back before stopping:
npx sequant locks checkout release --issue=999999999 || true| Error | Cause | Resolution |
| "Not on main" | Wrong branch | git checkout main |
| "Uncommitted changes" | Dirty working tree | git stash or commit |
| "Behind origin" | Remote has new commits | git pull origin main |
| "Tests failed" | Failing tests | Fix tests first |
| "Version exists" | Already published | Choose different version |
| "Not logged in (npm)" | npm auth expired | npm login |
| "Not logged in (gh)" | GitHub auth expired | gh auth login |
| "OTP required" | npm 2FA enabled | Enter code when prompted |
| "Package too large" | Bloated package | Check .npmignore |
Best Practices
Versioning
- Follow Semantic Versioning
- Use pre-release tags for testing:
1.4.0-beta.1 - Major version 0.x.x indicates unstable API
Release Notes
- Write for users, not developers
- Highlight breaking changes prominently
- Include migration guides for major releases
- Link to relevant issues/PRs
npm
- Keep package size small (< 500KB ideal)
- Use
filesin package.json or.npmignore - Test with
npm packbefore publishing - Use
--tagfor pre-releases to avoid becoming "latest"
GitHub
- Use pre-release flag for beta/RC versions
- Attach relevant assets (if any)
- Use consistent title format
Security
- Run
npm auditbefore releasing - Don't include sensitive files (check
npm pack --dry-run) - Verify
npm whoamishows correct account
Examples
Standard Patch Release
/release patchMinor Release with Custom Notes
/release minor
# Review generated notes
# Edit if needed
# Approve to publishBeta Pre-release
/release minor --prerelease betaCreates: 1.4.0-beta.0
Preview Without Publishing
/release minor --dry-runDoes 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
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.

