
Appshots Maestro Capture
- 4 installs
- 1 repo stars
- Updated March 19, 2026
- truongduy2611/app-screenshots-cli-skills
Helps with ai & agent building tasks.
About
appshots-maestro-capture is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- appshots-maestro-capture
- AI & Agent Building
- AI-coding skill
Appshots Maestro Capture by the numbers
- 4 all-time installs (skills.sh)
- Ranked #13,372 of 16,546 AI & Agent Building 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-maestro-captureAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 4 |
|---|---|
| repo stars | ★ 1 |
| Last updated | March 19, 2026 |
| Repository | truongduy2611/app-screenshots-cli-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
Maestro Screenshot Capture
Maestro is a declarative, YAML-driven mobile UI automation tool with cross-framework support (Flutter, SwiftUI, UIKit, Compose, React Native). Use it to navigate apps, interact with elements, and capture screenshots — without writing bash scripts or manual sleep timers.
Install → Write YAML Flows → Run → ScreenshotsPrerequisites
# Install Maestro CLI (official installer — NOT brew, which installs a different app)
curl -fsSL "https://get.maestro.mobile.dev" | bash
# Add to PATH (the installer adds this to .zshrc, but for current session)
export PATH="$PATH:$HOME/.maestro/bin"
# Verify installation
maestro --version⚠️ Do NOT use `brew install maestro` — that installs a code editor cask, not the mobile testing CLI.
Supported frameworks: Flutter (3.19+), SwiftUI, UIKit, Jetpack Compose, Android XML, React Native.
Core Concepts
Why Maestro over Bash + AXe?
| Problem with Bash/AXe | Maestro Solution |
|---|---|
Manual sleep timers | Auto-waits for elements to appear |
| Coordinate-based taps break across devices | Uses semantic identifiers |
| No retry on flaky interactions | Built-in retry with configurable tolerance |
| iOS Simulator only | iOS + Android with same YAML flow |
| Long imperative scripts | Short declarative YAML |
Element Selection Priority
Maestro finds elements in this order (use the most specific one available):
1. `id:` — Matches accessibility identifiers ← preferred
- Flutter:
Semantics(identifier: '...') - SwiftUI:
.accessibilityIdentifier("...") - UIKit:
.accessibilityIdentifier = "..." - Compose:
Modifier.semantics { testTag = "..." } - Android XML:
android:tag="..." - React Native:
testID="..."
2. `text:` — Matches visible text on screen 3. `label:` — Matches accessibility label 4. Relative selectors — below:, above:, leftOf:, rightOf:, containedIn:
See the `appshots-accessibility-ids` skill for detailed per-framework identifier setup.
Writing a Capture Flow
Create .maestro/ directory at your project root. Each flow is a .yaml file.
Basic Flow: capture_home.yaml
appId: com.example.myapp # Your app's bundle ID
---
- launchApp
# Home screen
- takeScreenshot: 01_home
# Navigate to a tab
- tapOn:
id: "tab_search" # Uses accessibility identifier
- takeScreenshot: 02_search
# Scroll down
- scroll
- takeScreenshot: 03_scrolledAdvanced Flow: capture_detail.yaml
appId: com.example.myapp
---
- launchApp
# Scroll to find a card
- scrollUntilVisible:
element:
id: "card_featured_item"
direction: DOWN
timeout: 10000
# Tap the card
- tapOn:
id: "card_featured_item"
# Wait for detail screen to load
- tapOn:
id: "play_button"
# Wait for content to appear
- extendedWaitUntil:
visible: "SCORE"
timeout: 8000
- takeScreenshot: detail_gameplayFlow with Conditional Waits
appId: com.example.myapp
---
- launchApp
# Navigate to a feature
- tapOn:
id: "feature_camera"
# Tap a sample item
- tapOn:
id: "sample_item_0"
# Wait for processing to finish (look for a result element)
- extendedWaitUntil:
visible:
id: "result_item_0"
timeout: 15000
# Tap result to show detail popup
- tapOn:
id: "result_item_0"
# Wait for popup to animate in
- extendedWaitUntil:
visible:
id: "detail_popup"
timeout: 5000
- takeScreenshot: feature_detail_popupRunning Flows
Single flow
maestro test .maestro/capture_home.yamlAll flows in directory
maestro test .maestro/Specific device
maestro test --device <UDID> .maestro/capture_home.yamlCustom output directory
maestro test --test-output-dir ./screenshots/raw .maestro/capture_home.yamlMulti-Locale Screenshot Capture
Strategy: One Simulator Per Locale (Parallel)
For fastest captures, create a dedicated simulator per locale and run flows in parallel.
#!/bin/bash
# scripts/maestro-multi-locale.sh
# Usage: bash scripts/maestro-multi-locale.sh [locale1] [locale2] ...
# Example: bash scripts/maestro-multi-locale.sh en vi ja
set -e
DEVICE_TYPE="com.apple.CoreSimulator.SimDeviceType.iPhone-16-Pro-Max"
RUNTIME="com.apple.CoreSimulator.SimRuntime.iOS-18-3"
APP_PATH="build/ios/iphonesimulator/Runner.app"
BUNDLE_ID="com.example.app"
FLOW_DIR=".maestro"
OUTPUT_BASE="./screenshots/raw"
# 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
LOCALES="${@:-en}"
# Map locale codes to Apple format
get_apple_locale() {
case "$1" in
en) echo "en_US" ;;
vi) echo "vi_VN" ;;
ja) echo "ja_JP" ;;
ko) echo "ko_KR" ;;
zh) echo "zh_CN" ;;
de) echo "de_DE" ;;
fr) echo "fr_FR" ;;
es) echo "es_ES" ;;
pt) echo "pt_BR" ;;
*) echo "${1}_${1^^}" ;;
esac
}
capture_locale() {
local locale="$1"
local apple_locale
apple_locale=$(get_apple_locale "$locale")
local lang="${locale}"
local sim_name="Screenshot_${locale}"
local output_dir="${OUTPUT_BASE}/${locale}"
echo "[$locale] Creating simulator..."
local udid
udid=$(xcrun simctl create "$sim_name" "$DEVICE_TYPE" "$RUNTIME" 2>/dev/null || \
xcrun simctl list devices | grep "$sim_name" | grep -oE '[A-F0-9-]{36}' | head -1)
echo "[$locale] UDID: $udid"
# Boot and set locale
xcrun simctl boot "$udid" 2>/dev/null || true
sleep 2
if [ "$IS_NATIVE_IOS" = true ]; then
# Native iOS: change system locale
xcrun simctl spawn "$udid" defaults write "Apple Global Domain" AppleLocale "$apple_locale"
xcrun simctl spawn "$udid" defaults write "Apple Global Domain" AppleLanguages -array "$lang"
xcrun simctl shutdown "$udid"
sleep 1
xcrun simctl boot "$udid"
sleep 5
else
# Flutter / Shared Prefs: write directly to app preferences
local prefs_dir=$(xcrun simctl get_app_container "$udid" "$BUNDLE_ID" data)/Library/Preferences
defaults write "$prefs_dir/$BUNDLE_ID" selected_locale -string "$lang"
# No reboot needed for app-level locale
fi
# Install app
xcrun simctl install "$udid" "$APP_PATH"
# Run Maestro flows
mkdir -p "$output_dir"
maestro test \
--device "$udid" \
--test-output-dir "$output_dir" \
"$FLOW_DIR/"
echo "[$locale] Capture complete → $output_dir"
# Cleanup: delete the temporary simulator
xcrun simctl shutdown "$udid" 2>/dev/null || true
xcrun simctl delete "$udid" 2>/dev/null || true
}
if [ "$BUILD_APP" = true ]; then
# Build the app first (adjust for your framework)
# Flutter: flutter build ios --simulator
# Xcode (native): xcodebuild -scheme MyApp -sdk iphonesimulator -derivedDataPath build
# React Native: npx react-native build-ios --mode=Release --simulator
echo "Building app for simulator..."
# ⬇ Replace with your framework's build command
flutter build ios --simulator
fi
# Run captures (parallel for speed)
for locale in $LOCALES; do
capture_locale "$locale" &
done
wait
echo "All captures complete! Check $OUTPUT_BASE"Maestro Studio (Interactive Discovery)
Use maestro studio to visually inspect your app's UI tree and generate YAML commands interactively:
maestro studioThis launches a browser-based UI where you can:
- Click elements to see their identifiers, text, and labels
- Generate YAML by performing actions in the studio
- Verify selectors before writing them into flow files
This is the Maestro equivalent of axe describe-ui, but visual and interactive.Agent Guidelines
Thinking Process
When given a task like "capture screenshots of my app across 3 locales":
1. Check for `.maestro/` directory — Reuse existing flows if present 2. Discover identifiers — Run maestro studio or inspect code for accessibility identifiers (see appshots-accessibility-ids skill) 3. Write YAML flows — One flow per logical screen group (home, detail, settings) 4. Test single locale first — maestro test .maestro/ on the default simulator 5. Scale to multi-locale — Use scripts/maestro-multi-locale.sh en vi ja 6. Hand off to design — Switch to appshots-design-workflow skill for framing and styling
Key Tips
- Always use `id:` over `text:` — Text changes across locales, identifiers don't
- Use `extendedWaitUntil` — For screens that need loading time (network, animations)
- Use `scrollUntilVisible` — Instead of manual swipe coordinates
- One flow per screen group — Keeps flows focused and debuggable
- Use `--test-output-dir` — To control exactly where screenshots land
When to Fall Back to AXe
Maestro covers 95% of use cases. Use AXe (axe describe-ui, axe tap) only when:
- You need the raw accessibility tree JSON for debugging
- Maestro doesn't support a specific simulator interaction (e.g., hardware buttons)
- You're working in a CI environment that doesn't support Maestro
Reference: Maestro Commands for Screenshots
| Command | Purpose |
|---|---|
launchApp | Launch the app (auto-waits for ready) |
tapOn: | Tap an element by id, text, or label |
longPressOn: | Long press an element |
scroll | Scroll down on current screen |
scrollUntilVisible: | Scroll until an element is found |
swipe: | Swipe in a direction (LEFT, RIGHT, UP, DOWN) |
inputText: | Type text into focused field |
takeScreenshot: | Save screenshot with given name |
extendedWaitUntil: | Wait for element to appear (with timeout) |
assertVisible: | Assert an element is on screen |
back | Press back button (Android) |
hideKeyboard | Dismiss the keyboard |
clearState | Clear app data |
stopApp | Force stop the app |
# Example: Detail / Interactive Screen Capture Flow
# Captures screens that require navigation + interaction before screenshot.
appId: com.example.myapp # Replace with your bundle ID
---
# ============================================================
# Interactive List Item
# ============================================================
- launchApp
- scrollUntilVisible:
element:
id: "card_featured"
direction: DOWN
timeout: 10000
- tapOn:
id: "card_featured"
- extendedWaitUntil:
visible:
id: "detail_content"
timeout: 5000
- takeScreenshot: detail_featured
# ============================================================
# Interactive Screen with Play Button
# ============================================================
- launchApp
- scrollUntilVisible:
element:
id: "card_activity"
direction: DOWN
timeout: 10000
- tapOn:
id: "card_activity"
# Tap a start/play button within the screen
- tapOn:
id: "play_button"
# Wait for content to load
- extendedWaitUntil:
visible:
id: "activity_content"
timeout: 8000
- takeScreenshot: activity_gameplay
# ============================================================
# Screen with Delayed Content (e.g., animations, network data)
# ============================================================
- launchApp
- tapOn:
id: "card_dashboard"
# Wait for animated content to settle
- extendedWaitUntil:
visible:
id: "dashboard_chart"
timeout: 10000
- takeScreenshot: dashboard_loaded
# Example: Home Screen Capture Flow
# Captures the main screens of a typical mobile app.
# Place in .maestro/ directory at project root.
appId: com.example.myapp # Replace with your bundle ID
---
# ============================================================
# Home Screen
# ============================================================
- launchApp
- takeScreenshot: 01_home
# Scroll to show more content
- scroll
- takeScreenshot: 02_home_scrolled
# ============================================================
# Tab Navigation
# ============================================================
- tapOn:
id: "tab_search"
- takeScreenshot: 03_search
- tapOn:
id: "tab_profile"
- takeScreenshot: 04_profile
# ============================================================
# Feature Screen (e.g., Camera / Scanner)
# ============================================================
- launchApp # Fresh start
- tapOn:
id: "feature_camera"
- takeScreenshot: 05_camera
# Tap a sample item and wait for processing
- tapOn:
id: "sample_item_0"
- extendedWaitUntil:
visible:
id: "result_item_0"
timeout: 15000
# Tap result to show detail popup
- tapOn:
id: "result_item_0"
- extendedWaitUntil:
visible:
id: "detail_popup"
timeout: 5000
- takeScreenshot: 06_detail_popup
# ============================================================
# Settings
# ============================================================
- launchApp
- tapOn:
id: "tab_settings"
- takeScreenshot: 07_settings