Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
truongduy2611 avatar

Appshots Automation Pipeline

  • 8 installs
  • 1 repo stars
  • Updated March 19, 2026
  • truongduy2611/app-screenshots-cli-skills

Helps with automation & workflows tasks.

About

appshots-automation-pipeline is a Claude Code skill in the Automation & Workflows category.

  • appshots-automation-pipeline
  • Automation & Workflows
  • AI-coding skill

Appshots Automation Pipeline by the numbers

  • 8 all-time installs (skills.sh)
  • Ranked #1,519 of 2,715 Automation & Workflows skills by installs in the Skillselion catalog
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/truongduy2611/app-screenshots-cli-skills --skill appshots-automation-pipeline

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs8
repo stars1
Last updatedMarch 19, 2026
Repositorytruongduy2611/app-screenshots-cli-skills

What it does

Helps with automation & workflows tasks.

Files

SKILL.mdMarkdownGitHub ↗

App Screenshot Automation Pipeline

This skill teaches an AI agent how to automate the full App Store screenshot capture workflow:

Prepare App (Semantics) → Boot Simulators → Capture (Maestro/AXe) → Design Hand-off

Tool Selection

Choose the right capture tool based on your situation:

Need auto-waits, retry logic, or cross-platform capture?
├── YES → Use Maestro (see appshots-maestro-capture skill)
│         ├── Declarative YAML flows
│         ├── Auto-waits, no sleep timers
│         ├── Built-in takeScreenshot
│         ├── Cross-platform (iOS + Android)
│         └── Works with: Flutter, SwiftUI, UIKit, Compose, React Native
│
└── NO → Need raw accessibility tree JSON or iOS-only advanced control?
    ├── YES → Use AXe + bash scripts (see below)
    └── NO → Use Maestro
Recommended for most apps: Always start with Maestro. Fall back to AXe only for edge cases.

When to Use Each Tool

ScenarioTool
Any mobile app — cross-platform captureMaestro
Need auto-waits and retry logicMaestro
Need raw accessibility tree JSONAXe (axe describe-ui)
iOS-only native app (Swift/ObjC)Either (Maestro or AXe)
CI environment without Maestro supportAXe
Hardware button simulationAXe

Prerequisites

# Option A: Maestro (Recommended — use official installer, NOT brew)
curl -fsSL "https://get.maestro.mobile.dev" | bash
export PATH="$PATH:$HOME/.maestro/bin"

# Option B: AXe (Fallback)
brew install cameroncooke/axe/axe

# Always needed
# xcrun simctl — Built-in with Xcode (no install needed)

Workflow Overview

Phase 0: Prepare the App

Before capture, ensure your app has stable accessibility identifiers. See the `appshots-accessibility-ids` skill for:

  • Adding accessibility identifiers to tappable elements (per-framework API reference)
  • Naming conventions (screen_element pattern)
  • Verification with maestro studio or axe describe-ui

Phase 0.5: Demo Data & Pre-Capture Setup

Screenshots need the app to look populated and polished — empty states, blank lists, and "getting started" screens ruin App Store screenshots.

Before writing any capture flows, ask the user:

1. Does the app need demo/seed data? — Pre-populated lists, sample content, user profiles, etc. 2. Does the app have a demo mode or test account? — Some apps have built-in mechanisms. 3. Does any content need to be locale-specific? — Sample names, dates, currencies. 4. Are there any onboarding/tutorial screens to skip? — First-launch modals, permission dialogs. 5. Should the app show a specific state? — Logged-in, premium unlocked, specific tab selected.

Common Setup Strategies
StrategyWhen to useHow
Seed databaseApp has local DB (SQLite, Hive, Realm)Pre-populate with sample records via script or test fixture
SharedPreferences / UserDefaultsSkip onboarding, set flagsWrite keys before launch: xcrun simctl spawn <UDID> defaults write <bundle_id> has_seen_onboarding -bool true
Test accountApp requires loginUse a dedicated screenshot account with curated content
Demo mode flagApp supports itLaunch with flag: xcrun simctl launch <UDID> <bundle_id> --demo-mode
Mock APIApp fetches from serverUse a local mock server or cached responses
Clear and rebuildEnsure clean statexcrun simctl uninstall <UDID> <bundle_id> then reinstall
Example: SharedPreferences Setup (Flutter)
# Skip onboarding and set demo state before launching the app
UDID="booted"
BUNDLE_ID="com.example.myapp"

# Write to the app's shared preferences via simctl
PREFS_DIR=$(xcrun simctl get_app_container "$UDID" "$BUNDLE_ID" data)/Library/Preferences
defaults write "$PREFS_DIR/$BUNDLE_ID" has_completed_onboarding -bool true
defaults write "$PREFS_DIR/$BUNDLE_ID" demo_data_loaded -bool true
Key principle: The agent should always ask about demo data needs BEFORE writing capture flows. Capturing an empty app is wasted effort.

