Ship — one-command neo-plugin release
Runs the full release chain for this repo end to end: bump → commit → tag →
push → GitHub release. It codifies the "Versioning and releases" flow in the
root CLAUDE.md; that section is the source of truth — this skill is the doer.
Argument: /ship <major|minor|patch> selects the version bump.
/ship minor→ pack everything, bump the minor version, ship.- No argument → infer the bump from the change type (see step 2) and show it in the preview for the user to confirm.
Safety model (fixed): the skill drafts everything, does the local commit + tag, then STOPS for one confirmation before the irreversible push + release. Local commit/tag are cheap to amend or delete; push and a public GitHub release are not.
Non-negotiables
.claude-plugin/plugin.jsonis the canonical version source. Sync the same version to.plugin/plugin.jsonin the same bump.marketplace.jsoncarries no version field — never touch it for versioning.- Semver by change type:
patch= fix/docs,minor= new skill/feature,major= breaking. - One annotated tag per bump, created after the commit:
v<version>(v-prefix), messageneo <version> — <headline>. - Release title is the version only (
v<version>); the<headline>goes in the notes body, never the title. - Commit to the current branch (this repo ships from
main— do not open a feature branch). - Every commit message ends with a
Co-Authored-By:trailer naming the model that wrote it — the one you are running as right now, taken from your own session, never copied from this file or from an earlier commit:Co-Authored-By: Claude <your model name> <noreply@anthropic.com>. A hardcoded name outlives the model and writes a false author into history that a force-push is the only way to correct. If your session does not tell you which model you are, drop the name rather than guess one:Co-Authored-By: Claude <noreply@anthropic.com>
Workflow
1. Preconditions
git rev-parse --show-toplevel # must be the neo-plugin repo root
git branch --show-current # the branch we commit + push
gh auth status # gh must be authenticated
git status --porcelain # must be NON-empty — else "nothing to ship", stop
2. Determine the new version
Read the current version and compute the next one from the bump type. When no
bump arg was given, infer it: any breaking change → major; a new skill or
feature → minor; otherwise (fix/docs/refactor) → patch.
cur=$(python3 -c "import json;print(json.load(open('.claude-plugin/plugin.json'))['version'])")
IFS=. read -r MA MI PA <<< "$cur"
case "$BUMP" in
major) next="$((MA+1)).0.0";;
minor) next="$MA.$((MI+1)).0";;
patch) next="$MA.$MI.$((PA+1))";;
esac
echo "$cur -> $next"
3. Stage + draft (no commit yet)
Bump the
versionfield in.claude-plugin/plugin.jsonand sync the same value into.plugin/plugin.json(Edit the"version": "<cur>"line to<next>in both).Pack everything:
git add -A(all changes incl. untracked, plus the manifest).Draft the commit message — Conventional Commits (
type(scope): subject), a body saying what changed and why, derived fromgit diff --cached. End with the Co-Authored-By trailer. Write it to a temp file (your scratchpad).Draft the release notes —
### neo <next> — <headline>, then the sections that apply (Added/Changed/Fixed/Removed/Notes), matching the shape of recent releases. Check the last one first:gh release view "$(git tag --sort=-v:refname | head -1)"Write the notes to a temp file.
4. Preview → local commit + tag → STOP
Show the user, in one message:
cur -> nextversion and the inferred/selected bump type,git diff --cached --stat(what is being packed),- the drafted commit message,
- the drafted release notes.
Then do the local steps only:
git commit -F <commit-msg-file>
git tag -a "v$next" -m "neo $next — <headline>"
Stop here. Ask the user to confirm the push + release (a single yes). Do NOT
run step 5 until they reply. If they want changes: git tag -d v$next, amend the
commit or edit the notes, re-preview.
5. On confirm — push + publish
git push origin "$(git branch --show-current)" && git push origin "v$next"
gh release create "v$next" --title "v$next" --notes-file <notes-file> --latest
6. Report
Version, commit SHA, tag, and the release URL. Update any relevant auto-memory release-status line from "NOT committed" to "SHIPPED v".
Red flags — stop and fix, don't ship through them
- Working tree is clean → nothing to ship; do not cut an empty release.
gh auth statusfails → resolve auth first; a half-done chain (pushed, no release) is worse than not starting.- Tempted to edit
marketplace.jsonfor the version → don't; it has no version field. - Release title contains the headline → wrong; title is
v<version>only. - Pushing before the user confirmed the preview → never; the confirm gate is the point.