| name | update-release |
| description | Automates the release process for the ts-oauth2-server project. |
| license | Complete terms in LICENSE |
Release Creator Skill
Purpose
Automates the release process for the ts-oauth2-server project by:
- Bumping version numbers in both
package.json and jsr.json
- Generating changelog entries based on git commits since the last tagged version
- Updating the CHANGELOG.md file with proper formatting
- Maintaining Keep a Changelog format and Semantic Versioning compliance
- Always running the full pre-flight (
pnpm typecheck && pnpm build && pnpm test) before release, aborting if any step fails
- Pausing for user confirmation, then committing, tagging, pushing, and creating the GitHub release (which triggers npm + JSR publish via
.github/workflows/publish.yml)
When to Use This Skill
Use this skill when the user requests:
- Creating a new release
- Bumping the version (patch, minor, major, premajor, preminor, prepatch, prerelease)
- Updating the changelog
- Preparing for a release
- Publishing a new version
Prerequisites
- Git repository with commit history
- Existing tags for version tracking
package.json and jsr.json files present
CHANGELOG.md file exists
Core Functionality
Version Bump Types
Supported version bump types (following semver):
- patch: Bug fixes (1.0.0 → 1.0.1)
- minor: New features (1.0.0 → 1.1.0)
- major: Breaking changes (1.0.0 → 2.0.0)
- prepatch: Pre-release patch (1.0.0 → 1.0.1-rc.0)
- preminor: Pre-release minor (1.0.0 → 1.1.0-rc.0)
- premajor: Pre-release major (1.0.0 → 2.0.0-rc.0)
- prerelease: Increment pre-release (1.0.0-rc.0 → 1.0.0-rc.1)
Prerelease format (REQUIRED): -rc.#. This project's prereleases are always X.Y.Z-rc.N, starting at rc.0 and incrementing the trailing number (rc.0 → rc.1 → …). Never use bare -0 or -next.N suffixes. The new prerelease number must be strictly greater than the previous one for the same X.Y.Z.
Implementation Steps
1. Get Current Version
import { readFileSync } from 'fs';
function getCurrentVersion(): string {
const pkg = JSON.parse(readFileSync('package.json', 'utf-8'));
return pkg.version;
}
2. Calculate New Version
function bumpVersion(current: string, bumpType: string): string {
const parts = current.split('-');
const [major, minor, patch] = parts[0].split('.').map(Number);
const prerelease = parts[1];
switch (bumpType) {
case 'major':
return `${major + 1}.0.0`;
case 'minor':
return `${major}.${minor + 1}.0`;
case 'patch':
return `${major}.${minor}.${patch + 1}`;
case 'premajor':
return `${major + 1}.0.0-rc.0`;
case 'preminor':
return `${major}.${minor + }.0-rc.0`;
:
;
:
(prerelease) {
match = prerelease.();
(!match) {
();
}
;
}
;
:
();
}
}
3. Get Git Commits Since Last Tag
last_tag=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
if [ -z "$last_tag" ]; then
commits=$(git log --pretty=format:"%s" --no-merges)
else
commits=$(git log ${last_tag}..HEAD --pretty=format:"%s" --no-merges)
fi
4. Categorize Commits
Parse commits and categorize them based on conventional commit format:
feat: or feature: → Added section
fix: → Fixed section
BREAKING CHANGE: or !: → Changed section (breaking)
docs: → Skip (or note in documentation)
chore:, refactor:, test: → Skip or group under "Changed"
security: → Security section
5. Generate Changelog Entry
Follow Keep a Changelog format:
## [Version] - YYYY-MM-DD
### Added
- New feature A
- New feature B
### Changed
- **BREAKING**: Changed behavior X
- Updated Y
### Fixed
- Fixed bug in Z
- Resolved issue with W
### Security
- Security fix for vulnerability V
6. Update Files
Update both package.json and jsr.json with the new version, and prepend the new changelog entry to CHANGELOG.md under the ## [Unreleased] section.
7. Run the Full Pre-flight (REQUIRED)
Always run the full pre-flight locally before pausing for confirmation. The publish workflow runs pnpm build && pnpm test in CI, but failures there fire only after the release event — too late to undo a consumed version. Catch them locally first.
Run all three, in order, and abort the release if any fails:
pnpm typecheck
pnpm build
pnpm test
If any step fails, do NOT proceed to commit/tag/release — surface the failure and stop. pnpm build writes to the gitignored dist/, so it does not dirty the release commit; confirm git status --short shows only CHANGELOG.md, package.json, and jsr.json before committing.
8. Confirm With User, Then Commit + Tag + Push + GitHub Release
After the file edits land and the pre-flight is green, stop and ask the user for confirmation before any git mutation. Do NOT proceed automatically — the user may want to review the diff or hand-edit changelog wording first.
Once confirmed, run these in order:
git add CHANGELOG.md package.json jsr.json
git commit -m "chore: release vX.Y.Z"
git tag vX.Y.Z
git push && git push --tags
gh release create vX.Y.Z --title "vX.Y.Z" --notes "<changelog body for this version>"
For the --notes body, pass the section content from CHANGELOG.md for the new version (everything between the ## [X.Y.Z] header and the next ## header), without the version header itself. Use a HEREDOC for multiline notes.
For prereleases (always -rc.#, e.g. 5.0.0-rc.0), add --prerelease to gh release create so the workflow's prerelease branch publishes to npm under the next dist-tag.
After gh release create returns the release URL, share it with the user and note that npm + JSR publishing is now running in CI.
Usage Examples
Example 1: Patch Release
const currentVersion = "4.1.1";
const newVersion = "4.1.2";
Example 2: Minor Release with Features
const currentVersion = "4.1.1";
const newVersion = "4.2.0";
Example 3: Major Release with Breaking Changes
const currentVersion = "4.1.1";
const newVersion = "5.0.0";
Implementation Script
Here's a complete implementation approach:
interface ChangelogEntry {
added: string[];
changed: string[];
deprecated: string[];
removed: string[];
fixed: string[];
security: string[];
}
async function createRelease(bumpType: string): Promise<void> {
const currentVersion = getCurrentVersion();
console.log(`Current version: ${currentVersion}`);
const newVersion = bumpVersion(currentVersion, bumpType);
console.log(`New version: ${newVersion}`);
const lastTag = await getLastTag();
const commits = await getCommitsSinceTag(lastTag);
console.log(`Found ${commits.length} commits since ${lastTag || 'beginning'}`);
changelog = (commits);
changelogEntry = (newVersion, changelog);
.();
(newVersion);
(newVersion);
(changelogEntry);
.();
.();
.();
}
(): {
: = {
: [],
: [],
: [],
: [],
: [],
: [],
};
( commit commits) {
lower = commit.();
(lower.() || lower.()) {
entry..(commit.(, ));
} (lower.() || lower.()) {
entry..(commit.(, ));
} (lower.()) {
entry..(commit.(, ));
} (lower.()) {
entry..(commit.(, ));
} (lower.()) {
entry..(commit.(, ));
} (lower.()) {
entry..(commit.(, ));
}
}
entry;
}
(): {
date = ().().()[];
entry = ;
(changelog.. > ) {
entry += ;
( item changelog.) {
entry += ;
}
entry += ;
}
(changelog.. > ) {
entry += ;
( item changelog.) {
entry += ;
}
entry += ;
}
(changelog.. > ) {
entry += ;
( item changelog.) {
entry += ;
}
entry += ;
}
(changelog.. > ) {
entry += ;
( item changelog.) {
entry += ;
}
entry += ;
}
(changelog.. > ) {
entry += ;
( item changelog.) {
entry += ;
}
entry += ;
}
(changelog.. > ) {
entry += ;
( item changelog.) {
entry += ;
}
entry += ;
}
entry;
}
Best Practices
- Always verify versions match: Ensure package.json and jsr.json versions are in sync before and after bumping
- Review commits carefully: Not all commits should appear in changelog
- Use conventional commits: Encourage the team to use conventional commit format
- Breaking changes: Always highlight breaking changes prominently
- Date format: Use ISO date format (YYYY-MM-DD) for consistency
- Manual review: Always review generated changelog before committing
Error Handling
- No git repository: Warn user and exit gracefully
- Uncommitted changes: Warn user to commit or stash changes first
- Invalid bump type: Show available options and exit
- Version mismatch: If package.json and jsr.json versions don't match, warn user
- No commits: If no commits since last tag, warn user and ask if they want to proceed
Output Format
The skill should provide clear feedback:
Current version: 4.1.1
Bumping version: patch
New version: 4.1.2
Found 5 commits since v4.1.1
Updated package.json, jsr.json, CHANGELOG.md
Pre-flight: typecheck ✓ build ✓ test ✓
Files prepared for v4.1.2.
Confirm to proceed with: commit + tag + push + GitHub release
(GitHub release triggers npm + JSR publish via .github/workflows/publish.yml)
After user confirmation, run the git+gh commands and report the release URL.
Integration with Project
This skill respects the project's:
- Semantic versioning (as stated in CHANGELOG.md)
- Keep a Changelog format (as shown in existing CHANGELOG.md)
- Git workflow and tagging conventions
- Dual package manager support (npm and JSR)
Notes
- The skill prepares the file edits, then pauses for explicit user confirmation before any git mutation
- After confirmation, it runs commit + tag + push +
gh release create in sequence — the GitHub release is what triggers .github/workflows/publish.yml to publish to npm and JSR (a tag push alone does NOT trigger publish)
- The skill is idempotent up to the confirmation gate — re-running before confirming is safe; after the tag is pushed, version bumps require a new patch
- ALWAYS ensure package.json and jsr.json versions are in sync before and after bumping