Releasing
Cut a new ntucool release — bump versions across the workspace + plugin manifest, push, tag, let cargo-dist build prebuilt binaries on GitHub. Use whenever the user says "ship", "release", "publish", "tag a version", "cut a release", or asks "should we release v0.0.X?". Covers the dual-channel ship (binary via cargo-dist tag, plugin via plugin.json version) and the version-file matrix that's easy to miss.From its SKILL.md
npx -y skills add kc0506/ntucool --skill releasingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 10 stars10 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
6.7 KB, as published. Nobody here has run it
Releasing ntucool
ntucool ships two artifacts on every release that have independent version sources in the repo:
| Artifact | Version source | Distribution channel | Trigger |
|---|---|---|---|
cool binary | [workspace.package] version in root Cargo.toml (+ 3 internal path-dep pins) | GitHub Release via cargo-dist | git push <tag> matching v* |
| Claude Code plugin | version in plugins/ntucool/.claude-plugin/plugin.json | Claude Code's plugin update reads marketplace.json from origin/main and diffs plugin.json versions | git push origin main |
Both must bump for a release to flow end-to-end. Skipping the plugin bump is a silent failure — Claude Code's plugin update won't notice anything changed (it keys on plugin.json version, not git tags).
Pre-flight (run before bumping)
cargo test --workspace --quiet
cargo check -p ntucool --quiet
Quick MCP smoke (kept in scripts/):
cargo build --release -p ntucool --quiet
(printf '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"smoke","version":"0"}}}\n{"jsonrpc":"2.0","method":"notifications/initialized"}\n{"jsonrpc":"2.0","id":2,"method":"tools/list"}\n'; sleep 0.5) \
| timeout 5 ./target/release/cool mcp 2>/dev/null \
| grep -o '"name":"[^"]*"' | wc -l # expect ≥ 24
Tag the current expected tool count somewhere and treat a deviation as a regression — past releases settled at 24 - 25.
The bump matrix (5 lines, in 4 files)
Same OLD → NEW substitution everywhere:
Cargo.toml: [workspace.package] version
cool-cli/Cargo.toml (2 lines): path-dep pins to cool-api, cool-tools
cool-tools/Cargo.toml: path-dep pin to cool-api
cool-tui/Cargo.toml: path-dep pin to cool-api
plugins/ntucool/.claude-plugin/plugin.json: version
One-liner that bumps all five at once (verify the match list first):
OLD=0.0.2 NEW=0.0.3
sed -i "s/version = \"$OLD\"/version = \"$NEW\"/g" \
Cargo.toml cool-cli/Cargo.toml cool-tools/Cargo.toml cool-tui/Cargo.toml
sed -i "s/\"version\": \"$OLD\"/\"version\": \"$NEW\"/" plugins/ntucool/.claude-plugin/plugin.json
cargo check -p ntucool --quiet # touches Cargo.lock
git diff --stat
Confirm the diff hits exactly those 4 files + Cargo.lock. Anything else means the sed picked up a co-incidental "0.0.X" somewhere.
Decision: which version number?
The project follows 0.0.x "everything is alpha" semantics. Breaking changes (renaming an MCP tool, changing the .mcp.json command users copy, removing a CLI subcommand) are fine inside 0.0.x — no minor bump needed.
If the contract feels stable enough to advertise, bump to 0.1.0. Until then, default to 0.0.<next>.
Ship sequence
# 1. Commit the bump
git add Cargo.toml Cargo.lock cool-*/Cargo.toml plugins/ntucool/.claude-plugin/plugin.json
git commit -m "chore(release): bump workspace + plugin to v0.0.X
What changed since v0.0.<prev>:
- <bullet 1>
- <bullet 2>
..."
# 2. Push main FIRST — this is what plugin-update reads from
git push origin main
# 3. Tag and push the tag — this triggers cargo-dist
git tag v0.0.X
git push origin v0.0.X
gh run watch the resulting Release workflow. Expected jobs:
plan → build-local-artifacts (3 targets) → build-global-artifacts → host → announce
15 - 20 min wall clock. On success, gh release view v0.0.X --json assets should list:
ntucool-installer.sh / .ps1
ntucool-x86_64-{pc-windows-msvc.zip, unknown-linux-{gnu,musl}.tar.xz}
each artifact's .sha256
dist-manifest.json + source.tar.gz
Known caveats — surface these in the release notes
- macOS prebuilt is disabled.
dist-workspace.tomlhas it commented out (GitHub free-tier macOS runner queue stalled v0.0.1 for 12h+). macOS users currently can'tcargo install ntucooleither — the crate has never been pushed to crates.io. State this honestly in the release announcement. cool updateavailable since v0.0.3. The embeddedaxoupdaterreads an install-receipt that cargo-dist's shell/powershell installer drops at$XDG_CONFIG_HOME/ntucool/ntucool-receipt.json. Users on v0.0.2 or older need one last manualcurl ...installer.sh | shto reach a binary that has the subcommand; from then oncool updateself-sustains.cargo installusers never have a receipt — error message incommands/update.rstells them tocargo install ntucool --force.- Plugin / binary version skew is possible. A user can have plugin v0.0.3 with binary v0.0.1 (or reversed).
setup.mddoesn't (yet) check version drift.
Pitfalls (post-mortems of past mistakes)
install-updater = trueis NOT what makes the receipt get written. v0.0.3 set this flag thinking it was needed forcool updateto find a receipt. Wrong — cargo-dist 0.9+ writes the receipt regardless. The flag actually ships a standalonentucool-updatebinary alongsidecool, undoing the single-binary principle. v0.0.4 reverted tofalse. Keep itfalseunless you specifically want a separate updater binary on PATH. Verify by checkinggh release view v0.0.X --json assets— there should be NOntucool-x86_64-*-updateentries.- Don't push the tag before pushing main. If origin/main has commits you don't have locally (e.g. the user pushed a README edit while you were preparing the bump), pushing the tag first succeeds but pushing the branch fails with non-fast-forward. Recovery sequence:
gh run cancel <id>for any triggered CI,git push origin :v0.0.Xto delete the orphan tag,git pull --rebase(orgit fetch && git rebase origin/main),git tag v0.0.Xat the rebased HEAD, thengit push origin HEAD:main v0.0.Xtogether. - Main worktree may have uncommitted WIP that blocks a local fast-forward. If
git -C <main-worktree> merge --ff-onlyreports "Your local changes would be overwritten", do NOT stash the user's WIP without asking. Push the worktree branch directly to origin/main withgit push origin HEAD:mainfrom the release worktree — this updates origin without touching the main worktree's working tree.
What this skill does NOT cover
cargo publishto crates.io — never done, namespace deferred indefinitely- macOS-target restoration in cargo-dist
- Generating release notes from git log automatically (do it by hand for now)