appstore-precheck 1.1.0 → 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 CHANGED
@@ -3,6 +3,157 @@
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
+
124
+ ## [1.1.1] - 2026-06-30
125
+
126
+ ### Changed
127
+ - **Pierre now speaks in a trilingual block.** The verdict opens with his native **French** line,
128
+ then an **English** rendering, then a rendering in the **user's conversation language** — each an
129
+ idiomatic, in-character re-expression in that language's own rhythm, not a literal translation.
130
+ Collapses to two lines when the user already converses in French or English. The block stays
131
+ flavor only; the FAIL/WARN list, `file:line` references, and fixes below it remain plain and
132
+ machine-faithful. Updated the output contract, Phase 4 step 3, and the behavioral eval
133
+ assertions accordingly.
134
+
135
+ ### Fixed
136
+ - **Pierre no longer treats local-only files as Apple submission evidence.** The Phase 3 prompt
137
+ now scopes reject-risk evidence to Apple-facing artifacts (fastlane metadata, paywall Swift,
138
+ String Catalog, Info.plist, PrivacyInfo.xcprivacy). Internal/local files (`.planning/` notes,
139
+ `reviewPrepNotes` drafts, build scripts) and Google Play / non-Apple sections are out of scope —
140
+ cited at most as a WARN labeled "internal draft — not submitted to Apple", never REJECT-RISK. A
141
+ REJECT risk now requires a contradiction *within* submission-facing artifacts, not an internal
142
+ doc disagreeing with metadata. An eligibility-gated/conditional offer paired with metadata that
143
+ mentions it is WARN at most (unless the metadata promises it unconditionally). Prevents the
144
+ false REJECT-RISK overreach seen when dogfooding an already-approved build.
145
+ - **2.3.7 locale check no longer hard-FAILs on a config/disk mismatch.** A locale listed in
146
+ `.appstore-precheck.json` `locales` but with no metadata folder on disk is now a WARN (with an
147
+ actionable "add it or remove it from the config" message), not a FAIL — that locale was simply
148
+ never submitted, so it must not turn an approved set RED. A missing *file* inside a present
149
+ locale folder is still a FAIL.
150
+ - **2.1 placeholder check no longer false-fires on words containing "changeme".** The `changeme`
151
+ pattern is now word-bounded (`\bchangeme\b`) in both the metadata-URL and store-copy scans, so
152
+ legitimate copy such as the French "changement" ("change") is not flagged as unfinished.
153
+ - Added regression coverage in `tests/test-config.sh` for both fixes.
154
+ - CI: bumped `actions/checkout@v4 -> v7` and `actions/setup-node@v4 -> v6` to clear the
155
+ GitHub Actions Node.js 20 deprecation warning (both now run natively on Node 24).
156
+
6
157
  ## [1.1.0] - 2026-06-28
7
158
 
8
159
  ### Added
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, and finishes with an adversarial review pass, then hands you a single **GREEN / YELLOW /
20
- RED** verdict. It never edits your code.
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,95 +30,243 @@ 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. A GREEN from
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. The breakdown beneath it, every `file:line` and fix, stays
39
- plain and surgical.
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
- 20 rejection vectors across code, fastlane metadata, screenshots, `PrivacyInfo.xcprivacy`, and the paywall:
47
+ 41 rejection vectors across code, fastlane metadata, screenshots, `PrivacyInfo.xcprivacy`, and the paywall:
44
48
 
45
49
  | Guideline | Check |
46
50
  |-----------|-------|
47
- | **5.1.1(v)** | Privacy Manifest Required Reason API parity |
48
- | **5.1.1** | A non-empty purpose string for every sensitive framework |
49
- | **5.1.2** | ATT usage `NSUserTrackingUsageDescription` |
50
- | **2.3.10** | No other-platform / competitor names in metadata |
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.7** | Localized metadata parity across every locale |
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
- | **2.5.1** | No private / banned APIs |
57
- | **4.0** | Minimum functionality (real navigation) |
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
- | **3.1.1(a)** | External purchase link entitlement + disclosure, when external purchase APIs are used |
60
- | **5.1.5** | Sensitive-API justification *(opt-in)* |
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.
140
+
141
+ ### Supported app types
142
+
143
+ Every check that reads store metadata, the privacy manifest, screenshots, or the export-compliance
144
+ key works on **any iOS app**. The code-level checks read Swift source, so their coverage depends on
145
+ how the app is built:
146
+
147
+ | App type | Coverage |
148
+ |----------|----------|
149
+ | 🟢 **Native Swift / SwiftUI** | **Full.** All 41 vectors apply. |
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. |
68
151
 
