11 KiB
Getting started
The model. storebound is installed once per machine (npx storebound / the desktop app, one version number).
Each project has its storebound.config.ts and a gitignored .storebound/ folder (upload keystore, review contact
and demo account). Store accounts live once per machine in ~/.storebound/credentials (shared by CLI and app:
storebound accounts), and so does the list of app folders (~/.storebound/projects.json: storebound apps folders,
the desktop's App folders). npx storebound desktop installs the app (asks once) and opens it on the current folder.
5-minute quick start
You need Node 20+ or Bun 1.1+ (and ffmpeg for icons, screenshots and video).
With Node, run the commands as npx storebound ...; with Bun, as bunx storebound ... (used below). Both run the
same CLI.
cd my-app
npx storebound init # Node 20+ (npm i -D storebound playwright to pin it in the project)
bunx storebound init # or Bun (bun add -d storebound playwright, or bun add -d git+https://git.crt.fyi/CRT/storebound.git playwright)
# --profile app (default) | subscription-app | game: config, store/*.md, declarations, privacy policy, .storebound/ (gitignored)
# fill in storebound.config.ts and the store/ files
bunx storebound check # offline: config, copy limits, Data safety answers, products, files
Add credentials (below), then walk the journey with dry runs first:
bunx storebound apple bundle --dry
bunx storebound play listing --dry
bunx storebound play products --dry && bunx storebound apple iap --dry
bunx storebound apple subscriptions --dry && bunx storebound play subscriptions --dry
bunx storebound apple review-details --dry # contact + demo account from .storebound/ (masked) + notes
bunx storebound play data-safety --dry --csv store/data-safety.csv
bunx storebound apple appinfo --dry
bunx storebound icons --dry
bunx storebound shots capture && bunx storebound shots render && bunx storebound shots gallery
bunx storebound validate # read-only: what still blocks a submission, per store, in fix order
bunx storebound catalog diff # read-only: products + subscriptions on both stores vs the config, prices per region
After launch:
bunx storebound apple testflight what-to-test --build 42 --dry # then distribute --build 42 --group Team
bunx storebound health --weekly # vitals + diagnostics, .storebound/health/<date>.md
bunx storebound reviews draft # reply drafts in .storebound/replies/, then reviews send
bunx storebound prices export && bunx storebound prices import --dry
bunx storebound analytics snapshot && bunx storebound analytics compare
Or ask your agent: "take my app to Google Play and the App Store", "fill in the store questionnaires", "set up the in-app purchases and a yearly subscription with a free trial on both stores", "do both stores sell the same catalog", "the reviewer could not sign in", "make store screenshots in 5 languages", "is my app ready to submit", "why was my update rejected", "put build 42 on TestFlight", "how are our crash rates", "answer the new reviews", "did the new screenshots help". The matching skill takes over.
An app that is already live
storebound init, then pull what the stores already have instead of typing it in:
bunx storebound apple listing pull --write && bunx storebound play listing pull --write # texts per language
bunx storebound onboard # the plan: products + prices, contact, review contact, sign-in, deletion page
bunx storebound onboard --write # into storebound.config.ts and .storebound/credentials.json
bunx storebound catalog diff # clean: the config now sells what the stores sell
onboard is read-only on the stores and never changes a store price. It imports the live one-time products and
subscriptions (usd = the US price; Apple's base territory as an override when it is not the USA; an item on one store
only keeps platforms), replaces template contact values (support@example.com, example.com URLs) with Play's and App
Store Connect's, puts the App Review contact and a demo account set on App Store Connect into
.storebound/credentials.json (never printed), decides "sign-in required for review" from Apple's review detail and the
code, and looks for the account deletion page on the app's own site. What nothing proves is listed as owner questions
at the end, in one batch. An iOS app whose source is not on this machine: storebound apps add --store apple --app-id <id> --account <profile> shows it in the desktop, read-only.
Coming from fastlane
storebound import fastlane turns an existing fastlane setup into storebound files. It only reads (no Ruby is run),
never overwrites anything and prints what it could not map:
bunx storebound import fastlane --dry # the plan + report; default --dir: fastlane/, ios/fastlane/, android/fastlane/
bunx storebound import fastlane # writes the files below; an existing file gets a <name>.imported.<ext> copy
bunx storebound init # adds the remaining templates, keeps the imported files
bunx storebound check
| fastlane | storebound |
|---|---|
Appfile app_identifier, package_name |
apple.bundleId, play.packageName |
deliver metadata/<locale>/*.txt (+ default/) |
store/appstore.md per App Store locale; URLs, copyright, categories and review notes in the config |
supply metadata/android/<lang>/*.txt, newest changelogs/<versionCode>.txt |
store/listing.md per Play language |
| deliver and supply language folders | languages[] with both store codes (tr + tr-TR, zh-Hans + zh-CN, he + iw-IL) |
screenshots/<locale>/, images/phoneScreenshots/, tenInchScreenshots/ |
store/shots/frames/<lang>/<phone|tablet>/NN.png with a folder capture source |
supply icon, featureGraphic |
play.icon, store/renders/<lang>/feature/1024x500.* |
Keys and accounts are never copied: json_key_file, the API key, team ids, the App Review contact and demo account are
named in the report with where they go (.storebound/credentials.json). Submission and release flags, price_tier,
the age rating config, lanes and signing (match) are listed with their storebound counterpart. The
migrate-from-fastlane skill walks an agent through it.
Credentials (never in the repo)
Store accounts belong to the machine, everything else to the project:
- Store accounts (App Store Connect team key, Play service account, vendor number): a credential profile in
~/.storebound/credentials/<name>.json, its key files next to it in<name>/(mode 0600). The desktop app adds and reads the same profiles. Already have the keys in a project's.storebound/credentials.json? Move them once:npx storebound accounts import --dry, thennpx storebound accounts import --yesin every project: the same key id or service account is one profile (defaultfor the first one), and the project file keeps the rest and gets"account": "<name>".npx storebound accountslists the profiles and which one this folder uses. - Which profile:
--account <name>for one run,account: '<name>'in the config,"account"in the project's.storebound/credentials.json, else the only profile on the machine (or the one nameddefault). - Per project: the Android upload keystore, the App Review contact and demo account stay in the project's
.storebound/credentials.json. - Order: environment variables win over everything, then a profile the project names, then the project file, then the default profile.
The names (env, profile and project file alike; the project file is gitignored by init):
| name | what |
|---|---|
PLAY_SERVICE_ACCOUNT |
path to the Google Cloud service account JSON key (or the JSON itself) with access to your app in Play Console |
ASC_KEY_ID, ASC_ISSUER_ID |
App Store Connect API team key (Users and Access > Integrations) |
ASC_P8 |
path to AuthKey_<id>.p8 (default ~/.appstoreconnect/private_keys/AuthKey_<id>.p8) |
ASC_VENDOR_NUMBER |
vendor number for reports apple-sales / apple-finance (Payments and Financial Reports) |
ANDROID_KEYSTORE, ANDROID_KEYSTORE_PASSWORD, ANDROID_KEY_ALIAS, ANDROID_KEY_PASSWORD |
upload key for play build (path to the .jks / .p12 and its passwords; handed to Gradle through the environment) |
reviewContact |
(file only) App Review contact name / phone / email |
reviewDemoAccount / REVIEW_DEMO_USER, REVIEW_DEMO_PASSWORD |
demo account for App Review (reviewAccess.signInRequired); never in the config or the repo, masked in logs |
Several store accounts on one machine: one profile each, picked as above or by a workspace (Many apps and studios).
Install for your AI tool
Every route gives the agent the same skills and the same MCP tools. Nothing here requires Claude: the skills are plain Markdown and the CLI is a normal command.
| tool | install |
|---|---|
| Claude Code | /plugin marketplace add https://git.crt.fyi/CRT/storebound.git then /plugin install storebound@crtkafa (skills, MCP server, storebound on PATH). Or per project: bunx storebound install --agent claude |
| OpenAI Codex | bunx storebound install --agent codex (skills in .agents/skills, MCP in .codex/config.toml; Codex reads that file for trusted projects). The repo is also a Codex plugin (.codex-plugin/plugin.json, skills only) |
| Gemini CLI | gemini extensions install https://git.crt.fyi/CRT/storebound.git (skills, MCP server, GEMINI.md). Or bunx storebound install --agent gemini |
| Cursor | bunx storebound install --agent cursor (skills in .agents/skills, .cursor/mcp.json); the repo's .cursor/rules/storebound.mdc is an agent-requested rule you can copy |
| VS Code / GitHub Copilot | bunx storebound install --agent copilot (skills in .github/skills, .vscode/mcp.json) |
| Windsurf (Devin Desktop) | bunx storebound install --agent windsurf (skills in .agents/skills, MCP in your global mcp_config.json) |
| OpenCode | bunx storebound install --agent opencode (skills in .opencode/skills, opencode.json) |
anything that reads AGENTS.md |
copy or link AGENTS.md (rules + skill index) and use the CLI |
install takes several agents at once (--agent codex,cursor), --dry to preview, --no-skills / --no-mcp,
and --command "<cmd>" to choose how the MCP server starts. It only adds or updates the storebound entry and its
own skill folders; everything else in those files stays as it is, and re-running it is safe.