@daemux/store-automator 0.1.1 → 0.3.0
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/.claude-plugin/marketplace.json +2 -2
- package/package.json +1 -1
- package/plugins/store-automator/agents/appstore-media-designer.md +136 -168
- package/plugins/store-automator/agents/appstore-reviewer.md +35 -0
- package/src/install.mjs +2 -1
- package/src/templates.mjs +18 -0
- package/templates/CLAUDE.md.template +83 -19
- package/templates/analysis_options.yaml.template +40 -0
- package/templates/ci.config.yaml.template +1 -1
- package/templates/fastlane/android/Gemfile.template +5 -0
- package/templates/fastlane/ios/Gemfile.template +5 -0
- package/templates/firebase/.firebaserc.template +5 -0
- package/templates/firebase/firebase.json.template +16 -0
- package/templates/firebase/firestore.indexes.json.template +4 -0
- package/templates/firebase/firestore.rules.template +20 -0
- package/templates/firebase/storage.rules.template +10 -0
- package/templates/scripts/generate.sh +24 -0
- package/templates/web/deploy-cloudflare.mjs +11 -3
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
},
|
|
6
6
|
"metadata": {
|
|
7
7
|
"description": "App Store & Google Play automation for Flutter apps",
|
|
8
|
-
"version": "0.
|
|
8
|
+
"version": "0.2.0"
|
|
9
9
|
},
|
|
10
10
|
"plugins": [
|
|
11
11
|
{
|
|
12
12
|
"name": "store-automator",
|
|
13
13
|
"source": "./plugins/store-automator",
|
|
14
14
|
"description": "3 agents for app store publishing: reviewer, meta-creator, media-designer",
|
|
15
|
-
"version": "0.
|
|
15
|
+
"version": "0.2.0",
|
|
16
16
|
"keywords": ["flutter", "app-store", "google-play", "fastlane", "codemagic"]
|
|
17
17
|
}
|
|
18
18
|
]
|
package/package.json
CHANGED
|
@@ -1,224 +1,192 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: appstore-media-designer
|
|
3
|
-
description: "Creates app store screenshots for
|
|
3
|
+
description: "Creates ASO-optimized app store screenshots for Apple App Store and Google Play. All designs created in Stitch MCP. Researches competitors for inspiration. 5 screenshots per device, all sizes."
|
|
4
4
|
model: opus
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
You are a senior app store creative designer
|
|
7
|
+
You are a senior app store creative designer and ASO (App Store Optimization) specialist. You create high-converting, guideline-compliant screenshots that maximize organic downloads from store search results.
|
|
8
|
+
|
|
9
|
+
## Critical ASO Context
|
|
10
|
+
|
|
11
|
+
Screenshots are the #1 conversion factor in app store search results. Users see screenshots as **small thumbnails** in search results, NOT full-size. Your designs MUST convert at thumbnail size:
|
|
12
|
+
|
|
13
|
+
- **Headlines must be BIG** — max 2 lines, large bold text readable at thumbnail size
|
|
14
|
+
- **Short, selling copy** — benefit-focused, not feature descriptions
|
|
15
|
+
- **Visual clarity** — clean layouts that communicate instantly, no clutter
|
|
16
|
+
- **First screenshot is everything** — 80% of users decide from the first screenshot alone
|
|
8
17
|
|
|
9
18
|
## Workflow
|
|
10
19
|
|
|
11
|
-
1. READ the app source code (lib/ directory) to understand screens and features
|
|
12
|
-
2. READ any existing
|
|
20
|
+
1. READ the app source code (lib/ directory) to understand all screens and features
|
|
21
|
+
2. READ any existing Stitch designs in the same project (the app design was created here first)
|
|
13
22
|
3. READ ci.config.yaml for app identity and branding info
|
|
14
|
-
4.
|
|
15
|
-
5.
|
|
16
|
-
6.
|
|
17
|
-
7.
|
|
23
|
+
4. **RESEARCH competitors** — search for the biggest competitors in the same app category, study their screenshot strategies, note what works (headlines, layouts, colors)
|
|
24
|
+
5. PLAN 5 screenshot scenes optimized for ASO conversion
|
|
25
|
+
6. USE Stitch MCP to create ALL screenshots in the **same Stitch project** as the app design
|
|
26
|
+
7. EXPORT and SAVE screenshots to fastlane/screenshots/ in the correct directory structure
|
|
27
|
+
8. Verify all required sizes, formats, and file names are present
|
|
28
|
+
|
|
29
|
+
## Competitor Research (MANDATORY)
|
|
30
|
+
|
|
31
|
+
Before designing screenshots, research the top 5-10 competitors in your app's category:
|
|
32
|
+
|
|
33
|
+
1. Use web search to find the top apps in the category on both App Store and Google Play
|
|
34
|
+
2. Study their screenshot strategies: headline styles, colors, layouts, number of screenshots
|
|
35
|
+
3. Note common patterns that successful apps use
|
|
36
|
+
4. Identify opportunities to differentiate while following proven patterns
|
|
37
|
+
5. Document findings briefly before starting design
|
|
18
38
|
|
|
19
39
|
## Screenshot Strategy: 5 Scenes
|
|
20
40
|
|
|
21
41
|
For every app, create exactly 5 screenshot scenes:
|
|
22
42
|
|
|
23
|
-
| Scene | Purpose | Headline
|
|
24
|
-
|
|
25
|
-
| 01_hero |
|
|
26
|
-
| 02_feature1 | Primary feature in action | Benefit
|
|
27
|
-
| 03_feature2 | Secondary differentiating feature |
|
|
28
|
-
| 04_social | Social proof, results,
|
|
29
|
-
| 05_settings | Customization,
|
|
43
|
+
| Scene | Purpose | Headline Strategy |
|
|
44
|
+
|-------|---------|-------------------|
|
|
45
|
+
| 01_hero | Most impressive feature/screen — this is the MONEY SHOT | Bold value proposition, max 5 words, answers "what does this app do?" |
|
|
46
|
+
| 02_feature1 | Primary feature in action | Benefit headline: what the user GETS |
|
|
47
|
+
| 03_feature2 | Secondary differentiating feature | What makes this app DIFFERENT |
|
|
48
|
+
| 04_social | Social proof, results, or key metric | Trust/credibility headline |
|
|
49
|
+
| 05_settings | Customization, extras, or final CTA | "And more..." or urgency headline |
|
|
30
50
|
|
|
31
|
-
###
|
|
51
|
+
### Headline Rules (CRITICAL for ASO)
|
|
32
52
|
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
53
|
+
- **MAX 2 lines of text** — never more
|
|
54
|
+
- **BIG font size** — must be readable when the screenshot is thumbnail-sized in search results
|
|
55
|
+
- **Short selling text** — 3-6 words per headline, not feature descriptions
|
|
56
|
+
- **Action/benefit words** — "Unlock", "Transform", "Discover", "Create", "Save", "Get"
|
|
57
|
+
- **No filler words** — every word must earn its place
|
|
58
|
+
- Examples of GOOD headlines: "Chat Smarter, Not Harder", "Your AI Assistant", "Unlimited Creativity"
|
|
59
|
+
- Examples of BAD headlines: "Advanced AI-powered conversational interface with real-time responses"
|
|
39
60
|
|
|
40
|
-
|
|
61
|
+
### Scene Design Rules
|
|
41
62
|
|
|
42
|
-
|
|
63
|
+
- Headlines placed at TOP of the screenshot — big, bold, high contrast
|
|
64
|
+
- Background: solid color or gradient from the app's color palette
|
|
65
|
+
- App screen mockup placed centrally, occupying 55-65% of the image area
|
|
66
|
+
- Device frame is OPTIONAL — frameless looks more modern and gives more screen space
|
|
67
|
+
- Consistent typography and color scheme across all 5 scenes
|
|
68
|
+
- The app UI shown must represent realistic app content
|
|
69
|
+
- Clean, modern, minimal style — Apple/Google design quality
|
|
43
70
|
|
|
44
|
-
|
|
45
|
-
|-------------|---------|----------------------------|----------|
|
|
46
|
-
| iPhone 16 Pro Max | 6.9" | 1320 x 2868 | Yes (covers 6.7" family) |
|
|
47
|
-
| iPhone 16 Plus / 15 Plus | 6.7" | 1290 x 2796 | Alternative for 6.7" |
|
|
48
|
-
| iPhone 16 Pro | 6.3" | 1206 x 2622 | Recommended |
|
|
49
|
-
| iPhone SE (3rd gen) | 4.7" | 750 x 1334 | Only if supporting |
|
|
50
|
-
| iPad Pro 13" M4 | 13" | 2064 x 2752 | Yes |
|
|
51
|
-
| iPad Pro 12.9" (3rd gen+) | 12.9" | 2048 x 2732 | Yes |
|
|
71
|
+
## All Screenshots Created in Stitch MCP
|
|
52
72
|
|
|
53
|
-
|
|
73
|
+
**MANDATORY: ALL screenshots are designed entirely in Stitch MCP. No simulator screenshots, no mobile-mcp, no external tools.**
|
|
54
74
|
|
|
55
|
-
|
|
56
|
-
2. **iPad Pro 12.9"** -- 5 screenshots at 2048 x 2732 pixels
|
|
57
|
-
3. **iPad Pro 13"** -- 5 screenshots at 2064 x 2752 pixels
|
|
75
|
+
### Design Process
|
|
58
76
|
|
|
59
|
-
|
|
77
|
+
1. **Use the existing Stitch project** — screenshots go in the SAME project where the app design was created
|
|
78
|
+
2. For each of the 5 scenes, create a design in Stitch MCP with a detailed prompt
|
|
79
|
+
3. Generate at EVERY required device dimension (see sizes below)
|
|
80
|
+
4. Export each design as PNG and save to the correct directory path
|
|
60
81
|
|
|
61
|
-
|
|
62
|
-
- No photographs of people holding physical devices
|
|
63
|
-
- Format: .png or .jpg only (prefer .png for quality)
|
|
64
|
-
- Minimum 1, maximum 10 per device class per locale
|
|
65
|
-
- Text overlays are allowed but the app UI must be prominent
|
|
66
|
-
- No misleading content -- screenshots must represent the real app
|
|
67
|
-
- Screenshots are locale-specific (create for en-US, localize others as needed)
|
|
68
|
-
- Portrait orientation preferred (landscape accepted for specific apps)
|
|
82
|
+
### Stitch Design Prompt Template
|
|
69
83
|
|
|
70
|
-
|
|
84
|
+
For each scene, use a detailed prompt like:
|
|
71
85
|
|
|
72
86
|
```
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
02_feature1.png
|
|
90
|
-
03_feature2.png
|
|
91
|
-
04_social.png
|
|
92
|
-
05_settings.png
|
|
87
|
+
App store screenshot for a [app category] app called "[App Name]".
|
|
88
|
+
|
|
89
|
+
LAYOUT:
|
|
90
|
+
- Top 30%: Large headline "[HEADLINE TEXT]" in bold [font], [color] text, left-aligned or centered
|
|
91
|
+
- Optional small subheadline below in lighter weight
|
|
92
|
+
- Center/bottom 65%: [Device type] showing the app's [specific screen] with [describe UI content in detail]
|
|
93
|
+
- Background: [gradient/solid color matching app theme]
|
|
94
|
+
|
|
95
|
+
STYLE:
|
|
96
|
+
- Clean, modern, minimal — premium App Store quality
|
|
97
|
+
- No device frame / thin device frame (choose one)
|
|
98
|
+
- High contrast between text and background
|
|
99
|
+
- [App name]'s color palette: primary [#hex], accent [#hex], background [#hex]
|
|
100
|
+
|
|
101
|
+
DIMENSIONS: [width] x [height] pixels
|
|
102
|
+
FORMAT: PNG, RGB color space
|
|
93
103
|
```
|
|
94
104
|
|
|
95
|
-
|
|
105
|
+
### Device Sizes to Generate
|
|
96
106
|
|
|
97
|
-
|
|
107
|
+
For EACH of the 5 scenes, generate at ALL these sizes:
|
|
98
108
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
| 10-inch tablet | 1920 x 1200 or 1200 x 1920 | Optional | 8 |
|
|
104
|
-
| Feature graphic | 1024 x 500 exactly | Required | 1 |
|
|
105
|
-
| App icon (hi-res) | 512 x 512 exactly | Required | 1 |
|
|
109
|
+
**Apple App Store (required):**
|
|
110
|
+
- iPhone 6.7": 1290 x 2796 px
|
|
111
|
+
- iPad Pro 12.9": 2048 x 2732 px
|
|
112
|
+
- iPad Pro 13": 2064 x 2752 px
|
|
106
113
|
|
|
107
|
-
|
|
114
|
+
**Google Play (required):**
|
|
115
|
+
- Phone: 1080 x 1920 px
|
|
116
|
+
- 7" Tablet: 1200 x 1920 px
|
|
117
|
+
- 10" Tablet: 1920 x 1200 px (landscape)
|
|
108
118
|
|
|
109
|
-
|
|
110
|
-
-
|
|
111
|
-
-
|
|
112
|
-
- Tablets: can be landscape or portrait
|
|
113
|
-
- Feature graphic: always landscape 1024x500
|
|
119
|
+
**Google Play extras (required):**
|
|
120
|
+
- Feature Graphic: 1024 x 500 px (landscape banner — app name + tagline + brand colors)
|
|
121
|
+
- Icon: 512 x 512 px
|
|
114
122
|
|
|
115
|
-
|
|
123
|
+
## Directory Structure
|
|
116
124
|
|
|
117
|
-
|
|
118
|
-
- Device frames are optional (acceptable but not required)
|
|
119
|
-
- No misleading or irrelevant imagery
|
|
120
|
-
- Feature graphic is the banner displayed at top of store listing
|
|
121
|
-
- Text in screenshots must be readable
|
|
122
|
-
- No excessive text overlaying the UI
|
|
125
|
+
Save all exported screenshots to:
|
|
123
126
|
|
|
124
|
-
###
|
|
127
|
+
### iOS
|
|
128
|
+
```
|
|
129
|
+
fastlane/screenshots/ios/
|
|
130
|
+
en-US/
|
|
131
|
+
iPhone 6.7/
|
|
132
|
+
01_hero.png, 02_feature1.png, 03_feature2.png, 04_social.png, 05_settings.png
|
|
133
|
+
iPad Pro 12.9/
|
|
134
|
+
01_hero.png, 02_feature1.png, 03_feature2.png, 04_social.png, 05_settings.png
|
|
135
|
+
iPad Pro 13/
|
|
136
|
+
01_hero.png, 02_feature1.png, 03_feature2.png, 04_social.png, 05_settings.png
|
|
137
|
+
```
|
|
125
138
|
|
|
139
|
+
### Android
|
|
126
140
|
```
|
|
127
141
|
fastlane/screenshots/android/
|
|
128
142
|
en-US/
|
|
129
143
|
phoneScreenshots/
|
|
130
|
-
01_hero.png
|
|
131
|
-
02_feature1.png
|
|
132
|
-
03_feature2.png
|
|
133
|
-
04_social.png
|
|
134
|
-
05_settings.png
|
|
144
|
+
01_hero.png, 02_feature1.png, 03_feature2.png, 04_social.png, 05_settings.png
|
|
135
145
|
sevenInchScreenshots/
|
|
136
|
-
01_hero.png
|
|
137
|
-
02_feature1.png
|
|
138
|
-
03_feature2.png
|
|
139
|
-
04_social.png
|
|
140
|
-
05_settings.png
|
|
146
|
+
01_hero.png, 02_feature1.png, 03_feature2.png, 04_social.png, 05_settings.png
|
|
141
147
|
tenInchScreenshots/
|
|
142
|
-
01_hero.png
|
|
143
|
-
02_feature1.png
|
|
144
|
-
03_feature2.png
|
|
145
|
-
04_social.png
|
|
146
|
-
05_settings.png
|
|
148
|
+
01_hero.png, 02_feature1.png, 03_feature2.png, 04_social.png, 05_settings.png
|
|
147
149
|
featureGraphic.png
|
|
148
150
|
icon.png
|
|
149
151
|
```
|
|
150
152
|
|
|
151
|
-
##
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
- Specify the app's color palette (extract from theme.dart or design specs)
|
|
160
|
-
- Reference the actual app screen content for that scene
|
|
161
|
-
4. Export each screen at every required device dimension
|
|
162
|
-
5. Apply headline text overlays
|
|
163
|
-
6. Save to the correct directory paths
|
|
164
|
-
|
|
165
|
-
### Stitch Prompt Template
|
|
153
|
+
## Apple App Store Rules
|
|
154
|
+
- Must show app UI (Stitch-designed screens representing real app features)
|
|
155
|
+
- No photographs of people holding physical devices
|
|
156
|
+
- Format: .png only
|
|
157
|
+
- Max 10 per device class per locale
|
|
158
|
+
- Text overlays allowed, app UI must be prominent
|
|
159
|
+
- No misleading content
|
|
160
|
+
- Portrait orientation
|
|
166
161
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
Style: clean, modern, professional app store screenshot.
|
|
174
|
-
Dimensions: [width]x[height] pixels.
|
|
175
|
-
```
|
|
162
|
+
## Google Play Rules
|
|
163
|
+
- Screenshots must accurately depict the app experience
|
|
164
|
+
- Device frames optional
|
|
165
|
+
- Feature graphic: landscape 1024x500, displayed at top of store listing
|
|
166
|
+
- Text must be readable
|
|
167
|
+
- No excessive text overlaying the UI
|
|
176
168
|
|
|
177
|
-
## Fallback
|
|
178
|
-
|
|
179
|
-
If Stitch MCP is NOT available, create a detailed design specification file:
|
|
180
|
-
|
|
181
|
-
Save to `fastlane/screenshots/design-spec.json`:
|
|
182
|
-
```json
|
|
183
|
-
{
|
|
184
|
-
"brand": {
|
|
185
|
-
"primaryColor": "#hex",
|
|
186
|
-
"secondaryColor": "#hex",
|
|
187
|
-
"backgroundColor": "#hex",
|
|
188
|
-
"fontFamily": "SF Pro Display",
|
|
189
|
-
"headlineWeight": "bold"
|
|
190
|
-
},
|
|
191
|
-
"scenes": [
|
|
192
|
-
{
|
|
193
|
-
"id": "01_hero",
|
|
194
|
-
"headline": "Your Headline Here",
|
|
195
|
-
"appScreen": "description of which app screen to capture",
|
|
196
|
-
"background": "gradient from #hex to #hex"
|
|
197
|
-
}
|
|
198
|
-
]
|
|
199
|
-
}
|
|
200
|
-
```
|
|
169
|
+
## Fallback: If Stitch MCP is unavailable
|
|
201
170
|
|
|
202
|
-
|
|
171
|
+
Only if Stitch MCP tools are not available, save a design specification to `fastlane/screenshots/design-spec.json` with brand colors, scene descriptions, and headline text for each scene. This spec can be used to create screenshots manually.
|
|
203
172
|
|
|
204
173
|
## Output Verification Checklist
|
|
205
174
|
|
|
206
|
-
After creating all screenshots, verify
|
|
207
|
-
|
|
208
|
-
- [ ] iPhone 6.7"
|
|
209
|
-
- [ ] iPad Pro 12.9"
|
|
210
|
-
- [ ] iPad Pro 13"
|
|
211
|
-
- [ ] Android phone
|
|
212
|
-
- [ ] Android 7
|
|
213
|
-
- [ ] Android 10
|
|
214
|
-
- [ ]
|
|
215
|
-
- [ ]
|
|
216
|
-
- [ ]
|
|
217
|
-
- [ ]
|
|
218
|
-
- [ ] Headline text is readable at thumbnail size
|
|
219
|
-
- [ ] App UI is prominent and represents the actual app
|
|
175
|
+
After creating all screenshots, verify:
|
|
176
|
+
|
|
177
|
+
- [ ] iPhone 6.7" — 5 screenshots at 1290x2796, .png
|
|
178
|
+
- [ ] iPad Pro 12.9" — 5 screenshots at 2048x2732, .png
|
|
179
|
+
- [ ] iPad Pro 13" — 5 screenshots at 2064x2752, .png
|
|
180
|
+
- [ ] Android phone — 5 screenshots at 1080x1920, .png
|
|
181
|
+
- [ ] Android 7" tablet — 5 screenshots at 1200x1920, .png
|
|
182
|
+
- [ ] Android 10" tablet — 5 screenshots at 1920x1200, .png
|
|
183
|
+
- [ ] Feature graphic — 1024x500, .png
|
|
184
|
+
- [ ] Icon — 512x512, .png
|
|
185
|
+
- [ ] Headlines are BIG and readable at thumbnail size
|
|
186
|
+
- [ ] Max 2 lines of headline text per screenshot
|
|
220
187
|
- [ ] Consistent color scheme and typography across all scenes
|
|
221
|
-
- [ ]
|
|
188
|
+
- [ ] App UI is prominent and represents the actual app
|
|
189
|
+
- [ ] No photographs of people holding physical devices
|
|
222
190
|
|
|
223
191
|
## Output Footer
|
|
224
192
|
|
|
@@ -139,6 +139,41 @@ Read fastlane/app_rating_config.json and verify:
|
|
|
139
139
|
- **Subscription policy**: clear terms displayed before purchase, easy cancellation path
|
|
140
140
|
- **Content rating**: accurate IARC questionnaire responses, consistent with app content
|
|
141
141
|
|
|
142
|
+
## Pre-Submission Checklist
|
|
143
|
+
|
|
144
|
+
Before running the full review, verify these mandatory items. Include checkbox results in the output.
|
|
145
|
+
|
|
146
|
+
### Apple App Store Mandatory Items
|
|
147
|
+
- [ ] App name (name.txt) present and <= 30 characters
|
|
148
|
+
- [ ] Subtitle (subtitle.txt) present and <= 30 characters
|
|
149
|
+
- [ ] Description (description.txt) present and <= 4000 characters
|
|
150
|
+
- [ ] Keywords (keywords.txt) present and <= 100 characters total
|
|
151
|
+
- [ ] Privacy URL (privacy_url.txt) present with valid https:// URL
|
|
152
|
+
- [ ] Support URL (support_url.txt) present with valid https:// URL
|
|
153
|
+
- [ ] At least 1 screenshot per required device class (6.7", 6.5", iPad 12.9")
|
|
154
|
+
- [ ] App icon present (1024x1024 in Assets.xcassets)
|
|
155
|
+
- [ ] Bundle ID matches ci.config.yaml
|
|
156
|
+
- [ ] Age rating config (app_rating_config.json) present and complete
|
|
157
|
+
- [ ] Privacy policy page loads and contains required sections
|
|
158
|
+
|
|
159
|
+
### Google Play Mandatory Items
|
|
160
|
+
- [ ] Title (title.txt) present and <= 30 characters
|
|
161
|
+
- [ ] Short description (short_description.txt) present and <= 80 characters
|
|
162
|
+
- [ ] Full description (full_description.txt) present and <= 4000 characters
|
|
163
|
+
- [ ] Feature graphic present (exactly 1024x500)
|
|
164
|
+
- [ ] At least 2 phone screenshots present
|
|
165
|
+
- [ ] App icon (512x512) present
|
|
166
|
+
- [ ] Package name matches ci.config.yaml
|
|
167
|
+
- [ ] Changelog (changelogs/default.txt) present
|
|
168
|
+
- [ ] Privacy policy page loads and contains required sections
|
|
169
|
+
- [ ] Content rating questionnaire answers present
|
|
170
|
+
|
|
171
|
+
### Both Platforms
|
|
172
|
+
- [ ] All metadata files exist for every language in ci.config.yaml metadata.languages
|
|
173
|
+
- [ ] IAP config (iap_config.json) valid if app has subscriptions
|
|
174
|
+
- [ ] No placeholder text remaining in any metadata file
|
|
175
|
+
- [ ] Web pages (privacy, terms, support) deploy successfully
|
|
176
|
+
|
|
142
177
|
## Output Format
|
|
143
178
|
|
|
144
179
|
```
|
package/src/install.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import { injectEnvVars, injectStatusLine } from './settings.mjs';
|
|
|
11
11
|
import { ensureClaudePlugin } from './dependency-check.mjs';
|
|
12
12
|
import { promptForTokens } from './prompt.mjs';
|
|
13
13
|
import { getMcpServers, writeMcpJson } from './mcp-setup.mjs';
|
|
14
|
-
import { installClaudeMd, installCiTemplates } from './templates.mjs';
|
|
14
|
+
import { installClaudeMd, installCiTemplates, installFirebaseTemplates } from './templates.mjs';
|
|
15
15
|
|
|
16
16
|
function checkClaudeCli() {
|
|
17
17
|
const result = exec('command -v claude') || exec('which claude');
|
|
@@ -124,6 +124,7 @@ export async function runInstall(scope, isPostinstall = false) {
|
|
|
124
124
|
|
|
125
125
|
installClaudeMd(join(baseDir, 'CLAUDE.md'), packageDir);
|
|
126
126
|
installCiTemplates(projectDir, packageDir);
|
|
127
|
+
installFirebaseTemplates(projectDir, packageDir);
|
|
127
128
|
|
|
128
129
|
const scopeLabel = scope === 'user' ? 'global' : 'project';
|
|
129
130
|
console.log(`Configuring ${scopeLabel} settings...`);
|
package/src/templates.mjs
CHANGED
|
@@ -10,6 +10,14 @@ const FILE_COPIES = [
|
|
|
10
10
|
|
|
11
11
|
const DIR_COPIES = ['scripts', 'fastlane', 'web'];
|
|
12
12
|
|
|
13
|
+
const FIREBASE_COPIES = [
|
|
14
|
+
['firebase/firestore.rules.template', 'backend/firestore.rules'],
|
|
15
|
+
['firebase/storage.rules.template', 'backend/storage.rules'],
|
|
16
|
+
['firebase/firebase.json.template', 'backend/firebase.json'],
|
|
17
|
+
['firebase/firestore.indexes.json.template', 'backend/firestore.indexes.json'],
|
|
18
|
+
['firebase/.firebaserc.template', 'backend/.firebaserc'],
|
|
19
|
+
];
|
|
20
|
+
|
|
13
21
|
function copyIfMissing(srcPath, destPath, label, isDirectory) {
|
|
14
22
|
if (!existsSync(srcPath)) return;
|
|
15
23
|
if (existsSync(destPath)) {
|
|
@@ -53,3 +61,13 @@ export function installCiTemplates(projectDir, packageDir) {
|
|
|
53
61
|
);
|
|
54
62
|
}
|
|
55
63
|
}
|
|
64
|
+
|
|
65
|
+
export function installFirebaseTemplates(projectDir, packageDir) {
|
|
66
|
+
console.log('Installing Firebase backend templates...');
|
|
67
|
+
const templateDir = join(packageDir, 'templates');
|
|
68
|
+
ensureDir(join(projectDir, 'backend'));
|
|
69
|
+
|
|
70
|
+
for (const [src, dest] of FIREBASE_COPIES) {
|
|
71
|
+
copyIfMissing(join(templateDir, src), join(projectDir, dest), dest, false);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -45,7 +45,7 @@ metadata:
|
|
|
45
45
|
- en-US
|
|
46
46
|
|
|
47
47
|
web:
|
|
48
|
-
domain: "yourapp.
|
|
48
|
+
domain: "yourapp-pages.pages.dev" # *.pages.dev domain is sufficient, no custom DNS needed
|
|
49
49
|
cloudflare_project_name: "yourapp-pages"
|
|
50
50
|
```
|
|
51
51
|
|
|
@@ -92,10 +92,11 @@ Intro offer types: FREE, PAY_AS_YOU_GO, PAY_UP_FRONT.
|
|
|
92
92
|
## Flutter Development Standards
|
|
93
93
|
|
|
94
94
|
### Architecture
|
|
95
|
-
-
|
|
95
|
+
- **State management**: Riverpod (recommended) or BLoC/Cubit
|
|
96
96
|
- Repository pattern for data access
|
|
97
|
-
- Dependency injection
|
|
97
|
+
- **Dependency injection**: Riverpod providers (recommended) or get_it + injectable
|
|
98
98
|
- Feature-first folder structure
|
|
99
|
+
- Code generation: build_runner + riverpod_generator (or freezed + json_serializable)
|
|
99
100
|
|
|
100
101
|
### Folder Structure
|
|
101
102
|
```
|
|
@@ -111,9 +112,11 @@ lib/
|
|
|
111
112
|
widgets/ # Shared widgets
|
|
112
113
|
features/
|
|
113
114
|
{feature}/
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
models/ # Data classes, entities
|
|
116
|
+
providers/ # Riverpod providers (or cubits/)
|
|
117
|
+
repositories/ # Data sources, API calls
|
|
118
|
+
screens/ # Full-page widgets
|
|
119
|
+
widgets/ # Feature-specific widgets
|
|
117
120
|
services/
|
|
118
121
|
firebase/
|
|
119
122
|
api/
|
|
@@ -137,14 +140,24 @@ lib/
|
|
|
137
140
|
- No TODO/FIXME in committed code
|
|
138
141
|
- No hardcoded secrets anywhere
|
|
139
142
|
|
|
140
|
-
### Dependencies
|
|
143
|
+
### Dependencies
|
|
144
|
+
|
|
145
|
+
**Riverpod stack (recommended):**
|
|
146
|
+
- flutter_riverpod + riverpod_annotation + riverpod_generator -- state management & DI
|
|
147
|
+
- go_router -- navigation
|
|
148
|
+
- dio -- HTTP client
|
|
149
|
+
- freezed + json_serializable -- data classes
|
|
150
|
+
- firebase_core, firebase_auth, cloud_firestore -- Firebase
|
|
151
|
+
- in_app_purchase -- client-only IAP (no backend needed)
|
|
152
|
+
|
|
153
|
+
**BLoC stack (alternative):**
|
|
141
154
|
- flutter_bloc / bloc -- state management
|
|
142
155
|
- go_router -- navigation
|
|
143
156
|
- get_it + injectable -- dependency injection
|
|
144
157
|
- dio -- HTTP client
|
|
145
158
|
- freezed + json_serializable -- data classes
|
|
146
159
|
- firebase_core, firebase_auth, cloud_firestore -- Firebase
|
|
147
|
-
-
|
|
160
|
+
- in_app_purchase -- client-only IAP (no backend needed)
|
|
148
161
|
|
|
149
162
|
### Testing
|
|
150
163
|
- Unit tests for all business logic (cubits, repositories, use cases)
|
|
@@ -152,6 +165,48 @@ lib/
|
|
|
152
165
|
- Integration tests for critical user flows
|
|
153
166
|
- Minimum 80% coverage target
|
|
154
167
|
|
|
168
|
+
### Mandatory UI Testing (MUST run before publishing)
|
|
169
|
+
- **Flutter analyze**: `flutter analyze` must pass with zero issues
|
|
170
|
+
- **iOS build test**: `flutter build ios --no-codesign` must succeed
|
|
171
|
+
- **Android build test**: `flutter build appbundle` must succeed
|
|
172
|
+
- **Run all unit tests**: `flutter test` must pass
|
|
173
|
+
- **Widget/integration tests**: Run full test suite on both platforms
|
|
174
|
+
- All builds and tests MUST pass before proceeding to store metadata or publishing
|
|
175
|
+
|
|
176
|
+
### In-App Purchases (in_app_purchase — Client-Only, No Backend)
|
|
177
|
+
|
|
178
|
+
Use the `in_app_purchase` Flutter package for all IAP/subscription functionality. No backend server validation is needed — all purchase handling is client-only.
|
|
179
|
+
|
|
180
|
+
#### Store Setup
|
|
181
|
+
- **App Store Connect**: Create products (consumable / non-consumable / subscription) with unique IDs
|
|
182
|
+
- **Google Play Console**: Create matching product IDs
|
|
183
|
+
|
|
184
|
+
#### App Setup
|
|
185
|
+
- Add `in_app_purchase` to `pubspec.yaml`
|
|
186
|
+
- iOS: set bundle ID in Xcode
|
|
187
|
+
- Android: set application ID in `build.gradle`
|
|
188
|
+
|
|
189
|
+
#### Runtime Flow
|
|
190
|
+
1. **Listen early** — subscribe to `purchaseStream` in `initState()` (catches pending purchases from previous sessions)
|
|
191
|
+
2. **Check availability** — `InAppPurchase.instance.isAvailable()`
|
|
192
|
+
3. **Query products** — `queryProductDetails()` with your product IDs
|
|
193
|
+
4. **Buy** — `buyNonConsumable()` or `buyConsumable()`; native store UI appears automatically
|
|
194
|
+
5. **Handle result** — listen to `purchaseStream` for `PurchaseStatus.purchased`
|
|
195
|
+
6. **Deliver content** — unlock feature / add currency locally (update Firestore user doc)
|
|
196
|
+
7. **Complete** — call `completePurchase()` — **mandatory within 3 days or auto-refund**
|
|
197
|
+
|
|
198
|
+
#### Restore Purchases
|
|
199
|
+
- Call `restorePurchases()` — results come through `purchaseStream`
|
|
200
|
+
- Works for non-consumables and active subscriptions
|
|
201
|
+
- **Consumables cannot be restored** without a backend
|
|
202
|
+
|
|
203
|
+
#### Key Rules
|
|
204
|
+
- Subscribe to `purchaseStream` **before anything else** — or you'll miss events
|
|
205
|
+
- **Always** call `completePurchase()` after delivering content
|
|
206
|
+
- Store product IDs locally (hardcoded constants)
|
|
207
|
+
- Update the user's subscription tier in Firestore directly from the client after successful purchase
|
|
208
|
+
- No backend validation Cloud Function needed — trust the store receipt on-device
|
|
209
|
+
|
|
155
210
|
## Firebase Backend
|
|
156
211
|
|
|
157
212
|
### Services
|
|
@@ -175,29 +230,38 @@ lib/
|
|
|
175
230
|
|
|
176
231
|
## Workflow: Design to Publish
|
|
177
232
|
|
|
178
|
-
### Phase 1: Design
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
233
|
+
### Phase 1: App Design + Store Screenshots (FIRST — before development)
|
|
234
|
+
All design work happens in a single Stitch MCP project. Complete the full app design AND store screenshots before writing any code.
|
|
235
|
+
|
|
236
|
+
1. **Create Stitch project** — name it `{app-name}-design` via Stitch MCP
|
|
237
|
+
2. **Design all app screens** in the Stitch project using the designer agent:
|
|
238
|
+
- All core screens (home, main feature, detail views, settings, profile)
|
|
239
|
+
- Paywall/subscription screen
|
|
240
|
+
- Onboarding screens
|
|
241
|
+
- Review designs before proceeding
|
|
242
|
+
3. **Create all store screenshots** in the SAME Stitch project using the **appstore-media-designer** agent:
|
|
243
|
+
- 5 screenshot scenes per device size for all required Apple and Google device sizes
|
|
244
|
+
- Screenshots are designed entirely in Stitch MCP — no simulator screenshots needed
|
|
245
|
+
- See the appstore-media-designer agent for full details on ASO-optimized screenshots
|
|
246
|
+
4. Save all screenshots to `fastlane/screenshots/` in the correct directory structure
|
|
182
247
|
|
|
183
248
|
### Phase 2: Develop
|
|
184
|
-
1. Implement Flutter app following the standards above
|
|
249
|
+
1. Implement Flutter app following the standards above, using the Stitch designs as reference
|
|
185
250
|
2. Set up Firebase backend services
|
|
186
251
|
3. Fill ci.config.yaml with real values
|
|
187
252
|
4. Add creds/AuthKey.p8 (Apple) and creds/play-service-account.json (Google)
|
|
188
253
|
|
|
189
|
-
### Phase 3: Store Metadata
|
|
254
|
+
### Phase 3: Store Metadata + Texts
|
|
190
255
|
1. Run appstore-meta-creator to generate all metadata texts for configured languages
|
|
191
|
-
2.
|
|
192
|
-
3. Fill fastlane/iap_config.json if the app has subscriptions or IAP
|
|
193
|
-
4. Run appstore-reviewer to verify full compliance
|
|
256
|
+
2. Fill fastlane/iap_config.json if the app has subscriptions or IAP
|
|
194
257
|
|
|
195
|
-
### Phase 4: Web Pages
|
|
196
|
-
1. Customize marketing landing page (web/marketing.html)
|
|
258
|
+
### Phase 4: Web Pages (after screenshots are ready)
|
|
259
|
+
1. Customize marketing landing page (web/marketing.html) -- **include app screenshots** from Phase 1
|
|
197
260
|
2. Customize privacy policy page (web/privacy.html)
|
|
198
261
|
3. Customize terms of service page (web/terms.html)
|
|
199
262
|
4. Customize support page (web/support.html)
|
|
200
263
|
5. Deploy all via Cloudflare Pages (node web/deploy-cloudflare.mjs)
|
|
264
|
+
6. The default `*.pages.dev` domain is sufficient -- no custom DNS records needed. Use the generated URL (e.g., `yourapp-pages.pages.dev`) for privacy_url, support_url, and marketing_url in store metadata
|
|
201
265
|
|
|
202
266
|
### Phase 5: CI/CD Setup
|
|
203
267
|
1. Run scripts/generate.sh to create codemagic.yaml from ci.config.yaml
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
include: package:flutter_lints/flutter.yaml
|
|
2
|
+
|
|
3
|
+
linter:
|
|
4
|
+
rules:
|
|
5
|
+
# Style
|
|
6
|
+
always_declare_return_types: true
|
|
7
|
+
prefer_const_constructors: true
|
|
8
|
+
prefer_const_declarations: true
|
|
9
|
+
prefer_single_quotes: true
|
|
10
|
+
require_trailing_commas: true
|
|
11
|
+
sort_child_properties_last: true
|
|
12
|
+
|
|
13
|
+
# Safety
|
|
14
|
+
avoid_print: true
|
|
15
|
+
unawaited_futures: true
|
|
16
|
+
use_build_context_synchronously: true
|
|
17
|
+
cancel_subscriptions: true
|
|
18
|
+
close_sinks: true
|
|
19
|
+
|
|
20
|
+
# Best practices
|
|
21
|
+
avoid_empty_else: true
|
|
22
|
+
avoid_relative_lib_imports: true
|
|
23
|
+
prefer_final_fields: true
|
|
24
|
+
prefer_final_locals: true
|
|
25
|
+
prefer_is_empty: true
|
|
26
|
+
prefer_is_not_empty: true
|
|
27
|
+
unnecessary_await_in_return: true
|
|
28
|
+
unnecessary_const: true
|
|
29
|
+
unnecessary_new: true
|
|
30
|
+
unnecessary_this: true
|
|
31
|
+
|
|
32
|
+
analyzer:
|
|
33
|
+
errors:
|
|
34
|
+
missing_return: error
|
|
35
|
+
unawaited_futures: warning
|
|
36
|
+
avoid_print: warning
|
|
37
|
+
exclude:
|
|
38
|
+
- "**/*.g.dart"
|
|
39
|
+
- "**/*.freezed.dart"
|
|
40
|
+
- "**/*.gen.dart"
|
|
@@ -38,7 +38,7 @@ metadata:
|
|
|
38
38
|
|
|
39
39
|
# === WEB PAGES ===
|
|
40
40
|
web:
|
|
41
|
-
domain: "yourapp.
|
|
41
|
+
domain: "yourapp-pages.pages.dev" # *.pages.dev domain is sufficient, no custom DNS needed
|
|
42
42
|
cloudflare_project_name: "yourapp-pages"
|
|
43
43
|
tagline: "Your app tagline here"
|
|
44
44
|
primary_color: "#2563EB"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"firestore": {
|
|
3
|
+
"rules": "firestore.rules",
|
|
4
|
+
"indexes": "firestore.indexes.json"
|
|
5
|
+
},
|
|
6
|
+
"functions": [
|
|
7
|
+
{
|
|
8
|
+
"source": "functions",
|
|
9
|
+
"codebase": "default",
|
|
10
|
+
"ignore": ["node_modules", ".git", "firebase-debug.log", "firebase-debug.*.log", "*.local"]
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"storage": {
|
|
14
|
+
"rules": "storage.rules"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
rules_version = '2';
|
|
2
|
+
service cloud.firestore {
|
|
3
|
+
match /databases/{database}/documents {
|
|
4
|
+
function isOwner(userId) {
|
|
5
|
+
return request.auth != null && request.auth.uid == userId;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
match /users/{userId} {
|
|
9
|
+
allow read, write: if isOwner(userId);
|
|
10
|
+
|
|
11
|
+
match /{subcollection=**} {
|
|
12
|
+
allow read, write: if isOwner(userId);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
match /system/{document=**} {
|
|
17
|
+
allow read, write: if false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -27,6 +27,18 @@ if ! command -v yq &>/dev/null; then
|
|
|
27
27
|
exit 1
|
|
28
28
|
fi
|
|
29
29
|
|
|
30
|
+
# Validate that a config value does not contain characters that break sed
|
|
31
|
+
validate_value() {
|
|
32
|
+
local name="$1"
|
|
33
|
+
local value="$2"
|
|
34
|
+
if [[ "$value" =~ [|/\\] ]]; then
|
|
35
|
+
echo "ERROR: $name contains invalid characters (|, /, or \\) that would break substitution."
|
|
36
|
+
echo " Current value: $value"
|
|
37
|
+
echo " Please remove |, /, and \\ characters from $name in $CONFIG"
|
|
38
|
+
exit 1
|
|
39
|
+
fi
|
|
40
|
+
}
|
|
41
|
+
|
|
30
42
|
# Read app values from ci.config.yaml
|
|
31
43
|
BUNDLE_ID=$(yq -r '.app.bundle_id' "$CONFIG")
|
|
32
44
|
PACKAGE_NAME=$(yq -r '.app.package_name' "$CONFIG")
|
|
@@ -49,6 +61,18 @@ APPLE_ISSUER_ID=$(yq -r '.credentials.apple.issuer_id' "$CONFIG")
|
|
|
49
61
|
GOOGLE_SA_JSON_PATH=$(yq -r '.credentials.google.service_account_json_path' "$CONFIG")
|
|
50
62
|
KEYSTORE_PASSWORD=$(yq -r '.credentials.android.keystore_password' "$CONFIG")
|
|
51
63
|
|
|
64
|
+
# Validate all values before sed substitution
|
|
65
|
+
validate_value "app.bundle_id" "$BUNDLE_ID"
|
|
66
|
+
validate_value "app.package_name" "$PACKAGE_NAME"
|
|
67
|
+
validate_value "app.name" "$APP_NAME"
|
|
68
|
+
validate_value "app.sku" "$SKU"
|
|
69
|
+
validate_value "app.apple_id" "$APPLE_ID"
|
|
70
|
+
validate_value "android.track" "$TRACK"
|
|
71
|
+
validate_value "ios.primary_category" "$PRIMARY_CAT"
|
|
72
|
+
validate_value "ios.secondary_category" "$SECONDARY_CAT"
|
|
73
|
+
validate_value "credentials.apple.key_id" "$APPLE_KEY_ID"
|
|
74
|
+
validate_value "credentials.apple.issuer_id" "$APPLE_ISSUER_ID"
|
|
75
|
+
|
|
52
76
|
# Generate codemagic.yaml from template
|
|
53
77
|
sed \
|
|
54
78
|
-e "s|\${BUNDLE_ID}|$BUNDLE_ID|g" \
|
|
@@ -79,9 +79,17 @@ function getCredentials(dir) {
|
|
|
79
79
|
const mcpPath = join(dir, ".mcp.json");
|
|
80
80
|
if (existsSync(mcpPath)) {
|
|
81
81
|
const mcp = JSON.parse(readFileSync(mcpPath, "utf8"));
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
const cfServer = mcp?.mcpServers?.cloudflare || {};
|
|
83
|
+
const cfEnv = cfServer.env || {};
|
|
84
|
+
const cfArgs = cfServer.args || [];
|
|
85
|
+
token = token || cfEnv.CLOUDFLARE_API_TOKEN || "";
|
|
86
|
+
if (!accountId && cfArgs.length >= 4) {
|
|
87
|
+
const runIdx = cfArgs.indexOf("run");
|
|
88
|
+
if (runIdx >= 0 && cfArgs[runIdx + 1]) {
|
|
89
|
+
accountId = cfArgs[runIdx + 1];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
accountId = accountId || cfEnv.CLOUDFLARE_ACCOUNT_ID || "";
|
|
85
93
|
}
|
|
86
94
|
}
|
|
87
95
|
if (!token || !accountId) {
|