Skip to content
Back to skills

Release

ASecurity

Release a shihwesley plugin with semver versioning, conventional commit parsing, changelog generation, and GitHub Release creation. Use when user says /release, wants to tag a new version, publish a release, bump version, or create a changelog. Supports patch/minor/major and --dry-run.

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 26, 2026
developmentpythongoswiftbashnodegitapi

Works with

  • cli
  • api

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add shihwesley/shihwesley-plugins --skill release --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Release?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Release
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/shihwesley-release/badge)](https://www.skillsdirectory.com/skills/shihwesley-release)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: release
description: "Release a shihwesley plugin with semver versioning, conventional commit parsing, changelog generation, and GitHub Release creation. Use when user says /release, wants to tag a new version, publish a release, bump version, or create a changelog. Supports patch/minor/major and --dry-run."
metadata:
  author: shihwesley
  version: 1.0.0
---

# Release: $ARGUMENTS

Full release pipeline for shihwesley plugins. Follows semver, conventional commits, and GitHub Release best practices.

## Usage

```
/release [patch|minor|major] [--dry-run]
```

- **patch** (default): bug fixes → `v2.1.0` → `v2.1.1`
- **minor**: new features, backward compatible → `v2.1.0` → `v2.2.0`
- **major**: breaking changes → `v2.1.0` → `v3.0.0`
- **--dry-run**: run all checks, show what would happen, push nothing

## Conventional Commits

Version auto-detection uses conventional commit prefixes, not keyword guessing:

| Prefix | Bump | Example |
|--------|------|---------|
| `fix:` `fix(scope):` | patch | `fix: handle empty query in BM25 search` |
| `feat:` `feat(scope):` | minor | `feat: add Phase 0 knowledge store search` |
| `BREAKING CHANGE:` in body, or `!` after type | major | `feat!: rewrite search API` |
| `docs:` `chore:` `refactor:` `test:` `ci:` | no bump | maintenance, skip for version calc |

If commits don't follow conventional format, fall back to asking the user.

---

## Step 1: Identify the plugin

```bash
PLUGIN_DIR=$(pwd)
REMOTE=$(git remote get-url origin 2>/dev/null)
```

Match `$REMOTE` against `repository` fields in:
`/Users/quartershots/Source/shihwesley-plugins/.claude-plugin/marketplace.json`

Extract: `PLUGIN_NAME`, `CURRENT_VERSION`, `REPO_URL`.

If no match → error: "Not in a recognized shihwesley plugin repo. Run from a plugin directory."

---

## Step 2: Validate release readiness

All checks must pass:

```bash
# 1. On main branch
BRANCH=$(git branch --show-current)
[ "$BRANCH" = "main" ] || error "Not on main branch."

# 2. Clean working tree (no uncommitted changes)
[ -z "$(git status --porcelain)" ] || error "Uncommitted changes. Commit or stash first."

# 3. Synced with remote
git fetch origin main --quiet
LOCAL=$(git rev-parse HEAD)
REMOTE_HEAD=$(git rev-parse origin/main)
[ "$LOCAL" = "$REMOTE_HEAD" ] || error "Local differs from origin/main. Pull or push first."

# 4. Commits exist since last tag
LAST_TAG=$(git tag --sort=-v:refname | head -1)
COMMIT_COUNT=$(git rev-list "$LAST_TAG"..HEAD --count)
[ "$COMMIT_COUNT" -gt 0 ] || error "No commits since $LAST_TAG. Nothing to release."

# 5. HEAD not already tagged
TAGGED=$(git tag --points-at HEAD)
[ -z "$TAGGED" ] || error "HEAD is already tagged $TAGGED."
```

---

## Step 3: Run tests

Detect project type and run the appropriate test suite:

```bash
# Python (pyproject.toml or setup.py)
if [ -f pyproject.toml ] || [ -f setup.py ]; then
    pytest tests/ -x --tb=short
fi

# Node.js (package.json with test script)
if [ -f package.json ] && grep -q '"test"' package.json; then
    npm test
fi

# Swift (Package.swift or .xcodeproj)
if [ -f Package.swift ]; then
    swift test
elif ls *.xcodeproj 1>/dev/null 2>&1; then
    set -o pipefail && xcodebuild test -scheme "$(xcodebuild -list -json | python3 -c 'import sys,json; print(json.load(sys.stdin)["project"]["schemes"][0])')" 2>&1 | xcbeautify
fi
```

If tests fail → abort release. Show failure output.

If no test runner detected → warn "No test suite found. Proceeding without tests." and ask user to confirm.

---

## Step 4: Determine next version

### Parse conventional commits

```bash
git log "$LAST_TAG"..HEAD --pretty=format:"%s" --no-merges
```

Scan each commit subject:
- `^feat!:` or body contains `BREAKING CHANGE:` → **major**
- `^feat(\(.*\))?:` → **minor**
- `^fix(\(.*\))?:` → **patch**
- `^docs:|^chore:|^refactor:|^test:|^ci:|^style:|^perf:` → no bump (but include in changelog)

Highest bump wins. If no conventional commits found, ask the user to pick patch/minor/major.

Override: if `$ARGUMENTS` explicitly says `patch`, `minor`, or `major`, use that regardless of commit analysis.

### Compute version

Parse `LAST_TAG` (strip `v` prefix), split on `.`, bump the right segment, reset lower segments to 0.

Example: `v2.1.3` + minor → `v2.2.0`

---

## Step 5: Generate CHANGELOG entry

Build the entry from conventional commits, grouped by type:

```markdown
## [X.Y.Z] - YYYY-MM-DD

### Added
- Phase 0 knowledge store search (#commit-hash)

### Fixed
- BM25 multi-word queries returning 0 results (#commit-hash)

### Changed
- Research pipeline now runs 6 phases instead of 5

### Breaking Changes
- (only if major bump)
```

Mapping:
- `feat:` → **Added**
- `fix:` → **Fixed**
- `refactor:` `perf:` → **Changed**
- `BREAKING CHANGE:` → **Breaking Changes**
- `docs:` `chore:` `test:` `ci:` → omit from changelog (internal)

Non-conventional commits go under **Other** with their full subject line.

### Write CHANGELOG.md

If `CHANGELOG.md` exists, prepend the new entry after the `# Changelog` header.
If it doesn't exist, create it with a header and this first entry.

Stage the file: `git add CHANGELOG.md`

---

## Step 6: Update version files

Detect and update version strings in project files:

```bash
# Python: pyproject.toml
if [ -f pyproject.toml ]; then
    # Update: version = "X.Y.Z" under [project] or [tool.poetry]
fi

# Node.js: package.json
if [ -f package.json ]; then
    # Update: "version": "X.Y.Z"
fi

# Claude plugin: .claude-plugin/plugin.json or similar
if [ -f .claude-plugin/plugin.json ]; then
    # Update version field
fi
```

Stage changed files: `git add pyproject.toml package.json` (whichever exist)

---

## Step 7: Confirm with user

Use AskUserQuestion:
- Header: "Release"
- Question: "Release $PLUGIN_NAME $NEXT_VERSION?"
- Show: commit count, changelog preview (first 10 lines), files that will change
- Options:
  1. "Release $NEXT_VERSION" — proceed
  2. "Change version" — let user type a version
  3. "Abort" — cancel, unstage everything

---

## Step 8: Commit, tag, and push

```bash
# Commit version bump + changelog (if any files were staged)
if ! git diff --cached --quiet; then
    git commit -m "chore: release $NEXT_VERSION"
fi

# Tag
git tag -a "$NEXT_VERSION" -m "Release $NEXT_VERSION"

# Push commit + tag
git push origin main
git push origin "$NEXT_VERSION"
```

---

## Step 9: Create GitHub Release

```bash
# Generate release notes from changelog entry
gh release create "$NEXT_VERSION" \
  --title "$PLUGIN_NAME $NEXT_VERSION_NO_V" \
  --notes-file <(extract changelog entry for this version) \
  --latest
```

If `gh` is not installed or auth fails → warn and skip. The tag is already pushed; a GitHub Release can be created manually.

---

## Step 10: Update marketplace.json

```bash
MARKETPLACE="/Users/quartershots/Source/shihwesley-plugins/.claude-plugin/marketplace.json"
ORIGINAL_DIR=$(pwd)
```

Read marketplace.json. Find plugin entry by `name == $PLUGIN_NAME`. Update `"version"` to `$NEXT_VERSION` (without `v` prefix).

Write the file.

```bash
cd /Users/quartershots/Source/shihwesley-plugins
git add .claude-plugin/marketplace.json
git commit -m "chore: bump $PLUGIN_NAME to $NEXT_VERSION_NO_V"
git push origin main
cd "$ORIGINAL_DIR"
```

If shihwesley-plugins has uncommitted changes → `git stash` before, `git stash pop` after.

---

## Step 11: Report

```
Released: $PLUGIN_NAME $NEXT_VERSION

Tag:        $NEXT_VERSION → $REPO_URL
GitHub:     https://github.com/shihwesley/$PLUGIN_NAME/releases/tag/$NEXT_VERSION
Changelog:  CHANGELOG.md updated
Marketplace: shihwesley-plugins bumped to $NEXT_VERSION_NO_V
Commits:    $COMMIT_COUNT included

Reinstall:  claude plugins update shihwesley-plugins/$PLUGIN_NAME
```

---

## Dry Run Mode

If `--dry-run` in `$ARGUMENTS`:
- Run Steps 1-5 (validate, test, detect version, generate changelog preview)
- Show what would be written to CHANGELOG.md
- Show what version files would change
- Show the `gh release create` command that would run
- Skip Steps 6-10 (no writes, no tags, no pushes)
- Report: "Dry run complete. No changes made."

---

## Edge Cases

- **No conventional commits:** ask user for bump type, generate changelog from raw subjects
- **marketplace.json not found:** warn, skip marketplace update, still tag and release
- **Plugin not in marketplace:** warn, tag pushed but marketplace not updated
- **Tests fail:** abort entirely, show failure output
- **gh CLI not installed:** skip GitHub Release, warn user
- **Multiple tags at different commits:** use most recent semver tag only
- **Pre-release versions (alpha/beta):** not supported yet — use manual tagging

## Rules

- Never force-push
- Never skip hooks (no `--no-verify`)
- Always run tests before tagging
- Always confirm with user before tagging
- Always return to original directory after marketplace update
- Changelog follows [Keep a Changelog](https://keepachangelog.com/) format
- Versions follow [Semantic Versioning](https://semver.org/) strictly

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.