Repository navigation
finalize app release
Shipping one of my Mac apps (LanGuard, MaccyPlus, VaultBar, Window Organizer, SmartHiddenBar, MeetAlert, Agents Monitor, agentctl, claude-jam) is twenty small steps across four repos: the version, the CHANGELOG, the README, the wiki, Ko-fi links, the GitHub release and its zip, the Homebrew cask, the tap's README, pushing it all, and upgrading the copy you actually run. Each one is easy; one of them is always forgotten. This plugin is one skill that runs the whole checklist.
/plugin marketplace add roypadina/padina-claude-code-plugins
/plugin install finalize-app-release@padina
Requires macOS, git, the GitHub CLI gh logged in as the repo owner, Homebrew, and the tap
checked out at ~/Code/Padina/homebrew-tap (override with TAP_WORK). It is written for my own repos
and accounts — fork it and change OWNER in the script and the account details in SKILL.md.
-
You:
/finalize-app-release [app dir or name] [recheck]at the end of a session. - Claude, on its own: when work on one of these apps wraps up — "release", "ship it", "push all", a version bump, a release build, the session ending — it runs the state check and offers the fixes.
skills/finalize-app-release/scripts/release-state.sh <repo> prints OK / GAP / WARN / INFO
lines in about ten seconds. It only reads (plus git fetch --tags).
| Area | Checks |
|---|---|
| Repo | clean tree, branch pushed (compared with the server), repo-local noreply commit email |
| Version | finds the source of truth (package.json, *.xcconfig, package script, Info.plist, pbxproj), tag exists, commits since the tag |
| Release | latest GitHub release = source version, its asset names |
| Tap | cask/formula version, cask url asset + sha256 = the release asset, tap README row, tap working copy and brew's clone = the server |
| Docs | CHANGELOG / appcast.xml top entry, README brew install line, mentions of the previous version left in README, docs and wiki |
| Wiki | initialised on GitHub, cloned locally, committed, pushed, changed since the release |
| Ko-fi (public repos) | README badge + ## Support, FUNDING.yml, wiki sidebar/footer/Home, a Ko-fi link in the app source |
| Local install |
/Applications copy is the source version, not quarantined, one copy running from /Applications
|
Claude works through what the script flagged, plus what a script can't judge — does the README and the wiki describe what the app does now?
- Code — build and tests green, committed and pushed (PR + CI on protected repos).
- Docs — CHANGELOG entry (created once from past release notes if missing), README, wiki pages and sidebar, other docs; stale version strings fixed.
-
Ko-fi — README,
FUNDING.yml, wiki, in-app About window and menu items (built from a reference for a new app; only verified for existing ones). -
Release — zip from the repo's own script, asset named like the cask URL, tag,
gh release, downloaded sha256 checked. -
Tap — cask bumped, tap README row current,
brew styleclean, pushed over SSH and verified on the server. -
Local install — upgraded from the tap (or the app's own dev build where its permissions need
that), quarantine cleared, relaunched from
/Applications, version checked. - Wrap-up — memory note, temp-file cleanup (asks first), stamp, a report table.
Public steps — push, tag, release, wiki, tap — run only when you asked for a release in that session. Anything net-new (making a repo public, a first cask, rewriting history) always asks first.
After a clean run, release-state.sh <repo> --stamp records the version and the repo, wiki and tap
commits in ~/.local/state/finalize-app-release/. The next run prints STAMP MATCH while none of
them changed, and Claude reports "already finalized" instead of reviewing again. Say recheck to
force a full pass.
references/new-public-app.md covers the first publish: privacy triple check (denylist grep over
tree, history, wiki and binary; gitleaks; an independent reviewer agent; PNG metadata), history squash,
community files, CI and branch protection, repo metadata, icons, the About window with Ko-fi, the wiki
(the first page has to be created in the web UI), and the first cask with its tap README row.