원클릭으로
release
Create a release with automated changelog generation from commits since last release using semantic versioning.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Create a release with automated changelog generation from commits since last release using semantic versioning.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Clean up merged and stale branches from local and remote repositories. Use when branch list is cluttered or after merging PRs.
Generate document index files (manifest, bundles, graph, router) for project documentation. Creates docs/.index/ with searchable registry, feature-grouped bundles, cross-reference dependency graph, and query routing. Supports flat mode (generic projects, script-driven) and grouped mode (projects with doc_id frontmatter, Claude-curated).
Comprehensive markdown document review - anchors, accuracy, SSOT, cross-references, and redundancy analysis.
Enforce complete implementation of all tiers/difficulty levels for tiered features. Prevents partial implementations.
Create well-structured GitHub issues using the 5W1H framework with proper labels and acceptance criteria.
Automate GitHub issue workflow - select issue, create branch, implement, build, test, and create PR.
| name | release |
| description | Create a release with automated changelog generation from commits since last release using semantic versioning. |
| argument-hint | <version> [--target <field>] [--draft] [--prerelease] [--solo|--team] [--reanchor-interval N] |
| user-invocable | true |
| disable-model-invocation | true |
| context | fork |
| allowed-tools | Bash(git *) Bash(gh *) |
| disallowed-tools | ["WebSearch","WebFetch","NotebookEdit"] |
| max_iterations | 5 |
| halt_conditions | [{"type":"success","expr":"Release PR merged and tag published"},{"type":"limit","expr":"CI failure persists after 3 retries"},{"type":"failure","expr":"integrity check fails"}] |
| on_halt | Report incomplete release state, leave tag/PR in whatever state they are in, do not force-publish |
| loop_safe | false |
| tiers | {"light":{"ref_docs":[],"deep_checks":false},"standard":{"ref_docs":["core"],"deep_checks":true},"deep":{"ref_docs":["core","advanced"],"deep_checks":true}} |
| default_tier | standard |
| iso_class | none |
Create a release with automated changelog generation from commits since last release.
/release <version> [--draft] [--prerelease] [--org <organization>]
/release <organization>/<project-name> <version> [--draft] [--prerelease]
Example:
/release 1.2.0 # Create release v1.2.0
/release 2.0.0 --draft # Create as draft release
/release 1.0.0-beta.1 --prerelease # Create pre-release
/release mycompany/myrepo 1.5.0 # Specify repository explicitly
/release 1.2.0 --solo # Force solo mode
/release 1.2.0 --team # Force team mode (parallel)
$ARGUMENTS format: <version> [options] or <organization>/<project-name> <version> [options]
suite (default), plugin, plugin-lite, settings-schema, hooks. See "Version Source" below.When this skill runs in the claude-config repository (or any repo that declares a
VERSION_MAP.yml at the root), versions are tracked as independent SemVer fields:
| Field | Consumers |
|---|---|
suite | README.md, README.ko.md (shields.io badge) |
plugin | plugin/.claude-plugin/plugin.json |
plugin-lite | plugin-lite/.claude-plugin/plugin.json |
settings-schema | global/settings.json, global/settings.windows.json |
hooks | VERSION_MAP.yml field only — SemVer-validated by check_versions, no consumer file to mirror; bumpable and taggable per hook-bundle rollout |
Each field moves on its own SemVer track — bumping plugin does NOT bump the others.
--target <field> selects which field to update; default is suite.
Propagation rule:
VERSION_MAP.yml — set <target>: <new-version>.scripts/sync_versions.sh (or .ps1 on Windows) to copy values
from the map into each consumer file.scripts/check_versions.sh runs in CI to catch any future drift.If no VERSION_MAP.yml exists at the repo root, the skill falls back to its legacy
single-version behavior (version passed as the sole argument, no multi-track bumping).
Parse $ARGUMENTS and determine organization:
# Check if --org flag is provided
if [[ "$ARGUMENTS" == *"--org"* ]]; then
VERSION=$(echo "$ARGUMENTS" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?')
ORG=$(echo "$ARGUMENTS" | sed -n 's/.*--org[[:space:]]*\([^[:space:]]*\).*/\1/p')
PROJECT=$(basename "$(pwd)")
# Check if first argument contains / (full path format)
elif [[ "$(echo "$ARGUMENTS" | awk '{print $1}')" == *"/"* ]]; then
REPO_PATH=$(echo "$ARGUMENTS" | awk '{print $1}')
ORG=$(echo "$REPO_PATH" | cut -d'/' -f1)
PROJECT=$(echo "$REPO_PATH" | cut -d'/' -f2)
VERSION=$(echo "$ARGUMENTS" | awk '{print $2}')
# Auto-detect from git remote
else
VERSION=$(echo "$ARGUMENTS" | awk '{print $1}')
ORG=$(git remote get-url origin 2>/dev/null | sed -E 's|.*[:/]([^/]+)/[^/]+\.git$|\1|' | sed -E 's|.*[:/]([^/]+)/[^/]+$|\1|')
PROJECT=$(git remote get-url origin 2>/dev/null | sed -E 's|.*/([^/]+)(\.git)?$|\1|' | sed 's/\.git$//')
if [[ -z "$ORG" ]]; then
echo "Error: Cannot detect organization. Use --org flag or full path format."
exit 1
fi
fi
| Option | Values | Default | Description |
|---|---|---|---|
--draft | flag | false | Create release as draft (not published) |
--prerelease | flag | false | Mark release as pre-release |
--target | suite|plugin|plugin-lite|settings-schema|hooks | suite | Independent SemVer track to bump |
--org | string | auto-detect | GitHub organization or user |
--solo or --team flag was providedExtract the flag from $ARGUMENTS. Use $EXEC_MODE directly.
Count commits since last release to estimate complexity:
PREVIOUS_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
if [[ -n "$PREVIOUS_TAG" ]]; then
COMMIT_COUNT=$(git rev-list --count $PREVIOUS_TAG..HEAD)
else
COMMIT_COUNT=$(git rev-list --count HEAD)
fi
| Signal | Solo (Recommended) | Team (Recommended) |
|---|---|---|
| Commits since last release | < 20 | 20+ |
| Release type | Patch (x.x.X) | Major/Minor (X.x.x, x.X.x) |
| Has breaking changes | No | Yes |
Decisive signals → apply silently. When the signals are unambiguous (all point the same direction — e.g. a patch release with < 20 commits and no breaking changes → solo, a major/minor release with 20+ commits or breaking changes → team), set $EXEC_MODE to the recommended mode WITHOUT asking and print a one-line notice, e.g. [Mode: solo — patch release, $COMMIT_COUNT commits; pass --team to override] (or [Mode: team — major release; pass --solo to override]). The user can still override with the --solo/--team flags handled in 0-1.
Conflicting signals → ask. Only when signals genuinely point in different directions (e.g. a patch version bump but 20+ commits, or a minor release with very few commits) use AskUserQuestion to present the choice:
Store the result in $EXEC_MODE (solo | team).
$EXEC_MODE == "solo" → Execute Solo Mode (Steps 1-6 below, unchanged)$EXEC_MODE == "team" → Execute Team Mode InstructionsBefore starting the release, verify that main has not diverged from develop
due to unauthorized direct merges:
git fetch origin main develop
# Verify all commits on main are ancestors of develop
if ! git merge-base --is-ancestor origin/main origin/develop; then
echo "WARNING: main contains commits not present on develop."
echo "This indicates unauthorized direct merges to main."
echo ""
echo "Divergent commits on main:"
git log origin/develop..origin/main --oneline
echo ""
echo "Resolution: cherry-pick divergent commits to develop or reset main to last release tag."
fi
If divergence is detected: Stop and ask the user how to resolve before proceeding. Do NOT create a release PR while main and develop have diverged.
Ensure version follows semantic versioning:
# Validate semver format
if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$'; then
echo "Error: Invalid version format. Use semantic versioning (e.g., 1.2.0, 2.0.0-beta.1)"
exit 1
fi
# Check if tag already exists
if git tag -l "v$VERSION" | grep -q "v$VERSION"; then
echo "Error: Tag v$VERSION already exists"
exit 1
fi
If VERSION_MAP.yml exists at the repo root, parse --target (default suite),
update the map, and propagate to consumers before cutting the release PR:
if [ -f VERSION_MAP.yml ]; then
# Parse --target flag (default: suite)
TARGET="suite"
if [[ "$ARGUMENTS" =~ --target[[:space:]]+(suite|plugin|plugin-lite|settings-schema|hooks) ]]; then
TARGET="${BASH_REMATCH[1]}"
fi
# Update VERSION_MAP.yml: set <target>: <new-version>
sed -E -i.bak "s/^(${TARGET}:)[[:space:]]*[^[:space:]#]+/\1 ${VERSION}/" VERSION_MAP.yml
rm -f VERSION_MAP.yml.bak
# Propagate to consumer files
if [ -x scripts/sync_versions.sh ]; then
bash scripts/sync_versions.sh
fi
# Verify no drift before proceeding
if [ -x scripts/check_versions.sh ]; then
bash scripts/check_versions.sh
fi
# Verify README.md and README.ko.md still share the same heading skeleton
# (#623). A failure here means a section was added or removed in one
# README without mirroring in the other; resolve the divergence before
# the release ships rather than letting i18n debt accrue.
if [ -x scripts/diff-readme.sh ]; then
bash scripts/diff-readme.sh
fi
# Stage the changes so they land in the release PR
git add VERSION_MAP.yml
git add plugin/.claude-plugin/plugin.json plugin-lite/.claude-plugin/plugin.json 2>/dev/null || true
git add global/settings.json global/settings.windows.json 2>/dev/null || true
git add README.md README.ko.md 2>/dev/null || true
git commit -m "chore(release): bump ${TARGET} to ${VERSION}"
git push origin develop
fi
Tag format: for --target suite (or when VERSION_MAP.yml is absent), use v$VERSION.
For other targets, use <target>-v$VERSION (e.g., plugin-v2.3.1) to keep SemVer tracks
separate in the git tag history.
# Get the most recent tag
PREVIOUS_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
if [[ -z "$PREVIOUS_TAG" ]]; then
echo "No previous release found. Generating changelog from all commits."
COMMIT_RANGE="HEAD"
else
echo "Previous release: $PREVIOUS_TAG"
COMMIT_RANGE="$PREVIOUS_TAG..HEAD"
fi
Collect and categorize commits since last release:
# Get commits since last release
COMMITS=$(git log $COMMIT_RANGE --pretty=format:"%s|%h|%an" --no-merges)
# Initialize category arrays
ADDED=""
FIXED=""
CHANGED=""
DOCS=""
OTHER=""
# Categorize commits by type prefix
while IFS='|' read -r message hash author; do
# Extract type from conventional commit format
TYPE=$(echo "$message" | grep -oE '^(feat|fix|docs|refactor|perf|test|chore|style|build|ci)' || echo "other")
case "$TYPE" in
feat)
ADDED="$ADDED\n- $message ($hash)"
;;
fix)
FIXED="$FIXED\n- $message ($hash)"
;;
refactor|perf|style)
CHANGED="$CHANGED\n- $message ($hash)"
;;
docs)
DOCS="$DOCS\n- $message ($hash)"
;;
*)
OTHER="$OTHER\n- $message ($hash)"
;;
esac
done <<< "$COMMITS"
Structure the changelog with categories:
## [VERSION] - YYYY-MM-DD
### Added
- feat(scope): description (abc1234)
- feat: another feature (def5678)
### Fixed
- fix(scope): bug description (ghi9012)
### Changed
- refactor(scope): improvement (jkl3456)
- perf: optimization (mno7890)
### Documentation
- docs: update readme (pqr1234)
### Other
- chore: maintenance task (stu5678)
Create a pull request from develop to main with the changelog as the PR body:
# Ensure we are on the develop branch with latest changes
git checkout develop
git pull origin develop
# Create release PR targeting main
PR_URL=$(gh pr create --repo $ORG/$PROJECT \
--base main --head develop \
--title "release: v$VERSION" \
--body "$CHANGELOG")
PR_NUMBER=$(echo "$PR_URL" | grep -oE '[0-9]+$')
echo "Created release PR #$PR_NUMBER: $PR_URL"
Wait for CI to pass on the release PR, then squash merge:
# Wait briefly for workflows to register
sleep 8
# Poll CI checks (30s intervals, 10min max)
# See issue-work reference for full CI monitoring protocol
gh pr checks $PR_NUMBER --repo $ORG/$PROJECT
# ALL checks must pass before proceeding
gh pr merge $PR_NUMBER --repo $ORG/$PROJECT --squash
If CI fails, diagnose and fix on develop, push, and re-poll. Max 3 attempts.
The release skill creates a develop -> main PR. Apply the same Sonar
gate that pr-work uses (Step 10a) to release PRs as well — Quality Gate
regressions accumulated on develop must not slip into main.
Procedure: identical to pr-work Step 10a (fetch the sonarcloud[bot]
summary comment, parse the Quality Gate verdict, invoke sonar-fix on
FAIL, re-poll, halt if sonar-fix exhausts its max_iterations).
See: ~/.claude/skills/_internal/pr-work/SKILL.md Step 10a.
# Switch to main and pull the merged commit
git checkout main
git pull origin main
# Determine tag name based on --target (default: suite -> v$VERSION)
TAG_NAME="v$VERSION"
if [ -f VERSION_MAP.yml ] && [ "${TARGET:-suite}" != "suite" ]; then
TAG_NAME="${TARGET}-v$VERSION"
fi
# Create annotated tag on main
git tag -a "$TAG_NAME" -m "Release $TAG_NAME"
# Push tag to remote
git push origin "$TAG_NAME"
After squash merge, develop and main have diverged histories. Delete and recreate
develop from main to resynchronize:
# Delete old develop (remote + local)
git push origin --delete develop
git branch -D develop 2>/dev/null
# Recreate from main
git checkout -b develop main
git push -u origin develop
# Restore default branch setting
gh api -X PATCH repos/$ORG/$PROJECT -f default_branch=develop
# Restore branch protection on develop
gh api -X PUT repos/$ORG/$PROJECT/branches/develop/protection \
--input - <<'PROTECTION'
{
"required_status_checks": null,
"enforce_admins": true,
"required_pull_request_reviews": {
"required_approving_review_count": 0
},
"restrictions": null
}
PROTECTION
# Build release command. Re-derive TAG_NAME here (Step 7 may run in a
# separate shell) so non-suite tracks publish against the tag that was
# actually pushed (<target>-v$VERSION), not a mismatched v$VERSION.
TAG_NAME="v$VERSION"
if [ -f VERSION_MAP.yml ] && [ "${TARGET:-suite}" != "suite" ]; then
TAG_NAME="${TARGET}-v$VERSION"
fi
RELEASE_CMD="gh release create $TAG_NAME --repo $ORG/$PROJECT --title \"$TAG_NAME\""
# Add draft flag if specified
if [[ "$ARGUMENTS" == *"--draft"* ]]; then
RELEASE_CMD="$RELEASE_CMD --draft"
fi
# Add prerelease flag if specified
if [[ "$ARGUMENTS" == *"--prerelease"* ]]; then
RELEASE_CMD="$RELEASE_CMD --prerelease"
fi
# Add changelog as release notes
RELEASE_CMD="$RELEASE_CMD --notes \"\$CHANGELOG\""
# Execute release creation
eval $RELEASE_CMD
Commits are categorized based on conventional commit prefixes:
| Prefix | Category | Description |
|---|---|---|
feat | Added | New features |
fix | Fixed | Bug fixes |
refactor | Changed | Code refactoring |
perf | Changed | Performance improvements |
style | Changed | Code style changes |
docs | Documentation | Documentation updates |
test | Other | Test additions/changes |
chore | Other | Maintenance tasks |
build | Other | Build system changes |
ci | Other | CI configuration changes |
See reference/team-mode.md for the complete team mode workflow with changelog and release coordination.
See _policy.md for common rules, including the Atomic Multi-Phase Execution rule — when the user specifies multiple phases (e.g., "Phase 1/2/3"), complete all phases without pausing between them for confirmation.
| Item | Rule |
|---|---|
| Version format | Semantic versioning required (MAJOR.MINOR.PATCH) |
| Tag prefix | Always use v prefix (e.g., v1.2.0) |
| Duplicate tags | Prevent creation if tag exists |
After completion, provide summary:
## Release Created
| Item | Value |
|------|-------|
| Repository | ORG/PROJECT |
| Version | vVERSION |
| Type | Release / Draft / Pre-release |
| Execution mode | Solo / Team |
| Tag | TAG_NAME (`v$VERSION`, or `<target>-v$VERSION` for non-suite tracks) |
| URL | https://github.com/ORG/PROJECT/releases/tag/TAG_NAME |
### Changelog Summary
| Category | Count |
|----------|-------|
| Added | N |
| Fixed | N |
| Changed | N |
| Documentation | N |
| Other | N |
### Commits Included
- Total: N commits since PREVIOUS_TAG
--reanchor-interval N (default 5, 0 disables) controls how often the Core invariants block from global/skills/_internal/_shared/invariants.md is emitted during the release workflow.
Loop bind point: between each CI retry and each merge-gate verification. For a typical release flow this fires once or twice — the interval primarily protects prolonged retry storms where attention would otherwise drift from the English-only / squash-merge / CI-gate rules.
See reference/error-handling.md for prerequisite checks and runtime error handling.
This skill is loop_safe: false. It bumps versions, creates git tags, and publishes GitHub releases. Repeated invocation would create duplicate or conflicting tags/releases for the same version. The idempotency contract is "one release per version bump"; never wrap it in /loop.