From appmate
Optimizes App Store metadata (title, subtitle, keywords) for a single app using keyword ranking and popularity analysis. Automates ASO optimization via App Store Connect APIs.
How this skill is triggered — by the user, by Claude, or both
Slash command
/appmate:aso-optimizeThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> This skill is the single authoritative reference for the ASO optimization flow. Re-read it before every run.
This skill is the single authoritative reference for the ASO optimization flow. Re-read it before every run.
Every step in this skill calls App Store Connect APIs. Before any other step, run:
python3 scripts/appmate_config.py check
If exit code ≠ 0, STOP. Do not invoke any other part of this skill, do not run scripts/aso_optimize_v2.py. Tell the user AppMate credentials are not configured, show the precheck output verbatim, and tell them to invoke the appmate-setup skill. The downstream script also enforces this gate (exits 2 with the same message).
| Item | Content |
|---|---|
| User input | An app identifier (bundle id / App Store id / SKU / fuzzy name match — any one) |
| Final output | 3 paste-ready strings for App Store Connect: title, subtitle, keywords |
| Stage artifacts | data/phase_a_<slug>.json · data/phase_b_<slug>.json · data/aso_optimize_<slug>.md |
| Tool | Purpose | Called by |
|---|---|---|
data/apps_full.json | Static metadata | script |
data/sales_cache.json | Sales report (find the main market) | script |
| iTunes Search Top-200 | Keyword ranking (same source as the App Store web page) | script |
| Keyword reference table | popularity (1-99) + difficulty (1-99) | script |
| LLM (Claude) | CJK / Chinese-Japanese-Korean tokenization (when the App Store metadata is in those languages) / candidate generation / comparison / synthesis | conversation layer |
references/aso-methodology.md | Full methodology (§1-§12) | LLM reference |
<app> identifier.Everything in between runs automatically without interruption.
python3 scripts/aso_optimize_v2.py analyze <app>
pick_locales_for_country).title / subtitle / keywords.Artifact: data/phase_a_<slug>.json (metadata + tokens cut by the script's basic tokenizer).
Read the current_metadata field of data/phase_a_<slug>.json and cut real ASO words — apply CJK / Chinese-Japanese-Korean tokenization when the App Store metadata is in those languages (the metadata's own locale, independent of what language you're conversing in with the user):
网络翻译邮箱地图地球).Typical output: 30-40 real tokens covering all the semantics of the existing metadata.
python3 scripts/aso_optimize_v2.py validate <app> --candidates <Step 2 output>
Artifact: data/phase_b_<slug>.json
| Quadrant | Marker | Condition | Handling |
|---|---|---|---|
| Tier 1 — high relevance, high heat | 🟢 Target | rank ≤ 20 AND pop ≥ 40 | Keep / promote to Title or Subtitle |
| Tier 2 — high relevance, low heat | ⚪ Niche | rank ≤ 20 AND pop < 40 | Long-tail placeholder, keep in Keywords |
| Tier 3 — low relevance, low heat | 💀 Junk | pop ≤ 10 OR (rank > 100 AND pop ≤ 20) | Must remove |
| Tier 4 — low relevance, high heat | 🟡 Push | pop ≥ 40 AND rank > 50 | diff ≤ 60 push; > 60 skip |
Selection priority (§10.1): brand words > industry words > competitor words.
§10.1 industry-word tree expansion (core word + business association / generic suffix, 4 angles): common words, media-related, transaction-related, software platform.
The 7 strategies:
| # | Strategy | § basis |
|---|---|---|
| 1 | Synonym expansion | §10.1 |
| 2 | Long-tail combination (core + suffix) | §10.4 |
| 3 | Hypernyms / hyponyms | §10.1 |
| 4 | Spelling variants / plurals | §2.4 (English only) |
| 5 | Brand-ecosystem association | §10.1 |
| 6 | Similar-competitor inspiration | §10.1 |
| 7 | Plural / compound completion | §2.4 |
Before producing candidates, read the indie keyword reference for the target app's primary market:
zh-Hans / zh-Hant / cn → data/keyword_reference_cn.jsonen-US / en-GB / us → data/keyword_reference_us.jsonEach row carries {keyword, popularity, popularity_is_floor, difficulty, apps_count, source_apps_count, source_apps, top_category}. This is a static
table built from the top-100 indie apps in that store — it is internal
context only.
Use it as:
top_category matches the
target app's category (or adjacent). Promote those whose semantics overlap
the app's Step-2 tokens into the candidate pool before running the 7
strategies. Reason: indie-validated words have a higher prior of being
worth real estate.source_apps_count (indie-validated). When
popularity_is_floor: true, treat popularity as "no signal" (don't use
the literal 5) and decide via difficulty + indie evidence alone.source_apps_count ≥ 8 means ≥ 8% of all indie
top-100 apps target the word. Still usable, but reserve for Title /
Subtitle star slot; avoid as Keywords filler (saturated; low marginal CVR).popularity ≥ 40 AND source_apps_count ≤ 1 AND difficulty < 60 is the most valuable bucket. Seed these aggressively.Output discipline (do NOT do):
The user sees the standard pop / diff / rank numbers; the candidate set is
just smarter.
Produce 10-15 candidates per round.
Same as Step 3 — each round automatically runs validate, overwriting data/phase_b_<slug>.json.
Every validated candidate must land in a quadrant and be handled per its rule (same table as Step 4).
| Field | Admission condition |
|---|---|
| Title (≤30 char) | pop ≥ 40 OR rank ≤ 5 |
| Subtitle (≤30 char) | pop ≥ 30 OR rank ≤ 10; no duplicates of Title (§6.2) |
| Keywords (≤100 char) | pop ≥ 20 OR rank ≤ 20; no duplicates of Title / Subtitle tokens (§7.3 #3) |
| Long-tail combo (pop=5 rank≤5) | enters Keywords only when replacing a weaker token |
Candidate X replaces existing Y only when:
pop(X) ≥ pop(Y) × 1.5
OR
rank(X) ≤ 50 AND rank(Y) > 100
OR
pop(X) ≥ 20 AND pop(Y) ≤ 5
| Rule | Application |
|---|---|
| ① Comma-separated | Keywords forced to , |
| ② No duplicate tokens | case-insensitive dedup |
| ③ No duplicates of Title / Subtitle tokens | ✓ |
| ④ Order does not affect weight | (sort by pop descending for maintainability) |
| ⑤ Split phrases into words | Apple does mix-and-match automatically |
| ⑥ Singular form | English only; not applicable to Chinese |
| ⑦ Skip free words | reject: app, the, by, free, best, top, leading, 最, 免费, 工具, 软件 |
| ⑧ Use all 100 chars | utilization ≥ 95% |
| ⑨ Fill by pop descending | ✓ |
best / top / #1 / leading / free / install now / 最 / 推荐有道云笔记 / 印象笔记 / 滴答清单 / Notion / Evernote / Bear / Obsidian / Workflowy / LogseqHealth & Fitness / Utilities / Productivity / 工具 / 软件 / 免费 / 应用 (used alone as placeholders)|rank(singular) - rank(plural)| / rank(singular) > 15% → keep both forms.loop:
candidates = LLM_generate(rejected_pool, 10-15)
validated = script_validate(candidates)
passed = filter(validated, by 7.1-7.5)
if len(passed) >= 3:
proceed to Step 8
rejected_pool ∪= candidates
iteration += 1
if iteration >= 3:
proceed to Step 8 (with the current passed set)
else:
back to loop (avoiding rejected_pool)
| Rule | Application |
|---|---|
| Embedded token count | 3-5 (more hurts CVR §5.5) |
| Must contain a §4.2 star keyword | at least 1 with pop ≥ 40 AND rank ≤ 10 |
| Separator strategy (§5.3) | : separates brand + description; & replaces and; prefer word roots |
| Banned (§5.4) | best / top / #1 / leading / free / install now / 最 |
| Preserve brand recognition (§5.3) | a strong brand word must not be dropped |
Premise: the algorithm weight is insensitive to order, but order indirectly affects ranking strength through CVR.
| Position | What goes here | § basis |
|---|---|---|
| 1st | weak brand → descriptive core word first; strong brand → brand word first | §5.3 STARZ vs Down Dog example |
| 2nd-3rd | high-pop keywords (≥ 40) | §9.5 visual recognition → CVR |
| End | long-tail / modifiers / platform qualifiers | §5.3 space-saving technique |
Judging brand strength: the app's current rank on its own brand keyword. Already #1 → strong brand → brand word first. Not #1, or a category word is more famous → weak brand → descriptive word first.
| Rule | Application |
|---|---|
| No duplicates of Title tokens | ✓ §6.2 rule 1 |
| Avoid vague words | banned: most popular / social networking / 强大的 / 优秀的 / 极致 / 简洁 |
| Token count | 3-5 tokens with pop ≥ 30 across different dimensions |
| Dimension suggestions | function words / internationalized brand / competitor-adjacent / synonyms |
| Position | What goes here |
|---|---|
| 1st | the word that best explains the core function (the user's "one decisive sentence") |
| 2nd-3rd | synonyms / internationalized category words (English category words like Memo / Stickies) |
| End | long-tail placeholders / competitor-adjacent words |
| Rule | Application |
|---|---|
| ①-⑨ all 9 rules apply | see 7.4 |
| §10.4 CJK compression (zh/ja/ko metadata) | high-pop words may form a 3-5 word comma-free run |
| Character utilization | ≥ 95/100 char |
| Order has no effect (§7.3 #4 explicit) | sort by pop descending for readability only |
Must check: does the app have both zh-Hans + en-GB (or a same-region dual locale) enabled? If only one → Step 8 also outputs a second keyword-field suggestion (no duplicate words, expanding to 200 char).
The output footer must include:
pop AND diff columns (diff is needed to flag which words are diff<60 "short-term reachable" vs diff>60 "long-term weight only").analyze every 30 days to watch the rank trend.⚠️ Wording rule (must follow): in the deliverable document, every position / field reference must use the full name —
Title / Subtitle / Keywords(or the user's-language equivalent, e.g.主标题 / 副标题 / 关键词). The single letters T / S / K / X are not allowed (this applies to section rule text and checklists too). Reason: an abbreviation has no context when the user reviews it.
# <App> · ASO Optimization Suggestions
**Generated at** / **Main market** / **App info**
## Three strings (paste-ready)
Title (X/30 char): <NEW>
Subtitle (X/30 char): <NEW>
Keywords (X/100 char): <NEW>
## OLD vs NEW comparison
| Field | OLD | NEW | Δ |
## Deletion list
| Word | Original position | pop | diff | rank | Reason |
("Original position" column: write `Title` / `Subtitle` / `Keywords`, not T/S/K)
**Column rules**:
- `pop` / `diff` / `rank` are mandatory — all three numbers are needed to verify §10.1 Tier / §10.4 / §7.2 decisions.
- `Reason` column must be a **plain-language sentence** explaining why the word is being deleted — written so a non-technical reader understands. Cite specific evidence (pop / diff / rank values, Apple behavior, semantic reasoning) instead of `§` shortcuts. The official `§` rule may be cited inline as supporting context, but the bulk of the cell is a readable sentence, not a code-like reference. Bad: `§10.1 Tier 3`. Good: `Zero search volume (pop=5), and Apple's CJK tokenizer already splits "组件" out of the Title's "桌面小组件" — repeating it wastes 2 chars`.
## Addition list
| Word | New position | pop | diff | rank | Reason |
(same: `Title` / `Subtitle` / `Keywords`)
**Column rules**: same as the Deletion list. The `Reason` column is a plain-language sentence stating (a) why this word is worth a slot, (b) which evidence drives the decision (pop / diff / rank), (c) what the expected outcome is (short-term rank push vs long-term weight accumulation). Bad: `§10.1 Tier 4 push`. Good: `pop 65 high + diff 48 unusually low; OneSearch can also surface system SMS; placing it in the Title should reach top 20 in 30-60 days`.
## §10.2 dual-locale expansion audit (if applicable)
## §10.5 long-term weight tracking
## 📌 Post-delivery suggestions (required — user-facing options)
Rendered in the same language the user has been using in this conversation. Default to English; if the user has been writing in Chinese / Japanese / Spanish / etc., translate the template headers, labels and prose accordingly. The proposed App Store metadata strings (title / subtitle / keywords) must remain in the target App Store's locale (e.g. zh-Hans for the CN store) regardless — only the surrounding explanation follows the user's conversation language.
After delivering the 3 strings, always append these two suggestions for the user to choose from:
Trigger condition: the main-market locale ∈ {zh-Hans, zh-Hant, ja, ko}.
Output template (translate into the user's conversation language; this English version is the source):
### 📌 Suggestion ① · CJK comma-free compression
If your main market is CN/JP/KR/TW/HK, you can drop the commas in the Keywords field
and let Apple's CJK tokenizer mix-and-match automatically:
- **Gain**: frees N characters (1 per comma), letting you cram N more tokens
- **Risk**: loses the "explicit target word" boundary, relies on Apple's auto-tokenization
- **Recommended strategy**: keep commas around high-pop words (≥ 40); compress the low-pop long-tail at the end
Comparison:
Comma version (X/100, N words): A,B,C,D,E,F,...
Compressed (Y/100, M words): A,B,CDE,FGH,... ← tail compressed
Apply the compressed version? (yes / no / partial)
When not to output: if the main market is an English/Latin-script market, skip this suggestion.
Always output (regardless of market). Translate into the user's conversation language; this English version is the source:
### 📌 Suggestion ② · Whether to fill the character limit
Current utilization:
· Title X/30 char (X%)
· Subtitle X/30 char (X%)
· Keywords X/100 char (X%)
Options:
- **A. Keep as is** (tight, high-confidence word set)
- **B. Fill Title/Subtitle too** → I'll add more §10.1 second-tier long-tail words
- **C. Fill Keywords only to 100 char** → top up to 99-100 char
- **D. Fill everything** (max coverage but mixes in mid-pop words)
Which one? (A / B / C / D)
After the user chooses: re-output the 3 strings per the chosen option; leave the other sections unchanged.
# Step 1: anchor
python3 scripts/aso_optimize_v2.py analyze <app>
# Step 3 / Step 6: validate (each round)
python3 scripts/aso_optimize_v2.py validate <app> --candidates kw1,kw2,kw3,...
# Inspect
python3 scripts/aso_optimize_v2.py show-a <app>
python3 scripts/aso_optimize_v2.py show-b <app>
Steps 2 / 4 / 5 / 7 / 8 are all done by the LLM (Claude in the conversation).
| Parameter | Value | Source |
|---|---|---|
| Title max length | 30 char | §5.1 |
| Subtitle max length | 30 char | §6.1 |
| Keywords max length | 100 char | §7.1 |
| Keywords utilization floor | ≥ 95% | §7.3 #8 |
| Title embedded token count | 3-5 | §5.5 |
| Subtitle embedded token count | 3-5 | §6.2 |
| Single validate cap | 30 candidates | tool limit |
| Auto-iteration cap | 3 rounds | Step 7.7 |
| Passed-candidate threshold | ≥ 3 | Step 7.7 |
| Title pop admission | ≥ 40 OR rank ≤ 5 | Step 7.2 |
| Subtitle pop admission | ≥ 30 OR rank ≤ 10 | Step 7.2 |
| Keywords pop admission | ≥ 20 OR rank ≤ 20 | Step 7.2 |
| Replacement ratio (pop) | new ≥ old × 1.5 | Step 7.3 |
| §10.1 Tier 1 | rank ≤ 20 AND pop ≥ 40 | §10.1 |
| §10.1 Tier 3 | pop ≤ 10 must remove | §10.1 |
This is about tokenizing App Store metadata that is itself in CJK languages — independent of the language you're conversing in with the user.
Do not use jieba or any automatic tokenizer. Reasons: jieba boundaries are often wrong (e.g. 便利贴 cut into 便利 + 贴); long CJK mashed runs produce noise; the LLM has semantic understanding and can recognize complete ASO words. LLM tokenization features: realistic word length (mostly 2-4 chars for Chinese), no long mashed runs, recognizes compound words (桌面便签 is one ASO word, not 桌面 + 便签), brand variants (便笺 ≠ 便签), typos / homophones.
source_apps_count, top_category, or data/keyword_reference_* anywhere in the deliverable (§5.0 consultation must stay silent)aso-daily-report skill finds an app's keyword dropping out of the top 20 → trigger this workflow (aso_optimize_v2.py analyze <app>) for a deep optimization.npx claudepluginhub fengyiqicoder/appmate --plugin appmateConducts keyword research, optimizes metadata, and analyzes competitors for Apple App Store and Google Play Store listings. Helps improve app visibility and conversion rates.
Generates optimized App Store/Google Play metadata—titles, subtitles, keywords, descriptions, promo text, What's New—in multiple languages via 4 agent teams for keyword research, competitor analysis, copywriting, localization. Outputs MD reports.
Researches keywords, analyzes competitors, optimizes metadata, and tracks performance for Apple App Store and Google Play Store listings.