- one version: package.json's version is the desktop app's too (desktopVersion removed); tools/desktop-version.ts keeps the plugin manifests and the app's copies equal, desktop-release publish refuses another version, release order app first then npm publish; the unpublished desktop 0.1.0 candidate is noted in the docs and CHANGELOG - storebound desktop installs (per user, no admin: macOS DMG -> /Applications or ~/Applications, Windows NSIS /S, Linux AppImage + .desktop with the storebound:// handler), updates an older app, notes a newer one (downgrade only with --force), asks once ([Y/n], --yes, exit 3 without a terminal), opens on the folder; --dry / --json / --download; the install steps are pure plans (cli/lib/desktop-install.ts); the version's own manifest dl/<version>/latest.json - credentials: one store, the CLI's credential profiles in ~/.storebound/credentials; the desktop writes and reads them (profiles.rs, files 0600), moves old keychain accounts into profiles on first start, shows profiles the CLI made; precedence env > named profile > project file > default profile; config `account`, "account" in the project's credentials.json; storebound accounts list | import (dedup by key id / service account e-mail, key files copied 0600, project file keeps keystore and review contact, backup, --dry, never prints values) | use - app folders: one list ~/.storebound/projects.json shared by the desktop (registry.rs), storebound desktop, apps folders | add | remove, ws init and ws without a workspace file - desktop: notice when a folder pins another storebound version; storebound-desktop --version - tests: version drift, desktop decisions and per-OS plans, credential precedence and import, registry (TS + Rust), keychain migration and profile round trip (Rust), boot check with old keychain state and the new shared state
137 lines
9.3 KiB
Markdown
137 lines
9.3 KiB
Markdown
# 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](https://nodejs.org) 20+ or [Bun](https://bun.sh) 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.
|
|
|
|
```sh
|
|
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:
|
|
|
|
```sh
|
|
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:
|
|
|
|
```sh
|
|
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.
|
|
|
|
## 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:
|
|
|
|
```sh
|
|
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`, then `npx storebound accounts import --yes` in every project: the same key
|
|
id or service account is one profile (`default` for the first one), and the project file keeps the rest and gets
|
|
`"account": "<name>"`. `npx storebound accounts` lists 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 named `default`).
|
|
- **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](studios.md#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.
|
|
|
|
---
|
|
|
|
[All docs](README.md) · [README](../README.md)
|