agentsclimarketplace

Release

Skill iroha924/mumei/.claude/skills/release

Release a new version of the mumei repository. Invoke when the user gives an explicit release instruction ("release it", "/release", "patch release", "ship 0.2.0"). Takes no argument or "patch" / "minor" / "major" for a SemVer bump, or a direct version such as "0.2.0". Wraps any uncommitted changes into a single commit, pushes to main, watches the main CI run, and on green creates an annotated tag, pushes it, and creates the GitHub Release page (--generate-notes) in one pass. If CI fails, aborts, reports to the user, and prompts a rerun after the fix.From its SKILL.md

Install
npx -y skills add iroha924/mumei --skill release

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

15.2 KB, as published. Nobody here has run it

<!-- Role: release skill for mumei itself (repo-specific) Input: argument (defaults to patch; or major/minor/patch; or a direct version like 0.2.0) Output: plugin.json bump committed and pushed to main, tag pushed, CI watched, completion report Principles: - Side effects are large: trigger only on the explicit release wording in the description (prevents accidental invocation) - CI watching is synchronous via gh run watch --exit-status - The tag is created only after confirming the commit's CI state (green) - If the tag already exists, retag only after user confirmation - No CHANGELOG.md (git log + tags are the history of record) -->

Release (mumei release skill)

Releases a new version of the mumei repo. Starts only when the user gives an explicit release instruction (matched via the description; side effects are large, so suppress accidental invocation).

Argument interpretation

ArgumentMeaning
(omitted)patch bump
patchpatch bump (0.1.0 → 0.1.1)
minorminor bump (0.1.0 → 0.2.0)
majormajor bump (0.1.0 → 1.0.0)
0.2.0 / v0.2.0direct version

A direct version that matches an existing tag is treated as a retag (re-cut).

No-CHANGELOG policy

mumei does not maintain a CHANGELOG.md. When release notes are needed:

git log v<prev>..v<curr> --oneline

or use the diff auto-generated by the GitHub release UI (Conventional Commits subjects become the release notes as-is). This means:

  • less work in the release skill (no CHANGELOG editing step)
  • zero risk of "implementation and CHANGELOG drift apart"
  • git tags are the single source of truth for version history

The release skill therefore never edits a CHANGELOG. The only version-bump target is .claude-plugin/plugin.json.

Overall flow

main is protected (required status checks, required conversation resolution, linear history, no force push), but enforce_admins: false: the maintainer is a repo admin and can push directly to main. The release flow uses that path — one commit, pushed straight to main, gated by the commit → push → CI watch sequence below. A push to main runs only the codeql job of ci.yml (lint / lint-extra / bats are PR-only), so the task release:check pre-flight gate is the lint/test evidence for the release commit.

1. Parse the argument → determine the new version
2. Pre-flight checks (incl. task release:check)
3. Bump the version in plugin.json
4. Show the user the diff and confirm (approval covers the direct push to main)
5. Commit on main → push to origin main
6. Watch the main CI run (gh run watch --exit-status)
7. CI green → create annotated tag + push (the tag push triggers release.yml)
8. Create the GitHub Release page (gh release create --generate-notes)
8.5. Watch the release.yml workflow (tag-push trigger; SLSA / Sigstore / asset upload)
9. Final report
   CI failure → abort, ask the user to rerun after the fix

Step 1: Parse the argument

arg="${1:-patch}"
current="$(jq -r '.version' .claude-plugin/plugin.json)"
IFS='.' read -r major minor patch <<< "$current"

case "$arg" in
  patch) new="${major}.${minor}.$((patch + 1))" ;;
  minor) new="${major}.$((minor + 1)).0" ;;
  major) new="$((major + 1)).0.0" ;;
  v*)    new="${arg#v}" ;;
  [0-9]*.[0-9]*.[0-9]*) new="$arg" ;;
  *)     echo "Invalid argument: $arg"; exit 1 ;;
esac

echo "Current: $current"
echo "New:     $new"

Step 2: Pre-flight checks

Run in order. Abort on the first failure:

  1. Branch check: git rev-parse --abbrev-ref HEAD is main.

  2. origin sync: git fetch origin main && git rev-list --count HEAD..origin/main is 0 (not behind).

  3. gh CLI available: command -v gh. Required for CI watching. If missing, ask the user to install it and abort.

  4. Local sanity gate: task release:check (lint + test + cron alignment) exits clean. A push to main does not run the lint / bats CI jobs, so this local gate is mandatory before the release commit.

  5. Existing tag check:

    if git rev-parse "v$new" >/dev/null 2>&1; then
      existing_sha="$(git rev-parse "v$new")"
      head_sha="$(git rev-parse HEAD)"
      if [[ "$existing_sha" == "$head_sha" ]]; then
        echo "Tag v$new already points at HEAD. No retag needed."
        retag_only=1
      else
        echo "WARN: Tag v$new exists ($existing_sha) and points at a different commit."
        echo "Re-cut it? (yes/no): "
        # set the retag flag after user confirmation
      fi
    fi
    

