Skip to main content
AI/MLjeremylongshore

promote

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.

Stars
2,267
Source
jeremylongshore/claude-code-plugins-plus-skills
Updated
2026-05-31
Slug
jeremylongshore--claude-code-plugins-plus-skills--promote
View on GitHubRaw SKILL.md

// install — copy + paste into any project

mkdir -p .claude/skills && curl -fsSL https://raw.githubusercontent.com/jeremylongshore/claude-code-plugins-plus-skills/HEAD/plugins/productivity/claude-workflow-skills/skills/promote/SKILL.md -o .claude/skills/promote.md

Drops the SKILL.md into .claude/skills/promote.md. Works with Claude Code, Cursor, and any agent that loads SKILL.md files from .claude/skills/.

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:

!`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

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:

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:

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:

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:

    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:

    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:

    gh pr create --title "<type>: <summary>" --body "$(cat <<'EOF'
    ## Summary
    <bullet points from commits>
    
    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):

    gh pr merge --auto --squash
    
  4. Poll until merged:

    gh pr view --json state --jq '.state'
    
  5. Switch to main and pull:

    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 <next-version> 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 <next-version> (no v prefix — plugin manifests use bare semver like 1.2.3):

node -e "
const fs = require('fs');
const f = '.claude-plugin/plugin.json';
const d = JSON.parse(fs.readFileSync(f, 'utf8'));
d.version = '<next-version>';
fs.writeFileSync(f, JSON.stringify(d, null, 2) + '\n');
"

Then commit and push:

git add .claude-plugin/plugin.json
git commit -m "chore: sync plugin.json version to v<next-version>"
git push

Skip this step entirely if .claude-plugin/plugin.json does not exist.

Step 6: Tag and release

git tag -a v<next-version> -m "Release v<next-version>"
git push origin v<next-version>
gh release create v<next-version> \
  --generate-notes \
  --title "v<next-version>"

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:

gh issue close <N> --comment "Resolved in $(gh release view v<next-version> --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:

git branch -d <feature-branch>
git push origin --delete <feature-branch>

Step 9: Confirm clean state

git status
git log --oneline -5

Report the GitHub release URL from gh release view v<next-version> --json url --jq '.url'.