MCP / CLI / SKILL  ·  V 2.0.0 AVAILABLE NOW

Push all App Store Connect fields
with one config.
All devices. All locales.

StoreScreens drives your release pipeline from a single storescreens.yml: XCUITest-driven captures across every device size, framed and captioned renders, metadata + screenshot upload to App Store Connect, and the build archive itself. No fastlane Ruby dependency hell. No lost afternoons to provisioning profile weirdness.

MCP/CLI
$ brew tap ciscoriordan/tap
$ brew install storescreens
agent skill
$ npx skills add ciscoriordan/storescreens-skill
preview.html · iPhone 6.9" · light · en-US
Storescreens preview gallery showing 8 iPhone screenshots captured in parallel

Four commands. One config.

Each step is independent and idempotent. Run the whole chain in CI, or just the one you need during an afternoon iteration on a caption.

  1. 01
    storescreens capture one UI test file · N simulators · runs in parallel

    XCUITest across every configured simulator in parallel. Named screenshots per device × locale × light/dark. One HTML preview gallery at the end.

    $ storescreens capture --verbose
    
    ▸ iPhone 17 Pro Max  · 6.9"   · en-US  [#####.....]  5 / 19
    ▸ iPhone 17 Pro      · 6.3"   · en-US  [######....]  6 / 19
    ▸ iPad Pro 13"       · ipad   · en-US  [###.......]  3 / 19
    ▸ iPhone 17 Pro Max  · 6.9"   · ja     [##........]  2 / 19
    
    ./storescreens-output/  →  preview.html
  2. 02
    storescreens render bezels · captions · backgrounds · logos

    Raw captures become App Store–ready PNGs: device bezels, markdown captions with per-word highlights, panoramic backgrounds, per-slide logos. Pure Swift image pipeline, no Figma.

    $ storescreens render
    
    ▸ loading 19 captures × 3 devices × 2 locales
    ▸ composing bezels           ✓  (M5 iPad, 17 Pro Max, 17 Pro)
    ▸ rendering captions         ✓  Instrument Serif + highlights
    ▸ stamping background + logo ✓  panoramic, first-slide logo
    ▸ writing PNGs               ✓  ./storescreens-framed/
  3. 03
    storescreens upload-build altool · non-beta Xcode · version auto-bump

    xcodebuild archive, export .ipa, upload via altool. DEVELOPER_DIR is auto-pinned to a non-beta Xcode. If the version tuple is already shipped or the build number would collide with TestFlight, it auto-bumps MARKETING_VERSION / CURRENT_PROJECT_VERSION before archiving.

    $ storescreens upload-build
    
    ▸ DEVELOPER_DIR → /Applications/Xcode.app   (non-beta, pinned)
    ▸ asking ASC about 1.7.3 (42)
      ✗ 1.7.3 (42) already on TestFlight
      → bumping to 1.7.4 (1)
    ▸ xcodebuild archive          ✓   42.1s
    ▸ export .ipa                 ✓
    ▸ altool upload               ✓   uploaded to App Store Connect
  4. 04
    storescreens submit screenshots · metadata · asc api · optional auto-submit

    Uploads rendered screenshots + per-locale metadata (name, subtitle, description, keywords, what's new, promotional text) to App Store Connect via Apple's official API. Optional auto-submit-for-review.

    $ storescreens submit
    
    ▸ app: com.example.myapp   version: 1.7.4
    ▸ en-US  · 19 screenshots · keywords · description · what's new   ✓
    ▸ ja     · 19 screenshots · keywords · description · what's new   ✓
    ▸ de-DE  · 19 screenshots · keywords · description · what's new   ✓
    ▸ submit_for_review: false   (manual click in ASC)
    
      status  →  Prepare for Submission

→ every command reads the same storescreens.yml. no other glue.

storescreens.yml
is the whole contract.

Scheme, the devices to cover, locales, render styling, App Store Connect bundle id, what to upload. Every subcommand reads this same file. Your CI runs the same commands you ran at your desk.

  • No Gymfile, no Fastfile, no Appfile, no Deliverfile.
  • Env-driven ASC auth (ASC_KEY_ID, ASC_ISSUER_ID, ASC_KEY_PATH) - or run storescreens auth login once.
  • Metadata lives as plain text in ./metadata/<locale>/*.txt. Edit in your editor, diff in git.
storescreens.yml
# what to build
project: "MyApp.xcodeproj"
scheme:  "MyApp"

# what to shoot
devices:
  - simulator: "iPhone 17 Pro Max"   # 6.9"
  - simulator: "iPhone 17 Pro"       # 6.3"
  - simulator: "iPad Pro 13-inch (M5)"

appearances: [light, dark]
locales:     [en-US, ja, de-DE]

test_target: MyAppUITests
test_class:  ScreenshotTests

# how to frame them
render:
  enabled: true
  output_dir: ./storescreens-framed
  background: { color: "#0d0d0b" }
  chrome:     { style: stroke }
  caption:
    title: { font: system, weight: bold,
             font_size_pct: 5.5 }
  slides:
    "01_Home":   { caption: "Your recipes, **organized**." }
    "02_Search": { caption: "Find it, instantly." }

# where to ship it
app_store_connect:
  bundle_id: com.example.myapp
  metadata_dir: ./metadata
  submit:
    create_version:    "1.8.0"
    screenshots:       true
    metadata:          true
    submit_for_review: false
  upload_build:
    export_method:     app-store-connect
    team_id:           "ABCDE12345"
01 dependencies

Only Apple Design Resources.

The one outside asset is Apple Design Resources for device bezels. Apple's license doesn't allow redistribution, so the PSDs can't live in the repo. Mount the DMG, run storescreens bezels import, and the CLI scans /Volumes/, classifies each PSD by device, and writes transparent-screen PNGs locally. One-time setup per machine.

02 versioning

Auto-bump before archive.

Queries App Store Connect and TestFlight first. If MARKETING_VERSION is already shipped, bumps the patch version (1.2.3 → 1.2.4) and resets CURRENT_PROJECT_VERSION to 1. If the version is still editable but that build number is taken on TestFlight, bumps the build only. No "build number already exists" from altool.

03 rendering

Your real UI, not a mock.

render takes the exact screens your XCUITests captured - real data, real layout, real fonts - and composes bezels + captions on top. When the UI changes, the store artwork regenerates.

04 i18n

Locales are a list, not a project.

Add a line to locales. The same UI tests run per locale; metadata for every language lives as plain .txt files per locale in ./metadata/. Translation diffs cleanly in PRs.

05 ai / agents

MCP server in the box.

storescreens-mcp exposes every capability as MCP tools with streaming progress. Drop it in .mcp.json and Claude Code can drive a capture, inspect a screenshot inline, and edit a caption.

06 foundations

One YAML. No Ruby.

A single static binary, Homebrew-installable, MIT-licensed. No Gymfile, no Deliverfile, no Fastfile, no gem lockfile drift. Reads one storescreens.yml. That is the whole surface area.