Step 3: Version bump (plugin.json)

Unless retag_only, update only the version field of .claude-plugin/plugin.json:

jq --arg v "$new" '.version = $v' .claude-plugin/plugin.json > .claude-plugin/plugin.json.tmp \
  && mv .claude-plugin/plugin.json.tmp .claude-plugin/plugin.json

Do not touch CHANGELOG.md (mumei has none; release notes are auto-generated from git log by gh release create --generate-notes). CI badges track the latest GitHub Actions run, so no manual update. The README carries no version string either (badge / Status line are not present).

Step 4: Approval

git status --short
git diff --stat HEAD

Show these to the user and ask: "Create the release commit with this content and push it to main? (yes / no)". Make it explicit that any uncommitted changes will all be included in the commit, and that approval also authorizes the direct push to main (an AI push to main always requires user confirmation).

If no, abort.

Step 5: Commit on main and push

git add -A
git commit -m "chore: release v${new}"
git push origin main

Follow the global rule (~/.claude/rules/git-commit.md):

  • single-line subject
  • Conventional Commits (chore:)
  • imperative mood
  • no Co-Authored-By
  • never --no-verify

Step 6: Watch the main CI run

The push triggers ci.yml on main. Find the run for the release commit and wait synchronously:

head_sha="$(git rev-parse HEAD)"
main_run_id=""
for _ in $(seq 1 15); do
  main_run_id="$(gh run list --workflow ci.yml --branch main \
    --json databaseId,headSha --jq ".[] | select(.headSha == \"${head_sha}\") | .databaseId" | head -n1)"
  [[ -n "$main_run_id" ]] && break
  sleep 6
done

if [[ -z "$main_run_id" ]]; then
  echo "WARN: no ci.yml run appeared for ${head_sha} within 90 seconds."
  echo "Check manually: gh run list --workflow ci.yml --branch main"
  exit 1
fi

echo "Watching main CI run ${main_run_id}..."
if ! gh run watch "$main_run_id" --exit-status; then
  echo "× main CI failed. Aborting the release."
  echo "Failure log: gh run view ${main_run_id} --log-failed"
  echo "See the Recovery section of this skill."
  exit 1
fi
echo "main CI passed."

If CI fails, do not create the tag. Abort and report to the user.

Step 7: Create and push the annotated tag

Tag the commit whose CI just passed:

# Delete the existing tag if present (retag scenario)
if git rev-parse "v$new" >/dev/null 2>&1; then
  git tag -d "v$new"
  git push origin ":refs/tags/v$new" 2>/dev/null || true
fi

# Annotated tag (unsigned). git commit / tag signing is disabled
# (the Sigstore-signed badge in the README
# refers to the tarball asset signature, not a git tag signature).
git tag -a "v$new" -m "Release ${new}"
git push origin "v$new"

Step 8: Create the GitHub Release page

Pushing the tag alone leaves the GitHub Releases tab empty. Create the release page so watcher notifications and the "Latest release" badge appear:

release_url=""
if release_url="$(gh release create "v$new" --title "v$new" --generate-notes 2>&1)"; then
  : # success; release_url holds the URL
else
  # A failed release-page creation (permissions, network, pre-existing release,
  # etc.) is a warning. The tag is already pushed (Step 7 done), so the release
  # stands; the page can be created manually later. Log the failure and do not
  # stop the process.
  echo "WARN: failed to create the release page. Create manually: gh release create v$new --title 'v$new' --generate-notes"
  echo "  Failure log: $release_url"
  release_url=""
fi

If release_url stays empty, it signals the failed page creation in the Step 9 report.

--generate-notes auto-classifies the commits since the previous tag by their Conventional Commits subjects. Add --draft to publish later after editing (normally unnecessary: generate, then edit in the GitHub UI if needed).

In the retag scenario, gh release create fails when a release already exists. To retag, delete it first with gh release delete "v$new" --yes and recreate (this skill never does that automatically; the user decides).

Step 8.5: Watch release.yml

When Step 7's git push origin "v$new" lands, release.yml starts on the tag-push trigger (on: push: tags: ['v*']) and runs SLSA / Sigstore signing / provenance generation / tarball asset upload. Skipping this watch makes the completion report a lie — even with the tag pushed and the release page created, a failed release.yml means assets / signatures are incomplete. Wait for the workflow with this pattern:

push_tag_sha="$(git rev-parse "v${new}^{commit}")"
release_run_id=""
for _ in $(seq 1 15); do
  release_run_id="$(gh run list --workflow release.yml \
    --json databaseId,headSha --jq ".[] | select(.headSha == \"${push_tag_sha}\") | .databaseId" | head -n1)"
  [[ -n "$release_run_id" ]] && break
  sleep 6
