release
CONTRIBUTOR TOOL - Cut a plugin release: bump plugin.json version, finalize CHANGELOG, update README if needed, gate on make ci, commit, tag vX.Y.Z, and create the GitHub release. Use when shipping a new plugin version. NOT distributed.
Install
npx skills add https://github.com/oliver-kriska/claude-elixir-phoenix/tree/main/.claude/skills/release
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install oliver-kriska-claude-elixir-phoenix@llmmart
git clone https://github.com/oliver-kriska/claude-elixir-phoenix.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole oliver-kriska/claude-elixir-phoenix collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Plugin Release
Cuts a versioned release of the Elixir/Phoenix plugin. Drives the full
checklist from CLAUDE.md (Release + Versioning) so every release is
consistent. Contributor tooling — not shipped in the plugin.
Iron Laws — Never Violate These
- NEVER release on a red
make ci— the gate runs BEFORE committing. No green, no release. - FOUR TAGS PER RELEASE —
vX.Y.Zplusphx--vX.Y.Z,ecto--vX.Y.Z,lv--vX.Y.Zfromclaude plugin tag(what dependency ranges resolve against). Tag only the release commit. - THREE NUMBERS MUST MATCH —
plugin.jsonversion == CHANGELOG heading == git tag (vX.Y.Z). Verify before pushing. - CONFIRM BEFORE PUBLISHING — pushing the tag and
gh release createare outward-facing and hard to reverse. Stop and confirm with the user; show exactly what will be pushed/published first. - USERS ONLY UPDATE ON A
plugin.jsonBUMP — never ship CHANGELOG/code changes without bumping the version, or installed users get nothing (cache). - ALWAYS leave a fresh empty
## [Unreleased]— one[Unreleased]becomes one version heading; re-add an empty one on top. - NEVER force-push —
git push --forceis hook-blocked here. If history needs rewriting, the user runs it via!. - EVERY release body links the docs site — append the
https://phxagents.devfooter. Releases are this project's one measured promotion lever (v3.0.1: 51 → 120 cloners in a day). - UPGRADE-BREAKING RELEASES LEAD WITH THE WARNING — if users must do anything beyond
/plugin update, the release body opens with a> [!WARNING]block carrying the exact commands (see #135).
Step 0: Preconditions
- On
main, working tree clean except intended release files. If feature work is uncommitted, commit it first. - Determine version. Run
git describe --tags --abbrev=0 --match 'v*'FIRST (the annotatedphx--/ecto--/lv--tags would win otherwise) — the last released tag is the bump base, NOTplugin.json(which may carry an unreleased phased bump). Ifplugin.jsonis already ahead of the tag, apply the consolidation check below before picking a number. - Read current
plugins/elixir-phoenix/.claude-plugin/plugin.json. Pick bump from## [Unreleased]contents:- MAJOR — breaking change (removed command, workflow redesign)
- MINOR — new skill / agent / command / hook
- PATCH — bug fix, doc/reference update, description tweak
- Consolidation check (per memory): if several phased branch bumps never released, collapse to ONE bump from the last released tag — don't stack intermediate versions.
Step 1: Bump the version — five files by hand, two generated
A partial bump does not just cost users the update; it fails
scripts/tests/test_codex.py, which asserts the Codex manifest matches canonical.
Set "version" to X.Y.Z in:
plugins/elixir-phoenix/.claude-plugin/plugin.json— canonicalplugins/ecto/.claude-plugin/plugin.jsonplugins/lv/.claude-plugin/plugin.jsonpackage.json— Pi package metadata, tracks the plugin version since v3.0.0package-lock.json— runnpm install --package-lock-only, never hand-edit (an unrelated dependency can share the old version string)
Then regenerate the two templated manifests and bless their digests:
make generated-skills-sync # updates targets/codex + targets/pi manifests
make generated-skills-snapshots # re-bless after reviewing the diff
Confirm every file agrees before moving on:
grep -rn '"version"' plugins/*/.claude-plugin/plugin.json package.json \
targets/codex/.codex-plugin/plugin.json targets/pi/package.json
(Often already bumped during the feature work — confirm it matches the target.)
Step 2: Finalize CHANGELOG
In CHANGELOG.md:
- Rename
## [Unreleased]→## [X.Y.Z] - YYYY-MM-DD(today's date). - Insert a fresh empty section on top (see
${CLAUDE_SKILL_DIR}/references/templates.md). - Optionally add a one-line summary under the new heading (past releases do).
Step 3: README + intro (only if needed)
- Update
README.mdONLY if counts/version callouts changed: skill count, agent count (grep -nE "[0-9]+ (skills|agents|specialist)" README.md), or a version banner. A pure doc/reference PATCH usually needs no README change — verify, don't assume. - Check
plugins/elixir-phoenix/skills/intro/references/tutorial-content.mdcheat sheet if commands/skills/agents were added, removed, or renamed.
Step 4: Gate on make ci
Run make ci (lint + test + validate + eval-all). Must be green. If lint trips on
untracked non-source dirs (e.g. social/, .rtk/), that is not a code failure — exclude
them, don't ship around real failures. See ${CLAUDE_SKILL_DIR}/references/templates.md.
Step 5: Commit
git add CHANGELOG.md plugins/elixir-phoenix/.claude-plugin/plugin.json # + README if touched
git commit # message below
Commit subject (matches history): Release vX.Y.Z — <short summary>
End the message with the Co-Authored-By trailer (see CLAUDE.md).
Step 6: Tag
After the release commit, tag each plugin. claude plugin tag validates the
plugin, confirms plugin.json matches the marketplace entry, and refuses a
dirty tree or an existing tag — so a failure here means the release commit is
wrong, not the tag. Run it with --dry-run first if unsure.
for p in elixir-phoenix ecto lv; do claude plugin tag plugins/$p || break; done
git tag vX.Y.Z
git tag --list 'v*' '*--v*' --points-at HEAD # expect 4 tags
Step 7: CONFIRM, then publish (outward-facing)
Show the user the pending commit, tag, and release notes. On confirmation:
git push origin main
git push origin vX.Y.Z phx--vX.Y.Z ecto--vX.Y.Z lv--vX.Y.Z
gh release create vX.Y.Z --title "vX.Y.Z — <summary>" --notes-file <changelog-section>
Use the new CHANGELOG section as release notes (extract it to a temp file or --notes),
then prepend any upgrade warning (Iron Law 9) and append the docs footer before
publishing — a release body is read at the moment someone decides whether to install:
---
Docs, install guides, and the runtime compatibility matrix: <https://phxagents.dev>
See ${CLAUDE_SKILL_DIR}/references/templates.md for the one-line printf that
appends it to the extracted notes.
Step 8: Verify
gh release view vX.Y.Z
git describe --tags --abbrev=0 --match 'v*' # should print vX.Y.Z
Confirm to the user: released, tag pushed, GitHub release live.
Reference
${CLAUDE_SKILL_DIR}/references/templates.md— CHANGELOG/release-notes templates, gate snippets, gotchas
Files (claude-elixir-phoenix)
-
references
-
templates.md 5.1 KB
# Release Templates & Gotchas ## CHANGELOG: before → after **Before** (work accumulates here): ```markdown ## [Unreleased] ### Added - Feature X ... ### Fixed - Bug Y ... ``` **After** (finalize for release `X.Y.Z` on date `YYYY-MM-DD`): ```markdown ## [Unreleased] ### Added ### Changed ### Fixed ## [X.Y.Z] - YYYY-MM-DD <optional one-line summary of the release> ### Added - Feature X ... ### Fixed - Bug Y ... ``` Keep the empty `## [Unreleased]` scaffold on top so the next cycle has a home. ## Release notes for `gh` Use the new version's CHANGELOG section verbatim. Extract it to a temp file: ```bash # pull the section between "## [X.Y.Z]" and the next "## [" awk '/^## \[X\.Y\.Z\]/{f=1} f&&/^## \[/&&!/X\.Y\.Z/{exit} f' CHANGELOG.md > /tmp/relnotes.md # always append the docs footer — the release body is read at install-decision time printf '\n---\n\nDocs, install guides, and the runtime compatibility matrix: <https://phxagents.dev>\n' \ >> /tmp/relnotes.md gh release create vX.Y.Z --title "vX.Y.Z — <summary>" --notes-file /tmp/relnotes.md ``` ## Upgrade warning block (Iron Law 9) When the release requires anything beyond `/plugin update` — a new dependency, a renamed manifest, a manual migration — **prepend** this block so it is the first thing on the release page, above the changelog body: ```bash cat > /tmp/relnotes.md <<'EOF' > [!WARNING] > **Upgrading from vN.x requires these commands in this order.** <one line on > what breaks otherwise, in user-visible terms.> > > ```bash > <exact commands> > ``` EOF awk '/^## \[X\.Y\.Z\]/{f=1} f&&/^## \[/&&!/X\.Y\.Z/{exit} f' CHANGELOG.md >> /tmp/relnotes.md ``` State the blast radius in what the user loses, not in mechanism. "The plugin fails to load — all 36 `/phx:*` commands disappear" lands; "enters a missing-dependency state" does not. v3.0.0 used the second phrasing, buried in a `### Changed` bullet, and users still upgraded into a broken install (issue #135). Title format matches history: `vX.Y.Z — <short summary>` (em dash). **The docs footer is not optional.** A release body is read at the exact moment someone is deciding whether to install, and releases are the one promotion lever in this project with a measured effect: v3.0.1 drove unique cloners 51 → 120 in one day (2.4x), decaying to baseline over ~4 days. The repo is also the higher-traffic discovery surface — Google sends ~4x more repo visitors than phxagents.dev does — so every release body should point back to the docs site. ## Commit message shape (matches `git log`) ``` Release vX.Y.Z — <short summary> <optional body: what changed, why it's this bump level> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> ``` Release commits historically touch only `CHANGELOG.md` + `plugin.json` (+ `README.md` when counts/banners change). Keep unrelated files out. ## `make ci` gate notes `make ci` = `lint test validate eval-all`. The lint glob is `**/*.md` minus a fixed `--ignore` list. Untracked non-source dirs (`social/`, `.rtk/`, scratch) are NOT plugin docs — if they trip lint, add them to the `--ignore` list in the `lint` / `lint-fix` Makefile targets rather than editing the promo/cache files or shipping with a red gate. That fix is a standalone `chore(lint)` commit, not part of the release commit. If only specific changed files matter, lint them directly to confirm clean: ```bash npx markdownlint CHANGELOG.md README.md plugins/.../changed.md ``` ## Gotchas - **Each release gets four tags.** `git tag vX.Y.Z` carries the GitHub release; `claude plugin tag plugins/<name>` (verified on CC 2.1.284 with this marketplace layout) creates `phx--vX.Y.Z`, `ecto--vX.Y.Z`, `lv--vX.Y.Z` — the scheme dependency version ranges (`^3.1`) resolve against. Push all four together. - **Users only get updates when `plugin.json` version changes** (install cache). CHANGELOG/code changes alone are invisible to installed users. - **The version lives in seven files, not one.** Five by hand (`plugins/{elixir-phoenix,ecto,lv}/.claude-plugin/plugin.json`, `package.json`, `package-lock.json`) and two generated from canonical (`targets/codex/.codex-plugin/plugin.json`, `targets/pi/package.json`, refreshed by `make generated-skills-sync`). A partial bump fails `scripts/tests/test_codex.py`, which asserts the Codex manifest matches canonical — caught in the v3.0.1 release. Regenerate `package-lock.json` with `npm install --package-lock-only`; a hand-edit can hit an unrelated dependency that happens to share the old version string. - **Version consolidation**: phased per-branch bumps that never released should collapse into ONE bump measured from the last released tag — don't release a chain of intermediate patch versions. - **Force-push is hook-blocked.** `block-dangerous-ops.sh` rejects `git push --force`. If you truly need it, the user runs it via `!`. - **Three-way match**: `plugin.json` version, the CHANGELOG `## [X.Y.Z]` heading, and the `vX.Y.Z` tag must all agree. A mismatch ships a confusing release. - **Tag the release commit**, not an earlier one: create the tag AFTER the `Release vX.Y.Z` commit so `git describe` resolves correctly.
-
-
SKILL.md 6.9 KB
--- name: release description: | CONTRIBUTOR TOOL - Cut a plugin release: bump plugin.json version, finalize CHANGELOG, update README if needed, gate on make ci, commit, tag vX.Y.Z, and create the GitHub release. Use when shipping a new plugin version. NOT distributed. argument-hint: "[patch|minor|major] | [X.Y.Z]" effort: medium --- # Plugin Release Cuts a versioned release of the Elixir/Phoenix plugin. Drives the full checklist from `CLAUDE.md` (Release + Versioning) so every release is consistent. **Contributor tooling — not shipped in the plugin.** ## Iron Laws — Never Violate These 1. **NEVER release on a red `make ci`** — the gate runs BEFORE committing. No green, no release. 2. **FOUR TAGS PER RELEASE** — `vX.Y.Z` plus `phx--vX.Y.Z`, `ecto--vX.Y.Z`, `lv--vX.Y.Z` from `claude plugin tag` (what dependency ranges resolve against). Tag only the release commit. 3. **THREE NUMBERS MUST MATCH** — `plugin.json` version == CHANGELOG heading == git tag (`vX.Y.Z`). Verify before pushing. 4. **CONFIRM BEFORE PUBLISHING** — pushing the tag and `gh release create` are outward-facing and hard to reverse. Stop and confirm with the user; show exactly what will be pushed/published first. 5. **USERS ONLY UPDATE ON A `plugin.json` BUMP** — never ship CHANGELOG/code changes without bumping the version, or installed users get nothing (cache). 6. **ALWAYS leave a fresh empty `## [Unreleased]`** — one `[Unreleased]` becomes one version heading; re-add an empty one on top. 7. **NEVER force-push** — `git push --force` is hook-blocked here. If history needs rewriting, the user runs it via `!`. 8. **EVERY release body links the docs site** — append the `https://phxagents.dev` footer. Releases are this project's one measured promotion lever (v3.0.1: 51 → 120 cloners in a day). 9. **UPGRADE-BREAKING RELEASES LEAD WITH THE WARNING** — if users must do anything beyond `/plugin update`, the release body opens with a `> [!WARNING]` block carrying the exact commands (see #135). ## Step 0: Preconditions - On `main`, working tree clean except intended release files. If feature work is uncommitted, commit it first. - Determine version. Run `git describe --tags --abbrev=0 --match 'v*'` FIRST (the annotated `phx--`/`ecto--`/`lv--` tags would win otherwise) — the last released tag is the bump base, NOT `plugin.json` (which may carry an unreleased phased bump). If `plugin.json` is already ahead of the tag, apply the consolidation check below before picking a number. - Read current `plugins/elixir-phoenix/.claude-plugin/plugin.json`. Pick bump from `## [Unreleased]` contents: - **MAJOR** — breaking change (removed command, workflow redesign) - **MINOR** — new skill / agent / command / hook - **PATCH** — bug fix, doc/reference update, description tweak - **Consolidation check** (per memory): if several phased branch bumps never released, collapse to ONE bump from the last released tag — don't stack intermediate versions. ## Step 1: Bump the version — five files by hand, two generated A partial bump does not just cost users the update; it fails `scripts/tests/test_codex.py`, which asserts the Codex manifest matches canonical. Set `"version"` to `X.Y.Z` in: 1. `plugins/elixir-phoenix/.claude-plugin/plugin.json` — canonical 2. `plugins/ecto/.claude-plugin/plugin.json` 3. `plugins/lv/.claude-plugin/plugin.json` 4. `package.json` — Pi package metadata, tracks the plugin version since v3.0.0 5. `package-lock.json` — run `npm install --package-lock-only`, never hand-edit (an unrelated dependency can share the old version string) Then regenerate the two templated manifests and bless their digests: ``` make generated-skills-sync # updates targets/codex + targets/pi manifests make generated-skills-snapshots # re-bless after reviewing the diff ``` Confirm every file agrees before moving on: ``` grep -rn '"version"' plugins/*/.claude-plugin/plugin.json package.json \ targets/codex/.codex-plugin/plugin.json targets/pi/package.json ``` (Often already bumped during the feature work — confirm it matches the target.) ## Step 2: Finalize CHANGELOG In `CHANGELOG.md`: 1. Rename `## [Unreleased]` → `## [X.Y.Z] - YYYY-MM-DD` (today's date). 2. Insert a fresh empty section on top (see `${CLAUDE_SKILL_DIR}/references/templates.md`). 3. Optionally add a one-line summary under the new heading (past releases do). ## Step 3: README + intro (only if needed) - Update `README.md` ONLY if counts/version callouts changed: skill count, agent count (`grep -nE "[0-9]+ (skills|agents|specialist)" README.md`), or a version banner. A pure doc/reference PATCH usually needs **no** README change — verify, don't assume. - Check `plugins/elixir-phoenix/skills/intro/references/tutorial-content.md` cheat sheet if commands/skills/agents were added, removed, or renamed. ## Step 4: Gate on `make ci` Run `make ci` (lint + test + validate + eval-all). **Must be green.** If lint trips on untracked non-source dirs (e.g. `social/`, `.rtk/`), that is not a code failure — exclude them, don't ship around real failures. See `${CLAUDE_SKILL_DIR}/references/templates.md`. ## Step 5: Commit ``` git add CHANGELOG.md plugins/elixir-phoenix/.claude-plugin/plugin.json # + README if touched git commit # message below ``` Commit subject (matches history): `Release vX.Y.Z — <short summary>` End the message with the `Co-Authored-By` trailer (see `CLAUDE.md`). ## Step 6: Tag After the release commit, tag each plugin. `claude plugin tag` validates the plugin, confirms `plugin.json` matches the marketplace entry, and refuses a dirty tree or an existing tag — so a failure here means the release commit is wrong, not the tag. Run it with `--dry-run` first if unsure. ``` for p in elixir-phoenix ecto lv; do claude plugin tag plugins/$p || break; done git tag vX.Y.Z git tag --list 'v*' '*--v*' --points-at HEAD # expect 4 tags ``` ## Step 7: CONFIRM, then publish (outward-facing) Show the user the pending commit, tag, and release notes. **On confirmation:** ``` git push origin main git push origin vX.Y.Z phx--vX.Y.Z ecto--vX.Y.Z lv--vX.Y.Z gh release create vX.Y.Z --title "vX.Y.Z — <summary>" --notes-file <changelog-section> ``` Use the new CHANGELOG section as release notes (extract it to a temp file or `--notes`), then **prepend any upgrade warning** (Iron Law 9) and **append the docs footer** before publishing — a release body is read at the moment someone decides whether to install: ``` --- Docs, install guides, and the runtime compatibility matrix: <https://phxagents.dev> ``` See `${CLAUDE_SKILL_DIR}/references/templates.md` for the one-line `printf` that appends it to the extracted notes. ## Step 8: Verify ``` gh release view vX.Y.Z git describe --tags --abbrev=0 --match 'v*' # should print vX.Y.Z ``` Confirm to the user: released, tag pushed, GitHub release live. ## Reference - `${CLAUDE_SKILL_DIR}/references/templates.md` — CHANGELOG/release-notes templates, gate snippets, gotchas
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.