Back to skills
extension
Category: Development & EngineeringNo API key required

App Store 审核图自动生成器

基于 Intel OpenVINO 加速的 Z-Image-Turbo INT4 量化模型,在本地 AI PC 上自动生成符合 Apple App Store 规范的产品上架审核截图。

personAuthor: dudu007hubModelScope

Model: snake7gun/Z-Image-Turbo-int4-ov (ModelScope INT4)
SKILL_VERSION: v1.0.0

Network usage: Setup downloads pip dependencies (some pinned to git+https commits) from github.com, and the model (~10 GB, resume supported) from modelscope.cn. Inference is fully offline — no network calls once setup is complete.

First time? Before using this skill, run these two scripts once in a terminal:

python setup.py          # creates venv, installs dependencies (~5 min)
python download_model.py # downloads the model (~10 GB, resumable)

Both scripts are in the skill directory alongside this SKILL.md.

Directory layout (all auto-created)

{USERNAME}_openvino\
├── venv\                               ← shared venv (created by setup.py)
└── appstore_screenshots\
    ├── state.json                      ← written by setup.py
    ├── generate_appstore.py            ← written in Step 2
    ├── Z-Image-Turbo-int4-ov\         ← downloaded by download_model.py (~10 GB)
    └── outputs\YYYYMMDD_HHMMSS_{app_name}_{device_type}.png

⚠️ Agent instructions

  1. Windows / PowerShell only. Never use Linux commands (ls, rm, cat). Never use && or call.
  2. Every step reads state.json itself — do not pass paths between steps manually.
  3. Use VENV_PY from state.json for all python calls — never use system python for inference.
  4. CRITICAL — Never skip Step 2. Always run the version-check python script to write generate_appstore.py. Never use the Write tool to create or modify it manually.
  5. CRITICAL — If generate_appstore.py fails, do NOT rewrite it manually. Delete it and re-run Step 2's python script to regenerate.
  6. Goal: generate Apple App Store-compliant listing screenshots and send previews.

Auto-recovery policy — try before asking user:

  • If STATE=MISSING or VENV_PY=BROKEN: automatically run setup.py (up to 3 attempts). Only ask user if all 3 fail.
  • If MODEL_STATUS=MISSING: automatically run download_model.py (up to 3 attempts). Stop if a single attempt exceeds 20 minutes — download supports resume, partial progress is not lost.
  • Always announce before each attempt: ⚙️ Auto-installing environment (attempt N/3)…

Pipeline — follow exactly in order, no skipping:

Step 0: extract app info  → APP_NAME, CATEGORY, TONE, DEVICE_TYPE, STYLE, EXPANDED_PROMPT
Step 1: verify environment  → VENV_PY, APPSTORE_DIR confirmed ready
         ↳ if STATE=MISSING or VENV_BROKEN: auto-run setup.py (3 attempts)
         ↳ if MODEL_STATUS=MISSING: auto-run download_model.py (3 attempts)
Step 2: verify deps + write generate_appstore.py → SCRIPT_UPDATE=DONE/SKIPPED  ← NEVER skip
Step 3: generate + send     → [SUCCESS] + image previews

Step 0: extract app info and build generation prompt (LLM only — no tools)

Analyze the user's input (app screenshot image + text description) and extract:

  1. APP_NAME: App name (English or Chinese)
  2. CATEGORY: App category — choose from: productivity, utility, finance, health, education, entertainment, social, shopping
  3. TONE: Visual tone — choose from: minimalist, professional, tech, playful, casual, vibrant
  4. DEVICE_TYPE: Target device — choose from: iphone_67 (1290x2796), iphone_61 (1179x2556), ipad_129 (2048x2732). Default: iphone_67
  5. STYLE: Visual style for generation — choose from: minimalist, tech, playful, professional
  6. EXPANDED_PROMPT: Detailed image generation prompt in the following structure:

Prompt structure: [app category] [key features] [visual style] [color scheme] [layout] [quality tags]

Quality tags: App Store screenshot, clean UI, modern design, high quality, professional