done

if [[ -n "$release_run_id" ]]; then
  echo "Release workflow: https://github.com/iroha924/mumei/actions/runs/${release_run_id}"
  gh run watch "$release_run_id" --exit-status \
    || { echo "× release.yml failed. The tag is pushed, but assets / signatures may be
incomplete. Inspect with gh run view ${release_run_id} --log-failed."; }
else
  echo "WARN: no release.yml run was registered within 90 seconds (tag_sha=${push_tag_sha}).
Check manually with gh run list --workflow release.yml."
fi

Ordering note: Step 8 (gh release create) and Step 8.5 are swappable (the release page and the release.yml workflow are independent). The skill places Step 8 first so the release page URL is reliably available for the Step 9 report, and keeps the release.yml watch as the final gate in Step 8.5.

Step 9: Final report

✓ Released v${new}
  - commit:       $(git rev-parse HEAD)
  - tag:          v${new} (annotated)
  - main CI:      passed (run ${main_run_id})
  - release.yml:  passed (run ${release_run_id})
  - release page: ${release_url:-"(creation failed; create manually)"}
  - changes:      git log v<prev>..v${new} --oneline

Error handling

Abort immediately and report state when any step fails:

  • Pre-flight failure → state which condition is unsatisfied. Propose a fix.
  • jq failure (malformed plugin.json) → ask for a manual repair and abort.
  • Push rejected → main may have moved (fetch and rerun the pre-flight checks), or branch protection may have changed; inspect gh api .../branches/main/protection for the current rules.
  • main CI failure → see Recovery below.

Recovery: main CI failed after the push (Step 6)

When Step 6 exits on a failed run, this state remains:

  • the release commit is already on main
  • no tag has been created

Recovery steps:

  1. Identify the cause: gh run view <run_id> --log-failed.

  2. Transient (network etc.): Re-run failed jobs in the GitHub UI or gh run rerun --failed <run_id>. On green, resume at Step 7 (tag).

  3. Real bug: fix forward through a PR — branch from main, commit the fix, push, gh pr create (follow .github/PULL_REQUEST_TEMPLATE.md). The PR runs the full required status checks: lint, lint-extra, bats (ubuntu-latest), bats (macos-latest), codeql (actions), scan / osv-scan, mutable-tag-guard, pr-target-guard. .github/workflows/review.yml (a caller workflow that delegates to review-reusable.yml, running claude-code-action) reviews the PR and posts review threads; branch protection's required_conversation_resolution: true blocks merge while any thread is unresolved. List the threads with:

    gh api graphql -f query="
      query {
        repository(owner: \"iroha924\", name: \"mumei\") {
          pullRequest(number: ${pr_number}) {
            reviewThreads(first: 50) {
              nodes { id isResolved path line comments(first:5){nodes{author{login} body}} }
            }
          }
        }
      }"
    

    Triage each finding: valid → push a fix commit; invalid → reply with the reason and close via the resolveReviewThread mutation. When CI is green and all threads are resolved, ask the user whether to merge; on approval the AI runs gh pr merge --squash (gh pr * is allowed in .claude/settings.local.json; only local git merge is denied). Then rerun /release <X.Y.Z> from the updated main.

  4. Manual fallback (cutting the release without the skill):

    # bump plugin.json → commit on main → push
    git push origin main
    # after the main CI run is green:
    git tag -a "v${new}" -m "Release ${new}"
    git push origin "v${new}"
    gh release create "v${new}" --title "v${new}" --generate-notes
    

What this skill does not do

  • Never pushes to main without the user's explicit approval in Step 4. Branch protection permits the admin push (enforce_admins: false), but user confirmation is mandatory.
  • Never merges a recovery PR without user confirmation. gh pr merge --squash is permitted (.claude/settings.local.json allows gh pr *; only local git merge is denied), but only after CI is green, review threads are resolved, and the user approves.
  • Never --force-pushes to main. A retag is the two-step delete (git push origin :refs/tags/<tag>) followed by pushing the new tag.
  • Never skips hooks with --no-verify (global rule).
  • Never edits CHANGELOG.md (mumei does not maintain one).
  • Never edits the README CI badges (they track the latest Actions run automatically); the README carries no version string to bump.
  • Never creates the release page as a draft. Publish with --generate-notes; drafts are rarely needed and the user can edit in the GitHub UI afterwards, so keep the complexity down. If the user wants a draft, they run gh release create --draft manually.
  • Never deletes an existing release automatically on retag. gh release delete is destructive, so the skill does not run it; Step 8 fails and the user decides.
  • Never releases multiple versions at once. One /release, one version.
  • Never silently includes uncommitted changes. Step 4 asks for explicit approval.

Keep looking

Skills are one crate of 627,801. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.