appstore-precheck 1.1.1 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +118 -0
- package/README.md +216 -52
- package/package.json +2 -2
- package/skills/appstore-precheck/SKILL.md +200 -81
- package/skills/appstore-precheck/evals/README.md +3 -2
- package/skills/appstore-precheck/evals/evals.json +6 -3
- package/skills/appstore-precheck/guidelines-baseline.json +19 -7
- package/skills/appstore-precheck/references/methodology.md +79 -7
- package/skills/appstore-precheck/references/pierre-deep-review.md +272 -0
- package/skills/appstore-precheck/scripts/scan.sh +319 -10
- package/skills/appstore-precheck/scripts/verdict.sh +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,124 @@
|
|
|
3
3
|
All notable changes to this project are documented here. Versioning follows
|
|
4
4
|
[SemVer](https://semver.org/). Released as git tags.
|
|
5
5
|
|
|
6
|
+
## [1.5.1] - 2026-06-30
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
|
|
10
|
+
- Plugin manifest descriptions no longer embed drifting check counts
|
|
11
|
+
- README uninstall: Codex uses /plugins UI
|
|
12
|
+
- cross-tool-verification.md plugin install paths documented
|
|
13
|
+
- GitHub release v1.5.0 notes amended; npm 1.5.1 published
|
|
14
|
+
|
|
15
|
+
## [1.5.0] - 2026-06-30
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- **Tier B v1 — 6 heuristic Pierre deep-review checks** (28 total, up from 22). New Phase 4 items
|
|
19
|
+
in guideline order: **2.1** review notes / demo account quality, **2.2** beta/test language,
|
|
20
|
+
**2.3.4** app preview consistency, **2.3.9** incentivized review copy, **4.5.1–4.5.3** push /
|
|
21
|
+
HomeKit abuse patterns, **5.6.4–5.6.7** rating manipulation dark patterns. Marked † in docs;
|
|
22
|
+
higher false-positive risk — advisory `REVIEW-FINDING: WARN` only.
|
|
23
|
+
- Expanded `covered_by_pierre_deep_review` in `guidelines-baseline.json` with 2.2, 2.3.4, 2.3.9,
|
|
24
|
+
4.5.1–4.5.3, 5.6.4–5.6.7.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- Phase 4 checklist 22 → **28** across `pierre-deep-review.md`, `SKILL.md`, `methodology.md`, README
|
|
28
|
+
(full guideline-ordered table with † Tier B labels), plugin manifest, and social preview
|
|
29
|
+
(`41 static + 28 Pierre deep checks`).
|
|
30
|
+
|
|
31
|
+
## [1.4.0] - 2026-06-30
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
- **Phase 4: Pierre deep review — 22 semantic checks.** After Phase 3 (explaining every scan
|
|
35
|
+
FAIL/WARN), Pierre runs a read-only Review Simulator: fetches privacy/support URLs, reads
|
|
36
|
+
screenshots, compares metadata claims to Swift code, validates paywall disclosure quality, and
|
|
37
|
+
cross-checks permissions vs policy. Each check emits `REVIEW-PASS:` or advisory
|
|
38
|
+
`REVIEW-FINDING: … WARN` (does not change GREEN/YELLOW/RED counts). Full checklist in
|
|
39
|
+
[`references/pierre-deep-review.md`](skills/appstore-precheck/references/pierre-deep-review.md).
|
|
40
|
+
- **`covered_by_pierre_deep_review`** in `guidelines-baseline.json` (26 guideline numbers across
|
|
41
|
+
the 22 checks). Consolidation moves to **Phase 5** (6 phases total: 0–5).
|
|
42
|
+
- [`examples/pierre-deep-review.md`](examples/pierre-deep-review.md) showing `REVIEW-FINDING` output.
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
- Flow is now **6 phases (0–5)**: Phase 3 = scan commentary, Phase 4 = deep review, Phase 5 =
|
|
46
|
+
verdict + token. Updated `SKILL.md`, `methodology.md`, README (static table + deep-review table
|
|
47
|
+
in guideline order), examples, plugin manifest, and social preview copy (`41 static + 22 Pierre
|
|
48
|
+
deep checks`).
|
|
49
|
+
|
|
50
|
+
## [1.3.1] - 2026-06-30
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
- **Phase 3 (Pierre) now explains every FAIL and WARN** from Phases 0–2 in **2–3 sentences
|
|
54
|
+
each** — no more random 5-guideline sampling. Pierre repeats each machine line verbatim,
|
|
55
|
+
then explains why Apple cares, what the scan found, and what to fix. Updated `SKILL.md`,
|
|
56
|
+
`methodology.md`, the README example, `examples/red-reject.md`, and behavioral eval
|
|
57
|
+
assertions in lockstep.
|
|
58
|
+
- Phase 4 presentation order clarified: trilingual verdict block → Pierre commentary → verbatim
|
|
59
|
+
scan lines + `file:line` fixes → verdict/token.
|
|
60
|
+
- **Trilingual verdict block format:** each language in its own **bold label + blockquote**, separated
|
|
61
|
+
by horizontal rules (`---`) under a `### Pierre` heading — never FR/EN/user-lang compressed on one line.
|
|
62
|
+
|
|
63
|
+
## [1.3.0] - 2026-06-30
|
|
64
|
+
|
|
65
|
+
### Added
|
|
66
|
+
- **Eleven new signal-gated advisory checks (30 → 41 rejection vectors), all WARN-only:**
|
|
67
|
+
- **2.1** a login-gated app with no demo account / credentials for App Review (fastlane
|
|
68
|
+
`review_information` or `.reviewPrepNotes`).
|
|
69
|
+
- **2.5.2** executable-code download / native hot-patching (JSPatch, Rollout, DynamicCocoa).
|
|
70
|
+
Allowed JS-bundle OTA (React Native CodePush) is deliberately **not** flagged.
|
|
71
|
+
- **2.5.4** a background mode declared in `UIBackgroundModes` with no matching API used in Swift.
|
|
72
|
+
- **3.1.5(a)** a cryptocurrency wallet / exchange / mining signal.
|
|
73
|
+
- **4.2.3** a thin WKWebView wrapper around a website (heuristic: WKWebView + very few Swift files).
|
|
74
|
+
- **4.2.7** a remote-desktop / host-mirroring signal.
|
|
75
|
+
- **4.4.2** a Safari content-blocker / web extension.
|
|
76
|
+
- **5.1.1(v)** account creation offered without an in-app account-deletion path (the real
|
|
77
|
+
5.1.1(v) Account Sign-In rule).
|
|
78
|
+
- **5.1.4** metadata targeting a child audience while a third-party ads/analytics SDK is linked.
|
|
79
|
+
- **5.3.4** real-money gambling language in metadata.
|
|
80
|
+
- **5.5** a Mobile Device Management (MDM) signal.
|
|
81
|
+
- `tests/fixtures/risky-app-2` (advisory §31–§41 except web-wrapper) and `tests/fixtures/webview-app`
|
|
82
|
+
(the 4.2.3 heuristic), with assertions in `tests/run.sh`. Both are advisory-only (no FAIL).
|
|
83
|
+
|
|
84
|
+
### Changed
|
|
85
|
+
- **Corrected the Required Reason API label from `5.1.1(v)` to `5.1.1`.** Apple documents the
|
|
86
|
+
Required Reason API rules under 5.1.1 + the privacy-manifest developer docs; sub-item **(v)** is
|
|
87
|
+
"Account Sign-In", a different rule. The `(v)` label now belongs to the new account-deletion
|
|
88
|
+
check (vector 38). Updated `scan.sh` output, the methodology table, the README table, the
|
|
89
|
+
examples, and the field-test notes in lockstep.
|
|
90
|
+
- §18 (support / privacy URL) now also cites the guidelines it satisfies: **1.5** (developer
|
|
91
|
+
contact via the support URL) and **5.1.1(i)** (privacy policy link).
|
|
92
|
+
- Expanded `guidelines-baseline.json` `covered_by_scan` with 1.5, 2.5.2, 2.5.4, 3.1.5, 4.2.3,
|
|
93
|
+
4.2.7, 4.4.2, 5.1.4, 5.3.4, and 5.5.
|
|
94
|
+
|
|
95
|
+
## [1.2.0] - 2026-06-30
|
|
96
|
+
|
|
97
|
+
### Added
|
|
98
|
+
- **Ten new signal-gated advisory checks (20 → 30 rejection vectors), all WARN-only:**
|
|
99
|
+
- **3.1.1** third-party payment SDK (Stripe, Braintree, PayPal, Square, Adyen, …) linked for
|
|
100
|
+
digital goods instead of in-app purchase.
|
|
101
|
+
- **1.2** user-generated content detected without a report / block / moderation affordance.
|
|
102
|
+
- **1.6** App Transport Security disabled app-wide (`NSAllowsArbitraryLoads=true`).
|
|
103
|
+
- **4.9** recurring Apple Pay without the renewal / cancel disclosure.
|
|
104
|
+
- **5.6.1** a direct App Store write-review link/prompt without the system `requestReview` API.
|
|
105
|
+
- **2.3.1** misleading marketing claims (iOS virus / malware scanners, fake speed boosters) in metadata.
|
|
106
|
+
- **2.3.8** "For Kids" / "For Children" wording outside the Kids Category.
|
|
107
|
+
- **4.4.1** keyboard extension requiring full access (`RequestsOpenAccess=true`).
|
|
108
|
+
- **5.1.3** HealthKit used together with an iCloud / CloudKit sync path.
|
|
109
|
+
- **5.4** VPN / NetworkExtension (`NEVPNManager`) usage.
|
|
110
|
+
- `tests/fixtures/risky-app` plus assertions in `tests/run.sh` exercising all ten new vectors
|
|
111
|
+
(advisory only — the fixture is YELLOW, never RED).
|
|
112
|
+
|
|
113
|
+
### Changed
|
|
114
|
+
- **YELLOW threshold raised from 3+ to 5+ WARN.** The ten new advisory checks are signal-gated
|
|
115
|
+
(most apps trip only one or two), but the bump keeps a normal submission from sliding into
|
|
116
|
+
YELLOW on advisory noise alone. GREEN is now 0 FAIL and ≤4 WARN; YELLOW is 0 FAIL and ≥5 WARN.
|
|
117
|
+
Updated `verdict.sh`, the verdict tests, the output-contract tables, and the docs in lockstep.
|
|
118
|
+
- Corrected the minimum-functionality check's label from `4.0` to `4.2` (its real guideline
|
|
119
|
+
number) in the scanner output, the methodology table, and the README.
|
|
120
|
+
- Reconciled `guidelines-baseline.json` against the live guidelines (Last Updated June 8, 2026):
|
|
121
|
+
no structural section drift; expanded `covered_by_scan` to reflect every guideline the scan now
|
|
122
|
+
touches (adds 1.2, 1.6, 2.1, 2.3, 2.3.8, 3.1.1, 4.4.1, 4.8, 4.9, 5.1.3, 5.4, 5.6.1; 4.0 → 4.2).
|
|
123
|
+
|
|
6
124
|
## [1.1.1] - 2026-06-30
|
|
7
125
|
|
|
8
126
|
### Changed
|
package/README.md
CHANGED
|
@@ -16,8 +16,10 @@
|
|
|
16
16
|
|
|
17
17
|
`appstore-precheck` is a read-only, pre-submission gate for iOS apps. It statically scans the most
|
|
18
18
|
common rejection vectors, runs Apple's own metadata linter, watches the App Store Review Guidelines
|
|
19
|
-
for drift,
|
|
20
|
-
|
|
19
|
+
for drift, has Pierre explain every FAIL and WARN, then runs **28 semantic deep-review checks**
|
|
20
|
+
(22 high-confidence Tier A + 6 heuristic Tier B v1 — beta language, review notes quality, app preview,
|
|
21
|
+
incentivized review, push/HomeKit abuse, rating manipulation).
|
|
22
|
+
It hands you a single **GREEN / YELLOW / RED** verdict. It never edits your code.
|
|
21
23
|
|
|
22
24
|
It ships as a portable [Agent Skill](https://agentskills.io): the same `SKILL.md` runs natively in
|
|
23
25
|
Claude Code, OpenAI Codex, Cursor, and Gemini CLI. The scanner is plain Bash, so you can also run it
|
|
@@ -28,43 +30,113 @@ by hand or wire it into CI.
|
|
|
28
30
|
<img src="assets/mascot.png" align="right" width="124" alt="Pierre, the French app reviewer">
|
|
29
31
|
|
|
30
32
|
Your verdict is delivered by **Pierre**, a French critic who has seen ten thousand rejections and is
|
|
31
|
-
impressed by none of them. He reviews your build harder than Apple would, in private
|
|
33
|
+
impressed by none of them. He reviews your build harder than Apple would, in private — first with a
|
|
34
|
+
fast static scan, then with **28 deep semantic checks** (22 confident + 6 heuristic). A GREEN from
|
|
32
35
|
Pierre means Apple will wave you through.
|
|
33
36
|
|
|
34
37
|
- 🔴 **RED**: *"Non. Restore Purchases, absent. Guideline 3.1.2. Suivant."*
|
|
35
38
|
- 🟡 **YELLOW**: *"A few small uglinesses. I would not reject. But I noticed."*
|
|
36
39
|
- 🟢 **GREEN**: *"Hmf. I find nothing. Acceptable. Do not make me regret this."*
|
|
37
40
|
|
|
38
|
-
The verdict line is in Pierre's voice
|
|
39
|
-
|
|
41
|
+
The verdict line is in Pierre's voice — **three separate language blocks** (bold label + blockquote
|
|
42
|
+
each, divided by horizontal rules), not one compressed sentence. The breakdown beneath it, every
|
|
43
|
+
`file:line` and fix, stays plain and surgical.
|
|
40
44
|
|
|
41
45
|
## What it checks
|
|
42
46
|
|
|
43
|
-
|
|
47
|
+
41 rejection vectors across code, fastlane metadata, screenshots, `PrivacyInfo.xcprivacy`, and the paywall:
|
|
44
48
|
|
|
45
49
|
| Guideline | Check |
|
|
46
50
|
|-----------|-------|
|
|
47
|
-
| **
|
|
48
|
-
| **
|
|
49
|
-
| **
|
|
50
|
-
| **2.
|
|
51
|
+
| **1.2** | User-generated content without a report / block / moderation mechanism |
|
|
52
|
+
| **1.6** | App Transport Security disabled app-wide (`NSAllowsArbitraryLoads`) |
|
|
53
|
+
| **2.1** | No placeholder / dummy copy (lorem ipsum, TODO, `example.com`) in store metadata |
|
|
54
|
+
| **2.1** | A login-gated app ships demo credentials / review notes for App Review |
|
|
55
|
+
| **2.3** | A working support URL and a privacy URL in fastlane metadata (no placeholders) — also satisfies 1.5 and 5.1.1(i) |
|
|
51
56
|
| **2.3.1** | Metadata length limits (name, subtitle, keywords, promo, description) |
|
|
52
|
-
| **2.3.
|
|
57
|
+
| **2.3.1** | Misleading marketing claims (iOS virus / malware scanners, fake speed boosters) in metadata |
|
|
53
58
|
| **2.3.3** | At least one screenshot per locale |
|
|
59
|
+
| **2.3.7** | Localized metadata parity across every locale |
|
|
60
|
+
| **2.3.8** | "For Kids" / "For Children" wording outside the Kids Category |
|
|
61
|
+
| **2.3.10** | No other-platform / competitor names in metadata |
|
|
62
|
+
| **2.5.1** | No private / banned APIs |
|
|
63
|
+
| **2.5.2** | No executable-code download / native hot-patching (JSPatch, Rollout, …) |
|
|
64
|
+
| **2.5.4** | Background modes declared in `UIBackgroundModes` but never used |
|
|
65
|
+
| **3.1.1** | Third-party payment SDK (Stripe, Braintree, PayPal, …) linked for digital goods instead of in-app purchase |
|
|
66
|
+
| **3.1.1(a)** | External purchase link entitlement + disclosure, when external purchase APIs are used |
|
|
54
67
|
| **3.1.2** | Trial & auto-renew subscription disclosures |
|
|
55
68
|
| **3.1.2** | Restore Purchases + Terms (EULA) + Privacy Policy on the paywall |
|
|
56
|
-
| **
|
|
57
|
-
| **4.
|
|
69
|
+
| **3.1.5(a)** | Cryptocurrency wallet / exchange / mining signal |
|
|
70
|
+
| **4.2** | Minimum functionality (real navigation) |
|
|
71
|
+
| **4.2.3** | Thin WKWebView wrapper around a website |
|
|
72
|
+
| **4.2.7** | Remote-desktop / host-mirroring app |
|
|
73
|
+
| **4.4.1** | Keyboard extension that requires full access (`RequestsOpenAccess`) |
|
|
74
|
+
| **4.4.2** | Safari content-blocker / web extension |
|
|
58
75
|
| **4.8** | Sign in with Apple offered when a third-party social login is used |
|
|
59
|
-
| **
|
|
60
|
-
| **5.1.
|
|
76
|
+
| **4.9** | Recurring Apple Pay (`PKRecurringPaymentRequest`) — verify the renewal / cancel disclosure |
|
|
77
|
+
| **5.1.1** | A non-empty purpose string for every sensitive framework |
|
|
78
|
+
| **5.1.1** | Analytics SDK present ↔ `PrivacyInfo.xcprivacy` declares collected data / tracking domains |
|
|
79
|
+
| **5.1.1** | Privacy Manifest ↔ Required Reason API parity |
|
|
80
|
+
| **5.1.1(v)** | Account creation offered without an in-app account-deletion path |
|
|
81
|
+
| **5.1.2** | ATT usage ↔ `NSUserTrackingUsageDescription` |
|
|
61
82
|
| **5.1.2** | Tracking / IDFA SDK (AdMob, AppLovin, AppsFlyer, Adjust, …) shipped without an ATT prompt |
|
|
83
|
+
| **5.1.3** | HealthKit data with an iCloud / CloudKit sync path |
|
|
84
|
+
| **5.1.4** | Kids-audience metadata shipping a third-party ads / analytics SDK |
|
|
85
|
+
| **5.1.5** | Sensitive-API justification *(opt-in)* |
|
|
86
|
+
| **5.3.4** | Real-money gambling language in metadata |
|
|
87
|
+
| **5.4** | VPN / NetworkExtension usage (org account + on-screen data disclosure) |
|
|
88
|
+
| **5.5** | Mobile Device Management (MDM) signal |
|
|
89
|
+
| **5.6.1** | A custom App Store review prompt instead of the system `requestReview` API |
|
|
62
90
|
| **encryption** | `ITSAppUsesNonExemptEncryption` set, so App Store Connect skips the export-compliance question |
|
|
63
|
-
| **2.3** | A working support URL and a privacy URL in fastlane metadata (no placeholders) |
|
|
64
|
-
| **5.1.1** | Analytics SDK present ↔ `PrivacyInfo.xcprivacy` declares collected data / tracking domains |
|
|
65
|
-
| **2.1** | No placeholder / dummy copy (lorem ipsum, TODO, `example.com`) in store metadata |
|
|
66
91
|
|
|
67
|
-
Paywall checks are skipped automatically when no in-app-purchase signals are present
|
|
92
|
+
Paywall checks are skipped automatically when no in-app-purchase signals are present, and the
|
|
93
|
+
signal-gated advisory checks stay silent unless their triggering signal is found.
|
|
94
|
+
|
|
95
|
+
### Pierre deep review (28 semantic checks)
|
|
96
|
+
|
|
97
|
+
After the static scan, Pierre reads your project end-to-end and runs **28 evidence-based checks**
|
|
98
|
+
the grep layer cannot fully judge. These emit advisory `REVIEW-FINDING:` lines (they do **not**
|
|
99
|
+
change the GREEN/YELLOW/RED verdict). Full procedure:
|
|
100
|
+
[`references/pierre-deep-review.md`](skills/appstore-precheck/references/pierre-deep-review.md).
|
|
101
|
+
|
|
102
|
+
**22 checks (Tier A)** are high-confidence cross-reads (privacy policy fetch, claims vs code,
|
|
103
|
+
screenshots, paywall copy). **6 checks (Tier B v1, marked †)** are heuristic — useful pre-submit
|
|
104
|
+
signals with a higher false-positive rate; Pierre prefers `not applicable` when no signal is present.
|
|
105
|
+
|
|
106
|
+
| Guideline | Deep check |
|
|
107
|
+
|-----------|------------|
|
|
108
|
+
| **1.2.1** | User-generated content → is there a real report / block / moderation UI flow? |
|
|
109
|
+
| **1.4.1** | Health or medical claims in metadata/UI without appropriate disclaimers? |
|
|
110
|
+
| **2.1** | Store metadata claims match features actually implemented in code |
|
|
111
|
+
| **2.1** † | App Review demo account / review notes actionable (credentials, steps — not placeholder) |
|
|
112
|
+
| **2.2** † | Beta, test, preview, or work-in-progress language in store-facing copy or UI |
|
|
113
|
+
| **2.3.2** | Primary App Store category fits the app type |
|
|
114
|
+
| **2.3.4** † | App preview video/assets (if in-repo) match shipped features and metadata |
|
|
115
|
+
| **2.3.5** | Screenshot images match shipped features (no misleading frames or missing UI) |
|
|
116
|
+
| **2.3.6** | Metadata pricing / subscription language matches the paywall |
|
|
117
|
+
| **2.3.9** † | Incentivized review copy ("rate 5 stars", "review for reward") in metadata or UI |
|
|
118
|
+
| **2.3.11–2.3.13** | Cross-locale metadata materially consistent (trial terms, features, URLs) |
|
|
119
|
+
| **3.1.1** | Digital goods unlocked via external purchase links (web checkout in WebView, etc.) |
|
|
120
|
+
| **3.1.2** | Trial, auto-renew, and cancel disclosures are legible sentences — not keyword stubs |
|
|
121
|
+
| **4.2.1–4.2.2** | Minimum functionality beyond a thin WebView shell or template app |
|
|
122
|
+
| **4.5.1–4.5.3** † | Push notification or HomeKit entitlement used as intended (no spam-push / HomeKit without home UI) |
|
|
123
|
+
| **4.8** | Third-party login → Sign in with Apple offered, or a valid exempt case |
|
|
124
|
+
| **5.1.1(i)** | Privacy policy text (fetched live) matches code, PrivacyInfo, and SDK usage |
|
|
125
|
+
| **5.1.1(ii)** | Purpose strings are specific and tied to a visible feature |
|
|
126
|
+
| **5.1.1(iii)** | Permissions and SDKs proportionate to the app's stated purpose |
|
|
127
|
+
| **5.1.1(iv)** | Permission denial handled without forced re-prompt loops |
|
|
128
|
+
| **5.1.2** | ATT prompt, tracking description, privacy policy, and ad SDK usage align |
|
|
129
|
+
| **5.1.3** | HealthKit data not used for advertising or marketing |
|
|
130
|
+
| **5.1.4** | Kids-audience signals → parental gate before external links / IAP / account areas |
|
|
131
|
+
| **5.4** | VPN / NetworkExtension → on-screen disclosure copy visible in UI strings |
|
|
132
|
+
| **5.2.1–5.2.3** | Obvious third-party trademark or brand misuse in metadata or UI copy |
|
|
133
|
+
| **5.3.1–5.3.3** | Contest / sweepstakes copy includes official rules and eligibility |
|
|
134
|
+
| **5.6.2–5.6.3** | Developer identity consistent (app name, support URL content, domains) |
|
|
135
|
+
| **5.6.4–5.6.7** † | Rating / review manipulation dark patterns (withhold features until 5 stars, write-review links without `requestReview`) |
|
|
136
|
+
|
|
137
|
+
Pierre runs **all 28 every time** and reports each as `REVIEW-PASS:` or `REVIEW-FINDING:`. When the
|
|
138
|
+
static scan already flagged a guideline, the deep check adds semantic context the scanner could not see.
|
|
139
|
+
† Tier B v1 items are heuristic — treat findings as "verify before submit", not automatic blockers.
|
|
68
140
|
|
|
69
141
|
### Supported app types
|
|
70
142
|
|
|
@@ -74,46 +146,109 @@ how the app is built:
|
|
|
74
146
|
|
|
75
147
|
| App type | Coverage |
|
|
76
148
|
|----------|----------|
|
|
77
|
-
| 🟢 **Native Swift / SwiftUI** | **Full.** All
|
|
149
|
+
| 🟢 **Native Swift / SwiftUI** | **Full.** All 41 vectors apply. |
|
|
78
150
|
| 🟡 **React Native / Flutter** | Metadata, privacy manifest, screenshots, and export compliance apply in full. The Swift-source checks (ATT, paywall links, private API, SDK detection, navigation) **under-detect rather than misfire**: that logic lives in JS/Dart, so they stay quiet instead of blocking. |
|
|
79
151
|
|
|
80
152
|
## Quick start
|
|
81
153
|
|
|
82
|
-
**Claude Code
|
|
154
|
+
One `SKILL.md`, every host. **Claude Code, Cursor, and Codex** install as native **plugins** from
|
|
155
|
+
this GitHub repo. **Gemini CLI** installs the same skill with `gemini skills install` (no plugin
|
|
156
|
+
marketplace yet). [`install.sh`](install.sh) remains the fallback for vendoring into a project.
|
|
157
|
+
|
|
158
|
+
### Install the full skill (Pierre + all phases)
|
|
159
|
+
|
|
160
|
+
| Host | Install method |
|
|
161
|
+
|------|----------------|
|
|
162
|
+
| **Claude Code** | Plugin (recommended) |
|
|
163
|
+
| **Cursor** | Plugin (recommended) |
|
|
164
|
+
| **OpenAI Codex** | Plugin (recommended) |
|
|
165
|
+
| **Gemini CLI** | `gemini skills install` one-liner |
|
|
166
|
+
|
|
167
|
+
**Claude Code:**
|
|
83
168
|
|
|
84
169
|
```
|
|
85
170
|
/plugin marketplace add berkayturk/appstore-precheck
|
|
86
171
|
/plugin install appstore-precheck@appstore-precheck
|
|
87
172
|
```
|
|
88
173
|
|
|
89
|
-
**
|
|
174
|
+
**Cursor** — import this repo as a marketplace, then install the plugin:
|
|
175
|
+
|
|
176
|
+
1. **Customize → Plugins → Import marketplace**
|
|
177
|
+
2. Repo URL: `https://github.com/berkayturk/appstore-precheck`
|
|
178
|
+
3. Install **appstore-precheck**
|
|
179
|
+
|
|
180
|
+
After the plugin is listed on the [Cursor Marketplace](https://cursor.com/marketplace), you can
|
|
181
|
+
install from **Customize → Plugins** without importing the repo.
|
|
182
|
+
|
|
183
|
+
**OpenAI Codex:**
|
|
90
184
|
|
|
91
185
|
```bash
|
|
92
|
-
|
|
93
|
-
|
|
186
|
+
codex plugin marketplace add berkayturk/appstore-precheck
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Then run `codex`, open `/plugins`, select the **appstore-precheck** marketplace tab, and install
|
|
190
|
+
**appstore-precheck**. Start a new thread after install.
|
|
191
|
+
|
|
192
|
+
On newer Codex builds you may also be able to run:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
codex plugin add appstore-precheck@appstore-precheck
|
|
94
196
|
```
|
|
95
197
|
|
|
96
|
-
|
|
97
|
-
|
|
198
|
+
Refresh after repo updates:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
codex plugin marketplace upgrade appstore-precheck
|
|
202
|
+
```
|
|
98
203
|
|
|
99
|
-
**
|
|
204
|
+
**Gemini CLI** (native skill install — no plugin marketplace):
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
gemini skills install https://github.com/berkayturk/appstore-precheck.git \
|
|
208
|
+
--path skills/appstore-precheck --scope workspace
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Use `--scope user` for a global install. Verify with `gemini skills list`.
|
|
212
|
+
|
|
213
|
+
**Fallback — `install.sh`** (vendors the skill into `.claude/skills/` and/or `.agents/skills/`):
|
|
100
214
|
|
|
101
215
|
```bash
|
|
102
216
|
git clone https://github.com/berkayturk/appstore-precheck.git
|
|
103
|
-
|
|
217
|
+
cd your-ios-app
|
|
218
|
+
/path/to/appstore-precheck/install.sh # all hosts → .claude/skills + .agents/skills
|
|
219
|
+
/path/to/appstore-precheck/install.sh cursor # Cursor only
|
|
220
|
+
/path/to/appstore-precheck/install.sh codex # Codex only
|
|
221
|
+
/path/to/appstore-precheck/install.sh gemini # Gemini only
|
|
222
|
+
/path/to/appstore-precheck/install.sh claude user # Claude Code user-wide → ~/.claude/skills
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Then ask your agent to run the precheck before you submit. See [Cross-tool support](#cross-tool-support)
|
|
226
|
+
for which directory each host reads. Maintainer notes for marketplace submission:
|
|
227
|
+
[`docs/publishing-plugins.md`](docs/publishing-plugins.md).
|
|
228
|
+
|
|
229
|
+
### Scan only (no agent skill install)
|
|
230
|
+
|
|
231
|
+
**npx** — static scan + verdict in the terminal; no clone, no skill vendoring (Phases 3–4 Pierre
|
|
232
|
+
commentary require an agent with the skill installed):
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
npx appstore-precheck # scans the current directory, prints the verdict
|
|
236
|
+
npx appstore-precheck --fail-on YELLOW
|
|
104
237
|
```
|
|
105
238
|
|
|
106
|
-
**Standalone
|
|
239
|
+
**Standalone Bash** — same static scan, run by hand or in CI:
|
|
107
240
|
|
|
108
241
|
```bash
|
|
109
|
-
bash skills/appstore-precheck/scripts/scan.sh
|
|
242
|
+
bash skills/appstore-precheck/scripts/scan.sh # from a clone of this repo
|
|
110
243
|
```
|
|
111
244
|
|
|
112
|
-
|
|
245
|
+
### CI
|
|
246
|
+
|
|
247
|
+
Drop the static scan into a workflow with the bundled composite action. It fails the
|
|
113
248
|
job on a RED verdict; set `fail-on: YELLOW` to be stricter:
|
|
114
249
|
|
|
115
250
|
```yaml
|
|
116
|
-
- uses: berkayturk/appstore-precheck@v1.
|
|
251
|
+
- uses: berkayturk/appstore-precheck@v1.5.0
|
|
117
252
|
with:
|
|
118
253
|
working-directory: . # optional, default: . (repo root)
|
|
119
254
|
fail-on: RED # optional, default: RED (RED | YELLOW)
|
|
@@ -127,10 +262,11 @@ a RED verdict. No App Store Connect credentials are needed; the action runs the
|
|
|
127
262
|
| Phase | Step |
|
|
128
263
|
|-------|------|
|
|
129
264
|
| **0** | **Guideline drift**: diff the live App Store Review Guidelines against a tracked baseline. Never blocks. |
|
|
130
|
-
| **1** | **Static scan**: `scan.sh` over the
|
|
265
|
+
| **1** | **Static scan**: `scan.sh` over the 41 vectors above. |
|
|
131
266
|
| **2** | **`fastlane precheck`**: Apple's own metadata rule engine. |
|
|
132
|
-
| **3** | **
|
|
133
|
-
| **4** | **
|
|
267
|
+
| **3** | **Pierre commentary**: explains **every** FAIL and WARN from Phases 0–2 in 2–3 sentences each. |
|
|
268
|
+
| **4** | **Pierre deep review**: 28 semantic checks (22 Tier A + 6 Tier B v1 heuristic). Advisory only. |
|
|
269
|
+
| **5** | **Verdict**: GREEN / YELLOW / RED from Phases 0–2 counts, plus `.precheck-pass` token the upload guard gates on. |
|
|
134
270
|
|
|
135
271
|
## Demo
|
|
136
272
|
|
|
@@ -143,25 +279,45 @@ counts are deterministic ([`verdict.sh`](skills/appstore-precheck/scripts/verdic
|
|
|
143
279
|
|
|
144
280
|
## Example
|
|
145
281
|
|
|
282
|
+
### Pierre
|
|
283
|
+
|
|
284
|
+
**Français**
|
|
285
|
+
> *Non. Trois fautes. Apple en aurait trouvé moins. Suivant.*
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
**English**
|
|
290
|
+
> *No. Three faults. Apple would have found fewer. Next.*
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
**Türkçe**
|
|
295
|
+
> *Hayır. Üç hata. Apple daha azını bulurdu. Sıradaki.*
|
|
296
|
+
|
|
297
|
+
**RED — 3 FAIL** · submission blocked
|
|
298
|
+
|
|
146
299
|
```
|
|
147
|
-
|
|
300
|
+
FAIL: 3.1.2 Restore Purchases — not found in SubscriptionView.swift
|
|
301
|
+
Pierre: Apple requires a Restore Purchases control on every subscription paywall …
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
**Phase 4 (deep review, excerpt):**
|
|
148
305
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
• 5.1.1(v) FileTimestamp undeclared PrivacyInfo.xcprivacy → declare the reason
|
|
153
|
-
Submission blocked.
|
|
306
|
+
```
|
|
307
|
+
REVIEW-FINDING: 5.1.1(i) WARN — privacy policy says "no location data" but ContentView.swift uses CLLocationManager
|
|
308
|
+
Pierre: Apple compares your privacy policy to actual SDK and permission usage under 5.1.1(i) …
|
|
154
309
|
```
|
|
155
310
|
|
|
156
|
-
See [`examples/`](examples/) for full [GREEN](examples/green-pass.md)
|
|
157
|
-
|
|
311
|
+
See [`examples/`](examples/) for full [GREEN](examples/green-pass.md), [RED](examples/red-reject.md), and
|
|
312
|
+
[deep-review](examples/pierre-deep-review.md) runs, plus real [Phase 0 drift-check](examples/drift-check.md)
|
|
313
|
+
and [Phase 2 `fastlane precheck`](examples/fastlane-precheck.md) results.
|
|
158
314
|
|
|
159
315
|
## Output
|
|
160
316
|
|
|
161
317
|
| State | Meaning | Token | Upload guard |
|
|
162
318
|
|-------|---------|-------|--------------|
|
|
163
|
-
| 🟢 **GREEN** | 0 FAIL, ≤
|
|
164
|
-
| 🟡 **YELLOW** | 0 FAIL,
|
|
319
|
+
| 🟢 **GREEN** | 0 FAIL, ≤4 WARN | written (60 min) | allowed |
|
|
320
|
+
| 🟡 **YELLOW** | 0 FAIL, 5+ WARN | not written | blocked; needs confirmation |
|
|
165
321
|
| 🔴 **RED** | ≥1 FAIL | removed | blocked; shows the FAIL list |
|
|
166
322
|
|
|
167
323
|
## Configuration
|
|
@@ -205,16 +361,22 @@ Everything else works without one.
|
|
|
205
361
|
## Uninstall
|
|
206
362
|
|
|
207
363
|
```bash
|
|
208
|
-
/plugin uninstall appstore-precheck@appstore-precheck
|
|
209
|
-
|
|
210
|
-
|
|
364
|
+
/plugin uninstall appstore-precheck@appstore-precheck # Claude Code plugin
|
|
365
|
+
# Codex: run `codex`, open `/plugins`, uninstall appstore-precheck (CLI 0.125.x has no `plugin remove`)
|
|
366
|
+
# Cursor: Customize → Plugins → appstore-precheck → Uninstall
|
|
367
|
+
gemini skills uninstall appstore-precheck # Gemini (if installed via gemini skills)
|
|
368
|
+
rm -rf .claude/skills/appstore-precheck # install.sh (Claude Code / Cursor)
|
|
369
|
+
rm -rf .agents/skills/appstore-precheck # install.sh (Codex / Cursor / Gemini)
|
|
370
|
+
rm -rf .cursor/skills/appstore-precheck # if you mirrored there manually
|
|
371
|
+
rm -rf .gemini/skills/appstore-precheck # if installed to workspace scope manually
|
|
372
|
+
rm -f .precheck-pass # runtime token
|
|
211
373
|
```
|
|
212
374
|
|
|
213
375
|
## Development
|
|
214
376
|
|
|
215
377
|
```bash
|
|
216
378
|
npm run lint # bash -n on every script
|
|
217
|
-
npm run check-versions # plugin
|
|
379
|
+
npm run check-versions # plugin manifests / package.json / SKILL.md in lockstep
|
|
218
380
|
npm test # run scan.sh against fixture projects and assert
|
|
219
381
|
claude plugin validate .
|
|
220
382
|
```
|
|
@@ -223,9 +385,11 @@ CI runs ShellCheck, JSON validation, the version-consistency guard, and the fixt
|
|
|
223
385
|
|
|
224
386
|
## Disclaimer
|
|
225
387
|
|
|
226
|
-
This is a static heuristic tool. A GREEN result **lowers but does
|
|
227
|
-
Apple's guidelines change frequently and reviewer decisions are
|
|
228
|
-
|
|
388
|
+
This is a static heuristic tool plus Pierre's semantic deep review. A GREEN result **lowers but does
|
|
389
|
+
not eliminate** rejection risk; Apple's guidelines change frequently and reviewer decisions are
|
|
390
|
+
judgment calls. `REVIEW-FINDING` lines are advisory and do not block the token by themselves. It
|
|
391
|
+
performs no runtime crash testing; always do a final manual review before you submit. Not affiliated
|
|
392
|
+
with Apple.
|
|
229
393
|
|
|
230
394
|
## Star History
|
|
231
395
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "appstore-precheck",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Read-only iOS App Store pre-submission check, packaged as a portable Agent Skill
|
|
3
|
+
"version": "1.5.1",
|
|
4
|
+
"description": "Read-only iOS App Store pre-submission check, packaged as a portable Agent Skill with native Claude Code, Cursor, and Codex plugins, plus an npx CLI.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Berkay Turk (https://github.com/berkayturk)",
|
|
7
7
|
"homepage": "https://github.com/berkayturk/appstore-precheck",
|