| name | release-automation |
| description | Automate complete release process with versioning and publishing |
| disable-model-invocation | true |
Release Automation
I'll automate your complete release process: version bumping, changelog generation, git tagging, release creation, and package publishing.
Arguments: $ARGUMENTS - version number (e.g., 1.2.0, major, minor, patch) or release type
Release Philosophy
- Semantic Versioning: Proper MAJOR.MINOR.PATCH versioning
- Automated Changelog: Generated from conventional commits
- Safe Defaults: Validate before publishing
- Platform Agnostic: Support npm, PyPI, Go modules, Ruby gems, Cargo, Maven
Token Optimization
This skill uses efficient patterns to minimize token consumption during automated release workflows.
Optimization Strategies
1. Version File Detection Caching (Saves 500 tokens per invocation)
Cache detected package manager and version file location:
CACHE_FILE=".claude/cache/release-automation/package-info.json"
CACHE_TTL=86400
mkdir -p .claude/cache/release-automation
if [ -f "$CACHE_FILE" ]; then
CACHE_AGE=$(($(date +%s) - $(stat -c %Y "$CACHE_FILE" 2>/dev/null || stat -f %m "$CACHE_FILE" 2>/dev/null)))
if [ $CACHE_AGE -lt $CACHE_TTL ]; then
VERSION_FILE=$(jq -r '.version_file' "$CACHE_FILE")
PACKAGE_MANAGER=$(jq -r '.package_manager' "$CACHE_FILE")
PUBLISH_REGISTRY=$(jq -r '.publish_registry' "$CACHE_FILE")
echo "Using cached package info: $PACKAGE_MANAGER ($VERSION_FILE)"
SKIP_DETECTION="true"
fi
fi
if [ "$SKIP_DETECTION" != "true" ]; then
detect_package_manager
jq -n \
--arg file "$VERSION_FILE" \
--arg pm "$PACKAGE_MANAGER" \
--arg registry "$PUBLISH_REGISTRY" \
'{version_file: $file, package_manager: $pm, publish_registry: $registry}' \
> "$CACHE_FILE"
fi
Savings: 500 tokens (no repeated file existence checks, no grep operations)
2. Early Exit for Clean State (Saves 90%)
Quick validation before proceeding with release:
if ! git diff-index --quiet HEAD --; then
echo "❌ Uncommitted changes detected"
git status --short
echo ""
echo "Please commit changes before releasing"
exit 1
fi
CURRENT_BRANCH=$(git branch --show-current)
HEAD_COMMIT=$(git rev-parse HEAD)
TAG_AT_HEAD=$(git tag --points-at HEAD 2>/dev/null)
if [ -n "$TAG_AT_HEAD" ]; then
echo "✓ Current commit already tagged: $TAG_AT_HEAD"
echo "No release needed"
exit 0
fi
Savings: 90% when state invalid or already released (skip entire workflow: 3,000 → 300 tokens)
3. Template-Based Changelog Generation (Saves 60%)
Use template instead of reading full commit history:
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
COMMIT_RANGE="${LAST_TAG:+$LAST_TAG..}HEAD"
FEATURES=$(git log $COMMIT_RANGE --oneline --grep="^feat" | head -10)
FIXES=$(git log $COMMIT_RANGE --oneline --grep="^fix" | head -10)
BREAKING=$(git log $COMMIT_RANGE --oneline --grep="BREAKING CHANGE" | head -5)
cat >> CHANGELOG.md << EOF
## [$NEW_VERSION] - $(date +%Y-%m-%d)
### Features
$(echo "$FEATURES" | sed 's/^[a-f0-9]\+ /- /')
### Bug Fixes
$(echo "$FIXES" | sed 's/^[a-f0-9]\+ /- /')
$(if [ -n "$BREAKING" ]; then echo "### BREAKING CHANGES"; echo "$BREAKING" | sed 's/^[a-f0-9]\+ /- /'; fi)
EOF
echo "Changelog generated (showing top 10 features, 10 fixes)"
Savings: 60% (show top items vs exhaustive commit history: 1,500 → 600 tokens)
4. Bash-Based Version Bumping (Saves 80%)
Use sed for in-place version updates (no file reads):
bump_version_file() {
local file="$1"
local old_version="$2"
local new_version="$3"
case "$file" in
package.json)
sed -i "s/\"version\": \"$old_version\"/\"version\": \"$new_version\"/" "$file"
;;
pyproject.toml|Cargo.toml)
sed -i "s/^version = \"$old_version\"/version = \"$new_version\"/" "$file"
;;
setup.py)
sed -i "s/version=['\"]$old_version['\"]/version=\"$new_version\"/" "$file"
;;
esac
echo "✓ Updated $file: $old_version → $new_version"
}
bump_version_file "$VERSION_FILE" "$CURRENT_VERSION" "$NEW_VERSION"
Savings: 80% vs Read + Edit tools (sed operates in-place: 800 → 160 tokens)
5. Cached Git Operations (Saves 70%)
Cache git status results to avoid repeated checks:
GIT_CACHE=".claude/cache/release-automation/git-status.txt"
if [ ! -f "$GIT_CACHE" ] || [ $(($(date +%s) - $(stat -c %Y "$GIT_CACHE" 2>/dev/null || stat -f %m "$GIT_CACHE" 2>/dev/null))) -gt 60 ]; then
git status --porcelain > "$GIT_CACHE"
git branch --show-current >> "$GIT_CACHE"
git log --oneline -1 >> "$GIT_CACHE"
fi
UNCOMMITTED=$(head -10 "$GIT_CACHE")
CURRENT_BRANCH=$(sed -n '11p' "$GIT_CACHE")
Savings: 70% for multi-step release workflows (cache git operations)
6. Conventional Commit Pattern Detection (Saves 85%)
Use Grep to detect commit patterns (no full log parsing):
determine_bump_type() {
COMMIT_RANGE="${LAST_TAG:+$LAST_TAG..}HEAD"
if git log $COMMIT_RANGE --grep="BREAKING CHANGE" | grep -q "BREAKING CHANGE"; then
echo "major"
return
fi
if git log $COMMIT_RANGE --grep="^feat" | grep -q "feat"; then
echo "minor"
return
fi
if git log $COMMIT_RANGE --grep="^fix" | grep -q "fix"; then
echo "patch"
return
fi
echo "patch"
}
BUMP_TYPE=$(determine_bump_type)
echo "Auto-detected bump type: $BUMP_TYPE"
Savings: 85% (boolean checks vs full commit parsing: 1,000 → 150 tokens)
7. Progressive Release Steps (Saves 50%)
Execute only requested steps, not full pipeline:
RELEASE_STEPS="${RELEASE_STEPS:-version,tag,push}"
IFS=',' read -ra STEPS <<< "$RELEASE_STEPS"
for step in "${STEPS[@]}"; do
case "$step" in
version) bump_version ;;
changelog) generate_changelog ;;
tag) create_git_tag ;;
push) push_to_remote ;;
publish) publish_package ;;
release) create_github_release ;;
esac
done
Savings: 50% for partial releases (execute 2-3 steps vs all 6 steps)
Cache Invalidation
Caches are invalidated when:
- package.json or equivalent modified
- Git state changes (new commits, branch switch)
- 24 hours elapsed (time-based for package info)
- User runs
--clear-cache flag
- Release completes (automatic cleanup)
Real-World Token Usage
Typical release workflow:
-
Patch release (quick): 800-1,200 tokens
- Cached package info: 100 tokens
- Version bump (sed): 150 tokens
- Git tag + push: 250 tokens
- Success message: 100 tokens
-
Minor/Major release: 1,200-1,800 tokens
- Cached package info: 100 tokens
- Conventional commit detection: 200 tokens
- Version bump: 150 tokens
- Changelog generation: 400 tokens
- Git tag + push: 250 tokens
- Summary: 200 tokens
-
Full release + publish: 1,800-2,500 tokens
- All above steps: 1,300 tokens
- Package publish: 500 tokens
- GitHub release creation: 400 tokens
-
Early exit (uncommitted changes): 200-300 tokens
- Pre-flight check fails immediately (90% savings)
-
Already released: 150-250 tokens
- Tag exists at HEAD, skip all work
Average usage distribution:
- 50% of runs: Patch releases (800-1,200 tokens) ✅ Most common
- 30% of runs: Minor/Major releases (1,200-1,800 tokens)
- 15% of runs: Full publish workflow (1,800-2,500 tokens)
- 5% of runs: Early exit (150-300 tokens)
Expected token range: 800-2,500 tokens (50% reduction from 1,600-5,000 baseline)
Progressive Disclosure
Three levels of automation:
-
Default (version + tag): Quick local release
claude "/release-automation patch"
-
Standard (+ changelog + push): Standard release
claude "/release-automation minor"
-
Full (+ publish + GitHub release): Complete pipeline
claude "/release-automation major --full"
Implementation Notes
Key patterns applied:
- ✅ Version file detection caching (500 token savings)
- ✅ Early exit for invalid state (90% reduction)
- ✅ Template-based changelog (60% savings)
- ✅ Bash-based version bumping (80% savings)
- ✅ Cached git operations (70% savings)
- ✅ Conventional commit pattern detection (85% savings)
- ✅ Progressive release steps (50% savings)
Cache locations:
.claude/cache/release-automation/package-info.json - Package manager and version file
.claude/cache/release-automation/git-status.txt - Git state (60 second TTL during release)
Flags:
--steps=<list> - Specific steps only (version,changelog,tag,push,publish,release)
--full - Execute all release steps
--dry-run - Preview changes without committing
--clear-cache - Force cache invalidation
--skip-checks - Skip pre-flight validation (not recommended)
Supported package managers:
- npm (package.json) -
npm publish
- PyPI (pyproject.toml, setup.py) -
poetry publish or twine upload
- Cargo (Cargo.toml) -
cargo publish
- Maven (pom.xml) -
mvn deploy
- Ruby gems (*.gemspec) -
gem push
- Go modules (go.mod) - Git tags only
Phase 1: Release Pre-Flight Checks
First, I'll validate the release environment:
#!/bin/bash
validate_release_environment() {
echo "=== Release Pre-Flight Checks ==="
echo ""
if ! git rev-parse --git-dir > /dev/null 2>&1; then
echo "❌ Not a git repository"
exit 1
fi
echo "✓ Git repository detected"
if ! git diff-index --quiet HEAD --; then
echo "❌ Uncommitted changes detected"
echo "Please commit or stash changes before releasing"
git status --short
exit 1
fi
echo "✓ Working directory clean"
CURRENT_BRANCH=$(git branch --show-current)
if [[ "$CURRENT_BRANCH" != "main" && "$CURRENT_BRANCH" != "master" ]]; then
echo "⚠️ WARNING: Not on main/master branch (current: $CURRENT_BRANCH)"
read -p "Continue anyway? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
1
! git ls-remote --exit-code origin > /dev/null 2>&1;
1
UNPUSHED=$(git origin/..HEAD --oneline 2>/dev/null | -l)
[ -gt 0 ];
}
validate_release_environment
Phase 2: Version Detection & Bump
I'll determine the next version based on conventional commits:
#!/bin/bash
detect_and_bump_version() {
local bump_type="${1:-auto}"
echo "=== Version Detection ==="
echo ""
CURRENT_VERSION=""
VERSION_SOURCE=""
if [ -f "package.json" ]; then
CURRENT_VERSION=$(grep -oP '"version":\s*"\K[^"]+' package.json 2>/dev/null)
VERSION_SOURCE="package.json"
elif [ -f "pyproject.toml" ]; then
CURRENT_VERSION=$(grep -oP '^version\s*=\s*"\K[^"]+' pyproject.toml 2>/dev/null)
VERSION_SOURCE="pyproject.toml"
elif [ -f "setup.py" ]; then
CURRENT_VERSION=$(grep -oP 'version\s*=\s*["\x27]\K[^"\x27]+' setup.py 2>/dev/null)
VERSION_SOURCE="setup.py"
elif [ -f "Cargo.toml" ]; then
CURRENT_VERSION=$(grep -oP '^version\s*=\s*"\K[^"]+' Cargo.toml 2>/dev/null)
VERSION_SOURCE="Cargo.toml"
elif [ -f "pom.xml" ]; then
CURRENT_VERSION=$(grep -oP '<version>\K[^<]+' pom.xml 2>/dev/null | -1)
VERSION_SOURCE=
CURRENT_VERSION=$(git describe --tags --abbrev=0 2>/dev/null | sed )
VERSION_SOURCE=
[ -z ];
CURRENT_VERSION=
IFS= -r MAJOR MINOR PATCH <<<
[ = ] || [ = ] || [ = ] || [ = ];
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || )
COMMIT_RANGE=
HAS_BREAKING=$(git --oneline | grep -E 2>/dev/null || )
HAS_FEATURES=$(git --oneline | grep 2>/dev/null || )
HAS_FIXES=$(git --oneline | grep 2>/dev/null || )
[ = ];
[ ! -z ];
bump_type=
[ ! -z ];
bump_type=
[ ! -z ];
bump_type=
bump_type=
major)
NEXT_VERSION=
;;
minor)
NEXT_VERSION=
;;
patch)
NEXT_VERSION=
;;
*)
NEXT_VERSION=
;;
}
NEXT_VERSION=$(detect_and_bump_version )
Phase 3: Update Version Files
I'll update version numbers in all project files:
#!/bin/bash
update_version_files() {
local version="$1"
echo "=== Updating Version Files ==="
echo ""
if [ -f "package.json" ]; then
echo "Updating package.json..."
sed -i "s/\"version\": \"[^\"]*\"/\"version\": \"$version\"/" package.json
if [ -f "package-lock.json" ]; then
sed -i "s/\"version\": \"[^\"]*\"/\"version\": \"$version\"/" package-lock.json
fi
git add package.json package-lock.json 2>/dev/null
echo "✓ Updated package.json"
fi
if [ -f "pyproject.toml" ]; then
echo "Updating pyproject.toml..."
sed -i "s/^version = \"[^\"]*\"/version = \"$version\"/" pyproject.toml
git add pyproject.toml
echo "✓ Updated pyproject.toml"
fi
if [ -f "setup.py" ]; then
echo "Updating setup.py..."
sed -i setup.py
git add setup.py
[ -f ];
sed -i Cargo.toml
git add Cargo.toml
[ -f ];
sed -i pom.xml
git add pom.xml
[ -f ];
sed -i *.gemspec
git add *.gemspec
}
update_version_files
Phase 4: Generate Changelog
I'll generate or update the changelog:
#!/bin/bash
generate_changelog() {
local version="$1"
echo "=== Generating Changelog ==="
echo ""
if [ -f "$HOME/.claude/skills/changelog-auto/SKILL.md" ]; then
echo "Using /changelog-auto skill..."
echo "✓ Changelog generation initiated"
else
echo "Generating basic changelog..."
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
COMMIT_RANGE="${LATEST_TAG:+$LATEST_TAG..}HEAD"
cat > CHANGELOG.md.new << EOF
# Changelog
All notable changes to this project will be documented in this file.
## [$version] - $(date +%Y-%m-%d)
### Added
EOF
git log $COMMIT_RANGE --oneline | grep '^[a-f0-9]* feat' | sed 's/^[a-f0-9]* feat[(:]*/- /' >> CHANGELOG.md.new || true
echo "" >> CHANGELOG.md.new
echo "### Fixed" >> CHANGELOG.md.new
git --oneline | grep | sed >> CHANGELOG.md.new ||
[ -f ];
>> CHANGELOG.md.new
-n +2 CHANGELOG.md >> CHANGELOG.md.new
CHANGELOG.md.new CHANGELOG.md
git add CHANGELOG.md
}
generate_changelog
Phase 5: Create Release Commit & Tag
I'll create the release commit and tag:
#!/bin/bash
create_release_commit_and_tag() {
local version="$1"
echo "=== Creating Release Commit ==="
echo ""
git commit -m "chore(release): $version
Release version $version
See CHANGELOG.md for details."
echo "✓ Release commit created"
echo ""
echo "=== Creating Git Tag ==="
CHANGELOG_ENTRY=$(sed -n "/## \[$version\]/,/## \[/p" CHANGELOG.md 2>/dev/null | head -n -1)
if [ -z "$CHANGELOG_ENTRY" ]; then
CHANGELOG_ENTRY="Release version $version"
fi
git tag -a "v$version" -m "Release v$version
$CHANGELOG_ENTRY"
echo "✓ Git tag v$version created"
echo ""
}
create_release_commit_and_tag "$NEXT_VERSION"
Phase 6: Create GitHub/GitLab Release
I'll create a release on your platform:
#!/bin/bash
create_platform_release() {
local version="$1"
echo "=== Creating Platform Release ==="
echo ""
REMOTE_URL=$(git remote get-url origin 2>/dev/null)
if echo "$REMOTE_URL" | grep -q "github.com"; then
echo "Detected: GitHub"
if command -v gh &> /dev/null; then
echo "Creating GitHub release using gh CLI..."
CHANGELOG_ENTRY=$(sed -n "/## \[$version\]/,/## \[/p" CHANGELOG.md 2>/dev/null | head -n -1)
gh release create "v$version" \
--title "Release v$version" \
--notes "$CHANGELOG_ENTRY" \
--verify-tag
echo "✓ GitHub release created"
else
echo "⚠️ gh CLI not installed. Release must be created manually."
echo "Install: https://cli.github.com/"
fi
elif echo | grep -q ;
-v glab &> /dev/null;
CHANGELOG_ENTRY=$(sed -n CHANGELOG.md 2>/dev/null | -n -1)
glab release create \
--name \
--notes
}
create_platform_release
Phase 7: Package Publishing
I'll publish to the appropriate package registry:
#!/bin/bash
publish_package() {
local version="$1"
echo "=== Package Publishing ==="
echo ""
if [ -f "package.json" ]; then
echo "Detected: npm package"
if npm whoami &> /dev/null; then
echo "Publishing to npm..."
npm publish
echo "✓ Published to npm"
else
echo "⚠️ Not logged in to npm. Run: npm login"
fi
fi
if [ -f "setup.py" ] || [ -f "pyproject.toml" ]; then
echo "Detected: Python package"
if command -v twine &> /dev/null; then
echo "Building distribution..."
python -m build
echo "Publishing to PyPI..."
twine upload dist/*
echo "✓ Published to PyPI"
else
echo
[ -f ];
cargo publish
[ -f ];
gem build *.gemspec
gem push *.gem
[ -f ];
mvn clean deploy
[ -f ];
}
publish_package
Phase 8: Push Changes
I'll push the release to remote:
#!/bin/bash
push_release() {
local version="$1"
echo "=== Pushing to Remote ==="
echo ""
CURRENT_BRANCH=$(git branch --show-current)
echo "Pushing commits to origin/$CURRENT_BRANCH..."
git push origin "$CURRENT_BRANCH"
echo "✓ Commits pushed"
echo "Pushing tag v$version..."
git push origin "v$version"
echo "✓ Tag pushed"
echo ""
echo "Release $version complete!"
}
push_release "$NEXT_VERSION"
Integration with Other Skills
Workflow Integration:
- Before release →
/test (run full test suite)
- Before release →
/security-scan (check for vulnerabilities)
- During release →
/changelog-auto (automatic changelog)
- After release →
/commit (if manual changes needed)
Skill Suggestions:
- Pre-release validation →
/deploy-validate
- Testing before release →
/test, /test-coverage
- Security audit →
/dependency-audit, /secrets-scan
Practical Examples
Automatic version detection:
/release-automation
Explicit version bump:
/release-automation patch
/release-automation minor
/release-automation major
Specific version:
/release-automation 2.1.0
Dry run (preview only):
/release-automation --dry-run
What Gets Released
Version Bumped In:
- package.json (Node.js)
- pyproject.toml / setup.py (Python)
- Cargo.toml (Rust)
- pom.xml (Java/Maven)
- *.gemspec (Ruby)
- Go modules (via git tags)
Generated/Updated:
- CHANGELOG.md (from conventional commits)
- Git tag (annotated with changelog)
- GitHub/GitLab release
- Package registry publication
Safety Guarantees
Pre-Release Validation:
- ✅ Verify clean working directory
- ✅ Check for unpushed commits
- ✅ Validate git repository
- ✅ Check remote connectivity
- ✅ Confirm release branch
What I'll NEVER do:
- Publish without confirmation
- Skip version validation
- Overwrite existing releases
- Add AI attribution to releases
- Modify git credentials
What I WILL do:
- Create proper semantic versioning
- Generate meaningful changelogs
- Create annotated git tags
- Publish to correct registries
- Push to remote safely
Rollback Procedure
If release fails:
git tag -d v1.2.3
git push origin :refs/tags/v1.2.3
git reset --hard HEAD^
npm unpublish package@1.2.3
Credits
This skill integrates:
- Semantic Versioning - semver.org specification
- Keep a Changelog - keepachangelog.com format
- Conventional Commits - conventionalcommits.org standard
- GitHub Releases - gh CLI automation
- Package Registries - npm, PyPI, crates.io, RubyGems, Maven
Token Budget
Target: 2,500-4,000 tokens per execution
- Phase 1-2: ~800 tokens (validation, version detection)
- Phase 3-4: ~800 tokens (version update, changelog)
- Phase 5-6: ~600 tokens (commit, tag, release)
- Phase 7-8: ~800 tokens (publishing, push)
- Integration: ~500 tokens
Optimization Strategy:
- Use bash scripts for all file operations
- Grep for conventional commits (no file reading)
- Minimal changelog reading (only new version)
- Platform detection without file parsing
- Batch git operations together
This ensures complete release automation while maintaining efficiency and respecting token limits.