A procedure, step by step

Cutting a plugin release

Reading time about 6 minutesWritten 2026-10-16For: the maintainer

Cut a release in 11 steps: bump the version, pass the checks, merge, tag, publish, then update your own copy.

Keep in your head (only these)
  1. Work in a worktree, on a branch named release/X.Y.Z.
  2. node tools/check-repo.mjs must exit 0 before the pull request opens.
  3. A release is a merged pull request, a tag and a GitHub release.

The 11 steps, in order

Cutting 1.9.0

Take: tick a row as you finish its step. The first open step is marked Next, and each row links to its step.

  1. What you need firstFour tools, with a one-line check for each.
  2. Prepare the release branchA worktree, the version, the changelog and the upgrade hash.
  3. Check it and open the pull requestUpgrade the shipped pages, run both checks, open the pull request.
  4. Publish it and update your copyMerge, tag, create the GitHub release, then update.
  5. When it goes wrongHow far each stage can be taken back.

Chapter 1 of 5, about 1 minute

What do I need before I start?

Before this: a release is a merged pull request, a tag and a GitHub release.

The steps use 1.9.0, the next release, and 1.8.1, the last one. Change both numbers for yours. Release work gets its own worktree, in a folder the repository already ignores.

  • Node 18 or newernode --version prints v18 or higher.
  • Git, on a clean maingit status --short prints nothing.
  • GitHub CLI, signed ingh auth status names your account.
  • Claude Codeclaude --version prints a version.

Take from this: four tools, one command each. If one prints nothing, fix it before step 1.

Remember: check the four tools first, and swap in your own version numbers.

Chapter 2 of 5, about 2 minutes

How do I prepare the release branch?

Before this: the four tools work and main is clean.

  1. Start a release branch in its own worktree.
    git worktree add .claude/worktrees/release-1.9.0 -b release/1.9.0 main
    cd .claude/worktrees/release-1.9.0

    Check: git worktree list shows the new folder on [release/1.9.0].

    If it fails: if the branch exists, drop -b release/1.9.0 main and name the branch last.

  2. Set the version in the manifest and the template.
    sed -i 's/1\.8\.1/1.9.0/' .claude-plugin/plugin.json skills/html-explainer/assets/template.html

    Check: git diff --stat shows 2 +- for both files.

    If it fails: any other count means a stray match. Undo it and edit both numbers by hand.

  3. List what shipped, then write the changelog section.
    git log --oneline v1.8.1..main

    Check: grep -n '^## ' CHANGELOG.md lists ## 1.9.0 above ## 1.8.1.

    If it fails: move the new section above the newest release.

  4. Find the template's hash for the upgrader.
    node skills/html-explainer/scripts/upgrade.mjs --hash skills/html-explainer/assets/template.html

    Check: 16 hex digits print. If KNOWN lists them, add nothing. If not, add 'digits': '1.9.0',.

    If it fails: run it from the worktree's top folder.

Take from this: after step 4 the branch holds the new version, the changelog and the hash.

Remember: a release branch starts in a worktree and ends with the version, the changelog and the hash.

Chapter 3 of 5, about 2 minutes

How do I check the release and open the pull request?

Before this: the branch holds the new version, the changelog and the hash.

  1. Bring the shipped pages to the new mark.
    node skills/html-explainer/scripts/upgrade.mjs skills/html-explainer/assets/gallery.html skills/html-explainer/examples skills/explainer-gather/examples docs/index.html docs/roadmap.html

    Check: every line says version mark set, upgraded or already current.

    If it fails: skipped: custom styles means hand edits. Paste the template's style and script in.

  2. Run both checks.
    node tools/check-repo.mjs
    claude plugin validate --strict .

    Check: they end with all checks passed and Validation passed, and both exit 0.

    If it fails: fix each FAIL line. A mark mismatch means step 2 missed a number.

  3. Commit, push and open the pull request.
    git add -A && git commit -m "chore(release): 1.9.0"
    git push -u origin release/1.9.0
    gh pr create --title "Release 1.9.0: public examples" --fill

    Check: gh pr checks --watch ends with check passing. The site job is skipped on a pull request.

    If it fails: gh pr checks --web opens the log. Fix the FAIL line here and push again.

Take from this: the checks run on your machine first, and CI runs the repository check again.

Remember: upgrade the shipped pages, run both checks, then open the pull request.

Chapter 4 of 5, about 2 minutes

How do I publish the release and update my copy?

Before this: the pull request is open and its check job passes.

  1. Merge the pull request and delete its branch.
    gh pr merge --merge
    git push origin --delete release/1.9.0

    Check: after git fetch, git log origin/main -1 --oneline names your pull request.

    If it fails: wait for the check job. If main moved, merge origin/main and push.

  2. Tag the merge and push the tag.
    git tag -a v1.9.0 -m "1.9.0: public examples" origin/main
    git push origin v1.9.0

    Check: git ls-remote --tags origin v1.9.0 lists the tag.

    If it fails: already exists means someone tagged it. Read git show v1.9.0 first.

  3. Create the GitHub release from the changelog.
    sed -n '/^## 1.9.0/,/^## 1.8.1/{//!p}' CHANGELOG.md > ../notes.md
    gh release create v1.9.0 --title "1.9.0: public examples" --notes-file ../notes.md

    Check: gh release view v1.9.0 prints the title and the 1.9.0 changelog text.

    If it fails: ../notes.md must hold only the 1.9.0 section.

  4. Update your own copy.
    claude plugin marketplace update html-explainer-skill
    claude plugin update html-explainer@html-explainer-skill

    Check: after a restart, claude plugin list shows Version: 1.9.0.

    If it fails: still old? Run both commands in this order, then restart.

Take from this: merge, tag and release are separate acts with separate checks.

Show how to confirm the site redeployed
gh run list --workflow ci --branch main --limit 1

The public site builds from main after the merge. The newest run should end in completed and success.

Remember: merge, tag, release, then update your own copy and restart.

Chapter 5 of 5, about 1 minute

What do I do when it goes wrong?

Before this: a release is a merge, a tag and a GitHub release, and each can be taken back.

  • Before the mergeClose the pull request and remove the worktree. Nothing reached main.
  • After the merge, before the tagRevert the merge in a new pull request, then run the checks again.
  • After the tag and the releaseDelete the release and its tag. Releases here are not locked.
  • After people have updatedYou cannot recall it. Fix forward with a patch release.

Take from this: the earlier you stop, the cheaper the way back. This repository has shipped fast fixes before: 1.8.1 followed 1.8.0 about three hours later.

Show the commands for each stage
# before the merge (run the second line from the main checkout)
gh pr close --comment "Not shipping"
git worktree remove .claude/worktrees/release-1.9.0

# after the merge, before the tag
git revert -m 1 <merge commit>

# after the tag and the release
gh release delete v1.9.0 --cleanup-tag --yes
git tag -d v1.9.0

A reader can stay on one release by adding its tag when they add the marketplace: ariasr47/html-explainer-skill#v1.8.1.

Remember: take it back as far as it got, and fix forward once people have updated.

Recap

  1. Check the four tools first, and swap in your own version numbers.
  2. A release branch starts in a worktree and ends with the version, the changelog and the hash.
  3. Upgrade the shipped pages, run both checks, then open the pull request.
  4. Merge, tag, release, then update your own copy and restart.
  5. Take it back as far as it got, and fix forward once people have updated.