| Input | Style | Expanded prompt | |-------|-------|-----------------| | 任务管理App | minimalist | A minimalist productivity app screenshot showing task list interface, clean white background with subtle shadows, modern iOS design, App Store screenshot, clean UI, high quality | | 健身追踪App | tech | A sleek fitness tracking app screenshot with dark gradient background, neon accent colors, data visualization charts, App Store screenshot, modern design, professional |

Show the result before proceeding:

📝 App:       {app_name}
   Category:  {category}
   Tone:      {tone}
   Device:    {device_type} ({width}x{height})
   Style:     {style}
   Prompt:    {expanded_prompt}

Step 1: verify environment and model

🔍 Step 1/3: checking environment and model…

python -c "
import json, os, string, subprocess
from pathlib import Path

state = None
for d in string.ascii_uppercase:
    sf = Path(f'{d}:\\\\') / f'{os.environ.get(\"USERNAME\",\"user\").lower()}_openvino' / 'appstore_screenshots' / 'state.json'
    if sf.exists():
        state = json.loads(sf.read_text(encoding='utf-8'))
        break

if not state:
    print('STATE=MISSING')
    exit(1)

venv_py      = Path(state['VENV_PY'])
appstore_dir = Path(state['APPSTORE_DIR'])
model_dir    = appstore_dir / 'Z-Image-Turbo-int4-ov'

r = subprocess.run([str(venv_py), '--version'], capture_output=True, timeout=10)
if r.returncode != 0:
    print('VENV_PY=BROKEN')
    exit(1)

print(f'VENV_PY={venv_py}')
print(f'APPSTORE_DIR={appstore_dir}')

required = ['transformer', 'vae_decoder', 'text_encoder']
missing  = [r for r in required if not (model_dir / r).exists()]
if not missing:
    total = sum(f.stat().st_size for f in model_dir.rglob('*') if f.is_file()) / 1024**3
    print(f'MODEL_STATUS=READY  ({total:.2f} GB)')
else:
    print(f'MODEL_STATUS=MISSING  missing={missing}')
    exit(1)
"

On success: record VENV_PY and APPSTORE_DIR from output, proceed to Step 2.


If STATE=MISSING or VENV_PY=BROKEN → auto-run setup.py

python -c "
from pathlib import Path
p = Path(r'{baseDir}') / 'setup.py'
print(f'SETUP_PY={p}') if p.exists() else print('SETUP_PY=NOT_FOUND')
"

Announce and run (up to 3 attempts):

⚙️ Environment not initialized — auto-installing (attempt 1/3)…
python "<SETUP_PY path>"

Re-run Step 1's check after each attempt. If all 3 fail, show manual fallback below.


If MODEL_STATUS=MISSING → auto-run download_model.py

python -c "
from pathlib import Path
p = Path(r'{baseDir}') / 'download_model.py'
print(f'DOWNLOAD_PY={p}') if p.exists() else print('DOWNLOAD_PY=NOT_FOUND')
"

Announce to user and ask how to proceed:

📥 Model not found — download required (~10 GB)
   Estimated time:
   • 100 Mbps → ~15 min
   •  50 Mbps → ~30 min
   •  10 Mbps → ~2 hr
   Download supports resume — safe to interrupt and retry.

   ✅ Start auto-download
   📂 I'll download manually — show me the link

Auto-download (up to 3 attempts, stop if a single attempt exceeds 20 minutes):

python "<DOWNLOAD_PY path>"

Re-run Step 1's check after each attempt.

Manual download fallback:

ModelScope page: https://modelscope.cn/models/snake7gun/Z-Image-Turbo-int4-ov/files

Place all files under <APPSTORE_DIR>\Z-Image-Turbo-int4-ov\. Required subdirs:

Z-Image-Turbo-int4-ov\
├── transformer\
├── vae_decoder\
└── text_encoder\

Then re-run Step 1's check to verify.


Manual fallback (only if all 3 setup auto-attempts fail)

