android-reverse-engineering.../docs/superpowers/plans/2026-06-21-clone-app-skill.md

55 KiB
Raw Blame History

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 (07); 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:

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:

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:

{
  "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:

"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:

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
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 <url-or-package> 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:

#!/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 /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:

#!/usr/bin/env bash
set -euo pipefail

input="${1:-}"
if [[ -z "$input" ]]; then
  echo "ERROR: usage: extract-package.sh <play-url-or-package>" >&2
  exit 1
fi

# Case 1: full URL containing id=<package>
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:

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
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:

#!/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 /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:

#!/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:

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
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 <package> <out-dir> and optional env CLONE_APP_CURL (override curl binary, used by test to inject a stub).

  • Produces: downloads to <out-dir>/app.<ext> 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:

#!/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 /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:

#!/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 <package> <out-dir>" >&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:

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
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 <package> fetches https://play.google.com/store/apps/details?id=<package>&hl=en&gl=US. For testing, --html-file <path> 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:

<!DOCTYPE html>
<html><head><title>Example App - Apps on Google Play</title>
<script type="application/ld+json">
{"@type":"SoftwareApplication","name":"Example App","author":{"name":"Example Studio"},
"aggregateRating":{"ratingValue":"4.3","ratingCount":"12873"},
"applicationCategory":"GAME_PUZZLE"}
</script>
</head><body>
<div>Updated on<span>Jun 1, 2026</span></div>
<div>1,000,000+<span>Downloads</span></div>
</body></html>
  • Step 2: Write the failing test

Create plugins/clone-app/tests/test-scrape-play-store.py:

#!/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:

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:

#!/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'<script type="application/ld\+json">(.*?)</script>',
                         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*<span>\s*Downloads', html)
    if m:
        out["installs"] = m.group(1)

    # updated date (e.g. "Updated on<span>Jun 1, 2026</span>")
    m = re.search(r'Updated on\s*<span>([^<]+)</span>', 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:

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
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 <search-term> queries https://itunes.apple.com/search?term=<term>&entity=software&limit=5. For testing, --json-file <path> 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:

{
  "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:

#!/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:

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:

#!/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:

python3 /Users/fatih.bulut/PythonWorks/clone_app_skill/plugins/clone-app/tests/test-check-appstore.py

Expected: all PASS, exit 0.

  • Step 6: Commit
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:

#!/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:

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 26 green).

  • Step 3: Commit
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 46. 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:

# 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:

# 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 (minmax 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 (minmax), then a TOTAL row.
  • Step 3: Write the infra cost guide

Create plugins/clone-app/skills/clone-app/references/infra-cost-guide.md:

# 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:

# 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
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:

---
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 "<user-input>")"

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

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:

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" <dep>; 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" <sources> "$WORK/output/names/".
  5. bash "$RE/find-api-calls.sh" <sources> (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

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:

python3 ${CLAUDE_PLUGIN_ROOT}/skills/clone-app/scripts/check-appstore.py "<title>" > "$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
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:

---
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:

# 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

/clone-app https://play.google.com/store/apps/details?id=com.example.app

Or natural language: "Analyze this Play Store app for cloning: ".

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:

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.

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
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:

#!/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:

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:

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
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.