Version bump
Version the Team plugin at land time (DEV-internal, not distributed): decide the SemVer level, compute the next free version against current `main`, update all five version strings, cut the `[Unreleased]` changelog body into a dated `## [X.Y.Z]` section, run the land-time consistency assertion, and commit `chore(version): X.Y.Z`. This is the Team-internal bumper; the generic runtime `/shipit` skill then pushes, waits for CI, and squash-merges. Use when landing a Team PR, or when the user asks to "bump the version" or "version this PR".From its SKILL.md
npx -y skills add bostonaholic/team --skill version-bumpAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 8 stars8 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
10.0 KB, ~2.6k tokens by cl100k_base, as published. Nobody here has run it
Version Bump — version a Team PR at land time
Follow
skills/progress-tracking/SKILL.md: this procedure has more than two steps — seed one todo item per step below before starting and mark each complete as you go.
This skill versions the Team plugin itself at land time. It is dev-only
(lives under .claude/, never distributed to plugin users). Tagging and the
GitHub release are not part of this procedure — release-on-merge.yml does
both automatically when the PR merges. Full policy:
docs/versioning.md.
The dev land process
Landing a Team PR is two steps, in order:
- Bump (this skill). Run
version-bumpagainst currentmain. It picks the level, assigns the next free version, and bumps the five version strings. It cuts the[Unreleased]changelog into a dated## [X.Y.Z]section, runs the land-time consistency assertion, and commitschore(version): X.Y.Z. - Land (the generic
/shipitskill). Run the distributed runtime/shipitskill to push the branch, wait for CI, and squash-merge.shipitis project-agnostic — it does no versioning. This skill is the Team-internal bumper it composes with.
Run this skill before /shipit, against the version of main you intend to
land onto.
The bump is conditional, not universal. Step 0 below decides if this PR
warrants a bump at all. Only PRs that change the distributed plugin bump. A
dev-only PR (CI, docs, tests, evals, .claude/ tooling) lands with no bump and
no changelog cut. Run step 0, see it say "no bump", and go straight to /shipit
with the plain conventional title.
Steps
0. Runtime-vs-dev gate — does this PR warrant a bump at all?
Run this before everything else. Most steps below only apply if it says yes.
The version, changelog, and GitHub release exist for plugin end users —
people who install Team and run /team. They are driven only by changes to the
distributed plugin. Contributor-facing / plugin-developer infrastructure does
not move the version, no matter what conventional-commit type it carries.
Using the Runtime vs. Development split in CLAUDE.md:
- Runtime (bump-worthy):
agents/,skills/,hooks/, and host manifest content —.claude-plugin/,.codex-plugin/,.agents/plugins/— meaning a real change to a manifest, not the bare"version"field. Every host's manifest ships to that host's end users, so a Codex-only manifest change is as much a runtime change as a Claude Code one. - Development (never bumps):
.github/,.claude/,docs/,tests/,evals/,package.json/bun.locktooling — everything that only validates or builds the plugin.
Check what this PR actually changed:
.github/scripts/version-bump-required.sh # HEAD_SHA/BASE_SHA from the PR; deterministic gate
# or, locally, just look:
git diff origin/main...HEAD --name-only
- No runtime files changed → DO NOT BUMP. Skip every step below. Leave the
version untouched, do not cut the changelog, and land with the plain
conventional title (
<type>: <subject>). Precedent:710d44c(CI),7d2e218(docs),0821129(evalsfeat:) all landed plain. Then go straight to/shipit. - Runtime files changed → continue to step 1.
This is a hard gate, not a judgment call. The same check runs deterministically
in CI, through .github/scripts/version-bump-required.sh, which
tests/version-bump-required.test.ts pins. It fails the PR if a dev-only
diff bumped, or a runtime diff did not.
1. Decide the bump level
Reached only when step 0 said a bump is warranted (the PR changed runtime files). The level question is never "does this bump?" — step 0 already answered that — only "how big is the bump?"
git log origin/main..HEAD --oneline
git diff origin/main...HEAD --stat
Pick the highest-impact runtime change in the PR:
- major — breaking change to the plugin's contract (commands, artifact formats, hook behavior).
- minor — new backward-compatible capability (
feat:). - patch — everything else (
fix:,docs:,chore:,refactor:,test:,ci:).
The conventional-commit type only picks the level. It never overrides step 0. A
ci:/test:/docs:/chore: commit that ships no runtime change never
reaches this table — it already stopped at step 0 with no bump.
State the chosen level and the reasoning. If genuinely ambiguous, ask.
2. Compute the next version
bash .claude/scripts/next-version.sh <level>
This prints bump(<default branch>'s version, level) — deterministic, a pure
function of the base and the level, with no open-PR scan. The base is read from
the remote's default branch (resolved through origin/HEAD, not a hardcoded
main). Under the land-time model the version is assigned against current main
and landing is serialized, so bump(main, level) is always free. A concurrent
race is handled by /shipit (rebase + recompute) and release-on-merge.yml's
duplicate-tag backstop.
3. Bump all five version strings
The version lives in five places across four files:
| File | Occurrences |
|---|---|
.claude-plugin/plugin.json | 1 (version) |
.claude-plugin/marketplace.json | 2 (metadata.version and plugins[0].version) |
.codex-plugin/plugin.json | 1 (version) |
package.json | 1 (version) |
Codex reads .codex-plugin/plugin.json in preference to the Claude manifest,
and shows the version it finds there. A stale one makes the same release look
like two different versions depending on the host.
Edit all four files, then prove it:
grep -rn '"version"' package.json .claude-plugin/plugin.json \
.claude-plugin/marketplace.json .codex-plugin/plugin.json
All five lines must show the new version. Zero may still show the old one.
4. Cut the changelog section
This moves the accumulated [Unreleased] body into a new dated section. It
is the inverse of release-on-merge.yml's awk extraction, because you write
the section the release workflow later reads. In CHANGELOG.md (Keep a Changelog
format, entry style per skills/changelog/SKILL.md):
- Move the entire
[Unreleased]body into a new## [X.Y.Z] - YYYY-MM-DD(today's date) section inserted directly below## [Unreleased]. Leave## [Unreleased]in place, now empty again. - Re-point the link-reference footer:
[Unreleased]compare base →vX.Y.Z...HEAD- Add
[X.Y.Z]: https://github.com/bostonaholic/team/compare/v<prev>...vX.Y.Z
This section becomes the GitHub release notes verbatim — write it for a reader
deciding if the upgrade is worth it. Any links must be absolute URLs: relative paths (e.g.
docs/versioning.md) render as dead links on the release page (see
skills/changelog/SKILL.md).
Empty-[Unreleased] edge case. A PR that reached this step passed step 0, so
it did change runtime files. An empty [Unreleased] on it means nobody wrote
the user-facing bullet.
Derive at least one bullet from the PR's runtime commits
(feat:/fix:/perf:/security: per skills/changelog/SKILL.md style). Never
write an empty section (release-on-merge.yml errors on empty release notes).
Empty [Unreleased] and no runtime change is not this case. That PR must
have stopped at step 0, with no bump and no changelog cut. Do not invent a
bullet to justify a bump that step 0 already declined. Go back and land plain.
5. Land-time consistency assertion
After the changelog cut and before committing, run the consistency check —
it must run after the cut (so the dated section exists to validate). This is
the in-tree replacement for the retired version-gate.yml:
bun test tests/version-consistency.test.ts
node -e "['.claude-plugin/plugin.json','.claude-plugin/marketplace.json','.codex-plugin/plugin.json','.agents/plugins/marketplace.json','package.json'].forEach(f=>JSON.parse(require('fs').readFileSync(f)));console.log('JSON OK')"
The tripwire asserts strict semver, that all five strings agree, and that the host manifests agree on the plugin and marketplace names. Additionally assert inline the released-section + footer-compare-link invariants (these hold only after the cut, so they live here, not in the tripwire):
V=$(jq -r .version .claude-plugin/plugin.json)
ESC=$(sed 's/\./\\./g' <<<"$V")
grep -qE "^## \[$ESC\] - [0-9]{4}-[0-9]{2}-[0-9]{2}$" CHANGELOG.md \
|| { echo "::error::no '## [$V] - YYYY-MM-DD' section — the cut did not land"; exit 1; }
grep -qE "^\[$ESC\]: https://" CHANGELOG.md \
|| { echo "::error::no footer compare link for $V"; exit 1; }
grep -q "\[Unreleased\]: https://github.com/bostonaholic/team/compare/v$V...HEAD" CHANGELOG.md \
|| { echo "::error::[Unreleased] footer does not compare from v$V"; exit 1; }
echo "OK: land-time consistency holds"
If any check fails, stop before committing and fix the cut.
6. Commit
Commit the bump as its own commit in the PR branch, for clean reverts:
git add .claude-plugin/plugin.json .claude-plugin/marketplace.json \
.codex-plugin/plugin.json package.json CHANGELOG.md
git commit -m "chore(version): X.Y.Z"
7. Title the PR
vX.Y.Z <type>: <subject> — e.g. v0.6.0 feat: add the shipit land skill. Set
it on the existing PR (gh pr edit --title). The PR title sync workflow
corrects drift, but it is a backstop — do not rely on it.
Then run /shipit (step 2 of the dev land process) to push, wait for CI, and
squash-merge.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.