Release prep
Pre-release orchestrator. Handles the safe, reversible parts of cutting a release (version bump, changelog, validation) and stops short of the irreversible step (tag push) so the user reviews everything before the GitHub release workflow fires.
This skill pairs with .cursor/skills/changelog/SKILL.md, which drafts the actual CHANGELOG entry. release-prep is the wrapper that runs changelog plus the surrounding validation.
Hard constraints
These constraints are absolute and override any other instructions:
- Never create or push the git tag. Print the exact command for the user to run. The user controls the moment of release.
- Never push commits. Stay on the local branch.
npm run checkmust pass before claiming "ready". If it fails, abort with the failure log and stop. Do not "fix-up and retry" — a failing check is real signal.- Only edit
package.json(version bump),package-lock.json(synced version fields), andCHANGELOG.md(via thechangelogskill). No other files.src/plugin.jsoncarries"%VERSION%"and is substituted at build time — never edit it. - If the proposed tag already exists, abort.
- If the working tree is dirty, abort with
git statusoutput. Don't try to be clever about which dirt is safe. - One commit. Title:
chore: prep v<version> release. Contains the version bump (manifest + lockfile) + CHANGELOG draft.
Workflow
Phase 0 — Preconditions
Verify the environment is safe to proceed:
Clean working tree:
git status --shortMust be empty. Abort otherwise.
On main branch (or a release branch — warn but allow):
git branch --show-currentDefault expectation:
main. If on another branch, warn the user — they may be cutting from a release branch intentionally, which is fine, but they should confirm.In sync with origin:
git fetch origin git log HEAD..origin/main --onelineMust be empty (no upstream commits we don't have). Otherwise abort with "Branch is behind origin/main by N commits. Pull first."
Capture the last tag:
git tag --sort=-v:refname | head -1Format
v<SemVer>. If no tags exist, abort with "No prior release tag found. First release must be done manually."
Phase 1 — Resolve target version
If the user passed /release-prep <version>, use it. Otherwise suggest one.
Version arg validation:
- Must be valid semver:
\d+\.\d+\.\d+(no pre-release suffixes — the repo uses none). - Must be strictly greater than the last tag (no regressions).
- Tag
v<version>must not already exist:git tag -l v<version>returns nothing.
Auto-suggestion (when no arg given):
Invoke the changelog skill's Phase 1 + Phase 2 logic to count categories since the last tag, then suggest:
- Any breaking change → major bump (
X.0.0) - Any feat → minor bump (
X.Y.0, reset patch to 0) - Else → patch bump (
X.Y.Z+1)
Print the suggestion and wait for user confirmation in the same turn before proceeding. Example:
Last release: v2.10.0
Since then: 3 feat, 7 fix, 5 chore, 0 breaking
Suggested: v2.11.0 (minor bump)
Reply with the version to proceed, or override with a different one.
Phase 2 — Bump version
The version lives in two files that must stay in lock-step: package.json (the manifest) and package-lock.json (two occurrences — root version + the root project entry under packages[""].version). src/plugin.json uses the literal "%VERSION%" placeholder substituted at build time and must not be edited by this skill.
Edit
package.json: replace the"version"field's value with the target version (novprefix — just"2.11.0").Edit
package-lock.json— bothversionfields:line 3: "version": "2.10.0", → "2.11.0" line 9: "version": "2.10.0", → "2.11.0" (under packages[""])Why a targeted edit and not
npm version/npm install --package-lock-only: those commands tend to pull in unrelated lockfile churn (peer-flag annotations, resolved-URL or integrity-hash drift, transitive range updates) on top of the version bump. A release-prep PR must contain only the bump, so we edit the two specific lines directly.npm version <ver> --no-git-tag-versionwould also create a commit + ignore the lockfile-only path;npm install --package-lock-onlyupdates the lockfile but with the noted churn risk. Targeted edit is the single deterministic option.If you ever observe more than the two version lines drifting in the lockfile from a previous developer's
npm install, that drift is separate from the release and belongs in its ownchore(deps)PR — do not bundle it.Verify nothing else needs the bump:
rg '"version"\s*:\s*"<previous-version>"' --glob '!node_modules' --glob '!.claude' --glob '!**/dist/**'Expected: only
package.jsonandpackage-lock.json(×2). Anything else (e.g., a new manifest a feature PR added) is a signal — surface it to the user before continuing.Run
npm run prettieron the edited files to keep formatting canonical.
Do not stage or commit yet — combine with the CHANGELOG draft into one commit at the end of Phase 3.
Phase 3 — Draft CHANGELOG
Invoke the changelog skill (or replicate its Phase 1-3 inline if the skill is unavailable). Pass the target version explicitly.
When the changelog skill is invoked from release-prep, override its Phase 3 commit step:
- The
changelogskill normally commits withchore: changelog for v<version>. - When called as a sub-step of
release-prep, skip the commit so we can combinepackage.json+CHANGELOG.mdinto one atomic release-prep commit. - Surface this expectation to the user in the run output: "CHANGELOG drafted in-tree but not yet committed; combining with version bump."
Phase 4 — Validate
Run
npm run check— the canonical pre-merge gate:npm run checkThe gate announces each step as it starts and stops at the first failure;
npm run check -- --listprints its composition without running it. If any step fails, abort. Print the failure log verbatim. Do not commit. Do not retry.Run
npm run build— confirm the production bundle still builds:npm run buildFailure here is rare but blocking. Abort if it fails.
Skip plugin signing. Per
docs/developer/RELEASE_PROCESS.md, signing is currently disabled (would requirepolicy_tokenin repo secrets). If signing is re-enabled later, this skill should be updated to runnpm run signhere.
Phase 5 — Commit and hand off
Verify only the expected files changed:
git diff --name-onlyMust be exactly:
CHANGELOG.md package-lock.json package.jsonIf anything else appears, abort. The skill should never modify other files.
Commit (do not push):
git add CHANGELOG.md package.json package-lock.json git commit -m "chore: prep v<version> release"Print the release summary:
Ready to release v<version>. Previous: v<last-version> Included: <N> PRs (<X> added, <Y> fixed, <Z> chore) To cut the release, run: git tag -a v<version> -m "Release v<version>" git push origin v<version> The `release.yml` workflow triggers on `v*` tag push and creates the GitHub release.The release-prep commit must already be merged before tagging, so the tag points at an upstream-known SHA. Then push the tag. Confirm this order with the user if they're unfamiliar.
Reuses
.cursor/skills/changelog/SKILL.md— drafts the changelog entry. Run as a sub-skill.npm run check— single command for the pre-merge gate. Its steps are declared inscripts/check.js.npm run build— production build verification.docs/developer/RELEASE_PROCESS.md— canonical release reference. If this skill diverges from that doc, update the doc.
Integration
- Invoked manually by the maintainer before each release.
- Pairs with
release.ymlGitHub workflow (which fires onv*tag push) — the skill ends where the workflow begins. - Pairs with
/changelog— release-prep calls it as a sub-step.
Abort conditions
The skill must abort cleanly (no partial state, no commits) if any of these are true:
| Condition | Reason |
|---|---|
| Working tree dirty | Cannot reason about what state is being released |
| Branch behind origin | Upstream commits would be missing from the release |
Tag v<version> already exists |
Cannot reuse a tag; double-tagging breaks GitHub releases |
| Version is not strictly > last tag | Regression — semver violation |
npm run check fails |
Test suite or lint catches a real problem |
npm run build fails |
Production bundle is broken |
git diff --name-only shows unexpected paths |
Skill must only touch package.json + package-lock.json + CHANGELOG.md |
When aborting, print the failure reason clearly and (where applicable) the exact log line that triggered the abort. Do not leave partial commits behind.
Context window management
- Phase 0: a handful of
gitinvocations; minimal context. - Phase 1: optional sub-invocation of
changelogPhase 1-2 logic (PR list summary). - Phase 2: small edits to
package.json(1 line) +package-lock.json(2 lines) viaEdit. - Phase 3: delegate to
changelogskill — its own context budget. - Phase 4: stream
npm run checkandnpm run buildoutput; on success, summarize; on failure, surface the relevant log lines. - Phase 5: one commit + report.
Total context per run: under 30k tokens for a typical release. Pull npm run check into the conversation lazily — only surface failing lines, not the whole pass log.
Expected invocation patterns
- Routine release: maintainer runs
/release-prep <version>(or/release-prepfor an auto-suggested version) before each release window. - Sprint review: maintainer runs
/release-prepwithout confirming, captures the category counts + suggested version, and uses that to communicate scope. Then reverts the local changes and reruns once the release window is open. - Post-incident hotfix: maintainer runs
/release-prep <patch-version>for an urgent fix. The skill enforces the same gates (npm run checkmust pass) without bypass.
What this skill does NOT do
- Push commits or tags
- Sign the plugin (disabled per RELEASE_PROCESS.md)
- Run E2E tests (
npm run e2eis not part ofnpm run check; if needed, the user runs it separately) - Coordinate cross-repo releases (e.g.,
grafana-recommender) — out of scope - Edit
docs/sources/user-facing docs — those follow a different update cadence - Bump dependencies —
chore(deps)PRs are independent of release prep
Worked example
> /release-prep
Last release: v2.10.0 (2026-04-15)
Since then: 3 feat, 7 fix, 5 chore, 0 breaking, 0 security
Suggested: v2.11.0 (minor bump)
Reply with the version to proceed.
> 2.11.0
✓ Working tree clean
✓ On main, in sync with origin
✓ No existing v2.11.0 tag
✓ package.json bumped to 2.11.0
✓ package-lock.json synced (2 version fields)
✓ No other files reference the previous version
✓ CHANGELOG drafted (3 added, 7 fixed, 5 chore)
✓ npm run check (132 suites, 2511 tests, all passing)
✓ npm run build (bundle produced)
✓ Committed: chore: prep v2.11.0 release
Ready to release v2.11.0.
To cut the release after the release-prep PR is merged, run:
git tag -a v2.11.0 -m "Release v2.11.0"
git push origin v2.11.0
The `release.yml` workflow triggers on `v*` tag push and creates the GitHub release.