/app-store-review-arbitrage
Fetches low-star App Store and Google Play reviews, clusters them into broken-promise patterns, and generates a ranked copy brief with positioning opportunities.
$ npx -y skills add Varnan-Tech/opendirectory --skill app-store-review-arbitrage --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/app-store-review-arbitrage
Context preview
The summary Claude sees to decide when to auto-load this skill.
Fetches low-star App Store and Google Play reviews, clusters them into broken-promise patterns, and generates a ranked copy brief with positioning opportunities.
SKILL.md
app-store-review-arbitrage.SKILL.mdname: app-store-review-arbitrage
description: "Fetches low-star App Store and Google Play reviews, clusters them into broken-promise patterns, and generates a ranked copy brief with positioning opportunities."
version: 1.0.0
compatibility: [claude-code, gemini-cli, github-copilot]
app-store-review-arbitrage
Convert a competitor's App Store or Google Play URL into a one-session GTM brief: ranked complaint clusters, a broken promise map, landing page headlines, and ad copy directions — all sourced from verbatim reviews.
---
Critical Rules (read before Step 1)
These rules apply throughout all steps. Violating any of them fails Self-QA (Step 6).
1. **Every quote must be verbatim.** No paraphrase, no grammar correction, no cleaning. Exact reviewer words only. 2. **No fabricated statistics.** Do not write "40% faster" or "2× more reliable" unless a reviewer explicitly used similar language. The Self-QA step checks for uncited percentages. 3. **Cluster names must use reviewer language.** Study the anti-pattern table in Step 3. 4. **Every headline and ad copy direction must cite its source cluster.** Format: `[cluster: "cluster-name"]`. 5. **Section 2 is always present** in the output — even when degraded. Never skip or omit it. 6. **No banned words** in any generated copy: powerful, robust, seamless, innovative, game-changing, streamline, leverage, revolutionize, transform.
---
Step 1 — Parse Input and Detect Platform
Accept a natural language prompt containing one app URL. Extract the URL.
**Platform detection:**
- `apps.apple.com` → App Store
- `play.google.com/store/apps/details?id=` → Google Play
- Any other URL → stop and respond: "Please provide a direct App Store or Google Play URL. I can't analyse review data from other sources."
**ID extraction (do this before calling the script):**
| Platform | What to extract | How | |---|---|---| | App Store | Numeric `app_id` | Digits after `/id` in the URL | | App Store | `country` | 2-letter code after `apps.apple.com/` (e.g., `us`, `gb`) | | Google Play | `package_name` | Value of `id=` query parameter |
Persist the extracted values — you will need them for the output filename in Step 7.
If `product_context` was provided in the user's prompt (what their own product does), store it — used to personalise copy in Step 5.
---
Step 2 — Collect Reviews & Metadata
Run the full fetch script:
python3 scripts/fetch_reviews.py "{app_url}" --output {tmpdir}/asr-raw.json*(Note: Replace `{tmpdir}` with your operating system's temp directory, e.g., `/tmp` on macOS/Linux or `C:\Temp` on Windows).*
This fetches both the store description metadata and the reviews.
- **App Store:** iTunes API — free, no auth. App Store reviews are fetched via Apple's public iTunes RSS feed. Some apps return 0 reviews due to Apple's API limitations — in that case the skill continues with available data and logs a warning. Google Play is the primary supported path.
- **Google Play:** `google-play-scraper` package — free, no auth
If the script fails, read the error from stderr. Common causes:
- Package not installed: run `pip install google-play-scraper`
- App not found: verify the URL is a current, live listing
- Google Play API error: run `pip install --upgrade google-play-scraper` and retry
The script will print collection progress to stderr. Wait for it to complete. After completion, read `{tmpdir}/asr-raw.json` and display the collection summary to the user:
✓ Collected [N] reviews ([N] low-star 1–3★) from [platform]
Date range: [oldest] to [newest]
Package: [iTunes API | google-play-scraper]
**Check the exit code:**
- Exit 0 → collection succeeded, check `metadata.store_description`. If null: note this — Section 2 will use the degraded state. Proceed to Step 3.
- Exit 1 → error (read stderr message, surface it to user, stop)
- Exit 2 → **Gate 1 triggered** (< 10 low-star reviews found)
**Gate 1 — Low signal stop:** If the script exits with code 2, read the `gate_message` from `{tmpdir}/asr-raw.json` and surface it to the user verbatim. Do not proceed to Step 3. Do not produce a partial brief.
---
Step 3 — Complaint Clustering
Load `low_star_reviews` from `{tmpdir}/asr-raw.json`.
Cluster all low-star reviews into **4–6 named complaint themes.** Apply this formula to score each review:
complaint_weight = (4 - rating) × recency_factor
recency_factor:
review age ≤ 90 days → 1.0
review age 91–365 days → 0.7
review age > 365 days → 0.4
`review age` = (today's date) − (review `date` field) in days.
`cluster_score` = sum of `complaint_weight` for all reviews in the cluster.
**Cluster naming — critical rule:**
You will want to write abstract names. Resist. Use the exact verb and noun from reviews.
| ❌ Abstracted (wrong) | ✅ Reviewer language (correct) | |---|---| | "Stability issues" | "Crashes when exporting to PDF" | | "Sync problems" | "Data lost after sync between phone and desktop" | | "Monetisation friction" | "Paywall appears after 3 days, not 14 as promised" | | "Performance degradation" | "App freezes every time I search" | | "Onboarding issues" | "Can't figure out how to invite a teammate" |
**Rules:**
- Each review belongs to exactly one cluster (assign to its dominant theme)
- Discard any cluster with fewer than 3 reviews — log it as noise
- Select 3–4 verbatim quotes per cluster: lowest star rating first, then most recent
**Gate 2 — Minimum cluster size:** After discarding sub-3-review clusters, check how many clusters remain.
**Gate 3 — Low-confidence flag:** If fewer than 3 clusters remain:
- Do NOT stop. Continue to output.
- Prepend this to the brief header immediately after the app metadata:
> ⚠ **LOW CONFIDENCE:** Only [N] complaint cluster(s) met the minimum evidence threshold (≥ 3 supporting reviews). Output reflects limited data. Consider a competitor with more reviews, or broaden the rating filter.
- Include Medium-tier clusters in the output (score ≥ 5)
**Tier c
Read more
name: app-store-review-arbitrage description: "Fetches low-star App Store and Google Play reviews, clusters them into broken-promise patterns, and generates a ranked copy brief with positioning opportunities." version: 1.0.0 compatibility: [claude-code, gemini-cli, github-copilot]
app-store-review-arbitrage
Convert a competitor's App Store or Google Play URL into a one-session GTM brief: ranked complaint clusters, a broken promise map, landing page headlines, and ad copy directions — all sourced from verbatim reviews.
---
Critical Rules (read before Step 1)
These rules apply throughout all steps. Violating any of them fails Self-QA (Step 6).
1. **Every quote must be verbatim.** No paraphrase, no grammar correction, no cleaning. Exact reviewer words only. 2. **No fabricated statistics.** Do not write "40% faster" or "2× more reliable" unless a reviewer explicitly used similar language. The Self-QA step checks for uncited percentages. 3. **Cluster names must use reviewer language.** Study the anti-pattern table in Step 3. 4. **Every headline and ad copy direction must cite its source cluster.** Format: `[cluster: "cluster-name"]`. 5. **Section 2 is always present** in the output — even when degraded. Never skip or omit it. 6. **No banned words** in any generated copy: powerful, robust, seamless, innovative, game-changing, streamline, leverage, revolutionize, transform.
---
Step 1 — Parse Input and Detect Platform
Accept a natural language prompt containing one app URL. Extract the URL.
**Platform detection:**
- `apps.apple.com` → App Store
- `play.google.com/store/apps/details?id=` → Google Play
- Any other URL → stop and respond: "Please provide a direct App Store or Google Play URL. I can't analyse review data from other sources."
**ID extraction (do this before calling the script):**
| Platform | What to extract | How | |---|---|---| | App Store | Numeric `app_id` | Digits after `/id` in the URL | | App Store | `country` | 2-letter code after `apps.apple.com/` (e.g., `us`, `gb`) | | Google Play | `package_name` | Value of `id=` query parameter |
Persist the extracted values — you will need them for the output filename in Step 7.
If `product_context` was provided in the user's prompt (what their own product does), store it — used to personalise copy in Step 5.
---
Step 2 — Collect Reviews & Metadata
Run the full fetch script:
python3 scripts/fetch_reviews.py "{app_url}" --output {tmpdir}/asr-raw.json*(Note: Replace `{tmpdir}` with your operating system's temp directory, e.g., `/tmp` on macOS/Linux or `C:\Temp` on Windows).*
This fetches both the store description metadata and the reviews.
- **App Store:** iTunes API — free, no auth. App Store reviews are fetched via Apple's public iTunes RSS feed. Some apps return 0 reviews due to Apple's API limitations — in that case the skill continues with available data and logs a warning. Google Play is the primary supported path.
- **Google Play:** `google-play-scraper` package — free, no auth
If the script fails, read the error from stderr. Common causes:
- Package not installed: run `pip install google-play-scraper`
- App not found: verify the URL is a current, live listing
- Google Play API error: run `pip install --upgrade google-play-scraper` and retry
The script will print collection progress to stderr. Wait for it to complete. After completion, read `{tmpdir}/asr-raw.json` and display the collection summary to the user:
✓ Collected [N] reviews ([N] low-star 1–3★) from [platform] Date range: [oldest] to [newest] Package: [iTunes API | google-play-scraper]
**Check the exit code:**
- Exit 0 → collection succeeded, check `metadata.store_description`. If null: note this — Section 2 will use the degraded state. Proceed to Step 3.
- Exit 1 → error (read stderr message, surface it to user, stop)
- Exit 2 → **Gate 1 triggered** (< 10 low-star reviews found)
**Gate 1 — Low signal stop:** If the script exits with code 2, read the `gate_message` from `{tmpdir}/asr-raw.json` and surface it to the user verbatim. Do not proceed to Step 3. Do not produce a partial brief.
---
Step 3 — Complaint Clustering
Load `low_star_reviews` from `{tmpdir}/asr-raw.json`.
Cluster all low-star reviews into **4–6 named complaint themes.** Apply this formula to score each review:
complaint_weight = (4 - rating) × recency_factor recency_factor: review age ≤ 90 days → 1.0 review age 91–365 days → 0.7 review age > 365 days → 0.4
`review age` = (today's date) − (review `date` field) in days.
`cluster_score` = sum of `complaint_weight` for all reviews in the cluster.
**Cluster naming — critical rule:**
You will want to write abstract names. Resist. Use the exact verb and noun from reviews.
| ❌ Abstracted (wrong) | ✅ Reviewer language (correct) | |---|---| | "Stability issues" | "Crashes when exporting to PDF" | | "Sync problems" | "Data lost after sync between phone and desktop" | | "Monetisation friction" | "Paywall appears after 3 days, not 14 as promised" | | "Performance degradation" | "App freezes every time I search" | | "Onboarding issues" | "Can't figure out how to invite a teammate" |
**Rules:**
- Each review belongs to exactly one cluster (assign to its dominant theme)
- Discard any cluster with fewer than 3 reviews — log it as noise
- Select 3–4 verbatim quotes per cluster: lowest star rating first, then most recent
**Gate 2 — Minimum cluster size:** After discarding sub-3-review clusters, check how many clusters remain.
**Gate 3 — Low-confidence flag:** If fewer than 3 clusters remain:
- Do NOT stop. Continue to output.
- Prepend this to the brief header immediately after the app metadata:
> ⚠ **LOW CONFIDENCE:** Only [N] complaint cluster(s) met the minimum evidence threshold (≥ 3 supporting reviews). Output reflects limited data. Consider a competitor with more reviews, or broaden the rating filter.
- Include Medium-tier clusters in the output (score ≥ 5)
**Tier c
AI Agent Skills built for Founders who hate Marketing
Repo: Varnan-Tech/opendirectory
Other skills on opendirectory-gtm-skills.
- /blog-cover-image-cli
Use when the user asks to generate a blog cover image, thumbnail, or article header. Automatically uses modern typography, brand logos, and Google Search grounding to create beautiful 16:9 images with Gemini 3.1 Flash Image Preview.
Open skill - /brand-alchemy
World-class brand strategist and naming expert. Uses an interrogation-led discovery phase to extract your brand's DNA, then applies scientific naming frameworks (Phonosemantics) and automated multi-TLD domain checking.
Open skill - /claude-md-generator
Use when the user asks to generate or update a project's CLAUDE or AGENTS context file from a codebase scan. Writes a focused file under 100 lines containing only the non-obvious build commands, conventions, and gotchas Claude Code needs.
Open skill - /cold-email-verifier
Use when the user wants to verify cold emails, enrich a lead list, or autonomously guess email addresses from a CSV using ValidEmail.co or the open-source Reacher engine.
Open skill - /company-radar
Competitive intelligence orchestrator tracking companies across 8+ platforms (GitHub, Twitter, Reddit, HN, PH, YC Jobs) with heat scores and AI briefings.
Open skill - /competitor-pr-finder
Give it your product URL or description. It finds your top 5 competitors, runs three-track PR research across all of them (editorial, podcasts, communities), identifies which channels appear most frequently, looks up the journalist or host for each, and returns a tiered outreach
Open skill

