diff --git a/docs/superpowers/plans/2026-06-21-clone-app-skill.md b/docs/superpowers/plans/2026-06-21-clone-app-skill.md new file mode 100644 index 0000000..cb90bee --- /dev/null +++ b/docs/superpowers/plans/2026-06-21-clone-app-skill.md @@ -0,0 +1,1449 @@ +# Clone App Skill Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Build a `/clone-app` Claude Code skill that downloads an Android app's APK from a Google Play URL, reverse engineers it (via the existing `android-reverse-engineering` plugin), analyzes store presence, estimates AI-assisted clone effort + infra cost, produces a market-viability report, and optionally hands off to `writing-plans`. + +**Architecture:** A new `clone-app` plugin living **inside the fork** of `masa2146/android-reverse-engineering-skill`, alongside the untouched upstream `android-reverse-engineering` plugin. The orchestrator `SKILL.md` drives 8 phases (0–7); deterministic steps (URL parsing, APK download, store scrape, package-name extraction) are factored into small standalone bash/python helper scripts that are unit-testable. The RE plugin's existing scripts are invoked directly by path. AI-judgment phases (stack recommendation, effort estimate, viability verdict) live as prose instructions + reference templates in the skill. + +**Tech Stack:** Bash (helper scripts + glue), Python 3 (store scrape + JSON parsing where bash is too brittle), Markdown (skill/command/reference docs), `bats` or plain bash assertions for script tests, `curl`, the upstream RE scripts (jadx/Java). + +## Global Constraints + +- Repo root: `/Users/fatih.bulut/PythonWorks/clone_app_skill` (the cloned fork). +- Upstream plugin dir `plugins/android-reverse-engineering/` is **NEVER modified** — zero edits, to keep `git pull upstream master` conflict-free. +- New plugin dir: `plugins/clone-app/`. +- RE scripts are referenced via `${CLAUDE_PLUGIN_ROOT}` of the clone-app plugin, resolving sibling: `"$(dirname "$CLAUDE_PLUGIN_ROOT")/android-reverse-engineering/skills/android-reverse-engineering/scripts"`. +- All skill `${CLAUDE_PLUGIN_ROOT}` references use that exact env var (Claude Code sets it per-plugin). +- Working dir for runtime artifacts: `./work/{package}/` (relative to user's cwd, NOT inside the plugin). +- Package name regex (verbatim): `id=([a-zA-Z0-9._]+)` +- APKPure download URL (verbatim): `https://d.apkpure.com/b/APK/{PACKAGE}?version=latest` +- "AI Sprint" = one focused Claude Code session (~2-4 hours human review time). Use this unit everywhere; never calendar time. +- Bash scripts: `set -euo pipefail` at top, `#!/usr/bin/env bash` shebang. +- Python scripts: `#!/usr/bin/env python3`, stdlib only (no pip installs) — use `urllib`, `json`, `re`, `html.parser`. +- Report filename (verbatim): `clone-report-YYYY-MM-DD.md` where date is the run date. + +--- + +## File Structure + +**Plugin scaffold:** +- `plugins/clone-app/.claude-plugin/plugin.json` — plugin manifest +- `.claude-plugin/marketplace.json` — MODIFY: add clone-app entry (this is the only root-level shared file we touch; low conflict risk) +- `plugins/clone-app/skills/clone-app/SKILL.md` — orchestrator, all 8 phases +- `plugins/clone-app/commands/clone-app.md` — `/clone-app` slash command + +**Helper scripts (deterministic, testable):** +- `plugins/clone-app/skills/clone-app/scripts/extract-package.sh` — Google Play URL → package name +- `plugins/clone-app/skills/clone-app/scripts/download-apk.sh` — APKPure download w/ retries + filetype detect +- `plugins/clone-app/skills/clone-app/scripts/resolve-re-scripts.sh` — locate sibling RE scripts dir, fail loud if absent +- `plugins/clone-app/skills/clone-app/scripts/scrape-play-store.py` — Google Play page → metrics JSON +- `plugins/clone-app/skills/clone-app/scripts/check-appstore.py` — iTunes Search API → iOS presence JSON + +**Reference docs (AI-judgment guidance):** +- `plugins/clone-app/skills/clone-app/references/effort-estimation-guide.md` — how to build the AI-sprint table +- `plugins/clone-app/skills/clone-app/references/infra-cost-guide.md` — hosting/db/CDN cost heuristics +- `plugins/clone-app/skills/clone-app/references/report-template.md` — the clone-report skeleton +- `plugins/clone-app/skills/clone-app/references/stack-recommendation-guide.md` — how to pick/present 2-3 stacks + +**Tests:** +- `plugins/clone-app/tests/test-extract-package.sh` +- `plugins/clone-app/tests/test-download-apk.sh` +- `plugins/clone-app/tests/test-resolve-re-scripts.sh` +- `plugins/clone-app/tests/test-scrape-play-store.py` +- `plugins/clone-app/tests/test-check-appstore.py` +- `plugins/clone-app/tests/fixtures/` — sample HTML/JSON fixtures +- `plugins/clone-app/tests/run-all.sh` — runs every test + +**Docs:** +- `plugins/clone-app/README.md` — install + usage + +--- + +## Task 1: Repo bootstrap + plugin scaffold + +**Files:** +- Verify: `/Users/fatih.bulut/PythonWorks/clone_app_skill/` is the cloned fork with `plugins/android-reverse-engineering/` present +- Create: `plugins/clone-app/.claude-plugin/plugin.json` +- Modify: `.claude-plugin/marketplace.json` (add clone-app plugin entry) + +**Interfaces:** +- Produces: a valid plugin discoverable by `/plugin install clone-app@android-reverse-engineering-skill`. Plugin name string: `clone-app`. Marketplace name string stays: `android-reverse-engineering-skill`. + +- [ ] **Step 1: Verify the working dir is the cloned fork** + +Run: +```bash +cd /Users/fatih.bulut/PythonWorks/clone_app_skill && \ + ls plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/decompile.sh && \ + git remote -v +``` +Expected: the `decompile.sh` path lists successfully, and `git remote -v` shows an `origin` pointing at `masa2146/android-reverse-engineering-skill`. + +If the directory is empty (fork not cloned yet), run: +```bash +cd /Users/fatih.bulut/PythonWorks && \ + rm -rf clone_app_skill && \ + git clone https://github.com/masa2146/android-reverse-engineering-skill.git clone_app_skill && \ + cd clone_app_skill && \ + git remote add upstream https://github.com/SimoneAvogadro/android-reverse-engineering-skill.git +``` +Expected: clone succeeds, upstream remote added. Re-run the verify command above. + +- [ ] **Step 2: Create the plugin manifest** + +Create `plugins/clone-app/.claude-plugin/plugin.json`: +```json +{ + "name": "clone-app", + "version": "0.1.0", + "description": "Analyze a Google Play app: download APK, reverse engineer it, estimate AI-assisted clone effort and infra cost, assess market viability, and optionally generate an implementation plan.", + "author": { + "name": "masa2146" + }, + "repository": "https://github.com/masa2146/android-reverse-engineering-skill", + "license": "Apache-2.0", + "keywords": ["android", "clone", "feasibility", "effort-estimation", "market-analysis", "reverse-engineering"], + "skills": "./skills/", + "commands": "./commands/" +} +``` + +- [ ] **Step 3: Add clone-app to the marketplace catalog** + +Read the current `.claude-plugin/marketplace.json`, then append a second object to the `plugins` array (keep the existing `android-reverse-engineering` entry untouched). The `plugins` array becomes: +```json +"plugins": [ + { + "name": "android-reverse-engineering", + "source": "./plugins/android-reverse-engineering", + "description": "Decompile Android APK/JAR/AAR with jadx, trace call flows through libraries, and document extracted APIs.", + "version": "1.5.0", + "author": { "name": "Simone Avogadro" }, + "repository": "https://github.com/SimoneAvogadro/android-reverse-engineering-skill", + "license": "Apache-2.0", + "keywords": ["android", "reverse-engineering", "apk", "jadx", "decompile", "api-extraction"], + "category": "security" + }, + { + "name": "clone-app", + "source": "./plugins/clone-app", + "description": "Analyze a Google Play app for cloning: APK reverse engineering, AI-assisted effort estimation, market viability.", + "version": "0.1.0", + "author": { "name": "masa2146" }, + "repository": "https://github.com/masa2146/android-reverse-engineering-skill", + "license": "Apache-2.0", + "keywords": ["android", "clone", "feasibility", "effort-estimation", "market-analysis"], + "category": "security" + } +] +``` + +- [ ] **Step 4: Validate JSON** + +Run: +```bash +cd /Users/fatih.bulut/PythonWorks/clone_app_skill && \ + python3 -c "import json; json.load(open('plugins/clone-app/.claude-plugin/plugin.json')); json.load(open('.claude-plugin/marketplace.json')); print('OK')" +``` +Expected: `OK` + +- [ ] **Step 5: Commit** + +```bash +cd /Users/fatih.bulut/PythonWorks/clone_app_skill && \ + git add plugins/clone-app/.claude-plugin/plugin.json .claude-plugin/marketplace.json && \ + git commit -m "feat(clone-app): scaffold plugin manifest and marketplace entry" +``` + +--- + +## Task 2: `extract-package.sh` — Google Play URL → package name + +**Files:** +- Create: `plugins/clone-app/skills/clone-app/scripts/extract-package.sh` +- Test: `plugins/clone-app/tests/test-extract-package.sh` + +**Interfaces:** +- Produces: `extract-package.sh ` prints the bare package name to stdout, exit 0. On no match, prints nothing to stdout, writes `ERROR: could not extract package` to stderr, exit 1. Accepts either a full Play URL or an already-bare package name (passthrough if it matches `^[a-zA-Z0-9._]+$` with a dot). + +- [ ] **Step 1: Write the failing test** + +Create `plugins/clone-app/tests/test-extract-package.sh`: +```bash +#!/usr/bin/env bash +set -uo pipefail +SCRIPT="$(dirname "$0")/../skills/clone-app/scripts/extract-package.sh" +fail=0 +check() { + local desc="$1" expected="$2" actual="$3" + if [[ "$expected" == "$actual" ]]; then + echo "PASS: $desc" + else + echo "FAIL: $desc — expected '$expected' got '$actual'"; fail=1 + fi +} + +check "full url" "com.example.app" \ + "$(bash "$SCRIPT" 'https://play.google.com/store/apps/details?id=com.example.app')" +check "url with extra params" "com.whatsapp" \ + "$(bash "$SCRIPT" 'https://play.google.com/store/apps/details?id=com.whatsapp&hl=en&gl=US')" +check "bare package passthrough" "com.spotify.music" \ + "$(bash "$SCRIPT" 'com.spotify.music')" + +# invalid input → exit 1, empty stdout +out="$(bash "$SCRIPT" 'not a url' 2>/dev/null)"; rc=$? +check "invalid exit code" "1" "$rc" +check "invalid empty stdout" "" "$out" + +exit $fail +``` + +- [ ] **Step 2: Run test to verify it fails** + +Run: +```bash +bash /Users/fatih.bulut/PythonWorks/clone_app_skill/plugins/clone-app/tests/test-extract-package.sh +``` +Expected: FAIL (script does not exist yet — `bash: ...extract-package.sh: No such file or directory`). + +- [ ] **Step 3: Write minimal implementation** + +Create `plugins/clone-app/skills/clone-app/scripts/extract-package.sh`: +```bash +#!/usr/bin/env bash +set -euo pipefail + +input="${1:-}" +if [[ -z "$input" ]]; then + echo "ERROR: usage: extract-package.sh " >&2 + exit 1 +fi + +# Case 1: full URL containing id= +if [[ "$input" =~ id=([a-zA-Z0-9._]+) ]]; then + echo "${BASH_REMATCH[1]}" + exit 0 +fi + +# Case 2: already a bare package (must contain a dot, valid chars only) +if [[ "$input" =~ ^[a-zA-Z0-9_]+(\.[a-zA-Z0-9_]+)+$ ]]; then + echo "$input" + exit 0 +fi + +echo "ERROR: could not extract package from '$input'" >&2 +exit 1 +``` + +- [ ] **Step 4: Run test to verify it passes** + +Run: +```bash +chmod +x /Users/fatih.bulut/PythonWorks/clone_app_skill/plugins/clone-app/skills/clone-app/scripts/extract-package.sh && \ +bash /Users/fatih.bulut/PythonWorks/clone_app_skill/plugins/clone-app/tests/test-extract-package.sh +``` +Expected: all `PASS`, exit 0. + +- [ ] **Step 5: Commit** + +```bash +cd /Users/fatih.bulut/PythonWorks/clone_app_skill && \ + git add plugins/clone-app/skills/clone-app/scripts/extract-package.sh plugins/clone-app/tests/test-extract-package.sh && \ + git commit -m "feat(clone-app): add extract-package.sh URL parser with tests" +``` + +--- + +## Task 3: `resolve-re-scripts.sh` — locate sibling RE scripts dir + +**Files:** +- Create: `plugins/clone-app/skills/clone-app/scripts/resolve-re-scripts.sh` +- Test: `plugins/clone-app/tests/test-resolve-re-scripts.sh` + +**Interfaces:** +- Consumes: env var `CLAUDE_PLUGIN_ROOT` (path to the clone-app plugin dir). Falls back to deriving its own location from `$0` if the env var is unset (so it works when run standalone in tests). +- Produces: `resolve-re-scripts.sh` prints the absolute path to the RE `scripts/` dir to stdout, exit 0. If that dir doesn't exist, writes a clear error naming the install command to stderr, exit 1. + +- [ ] **Step 1: Write the failing test** + +Create `plugins/clone-app/tests/test-resolve-re-scripts.sh`: +```bash +#!/usr/bin/env bash +set -uo pipefail +SCRIPT="$(dirname "$0")/../skills/clone-app/scripts/resolve-re-scripts.sh" +fail=0 +check() { [[ "$2" == "$3" ]] && echo "PASS: $1" || { echo "FAIL: $1 — expected '$2' got '$3'"; fail=1; }; } + +# In the real repo layout, the RE scripts dir exists as a sibling plugin. +out="$(bash "$SCRIPT" 2>/dev/null)"; rc=$? +check "exit 0 when RE present" "0" "$rc" +check "ends with scripts dir" "android-reverse-engineering/skills/android-reverse-engineering/scripts" \ + "$(basename "$(dirname "$(dirname "$(dirname "$out")")")")/$(basename "$(dirname "$(dirname "$out")")")/$(basename "$(dirname "$out")")/$(basename "$out")" +check "decompile.sh exists under resolved dir" "yes" \ + "$([[ -f "$out/decompile.sh" ]] && echo yes || echo no)" + +# When CLAUDE_PLUGIN_ROOT points somewhere with no sibling RE plugin → exit 1 +tmp="$(mktemp -d)" +out2="$(CLAUDE_PLUGIN_ROOT="$tmp/clone-app" bash "$SCRIPT" 2>/dev/null)"; rc2=$? +check "exit 1 when RE missing" "1" "$rc2" +rm -rf "$tmp" + +exit $fail +``` + +- [ ] **Step 2: Run test to verify it fails** + +Run: +```bash +bash /Users/fatih.bulut/PythonWorks/clone_app_skill/plugins/clone-app/tests/test-resolve-re-scripts.sh +``` +Expected: FAIL (script missing). + +- [ ] **Step 3: Write minimal implementation** + +Create `plugins/clone-app/skills/clone-app/scripts/resolve-re-scripts.sh`: +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Determine this plugin's root. Prefer the env var Claude Code sets; fall back +# to deriving from this script's own location (…/clone-app/skills/clone-app/scripts). +if [[ -n "${CLAUDE_PLUGIN_ROOT:-}" ]]; then + plugin_root="$CLAUDE_PLUGIN_ROOT" +else + script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + # scripts -> clone-app -> skills -> clone-app(plugin root) + plugin_root="$(cd "$script_dir/../../.." && pwd)" +fi + +# Sibling RE plugin lives next to clone-app under plugins/ +re_scripts="$(cd "$plugin_root/.." 2>/dev/null && pwd)/android-reverse-engineering/skills/android-reverse-engineering/scripts" + +if [[ ! -d "$re_scripts" ]]; then + echo "ERROR: android-reverse-engineering scripts not found at: $re_scripts" >&2 + echo "Install it: /plugin install android-reverse-engineering@android-reverse-engineering-skill" >&2 + exit 1 +fi + +echo "$re_scripts" +``` + +- [ ] **Step 4: Run test to verify it passes** + +Run: +```bash +chmod +x /Users/fatih.bulut/PythonWorks/clone_app_skill/plugins/clone-app/skills/clone-app/scripts/resolve-re-scripts.sh && \ +bash /Users/fatih.bulut/PythonWorks/clone_app_skill/plugins/clone-app/tests/test-resolve-re-scripts.sh +``` +Expected: all `PASS`, exit 0. + +- [ ] **Step 5: Commit** + +```bash +cd /Users/fatih.bulut/PythonWorks/clone_app_skill && \ + git add plugins/clone-app/skills/clone-app/scripts/resolve-re-scripts.sh plugins/clone-app/tests/test-resolve-re-scripts.sh && \ + git commit -m "feat(clone-app): add resolve-re-scripts.sh sibling-plugin locator with tests" +``` + +--- + +## Task 4: `download-apk.sh` — APKPure download with retries + filetype detect + +**Files:** +- Create: `plugins/clone-app/skills/clone-app/scripts/download-apk.sh` +- Test: `plugins/clone-app/tests/test-download-apk.sh` + +**Interfaces:** +- Consumes: `download-apk.sh ` and optional env `CLONE_APP_CURL` (override curl binary, used by test to inject a stub). +- Produces: downloads to `/app.` where ext is `xapk` or `apk` decided by magic bytes (ZIP `PK\x03\x04` for both; distinguish by presence of `manifest.json` + multiple `.apk` entries → xapk, else apk). Prints the final file path to stdout on success, exit 0. Retries up to 3 times on curl failure. On total failure: stderr message, exit 1. + +- [ ] **Step 1: Write the failing test** + +Create `plugins/clone-app/tests/test-download-apk.sh`: +```bash +#!/usr/bin/env bash +set -uo pipefail +SCRIPT="$(dirname "$0")/../skills/clone-app/scripts/download-apk.sh" +fail=0 +check() { [[ "$2" == "$3" ]] && echo "PASS: $1" || { echo "FAIL: $1 — expected '$2' got '$3'"; fail=1; }; } + +tmp="$(mktemp -d)" + +# Stub curl: writes a minimal zip containing manifest.json + two apk entries → xapk +cat > "$tmp/fakecurl-xapk.sh" <<'EOF' +#!/usr/bin/env bash +out=""; prev="" +for a in "$@"; do [[ "$prev" == "--output" ]] && out="$a"; prev="$a"; done +workdir="$(mktemp -d)" +echo '{}' > "$workdir/manifest.json" +echo 'a' > "$workdir/base.apk"; echo 'b' > "$workdir/config.apk" +( cd "$workdir" && zip -q -r "$out" . ) +exit 0 +EOF +chmod +x "$tmp/fakecurl-xapk.sh" + +path="$(CLONE_APP_CURL="$tmp/fakecurl-xapk.sh" bash "$SCRIPT" com.example.app "$tmp/out" 2>/dev/null)"; rc=$? +check "xapk exit 0" "0" "$rc" +check "xapk extension" "xapk" "${path##*.}" + +# Stub curl that always fails → exit 1 after retries +cat > "$tmp/fakecurl-fail.sh" <<'EOF' +#!/usr/bin/env bash +exit 22 +EOF +chmod +x "$tmp/fakecurl-fail.sh" +out2="$(CLONE_APP_CURL="$tmp/fakecurl-fail.sh" bash "$SCRIPT" com.example.app "$tmp/out2" 2>/dev/null)"; rc2=$? +check "fail exit 1" "1" "$rc2" + +rm -rf "$tmp" +exit $fail +``` + +- [ ] **Step 2: Run test to verify it fails** + +Run: +```bash +bash /Users/fatih.bulut/PythonWorks/clone_app_skill/plugins/clone-app/tests/test-download-apk.sh +``` +Expected: FAIL (script missing). + +- [ ] **Step 3: Write minimal implementation** + +Create `plugins/clone-app/skills/clone-app/scripts/download-apk.sh`: +```bash +#!/usr/bin/env bash +set -euo pipefail + +package="${1:-}" +out_dir="${2:-}" +curl_bin="${CLONE_APP_CURL:-curl}" + +if [[ -z "$package" || -z "$out_dir" ]]; then + echo "ERROR: usage: download-apk.sh " >&2 + exit 1 +fi + +mkdir -p "$out_dir" +tmp_file="$out_dir/app.download" +url="https://d.apkpure.com/b/APK/${package}?version=latest" + +ok=0 +for attempt in 1 2 3; do + if "$curl_bin" -L --fail "$url" --output "$tmp_file" 2>/dev/null; then + ok=1; break + fi + echo "download attempt $attempt failed, retrying..." >&2 + sleep 1 +done + +if [[ "$ok" -ne 1 ]]; then + echo "ERROR: failed to download $package after 3 attempts from $url" >&2 + rm -f "$tmp_file" + exit 1 +fi + +# Decide extension: both APK and XAPK are ZIPs. XAPK = zip containing manifest.json + >=2 .apk entries. +ext="apk" +if entries="$(unzip -Z1 "$tmp_file" 2>/dev/null)"; then + apk_count="$(grep -c '\.apk$' <<<"$entries" || true)" + if grep -q '^manifest\.json$' <<<"$entries" && [[ "$apk_count" -ge 1 ]]; then + ext="xapk" + fi +fi + +final="$out_dir/app.$ext" +mv "$tmp_file" "$final" +echo "$final" +``` + +- [ ] **Step 4: Run test to verify it passes** + +Run: +```bash +chmod +x /Users/fatih.bulut/PythonWorks/clone_app_skill/plugins/clone-app/skills/clone-app/scripts/download-apk.sh && \ +bash /Users/fatih.bulut/PythonWorks/clone_app_skill/plugins/clone-app/tests/test-download-apk.sh +``` +Expected: all `PASS`, exit 0. (Requires `zip`/`unzip` available — standard on macOS.) + +- [ ] **Step 5: Commit** + +```bash +cd /Users/fatih.bulut/PythonWorks/clone_app_skill && \ + git add plugins/clone-app/skills/clone-app/scripts/download-apk.sh plugins/clone-app/tests/test-download-apk.sh && \ + git commit -m "feat(clone-app): add download-apk.sh with retries and xapk/apk detection" +``` + +--- + +## Task 5: `scrape-play-store.py` — Google Play page → metrics JSON + +**Files:** +- Create: `plugins/clone-app/skills/clone-app/scripts/scrape-play-store.py` +- Create: `plugins/clone-app/tests/fixtures/play-sample.html` +- Test: `plugins/clone-app/tests/test-scrape-play-store.py` + +**Interfaces:** +- Consumes: `scrape-play-store.py ` fetches `https://play.google.com/store/apps/details?id=&hl=en&gl=US`. For testing, `--html-file ` reads local HTML instead of network. +- Produces: prints a JSON object to stdout with keys: `package` (str), `title` (str|null), `rating` (float|null), `rating_count` (int|null), `installs` (str|null), `category` (str|null), `developer` (str|null), `updated` (str|null), `source` (str: "google-play"). Missing fields are `null`, never absent. Exit 0 even on partial extraction; exit 1 only on fetch error (network/HTTP) when not using `--html-file`. + +- [ ] **Step 1: Create the test fixture** + +Create `plugins/clone-app/tests/fixtures/play-sample.html` — a trimmed but realistic Play page. Include the data-bearing markup the scraper targets: +```html + +Example App - Apps on Google Play + + +
Updated onJun 1, 2026
+
1,000,000+Downloads
+ +``` + +- [ ] **Step 2: Write the failing test** + +Create `plugins/clone-app/tests/test-scrape-play-store.py`: +```python +#!/usr/bin/env python3 +import json, subprocess, sys, os + +HERE = os.path.dirname(os.path.abspath(__file__)) +SCRIPT = os.path.join(HERE, "..", "skills", "clone-app", "scripts", "scrape-play-store.py") +FIXTURE = os.path.join(HERE, "fixtures", "play-sample.html") + +def run(): + out = subprocess.check_output( + [sys.executable, SCRIPT, "com.example.app", "--html-file", FIXTURE]) + return json.loads(out) + +def main(): + d = run() + fails = [] + def check(name, cond): + print(f"{'PASS' if cond else 'FAIL'}: {name}") + if not cond: fails.append(name) + check("package", d["package"] == "com.example.app") + check("title", d["title"] == "Example App") + check("rating", abs((d["rating"] or 0) - 4.3) < 0.001) + check("rating_count", d["rating_count"] == 12873) + check("developer", d["developer"] == "Example Studio") + check("category", d["category"] == "GAME_PUZZLE") + check("installs", d["installs"] == "1,000,000+") + check("source", d["source"] == "google-play") + # all expected keys present even if null + for k in ["package","title","rating","rating_count","installs","category","developer","updated","source"]: + check(f"key present: {k}", k in d) + sys.exit(1 if fails else 0) + +main() +``` + +- [ ] **Step 3: Run test to verify it fails** + +Run: +```bash +python3 /Users/fatih.bulut/PythonWorks/clone_app_skill/plugins/clone-app/tests/test-scrape-play-store.py +``` +Expected: FAIL (script missing — `FileNotFoundError` / non-zero exit). + +- [ ] **Step 4: Write minimal implementation** + +Create `plugins/clone-app/skills/clone-app/scripts/scrape-play-store.py`: +```python +#!/usr/bin/env python3 +"""Scrape a Google Play store page into a metrics JSON object. + +Primary source is the embedded ld+json SoftwareApplication block (stable). +Falls back to light regex for installs/updated which aren't always in ld+json. +""" +import sys, json, re, argparse, urllib.request + +KEYS = ["package", "title", "rating", "rating_count", "installs", + "category", "developer", "updated", "source"] + +def fetch(package): + url = f"https://play.google.com/store/apps/details?id={package}&hl=en&gl=US" + req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"}) + with urllib.request.urlopen(req, timeout=30) as r: + return r.read().decode("utf-8", "replace") + +def parse(html, package): + out = {k: None for k in KEYS} + out["package"] = package + out["source"] = "google-play" + + # ld+json SoftwareApplication block + for m in re.finditer(r'', + html, re.DOTALL): + try: + data = json.loads(m.group(1).strip()) + except Exception: + continue + if isinstance(data, dict) and data.get("@type") == "SoftwareApplication": + out["title"] = data.get("name") + auth = data.get("author") + if isinstance(auth, dict): + out["developer"] = auth.get("name") + out["category"] = data.get("applicationCategory") + ar = data.get("aggregateRating") or {} + if ar.get("ratingValue") is not None: + try: out["rating"] = float(ar["ratingValue"]) + except Exception: pass + if ar.get("ratingCount") is not None: + try: out["rating_count"] = int(ar["ratingCount"]) + except Exception: pass + break + + # installs (e.g. "1,000,000+") + m = re.search(r'([\d,]+\+)\s*\s*Downloads', html) + if m: + out["installs"] = m.group(1) + + # updated date (e.g. "Updated onJun 1, 2026") + m = re.search(r'Updated on\s*([^<]+)', html) + if m: + out["updated"] = m.group(1).strip() + + return out + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("package") + ap.add_argument("--html-file") + args = ap.parse_args() + + if args.html_file: + with open(args.html_file, encoding="utf-8") as f: + html = f.read() + else: + try: + html = fetch(args.package) + except Exception as e: + print(f"ERROR: failed to fetch Play page: {e}", file=sys.stderr) + sys.exit(1) + + print(json.dumps(parse(html, args.package), indent=2)) + +if __name__ == "__main__": + main() +``` + +- [ ] **Step 5: Run test to verify it passes** + +Run: +```bash +python3 /Users/fatih.bulut/PythonWorks/clone_app_skill/plugins/clone-app/tests/test-scrape-play-store.py +``` +Expected: all `PASS`, exit 0. + +- [ ] **Step 6: Commit** + +```bash +cd /Users/fatih.bulut/PythonWorks/clone_app_skill && \ + git add plugins/clone-app/skills/clone-app/scripts/scrape-play-store.py \ + plugins/clone-app/tests/test-scrape-play-store.py \ + plugins/clone-app/tests/fixtures/play-sample.html && \ + git commit -m "feat(clone-app): add scrape-play-store.py with ld+json parsing and tests" +``` + +--- + +## Task 6: `check-appstore.py` — iOS presence via iTunes Search API + +**Files:** +- Create: `plugins/clone-app/skills/clone-app/scripts/check-appstore.py` +- Create: `plugins/clone-app/tests/fixtures/itunes-sample.json` +- Test: `plugins/clone-app/tests/test-check-appstore.py` + +**Interfaces:** +- Consumes: `check-appstore.py ` queries `https://itunes.apple.com/search?term=&entity=software&limit=5`. For testing, `--json-file ` reads a local iTunes response instead of network. (Note: there is no Android package → App Store ID mapping; we search by app title/term, best-effort.) +- Produces: prints JSON to stdout: `{ "found": bool, "source": "app-store", "results": [ {"name","seller","rating","rating_count","price","url"} ... ] }`. On network error without `--json-file`: still prints `{"found": false, "source":"app-store", "results": [], "error": "..."}` and exit 0 (App Store absence is non-fatal per spec). + +- [ ] **Step 1: Create the test fixture** + +Create `plugins/clone-app/tests/fixtures/itunes-sample.json`: +```json +{ + "resultCount": 1, + "results": [ + { + "trackName": "Example App", + "sellerName": "Example Studio", + "averageUserRating": 4.5, + "userRatingCount": 8421, + "formattedPrice": "Free", + "trackViewUrl": "https://apps.apple.com/app/id123456789" + } + ] +} +``` + +- [ ] **Step 2: Write the failing test** + +Create `plugins/clone-app/tests/test-check-appstore.py`: +```python +#!/usr/bin/env python3 +import json, subprocess, sys, os + +HERE = os.path.dirname(os.path.abspath(__file__)) +SCRIPT = os.path.join(HERE, "..", "skills", "clone-app", "scripts", "check-appstore.py") +FIXTURE = os.path.join(HERE, "fixtures", "itunes-sample.json") + +def main(): + out = subprocess.check_output( + [sys.executable, SCRIPT, "Example App", "--json-file", FIXTURE]) + d = json.loads(out) + fails = [] + def check(name, cond): + print(f"{'PASS' if cond else 'FAIL'}: {name}") + if not cond: fails.append(name) + check("found true", d["found"] is True) + check("source", d["source"] == "app-store") + check("one result", len(d["results"]) == 1) + r = d["results"][0] + check("name", r["name"] == "Example App") + check("seller", r["seller"] == "Example Studio") + check("rating", abs(r["rating"] - 4.5) < 0.001) + check("rating_count", r["rating_count"] == 8421) + check("price", r["price"] == "Free") + check("url", r["url"].endswith("id123456789")) + sys.exit(1 if fails else 0) + +main() +``` + +- [ ] **Step 3: Run test to verify it fails** + +Run: +```bash +python3 /Users/fatih.bulut/PythonWorks/clone_app_skill/plugins/clone-app/tests/test-check-appstore.py +``` +Expected: FAIL (script missing). + +- [ ] **Step 4: Write minimal implementation** + +Create `plugins/clone-app/skills/clone-app/scripts/check-appstore.py`: +```python +#!/usr/bin/env python3 +"""Best-effort check whether an iOS App Store equivalent exists, via the +public iTunes Search API. There is no reliable Android-package → App-Store-ID +mapping, so we search by term (app title) and return the top matches.""" +import sys, json, argparse, urllib.request, urllib.parse + +def fetch(term): + q = urllib.parse.urlencode({"term": term, "entity": "software", "limit": 5}) + url = f"https://itunes.apple.com/search?{q}" + req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"}) + with urllib.request.urlopen(req, timeout=30) as r: + return json.loads(r.read().decode("utf-8", "replace")) + +def shape(data): + results = [] + for r in data.get("results", []): + results.append({ + "name": r.get("trackName"), + "seller": r.get("sellerName"), + "rating": r.get("averageUserRating"), + "rating_count": r.get("userRatingCount"), + "price": r.get("formattedPrice"), + "url": r.get("trackViewUrl"), + }) + return {"found": len(results) > 0, "source": "app-store", "results": results} + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("term") + ap.add_argument("--json-file") + args = ap.parse_args() + + if args.json_file: + with open(args.json_file, encoding="utf-8") as f: + data = json.load(f) + else: + try: + data = fetch(args.term) + except Exception as e: + print(json.dumps({"found": False, "source": "app-store", + "results": [], "error": str(e)}, indent=2)) + sys.exit(0) + + print(json.dumps(shape(data), indent=2)) + +if __name__ == "__main__": + main() +``` + +- [ ] **Step 5: Run test to verify it passes** + +Run: +```bash +python3 /Users/fatih.bulut/PythonWorks/clone_app_skill/plugins/clone-app/tests/test-check-appstore.py +``` +Expected: all `PASS`, exit 0. + +- [ ] **Step 6: Commit** + +```bash +cd /Users/fatih.bulut/PythonWorks/clone_app_skill && \ + git add plugins/clone-app/skills/clone-app/scripts/check-appstore.py \ + plugins/clone-app/tests/test-check-appstore.py \ + plugins/clone-app/tests/fixtures/itunes-sample.json && \ + git commit -m "feat(clone-app): add check-appstore.py iTunes search with tests" +``` + +--- + +## Task 7: Test runner aggregator + +**Files:** +- Create: `plugins/clone-app/tests/run-all.sh` + +**Interfaces:** +- Produces: `run-all.sh` runs every test (bash + python), prints a summary, exits non-zero if any fail. + +- [ ] **Step 1: Write the runner** + +Create `plugins/clone-app/tests/run-all.sh`: +```bash +#!/usr/bin/env bash +set -uo pipefail +HERE="$(cd "$(dirname "$0")" && pwd)" +fail=0 + +echo "=== bash tests ===" +for t in "$HERE"/test-*.sh; do + echo "--- $(basename "$t") ---" + bash "$t" || fail=1 +done + +echo "=== python tests ===" +for t in "$HERE"/test-*.py; do + echo "--- $(basename "$t") ---" + python3 "$t" || fail=1 +done + +echo +if [[ "$fail" -eq 0 ]]; then echo "ALL TESTS PASSED"; else echo "SOME TESTS FAILED"; fi +exit $fail +``` + +- [ ] **Step 2: Run it** + +Run: +```bash +chmod +x /Users/fatih.bulut/PythonWorks/clone_app_skill/plugins/clone-app/tests/run-all.sh && \ +bash /Users/fatih.bulut/PythonWorks/clone_app_skill/plugins/clone-app/tests/run-all.sh +``` +Expected: `ALL TESTS PASSED`, exit 0 (all of Tasks 2–6 green). + +- [ ] **Step 3: Commit** + +```bash +cd /Users/fatih.bulut/PythonWorks/clone_app_skill && \ + git add plugins/clone-app/tests/run-all.sh && \ + git commit -m "test(clone-app): add aggregate test runner" +``` + +--- + +## Task 8: Reference docs — effort, infra cost, stack, report template + +**Files:** +- Create: `plugins/clone-app/skills/clone-app/references/stack-recommendation-guide.md` +- Create: `plugins/clone-app/skills/clone-app/references/effort-estimation-guide.md` +- Create: `plugins/clone-app/skills/clone-app/references/infra-cost-guide.md` +- Create: `plugins/clone-app/skills/clone-app/references/report-template.md` + +**Interfaces:** +- Produces: four reference docs the SKILL.md links into during Phases 4–6. No code; these are the judgment rubrics that keep AI output consistent. + +- [ ] **Step 1: Write the stack recommendation guide** + +Create `plugins/clone-app/skills/clone-app/references/stack-recommendation-guide.md`: +```markdown +# Stack Recommendation Guide (Phase 4) + +Goal: present the user 2-3 concrete stack options for building the clone, then +let them pick. The pick locks effort + cost math in Phase 5. + +## Inputs you have +- Detected original framework (from RE fingerprint): Native Kotlin/Java, Flutter, React Native, etc. +- Detected HTTP stack + backend signals (Retrofit/Ktor/Apollo/GraphQL/WebSocket). +- Feature surface (screen count, SDKs, permissions). + +## How to choose the 2-3 options +Always include: +1. **Fastest for AI-assisted dev** — default to **Flutter** (single codebase, strong AI codegen, fast UI) unless the app is heavily native-platform-dependent. +2. **JS-ecosystem option** — **React Native + Expo** when the team is JS-leaning or web reuse matters. +3. **Match-the-original** — only when the original's nativeness is essential (deep platform APIs, AR, heavy native SDKs). Note the higher effort. + +## Backend +- If RE shows first-party API hosts → a backend is required. Recommend **Node/TS (NestJS or Express)** or **Supabase** (fastest with AI) unless GraphQL detected → recommend **Apollo Server** or **Hasura**. +- If only third-party hosts (Firebase, payment SDKs) → likely **BaaS / no custom backend**; note it. + +## Output format to the user +Present a short table: Option | Mobile stack | Backend | Why | Relative effort (Low/Med/High). +Then ask: "Which stack should I base the effort + cost estimate on?" +``` + +- [ ] **Step 2: Write the effort estimation guide** + +Create `plugins/clone-app/skills/clone-app/references/effort-estimation-guide.md`: +```markdown +# Effort Estimation Guide (Phase 5) — AI-Assisted Units + +Estimate in **AI Sprints**, NOT human days. 1 AI Sprint = one focused Claude +Code session producing a reviewable increment (~2-4h human review time). + +## Method +1. Build the feature list from RE output: + - Screens = Activity + Fragment count (dedupe obvious base classes). + - API surface = endpoint count from find-api-calls. + - Integrations = third-party SDKs (auth, payment, maps, analytics, push, chat). + - Backend = REST/GraphQL/WebSocket presence + first-party host count. +2. Assign each feature a complexity and sprint range using the table below. +3. Sum to a range (min–max sprints). Never give a single false-precise number. + +## Reference sprint costs +| Feature class | Low | Typical | High | Notes | +|---|---|---|---|---| +| Project scaffold + CI | 0.5 | 1 | 1 | nav, theming, state mgmt setup | +| Auth (email/social) | 1 | 1.5 | 2 | +1 if custom backend auth | +| Simple list/detail screen | 0.3 | 0.5 | 1 | per screen, AI-fast | +| Complex interactive screen | 1 | 2 | 3 | maps, editors, realtime | +| API integration layer | 1 | 2 | 3 | scales with endpoint count | +| Each major SDK integration | 0.5 | 1 | 2 | payment > maps > analytics | +| Custom backend (CRUD) | 2 | 4 | 8 | scales with entity + endpoint count | +| Realtime (WebSocket/push) | 1 | 2 | 4 | | +| Offline/sync | 2 | 3 | 5 | | +| Polish/QA/store submission | 1 | 2 | 3 | | + +## Obfuscation caveat +If RE flagged heavy R8/Flutter (feature list incomplete), add a **+20-40% +uncertainty band** and state it explicitly in the report. + +## Output +A table: Category | Complexity | Sprints (min–max), then a TOTAL row. +``` + +- [ ] **Step 3: Write the infra cost guide** + +Create `plugins/clone-app/skills/clone-app/references/infra-cost-guide.md`: +```markdown +# Infrastructure Cost Guide (Phase 5) + +Estimate **monthly USD** at three scales: MVP (<1k MAU), Growth (~50k MAU), +Scale (~500k MAU). Use round heuristics; mark assumptions. + +## Components +| Component | MVP | Growth | Scale | Notes | +|---|---|---|---|---| +| App hosting/backend | $0-20 | $50-200 | $500-2k | Railway/Render/Fly → AWS/GCP | +| Managed DB (Postgres) | $0-15 | $50-150 | $300-1k | Supabase/Neon/RDS | +| Object storage/CDN | $0-5 | $20-80 | $200-800 | S3+CloudFront/Cloudflare | +| Auth (managed) | $0 | $0-100 | $200-500 | Supabase/Auth0 tiers | +| Push notifications | $0 | $0-50 | $50-300 | FCM free; OneSignal paid tiers | +| Third-party APIs | varies | varies | varies | maps, SMS, payments % | +| Monitoring | $0 | $20-50 | $100-300 | Sentry/Datadog | + +## Third-party cost flags from RE +- Maps SDK → Google Maps billing after free tier. +- Payment SDK → % per transaction (Stripe ~2.9%+30¢). +- SMS/OTP → per-message (Twilio). +List each detected paid dependency explicitly. + +## Output +A 3-column (MVP/Growth/Scale) monthly cost table + a one-line "biggest cost driver". +``` + +- [ ] **Step 4: Write the report template** + +Create `plugins/clone-app/skills/clone-app/references/report-template.md`: +```markdown +# Clone Feasibility Report — {APP_TITLE} + +**Package:** {PACKAGE} +**Date:** {DATE} +**Analyzed by:** clone-app skill + +## 1. App Overview +- Title / developer / category / installs / rating ({RATING} from {RATING_COUNT} reviews) +- iOS App Store presence: {YES/NO + link} +- One-paragraph description of what the app does. + +## 2. Tech Stack (Detected) +- Mobile framework: {framework} (RE fingerprint marker: {marker}) +- HTTP stack: {Retrofit/Ktor/...} +- Backend signals: {first-party hosts, REST/GraphQL/WS} +- Notable SDKs: {list} +- Obfuscation level: {low/med/high} — analysis completeness caveat if high. + +## 3. Recommended Clone Stack +- Selected by user: {stack} +- Rationale: {1-2 lines} + +## 4. Feature List (from APK) +- Screens: {n} +- API endpoints: {n} (key ones listed) +- Integrations: {list} +- Backend required: {yes/no + why} + +## 5. Effort Estimate (AI-Assisted) +{effort table from effort-estimation-guide} +**Total: {min}–{max} AI Sprints** (1 sprint ≈ one focused Claude Code session). +Uncertainty band: {±%} due to {reason}. + +## 6. Infrastructure Cost Estimate (monthly) +{MVP/Growth/Scale table from infra-cost-guide} +Biggest cost driver: {item}. + +## 7. Market Analysis +- Current app metrics: installs {x}, rating {y}, review velocity {if known}. +- Competitor landscape: {2-4 named competitors}. +- Target market size: {rough estimate + basis}. +- Differentiation opportunities: {list}. + +## 8. Viability Verdict +**{GO / CONDITIONAL GO / NO GO}** +- Key risks: {list} +- Key opportunities: {list} +- Recommendation rationale: {2-3 sentences tying effort+cost vs market}. + +## 9. Next Step +{Link to implementation plan if user proceeds, else "report only".} +``` + +- [ ] **Step 5: Commit** + +```bash +cd /Users/fatih.bulut/PythonWorks/clone_app_skill && \ + git add plugins/clone-app/skills/clone-app/references/ && \ + git commit -m "docs(clone-app): add stack/effort/infra/report reference guides" +``` + +--- + +## Task 9: Orchestrator SKILL.md — all 8 phases + +**Files:** +- Create: `plugins/clone-app/skills/clone-app/SKILL.md` + +**Interfaces:** +- Consumes: all helper scripts (Tasks 2-6), the RE script dir (Task 3 resolver), and reference docs (Task 8). Uses `${CLAUDE_PLUGIN_ROOT}` for its own scripts. +- Produces: the activatable skill that runs the full Phase 0-7 workflow. Trigger phrases + frontmatter so Claude Code auto-loads it. + +- [ ] **Step 1: Write SKILL.md** + +Create `plugins/clone-app/skills/clone-app/SKILL.md`: +```markdown +--- +description: Analyze a Google Play app to assess cloning it — download the APK, reverse engineer the tech stack and APIs, analyze app-store presence, estimate AI-assisted build effort and infrastructure cost, judge market viability, and optionally generate an implementation plan. Use when the user gives a Google Play URL or package name and wants a clone feasibility analysis, effort estimate, or tech-stack breakdown. 中文触发词:克隆应用、复刻这个app、分析可行性、估算开发量、克隆可行性分析 +trigger: clone app|clone this app|clone feasibility|feasibility analysis|estimate effort to build|reverse engineer and clone|analyze this play store app|can I clone|克隆应用|复刻|可行性分析 +--- + +# Clone App — Feasibility & Effort Analysis + +Take a Google Play URL (or package name), reverse engineer the app, analyze its +market, estimate AI-assisted clone effort and infrastructure cost, and produce a +viability report. If the user approves, hand off to the writing-plans skill to +generate a full implementation plan. + +This skill orchestrates 8 phases. Deterministic steps are factored into helper +scripts under `${CLAUDE_PLUGIN_ROOT}/skills/clone-app/scripts/`. Reverse +engineering reuses the sibling `android-reverse-engineering` plugin's scripts. + +## Legal note +Only analyze apps you are authorized to (your own, or for lawful interoperability +/ research). Surface this to the user if intent is unclear. Do not proceed for +clearly infringing intent. + +## Phase 0: Input & Validation + +Extract the package name: +```bash +PKG="$(bash ${CLAUDE_PLUGIN_ROOT}/skills/clone-app/scripts/extract-package.sh "")" +``` +If it exits non-zero, ask the user for the package name directly, then re-run. + +Create the working dir: `WORK="./work/$PKG"` and `mkdir -p "$WORK"`. + +## Phase 1: APK Download + +```bash +APK="$(bash ${CLAUDE_PLUGIN_ROOT}/skills/clone-app/scripts/download-apk.sh "$PKG" "$WORK")" +``` +The script retries 3× and prints the path (`app.apk` or `app.xapk`). If it exits +non-zero, tell the user the download failed and ask for a local APK/XAPK path; +set `APK` to that path. + +## Phase 2: Reverse Engineering + +Resolve the sibling RE scripts: +```bash +RE="$(bash ${CLAUDE_PLUGIN_ROOT}/skills/clone-app/scripts/resolve-re-scripts.sh)" +``` +If it exits non-zero, show its error (RE plugin not installed) and stop. + +Run, in order, reading each output before the next: +1. `bash "$RE/fingerprint.sh" "$APK"` — framework, HTTP stack, obfuscation, SDKs, native libs. + - **If framework is Flutter / React Native / Cordova / Xamarin:** tell the user Java + decompilation is limited; proceed but rely on manifest + strings + hardcoded URLs + + the fingerprint SDK list. Skip steps 3-5's deep API extraction expectations. +2. `bash "$RE/check-deps.sh"` — parse `INSTALL_REQUIRED:` / `INSTALL_OPTIONAL:` lines. + Install required deps with `bash "$RE/install-dep.sh" `; re-run check-deps until clean. + Ask the user before installing optional deps (vineflower, dex2jar). +3. `bash "$RE/decompile.sh" "$APK"` (add `--deobf` if fingerprint showed heavy obfuscation). + Output lands under the RE script's `output/` — note the `sources/` path it prints. +4. If the app is Kotlin: `bash "$RE/recover-kotlin-names.sh" "$WORK/output/names/"`. +5. `bash "$RE/find-api-calls.sh" ` (full scan; add `--ktor`/`--apollo`/`--paths` as + the fingerprint suggests). + +From these outputs assemble: framework, HTTP stack, **API endpoint list**, +first-party vs third-party hosts, AndroidManifest summary (permissions, components), +and a **feature list** (screen count, SDKs, backend signals). Keep this in context +for later phases. + +## Phase 3: Store Analysis + +```bash +python3 ${CLAUDE_PLUGIN_ROOT}/skills/clone-app/scripts/scrape-play-store.py "$PKG" > "$WORK/play.json" +``` +Read `play.json` for rating, rating_count, installs, category, developer, updated. +Use the `title` to check iOS: +```bash +python3 ${CLAUDE_PLUGIN_ROOT}/skills/clone-app/scripts/check-appstore.py "" > "$WORK/appstore.json" +``` +If Play scrape returned mostly nulls (page layout changed), fall back to a web +search for the app's metrics and note the source. App Store absence is fine — +continue with Google Play data only. + +## Phase 4: Stack Recommendation + +Read `${CLAUDE_PLUGIN_ROOT}/skills/clone-app/references/stack-recommendation-guide.md`. +Using the RE results + store data, present 2-3 stack options as a table and ask +the user to choose. **Wait for the user's choice before Phase 5.** Lock it. + +## Phase 5: Effort & Cost Estimation + +Read `${CLAUDE_PLUGIN_ROOT}/skills/clone-app/references/effort-estimation-guide.md` +and `infra-cost-guide.md`. Build: +- the feature list → AI-Sprint effort table (min-max total, uncertainty band), +- the MVP/Growth/Scale monthly infra cost table. +Base both on the **user-selected stack** from Phase 4. + +## Phase 6: Market Viability Report + +Read `${CLAUDE_PLUGIN_ROOT}/skills/clone-app/references/report-template.md`. +Fill every section from the data gathered. For market analysis (competitors, +market size), use web search as needed. Produce a GO / CONDITIONAL GO / NO GO +verdict tying effort + cost against market opportunity. + +Write the report: +``` +$WORK/clone-report-<YYYY-MM-DD>.md +``` +(Use the actual run date.) Show the user a concise summary + the verdict. + +## Phase 7: Decision Gate + +Ask: "Report saved to `$WORK/clone-report-<date>.md`. Proceed to build the +implementation plan?" +- **Yes** → invoke the `superpowers:writing-plans` skill, passing the report as + context (the selected stack, feature list, and effort table become the plan's spec). +- **No** → stop; the report stands on its own. + +## Error Handling Summary +| Scenario | Action | +|---|---| +| Package not in URL | ask user for package name | +| Download fails 3× | ask for local APK path | +| RE plugin missing | show resolver error, stop | +| Flutter/RN/Cordova/Xamarin | warn, continue with limited RE | +| App Store not found | continue Google Play only | +| Play scrape returns nulls | web-search fallback, note source | +| Heavy obfuscation | add uncertainty band, note in report | +| writing-plans unavailable | write the plan as Markdown manually | +``` + +- [ ] **Step 2: Validate frontmatter + links** + +Run: +```bash +cd /Users/fatih.bulut/PythonWorks/clone_app_skill && \ +python3 - <<'EOF' +import re, sys, os +p = "plugins/clone-app/skills/clone-app/SKILL.md" +s = open(p, encoding="utf-8").read() +assert s.startswith("---"), "missing frontmatter" +fm = s.split("---",2)[1] +assert "description:" in fm and "trigger:" in fm, "frontmatter keys missing" +# every referenced script/reference path under the plugin must exist +base = "plugins/clone-app/skills/clone-app" +for rel in ["scripts/extract-package.sh","scripts/download-apk.sh", + "scripts/resolve-re-scripts.sh","scripts/scrape-play-store.py", + "scripts/check-appstore.py","references/stack-recommendation-guide.md", + "references/effort-estimation-guide.md","references/infra-cost-guide.md", + "references/report-template.md"]: + assert os.path.isfile(os.path.join(base, rel)), f"missing {rel}" +print("OK") +EOF +``` +Expected: `OK` + +- [ ] **Step 3: Commit** + +```bash +cd /Users/fatih.bulut/PythonWorks/clone_app_skill && \ + git add plugins/clone-app/skills/clone-app/SKILL.md && \ + git commit -m "feat(clone-app): add orchestrator SKILL.md (phases 0-7)" +``` + +--- + +## Task 10: `/clone-app` slash command + README + +**Files:** +- Create: `plugins/clone-app/commands/clone-app.md` +- Create: `plugins/clone-app/README.md` + +**Interfaces:** +- Consumes: SKILL.md (Task 9). +- Produces: the user-invocable `/clone-app <url>` command and install/usage docs. + +- [ ] **Step 1: Write the command file** + +Create `plugins/clone-app/commands/clone-app.md`: +```markdown +--- +allowed-tools: Bash, Read, Glob, Grep, Write, Edit, WebFetch, WebSearch, Skill +description: Analyze a Google Play app for cloning — RE, effort, market viability, optional plan +user-invocable: true +argument-hint: <Google Play URL or package name> +argument: Google Play URL or package name (optional) +--- + +# /clone-app + +Run the full clone-feasibility workflow on a Google Play app. + +## Instructions + +Follow the clone-app skill workflow in +`${CLAUDE_PLUGIN_ROOT}/skills/clone-app/SKILL.md` exactly, phases 0 through 7. + +### Step 1: Get the target +If the user passed a URL or package name as an argument, use it. Otherwise ask +for the Google Play URL or package name. + +### Step 2: Run the skill +Execute Phase 0 → Phase 7 from SKILL.md. Pause for the user at: +- Phase 4 (stack choice), +- Phase 7 (proceed to implementation plan?). + +Honor the Error Handling Summary table in SKILL.md at every phase. + +### Step 3: Deliver +Ensure the report is written to `./work/<package>/clone-report-<date>.md` and +summarize the verdict. If the user approves at Phase 7, invoke +`superpowers:writing-plans` with the report as the spec. +``` + +- [ ] **Step 2: Write the README** + +Create `plugins/clone-app/README.md`: +```markdown +# clone-app — Clone Feasibility Analyzer (Claude Code skill) + +Give it a Google Play URL. It downloads the APK, reverse engineers the tech +stack and APIs (via the sibling `android-reverse-engineering` plugin), analyzes +the app's store presence, estimates **AI-assisted** build effort (in AI Sprints) +and monthly infrastructure cost, judges market viability (GO / CONDITIONAL GO / +NO GO), and — if you approve — generates a full implementation plan. + +## Requirements +- The `android-reverse-engineering` plugin (ships in this same repo). +- Java JDK 17+ and jadx (the RE plugin auto-installs jadx if missing). +- `curl`, Python 3 (stdlib only), `unzip`. + +## Install +```text +/plugin marketplace add /path/to/this/repo +/plugin install android-reverse-engineering@android-reverse-engineering-skill +/plugin install clone-app@android-reverse-engineering-skill +``` + +## Usage +```text +/clone-app https://play.google.com/store/apps/details?id=com.example.app +``` +Or natural language: "Analyze this Play Store app for cloning: <url>". + +The skill pauses twice for your input: choosing the clone stack, and deciding +whether to generate the implementation plan. + +## Output +``` +./work/<package>/ +├── app.apk | app.xapk +├── output/ # decompiled sources + Kotlin name maps +├── play.json # store metrics +├── appstore.json # iOS presence +└── clone-report-YYYY-MM-DD.md +``` + +## Keeping the RE plugin up to date +This repo is a fork. To pull upstream improvements: +```bash +git remote add upstream https://github.com/SimoneAvogadro/android-reverse-engineering-skill.git +git pull upstream master +``` +The clone-app plugin lives in its own directory, so upstream updates to +`android-reverse-engineering` merge cleanly. + +## Legal +For lawful use only — your own apps, authorized interoperability, security +research, or education. You are responsible for compliance. +``` + +- [ ] **Step 3: Validate command frontmatter** + +Run: +```bash +cd /Users/fatih.bulut/PythonWorks/clone_app_skill && \ +python3 -c " +s=open('plugins/clone-app/commands/clone-app.md',encoding='utf-8').read() +assert s.startswith('---') and 'user-invocable: true' in s and 'allowed-tools:' in s +print('OK')" +``` +Expected: `OK` + +- [ ] **Step 4: Commit** + +```bash +cd /Users/fatih.bulut/PythonWorks/clone_app_skill && \ + git add plugins/clone-app/commands/clone-app.md plugins/clone-app/README.md && \ + git commit -m "feat(clone-app): add /clone-app command and README" +``` + +--- + +## Task 11: End-to-end smoke test + final verification + +**Files:** +- Create: `plugins/clone-app/tests/smoke-structure.sh` + +**Interfaces:** +- Produces: a structural smoke test verifying the whole plugin is wired correctly (all files present, all scripts executable, JSON valid, full test suite green). No network — this validates the package, not a live download. + +- [ ] **Step 1: Write the smoke test** + +Create `plugins/clone-app/tests/smoke-structure.sh`: +```bash +#!/usr/bin/env bash +set -uo pipefail +ROOT="$(cd "$(dirname "$0")/../../.." && pwd)" # repo root +P="$ROOT/plugins/clone-app" +fail=0 +must_exist() { [[ -e "$1" ]] && echo "PASS exists: ${1#$ROOT/}" || { echo "FAIL missing: ${1#$ROOT/}"; fail=1; }; } +must_exec() { [[ -x "$1" ]] && echo "PASS exec: ${1#$ROOT/}" || { echo "FAIL not exec: ${1#$ROOT/}"; fail=1; }; } + +must_exist "$P/.claude-plugin/plugin.json" +must_exist "$P/skills/clone-app/SKILL.md" +must_exist "$P/commands/clone-app.md" +must_exist "$P/README.md" +for s in extract-package.sh download-apk.sh resolve-re-scripts.sh; do + must_exist "$P/skills/clone-app/scripts/$s"; must_exec "$P/skills/clone-app/scripts/$s" +done +for s in scrape-play-store.py check-appstore.py; do + must_exist "$P/skills/clone-app/scripts/$s" +done +for r in stack-recommendation-guide effort-estimation-guide infra-cost-guide report-template; do + must_exist "$P/skills/clone-app/references/$r.md" +done + +# JSON validity +python3 -c "import json;json.load(open('$P/.claude-plugin/plugin.json'));json.load(open('$ROOT/.claude-plugin/marketplace.json'))" \ + && echo "PASS json valid" || { echo "FAIL json invalid"; fail=1; } + +# clone-app present in marketplace +python3 -c " +import json;d=json.load(open('$ROOT/.claude-plugin/marketplace.json')) +names=[p['name'] for p in d['plugins']] +assert 'clone-app' in names and 'android-reverse-engineering' in names +print('PASS marketplace has both plugins')" || { echo "FAIL marketplace entries"; fail=1; } + +exit $fail +``` + +- [ ] **Step 2: Run smoke + full suite** + +Run: +```bash +chmod +x /Users/fatih.bulut/PythonWorks/clone_app_skill/plugins/clone-app/tests/smoke-structure.sh && \ +bash /Users/fatih.bulut/PythonWorks/clone_app_skill/plugins/clone-app/tests/smoke-structure.sh && \ +bash /Users/fatih.bulut/PythonWorks/clone_app_skill/plugins/clone-app/tests/run-all.sh +``` +Expected: smoke test all `PASS`; `ALL TESTS PASSED`. Both exit 0. + +- [ ] **Step 3: Verify upstream plugin untouched** + +Run: +```bash +cd /Users/fatih.bulut/PythonWorks/clone_app_skill && \ + git status --porcelain plugins/android-reverse-engineering/ && \ + echo "---" && \ + git log --oneline -1 +``` +Expected: the `git status` line for `plugins/android-reverse-engineering/` is **empty** (no modifications to upstream). The `---` separator prints, then the last commit shows. + +- [ ] **Step 4: Commit** + +```bash +cd /Users/fatih.bulut/PythonWorks/clone_app_skill && \ + git add plugins/clone-app/tests/smoke-structure.sh && \ + git commit -m "test(clone-app): add structural smoke test" +``` + +- [ ] **Step 5: Final manual checklist (report to user, do not automate)** + +Confirm and report: +- [ ] `git pull upstream master` strategy documented in README (Task 10) ✓ +- [ ] Upstream `plugins/android-reverse-engineering/` has zero diffs (Step 3) ✓ +- [ ] All scripts tested green (Step 2) ✓ +- [ ] Skill triggers + command are wired (Tasks 9-10) ✓ +- [ ] Recommend the user do ONE live run against a known small app to validate the + network paths (APKPure download + Play scrape), since those aren't covered by + offline tests. Suggest: pick a small free app, run `/clone-app <url>`, stop + after Phase 3 to confirm download + store JSON look right. + +--- + +## Self-Review Notes + +**Spec coverage check:** +- Phase 0-7 → Task 9 SKILL.md ✓ (all phases present, in order) +- Repo structure / upstream-untouched → Task 1 + Task 11 Step 3 verification ✓ +- APK download w/ retries + xapk detect → Task 4 ✓ +- RE script invocation via sibling path → Task 3 resolver + Task 9 Phase 2 ✓ +- Store analysis (Play + App Store) → Tasks 5, 6 + Phase 3 ✓ +- Stack recommendation (Option C + user choice) → Task 8 guide + Phase 4 ✓ +- Effort estimation in AI Sprints → Task 8 guide + Phase 5 ✓ +- Infra cost → Task 8 guide + Phase 5 ✓ +- Market viability report + GO/NO-GO → Task 8 template + Phase 6 ✓ +- Decision gate → writing-plans handoff → Phase 7 ✓ +- All error-handling rows → Phase 2/3 + SKILL.md error table ✓ +- Known limitations (iOS, obfuscation, install ranges) → reflected in guides/template ✓ +- Install instructions → Task 10 README ✓ + +**Placeholder scan:** No TBD/TODO; all code blocks complete; report-template uses +intentional `{PLACEHOLDER}` tokens that are fill-in fields for runtime, not plan gaps. + +**Type/name consistency:** Script names, JSON keys (`package/title/rating/rating_count/ +installs/category/developer/updated/source`; `found/source/results/name/seller/rating/ +rating_count/price/url`), and env vars (`CLAUDE_PLUGIN_ROOT`, `CLONE_APP_CURL`) match +across tasks. `$WORK`, `$PKG`, `$APK`, `$RE` consistent in SKILL.md. +``` diff --git a/docs/superpowers/specs/2026-06-21-clone-app-skill-design.md b/docs/superpowers/specs/2026-06-21-clone-app-skill-design.md new file mode 100644 index 0000000..aa891cc --- /dev/null +++ b/docs/superpowers/specs/2026-06-21-clone-app-skill-design.md @@ -0,0 +1,244 @@ +# Clone App Skill — Design Spec + +**Date:** 2026-06-21 +**Status:** Approved +**Repo:** fork of `masa2146/android-reverse-engineering-skill` + +--- + +## Overview + +A Claude Code skill (`/clone-app`) that takes a Google Play URL, downloads the APK, reverse engineers it, analyzes the app store presence, estimates clone effort with AI-assisted tooling, evaluates market viability, and optionally generates a full implementation plan. + +**Trigger:** +``` +/clone-app https://play.google.com/store/apps/details?id=com.example.app +``` + +--- + +## Repository Structure + +The skill lives inside the fork of `android-reverse-engineering-skill` as a separate plugin — upstream files are never modified. + +``` +masa2146/android-reverse-engineering-skill/ +├── plugins/ +│ ├── android-reverse-engineering/ ← upstream, untouched +│ │ └── skills/android-reverse-engineering/ +│ │ ├── SKILL.md +│ │ └── scripts/ ← called directly by clone-app +│ └── clone-app/ ← new skill +│ ├── .claude-plugin/ +│ │ └── plugin.json +│ ├── skills/ +│ │ └── clone-app/ +│ │ └── SKILL.md +│ └── commands/ +│ └── clone-app.md +``` + +**Upstream sync:** +```bash +git remote add upstream https://github.com/SimoneAvogadro/android-reverse-engineering-skill.git +git pull upstream master +``` +Conflict risk is low — upstream only modifies `plugins/android-reverse-engineering/` and root files (README, LICENSE). + +--- + +## Architecture + +### Approach: Orchestrator Skill (Option B) + +`clone-app` SKILL.md orchestrates all phases sequentially. RE bash scripts are called directly via relative path from within the same repo — no cross-plugin tool invocation needed. + +RE script path pattern: +```bash +RE_SCRIPTS="$(dirname "$CLAUDE_PLUGIN_ROOT")/../android-reverse-engineering/skills/android-reverse-engineering/scripts" +bash "$RE_SCRIPTS/fingerprint.sh" app.xapk +bash "$RE_SCRIPTS/decompile.sh" app.xapk +bash "$RE_SCRIPTS/find-api-calls.sh" output/sources/ +``` + +--- + +## Phase Workflow + +### Phase 0: Input Validation +- Extract package name from URL via regex: `id=([a-zA-Z0-9._]+)` +- If extraction fails → ask user to provide package name directly +- Create working directory: `./work/{package}/` + +### Phase 1: APK Download +```bash +curl -L "https://d.apkpure.com/b/APK/{PACKAGE}?version=latest" --output work/{package}/app.xapk +``` +- 3 retries on failure +- Detect file type (APK vs XAPK) from magic bytes +- If download fails after retries → ask user for local APK path + +### Phase 2: Reverse Engineering +Calls RE skill scripts in order: + +1. `fingerprint.sh` — framework detection (Flutter/RN/Kotlin/etc.), HTTP stack, obfuscation level +2. `check-deps.sh` — verify jadx, Java 17+ present; auto-install if missing via `install-dep.sh` +3. `decompile.sh` — full decompile to `work/{package}/output/sources/` +4. `recover-kotlin-names.sh` — R8 deobfuscation (if Kotlin app) +5. `find-api-calls.sh` — extract all API endpoints (Retrofit, OkHttp, Ktor, Apollo, etc.) + +**Output:** tech stack JSON, API endpoint list, AndroidManifest summary, detected features. + +**Flutter/RN/Cordova fallback:** fingerprint detects framework → warn user "Java decompile limited for this framework" → proceed with what's available (manifest, hardcoded URLs, SDK list). + +### Phase 3: Store Analysis +- Scrape Google Play store page for: rating, review count, install range, category, last update, developer +- Search App Store for same package → note if iOS version exists +- If App Store not found → continue with Google Play data only + +**Output:** store metrics JSON. + +### Phase 4: Stack Recommendation +AI analyzes RE results + store data and proposes 2-3 clone stack options: +- Considers detected original stack +- Considers AI-assisted development efficiency (Flutter and React Native favored for speed) +- Presents tradeoffs for each option + +Present to user: +> "Detected: Native Kotlin. Recommended for AI-assisted clone: Flutter (fastest) or React Native (JS ecosystem). Original stack also viable. Which do you prefer?" + +User selects stack → locked for Phase 5+. + +### Phase 5: Effort & Cost Estimation +Based on selected stack: + +**Feature list** extracted from APK: +- Screens (Activity/Fragment count) +- Permissions used +- API endpoints count +- Third-party SDKs (payment, auth, analytics, etc.) +- Detected backend patterns (REST/GraphQL/WebSocket) + +**AI-assisted effort table:** + +| Category | Complexity | AI Sprints | +|----------|-----------|------------| +| Auth flow | Medium | 1 | +| Core screens | High | 3-5 | +| API integration | Medium | 2 | +| Backend (if needed) | High | 3-8 | +| ... | ... | ... | + +"AI Sprint" = one focused Claude Code session (~2-4 hours of human review time). + +**Infrastructure cost estimate:** +- Backend hosting (Railway/Render/AWS estimate) +- Database +- CDN / storage +- Third-party API costs (maps, payments, etc.) + +### Phase 6: Market Viability Report + +Synthesizes all data into `work/{package}/clone-report-YYYY-MM-DD.md`: + +```markdown +## App Overview +## Tech Stack (Detected) +## Recommended Clone Stack +## Feature List (from APK) +## Effort Estimate (AI-assisted) +## Infrastructure Cost Estimate (monthly) +## Market Analysis + - Current app metrics + - Competitor landscape + - Target market size estimate +## Viability Verdict + - GO / CONDITIONAL GO / NO GO + - Key risks + - Key opportunities +``` + +### Phase 7: Decision Gate +Present report summary to user: + +> "Report saved to work/{package}/clone-report-YYYY-MM-DD.md. Want to proceed with building the implementation plan?" + +- **Yes** → invoke `writing-plans` skill with report as context +- **No** → save report, end skill + +--- + +## Error Handling + +| Scenario | Behavior | +|----------|----------| +| APKPure download fails (3x) | Ask user for local APK path | +| XAPK (split bundle) | RE skill handles automatically | +| Flutter/RN/Cordova app | Warn, continue with available data | +| App Store not found | Continue with Google Play only | +| RE skill not installed | Run `check-deps.sh`, auto-install missing tools | +| Package name not in URL | Ask user to input package name | +| `writing-plans` skill unavailable | Write plan as Markdown manually | +| Heavy obfuscation | Note in report, feature list may be incomplete | + +--- + +## Known Limitations + +- iOS API extraction not possible (store scrape only, no IPA download) +- Effort estimates are AI-sprint based, not calendar time — depends on developer review speed +- Play Store download counts are ranges (not exact) — viability estimate uses midpoint +- APKPure availability not guaranteed for all apps +- Heavy R8 obfuscation may limit feature detection accuracy + +--- + +## Working Directory Layout + +``` +./work/{package}/ +├── app.xapk ← downloaded APK +├── output/ +│ ├── sources/ ← decompiled Java/Kotlin +│ └── names/ ← Kotlin name recovery maps +├── clone-report-YYYY-MM-DD.md ← final report +└── implementation-plan.md ← generated by writing-plans +``` + +--- + +## Dependencies + +**Skill dependencies:** +- `android-reverse-engineering` plugin (same repo) +- Java JDK 17+ +- jadx (auto-installed if missing) +- curl + +**Optional:** +- Vineflower/Fernflower (better decompile quality) +- dex2jar + +--- + +## Installation + +```bash +# 1. Clone fork +git clone https://github.com/masa2146/android-reverse-engineering-skill.git +cd android-reverse-engineering-skill + +# 2. Install both plugins +/plugin marketplace add . +/plugin install android-reverse-engineering@android-reverse-engineering-skill +/plugin install clone-app@android-reverse-engineering-skill +``` + +--- + +## Out of Scope + +- Automated code generation of the clone (that's writing-plans → executing-plans) +- iOS IPA download/reverse engineering +- Real-time market data (uses scraped/estimated data) +- Legal review of clone viability