Phase 0.75: Detect Locale Handling Strategy

Apps handle locales in two fundamentally different ways. You MUST check the source code to determine which strategy the app uses — using the wrong one means screenshots won't change language.

Strategy A: System-Level Locale (Default)

The app follows the device/simulator language setting.

How to detect per framework:

FrameworkSystem locale indicators
FlutterStandard AppLocalizations with MaterialApp.localizationsDelegates, no custom locale provider
SwiftUI/UIKitUses NSLocalizedString / String(localized:), .lproj bundles, no in-app picker
AndroidUses strings.xml in values-ja/, values-ko/, etc., no custom locale logic
React NativeUses i18next or react-intl tied to I18nManager / system locale

How to switch locale (iOS Simulator):

xcrun simctl spawn "$UDID" defaults write "Apple Global Domain" AppleLanguages -array "ja"
xcrun simctl spawn "$UDID" defaults write "Apple Global Domain" AppleLocale "ja_JP"
xcrun simctl shutdown "$UDID" && xcrun simctl boot "$UDID"

How to switch locale (Android Emulator):

adb -s emulator-5554 shell "su 0 setprop persist.sys.locale ja-JP; setprop ctl.restart zygote"
Strategy B: App-Level Locale (Custom)

The app manages its own language setting internally. Changing the simulator/emulator locale has NO EFFECT.

How to detect per framework:

FrameworkApp-level locale indicators
FlutterSharedPreferences key for locale, custom LocaleProvider / LanguageCubit, easy_localization package, MaterialApp.locale set from state
SwiftUI/UIKitUserDefaults key for language, in-app language picker, custom LanguageManager class
AndroidSharedPreferences key for locale, custom LocaleHelper, in-app language dropdown, AppCompatDelegate.setApplicationLocales()
React NativeAsyncStorage key for language, i18next.changeLanguage() called from settings screen

How to switch locale:

# iOS: Write the preference directly before launch
PREFS_DIR=$(xcrun simctl get_app_container "$UDID" "$BUNDLE_ID" data)/Library/Preferences
defaults write "$PREFS_DIR/$BUNDLE_ID" selected_locale -string "ja"

# Android: Write SharedPreferences directly
adb shell "run-as $PACKAGE_NAME cat /data/data/$PACKAGE_NAME/shared_prefs/*.xml"
# Then modify and push back, or use Maestro to tap through the UI
# Or use Maestro to tap the in-app language picker
- tapOn:
    id: "tab_settings"
- tapOn:
    id: "language_picker"
- tapOn:
    text: "日本語"
Quick Detection Commands
# Flutter
grep -r "SharedPreferences.*locale\|SharedPreferences.*language\|easy_localization\|LocaleProvider\|LanguageCubit" lib/

# iOS Native (Swift)
grep -r "UserDefaults.*language\|LanguageManager\|setLanguage" --include="*.swift" .

# Android (Kotlin/Java)
grep -r "SharedPreferences.*locale\|LocaleHelper\|setApplicationLocales" --include="*.kt" --include="*.java" .

# React Native
grep -r "AsyncStorage.*language\|i18next.changeLanguage\|setLanguage" --include="*.js" --include="*.ts" --include="*.tsx" src/
If unsure, ask the user: _"Does your app follow the system locale, or does it have its own language picker in settings?"_

Phase 1: Capture Raw Screenshots

Option A: Maestro (Recommended)

See the `appshots-maestro-capture` skill for:

  • Writing YAML flows with takeScreenshot
  • Multi-locale parallel capture
  • scrollUntilVisible, extendedWaitUntil, and other smart commands

Option B: AXe + asc CLI (Fallback)

Create .asc/screenshots.json to define the steps AXe should take in the app.

{
  "version": 1,
  "app": {
    "bundle_id": "com.example.app",
    "udid": "booted",
    "output_dir": "./screenshots/raw"
  },
  "steps": [
    { "action": "launch" },
    { "action": "wait", "duration_ms": 2000 },
    { "action": "screenshot", "name": "01_home" },
    { "action": "tap", "id": "settings_tab" },
    { "action": "wait", "duration_ms": 1000 },
    { "action": "screenshot", "name": "02_settings" }
  ]
}

Note: Use `axe describe-ui` to find the correct `id` and `label` values for the steps.

Parallel Multi-Locale Capture (AXe)

Instead of sequentially rebooting one simulator, map each target locale to a dedicated simulator UDID created via xcrun simctl create. This allows parallel capture.

#!/bin/bash
# parallel-capture.sh — Fast multi-locale capture

declare -A LOCALE_UDID=(
  ["en-US"]="UDID_EN_US"
  ["ja-JP"]="UDID_JA_JP"
  ["ko-KR"]="UDID_KO_KR"
)