python -c "
from pathlib import Path
skill_dir = Path(r'{baseDir}')
for script in ['setup.py', 'download_model.py']:
    p = skill_dir / script
    if p.exists(): print(f'{script}={p}')
"

Show user:

⚠️ Auto-install failed. Please run manually in a terminal:

① Install environment:
   python "<full path to setup.py>"
   Takes ~5 min, fully automated.

② Download model (~10 GB):
   python "<full path to download_model.py>"
   Resumable — safe to interrupt and retry.

Come back here when done.

Step 2: verify deps and write generate_appstore.py

✍️ Step 2/3: checking dependencies and script version…

First verify dependencies (run via VENV_PY):

& "<VENV_PY>" -c "
import json, site
from pathlib import Path

EXPECTED_COMMITS = {
    'optimum_intel': '2f62e5ae',
    'diffusers':     'a1f36ee3',
}

def get_git_commit(pkg_name):
    dirs = site.getsitepackages()
    try: dirs += [site.getusersitepackages()]
    except Exception: pass
    for d in dirs:
        for dist in Path(d).glob(f'{pkg_name}*.dist-info'):
            url_file = dist / 'direct_url.json'
            if url_file.exists():
                data = json.loads(url_file.read_text(encoding='utf-8'))
                return data.get('vcs_info', {}).get('commit_id', 'no_vcs_info')
    return 'not_found'

results = {}
for pkg, imp in [('openvino','openvino'),('torch','torch'),('Pillow','PIL'),('modelscope','modelscope'),('cv2','cv2'),('yaml','yaml')]:
    try:
        ver = getattr(__import__(imp), '__version__', 'OK')
        results[pkg] = ('OK', ver)
    except ImportError as e:
        results[pkg] = ('MISSING', str(e))

try:
    from optimum.intel import OVZImagePipeline
    results['OVZImagePipeline'] = ('OK', 'importable')
except ImportError as e:
    results['OVZImagePipeline'] = ('MISSING', str(e))

for pkg_name, exp in EXPECTED_COMMITS.items():
    actual = get_git_commit(pkg_name)
    if actual == 'not_found':
        results[f'{pkg_name}@commit'] = ('MISSING', 'not installed via git+https')
    elif actual.startswith(exp):
        results[f'{pkg_name}@commit'] = ('OK', actual[:16])
    else:
        results[f'{pkg_name}@commit'] = ('WRONG', f'got {actual[:16]} want {exp}...')

all_ok = all(v[0] == 'OK' for v in results.values())
for k, (status, detail) in results.items():
    icon = '✅' if status == 'OK' else ('⚠️' if status == 'WRONG' else '❌')
    print(f'  {icon} {k}: {detail}')
print('DEP_CHECK=PASS' if all_ok else 'DEP_CHECK=FAIL')
"

| Output | Action | |--------|--------| | DEP_CHECK=PASS | ✅ Proceed to write script below | | DEP_CHECK=FAIL (MISSING) | ⛔ Re-run setup.py and retry | | DEP_CHECK=FAIL (@commit WRONG) | ⛔ Force reinstall: & "<VENV_PY>" -m pip uninstall optimum-intel diffusers -y then & "<VENV_PY>" -m pip install -r "{baseDir}\requirements_imagegen.txt" --no-cache-dir |

Then write generate_appstore.py:

python -c "
import json, os, string, re
from pathlib import Path

state = None
for d in string.ascii_uppercase:
    sf = Path(f'{d}:\\\\') / f'{os.environ.get(\"USERNAME\",\"user\").lower()}_openvino' / 'appstore_screenshots' / 'state.json'
    if sf.exists():
        state = json.loads(sf.read_text(encoding='utf-8'))
        break

if not state:
    print('[ERROR] state.json not found — re-run Step 1')
    exit(1)

appstore_dir = Path(state['APPSTORE_DIR'])
CURRENT_VERSION = 'v1.0.0'
script = appstore_dir / 'generate_appstore.py'

