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.json is the canonical version source. Sync the same
version to .plugin/plugin.json, .grok-plugin/plugin.json, root plugin.json,
and package.json in the same bump. Marketplace indexes carry no version
field — never touch them 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),
message neo <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 version field in .claude-plugin/plugin.json and sync the same
value into .plugin/plugin.json, .grok-plugin/plugin.json, root
plugin.json, and package.json (Edit the "version": "<cur>" line to
<next> in all five).
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 from git 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 -> next version 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 status fails → resolve auth first; a half-done chain (pushed, no
release) is worse than not starting.
- Tempted to edit a
marketplace.json for the version → don't; those indexes have 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.
1---2name: ship3description: One-command release for the neo-plugin — bump the plugin version, pack all working-tree changes into a single commit, tag, push, and publish a GitHub release, in that order. Takes the bump type as an argument (major | minor | patch); infers it from the diff when omitted. Use when the user says "/ship", "/ship minor", "/ship patch", "ship it", "cut a release", "bump version + commit + push + release", "ออก release", "bump + push + release ทีเดียว".4---56# Ship — one-command neo-plugin release78Runs the full release chain for this repo end to end: **bump → commit → tag →9push → GitHub release**. It codifies the "Versioning and releases" flow in the10root `CLAUDE.md`; that section is the source of truth — this skill is the doer.1112**Argument:** `/ship <major|minor|patch>` selects the version bump.1314- `/ship minor` → pack everything, bump the minor version, ship.15- No argument → **infer** the bump from the change type (see step 2) and show it16 in the preview for the user to confirm.1718**Safety model (fixed):** the skill drafts everything, does the **local** commit +19tag, then **STOPS for one confirmation before the irreversible push + release**.20Local commit/tag are cheap to amend or delete; push and a public GitHub release21are not.2223## Non-negotiables2425- **`.claude-plugin/plugin.json` is the canonical version source.** Sync the same26 version to `.plugin/plugin.json`, `.grok-plugin/plugin.json`, root `plugin.json`,27 and `package.json` in the same bump. Marketplace indexes carry no version28 field — never touch them for versioning.29- **Semver by change type:** `patch` = fix/docs, `minor` = new skill/feature,30 `major` = breaking.31- **One annotated tag per bump, created after the commit:** `v<version>` (v-prefix),32 message `neo <version> — <headline>`.33- **Release title is the version only** (`v<version>`); the `<headline>` goes in the34 notes body, never the title.35- **Commit to the current branch** (this repo ships from `main` — do not open a36 feature branch).37- Every commit message ends with a `Co-Authored-By:` trailer naming **the model that38 wrote it** — the one you are running as right now, taken from your own session,39 never copied from this file or from an earlier commit:40 `Co-Authored-By: Claude <your model name> <noreply@anthropic.com>`.41 A hardcoded name outlives the model and writes a false author into history that42 a force-push is the only way to correct. If your session does not tell you which43 model you are, drop the name rather than guess one:44 `Co-Authored-By: Claude <noreply@anthropic.com>`4546## Workflow4748### 1. Preconditions4950```bash51git rev-parse --show-toplevel # must be the neo-plugin repo root52git branch --show-current # the branch we commit + push53gh auth status # gh must be authenticated54git status --porcelain # must be NON-empty — else "nothing to ship", stop55```5657### 2. Determine the new version5859Read the current version and compute the next one from the bump type. When no60bump arg was given, infer it: any breaking change → `major`; a new skill or61feature → `minor`; otherwise (fix/docs/refactor) → `patch`.6263```bash64cur=$(python3 -c "import json;print(json.load(open('.claude-plugin/plugin.json'))['version'])")65IFS=. read -r MA MI PA <<< "$cur"66case "$BUMP" in67 major) next="$((MA+1)).0.0";;68 minor) next="$MA.$((MI+1)).0";;69 patch) next="$MA.$MI.$((PA+1))";;70esac71echo "$cur -> $next"72```7374### 3. Stage + draft (no commit yet)75761. Bump the `version` field in `.claude-plugin/plugin.json` and sync the same77 value into `.plugin/plugin.json`, `.grok-plugin/plugin.json`, root78 `plugin.json`, and `package.json` (Edit the `"version": "<cur>"` line to79 `<next>` in all five).802. Pack everything: `git add -A` (all changes incl. untracked, plus the manifest).813. Draft the **commit message** — Conventional Commits (`type(scope): subject`),82 a body saying what changed and why, derived from `git diff --cached`. End with83 the Co-Authored-By trailer. Write it to a temp file (your scratchpad).844. Draft the **release notes** — `### neo <next> — <headline>`, then the sections85 that apply (`Added` / `Changed` / `Fixed` / `Removed` / `Notes`), matching the86 shape of recent releases. Check the last one first:8788 ```bash89 gh release view "$(git tag --sort=-v:refname | head -1)"90 ```9192 Write the notes to a temp file.9394### 4. Preview → local commit + tag → STOP9596Show the user, in one message:9798- `cur -> next` version and the inferred/selected bump type,99- `git diff --cached --stat` (what is being packed),100- the drafted commit message,101- the drafted release notes.102103Then do the **local** steps only:104105```bash106git commit -F <commit-msg-file>107git tag -a "v$next" -m "neo $next — <headline>"108```109110**Stop here.** Ask the user to confirm the push + release (a single yes). Do NOT111run step 5 until they reply. If they want changes: `git tag -d v$next`, amend the112commit or edit the notes, re-preview.113114### 5. On confirm — push + publish115116```bash117git push origin "$(git branch --show-current)" && git push origin "v$next"118gh release create "v$next" --title "v$next" --notes-file <notes-file> --latest119```120121### 6. Report122123Version, commit SHA, tag, and the release URL. Update any relevant auto-memory124release-status line from "NOT committed" to "SHIPPED v<next>".125126## Red flags — stop and fix, don't ship through them127128- Working tree is clean → nothing to ship; do not cut an empty release.129- `gh auth status` fails → resolve auth first; a half-done chain (pushed, no130 release) is worse than not starting.131- Tempted to edit a `marketplace.json` for the version → don't; those indexes have no version field.132- Release **title** contains the headline → wrong; title is `v<version>` only.133- Pushing before the user confirmed the preview → never; the confirm gate is the point.