# Configuration
IS_NATIVE_IOS=false  # Set to true if app uses system locale
BUILD_APP=false      # Set to true to build the app before capturing
BUNDLE_ID="com.example.app"

if [ "$BUILD_APP" = true ]; then
  echo "Building app for simulator..."
  # ⬇ Replace with your framework's build command
  flutter build ios --simulator
fi

# 1. Boot all simulators and set their locales
for LOCALE in "${!LOCALE_UDID[@]}"; do
  (
    UDID="${LOCALE_UDID[$LOCALE]}"
    LANG="${LOCALE%%-*}"            # e.g., 'ja'
    APPLE_LOCALE="${LOCALE/-/_}"    # e.g., 'ja_JP'

    xcrun simctl boot "$UDID" || true
    
    if [ "$IS_NATIVE_IOS" = true ]; then
      # Native iOS: change system locale
      xcrun simctl spawn "$UDID" defaults write NSGlobalDomain AppleLanguages -array "$LANG"
      xcrun simctl spawn "$UDID" defaults write NSGlobalDomain AppleLocale -string "$APPLE_LOCALE"
      
      # Needs a reboot to apply locale changes cleanly
      xcrun simctl shutdown "$UDID"
      xcrun simctl boot "$UDID"
    else
      # Flutter / Shared Prefs: write directly to app preferences
      PREFS_DIR=$(xcrun simctl get_app_container "$UDID" "$BUNDLE_ID" data)/Library/Preferences
      defaults write "$PREFS_DIR/$BUNDLE_ID" selected_locale -string "$LANG"
    fi

    # Install the built app (adjust path for your framework)
    xcrun simctl install "$UDID" build/ios/iphonesimulator/Runner.app
  ) &
done
wait

# 2. Run captures in parallel using the asc plan
for LOCALE in "${!LOCALE_UDID[@]}"; do
  (
    UDID="${LOCALE_UDID[$LOCALE]}"
    asc screenshots run \
      --plan ".asc/screenshots.json" \
      --udid "$UDID" \
      --output-dir "./screenshots/raw/$LOCALE" \
      --output json
      
    echo "Captured $LOCALE on $UDID"
  ) &
done
wait
echo "All raw captures complete."

Phase 2: Design Hand-off

Once the raw screenshots are captured, immediately transition to the appshots-design-workflow skill to load these images into the App Screenshots desktop app for framing, backgrounds, text overlays, and PDF/PNG export.

---

Agent Guidelines

Thinking Process for AI Agents

When given a scenario like "capture screenshots of my fitness app", follow this process:

1. Ask about demo data — Does the app need seed data, a test account, or flags to skip onboarding? 2. Prepare the app — Add accessibility identifiers to key elements (see appshots-accessibility-ids skill) 3. Set up demo state — Seed data, write SharedPreferences flags, set up test account 4. Choose the capture tool — Maestro for most apps, AXe for edge cases 5. Discover the app — Find the bundle ID, build the app if needed 6. Boot a simulator — Pick the right device type for the target display 7. Write capture flows — Maestro YAML or asc JSON plan 8. Run single-locale first — Verify captures are correct 9. Scale to multi-locale — Parallel capture across all target locales 10. Design — Switch to appshots CLI skills to set up frames, text, backgrounds 11. Translate — Switch to appshots-translation skill for AI text translation 12. Export — Export final screenshots for all locales

Pre-Capture Questions Checklist

Always ask the user these questions before starting capture:

  • [ ] Which screens do you want to capture? (list all screenshots)
  • [ ] Does the app need demo/seed data to look populated?
  • [ ] Is there a test account or demo mode?
  • [ ] Any onboarding or permission dialogs to skip?
  • [ ] Which locales do you need? (e.g., en, ja, ko, de)
  • [ ] Any specific app state needed? (premium, logged in, specific tab)

Device Selection Strategy (App Store Connect)

When choosing which simulators to boot and generate screenshots for, follow this device tiering:

  • Required Default (6.9"): iPhone 16 Pro Max or iPhone 17 Pro Max. This covers the primary 6.9" App Store Connect requirement and should ALWAYS be captured.
  • Optional Sizes:
  • 6.5" (Optional): iPhone 15 Pro Max or iPhone 11 Pro Max.
  • 5.5" (Optional): iPhone 8 Plus (only if legacy compatibility is explicitly requested).

Key Tips

  • Prepare identifiers first — The biggest time sink is missing accessibility identifiers
  • Use Maestro for capture — Auto-waits eliminate fragile sleep timers
  • Use `--id` over coordinates — Identifiers are stable across locales and screen sizes
  • Test single locale first — Catch issues before scaling to all locales
  • Status Bar Overrides are Optional — Setting the simulator time to 9:41 and overriding network/battery states (xcrun simctl status_bar) is optional. By default, simulators usually look clean enough, and appshots designs often use backgrounds that can mask standard status bars.

Related skills

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.