| name | release |
| description | Release a package to NPM. Validates version bump, changelog, creates release branch, monitors CI, verifies publish. |
| argument-hint | <package-name> [base-branch] |
| disable-model-invocation | true |
Release Package
Release a package to NPM. Ensures version bump, changelog, release branch, CI pipeline, and npm publish.
The package is identified by the <package-name> argument, which is the directory name under packages/ (e.g., ocr-onnx, qvac-sdk, tts-onnx). The skill reads packages/<package-name>/package.json to determine the package type and npm scope.
Usage
/release <package-name> [base-branch]
Where:
<package-name> is the directory name under packages/ (e.g., ocr-onnx, tts-onnx)
[base-branch] is optional — the branch to create the release from. Defaults to main. Use this for patches, e.g., /release ocr-onnx release-ocr-onnx-0.6.0
Workflow
Step 1: Validate prerequisites
-
Read packages/$ARGUMENTS/package.json to get the current version and npm package name.
-
Compare version against the latest version published on npm:
npm view <npm-package-name> version
-
If the local version is not higher than the npm version, stop and ask the user to bump the version first.
-
Verify the CHANGELOG heading format exactly matches what the publish-release workflow extracts. The workflow uses this awk regex: ^## \[<version>\] — heading must be bracketed (Keep-a-Changelog style). Soft phrasing like "section for 0.1.0" is not enough; the heading line must literally be ## [0.1.0].
Run this exact check (mirrors the regex in .github/workflows/create-github-release.yml):
version=$(node -p "require('./packages/<package>/package.json').version")
if ! grep -qE "^## \[${version}\]" "packages/<package>/CHANGELOG.md"; then
echo "FAIL: packages/<package>/CHANGELOG.md is missing a '## [${version}]' heading."
echo " A heading like '## ${version}' (no brackets) will NOT match — publish-release will fail."
exit 1
fi
If the check fails, fix the heading or generate the section first:
- For addon packages (native C++): run
/qv-addon-changelog
- For SDK pod packages (TypeScript): run
/qv-sdk-changelog
Step 2: Confirm with user
Display a summary and ask for confirmation before proceeding:
Release summary:
Package: <package>
Version: <version>
Branch: release-<package>-<version>
Target: NPM (latest tag)
Proceed? (y/n)
Step 3: Create release branch
- Ensure we're on the base branch and up to date:
git checkout <base-branch>
git pull origin <base-branch>
Where <base-branch> is determined in Step 1 (main for new releases, release-<package>-<base-version> for patches).
- Create the release branch:
git checkout -b release-<package>-<version>
- Push the release branch to remote (required for workflow dispatch to find the ref):
git push origin release-<package>-<version>
- Trigger the release workflow manually:
gh workflow run "on-merge-<package>.yml" --repo tetherto/qvac --ref release-<package>-<version>
This workflow:
- Runs
release-merge-guard (validates version bump + changelog)
- Builds prebuilds across platforms
- Publishes to NPM with
latest tag
- Creates a GitHub release with tag
<package>-v<version>
Step 4: Monitor CI pipeline
Use /loop to poll the pipeline status every 2 minutes:
/loop 2m Check the CI pipeline status for the release branch release-<package>-<version>. Run: gh run list --branch release-<package>-<version> --limit 5. If all runs completed successfully, report SUCCESS and stop. If any run failed, report the failure details. If still running, report progress.
Step 5: Verify npm publish
Once CI completes successfully:
-
Check that the package was published to npm:
npm view @qvac/<package>@<version> version
(Note: some packages may not have the @qvac/ scope — check package.json for the actual package name)
-
Check that the GitHub release was created:
gh release view <package>-v<version>
-
Report final status to user:
Release complete:
Package: <npm-package-name>@<version>
NPM: published
GitHub Release: <package>-v<version>
Branch: release-<package>-<version>
Step 6: Verify public access
Verify the published package is publicly accessible without authentication by installing it in a clean temp directory with no npm token:
TMPDIR=$(mktemp -d)
cd $TMPDIR
echo "registry=https://registry.npmjs.org/" > .npmrc
npm install <npm-package-name>@<version> --ignore-scripts --no-package-lock
If the install succeeds, the package is publicly accessible. If it fails with a 401/403, the package may still be private — flag this to the user.
Sanity checks on the installed package:
npm pack <npm-package-name>@<version> --pack-destination $TMPDIR
tar tf $TMPDIR/<tarball-filename> | head -20
ls $TMPDIR/node_modules/<npm-package-name>/
node -e "console.log(require('$TMPDIR/node_modules/<npm-package-name>/package.json').version)"
Clean up:
rm -rf $TMPDIR
If any check fails, report the issue to the user before proceeding.
Step 7: Switch back to main
git checkout main
Error handling
- If
release-merge-guard fails: the version or changelog is missing/incorrect. Fix and re-trigger the workflow.
- If prebuild jobs fail: check the failing platform logs with
gh run view <run-id> --log-failed.
- If npm publish fails: check if the version already exists (
npm view), or if NPM_TOKEN is valid.
- If the release branch already exists: ask the user whether to use the existing branch or abort.
Important notes
- This skill creates a local release branch, pushes it to remote, and triggers CI manually.
- The on-merge workflow handles building and publishing — do not manually publish.
- Release branches are never merged back to main automatically. If main needs the changes, a separate PR is required.