existing = None
if script.exists():
    m = re.search(r\"SKILL_VERSION\s*=\s*[\\\"'](.*?)[\\\"']\", script.read_text(encoding='utf-8', errors='ignore'))
    if m: existing = m.group(1)

if existing == CURRENT_VERSION:
    print('SCRIPT_UPDATE=SKIPPED')
else:
    code = r\'\'\'
SKILL_VERSION = \"v1.0.0\"
import sys, io, os, json, string, argparse, re, subprocess
from datetime import datetime
from pathlib import Path

# ── Apple App Store specs ──────────────────────────────
APPLE_SPECS = {
    \"iphone_67\": {\"width\": 1290, \"height\": 2796, \"label\": \"iPhone 6.7 inch\"},
    \"iphone_61\": {\"width\": 1179, \"height\": 2556, \"label\": \"iPhone 6.1 inch\"},
    \"ipad_129\":  {\"width\": 2048, \"height\": 2732, \"label\": \"iPad 12.9 inch\"},
}

# ── Z-Image supported resolutions (closest match) ─────
SUPPORTED_RES = [
    \"720x1280 ( 9:16 )\",
    \"1152x2048 ( 9:16 )\",
    \"1024x1024 ( 1:1 )\",
    \"896x1152 ( 7:9 )\",
]

def get_state():
    for d in string.ascii_uppercase:
        sf = Path(f\"{d}:\\\\\") / f\"{os.environ.get('USERNAME','user').lower()}_openvino\" / \"appstore_screenshots\" / \"state.json\"
        if sf.exists():
            return json.loads(sf.read_text(encoding='utf-8'))
    return None

def get_device():
    import openvino as ov
    core = ov.Core()
    devs = core.available_devices
    print(f\"[INFO] Available devices: {devs}\")
    for d in devs:
        if \"GPU\" in d:
            print(f\"[INFO] Using Intel GPU: {d}\")
            return d
    print(\"[INFO] Using CPU\")
    return \"CPU\"

def find_closest_res(target_w, target_h):
    target_ratio = target_w / target_h
    best = SUPPORTED_RES[0]
    best_diff = float('inf')
    for res in SUPPORTED_RES:
        m = re.search(r\"(\\d+)\\s*[xX]\\s*(\\d+)\", res)
        if m:
            w, h = int(m.group(1)), int(m.group(2))
            diff = abs(w / h - target_ratio)
            if diff < best_diff:
                best_diff, best = diff, res
    return best

def parse_res(res):
    m = re.search(r\"(\\d+)\\s*[xX]\\s*(\\d+)\", res)
    return (int(m.group(1)), int(m.group(2))) if m else (1024, 1024)

def add_text_overlay(image, title, position=\"top\", font_size=48, color=(255, 255, 255)):
    \"\"\"Add text overlay using PIL.\"\"\"
    from PIL import ImageDraw, ImageFont
    draw = ImageDraw.Draw(image)
    try:
        font = ImageFont.truetype(\"arial.ttf\", font_size)
    except (OSError, IOError):
        font = ImageFont.load_default()
    bbox = draw.textbbox((0, 0), title, font=font)
    tw = bbox[2] - bbox[0]
    if position == \"top\":
        x, y = (image.width - tw) // 2, 40
    elif position == \"center\":
        x, y = (image.width - tw) // 2, (image.height - font_size) // 2
    else:
        x, y = (image.width - tw) // 2, image.height - 80 - font_size
    draw.text((x, y), title, fill=color, font=font)
    return image

def make_filename(app_name, device_type, style):
    date_str = datetime.now().strftime('%Y%m%d_%H%M%S')
    safe = re.sub(r'[^\\w]', '_', app_name.strip())[:20].strip('_')
    return f\"{date_str}_{safe}_{device_type}_{style}.png\"

def generate(prompt, app_name='', device_type='iphone_67', style='professional',
           steps=9, seed=42, title=None, output_path=None):
    state = get_state()
    if not state:
        print(\"[ERROR] state.json not found — run setup.py\")
        sys.exit(1)

    appstore_dir = Path(state['APPSTORE_DIR'])
    model_dir    = appstore_dir / 'Z-Image-Turbo-int4-ov'
    out_dir      = appstore_dir / 'outputs'
    out_dir.mkdir(parents=True, exist_ok=True)

    required = ['transformer', 'vae_decoder', 'text_encoder']
    missing  = [r for r in required if not (model_dir / r).exists()]
    if missing:
        print(f\"[ERROR] Model incomplete: {missing} — run download_model.py\")
        sys.exit(1)

    # Apple spec resolution
    spec = APPLE_SPECS.get(device_type, APPLE_SPECS[\"iphone_67\"])
    width, height = spec[\"width\"], spec[\"height\"]

    # Find closest supported resolution
    res_str = find_closest_res(width, height)
    gen_w, gen_h = parse_res(res_str)

    device = get_device()
    print(f\"[INFO] Loading model: {model_dir}\")

    import torch
    from optimum.intel import OVZImagePipeline
    from diffusers import FlowMatchEulerDiscreteScheduler

    pipe = OVZImagePipeline.from_pretrained(str(model_dir), device=device)
    pipe.scheduler = FlowMatchEulerDiscreteScheduler(
        num_train_timesteps=1000, shift=3.0)
    print(\"[INFO] Model loaded\")

    gen = torch.Generator('cpu').manual_seed(seed) if seed >= 0 else None
    print(f\"[INFO] Inference: {gen_w}x{gen_h} -> {width}x{height}, steps={steps}, seed={seed}\")
    print(f\"[INFO] Device: {device_type} ({spec['label']})\")

    image = pipe(
        prompt=prompt, height=gen_h, width=gen_w,
        num_inference_steps=steps, guidance_scale=0.0, generator=gen
    ).images[0]

    # Resize to exact Apple spec
    if gen_w != width or gen_h != height:
        image = image.resize((width, height))

    # Add title overlay if provided
    if title:
        image = add_text_overlay(image, title, position=\"top\")

    if output_path is None:
        output_path = str(out_dir / make_filename(app_name, device_type, style))
    image.save(output_path)
    print(f\"[SUCCESS] {output_path}\")
    print(f\"[META] device={device_type}, resolution={width}x{height}, style={style}, seed={seed}, steps={steps}\")
    try:
        subprocess.Popen(['explorer', output_path])
        print(\"[INFO] Opened in default viewer\")
    except Exception as e:
        print(f\"[WARN] Could not open image: {e}\")
    return output_path

if __name__ == \"__main__\":
    sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace', line_buffering=True)
    sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace', line_buffering=True)
    try:
        p = argparse.ArgumentParser()
        p.add_argument(\"--prompt\",    required=True)
        p.add_argument(\"--app_name\",  default='app')
        p.add_argument(\"--device\",    default='iphone_67')
        p.add_argument(\"--style\",     default='professional')
        p.add_argument(\"--title\",     default=None)
        p.add_argument(\"--steps\",     type=int, default=9)
        p.add_argument(\"--seed\",      type=int, default=42)
        p.add_argument(\"--output\",    default=None)
        args = p.parse_args()
        print(generate(args.prompt, args.app_name, args.device, args.style,
                       args.steps, args.seed, args.title, args.output))
        sys.stdout.flush()
    except Exception as e:
        import traceback
        print(f\"[FATAL] {type(e).__name__}: {e}\", flush=True)
        traceback.print_exc()
        sys.exit(1)
\'\'\'
    script.write_text(code.strip(), encoding='utf-8')
    print('SCRIPT_UPDATE=DONE')

print(f'EXISTS={script.exists()}')
"

| Output | Action | |--------|--------| | SCRIPT_UPDATE=SKIPPED | ✅ Already up to date, proceed to Step 3 | | SCRIPT_UPDATE=DONE | ✅ Script written, proceed to Step 3 | | EXISTS=False | ⛔ Write failed — check directory permissions on APPSTORE_DIR |


Step 3: generate App Store screenshot and send preview

🎨 Step 3/3: running inference…

Run these two commands separately:

$env:PYTHONUTF8 = "1"
& "<VENV_PY>" "<APPSTORE_DIR>\generate_appstore.py" --prompt "EXPANDED_PROMPT" --app_name "APP_NAME" --device "DEVICE_TYPE" --style "STYLE" --title "APP_NAME" --steps 9 --seed 42

Pass: stdout contains [SUCCESS]. Record OUTPUT_PATH from the [SUCCESS] line.

Send preview via message tool:

action: "send"  filePath: "OUTPUT_PATH"  message: "✅ APP_NAME - DEVICE_TYPE App Store Screenshot (STYLE style)"

Final announcement:

✅ Done! Path: <OUTPUT_PATH>
📝 App: {app_name} | Category: {category} | Style: {style}
⚙️ Device: {device_type} ({width}×{height}), steps=9, seed=42 | device: {CPU/GPU}

Apple App Store Specifications

| Device | Resolution | Aspect Ratio | Safe Area (top/bottom/left/right) | |--------|-----------|-------------|-----------------------------------| | iPhone 6.7" | 1290×2796 | 9:19.5 | 180 / 180 / 60 / 60 | | iPhone 6.1" | 1179×2556 | 9:19.5 | 160 / 160 / 50 / 50 | | iPad 12.9" | 2048×2732 | 3:4 | 200 / 200 / 100 / 100 |


Visual Styles

| Style | Colors | Best For | |-------|--------|----------| | minimalist | White, light gray, subtle accent | Productivity, utility, finance | | tech | Dark gradient, blue/purple accents | Technology, developer tools, SaaS | | playful | Vibrant colors, rounded shapes | Entertainment, social, gaming | | professional | Navy, white, gold accents | Business, finance, education |


Parameters

| Param | Default | Notes | |-------|---------|-------| | --prompt | required | English or Chinese | | --app_name | app | App name for filename | | --device | iphone_67 | iphone_67, iphone_61, ipad_129 | | --style | professional | minimalist, tech, playful, professional | | --title | None | Title text overlay on screenshot | | --steps | 9 | Higher = more detail; no hard limit | | --seed | 42 | -1 = random | | --output | auto | Custom absolute output path |

guidance_scale is fixed at 0.0 and not exposed as a parameter.


Multi-device batch generation

To generate screenshots for multiple device types at once, run Step 3 multiple times with different --device values:

& "<VENV_PY>" "<APPSTORE_DIR>\generate_appstore.py" --prompt "..." --device iphone_67 --style minimalist
& "<VENV_PY>" "<APPSTORE_DIR>\generate_appstore.py" --prompt "..." --device iphone_61 --style minimalist
& "<VENV_PY>" "<APPSTORE_DIR>\generate_appstore.py" --prompt "..." --device ipad_129 --style minimalist

Troubleshooting

| Error | Cause | Fix | |-------|-------|-----| | STATE=MISSING | setup.py never run | Run python setup.py from the skill directory | | VENV_PY=BROKEN | venv corrupted | Re-run python setup.py — rebuilds venv automatically | | MODEL_STATUS=MISSING | download never run or interrupted | Run python download_model.py — resumes automatically | | DEP_CHECK=FAIL (MISSING) | packages not installed in venv | Re-run setup.py | | DEP_CHECK=FAIL (@commit WRONG) | PyPI release installed instead of pinned commit | Uninstall optimum-intel + diffusers, reinstall with --no-cache-dir | | @commit shows not installed via git+https | git was missing when pip ran | Confirm git is installed, re-run setup.py | | [ERROR] Model incomplete | Download interrupted mid-file | Re-run download_model.py — resumes automatically | | [ERROR] state.json not found | state.json missing | Re-run Step 1 | | EXISTS=False | No write permission on APPSTORE_DIR | Check directory permissions | | RuntimeError on GPU | Insufficient VRAM | Lower resolution or hardcode return "CPU" in get_device() | | Black / noisy output | Too few steps | Use --steps ≥ 4; 9 recommended | | Download timeout | Network issue or proxy needed | Configure proxy and retry — download supports resume |