--- name: promote description: Runs the full release workflow for the current project. Commits any uncommitted changes, pushes to remote, creates and merges a PR if on a feature branch, determines the next semver version from conventional commits, creates an annotated git tag and GitHub release with generated notes, cleans up merged branches, and returns to a clean main. Use when the user says promote, ship, release, commit and push, tag and release, or get back to main. allowed-tools: Bash --- # Promote Changes Runs the full release workflow from current working state to a tagged GitHub release on main. Current branch: !`git branch --show-current 2>/dev/null || echo "unknown"` Git status: !`git status --short 2>/dev/null || echo "not a git repo"` Last tag: !`git describe --tags --abbrev=0 2>/dev/null || echo "none"` Recent commits since last tag: ```text !`git log $(git describe --tags --abbrev=0 2>/dev/null || echo "")..HEAD --oneline -10 2>/dev/null || git log --oneline -10` ``` ## Step 0: Pre-flight checks ```bash gh auth status 2>&1 || { echo "ERROR: gh is not authenticated. Run: gh auth login"; exit 1; } ``` Check for untracked `.env` files that could be accidentally staged: ```bash git status --short | grep -E '^\?\? .*\.env' && echo "WARNING: untracked .env files detected — review before staging" cat .gitignore 2>/dev/null | grep -q '\.env' || echo "WARNING: .gitignore does not exclude .env files" ``` If any `.env` files would be staged: ```bash if [ -t 0 ]; then # Interactive — ask for confirmation before continuing read -p "WARNING: .env files detected. Continue? (y/N) " confirm [[ "$confirm" =~ ^[Yy]$ ]] || { echo "Aborted."; exit 1; } else # Non-interactive — hard stop; too risky to proceed without human review echo "ERROR: untracked .env files detected in non-interactive mode. Aborting." exit 1 fi ``` ## Step 1: Commit any uncommitted changes If the git status above shows uncommitted or untracked changes: 1. Review the diff: `git diff` and `git diff --cached` 2. Stage changes selectively — prefer tracked files: `git add -u`, then review and add any intentional new files individually. Avoid `git add -A` unless the user explicitly confirms. 3. Draft a conventional commit message from the changes — lead with a type prefix (`feat:`, `fix:`, `docs:`, `chore:`, etc.) and a concise summary 4. Commit using a heredoc so multi-line messages format correctly If there is nothing uncommitted, skip to Step 2. ## Step 2: Push Push the current branch to remote: ```bash git push -u origin HEAD ``` ## Step 3: Merge to main (feature branch only) Skip this step if already on `main` (or the repo's default branch). If on a feature branch: 1. Scan commits since the last tag for issue references: ```bash git log $(git describe --tags --abbrev=0 2>/dev/null || echo "")..HEAD --format="%B" ``` Look for `Closes #N`, `Fixes #N`, `Resolves #N` (case-insensitive). Collect all issue numbers found. If none are found: ```bash if [ -t 0 ]; then # Interactive — ask the user read -p "Any GitHub issues this resolves? (e.g. 12 15 — or enter to skip) " issues else # Non-interactive — skip silently issues="" fi ``` 2. Create a PR targeting main, including closing keywords in the body so GitHub closes the issues automatically on merge: ```bash gh pr create --title ": " --body "$(cat <<'EOF' ## Summary Closes #N, Closes #N 🤖 Generated with [Claude Code](https://claude.com/claude-code) EOF )" ``` Omit the `Closes` lines if no issues were identified. 3. Enable auto-merge (squash preferred): ```bash gh pr merge --auto --squash ``` 4. Poll until merged: ```bash gh pr view --json state --jq '.state' ``` 5. Switch to main and pull: ```bash git checkout main && git pull ``` ## Step 4: Determine next version Analyse the commits since the last tag shown above. Apply semver rules: - Any commit with `BREAKING CHANGE:` in the footer, or a `!` suffix on the type → **major** bump - Any commit beginning with `feat:` or `feat(scope):` → **minor** bump - All other commits (`fix:`, `docs:`, `chore:`, `refactor:`, etc.) → **patch** bump If no previous tag exists, start at `v1.0.0`. Calculate `` from the current last tag and the rule above. ## Step 5: Sync plugin manifest (if present) If `.claude-plugin/plugin.json` exists, update its `version` field to `` (no `v` prefix — plugin manifests use bare semver like `1.2.3`): ```bash node -e " const fs = require('fs'); const f = '.claude-plugin/plugin.json'; const d = JSON.parse(fs.readFileSync(f, 'utf8')); d.version = ''; fs.writeFileSync(f, JSON.stringify(d, null, 2) + '\n'); " ``` Then commit and push: ```bash git add .claude-plugin/plugin.json git commit -m "chore: sync plugin.json version to v" git push ``` Skip this step entirely if `.claude-plugin/plugin.json` does not exist. ## Step 6: Tag and release ```text git tag -a v -m "Release v" git push origin v gh release create v \ --generate-notes \ --title "v" ``` ## Step 7: Close resolved issues (main branch only) Skip this step if a PR was created in Step 3 — GitHub will close the issues automatically when the PR merges via the `Closes #N` keywords in the PR body. If the promotion was directly on `main` (no PR), close any identified issues now: ```bash gh issue close --comment "Resolved in $(gh release view v --json url --jq '.url')" ``` ## Step 8: Clean up merged feature branch If a feature branch was merged in Step 3, delete it locally and remotely: ```text git branch -d git push origin --delete ``` ## Step 9: Confirm clean state ```bash git status git log --oneline -5 ``` Report the GitHub release URL from `gh release view v --json url --jq '.url'`.