69
152
  ## Quick start
70
153
 
71
- **Claude Code**: install as a plugin:
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:**
72
168
 
73
169
  ```
74
170
  /plugin marketplace add berkayturk/appstore-precheck
75
171
  /plugin install appstore-precheck@appstore-precheck
76
172
  ```
77
173
 
78
- **Run instantly with npx** (no clone, no install):
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:**
79
184
 
80
185
  ```bash
81
- npx appstore-precheck # scans the current directory, prints the verdict
82
- npx appstore-precheck --fail-on YELLOW
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
83
196
  ```
84
197
 
85
- It runs the static scan over the current directory and exits non-zero on RED (or on YELLOW with
86
- `--fail-on YELLOW`). Read-only, like everything else here.
198
+ Refresh after repo updates:
87
199
 
88
- **Codex, Cursor, Gemini, Claude**: clone, then run the installer from inside your iOS project:
200
+ ```bash
201
+ codex plugin marketplace upgrade appstore-precheck
202
+ ```
203
+
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/`):
89
214
 
90
215
  ```bash
91
216
  git clone https://github.com/berkayturk/appstore-precheck.git
92
- /path/to/appstore-precheck/install.sh # → ./.claude/skills and ./.agents/skills
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
93
237
  ```
94
238
 
95
- **Standalone**: the scanner is just Bash:
239
+ **Standalone Bash** same static scan, run by hand or in CI:
96
240
 
97
241
  ```bash
98
- bash skills/appstore-precheck/scripts/scan.sh
242
+ bash skills/appstore-precheck/scripts/scan.sh # from a clone of this repo
99
243
  ```
100
244
 
101
- **CI**: drop the static scan into a workflow with the bundled composite action. It fails the
245
+ ### CI
246
+
247
+ Drop the static scan into a workflow with the bundled composite action. It fails the
102
248
  job on a RED verdict; set `fail-on: YELLOW` to be stricter:
103
249
 
104
250
  ```yaml
105
- - uses: berkayturk/appstore-precheck@v1.0.0
251
+ - uses: berkayturk/appstore-precheck@v1.5.0
106
252
  with:
107
- working-directory: . # optional
108
- fail-on: RED # optional (RED | YELLOW)
253
+ working-directory: . # optional, default: . (repo root)
254
+ fail-on: RED # optional, default: RED (RED | YELLOW)
109
255
  ```
110
256
 
257
+ Both inputs are optional: with none set, the action scans the repo root and fails the job only on
258
+ a RED verdict. No App Store Connect credentials are needed; the action runs the static scan only.
259
+
111
260
  ## How it works
112
261
 
113
262
  | Phase | Step |
114
263
  |-------|------|
115
264
  | **0** | **Guideline drift**: diff the live App Store Review Guidelines against a tracked baseline. Never blocks. |
116
- | **1** | **Static scan**: `scan.sh` over the 20 vectors above. |
265
+ | **1** | **Static scan**: `scan.sh` over the 41 vectors above. |
117
266
  | **2** | **`fastlane precheck`**: Apple's own metadata rule engine. |
118
- | **3** | **Adversarial review**: Pierre role-plays a skeptical reviewer and drafts realistic rejection notices. |
119
- | **4** | **Verdict**: GREEN / YELLOW / RED, and a `.precheck-pass` token the upload guard gates on. |
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. |
120
270
 
121
271
  ## Demo
122
272
 
@@ -129,25 +279,45 @@ counts are deterministic ([`verdict.sh`](skills/appstore-precheck/scripts/verdic
129
279
 
130
280
  ## Example
131
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
+
299
+ ```
300
+ FAIL: 3.1.2 Restore Purchases — not found in SubscriptionView.swift
301
+ Pierre: Apple requires a Restore Purchases control on every subscription paywall …
132
302
  ```
133
- 🔴 Pierre: "Non. Three faults. Apple would have found one. Suivant."
134
303
 
135
- RED: 3 FAIL
136
- • 3.1.2 Restore Purchases missing SubscriptionView.swift:14 → add restorePurchases()
137
- • 2.3.10 "Android" in metadata en-US/description.txt:1 → remove the reference
138
- 5.1.1(v) FileTimestamp undeclared PrivacyInfo.xcprivacy declare the reason
139
- Submission blocked.
304
+ **Phase 4 (deep review, excerpt):**
305
+
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) …
140
309
  ```
