7.3 KiB
| name | description |
|---|---|
| whats-new | Write user-facing release notes ("What's new") for a mobile app or game update from the git log since the last tag - hook in the first line, plain user language, localized into every listing language, one file per language translated by the agent (or a provider hook the owner sets), checked against store limits (Google Play 500, App Store 4000 characters) and applied to both listings. Use when preparing an update, when the user asks for release notes / changelog for the stores, or before play release / apple listing for a new version. |
What's new (release notes)
CLI: storebound whats-new [--since v1.2.0] [--release 1.2.0] [--dry]. It takes a player-facing source when one exists (below); otherwise it reads git log <tag>..HEAD, drops housekeeping commits
(chore, ci, docs, refactor, test, merges, version bumps), turns feat: / fix: / perf: subjects into bullets and
writes a draft into the default language of store/listing.md (Play "What's new") and store/appstore.md
(whatsNew). Nothing is sent to a store. --all writes the draft into every language as a starting point for
translation; storebound check warns while a translation is still identical to the source.
Where the text comes from
Write from what players feel, never from commit messages. Commit subjects are written for developers ("Umami telemetry", "refactor save endpoint", SDK and tool names); players care about what they see and feel ("Levels load faster", "Your progress is saved more reliably"). So the draft comes from a player-facing source first, the first that exists:
whatsNew.sourcein the config (a notes file; a CHANGELOG-like file gives the version's## <version>section),docs/RELEASE-<version>.md,- the
CHANGELOG.mdsection of the version, else its## Unreleasedsection. The version is--release 1.2.0, elsepackage.json. Only without any of these (or with--from-git) the draft is built fromgit log; treat that draft as raw material and rewrite every line. If a project keeps a developer changelog, add a short player-facingdocs/RELEASE-<version>.mdinstead of reusing commit-speak.
The draft and whats-new check warn about internal jargon (never blocking): SDK / vendor names (Umami, Sentry,
Firebase, Capacitor, Tauri, Jenkins, Forgejo, ...), developer words (telemetry, refactor, endpoint, payload, API,
CI, PR, OTA, fingerprint, bump, deps, ...), version-control words (commit, merge, revert), file paths, issue ids and
code identifiers (saveSlot, max_lives). Each warning names the word and a plain alternative. Rewrite the line;
when a flagged word is a real player-facing term (a character called "Sentry"), add it to whatsNew.allowWords.
One source, two stores: tag store-specific lines
A notes source shared by both stores must not carry one store's lines into the other: a Mac App Store version was rejected under Guideline 2.3.10 for a What's New with "Google Play Games" and "Android" lines (store-pitfalls 77).
- Tag a line for one store:
[play] Achievements on Google Play Games.or• [apple] Game Center leaderboards.(also[android]/[ios]). A line that is only[play]starts a block for that store up to the next blank line. Untagged lines go to both stores; the tag itself is removed. - The draft leaves an untagged line that names the other side's platform out of that store's draft and says so
(tag it to make it explicit).
whats-new checkandapplyrefuse notes that still name Android, Google Play, Steam, ... in App Store text or iOS, iPhone, App Store, Game Center, TestFlight in Play text, with language, line and term;apple listing,play listing,play release/play upload(release notes),listing setand the desktop editor refuse the same, dry runs included. - A functional mention (a "Continue with Google" or "Sign in with Apple" button) is the owner's call: an entry in
listing.allowPlatformMentions({ store, text, reason }). Never add one yourself to get past the check.
Write it for your users
- Run
storebound whats-new --dry, read the draft with the owner. - Rewrite:
- First line = hook (the stores show roughly the first 170 characters before "more"): the most exciting change in user words ("New: shared lists with your family" for an app, "New: 20 desert levels and a daily challenge!" for a game).
- Then 2-5 bullets, most visible first. User words, not code words: "Lists open faster" / "Levels load faster", not "refactor asset loader"; never internal ids, branch names or ticket numbers.
- Fixes in one line unless a fix was a known user complaint.
- No promises of future features, no prices, no other platforms.
- Translate into every listing language (natural tone, the app's own terms, same meaning; not word by word). Arabic stays RTL; keep numbers and product names as they are.
- Limits: Google Play 500 characters per language, App Store 4000;
storebound checkenforces both. A first App Store version has no "What's new" (storebound leaves it empty there). - Push:
storebound play release ...uses the Play notes;storebound apple listing --version x --build Nthe App Store ones.
Many languages, one file each
storebound whats-new --languages en,de,tr,ja --dry, then without--dry(MCP:whats_new_languages). Values are language ids of the config or store codes; the first is the source. It writesstore/whats-new/<id>.md: the source gets the draft (the Play-sized one when the source language is on Play, so it fits 500), every other language a copy of it with the header<!-- whats-new lang=de source=en status=translate -->. Translated files (status=translated) are kept on later runs unless--force.- You translate each
status=translatefile: replace the text below the header, keep the header line and setstatus=translated. Follow step 3 above (natural tone, the app's terms, same meaning). The CLI never calls a translation service and holds no key for one. Optional provider hook (the owner's choice):--translate "<command>"runs that command once per language with the source text on stdin andSTOREBOUND_SOURCE_LANG/STOREBOUND_TARGET_LANG(App Store locale, else Play code) /STOREBOUND_TARGET_LANG_IDin the environment; its stdout becomes the file (status=translated). Any key it needs lives in the owner's environment, never in the config. Read hook output like any draft before applying it. storebound whats-new check(MCP:whats_new_check): per language the characters against the stores that language is listed on (Google Play 500, App Store 4000), untranslated files, a translation identical to the source, a first line over ~170 characters. Exit 1 while a language is not ready.storebound whats-new apply --dry, thenapply(MCP:whats_new_apply): writes every file into its Play sections ("What's new" for each Play code of the language) and its App Store locale (whatsNew). It refuses while any language fails the check.
Gotchas
- Squash-merged repos: the PR titles are the subjects; keep them in conventional form (
feat: ...) to get clean drafts. - Commits marked
[skip notes]or[internal]are left out;feat: [play] ...subjects go only into the Play draft. - Release notes are public: nothing about cheats, exploits, unreleased content or private names.