Automates semantic versioning and releases using Node.js semantic-release v25+ for all languages. Use when setting up automated releases, creating shareable configs, or configuring GitHub Actions.
Inherits all available tools
Additional assets for this skill
This skill inherits all available tools. When active, it can use any tool Claude has access to.
assets/templates/doppler.yamlassets/templates/github-workflow.ymlassets/templates/package.jsonassets/templates/releaserc-pypi-doppler.jsonassets/templates/releaserc.ymlassets/templates/shareable-config/README.mdassets/templates/shareable-config/index.jsassets/templates/shareable-config/package.jsonreferences/2025-updates.mdreferences/adr-release-linking.mdreferences/authentication.mdreferences/local-release-workflow.mdreferences/monorepo-support.mdreferences/pypi-publishing-with-doppler.mdreferences/python-projects-nodejs-semantic-release.mdreferences/resources.mdreferences/troubleshooting.mdreferences/version-alignment.mdreferences/workflow-patterns.mdscripts/create_org_config.shAutomate semantic versioning and release management using semantic-release v25+ (Node.js) following 2025 best practices. Works with all languages (JavaScript, TypeScript, Python, Rust, Go, C++, etc.) via the @semantic-release/exec plugin. Create shareable configurations for multi-repository setups, initialize individual projects with automated releases, and configure GitHub Actions workflows with OIDC trusted publishing.
Important: This skill uses semantic-release (Node.js) exclusively, NOT python-semantic-release, even for Python projects. Rationale: 23.5x larger community, 100x+ adoption, better future-proofing.
Invoke when:
22,900 GitHub stars - Large, active community
1.9M weekly downloads - Proven adoption
126,000 projects using it - Battle-tested at scale
35+ official plugins - Rich ecosystem
Multi-language support - Works with any language via @semantic-release/exec
Do NOT use python-semantic-release. It has a 23.5x smaller community (975 vs 22,900 stars), ~100x less adoption, and is not affiliated with the semantic-release organization.
Default approach: Run releases locally, not via GitHub Actions.
Primary argument: GitHub Actions is slow
Additional benefits:
package.json, CHANGELOG.md, tags updated immediatelygit pull after releasenpm run release:dry to preview changes before releaseGitHub Actions workflows are provided as optional automation, not the primary method:
gh auth login
# Browser authentication once
# Credentials stored in keyring
# All future releases: zero manual intervention
This is the minimum manual intervention possible for local semantic-release with GitHub plugin functionality.
This standard applies to ALL GitHub Actions workflows, not just semantic-release.
GitHub Actions workflows must NEVER include:
❌ Test execution:
# ❌ FORBIDDEN - Do not add to any workflow
- run: pytest
- run: npm test
- run: cargo test
- uses: actions/upload-test-results # Implies testing
❌ Linting/Formatting:
# ❌ FORBIDDEN - Do not add to any workflow
- run: ruff check
- run: eslint .
- run: cargo clippy
- run: prettier --check
- run: mypy
✅ Semantic-release (this workflow):
- run: npm run release # Version, changelog, GitHub release only
✅ Security scanning:
- run: npm audit signatures
- uses: github/codeql-action/analyze@v3
✅ Deployment:
- run: docker build && docker push
- run: aws s3 sync ./build s3://bucket
✅ Dependency updates:
- uses: dependabot/fetch-metadata@v2
Documentation-based: This standard is enforced through CLAUDE.md instructions, not pre-commit hooks.
When creating or modifying GitHub Actions workflows, Claude Code will check CLAUDE.md and this skill to ensure compliance.
semantic-release configuration follows a hierarchical, composable pattern:
Level 1: Skill - ${CLAUDE_PLUGIN_ROOT}/skills/semantic-release/ (Generic templates, system-wide tool)
Level 2: User Config - ~/semantic-release-config/ (@username/semantic-release-config)
Level 3: Organization Config - npm registry (@company/semantic-release-config)
Level 4: Project Config - .releaserc.yml in project root
Level 4 (Project) → overrides → Level 3 (Org) → overrides → Level 2 (User) → overrides → Defaults
semantic-release analyzes commit messages to determine version bumps:
<type>(<scope>): <subject>
feat: → MINOR version bump (0.1.0 → 0.2.0)fix: → PATCH version bump (0.1.0 → 0.1.1)BREAKING CHANGE: or feat!: → MAJOR version bump (0.1.0 → 1.0.0)docs:, chore:, style:, refactor:, perf:, test: → No version bump (by default)For Claude Code marketplace plugins, every change requires a version bump for users to receive updates.
Option A: Shareable Config (if published)
# .releaserc.yml
extends: "@terryli/semantic-release-config/marketplace"
Option B: Inline Configuration
# .releaserc.yml
plugins:
- - "@semantic-release/commit-analyzer"
- releaseRules:
# Marketplace plugins require version bump for ANY change
- { type: "docs", release: "patch" }
- { type: "chore", release: "patch" }
- { type: "style", release: "patch" }
- { type: "refactor", release: "patch" }
- { type: "test", release: "patch" }
- { type: "build", release: "patch" }
- { type: "ci", release: "patch" }
Result after configuration:
| Commit Type | Release Type |
|---|---|
feat: | minor (default) |
fix:, perf:, revert: | patch (default) |
docs:, chore:, style:, refactor:, test:, build:, ci: | patch (configured) |
Why marketplace plugins need this: Plugin updates are distributed via version tags. Without a version bump, users running /plugin update see no changes even if content was modified.
Pre-release validation: Before running semantic-release, verify releasable commits exist since last tag. A release without version increment is invalid.
Autonomous check sequence:
feat:, fix:, or BREAKING CHANGE: prefixesfeat: or fix: prefix for releasable changes."Commit type selection guidance:
fix: for any change that improves existing behavior (bug fixes, enhancements, documentation corrections that affect usage)feat: for new capabilities or significant additionschore:, docs:, refactor: for changes that truly don't warrant a releaseWhy this matters: A release without version increment creates confusion - users cannot distinguish between releases, package managers may cache old versions, and changelog entries become meaningless.
Feature (MINOR):
feat: add BigQuery data source support
Bug Fix (PATCH):
fix: correct timestamp parsing for UTC offsets
Breaking Change (MAJOR):
feat!: change API to require authentication
BREAKING CHANGE: All API calls now require API key in Authorization header.
Link Architecture Decision Records (ADRs) and Design Specs in release notes automatically.
Step 1: Set environment variable before running semantic-release:
export ADR_NOTES_SCRIPT="${CLAUDE_PLUGIN_ROOT:-$HOME/.claude/plugins/marketplaces/cc-skills/plugins/itp}/skills/semantic-release/scripts/generate-adr-notes.mjs"
Step 2: Add to .releaserc.yml before @semantic-release/changelog:
- - "@semantic-release/exec"
- generateNotesCmd: 'node "$ADR_NOTES_SCRIPT" ${lastRelease.gitTag}'
Why? @semantic-release/exec uses lodash templates which interpret ${...} as JavaScript. Using $VAR (no braces) bypasses lodash and lets bash expand it.
The script detects ADRs via:
docs/adr/*.md and docs/design/*/spec.mdADR: 2025-12-06-slug in commit messagesFull HTTPS URLs are generated (required for GitHub release pages).
See ADR Release Linking for detailed configuration.
CRITICAL: For multi-account GitHub setups, verify the active gh account matches the expected account for the current directory BEFORE any release operation. This check is non-negotiable.
Primary detection method: SSH Config
If your ~/.ssh/config uses directory-based account selection (via Match directives), it is the authoritative source for which GitHub account should be used. The gh CLI must align with it.
Example SSH config pattern (directory-based):
# SSH config can define directory-based account selection via Match directives:
Match host github.com exec "echo $PWD | grep -q '/path-pattern-a/'"
IdentityFile ~/.ssh/id_ed25519_account_a
Match host github.com exec "echo $PWD | grep -q '/path-pattern-b/'"
IdentityFile ~/.ssh/id_ed25519_account_b
Autonomous detection sequence:
Determine expected account from SSH (if directory-based config exists):
ssh -T git@github.comHi <username>! You've successfully authenticated...Identify active gh account:
gh auth statusCompare and auto-resolve:
gh auth switch --user <ssh-username>gh auth statusWhy alignment matters: Git push uses SSH authentication. GitHub API (semantic-release) uses gh token. If they authenticate as different accounts, you get "Repository not found" errors even for valid repositories.
Quick verification:
# SSH account (git operations)
ssh -T git@github.com
# gh account (API operations) - must match SSH
gh auth status | grep -A1 "Active account: true"
⚠️ ControlMaster Cache Warning: If your SSH config uses ControlMaster for connection pooling, cached connections may persist with stale authentication from a previous account. Even if account alignment checks pass, cached connections can cause "Repository not found" errors.
Quick fix (kill cached connections):
ssh -O exit git@github.com 2>/dev/null || pkill -f 'ssh.*github.com'
Prevention (recommended for multi-account setups):
# ~/.ssh/config - Disable ControlMaster for GitHub
Host github.com
ControlMaster no
See Local Release Workflow - ControlMaster Issues for detailed troubleshooting.
See Local Release Workflow for complete detection sequence.
See Authentication Guide for SSH and gh CLI setup.
cd /path/to/project
# Environment-agnostic path
PLUGIN_DIR="${CLAUDE_PLUGIN_ROOT:-$HOME/.claude/plugins/marketplaces/cc-skills/plugins/itp}"
"$PLUGIN_DIR/skills/semantic-release/scripts/init_project.sh" --user
# Or --org mycompany/semantic-release-config
# Or --inline
Follow the Local Release Workflow for the complete process.
Quick commands:
# Dry-run first (no changes)
/usr/bin/env bash -c 'GITHUB_TOKEN=$(gh auth token) npx semantic-release --no-ci --dry-run'
# Create actual release
/usr/bin/env bash -c 'GITHUB_TOKEN=$(gh auth token) npx semantic-release --no-ci'
Files updated instantly: package.json, CHANGELOG.md, Git tags, GitHub release.
The workflow guides you through:
For Python packages: semantic-release handles versioning, use the pypi-doppler skill for local PyPI publishing.
Quick setup:
# Install Doppler CLI for secure token management
brew install dopplerhq/cli/doppler
# Store token in Doppler (one-time)
doppler secrets set PYPI_TOKEN='your-pypi-token' --project claude-config --config prd
# Copy publish script from pypi-doppler skill (environment-agnostic)
PLUGIN_DIR="${CLAUDE_PLUGIN_ROOT:-$HOME/.claude/plugins/marketplaces/cc-skills/plugins/itp}"
cp "$PLUGIN_DIR/skills/pypi-doppler/scripts/publish-to-pypi.sh" scripts/
chmod +x scripts/publish-to-pypi.sh
# After semantic-release creates GitHub release:
./scripts/publish-to-pypi.sh # 30 seconds vs 3-5 minutes with GitHub Actions
See pypi-doppler skill for complete workflow with CI detection guards.
Only if you want CI/CD backup (not recommended as primary due to 2-5 minute delay):
Repository Settings → Actions → General → Workflow permissions → Enable "Read and write permissions"
For detailed information, see:
pypi-doppler skill - Local PyPI publishing with Doppler credentials and CI detection guards