141
310
 
142
- See [`examples/`](examples/) for full [GREEN](examples/green-pass.md) and [RED](examples/red-reject.md) runs,
143
- plus real [Phase 0 drift-check](examples/drift-check.md) and [Phase 2 `fastlane precheck`](examples/fastlane-precheck.md) results.
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.
144
314
 
145
315
  ## Output
146
316
 
147
317
  | State | Meaning | Token | Upload guard |
148
318
  |-------|---------|-------|--------------|
149
- | 🟢 **GREEN** | 0 FAIL, ≤2 WARN | written (60 min) | allowed |
150
- | 🟡 **YELLOW** | 0 FAIL, 3+ WARN | not written | blocked; needs confirmation |
319
+ | 🟢 **GREEN** | 0 FAIL, ≤4 WARN | written (60 min) | allowed |
320
+ | 🟡 **YELLOW** | 0 FAIL, 5+ WARN | not written | blocked; needs confirmation |
151
321
  | 🔴 **RED** | ≥1 FAIL | removed | blocked; shows the FAIL list |
152
322
 
153
323
  ## Configuration
@@ -177,8 +347,13 @@ App Store apps (DuckDuckGo, Pocket Casts, Wikipedia).
177
347
 
178
348
  ## Requirements
179
349
 
180
- `bash`, `git`, `grep`, `find` · `jq` (config + String Catalog checks) · `python3` (exact Unicode
181
- length counts) · `fastlane` + an App Store Connect API key for Phase 2 only.
350
+ `bash`, `git`, `grep`, `find` are all the static scan, `npx appstore-precheck`, and the GitHub
351
+ Action need, and they run with **zero credentials and no network**. `jq` (config + String Catalog
352
+ checks) and `python3` (exact Unicode length counts) are optional and sharpen a few checks.
353
+
354
+ Only the **optional Phase 2** (`fastlane precheck`) needs `fastlane` and an
355
+ [App Store Connect API key](https://developer.apple.com/documentation/appstoreconnectapi/creating-api-keys-for-app-store-connect-api).
356
+ Everything else works without one.
182
357
 
183
358
  **Secrets**: the ASC API key is read from your environment at runtime and deleted immediately after
184
359
  `fastlane precheck`. Never commit it; `.gitignore` blocks `*asc-key*.json` and `.env`.
@@ -186,16 +361,22 @@ length counts) · `fastlane` + an App Store Connect API key for Phase 2 only.
186
361
  ## Uninstall
187
362
 
188
363
  ```bash
189
- /plugin uninstall appstore-precheck@appstore-precheck # Claude Code plugin
190
- rm -rf .claude/skills/appstore-precheck .agents/skills/appstore-precheck # manual install
191
- rm -f .precheck-pass # runtime token
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
192
373
  ```
193
374
 
194
375
  ## Development
195
376
 
196
377
  ```bash
197
378
  npm run lint # bash -n on every script
198
- npm run check-versions # plugin.json / package.json / SKILL.md in lockstep
379
+ npm run check-versions # plugin manifests / package.json / SKILL.md in lockstep
199
380
  npm test # run scan.sh against fixture projects and assert
200
381
  claude plugin validate .
201
382
  ```
@@ -204,9 +385,11 @@ CI runs ShellCheck, JSON validation, the version-consistency guard, and the fixt
204
385
 
205
386
  ## Disclaimer
206
387
 
207
- This is a static heuristic tool. A GREEN result **lowers but does not eliminate** rejection risk;
208
- Apple's guidelines change frequently and reviewer decisions are judgment calls. It performs no runtime
209
- crash testing; always do a final manual review before you submit. Not affiliated with Apple.
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.
210
393
 
211
394
  ## Star History
212
395
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "appstore-precheck",
3
- "version": "1.1.0",
4
- "description": "Read-only iOS App Store pre-submission check, packaged as a portable Agent Skill / Claude Code plugin and an npx CLI.",
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",