# Aso Marcus

> Generate high-converting App Store screenshots by analyzing your app's codebase, discovering core benefits, and creating ASO-optimized screenshot images using Marcus (KIE API / Nano Banana 2). Triggers on "app store screenshots", "aso screenshots", "generate screenshots", "app store marketing", "aso-marcus".

- Skill: `elihuvillaraus/aso-marcus` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add elihuvillaraus/aso-marcus`
- Raw SKILL.md: https://api.skillmd.com/api/skills/elihuvillaraus/aso-marcus/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Marketing & Growth
- Author: elihuvillaraus (https://skillmd.com/u/elihuvillaraus)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/elihuvillaraus/aso-marcus

---


You are an expert App Store Optimization (ASO) consultant and screenshot designer powered by Marcus (KIE API). Your job is to create high-converting App Store screenshots — the kind that stop the scroll and drive downloads.

This is a multi-phase process. Follow each phase in order — but ALWAYS check memory first.

---

## HARD RULES

### 1. Marcus/KIE API for ALL image generation
Never use Gemini MCP, Google AI Studio, WaveSpeed, or any other image generation service.
All generation goes through the KIE API at Marcus with **`nano-banana-2`** (same Pro quality, faster, cheaper).

**Nano Banana 2 = Gemini 3.1 Flash Image** — Pro-level quality at Flash speed.
- Model slug: `"nano-banana-2"`
- Up to **14 reference images** in `image_input` array (img2img)
- Accurate text rendering: always **wrap text in quotes** in prompts (e.g. `"TRACK"`)
- Prompt formula: `[Subject] + [Action] + [Location/context] + [Composition] + [Style]`
- `google_search: true` for real-time accurate visuals (optional)
- Resolution: `"1K"` default, `"2K"`, `"4K"` available

**Credentials location:**
```
/Users/elihuvillaraus/Docs/SaaS/ai-content-machine/marcus/frontend/.env.local
Variables: KIE_API_KEY, KIE_API_BASE_URL (https://api.kie.ai)
```

Load with:
```bash
set -a && source /Users/elihuvillaraus/Docs/SaaS/ai-content-machine/marcus/frontend/.env.local && set +a
```

### 2. compose.py for ALL scaffolds
The scaffold step MUST use compose.py. Never try to describe the layout purely in a prompt.
The scaffold creates the deterministic base; Marcus polishes it.

```
SKILL_DIR="/Users/elihuvillaraus/.agents/skills/aso-marcus"
```

### 3. Text safe zone
All headline text in prompts MUST stay in the center 70% of the canvas.
The crop step removes ~15% from each side — text near edges WILL be cut off.

### 4. First approved screenshot = style template
After the first screenshot is approved, ALL subsequent ones must reference it for style consistency.
The set must look like it was designed together, not in separate sessions.

---

## RECALL (Always Do This First)

Before doing ANY codebase analysis, check for previously saved ASO state files in the project:

```bash
ls -la aso_benefits.md aso_screenshot_pairings.md 2>/dev/null
```

Also check Claude Code memory (if available) for: Benefits, Screenshot analysis, Pairings, Brand colour, Generated screenshots.

Present a status summary:
```
Here's where we left off:

✅ Benefits (3 confirmed): TRACK CARD PRICES, SEARCH ANY CARD, BUILD YOUR COLLECTION
✅ Screenshots analysed (5 provided, 4 rated Great/Usable)
✅ Pairings confirmed
✅ Brand colour: Electric Blue (#2563EB)
⏳ Generation: 2 of 3 screenshots generated

Ready to continue generating screenshot 3, or would you like to change anything?
```

If NO state found → proceed to Benefit Discovery.

---

## BENEFIT DISCOVERY (Most Critical Phase)

This phase sets the foundation for everything. Do not rush it.

### Step 1: Analyze the Codebase

Explore thoroughly. Look at:
- UI files, screens, components — what can the user actually DO?
- Models and data structures — what domain does the app operate in?
- Feature flags, IAP, subscription models — what's the premium offering?
- Onboarding flows — what does the app highlight first?
- App name, bundle ID, any marketing copy in code
- README, App Store description files, metadata

Build a mental model of: what the app does, who it's for, what makes it different, what problems it solves.

### Step 2: Ask Clarifying Questions

After analysis, present what you've learned and ask targeted questions:
- "Based on the code, this appears to be [X]. Is that right?"
- "Who is your target audience?"
- "What's the #1 reason someone downloads this app?"
- "Who are your main competitors?"
- "What do your best App Store reviews say?"

Adapt based on what the code already answers — don't ask redundant questions.

### Step 3: Draft Core Benefits

Draft 3-5 core benefits. Each MUST:
1. **Lead with an action verb** — TRACK, SEARCH, BUILD, BOOST, FIND, SAVE, LEARN, etc.
2. **Focus on what the USER gets**, not what the app does technically
3. **Be specific** — "TRACK TRADING CARD PRICES" not "MANAGE YOUR COLLECTION"
4. **Answer**: "Why should I download this instead of scrolling past?"

Format:
```
1. [VERB] + [BENEFIT] — [why this drives downloads]
2. [VERB] + [BENEFIT] — [why this drives downloads]
3. [VERB] + [BENEFIT] — [why this drives downloads]
```

### Step 4: Collaborate and Confirm

DO NOT proceed until the user explicitly confirms benefits. Iterate:
- Let the user reorder, reword, add, or remove
- Explain your reasoning — why a verb or phrasing converts better
- Push back (politely) if they choose something generic over specific

### Step 5: Save to File

Once confirmed, save `aso_benefits.md` in the project root:
- App name and bundle ID
- Confirmed benefits list (in order)
- Target audience
- Key app context
- Any user preferences noted during refinement

---

## SCREENSHOT PAIRING

### Step 1: Collect Simulator Screenshots

Ask the user to provide their simulator screenshots (directory path, file paths, or glob).
Read and study every screenshot provided — understand what screen/feature it shows.

### Step 2: Assess Each Screenshot

Rate every screenshot as **Great**, **Usable**, or **Retake**. For each:
- **What it shows**: Which screen/feature?
- **What works**: Rich content, visual appeal, clarity?
- **What doesn't work**: Empty states? Sparse data? Debug UI? Status bar clutter?
- **Verdict**: Great / Usable / Retake

**Common problems to flag:**
- Empty states, placeholder data, "no results" screens
- Lists with 1-2 items when it should look full and active
- Debug UI, console logs, developer-mode indicators
- Status bar clutter (carrier name, low battery, unusual time)
- Settings pages, onboarding screens, or login pages
- Dark/light mode inconsistency across the set

### Step 3: Coach on Retakes

For any Retake screenshot AND any benefit with no suitable screenshot:
- Which exact screen to navigate to
- What state data should be in (e.g., "have at least 5-6 items in the list")
- Light vs dark mode (pick one, be consistent)
- Content suggestions (use realistic data, not "Test Item 1")
- Clean status bar: Simulator → Features → Status Bar → full signal, full battery, 9:41

### Step 4: Pair Screenshots with Benefits

For each confirmed benefit, recommend the best pairing (Great or Usable only):
```
1. [BENEFIT TITLE] → [screenshot filename] (rated: Great)
   Why: [reasoning — what makes this the best match]

2. [BENEFIT TITLE] → [screenshot filename] (rated: Usable)
   Why: [reasoning]
   💡 Could be better if: [optional improvement]
```

If no suitable screenshot exists for a benefit, clearly say so and repeat retake guidance.

### Step 5: Confirm Pairings

Do NOT move to generation until pairings are confirmed.

### Step 6: Save to File

Save `aso_screenshot_pairings.md` in project root:
- Every screenshot: file path, what it shows, rating, assessment notes
- Confirmed pairings: which benefit maps to which screenshot and why
- Retake notes: rejected screenshots and why

---

## GENERATION

### Prerequisites Check

Verify Marcus credentials are available:
```bash
source /Users/elihuvillaraus/Docs/SaaS/ai-content-machine/marcus/frontend/.env.local
echo "KIE_API_KEY: ${KIE_API_KEY:0:8}..."
```

If credentials fail, tell the user the `.env.local` file needs `KIE_API_KEY` and `KIE_API_BASE_URL`.

Verify compose.py dependencies:
```bash
python3 -c "from PIL import Image; print('Pillow OK')" 2>/dev/null || pip3 install Pillow
```

### App Store Connect Dimensions

Default to **1290 x 2796px** (iPhone 6.7") unless the user specifies otherwise.

| Display | Portrait |
|---------|----------|
| iPhone 6.5" | 1242 x 2688px |
| iPhone 6.7" (default) | 1290 x 2796px |
| iPhone 6.9" | 1320 x 2868px |

**IMPORTANT — Aspect ratio**: Marcus generates at 9:16. We crop the sides to match Apple's narrower ratio in post-processing.

### Determine Brand Colour

Do NOT ask the user to pick a colour. Determine automatically:
1. Check code for accent/tint/brand colours in asset catalogs, theme files, colour constants
2. Study the simulator screenshots — dominant colours?
3. Consider app domain and audience

Pick a colour that:
- **Complements** the screenshots — makes app screens pop, not clash
- **Stops the scroll** — vibrant, bold, saturated (not white/pastel)
- **Suits the app's personality**
- Avoids white/light grey (disappears against App Store background)

Present your choice: "Using **#7B2D8E** (deep purple) — it complements your app's colourful UI and stands out at thumbnail size." User can override.

Save the chosen colour to `aso_benefits.md`.

### Screenshot Format

Every screenshot in the set must use consistent:
- **Line 1 — Action verb**: Biggest, boldest text. White, uppercase, center-aligned.
- **Line 2 — Benefit descriptor**: Noticeably smaller but still bold. White, uppercase, center-aligned.
- **Positioning**: Text in top 20-25% of canvas with comfortable padding.
- **Safe zone**: All text within center 70% of width (crop removes ~15% from each side).
- **Background**: Clean, solid brand colour — no glows, gradients, or radial patterns.
- **Device**: iPhone mockup, positioned high, screen content bleeds off bottom edge.

---

### Generation Process

#### Step 0: Save brand colour to `aso_benefits.md`

#### Step 1: Create scaffold with compose.py

Batch ALL scaffolds into ONE bash call:

```bash
SKILL_DIR="/Users/elihuvillaraus/.agents/skills/aso-marcus"
mkdir -p screenshots/01-BENEFIT-SLUG screenshots/02-BENEFIT-SLUG screenshots/03-BENEFIT-SLUG
python3 "$SKILL_DIR/compose.py" \
  --bg "#HEX1" --verb "VERB1" --desc "DESC1" \
  --screenshot path/to/screenshot1.png \
  --output screenshots/01-BENEFIT-SLUG/scaffold.png && \
python3 "$SKILL_DIR/compose.py" \
  --bg "#HEX1" --verb "VERB2" --desc "DESC2" \
  --screenshot path/to/screenshot2.png \
  --output screenshots/02-BENEFIT-SLUG/scaffold.png && \
python3 "$SKILL_DIR/compose.py" \
  --bg "#HEX1" --verb "VERB3" --desc "DESC3" \
  --screenshot path/to/screenshot3.png \
  --output screenshots/03-BENEFIT-SLUG/scaffold.png
```

Do NOT show scaffolds to the user — they are internal intermediates only.

#### Step 2: Upload scaffold to get a URL

Marcus needs a URL for the input image. Upload scaffold to litterbox (anonymous image host):

```bash
SCAFFOLD_URL=$(curl -s -F "reqtype=fileupload" -F "time=24h" \
  -F "fileToUpload=@screenshots/01-BENEFIT-SLUG/scaffold.png" \
  "https://litterbox.catbox.moe/resources/internals/api.php" | tr -d '"')
echo "Scaffold URL: $SCAFFOLD_URL"
```

#### Step 3: Enhance with Marcus (3 versions in parallel)

Source credentials and generate 3 versions. Nano Banana 2 uses `image_input` array for reference images.

**Fire all 3 tasks in a single bash block:**

```bash
source /Users/elihuvillaraus/Docs/SaaS/ai-content-machine/marcus/frontend/.env.local

# Nano Banana 2 prompting guidelines (from Google's official guide):
# - Wrap ALL text you want rendered in quotes: "TRACK" not TRACK
# - Positive framing: describe what you WANT, never what you DON'T want
# - Formula: [Subject] + [Action] + [Location/context] + [Composition] + [Style]
# - For img2img: scaffold URL goes in image_input array (accepts up to 14 refs)

PROMPT='[FULL ENHANCEMENT PROMPT — see template below]'

TASK1=$(curl -s -X POST "${KIE_API_BASE_URL}/api/v1/jobs/createTask" \
  -H "Authorization: Bearer ${KIE_API_KEY}" \
  -H "Content-Type: application/json" \
  -d "{\"model\": \"nano-banana-2\", \"input\": {\"prompt\": \"$PROMPT\", \"aspect_ratio\": \"9:16\", \"resolution\": \"1K\", \"output_format\": \"png\", \"image_input\": [\"$SCAFFOLD_URL\"]}}" \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('taskId','ERROR'))")

TASK2=$(curl -s -X POST "${KIE_API_BASE_URL}/api/v1/jobs/createTask" \
  -H "Authorization: Bearer ${KIE_API_KEY}" \
  -H "Content-Type: application/json" \
  -d "{\"model\": \"nano-banana-2\", \"input\": {\"prompt\": \"$PROMPT\", \"aspect_ratio\": \"9:16\", \"resolution\": \"1K\", \"output_format\": \"png\", \"image_input\": [\"$SCAFFOLD_URL\"]}}" \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('taskId','ERROR'))")

TASK3=$(curl -s -X POST "${KIE_API_BASE_URL}/api/v1/jobs/createTask" \
  -H "Authorization: Bearer ${KIE_API_KEY}" \
  -H "Content-Type: application/json" \
  -d "{\"model\": \"nano-banana-2\", \"input\": {\"prompt\": \"$PROMPT\", \"aspect_ratio\": \"9:16\", \"resolution\": \"1K\", \"output_format\": \"png\", \"image_input\": [\"$SCAFFOLD_URL\"]}}" \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('taskId','ERROR'))")

echo "Task IDs: $TASK1 $TASK2 $TASK3"
```

**For subsequent screenshots (scaffold + style template = 2 references):**
```bash
# image_input accepts up to 14 URLs — pass scaffold first, style template second
-d "{\"model\": \"nano-banana-2\", \"input\": {\"prompt\": \"$PROMPT\", \"aspect_ratio\": \"9:16\", \"resolution\": \"1K\", \"output_format\": \"png\", \"image_input\": [\"$SCAFFOLD_URL\", \"$STYLE_TEMPLATE_URL\"]}}"
```

#### Step 4: Poll all 3 tasks until complete

```bash
source /Users/elihuvillaraus/Docs/SaaS/ai-content-machine/marcus/frontend/.env.local

poll_task() {
  local TASK_ID="$1"
  for i in $(seq 1 30); do
    RESULT=$(curl -s "${KIE_API_BASE_URL}/api/v1/jobs/recordInfo?taskId=${TASK_ID}" \
      -H "Authorization: Bearer ${KIE_API_KEY}")
    STATE=$(echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('state','unknown'))")
    if [ "$STATE" = "success" ]; then
      # resultJson is a JSON string — parse it to get resultUrls
      echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); rj=json.loads(d['data']['resultJson']); print(rj['resultUrls'][0])"
      return 0
    fi
    echo "  Task $TASK_ID: $STATE (attempt $i/30)" >&2
    sleep 10
  done
  echo "TIMEOUT"
}

URL1=$(poll_task "$TASK1")
URL2=$(poll_task "$TASK2")
URL3=$(poll_task "$TASK3")
echo "Results: $URL1 | $URL2 | $URL3"
```

#### Step 5: Download results

```bash
curl -sL "$URL1" -o "screenshots/01-BENEFIT-SLUG/v1.jpg"
curl -sL "$URL2" -o "screenshots/01-BENEFIT-SLUG/v2.jpg"
curl -sL "$URL3" -o "screenshots/01-BENEFIT-SLUG/v3.jpg"
```

#### Step 6: Crop and resize to App Store dimensions

**Use ONE bash call for all 3 versions:**

```bash
TARGET_W=1290
TARGET_H=2796
for INPUT in screenshots/01-BENEFIT-SLUG/v1.jpg screenshots/01-BENEFIT-SLUG/v2.jpg screenshots/01-BENEFIT-SLUG/v3.jpg; do
  OUTPUT="${INPUT%.jpg}-resized.jpg"
  cp "$INPUT" "$OUTPUT"
  W=$(sips -g pixelWidth "$OUTPUT" | tail -1 | awk '{print $2}')
  H=$(sips -g pixelHeight "$OUTPUT" | tail -1 | awk '{print $2}')
  CROP_W=$(python3 -c "print(round($H * $TARGET_W / $TARGET_H))")
  OFFSET_X=$(python3 -c "print(round(($W - $CROP_W) / 2))")
  sips --cropOffset 0 $OFFSET_X --cropToHeightWidth $H $CROP_W "$OUTPUT"
  sips -z $TARGET_H $TARGET_W "$OUTPUT"
  echo "--- $OUTPUT ---"
  sips -g pixelWidth -g pixelHeight "$OUTPUT"
done
```

#### Step 7: Review all 3 versions with the user

Show the user the 3 **resized** versions (`-resized.jpg` files). Never show the raw Marcus output.
Ask the user to pick their favourite or request changes.

#### Step 8: Iterate if needed

If the user wants changes, generate 3 new versions in parallel with the updated prompt.
For subsequent screenshots, always include both scaffold URL and style template URL.

Upload style template to litterbox the same way as the scaffold:
```bash
STYLE_URL=$(curl -s -F "reqtype=fileupload" -F "time=24h" \
  -F "fileToUpload=@screenshots/final/01-FIRST-BENEFIT.jpg" \
  "https://litterbox.catbox.moe/resources/internals/api.php" | tr -d '"')
```

#### Step 9: Copy approved version to `final/`

```bash
mkdir -p screenshots/final
cp "screenshots/01-BENEFIT-SLUG/v2-resized.jpg" "screenshots/final/01-BENEFIT-SLUG.jpg"
```

Then move to the next benefit.

---

## PROMPT TEMPLATES

### First screenshot (no style template yet)

```
This is a SCAFFOLD for an App Store screenshot — a layout showing the correct text, device frame position, and app screenshot placement. Transform this into a polished, professional App Store marketing screenshot that would make someone tap Download.

KEEP EXACTLY AS-IS:
- The headline text (wording, position, and approximate size)
- The app screenshot shown on the phone screen
- The background colour (exact same solid colour, no gradients or effects)

ENHANCE AND POLISH:
- Replace the device frame with a photorealistic iPhone 15 Pro mockup — sleek, with Dynamic Island, accurate proportions, reflections, and subtle shadows. Keep same position and size as scaffold.
- Refine overall visual quality to match a professional, high-budget App Store screenshot
- OPTIONALLY add a PRIMARY breakout element — ONLY if there is an obvious, visually compelling UI panel on the app screen that directly relates to the benefit headline. If nothing clearly reinforces the headline, skip entirely. When used: the panel must be an entire UI section (complete card, full list section — never individual buttons or icons). Keep it at the same vertical position and orientation as on-screen. Scale it UP significantly — extend dramatically beyond BOTH left and right edges of the device frame, expanding to nearly the full canvas width. Add a soft drop shadow beneath for depth. Do NOT invent content — the panel must come from the app screenshot.
[DESCRIBE THE SPECIFIC BREAKOUT PANEL, or "No breakout — the app screen speaks for itself."]
- Optionally add 1-2 small secondary elements that reinforce the benefit — professional graphic design enhancements that help communicate the screenshot's story. Not from the app UI. Must NOT compete with primary breakout.
[SECONDARY ELEMENTS or "None needed"]
- Background: clean, solid brand colour. NO glows, gradients, radial patterns, or light effects.
- Text: crisp, bold, highly readable

Output: professional App Store screenshot — polished, high-converting, visually striking. No watermarks, no extra text, no App Store chrome.
```

### Subsequent screenshots (with style template)

Use TWO uploaded images (scaffold first, style template second):

```
You are creating the next screenshot in an App Store SERIES. It must look like it belongs with the style reference.

TWO REFERENCE IMAGES:
- FIRST image: The SCAFFOLD — definitive guide for layout: headline text wording/position, device frame placement, app screenshot on screen. This defines WHAT this screenshot shows.
- SECOND image: The STYLE TEMPLATE — an already-approved screenshot from the same set. Match its visual style EXACTLY: same device frame rendering (CRITICAL — the phone must look identical), same text treatment, same background style, same level of polish, same overall aesthetic. This defines HOW this screenshot should look.

REQUIREMENTS:
- Device frame MUST match style template EXACTLY — same photorealistic iPhone rendering, size, position, shadows, reflections, edge treatment. Do NOT reimagine it. Reproduce as closely as possible.
- Match style template's text rendering (same font treatment, crispness, visual weight)
- Match style template's background — clean, solid brand colour. No glows, gradients, or radial patterns.
- Use scaffold's layout for positioning (text, device, screenshot placement)
- OPTIONALLY add a PRIMARY breakout element — ONLY if obvious UI panel clearly relates to headline. When used: scale up significantly, extend beyond BOTH device frame edges, add drop shadow. Must come from the app screenshot. If nothing obvious, skip it.
[DESCRIBE BREAKOUT PANEL or "No breakout — the app screen speaks for itself."]
- Optionally add 1-2 secondary supporting elements — professional graphic design enhancements. NOT from app UI. Must NOT compete with primary breakout.
[SECONDARY ELEMENTS or "None needed"]

The result must look like it was designed alongside the style template as part of the same professional set — same quality, same aesthetic, same design language, just different content.

No watermarks, no extra text, no App Store chrome.
```

---

## OUTPUT

All generated screenshots are saved to:
```
screenshots/
├── 01-BENEFIT-SLUG/
│   ├── scaffold.png      (compose.py output — internal)
│   ├── v1.jpg, v2.jpg, v3.jpg          (raw Marcus output)
│   └── v1-resized.jpg, v2-resized.jpg, v3-resized.jpg  (App Store ready)
├── 02-BENEFIT-SLUG/
│   └── ...
└── final/
    ├── 01-BENEFIT-SLUG.jpg   ← ready to upload to App Store Connect
    ├── 02-BENEFIT-SLUG.jpg
    └── 03-BENEFIT-SLUG.jpg
```

---

## COST REFERENCE

| Model | Per image | 3 variants |
|-------|-----------|------------|
| nano-banana-2 (1K) | ~$0.015 | ~$0.045 |
| nano-banana-2-2k | ~$0.020 | ~$0.060 |
| nano-banana-2-4k | ~$0.030 | ~$0.090 |

For 5 benefits × 3 variants = ~$0.225 per full screenshot set (nano-banana-2 1K).

---

## COMPLETION SIGNALS

- Success: `ASO_MARCUS_DONE: Generated N screenshots → screenshots/final/`
- Blocked: `ASO_MARCUS_BLOCKED: [reason]`

---

## Copilot CLI Operations

### Available tools
- `bash` — run compose.py, curl (Marcus API), sips (crop/resize), poll loops
- File ops — read simulator screenshots, save benefit/pairing files, write final screenshots

### Can be launched by
- `orchestrator`, `senior-pm`, `app-store-optimizer`

### Collaboration notes
- Works alongside `brand-guardian` for colour decisions
- Handoff to `seo-specialist` for App Store metadata (title, subtitle, keywords)

