Merge pull request #1 from masa2146/feat/clone-app-re-subagent
Feat/clone app re subagent
This commit is contained in:
commit
adf93bacd1
|
|
@ -0,0 +1,543 @@
|
|||
# Clone App — RE-via-Subagent + Payload Extraction 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:** Rewrite clone-app Phase 2 to run the `android-reverse-engineering` skill inside an isolated subagent (falling back to direct scripts), extract Tier-2 payloads for auth/payment/core flows, and persist a durable digest contract to `$WORK/`.
|
||||
|
||||
**Architecture:** Phase 2 becomes probe → dispatch → consume. A dispatched subagent runs RE in its own context and writes three files (`re-digest.md`, `payloads.json`, `re-summary.txt`) to `$WORK/`; the orchestrator ingests only the ≤40-line summary. A new reference doc `re-digest-contract.md` is the single source of truth for the schema. Both the skill branch and the script-fallback branch write the same files, so Phases 3–7 stay branch-agnostic. The work product is Markdown (SKILL.md prose + reference doc) plus structural bash tests — there is no compiled runtime, so "tests" assert document structure and wiring, matching the repo's existing offline test philosophy.
|
||||
|
||||
**Tech Stack:** Bash (`#!/usr/bin/env bash`, invoked via `bash <path>`), Python 3 stdlib (`json` for validity checks), Markdown skill/reference docs.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- **Upstream untouched:** No edits under `plugins/android-reverse-engineering/`. After every commit, `git status --porcelain plugins/android-reverse-engineering/` MUST print nothing.
|
||||
- **All new/changed files live under `plugins/clone-app/`** (plus the design docs already committed).
|
||||
- **Bash scripts use `#!/usr/bin/env bash`** and are run with `bash <path>` (shell is zsh; `sh` breaks them). Tests use `set -uo pipefail` (NOT `-e`) and aggregate failures into a `fail` var so every assertion runs.
|
||||
- **Python is stdlib-only** — `json`, no pip, no venv.
|
||||
- **Effort is measured in "AI Sprints"** (one focused Claude session), never calendar time — preserve this wording in any SKILL.md edits.
|
||||
- **Conventional Commits scoped to the plugin:** `feat(clone-app): …`, `test(clone-app): …`, `docs(clone-app): …`.
|
||||
- **Two pause points in SKILL.md stay:** Phase 4 (choose stack) and Phase 7 (proceed to plan?). Do not add new mandatory pauses in Phase 2.
|
||||
- **Working dir convention:** `WORK="./work/$PKG"` relative to the user's cwd; decompile output lands at `$WORK/output/` via `decompile.sh -o`.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Digest contract reference doc + schema fixtures + contract test
|
||||
|
||||
Establishes the single source of truth for what the subagent must produce, plus offline fixtures and a structural test that locks the schema. This task is independently reviewable: a reviewer can accept the contract/fixtures/test without yet seeing the SKILL.md rewrite.
|
||||
|
||||
**Files:**
|
||||
- Create: `plugins/clone-app/skills/clone-app/references/re-digest-contract.md`
|
||||
- Create: `plugins/clone-app/tests/fixtures/re-digest.sample.md`
|
||||
- Create: `plugins/clone-app/tests/fixtures/payloads.sample.json`
|
||||
- Test: `plugins/clone-app/tests/test-re-digest-contract.sh`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces (consumed by Task 2's SKILL.md prose and by the subagent at runtime):
|
||||
- `re-digest.md` required section headings (exact strings): `## Framework & Stack`, `## Hosts`, `## Endpoint Inventory`, `## Key Flow Payloads`, `## BuildConfig Secrets`, `## Feature Signals`, `## RE Method`.
|
||||
- `payloads.json` required top-level keys: `package`, `re_method`, `endpoints`, `buildconfig`. Each `endpoints[]` object has keys: `host`, `method`, `path`, `auth`, `source`, `request_body`, `response`, `headers`.
|
||||
- `re-summary.txt`: plain text, ≤40 lines, fields — framework, host count, endpoint count, key-flow names, secrets-found count, RE method, blockers.
|
||||
- `RE Method` allowed values: `re-skill`, `direct-scripts`, `limited: <framework>`.
|
||||
|
||||
- [ ] **Step 1: Write the failing test**
|
||||
|
||||
Create `plugins/clone-app/tests/test-re-digest-contract.sh`:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -uo pipefail
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
P="$HERE/.." # plugins/clone-app
|
||||
CONTRACT="$P/skills/clone-app/references/re-digest-contract.md"
|
||||
DIGEST_FIX="$HERE/fixtures/re-digest.sample.md"
|
||||
PAYLOAD_FIX="$HERE/fixtures/payloads.sample.json"
|
||||
fail=0
|
||||
has() { grep -qF "$2" "$1" && echo "PASS: $3" || { echo "FAIL: $3 — '$2' not in ${1##*/}"; fail=1; }; }
|
||||
|
||||
# Contract doc exists and documents every required re-digest.md section heading
|
||||
for sec in "## Framework & Stack" "## Hosts" "## Endpoint Inventory" \
|
||||
"## Key Flow Payloads" "## BuildConfig Secrets" "## Feature Signals" "## RE Method"; do
|
||||
has "$CONTRACT" "$sec" "contract documents section $sec"
|
||||
done
|
||||
|
||||
# Contract doc documents every required payloads.json key
|
||||
for key in '"package"' '"re_method"' '"endpoints"' '"buildconfig"' \
|
||||
'"request_body"' '"response"' '"headers"'; do
|
||||
has "$CONTRACT" "$key" "contract documents json key $key"
|
||||
done
|
||||
|
||||
# Contract names the three output files and the three RE Method values
|
||||
for tok in "re-digest.md" "payloads.json" "re-summary.txt" \
|
||||
"re-skill" "direct-scripts" "limited:"; do
|
||||
has "$CONTRACT" "$tok" "contract names token $tok"
|
||||
done
|
||||
|
||||
# Digest fixture has every required section heading
|
||||
for sec in "## Framework & Stack" "## Hosts" "## Endpoint Inventory" \
|
||||
"## Key Flow Payloads" "## BuildConfig Secrets" "## Feature Signals" "## RE Method"; do
|
||||
has "$DIGEST_FIX" "$sec" "digest fixture has section $sec"
|
||||
done
|
||||
|
||||
# Payload fixture is valid JSON and has the required shape
|
||||
python3 -c "
|
||||
import json,sys
|
||||
d=json.load(open('$PAYLOAD_FIX'))
|
||||
for k in ('package','re_method','endpoints','buildconfig'):
|
||||
assert k in d, 'missing top key '+k
|
||||
assert isinstance(d['endpoints'],list) and d['endpoints'], 'endpoints must be non-empty list'
|
||||
for e in d['endpoints']:
|
||||
for k in ('host','method','path','auth','source','request_body','response','headers'):
|
||||
assert k in e, 'endpoint missing key '+k
|
||||
print('PASS: payload fixture shape valid')
|
||||
" || { echo "FAIL: payload fixture shape"; fail=1; }
|
||||
|
||||
exit $fail
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `bash plugins/clone-app/tests/test-re-digest-contract.sh`
|
||||
Expected: FAIL — contract doc and fixtures do not exist yet (grep errors / `FAIL:` lines), exit non-zero.
|
||||
|
||||
- [ ] **Step 3: Create the contract reference doc**
|
||||
|
||||
Create `plugins/clone-app/skills/clone-app/references/re-digest-contract.md`:
|
||||
|
||||
````markdown
|
||||
# RE Digest Contract
|
||||
|
||||
The Phase 2 subagent runs the reverse-engineering workflow in isolation and
|
||||
MUST produce these three files under `$WORK/`, then return only the summary.
|
||||
This file is the single source of truth for their schema. The subagent prompt
|
||||
points here; do not duplicate the schema into SKILL.md prose.
|
||||
|
||||
## File 1 — `$WORK/re-digest.md` (human-readable, main artifact)
|
||||
|
||||
Required section headings, in this order:
|
||||
|
||||
```
|
||||
# RE Digest — <pkg>
|
||||
## Framework & Stack framework, HTTP lib, DI, serialization, obfuscation level
|
||||
## Hosts first-party vs third-party (table)
|
||||
## Endpoint Inventory Tier-1: host | method | path | auth | source file
|
||||
## Key Flow Payloads Tier-2: auth, payment/checkout, 1-2 core flows
|
||||
— request body / response shape / headers / params
|
||||
## BuildConfig Secrets base URLs, API keys, feature flags, flavors
|
||||
## Feature Signals screen count, SDKs, permissions, components
|
||||
## RE Method re-skill | direct-scripts | limited: <framework>
|
||||
```
|
||||
|
||||
## File 2 — `$WORK/payloads.json` (machine-readable, durable memory)
|
||||
|
||||
Required top-level keys: `package`, `re_method`, `endpoints`, `buildconfig`.
|
||||
Each item in `endpoints` has: `host`, `method`, `path`, `auth`, `source`,
|
||||
`request_body`, `response`, `headers`.
|
||||
|
||||
```json
|
||||
{
|
||||
"package": "com.example.app",
|
||||
"re_method": "re-skill",
|
||||
"endpoints": [
|
||||
{
|
||||
"host": "api.example.com",
|
||||
"method": "POST",
|
||||
"path": "/v1/auth/login",
|
||||
"auth": "none",
|
||||
"source": "com/example/api/AuthApi.java",
|
||||
"request_body": { "email": "string", "password": "string" },
|
||||
"response": { "token": "string", "user": {} },
|
||||
"headers": { "Content-Type": "application/json" }
|
||||
}
|
||||
],
|
||||
"buildconfig": { "BASE_URL": "https://api.example.com/v1" }
|
||||
}
|
||||
```
|
||||
|
||||
- `request_body` and `response` are `null` for Tier-1-only endpoints.
|
||||
- Populate them ONLY for the key flows: **auth, payment/checkout, and the
|
||||
1–2 core feature endpoints** (decision Q3=B). Everything else is a Tier-1
|
||||
row with `null` payloads. Going Tier-2 on every endpoint is a non-goal —
|
||||
it is token-expensive and the RE skill itself warns against it.
|
||||
|
||||
## File 3 — `$WORK/re-summary.txt` (≤40 lines, the only RE text returned)
|
||||
|
||||
Plain text. The subagent's return value = the contents of this file plus the
|
||||
paths to the two files above. Fields:
|
||||
|
||||
- framework
|
||||
- host count (first-party / third-party)
|
||||
- endpoint count
|
||||
- key-flow names found (auth / payment / core)
|
||||
- secrets-found count (BuildConfig)
|
||||
- RE method: `re-skill` | `direct-scripts` | `limited: <framework>`
|
||||
- blockers / warnings (e.g. obfuscation, framework guard)
|
||||
|
||||
The subagent MUST NOT return raw decompiled sources.
|
||||
|
||||
## RE Method values
|
||||
|
||||
| Value | Meaning |
|
||||
|---|---|
|
||||
| `re-skill` | The `android-reverse-engineering` skill ran the workflow. |
|
||||
| `direct-scripts` | The skill was absent; the sibling plugin's bash scripts ran. |
|
||||
| `limited: <framework>` | Flutter / React Native / Cordova / Xamarin — Java decompile is shallow; payloads may be empty and the digest is partial. |
|
||||
|
||||
## Framework guard
|
||||
|
||||
If the fingerprint reports Flutter / React Native / Cordova / Xamarin, set
|
||||
`RE Method: limited: <framework>`, write whatever partial signals are
|
||||
available (manifest, strings, hardcoded URLs, SDK list), and leave payloads
|
||||
empty where they cannot be recovered. Downstream phases widen the
|
||||
uncertainty band accordingly.
|
||||
````
|
||||
|
||||
- [ ] **Step 4: Create the digest fixture**
|
||||
|
||||
Create `plugins/clone-app/tests/fixtures/re-digest.sample.md`:
|
||||
|
||||
```markdown
|
||||
# RE Digest — com.example.app
|
||||
|
||||
## Framework & Stack
|
||||
Native Kotlin · Retrofit + OkHttp · Hilt · kotlinx.serialization · obfuscation: moderate
|
||||
|
||||
## Hosts
|
||||
| Host | Party |
|
||||
|------|-------|
|
||||
| api.example.com | first |
|
||||
| analytics.thirdparty.io | third |
|
||||
|
||||
## Endpoint Inventory
|
||||
| Host | Method | Path | Auth | Source |
|
||||
|------|--------|------|------|--------|
|
||||
| api.example.com | POST | /v1/auth/login | none | com/example/api/AuthApi.java |
|
||||
| api.example.com | GET | /v1/users/profile | Bearer | com/example/api/UserApi.java |
|
||||
| api.example.com | POST | /v1/orders | Bearer | com/example/api/OrderApi.java |
|
||||
|
||||
## Key Flow Payloads
|
||||
### POST /v1/auth/login (auth)
|
||||
- request body: `{ "email": "string", "password": "string" }`
|
||||
- response: `{ "token": "string", "user": {} }`
|
||||
- headers: `Content-Type: application/json`
|
||||
|
||||
### POST /v1/orders (payment/core)
|
||||
- request body: `{ "items": [], "total": "number" }`
|
||||
- response: `{ "order_id": "string", "status": "string" }`
|
||||
- headers: `Authorization: Bearer <token>`
|
||||
|
||||
## BuildConfig Secrets
|
||||
- BASE_URL = https://api.example.com/v1
|
||||
- ANALYTICS_KEY = <redacted-present>
|
||||
|
||||
## Feature Signals
|
||||
~18 screens · SDKs: Firebase, AppsFlyer · permissions: INTERNET, ACCESS_NETWORK_STATE
|
||||
|
||||
## RE Method
|
||||
re-skill
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Create the payloads fixture**
|
||||
|
||||
Create `plugins/clone-app/tests/fixtures/payloads.sample.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"package": "com.example.app",
|
||||
"re_method": "re-skill",
|
||||
"endpoints": [
|
||||
{
|
||||
"host": "api.example.com",
|
||||
"method": "POST",
|
||||
"path": "/v1/auth/login",
|
||||
"auth": "none",
|
||||
"source": "com/example/api/AuthApi.java",
|
||||
"request_body": { "email": "string", "password": "string" },
|
||||
"response": { "token": "string", "user": {} },
|
||||
"headers": { "Content-Type": "application/json" }
|
||||
},
|
||||
{
|
||||
"host": "api.example.com",
|
||||
"method": "GET",
|
||||
"path": "/v1/users/profile",
|
||||
"auth": "Bearer",
|
||||
"source": "com/example/api/UserApi.java",
|
||||
"request_body": null,
|
||||
"response": null,
|
||||
"headers": { "Authorization": "Bearer <token>" }
|
||||
}
|
||||
],
|
||||
"buildconfig": { "BASE_URL": "https://api.example.com/v1" }
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 6: Run test to verify it passes**
|
||||
|
||||
Run: `bash plugins/clone-app/tests/test-re-digest-contract.sh`
|
||||
Expected: all `PASS:` lines, exit 0.
|
||||
|
||||
- [ ] **Step 7: Verify upstream untouched**
|
||||
|
||||
Run: `git status --porcelain plugins/android-reverse-engineering/`
|
||||
Expected: no output.
|
||||
|
||||
- [ ] **Step 8: Commit**
|
||||
|
||||
```bash
|
||||
git add plugins/clone-app/skills/clone-app/references/re-digest-contract.md \
|
||||
plugins/clone-app/tests/fixtures/re-digest.sample.md \
|
||||
plugins/clone-app/tests/fixtures/payloads.sample.json \
|
||||
plugins/clone-app/tests/test-re-digest-contract.sh
|
||||
git commit -m "test(clone-app): add RE digest contract, fixtures, structural test"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Rewrite SKILL.md Phase 2 + update Phases 5–6 + error table
|
||||
|
||||
Rewrites Phase 2 to probe/dispatch/consume with the subagent and fallback, wires the digest into Phases 5–6, and extends the error table. The contract test from Task 1 is extended here to assert the SKILL.md wiring, so this task is independently testable.
|
||||
|
||||
**Files:**
|
||||
- Modify: `plugins/clone-app/skills/clone-app/SKILL.md` (Phase 2 block lines 43–71; Phase 5 ~93–99; Phase 6 ~101–112; error table ~122–133)
|
||||
- Modify: `plugins/clone-app/tests/test-re-digest-contract.sh` (append SKILL.md wiring assertions)
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes (from Task 1): the three output filenames (`re-digest.md`, `payloads.json`, `re-summary.txt`), the `references/re-digest-contract.md` path, and the `RE Method` values.
|
||||
- Produces: Phase 2 prose that any later phase relies on — `$WORK/re-summary.txt` read in Phase 2c, `$WORK/payloads.json` read in Phase 5, `$WORK/re-digest.md` referenced in Phase 6.
|
||||
|
||||
- [ ] **Step 1: Append failing wiring assertions to the test**
|
||||
|
||||
Add to the end of `plugins/clone-app/tests/test-re-digest-contract.sh`, BEFORE the final `exit $fail` line (replace that final line):
|
||||
|
||||
```bash
|
||||
# --- SKILL.md Phase 2 wiring (Task 2) ---
|
||||
SKILL="$P/skills/clone-app/SKILL.md"
|
||||
hasS() { grep -qF "$1" "$SKILL" && echo "PASS: SKILL $2" || { echo "FAIL: SKILL $2 — '$1' missing"; fail=1; }; }
|
||||
hasReS() { grep -qE "$1" "$SKILL" && echo "PASS: SKILL $2" || { echo "FAIL: SKILL $2 — /$1/ missing"; fail=1; }; }
|
||||
|
||||
hasS "re-digest-contract.md" "Phase 2 points at the digest contract"
|
||||
hasReS "subagent|Agent tool|dispatch" "Phase 2 dispatches a subagent"
|
||||
hasS "android-reverse-engineering skill" "Phase 2 names the RE skill branch"
|
||||
hasS "direct-scripts" "Phase 2 names the script-fallback branch"
|
||||
hasS "re-summary.txt" "Phase 2c consumes the summary"
|
||||
hasS "payloads.json" "Phase 5 consumes payloads.json"
|
||||
hasS "Backend API Surface" "Phase 6 adds the Backend API Surface section"
|
||||
hasS "RE subagent" "error table covers subagent failure"
|
||||
|
||||
exit $fail
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run test to verify the new assertions fail**
|
||||
|
||||
Run: `bash plugins/clone-app/tests/test-re-digest-contract.sh`
|
||||
Expected: Task-1 assertions still PASS; the new `SKILL …` assertions FAIL (SKILL.md not yet rewritten), exit non-zero.
|
||||
|
||||
- [ ] **Step 3: Replace the Phase 2 block in SKILL.md**
|
||||
|
||||
In `plugins/clone-app/skills/clone-app/SKILL.md`, replace the entire `## Phase 2: Reverse Engineering` section (from the `## Phase 2` heading through the line ending `...Keep this in context for later phases.`) with:
|
||||
|
||||
````markdown
|
||||
## Phase 2: Reverse Engineering (probe → dispatch → consume)
|
||||
|
||||
RE runs inside an **isolated subagent** so the decompiled sources never flood
|
||||
this orchestrator's context. The subagent prefers the
|
||||
`android-reverse-engineering` **skill** and falls back to that plugin's bash
|
||||
**scripts**. Either way it writes the same digest files to `$WORK/`, defined
|
||||
in `${CLAUDE_PLUGIN_ROOT}/skills/clone-app/references/re-digest-contract.md`.
|
||||
|
||||
### Phase 2a — Probe
|
||||
|
||||
```bash
|
||||
RE="$(bash ${CLAUDE_PLUGIN_ROOT}/skills/clone-app/scripts/resolve-re-scripts.sh 2>/tmp/re-err)"; RC=$?
|
||||
```
|
||||
- `RC == 0` → the RE **scripts** are on disk (fallback is available). If it
|
||||
printed a `WARNING:` about bash version, the scripts need **bash 4+**
|
||||
(macOS ships 3.2; `${VAR,,}` fails as "bad substitution") — install one with
|
||||
`brew install bash` before the script-fallback branch can succeed.
|
||||
- Check your own available-skills list for `android-reverse-engineering` →
|
||||
is the RE **skill** registered?
|
||||
|
||||
Pick the branch:
|
||||
|
||||
| RE skill registered | RE scripts on disk (`RC`) | Branch |
|
||||
|---|---|---|
|
||||
| yes | any | **re-skill** |
|
||||
| no | 0 | **direct-scripts** |
|
||||
| no | 1 | **stop** — show the `/tmp/re-err` resolver error and halt |
|
||||
|
||||
### Phase 2b — Dispatch the subagent
|
||||
|
||||
Dispatch one subagent (Agent tool, `general-purpose` type — it can both invoke
|
||||
skills and run bash). Pass it: `$PKG`, `$APK`, `$WORK`, the chosen **branch**,
|
||||
the resolved `$RE` scripts dir, and the path to `re-digest-contract.md`. Its
|
||||
instructions:
|
||||
|
||||
1. **Run RE per branch.**
|
||||
- **re-skill:** invoke the `android-reverse-engineering` skill on `$APK`,
|
||||
output dir `$WORK/output` — run its full workflow (fingerprint, deps,
|
||||
decompile, Kotlin-name recovery if Kotlin, API extraction incl. Tier-2).
|
||||
- **direct-scripts:** run, in order, reading each output before the next:
|
||||
`bash "$RE/fingerprint.sh" "$APK"`, `bash "$RE/check-deps.sh"`
|
||||
(install required deps via `bash "$RE/install-dep.sh" <dep>`; ask before
|
||||
optional vineflower/dex2jar), `bash "$RE/decompile.sh" -o "$WORK/output" "$APK"`
|
||||
(add `--deobf` if obfuscation is heavy), `bash "$RE/recover-kotlin-names.sh"
|
||||
"$WORK/output/sources" "$WORK/output/names/"` if Kotlin, then
|
||||
`bash "$RE/find-api-calls.sh" "$WORK/output/sources"`.
|
||||
2. **Framework guard:** if the fingerprint is Flutter / React Native / Cordova
|
||||
/ Xamarin, Java decompile is shallow — produce a partial digest, set
|
||||
`RE Method: limited: <framework>`, payloads may be empty.
|
||||
3. **Extract** the Tier-1 endpoint inventory and Tier-2 payloads for **auth,
|
||||
payment/checkout, and the 1–2 core feature endpoints** (not every endpoint).
|
||||
4. **Write** `$WORK/re-digest.md`, `$WORK/payloads.json`, `$WORK/re-summary.txt`
|
||||
exactly per `re-digest-contract.md`.
|
||||
5. **Return** the contents of `$WORK/re-summary.txt` plus the two file paths —
|
||||
**never** raw decompiled sources.
|
||||
|
||||
If the subagent fails, retry once; if it still fails and the **direct-scripts**
|
||||
branch is available, re-dispatch on that branch; otherwise stop and report.
|
||||
|
||||
### Phase 2c — Consume
|
||||
|
||||
Read `$WORK/re-summary.txt` (the only RE text in this context). From it you have:
|
||||
framework, HTTP stack, host counts, endpoint count, key-flow names, secrets
|
||||
count, and the RE method. Read `$WORK/re-digest.md` or `$WORK/payloads.json`
|
||||
**on demand** when a later phase needs detail. Keep the summary in context for
|
||||
Phases 3–7.
|
||||
````
|
||||
|
||||
- [ ] **Step 4: Update Phase 5 to read payloads.json**
|
||||
|
||||
In `plugins/clone-app/skills/clone-app/SKILL.md`, in `## Phase 5: Effort & Cost Estimation`, replace the line:
|
||||
|
||||
```
|
||||
- the feature list → AI-Sprint effort table (min-max total, uncertainty band),
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```
|
||||
- read `$WORK/payloads.json`; the endpoint count and the payload complexity of
|
||||
the key flows size the backend work,
|
||||
- the feature list + backend surface → AI-Sprint effort table (min-max total,
|
||||
uncertainty band; widen the band when RE Method is `limited:`),
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Update Phase 6 to add the Backend API Surface section**
|
||||
|
||||
In `## Phase 6: Market Viability Report`, after the sentence ending
|
||||
`...Fill every section from the data gathered.` add:
|
||||
|
||||
```
|
||||
Include a **Backend API Surface** section: summarize the Tier-1 inventory from
|
||||
`$WORK/re-digest.md` and the key-flow payloads from `$WORK/payloads.json` (host
|
||||
list, endpoint count, auth model, and the auth/payment/core request+response
|
||||
shapes). If RE Method was `limited:`, say so and note the reduced confidence.
|
||||
```
|
||||
|
||||
- [ ] **Step 6: Extend the error table**
|
||||
|
||||
In `plugins/clone-app/skills/clone-app/SKILL.md`, in the `## Error Handling Summary` table, replace the row:
|
||||
|
||||
```
|
||||
| RE plugin missing | show resolver error, stop |
|
||||
```
|
||||
|
||||
with these rows:
|
||||
|
||||
```
|
||||
| RE skill + scripts both missing | show resolver error, stop |
|
||||
| RE subagent fails | retry once, then fall back to direct-scripts branch; else stop |
|
||||
| Subagent returned no digest files | re-dispatch once; if still missing, stop and report |
|
||||
```
|
||||
|
||||
- [ ] **Step 7: Run the contract test to verify it passes**
|
||||
|
||||
Run: `bash plugins/clone-app/tests/test-re-digest-contract.sh`
|
||||
Expected: all `PASS:` (Task 1 + the new SKILL wiring assertions), exit 0.
|
||||
|
||||
- [ ] **Step 8: Verify upstream untouched**
|
||||
|
||||
Run: `git status --porcelain plugins/android-reverse-engineering/`
|
||||
Expected: no output.
|
||||
|
||||
- [ ] **Step 9: Commit**
|
||||
|
||||
```bash
|
||||
git add plugins/clone-app/skills/clone-app/SKILL.md \
|
||||
plugins/clone-app/tests/test-re-digest-contract.sh
|
||||
git commit -m "feat(clone-app): run RE via isolated subagent with script fallback + payloads"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Register the contract doc in the smoke test + full suite green
|
||||
|
||||
Adds the new reference file to the structural smoke check and confirms the whole suite passes. A reviewer can reject this independently if the smoke test or aggregate run regresses.
|
||||
|
||||
**Files:**
|
||||
- Modify: `plugins/clone-app/tests/smoke-structure.sh:19-21` (the references loop)
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes (from Task 1): the reference filename `re-digest-contract`.
|
||||
- Produces: nothing downstream — terminal verification task.
|
||||
|
||||
- [ ] **Step 1: Add the new reference to the smoke test's existence check**
|
||||
|
||||
In `plugins/clone-app/tests/smoke-structure.sh`, replace:
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```bash
|
||||
for r in stack-recommendation-guide effort-estimation-guide infra-cost-guide report-template re-digest-contract; do
|
||||
must_exist "$P/skills/clone-app/references/$r.md"
|
||||
done
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run the smoke test**
|
||||
|
||||
Run: `bash plugins/clone-app/tests/smoke-structure.sh`
|
||||
Expected: all `PASS` lines including `PASS exists: plugins/clone-app/skills/clone-app/references/re-digest-contract.md`, exit 0.
|
||||
|
||||
- [ ] **Step 3: Run the full clone-app suite**
|
||||
|
||||
Run: `bash plugins/clone-app/tests/run-all.sh`
|
||||
Expected: every suite runs; final line `ALL TESTS PASSED`, exit 0. (`run-all.sh` auto-discovers `test-re-digest-contract.sh` via its `test-*.sh` glob — no registration needed.)
|
||||
|
||||
- [ ] **Step 4: Verify upstream untouched**
|
||||
|
||||
Run: `git status --porcelain plugins/android-reverse-engineering/`
|
||||
Expected: no output.
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add plugins/clone-app/tests/smoke-structure.sh
|
||||
git commit -m "test(clone-app): register re-digest-contract in smoke structure check"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Self-Review
|
||||
|
||||
**Spec coverage:**
|
||||
- Phase 2 probe/dispatch/consume → Task 2 Step 3. ✓
|
||||
- Subagent isolation (Agent tool, general-purpose) → Task 2 Step 3 Phase 2b. ✓
|
||||
- Branch decision table (skill / scripts / stop) → Task 2 Step 3 Phase 2a. ✓
|
||||
- Digest contract (3 files, schemas, RE Method values, framework guard) → Task 1 Step 3. ✓
|
||||
- Tier-2 payloads for auth/payment/core only → contract doc + Task 2 Phase 2b step 3. ✓
|
||||
- Durable memory in `$WORK/` (memory option 1) → files written by subagent, consumed on demand. ✓
|
||||
- Phase 5 reads payloads.json → Task 2 Step 4. ✓
|
||||
- Phase 6 Backend API Surface section → Task 2 Step 5. ✓
|
||||
- Error table additions (subagent fail, no digest, both branches missing) → Task 2 Step 6. ✓
|
||||
- New fixtures + structural tests, offline → Task 1. ✓
|
||||
- smoke-structure.sh updated → Task 3 Step 1. ✓
|
||||
- Upstream untouched check → every task's penultimate step + global constraint. ✓
|
||||
- `resolve-re-scripts.sh` unchanged → not modified in any task. ✓
|
||||
|
||||
**Placeholder scan:** No TBD/TODO; every code/doc step shows full content. ✓
|
||||
|
||||
**Type consistency:** Filenames (`re-digest.md`, `payloads.json`, `re-summary.txt`), JSON keys (`package`, `re_method`, `endpoints`, `buildconfig`, `request_body`, `response`, `headers`), section headings, and RE Method values (`re-skill`, `direct-scripts`, `limited:`) are identical across the contract doc, fixtures, tests, and SKILL.md assertions. ✓
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
# Clone App — RE-via-Subagent + Payload Extraction Design Spec
|
||||
|
||||
**Date:** 2026-06-21
|
||||
**Scope:** `plugins/clone-app/` only. `plugins/android-reverse-engineering/` stays byte-identical (upstream-untouched rule).
|
||||
**Supersedes:** Phase 2 of the original `2026-06-21-clone-app-skill-design.md`. All other phases unchanged unless noted.
|
||||
|
||||
## Problem
|
||||
|
||||
Two gaps in the current clone-app skill:
|
||||
|
||||
1. **The RE skill never runs.** clone-app Phase 2 calls the `android-reverse-engineering` plugin's bash *scripts* directly (via `resolve-re-scripts.sh`). The RE *skill's* prose workflow — fingerprint triage, two-tier API documentation, BuildConfig secret hunting, call-flow tracing, cross-platform `.ps1` handling — is bypassed. clone-app re-implements a thin, lossy version of it inline. The user never observes the RE skill triggering because it genuinely does not.
|
||||
|
||||
2. **Payloads are not extracted.** Phase 2 assembles only an endpoint *list* (host/method/path/auth). Request bodies, response shapes, and headers — the RE skill's "Tier 2" detail — are never produced and never persisted. Nothing reaches memory or the feasibility report.
|
||||
|
||||
## Decisions (locked during brainstorming)
|
||||
|
||||
- **Q1 → C (hybrid w/ fallback):** Invoke the `android-reverse-engineering` **skill** for the heavy RE workflow when it is installed; **fall back to direct scripts** when it is not. Full workflow when available, graceful degradation otherwise.
|
||||
- **Q2 → A (subagent isolation):** RE runs inside a dispatched subagent, in its own context. The orchestrator never ingests raw decompiled sources — only a concise summary plus on-demand file reads.
|
||||
- **Q3 → B (targeted payloads):** Tier-2 payloads (request body, response shape, headers, params) for **auth, payment/checkout, and the 1–2 core feature endpoints**. Tier-1 flat inventory for everything else. Matches the RE skill's own "Tier-1 always, Tier-2 for ~10 high-value" guidance.
|
||||
- **Memory → 1 (durable files):** Full detail written to files in `$WORK/`, off-context, re-readable. Only a ≤40-line summary enters orchestrator context.
|
||||
|
||||
## Architecture — Phase 2 rewrite (probe → dispatch → consume)
|
||||
|
||||
```
|
||||
Phase 2a PROBE resolve-re-scripts.sh → are RE scripts on disk? (exit code)
|
||||
orchestrator's skill list → is the RE skill registered?
|
||||
Phase 2b DISPATCH dispatch a subagent (Agent tool, general-purpose) that runs RE
|
||||
in isolation:
|
||||
├─ RE skill registered → subagent invokes the
|
||||
│ android-reverse-engineering skill
|
||||
└─ skill absent, scripts present → subagent runs direct scripts
|
||||
Subagent writes digest files to $WORK/, returns the summary text.
|
||||
Phase 2c CONSUME orchestrator reads $WORK/re-summary.txt only; pulls
|
||||
re-digest.md / payloads.json on demand in later phases.
|
||||
```
|
||||
|
||||
**Invariant:** both branches write the **same digest files**, so Phases 3–7 are branch-agnostic — one consume path regardless of how RE ran.
|
||||
|
||||
### Branch decision table
|
||||
|
||||
| RE skill registered | RE scripts on disk | Action |
|
||||
|---|---|---|
|
||||
| ✓ | — | subagent runs **RE skill** (preferred) |
|
||||
| ✗ | ✓ | subagent runs **direct scripts** (fallback) |
|
||||
| ✓ | ✓ | subagent runs **RE skill** (skill wins) |
|
||||
| ✗ | ✗ | **stop** — show `resolve-re-scripts.sh` error |
|
||||
|
||||
Two independent facts drive it: skill availability (orchestrator reads its own available-skills list) and script availability (`resolve-re-scripts.sh` exit code). `resolve-re-scripts.sh` is unchanged — it remains both the probe and the fallback locator.
|
||||
|
||||
**Why the subagent wraps the script-fallback branch too** (not only the skill branch): keeps decompile noise out of the orchestrator either way, and keeps Phases 3–7 on a single branch-agnostic consume path.
|
||||
|
||||
## Digest contract (subagent output)
|
||||
|
||||
Single source of truth: a new reference file `references/re-digest-contract.md`. The Phase-2 subagent prompt points at it. The subagent MUST write three files to `$WORK/` and return the summary.
|
||||
|
||||
### `$WORK/re-digest.md` — human-readable main artifact
|
||||
|
||||
```
|
||||
# RE Digest — <pkg>
|
||||
## Framework & Stack framework, HTTP lib, DI, serialization, obfuscation level
|
||||
## Hosts first-party vs third-party (table)
|
||||
## Endpoint Inventory Tier-1: host | method | path | auth | source file
|
||||
## Key Flow Payloads Tier-2: auth, payment/checkout, 1-2 core
|
||||
— request body / response shape / headers / params
|
||||
## BuildConfig Secrets base URLs, API keys, feature flags, flavors
|
||||
## Feature Signals screen count, SDKs, permissions, components
|
||||
## RE Method "re-skill" | "direct-scripts" | "limited: <framework>"
|
||||
```
|
||||
|
||||
### `$WORK/payloads.json` — machine-readable (memory requirement)
|
||||
|
||||
```json
|
||||
{
|
||||
"package": "com.example.app",
|
||||
"re_method": "re-skill",
|
||||
"endpoints": [
|
||||
{
|
||||
"host": "api.example.com",
|
||||
"method": "POST",
|
||||
"path": "/v1/auth/login",
|
||||
"auth": "none",
|
||||
"source": "com/example/api/AuthApi.java",
|
||||
"request_body": { "email": "string", "password": "string" },
|
||||
"response": { "token": "string", "user": {} },
|
||||
"headers": { "Content-Type": "application/json" }
|
||||
}
|
||||
],
|
||||
"buildconfig": { "BASE_URL": "https://api.example.com/v1" }
|
||||
}
|
||||
```
|
||||
|
||||
`request_body` / `response` are `null` for Tier-1-only endpoints. Populated only for auth/payment/core (Tier-2).
|
||||
|
||||
### `$WORK/re-summary.txt` — ≤40 lines, the only RE text entering orchestrator context
|
||||
|
||||
Contains: framework, host count, endpoint count, key-flow names, secrets-found count, RE method, blockers/warnings. The subagent's return value = contents of `re-summary.txt` + the two file paths. **The subagent must not dump raw decompiled sources into its return.**
|
||||
|
||||
### Contract rules
|
||||
|
||||
- **Tier-2 only for auth / payment / core** (decision Q3=B). All other endpoints → Tier-1 row, `null` payloads.
|
||||
- **Framework guard:** if fingerprint reports Flutter / React Native / Cordova / Xamarin, the subagent writes a partial digest, sets `RE Method: limited: <framework>`, and payloads may be empty. Downstream widens the uncertainty band.
|
||||
|
||||
## Subagent dispatch
|
||||
|
||||
clone-app dispatches via the **Agent tool** (`general-purpose` type — can both invoke skills and run bash). The single prompt carries: `$PKG`, `$APK`, `$WORK`, the chosen branch, and the digest contract verbatim (or a pointer to `re-digest-contract.md`). Subagent steps:
|
||||
|
||||
1. Run RE — the skill or the direct scripts, per branch.
|
||||
2. Extract Tier-1 inventory + Tier-2 payloads for auth/payment/core.
|
||||
3. Write `re-digest.md`, `payloads.json`, `re-summary.txt`.
|
||||
4. Return `re-summary.txt` contents + the two file paths. No raw sources.
|
||||
|
||||
## Downstream changes (Phases 3–7)
|
||||
|
||||
- **Phase 2c:** orchestrator reads `re-summary.txt` only; reads `re-digest.md` / `payloads.json` on demand if a later phase needs detail.
|
||||
- **Phase 5 (effort/cost):** read `payloads.json`; endpoint count + payload complexity feed the AI-Sprint estimate (backend surface = real work) instead of a loose guess.
|
||||
- **Phase 6 (report):** new optional report section **"Backend API Surface"** — Tier-1 table summary + key-flow payloads from the digest.
|
||||
- **Error table additions:**
|
||||
- RE subagent fails → retry once, then fall back to direct-scripts branch (if scripts present); else stop.
|
||||
- Both branches unavailable → stop with the resolver error.
|
||||
|
||||
## Files changed (all under `plugins/clone-app/`)
|
||||
|
||||
| File | Change |
|
||||
|---|---|
|
||||
| `skills/clone-app/SKILL.md` | Rewrite Phase 2 (probe/dispatch/consume); tweak Phases 5 & 6; extend error table |
|
||||
| `skills/clone-app/references/re-digest-contract.md` | **New** — digest schema, single source of truth for the subagent |
|
||||
| `skills/clone-app/scripts/resolve-re-scripts.sh` | Unchanged (remains probe + fallback locator) |
|
||||
| `tests/fixtures/re-digest.sample.md` | **New** — schema fixture |
|
||||
| `tests/fixtures/payloads.sample.json` | **New** — schema fixture |
|
||||
| `tests/test-re-digest-contract.sh` | **New** — structural assertions |
|
||||
| `tests/smoke-structure.sh` | Add new reference file to present-files check |
|
||||
|
||||
`plugins/android-reverse-engineering/` is **untouched** — the subagent *invokes* it, never edits it. `git status --porcelain plugins/android-reverse-engineering/` must stay empty.
|
||||
|
||||
## Tests (offline, fixture-based — matches existing pattern)
|
||||
|
||||
- `test-re-digest-contract.sh` asserts:
|
||||
- `re-digest-contract.md` lists every required `re-digest.md` section and every required `payloads.json` key.
|
||||
- `SKILL.md` Phase 2 references the contract, names both branches, and dispatches a subagent.
|
||||
- Sample fixtures parse (JSON valid; digest has all sections).
|
||||
- `smoke-structure.sh` gains the new reference file in its existence check.
|
||||
- **No live RE run in tests** — decompilation is too heavy and non-deterministic. Contract + wiring asserted structurally, same philosophy as the offline Python scraper tests.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Exhaustive Tier-2 payloads for every endpoint (Q3=C rejected — token-expensive, RE skill itself warns against it).
|
||||
- Modifying any upstream RE file.
|
||||
- Live decompilation inside the test suite.
|
||||
|
|
@ -40,35 +40,73 @@ a Cloudflare bot challenge), retries 3×, and prints the path (`app.apk` or
|
|||
format changed — tell the user the download failed and ask for a local APK/XAPK
|
||||
path; set `APK` to that path.
|
||||
|
||||
## Phase 2: Reverse Engineering
|
||||
## Phase 2: Reverse Engineering (probe → dispatch → consume)
|
||||
|
||||
RE runs inside an **isolated subagent** so the decompiled sources never flood
|
||||
this orchestrator's context. The subagent prefers the
|
||||
`android-reverse-engineering` **skill** and falls back to that plugin's bash
|
||||
**scripts**. Either way it writes the same digest files to `$WORK/`, defined
|
||||
in `${CLAUDE_PLUGIN_ROOT}/skills/clone-app/references/re-digest-contract.md`.
|
||||
|
||||
### Phase 2a — Probe
|
||||
|
||||
Resolve the sibling RE scripts:
|
||||
```bash
|
||||
RE="$(bash ${CLAUDE_PLUGIN_ROOT}/skills/clone-app/scripts/resolve-re-scripts.sh)"
|
||||
RE="$(bash ${CLAUDE_PLUGIN_ROOT}/skills/clone-app/scripts/resolve-re-scripts.sh 2>/tmp/re-err)"; RC=$?
|
||||
```
|
||||
If it exits non-zero, show its error (RE plugin not installed) and stop. If it
|
||||
prints a `WARNING:` about bash version, the RE scripts need **bash 4+** (macOS
|
||||
ships 3.2; their `${VAR,,}` syntax fails as "bad substitution" otherwise) —
|
||||
install one with `brew install bash` before continuing, then re-run Phase 2.
|
||||
- `RC == 0` → the RE **scripts** are on disk (fallback is available). If it
|
||||
printed a `WARNING:` about bash version, the scripts need **bash 4+**
|
||||
(macOS ships 3.2; `${VAR,,}` fails as "bad substitution") — install one with
|
||||
`brew install bash` before the script-fallback branch can succeed.
|
||||
- Check your own available-skills list for `android-reverse-engineering` →
|
||||
is the RE **skill** registered?
|
||||
|
||||
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" -o "$WORK/output" "$APK"` (add `--deobf` if fingerprint showed heavy obfuscation: `bash "$RE/decompile.sh" -o "$WORK/output" --deobf "$APK"`).
|
||||
Sources land at `$WORK/output/sources/`.
|
||||
4. If the app is Kotlin: `bash "$RE/recover-kotlin-names.sh" "$WORK/output/sources" "$WORK/output/names/"`.
|
||||
5. `bash "$RE/find-api-calls.sh" "$WORK/output/sources"` (full scan; add `--ktor`/`--apollo`/`--paths` as
|
||||
the fingerprint suggests).
|
||||
Pick the branch:
|
||||
|
||||
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.
|
||||
| RE skill registered | RE scripts on disk (`RC`) | Branch |
|
||||
|---|---|---|
|
||||
| yes | any | **re-skill** |
|
||||
| no | 0 | **direct-scripts** |
|
||||
| no | 1 | **stop** — show the `/tmp/re-err` resolver error and halt |
|
||||
|
||||
### Phase 2b — Dispatch the subagent
|
||||
|
||||
Dispatch one subagent (Agent tool, `general-purpose` type — it can both invoke
|
||||
skills and run bash). Pass it: `$PKG`, `$APK`, `$WORK`, the chosen **branch**,
|
||||
the resolved `$RE` scripts dir, and the path to `re-digest-contract.md`. Its
|
||||
instructions:
|
||||
|
||||
1. **Run RE per branch.**
|
||||
- **re-skill:** invoke the android-reverse-engineering skill on `$APK`,
|
||||
output dir `$WORK/output` — run its full workflow (fingerprint, deps,
|
||||
decompile, Kotlin-name recovery if Kotlin, API extraction incl. Tier-2).
|
||||
- **direct-scripts:** run, in order, reading each output before the next:
|
||||
`bash "$RE/fingerprint.sh" "$APK"`, `bash "$RE/check-deps.sh"`
|
||||
(install required deps via `bash "$RE/install-dep.sh" <dep>`; ask before
|
||||
optional vineflower/dex2jar), `bash "$RE/decompile.sh" -o "$WORK/output" "$APK"`
|
||||
(add `--deobf` if obfuscation is heavy), `bash "$RE/recover-kotlin-names.sh"
|
||||
"$WORK/output/sources" "$WORK/output/names/"` if Kotlin, then
|
||||
`bash "$RE/find-api-calls.sh" "$WORK/output/sources"`.
|
||||
2. **Framework guard:** if the fingerprint is Flutter / React Native / Cordova
|
||||
/ Xamarin, Java decompile is shallow — produce a partial digest, set
|
||||
`RE Method: limited: <framework>`, payloads may be empty.
|
||||
3. **Extract** the Tier-1 endpoint inventory and Tier-2 payloads for **auth,
|
||||
payment/checkout, and the 1–2 core feature endpoints** (not every endpoint).
|
||||
4. **Write** `$WORK/re-digest.md`, `$WORK/payloads.json`, `$WORK/re-summary.txt`
|
||||
exactly per `re-digest-contract.md`.
|
||||
5. **Return** the contents of `$WORK/re-summary.txt` plus the two file paths —
|
||||
**never** raw decompiled sources.
|
||||
|
||||
If the subagent fails, retry once; if it still fails and the **direct-scripts**
|
||||
branch is available (i.e. the Phase 2a probe returned `RC == 0`), re-dispatch on
|
||||
that branch; otherwise stop and report.
|
||||
|
||||
### Phase 2c — Consume
|
||||
|
||||
Read `$WORK/re-summary.txt` (the only RE text in this context). From it you have:
|
||||
framework, HTTP stack, host counts, endpoint count, key-flow names, secrets
|
||||
count, and the RE method. Read `$WORK/re-digest.md` or `$WORK/payloads.json`
|
||||
**on demand** when a later phase needs detail. Keep the summary in context for
|
||||
Phases 3–7.
|
||||
|
||||
## Phase 3: Store Analysis
|
||||
|
||||
|
|
@ -94,7 +132,10 @@ the user to choose. **Wait for the user's choice before Phase 5.** Lock it.
|
|||
|
||||
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),
|
||||
- read `$WORK/payloads.json`; the endpoint count and the payload complexity of
|
||||
the key flows size the backend work,
|
||||
- the feature list + backend surface → AI-Sprint effort table (min-max total,
|
||||
uncertainty band; widen the band when RE Method is `limited:`),
|
||||
- the MVP/Growth/Scale monthly infra cost table.
|
||||
Base both on the **user-selected stack** from Phase 4.
|
||||
|
||||
|
|
@ -104,6 +145,10 @@ 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.
|
||||
Include a **Backend API Surface** section: summarize the Tier-1 inventory from
|
||||
`$WORK/re-digest.md` and the key-flow payloads from `$WORK/payloads.json` (host
|
||||
list, endpoint count, auth model, and the auth/payment/core request+response
|
||||
shapes). If RE Method was `limited:`, say so and note the reduced confidence.
|
||||
|
||||
Write the report:
|
||||
```
|
||||
|
|
@ -124,7 +169,9 @@ implementation plan?"
|
|||
|---|---|
|
||||
| Package not in URL | ask user for package name |
|
||||
| Download fails 3× | ask for local APK path |
|
||||
| RE plugin missing | show resolver error, stop |
|
||||
| RE skill + scripts both missing | show resolver error, stop |
|
||||
| RE subagent fails | retry once, then fall back to direct-scripts branch; else stop |
|
||||
| Subagent returned no digest files | re-dispatch once; if still missing, stop and report |
|
||||
| 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 |
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
# RE Digest Contract
|
||||
|
||||
The Phase 2 subagent runs the reverse-engineering workflow in isolation and
|
||||
MUST produce these three files under `$WORK/`, then return only the summary.
|
||||
This file is the single source of truth for their schema. The subagent prompt
|
||||
points here; do not duplicate the schema into SKILL.md prose.
|
||||
|
||||
## File 1 — `$WORK/re-digest.md` (human-readable, main artifact)
|
||||
|
||||
Required section headings, in this order:
|
||||
|
||||
```
|
||||
# RE Digest — <pkg>
|
||||
## Framework & Stack framework, HTTP lib, DI, serialization, obfuscation level
|
||||
## Hosts first-party vs third-party (table)
|
||||
## Endpoint Inventory Tier-1: host | method | path | auth | source file
|
||||
## Key Flow Payloads Tier-2: auth, payment/checkout, 1-2 core flows
|
||||
— request body / response shape / headers / params
|
||||
## BuildConfig Secrets base URLs, API keys, feature flags, flavors
|
||||
## Feature Signals screen count, SDKs, permissions, components
|
||||
## RE Method re-skill | direct-scripts | limited: <framework>
|
||||
```
|
||||
|
||||
## File 2 — `$WORK/payloads.json` (machine-readable, durable memory)
|
||||
|
||||
Required top-level keys: `package`, `re_method`, `endpoints`, `buildconfig`.
|
||||
Each item in `endpoints` has: `host`, `method`, `path`, `auth`, `source`,
|
||||
`request_body`, `response`, `headers`.
|
||||
|
||||
```json
|
||||
{
|
||||
"package": "com.example.app",
|
||||
"re_method": "re-skill",
|
||||
"endpoints": [
|
||||
{
|
||||
"host": "api.example.com",
|
||||
"method": "POST",
|
||||
"path": "/v1/auth/login",
|
||||
"auth": "none",
|
||||
"source": "com/example/api/AuthApi.java",
|
||||
"request_body": { "email": "string", "password": "string" },
|
||||
"response": { "token": "string", "user": {} },
|
||||
"headers": { "Content-Type": "application/json" }
|
||||
}
|
||||
],
|
||||
"buildconfig": { "BASE_URL": "https://api.example.com/v1" }
|
||||
}
|
||||
```
|
||||
|
||||
- `request_body` and `response` are `null` for Tier-1-only endpoints.
|
||||
- Populate them ONLY for the key flows: **auth, payment/checkout, and the
|
||||
1–2 core feature endpoints** (decision Q3=B). Everything else is a Tier-1
|
||||
row with `null` payloads. Going Tier-2 on every endpoint is a non-goal —
|
||||
it is token-expensive and the RE skill itself warns against it.
|
||||
|
||||
## File 3 — `$WORK/re-summary.txt` (≤40 lines, the only RE text returned)
|
||||
|
||||
Plain text. The subagent's return value = the contents of this file plus the
|
||||
paths to the two files above. Fields:
|
||||
|
||||
- framework
|
||||
- host count (first-party / third-party)
|
||||
- endpoint count
|
||||
- key-flow names found (auth / payment / core)
|
||||
- secrets-found count (BuildConfig)
|
||||
- RE method: `re-skill` | `direct-scripts` | `limited: <framework>`
|
||||
- blockers / warnings (e.g. obfuscation, framework guard)
|
||||
|
||||
The subagent MUST NOT return raw decompiled sources.
|
||||
|
||||
## RE Method values
|
||||
|
||||
| Value | Meaning |
|
||||
|---|---|
|
||||
| `re-skill` | The `android-reverse-engineering` skill ran the workflow. |
|
||||
| `direct-scripts` | The skill was absent; the sibling plugin's bash scripts ran. |
|
||||
| `limited: <framework>` | Flutter / React Native / Cordova / Xamarin — Java decompile is shallow; payloads may be empty and the digest is partial. |
|
||||
|
||||
## Framework guard
|
||||
|
||||
If the fingerprint reports Flutter / React Native / Cordova / Xamarin, set
|
||||
`RE Method: limited: <framework>`, write whatever partial signals are
|
||||
available (manifest, strings, hardcoded URLs, SDK list), and leave payloads
|
||||
empty where they cannot be recovered. Downstream phases widen the
|
||||
uncertainty band accordingly.
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"package": "com.example.app",
|
||||
"re_method": "re-skill",
|
||||
"endpoints": [
|
||||
{
|
||||
"host": "api.example.com",
|
||||
"method": "POST",
|
||||
"path": "/v1/auth/login",
|
||||
"auth": "none",
|
||||
"source": "com/example/api/AuthApi.java",
|
||||
"request_body": { "email": "string", "password": "string" },
|
||||
"response": { "token": "string", "user": {} },
|
||||
"headers": { "Content-Type": "application/json" }
|
||||
},
|
||||
{
|
||||
"host": "api.example.com",
|
||||
"method": "GET",
|
||||
"path": "/v1/users/profile",
|
||||
"auth": "Bearer",
|
||||
"source": "com/example/api/UserApi.java",
|
||||
"request_body": null,
|
||||
"response": null,
|
||||
"headers": { "Authorization": "Bearer <token>" }
|
||||
}
|
||||
],
|
||||
"buildconfig": { "BASE_URL": "https://api.example.com/v1" }
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
# RE Digest — com.example.app
|
||||
|
||||
## Framework & Stack
|
||||
Native Kotlin · Retrofit + OkHttp · Hilt · kotlinx.serialization · obfuscation: moderate
|
||||
|
||||
## Hosts
|
||||
| Host | Party |
|
||||
|------|-------|
|
||||
| api.example.com | first |
|
||||
| analytics.thirdparty.io | third |
|
||||
|
||||
## Endpoint Inventory
|
||||
| Host | Method | Path | Auth | Source |
|
||||
|------|--------|------|------|--------|
|
||||
| api.example.com | POST | /v1/auth/login | none | com/example/api/AuthApi.java |
|
||||
| api.example.com | GET | /v1/users/profile | Bearer | com/example/api/UserApi.java |
|
||||
| api.example.com | POST | /v1/orders | Bearer | com/example/api/OrderApi.java |
|
||||
|
||||
## Key Flow Payloads
|
||||
### POST /v1/auth/login (auth)
|
||||
- request body: `{ "email": "string", "password": "string" }`
|
||||
- response: `{ "token": "string", "user": {} }`
|
||||
- headers: `Content-Type: application/json`
|
||||
|
||||
### POST /v1/orders (payment/core)
|
||||
- request body: `{ "items": [], "total": "number" }`
|
||||
- response: `{ "order_id": "string", "status": "string" }`
|
||||
- headers: `Authorization: Bearer <token>`
|
||||
|
||||
## BuildConfig Secrets
|
||||
- BASE_URL = https://api.example.com/v1
|
||||
- ANALYTICS_KEY = <redacted-present>
|
||||
|
||||
## Feature Signals
|
||||
~18 screens · SDKs: Firebase, AppsFlyer · permissions: INTERNET, ACCESS_NETWORK_STATE
|
||||
|
||||
## RE Method
|
||||
re-skill
|
||||
|
|
@ -16,7 +16,7 @@ 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
|
||||
for r in stack-recommendation-guide effort-estimation-guide infra-cost-guide report-template re-digest-contract; do
|
||||
must_exist "$P/skills/clone-app/references/$r.md"
|
||||
done
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
#!/usr/bin/env bash
|
||||
set -uo pipefail
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
P="$HERE/.." # plugins/clone-app
|
||||
CONTRACT="$P/skills/clone-app/references/re-digest-contract.md"
|
||||
DIGEST_FIX="$HERE/fixtures/re-digest.sample.md"
|
||||
PAYLOAD_FIX="$HERE/fixtures/payloads.sample.json"
|
||||
fail=0
|
||||
has() { grep -qF "$2" "$1" && echo "PASS: $3" || { echo "FAIL: $3 — '$2' not in ${1##*/}"; fail=1; }; }
|
||||
|
||||
# Contract doc exists and documents every required re-digest.md section heading
|
||||
for sec in "## Framework & Stack" "## Hosts" "## Endpoint Inventory" \
|
||||
"## Key Flow Payloads" "## BuildConfig Secrets" "## Feature Signals" "## RE Method"; do
|
||||
has "$CONTRACT" "$sec" "contract documents section $sec"
|
||||
done
|
||||
|
||||
# Contract doc documents every required payloads.json key
|
||||
for key in '"package"' '"re_method"' '"endpoints"' '"buildconfig"' \
|
||||
'"request_body"' '"response"' '"headers"'; do
|
||||
has "$CONTRACT" "$key" "contract documents json key $key"
|
||||
done
|
||||
|
||||
# Contract names the three output files and the three RE Method values
|
||||
for tok in "re-digest.md" "payloads.json" "re-summary.txt" \
|
||||
"re-skill" "direct-scripts" "limited:"; do
|
||||
has "$CONTRACT" "$tok" "contract names token $tok"
|
||||
done
|
||||
|
||||
# Digest fixture has every required section heading
|
||||
for sec in "## Framework & Stack" "## Hosts" "## Endpoint Inventory" \
|
||||
"## Key Flow Payloads" "## BuildConfig Secrets" "## Feature Signals" "## RE Method"; do
|
||||
has "$DIGEST_FIX" "$sec" "digest fixture has section $sec"
|
||||
done
|
||||
|
||||
# Payload fixture is valid JSON and has the required shape
|
||||
python3 -c "
|
||||
import json,sys
|
||||
d=json.load(open('$PAYLOAD_FIX'))
|
||||
for k in ('package','re_method','endpoints','buildconfig'):
|
||||
assert k in d, 'missing top key '+k
|
||||
assert isinstance(d['endpoints'],list) and d['endpoints'], 'endpoints must be non-empty list'
|
||||
for e in d['endpoints']:
|
||||
for k in ('host','method','path','auth','source','request_body','response','headers'):
|
||||
assert k in e, 'endpoint missing key '+k
|
||||
print('PASS: payload fixture shape valid')
|
||||
" || { echo "FAIL: payload fixture shape"; fail=1; }
|
||||
|
||||
# --- SKILL.md Phase 2 wiring (Task 2) ---
|
||||
SKILL="$P/skills/clone-app/SKILL.md"
|
||||
hasS() { grep -qF "$1" "$SKILL" && echo "PASS: SKILL $2" || { echo "FAIL: SKILL $2 — '$1' missing"; fail=1; }; }
|
||||
hasReS() { grep -qE "$1" "$SKILL" && echo "PASS: SKILL $2" || { echo "FAIL: SKILL $2 — /$1/ missing"; fail=1; }; }
|
||||
|
||||
hasS "re-digest-contract.md" "Phase 2 points at the digest contract"
|
||||
hasReS "subagent|Agent tool|dispatch" "Phase 2 dispatches a subagent"
|
||||
hasS "android-reverse-engineering skill" "Phase 2 names the RE skill branch"
|
||||
hasS "direct-scripts" "Phase 2 names the script-fallback branch"
|
||||
hasS "re-summary.txt" "Phase 2c consumes the summary"
|
||||
hasS "payloads.json" "Phase 5 consumes payloads.json"
|
||||
hasS "Backend API Surface" "Phase 6 adds the Backend API Surface section"
|
||||
hasS "RE subagent" "error table covers subagent failure"
|
||||
|
||||
exit $fail
|
||||
Loading…
Reference in New Issue