Use when cutting a new Flux release. Bump all manifest versions in sync, tag the release, create GitHub release notes, and prevent stale plugin metadata from drifting from the published tag. Triggers on literal `/flux:release`.
From fluxnpx claudepluginhub nairon-ai/flux --plugin fluxThis skill uses the workspace's default tool permissions.
Executes pre-written implementation plans: critically reviews, follows bite-sized steps exactly, runs verifications, tracks progress with checkpoints, uses git worktrees, stops on blockers.
Guides idea refinement into designs: explores context, asks questions one-by-one, proposes approaches, presents sections for approval, writes/review specs before coding.
Dispatches parallel agents to independently tackle 2+ tasks like separate test failures or subsystems without shared state or dependencies.
Bumps version across all manifest files, commits to main, and creates a GitHub release. This ensures claude plugin marketplace add always pulls the correct version.
On entry, set the session phase:
PLUGIN_ROOT="${DROID_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}}"
[ ! -d "$PLUGIN_ROOT/scripts" ] && PLUGIN_ROOT=$(ls -td ~/.claude/plugins/cache/nairon-flux/flux/*/ 2>/dev/null | head -1)
FLUXCTL="${PLUGIN_ROOT}/scripts/fluxctl"
$FLUXCTL session-phase set release
On completion, reset:
$FLUXCTL session-phase set idle
$ARGUMENTS format: <version> [--title "Release title"]
Examples:
/flux:release v2.4.0/flux:release v2.4.0 --title "Parallel Workers"If no --title is provided, the release title defaults to the version number.
These files contain the version string and MUST stay in sync:
package.json → "version": "X.Y.Z".claude-plugin/plugin.json → "version": "X.Y.Z".claude-plugin/marketplace.json → "version": "X.Y.Z" (inside plugins[0])README.md → version badge version-vX.Y.Z-greenCHANGELOG.md → move [Unreleased] entries under a new [X.Y.Z] - YYYY-MM-DD headingExtract version from $ARGUMENTS. Strip leading v if present for the manifest files (e.g., v2.4.0 → 2.4.0 in JSON, v2.4.0 in git tag).
Parse optional --title "..." — if not provided, default title to the version.
# Must be on main or a release branch
BRANCH=$(git branch --show-current)
if [[ "$BRANCH" != "main" && "$BRANCH" != release/* ]]; then
echo "Error: Must be on main or a release/* branch. Currently on: $BRANCH"
exit 1
fi
# Version must be valid semver
if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$'; then
echo "Error: Invalid semver: $VERSION"
exit 1
fi
# Check we're not re-releasing an existing tag
if git rev-parse "v$VERSION" >/dev/null 2>&1; then
echo "Error: Tag v$VERSION already exists"
exit 1
fi
Update all five files with the new version:
package.json — update the "version" field to "$VERSION".claude-plugin/plugin.json — update the "version" field to "$VERSION".claude-plugin/marketplace.json — update plugins[0].version to "$VERSION"README.md — update the badge from version-v*-green to version-v$VERSION-greenCHANGELOG.md — move everything under ## [Unreleased] into a new ## [$VERSION] - YYYY-MM-DD section (use today's date). Add a fresh empty ## [Unreleased] heading at the top. If there are no unreleased entries, create the version heading with a summary of what's in this release based on the GitHub release notes.git add package.json .claude-plugin/plugin.json .claude-plugin/marketplace.json README.md CHANGELOG.md
git commit -m "release: v$VERSION"
git push origin "$BRANCH"
Build release notes that explain why each change matters, not just what files changed. Do NOT use --generate-notes.
LAST_TAG=$(git tag --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1)
TAG_DATE=$(git log -1 --format=%aI "$LAST_TAG")
gh pr list --state merged --base main --search "merged:>=${TAG_DATE}" --limit 50 --json number,title,body
For each PR, extract the "What this PR does" section from its body. Use that as the description — it already explains the change in the author's own words.
Write release notes in this format:
## What's New
### [Descriptive heading that explains the value, not the implementation] (#PR)
[2-4 sentences explaining what changed and WHY it matters to users.
Lead with the problem being solved, then explain how this release addresses it.
Be specific — name the skills, commands, or behaviors that changed.
This is what users read first. Make it worth reading.]
### [Second feature heading] (#PR)
[Same structure — problem, solution, impact.]
---
## PRs
- #N — PR title
- #N — PR title
## Full Changelog
https://github.com/Nairon-AI/flux/compare/$LAST_TAG...v$VERSION
Writing guidelines:
.md files were edited--- between major sections for visual separationgh release create "v$VERSION" --target "$BRANCH" --title "$TITLE" --notes-file /tmp/release-notes.md
Release title format: v$VERSION — [short description of primary change] (e.g., v2.14.0 — Anti-Sycophancy & Viability Gate). If --title was provided by the user, use that instead.
# Confirm the tag exists
gh release view "v$VERSION" --json tagName,name --jq '"\(.tagName) — \(.name)"'
# Confirm manifest files match
PACKAGE_VER=$(jq -r '.version' package.json)
PLUGIN_VER=$(jq -r '.version' .claude-plugin/plugin.json)
MARKETPLACE_VER=$(jq -r '.plugins[0].version' .claude-plugin/marketplace.json)
if [[ "$PACKAGE_VER" != "$VERSION" || "$PLUGIN_VER" != "$VERSION" || "$MARKETPLACE_VER" != "$VERSION" ]]; then
echo "ERROR: Version mismatch after release!"
echo " package.json: $PACKAGE_VER"
echo " plugin.json: $PLUGIN_VER"
echo " marketplace.json: $MARKETPLACE_VER"
echo " Expected: $VERSION"
exit 1
fi
echo "Released v$VERSION — all manifest files in sync."