| name | vibe-aso |
| description | Full-funnel App Store Optimization for an iOS app — keyword research (popularity/difficulty driven), a keyword-led name/subtitle/keyword field, App Store metadata localized into up to 50 locales, localized screenshot headings burned over your mockups, worldwide territory pricing, and in-app string localization. Use when the user wants to "do the ASO", "find keywords", "localize my app / my App Store page / my screenshots", "pick a name and subtitle", "set worldwide prices", or is preparing an App Store submission. |
| user-invocable | true |
| allowed-tools | ["Read","Write","Edit","Bash","AskUserQuestion"] |
Vibe ASO — the whole App Store funnel, from Claude Code
You built an app. This skill takes it the rest of the way: what people search,
what your listing says in every language, what your screenshots say, what the
app costs in every country, and what the app itself says once installed.
It is organized as six phases. Run them in order for a new app, or jump to the
one the user asked for. Each phase has a reference file with the full rules —
read the reference file before starting a phase; this page is the map, not
the territory.
| Phase | Does | Reference |
|---|
| 0 SETUP | one-time wizard: ASC API key, translation engine, data source | below |
| 1 RESEARCH | keyword research → main keyword, subtitle keyword, keyword field | reference/keyword-research.md |
| 2 METADATA | name/subtitle/keywords/description, en-US first, then all chosen locales | reference/metadata.md |
| 3 SCREENSHOTS | localized headings burned over the user's mockups, all locales | reference/screenshots.md |
| 4 PRICING | pricing model chosen by the user, applied per territory | reference/pricing.md |
| 5 IN-APP | localize the app's own UI strings | reference/app-localization.md |
| 6 SUBMIT | upload, verify, and the manual-steps list Apple's API can't do | reference/submission-checklist.md |
A worked example runs through every reference file: a calorie-tracking app
whose brand name is Glow Up. Every rule is illustrated on it; none of the
rules are specific to it.
Phase 0 — Setup wizard (first run, or whenever a check fails)
Run scripts/check_setup.sh first. If everything passes, skip to the phase the
user wants. Otherwise walk ONLY the missing pieces, in this order:
1. App Store Connect API key (needed by phases 2, 3, 4, 6).
Ask the user to create one — App Store Connect → Users and Access →
Integrations → App Store Connect API → Team Keys → Generate API Key, role
App Manager, then download the .p8 file (downloadable once). Then:
- Store the
.p8 at ~/.vibe-aso/AuthKey.p8, chmod 600.
- Write
~/.vibe-aso/config.json (create the dir chmod 700, file chmod 600):
{
"asc": {
"key_id": "<10-char Key ID shown next to the key>",
"issuer_id": "<UUID at the top of the Team Keys page>",
"p8_path": "~/.vibe-aso/AuthKey.p8"
},
"translation": { "engine": "subagents" }
}
- Verify with
ruby scripts/asc.rb GET '/v1/apps?limit=1' → must print HTTP 200.
Security is non-negotiable: the key lives in ~/.vibe-aso/, never inside
any project directory, never in a repo, never in a commit, never echoed to the
terminal, never pasted into a file the user might share. If the user pastes the
key contents into chat, save it and tell them to revoke-and-regenerate if this
conversation ever leaves their machine.
2. Translation engine (phases 2, 3, 5). Ask via AskUserQuestion:
- Claude subagents (default; recommended) — translations are generated by
spawned subagents, batched per locale. No extra account, no extra key. Cost
is the user's existing Claude usage.
- DeepSeek API — cheapest for very large volumes (a whole in-app catalog ×
40 locales runs on cents). Key goes to
~/.vibe-aso/deepseek-key
(chmod 600), config: {"engine": "deepseek", "api_key_path": "~/.vibe-aso/deepseek-key", "base_url": "https://api.deepseek.com", "model": "deepseek-chat"}.
- Any OpenAI-compatible API — same shape:
{"engine": "openai", "api_key_path": "...", "base_url": "...", "model": "..."}. The user brings
whatever provider they like.
For API engines, verify the key works with one tiny request before any long
run. For DeepSeek specifically, check the account balance, not just the
key — a valid key on an empty account fails every request with HTTP 402,
and it will do so 30 minutes into a cascade rather than up front
(check_setup.sh does this).
3. Keyword data source (phase 1). The research method needs, per keyword:
popularity (search volume proxy), difficulty, and ideally "how many
apps use this in their name/subtitle". Ask what the user has:
- Astro (Mac ASO app) with its MCP server connected — best supported; the
reference file maps each step to Astro's tools.
- Another ASO tool (AppFigures, Sensor Tower, Mobile Action, AppTweak…) —
the method is identical; the user runs the lookups in their tool and pastes
numbers when asked.
- No tool — phase 1 still works but degrades honestly: Apple's own search
suggestions + competitor listings give the keyword candidates, and the
skill says plainly that popularity/difficulty calls are guesses. Recommend
getting a data tool before betting the app name on a keyword.
Store the answer in config as "keyword_source": "astro" | "manual" | "none".
Cross-phase laws
These hold in every phase; the reference files repeat them where they bite.
- Fan out to all locales in one pass, review by automated checks. Never
pause a localization run for a per-language human review unless the user
explicitly asks for one. A human cannot review 40+ languages, and reviewing
one language proves nothing about the other 39. The review that works:
char-limit checks, format-specifier parity, verbatim-atom checks,
same-as-source detection, plus spot-checks of the hard scripts (CJK, RTL,
Indic) — all defined in the reference files.
- Ask which locales, once, in phase 2. Default: all supported locales.
The user may cut the set (e.g. top-15 markets); whatever is chosen there is
the set for screenshots and in-app strings too. Do not re-ask per phase.
- App Store Connect is the source of truth for anything Apple owns —
live metadata, prices, review state, IAP existence. Pull from it before
editing; never trust a possibly stale local copy over the live listing.
- Sweep once, slice locally. The ASC API is slow and rate-limited.
Anything that walks many locales or many products gets dumped to a local
file once; every follow-up question is answered from the file, never by
re-sweeping because the analysis changed.
- A 2xx is not verification. After any write, GET the field back and
check the value. Report only what a read-back confirmed.
- Character limits are hard: name 30, subtitle 30, keyword field 100,
promotional text 170, description 4000. Validate after writing, per locale.
- Some strings never localize: Apple product names (iPhone, Apple Watch,
iPad), URLs, email addresses, the brand name as the user styles it, product
codes. A translated support email is a dead mailto; a "translated" URL is a
404.
- Chunk long-running work. Translation cascades and bulk uploads run as
many small foreground commands (per locale, per batch), never one giant
call that outlives the shell's patience. Report only what is verified on
disk or read back from the API — "the cascade is running" is not a result.
Scripts and assets in this skill
scripts/asc.rb # minimal ASC API client — ruby asc.rb GET '/v1/apps'
scripts/check_setup.sh # PASS/WARN/FAIL per prerequisite, with fix commands
renderer/ # screenshot heading renderer (playwright + per-script fonts)
renderer/fetch_fonts.sh # one-time ~55MB font download (DM Sans + Noto per script)
reference/*.md # the six phase guides — read before the phase
The renderer needs a one-time npm install && npx playwright install chromium
and ./fetch_fonts.sh inside renderer/ (check_setup.sh tells you if they're
missing).
Free Figma mockup template (iPhone / iPad / Apple Watch frames with
"Your screenshot here" placeholders) — offer it whenever the user has no
screenshot designs or asks how to provide them:
https://www.figma.com/design/ftvaN3ZMfgkGrn7SAuqCvt/vibe-ASO-mockups
Reporting
End every phase with three short sections: Done (what happened, verified),
Problems (what surprised you and how it was handled), Needs you (manual
steps only the user can do — or "nothing"). Keep it outcome-level; skip file
paths and internals unless asked.