@hitslop/shots 0.1.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.
Files changed (151) hide show
  1. package/SKILL.md +536 -0
  2. package/dist/cli.d.ts +3 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +113 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/auth.d.ts +7 -0
  7. package/dist/commands/auth.d.ts.map +1 -0
  8. package/dist/commands/auth.js +106 -0
  9. package/dist/commands/auth.js.map +1 -0
  10. package/dist/commands/crop.d.ts +5 -0
  11. package/dist/commands/crop.d.ts.map +1 -0
  12. package/dist/commands/crop.js +32 -0
  13. package/dist/commands/crop.js.map +1 -0
  14. package/dist/commands/default.d.ts +2 -0
  15. package/dist/commands/default.d.ts.map +1 -0
  16. package/dist/commands/default.js +142 -0
  17. package/dist/commands/default.js.map +1 -0
  18. package/dist/commands/feedback.d.ts +2 -0
  19. package/dist/commands/feedback.d.ts.map +1 -0
  20. package/dist/commands/feedback.js +6 -0
  21. package/dist/commands/feedback.js.map +1 -0
  22. package/dist/commands/generate.d.ts +3 -0
  23. package/dist/commands/generate.d.ts.map +1 -0
  24. package/dist/commands/generate.js +5 -0
  25. package/dist/commands/generate.js.map +1 -0
  26. package/dist/commands/init.d.ts +2 -0
  27. package/dist/commands/init.d.ts.map +1 -0
  28. package/dist/commands/init.js +5 -0
  29. package/dist/commands/init.js.map +1 -0
  30. package/dist/commands/install.d.ts +2 -0
  31. package/dist/commands/install.d.ts.map +1 -0
  32. package/dist/commands/install.js +13 -0
  33. package/dist/commands/install.js.map +1 -0
  34. package/dist/commands/scrape.d.ts +5 -0
  35. package/dist/commands/scrape.d.ts.map +1 -0
  36. package/dist/commands/scrape.js +5 -0
  37. package/dist/commands/scrape.js.map +1 -0
  38. package/dist/commands/studio.d.ts +7 -0
  39. package/dist/commands/studio.d.ts.map +1 -0
  40. package/dist/commands/studio.js +351 -0
  41. package/dist/commands/studio.js.map +1 -0
  42. package/dist/commands/styles.d.ts +2 -0
  43. package/dist/commands/styles.d.ts.map +1 -0
  44. package/dist/commands/styles.js +25 -0
  45. package/dist/commands/styles.js.map +1 -0
  46. package/dist/commands/view.d.ts +2 -0
  47. package/dist/commands/view.d.ts.map +1 -0
  48. package/dist/commands/view.js +25 -0
  49. package/dist/commands/view.js.map +1 -0
  50. package/dist/core/crop-pipeline.d.ts +28 -0
  51. package/dist/core/crop-pipeline.d.ts.map +1 -0
  52. package/dist/core/crop-pipeline.js +82 -0
  53. package/dist/core/crop-pipeline.js.map +1 -0
  54. package/dist/core/generate.d.ts +20 -0
  55. package/dist/core/generate.d.ts.map +1 -0
  56. package/dist/core/generate.js +388 -0
  57. package/dist/core/generate.js.map +1 -0
  58. package/dist/core/init.d.ts +3 -0
  59. package/dist/core/init.d.ts.map +1 -0
  60. package/dist/core/init.js +77 -0
  61. package/dist/core/init.js.map +1 -0
  62. package/dist/core/mock-generator.d.ts +5 -0
  63. package/dist/core/mock-generator.d.ts.map +1 -0
  64. package/dist/core/mock-generator.js +42 -0
  65. package/dist/core/mock-generator.js.map +1 -0
  66. package/dist/core/prompt-builder.d.ts +15 -0
  67. package/dist/core/prompt-builder.d.ts.map +1 -0
  68. package/dist/core/prompt-builder.js +195 -0
  69. package/dist/core/prompt-builder.js.map +1 -0
  70. package/dist/core/providers/fal.d.ts +3 -0
  71. package/dist/core/providers/fal.d.ts.map +1 -0
  72. package/dist/core/providers/fal.js +53 -0
  73. package/dist/core/providers/fal.js.map +1 -0
  74. package/dist/core/providers/index.d.ts +3 -0
  75. package/dist/core/providers/index.d.ts.map +1 -0
  76. package/dist/core/providers/index.js +2 -0
  77. package/dist/core/providers/index.js.map +1 -0
  78. package/dist/core/providers/managed.d.ts +3 -0
  79. package/dist/core/providers/managed.d.ts.map +1 -0
  80. package/dist/core/providers/managed.js +132 -0
  81. package/dist/core/providers/managed.js.map +1 -0
  82. package/dist/core/providers/openai.d.ts +3 -0
  83. package/dist/core/providers/openai.d.ts.map +1 -0
  84. package/dist/core/providers/openai.js +40 -0
  85. package/dist/core/providers/openai.js.map +1 -0
  86. package/dist/core/providers/replicate.d.ts +3 -0
  87. package/dist/core/providers/replicate.d.ts.map +1 -0
  88. package/dist/core/providers/replicate.js +87 -0
  89. package/dist/core/providers/replicate.js.map +1 -0
  90. package/dist/core/providers/resolve.d.ts +3 -0
  91. package/dist/core/providers/resolve.d.ts.map +1 -0
  92. package/dist/core/providers/resolve.js +41 -0
  93. package/dist/core/providers/resolve.js.map +1 -0
  94. package/dist/core/providers/types.d.ts +29 -0
  95. package/dist/core/providers/types.d.ts.map +1 -0
  96. package/dist/core/providers/types.js +2 -0
  97. package/dist/core/providers/types.js.map +1 -0
  98. package/dist/core/scraper.d.ts +22 -0
  99. package/dist/core/scraper.d.ts.map +1 -0
  100. package/dist/core/scraper.js +138 -0
  101. package/dist/core/scraper.js.map +1 -0
  102. package/dist/core/walkthrough.d.ts +10 -0
  103. package/dist/core/walkthrough.d.ts.map +1 -0
  104. package/dist/core/walkthrough.js +116 -0
  105. package/dist/core/walkthrough.js.map +1 -0
  106. package/dist/schema/config.d.ts +45 -0
  107. package/dist/schema/config.d.ts.map +1 -0
  108. package/dist/schema/config.js +36 -0
  109. package/dist/schema/config.js.map +1 -0
  110. package/dist/schema/manifest.d.ts +43 -0
  111. package/dist/schema/manifest.d.ts.map +1 -0
  112. package/dist/schema/manifest.js +5 -0
  113. package/dist/schema/manifest.js.map +1 -0
  114. package/dist/schema/style-meta.d.ts +8 -0
  115. package/dist/schema/style-meta.d.ts.map +1 -0
  116. package/dist/schema/style-meta.js +2 -0
  117. package/dist/schema/style-meta.js.map +1 -0
  118. package/dist/styles/installer.d.ts +2 -0
  119. package/dist/styles/installer.d.ts.map +1 -0
  120. package/dist/styles/installer.js +43 -0
  121. package/dist/styles/installer.js.map +1 -0
  122. package/dist/styles/registry.d.ts +20 -0
  123. package/dist/styles/registry.d.ts.map +1 -0
  124. package/dist/styles/registry.js +96 -0
  125. package/dist/styles/registry.js.map +1 -0
  126. package/dist/util/auth-store.d.ts +12 -0
  127. package/dist/util/auth-store.d.ts.map +1 -0
  128. package/dist/util/auth-store.js +46 -0
  129. package/dist/util/auth-store.js.map +1 -0
  130. package/dist/util/id.d.ts +2 -0
  131. package/dist/util/id.d.ts.map +1 -0
  132. package/dist/util/id.js +14 -0
  133. package/dist/util/id.js.map +1 -0
  134. package/dist/util/json.d.ts +3 -0
  135. package/dist/util/json.d.ts.map +1 -0
  136. package/dist/util/json.js +9 -0
  137. package/dist/util/json.js.map +1 -0
  138. package/dist/util/open.d.ts +2 -0
  139. package/dist/util/open.d.ts.map +1 -0
  140. package/dist/util/open.js +21 -0
  141. package/dist/util/open.js.map +1 -0
  142. package/dist/util/paths.d.ts +9 -0
  143. package/dist/util/paths.d.ts.map +1 -0
  144. package/dist/util/paths.js +21 -0
  145. package/dist/util/paths.js.map +1 -0
  146. package/package.json +37 -0
  147. package/studio/assets/index-BgiuT_Mv.css +1 -0
  148. package/studio/assets/index-DnN633_x.js +9 -0
  149. package/studio/index.html +13 -0
  150. package/styles/clean-premium/meta.json +7 -0
  151. package/styles/clean-premium/prompt.md +16 -0
package/SKILL.md ADDED
@@ -0,0 +1,536 @@
1
+ ---
2
+ name: shots
3
+ description: >
4
+ Use this skill when the user wants to generate or iterate on
5
+ App Store marketing screenshots. Triggers: "app store screenshots",
6
+ "marketing screenshots", "store listing images", "screenshot generation",
7
+ "app store assets", "shots", ".shots". Handles initialization of a
8
+ .shots/ workspace, AI image generation via GPT Image 2 through multiple
9
+ providers (OpenAI direct, fal.ai, or Replicate), sharp-based cropping/resizing
10
+ to App Store dimensions (1284x2778), App Store scraping for metadata/ASO,
11
+ and a style registry for prompt templates.
12
+ Do NOT use for general image generation, social media graphics, Google Play
13
+ screenshots, or non-App Store marketing assets.
14
+ ---
15
+
16
+ # Shots
17
+
18
+ AI-powered App Store screenshot generator. One command to set up, research, and generate.
19
+
20
+ ## Quick Start
21
+
22
+ ```bash
23
+ npx -y @hitslop/shots@latest # Open .shots, prep assets, launch an agent
24
+ npx -y @hitslop/shots@latest studio # Open the local workspace UI
25
+ npx -y @hitslop/shots@latest scrape <url> # Scrape App Store metadata
26
+ npx -y @hitslop/shots@latest install <style> # Install a bundled style
27
+ npx -y @hitslop/shots@latest styles # List available styles
28
+ npx -y @hitslop/shots@latest generate --prompt "..." # Generate screenshots
29
+ npx -y @hitslop/shots@latest feedback <shot-id> --text "..." # Save feedback without regenerating
30
+ ```
31
+
32
+ ## Workspace Prep
33
+
34
+ When launched from `npx -y @hitslop/shots@latest`, start by preparing the workspace before generating:
35
+
36
+ 1. Read `.shots/config.json`
37
+ 2. Inspect `.shots/app-screenshots/` and `.shots/inspo/`
38
+ 3. If app source code/docs exist in the current repo, inspect them
39
+ 4. Use App Store metadata already in config when present
40
+ 5. Use live web research when it helps fill missing product context
41
+ 6. Write as much of `.shots/config.json` as you can confidently
42
+ 7. Ask the user concise follow-up questions only for missing or uncertain fields
43
+ 8. Draft benefits/panels and get approval before running `generate`
44
+
45
+ The workspace may not be inside a product repo. Use whatever context exists instead of blocking on missing source code.
46
+
47
+ When updating `config.json`:
48
+
49
+ - Fill confident fields directly instead of leaving placeholders
50
+ - Preserve clearly intentional user-provided values unless new evidence is stronger
51
+ - Use app screenshots and inspo for visual understanding, not for copying existing marketing copy
52
+
53
+ ## Benefit Discovery
54
+
55
+ After the workspace is grounded, discover what the app does and draft benefit-driven headlines before generating screenshots.
56
+
57
+ ### Phase 1: Codebase Analysis
58
+
59
+ Scan the project in priority order to understand what the app does:
60
+
61
+ 1. **README / docs** — Top-level description, value proposition, feature list
62
+ 2. **Onboarding flows** — Files matching `*onboard*`, `*welcome*`, `*tutorial*`
63
+ 3. **Navigation / routing** — Tab bars, route definitions, navigation stacks
64
+ 4. **Models / schemas** — What data the app manages (database models, API types)
65
+ 5. **UI entry points** — `App.tsx`, `index.tsx`, main activity, root layout
66
+ 6. **`.shots/config.json` ASO data** — Already-scraped description, genres, ratings, reviews
67
+
68
+ If no repo exists, replace codebase analysis with:
69
+
70
+ 1. `.shots/config.json`
71
+ 2. App Store listing metadata and screenshots
72
+ 3. `.shots/app-screenshots/`
73
+ 4. `.shots/inspo/`
74
+ 5. Live web research about the product and positioning
75
+
76
+ ### Phase 2: Draft Benefits
77
+
78
+ For each major feature discovered, draft a headline using one of three approaches:
79
+
80
+ | Approach | What it does | Example |
81
+ |----------|-------------|---------|
82
+ | **Paint a moment** | Captures a specific micro-moment | "Check your coffee without opening the app" |
83
+ | **State an outcome** | Describes the end state achieved | "A home for every coffee you buy" |
84
+ | **Kill a pain** | Names the frustration eliminated | "Never waste a great bag of coffee" |
85
+
86
+ Draft 6-8 benefits covering the app's key features. Each benefit needs:
87
+ - `headline` — Action-verb headline, 3-5 words per line
88
+ - `subtitle` — Supporting line that grants permission or reframes
89
+ - `approach` — Which of the three approaches above (`moment`, `outcome`, or `pain`)
90
+ - `panelType` — From the Panel Styles table below (BoldClaim, ProductTour, etc.)
91
+ - `feature` — Brief description of the backing feature/screen
92
+ - `arcPosition` — Where it sits in the narrative arc (`hero`, `differentiator`, `core`, `trust`, `closer`)
93
+
94
+ ### Phase 3: Clarifying Questions
95
+
96
+ Before finalizing, ask the user only for the highest-value unknowns that you could not resolve confidently through repo analysis, App Store data, screenshots, inspo, or web research. Prioritize:
97
+ 1. What is the **#1 thing** your app does better than alternatives?
98
+ 2. What is the **most mentioned feature** in your reviews?
99
+ 3. What should the **first screenshot** convey to someone scrolling the App Store?
100
+
101
+ ### Phase 4: Save to Config
102
+
103
+ Write the finalized benefits array to `.shots/config.json`. Also write any other confident metadata improvements you discovered. The generate command will use these benefits to auto-build per-panel prompt descriptions.
104
+
105
+ ```json
106
+ {
107
+ "benefits": [
108
+ {
109
+ "headline": "See exactly where $200 went",
110
+ "subtitle": "Your spending, mapped by moment",
111
+ "approach": "moment",
112
+ "panelType": "BoldClaim",
113
+ "feature": "Spending breakdown dashboard",
114
+ "arcPosition": "hero"
115
+ }
116
+ ]
117
+ }
118
+ ```
119
+
120
+ ## Commands
121
+
122
+ ### `npx -y @hitslop/shots@latest` (default)
123
+
124
+ Initialize or reuse `.shots/`, open the workspace folder, print where to drop app screenshots and inspiration, and optionally launch Claude Code or Codex with this skill.
125
+
126
+ ### `npx -y @hitslop/shots@latest studio`
127
+
128
+ Start the local Studio UI for the current `.shots/` workspace. Studio is bundled into the CLI package and serves local data only: app screenshots, inspiration, previous screenshots, generated runs, and generated panels.
129
+
130
+ ### `npx -y @hitslop/shots@latest scrape <url>`
131
+
132
+ Scrape App Store metadata from a URL and populate `.shots/config.json` with ASO data, ratings, and screenshot URLs.
133
+
134
+ Options:
135
+ - `--download-screenshots` — Also download screenshot images to `.shots/app-screenshots/`
136
+ - `--country <code>` — App Store country code (default: `us`)
137
+
138
+ ### `npx -y @hitslop/shots@latest install <style-name>`
139
+
140
+ Install a bundled style to `.shots/styles/<name>/` and set it as the active style.
141
+
142
+ Available styles:
143
+ - `bold-gradient` — Vibrant gradients with punchy headlines
144
+ - `minimal-white` — Clean white backgrounds with precise typography
145
+ - `cinematic-dark` — Rich dark backgrounds with dramatic lighting
146
+ - `frutiger-aero` — Glossy bubbles, aqua glass, bright nature, and retro social/customization energy
147
+
148
+ ### `npx -y @hitslop/shots@latest styles`
149
+
150
+ List all available styles with descriptions and install status.
151
+
152
+ ### `npx -y @hitslop/shots@latest generate`
153
+
154
+ Generate App Store screenshots via the composite-then-crop pipeline. When generation finishes, the CLI opens the run folder in `.shots/runs/<shot-id>/`.
155
+
156
+ Options:
157
+ - `--prompt "..."` — The generation prompt (required)
158
+ - `--references path1.png,path2.png` — Reference images for edit endpoint
159
+ - `--quality high|medium|low` — Generation quality (default: `high`)
160
+ - `--feedback-for <shot-id>` — Iterate on a previous shot
161
+ - `--mock` — Use mock SVG generation instead of calling an API
162
+ - `--style <name>` — Use a specific style (overrides activeStyle in config)
163
+ - `--panels <number>` — Panels per run, max 3 (default: 3). For more panels, run generate multiple times.
164
+ - `--platform iphone|ipad|android` — Final panel platform (default: `iphone`)
165
+ - `--set-name <name>` — Store the run under a logical set name in metadata (default: `default`)
166
+ - `--provider openai|fal|replicate|managed` — Explicit provider choice (default: auto-detect from env vars)
167
+ - `--managed-provider openai|fal` — Backend provider when `--provider managed` is used (default: `openai`)
168
+ - `--mode composite|individual` — Generation mode (default: `composite`)
169
+
170
+ Managed generation v1 does not support `--references`. Use BYOK `openai` or
171
+ `fal` when reference images are required, or omit `--references` when using
172
+ `--provider managed`.
173
+
174
+ ### `npx -y @hitslop/shots@latest feedback <shot-id> --text "..."`
175
+
176
+ Append feedback to an existing shot history without creating a new image. Feedback is written to the manifest and, for v2 shots, to `.shots/runs/<shot-id>/feedback.json`.
177
+
178
+ ## Generation Pipeline
179
+
180
+ 1. Read `.shots/config.json` for app context
181
+ 2. If a style is active, load its `prompt.md` template and replace `{{placeholders}}`
182
+ 3. Build the full prompt with config context + user prompt
183
+ 4. Generate via the selected provider (OpenAI, fal.ai, or Replicate — auto-detected from env vars or explicit `--provider`)
184
+ 5. **Composite mode** (default): Generate one wide platform-aware image, crop into panels, and export exact final panel dimensions
185
+ 6. **Individual mode**: Generate each panel separately, then export exact final panel dimensions
186
+ 7. Create `.shots/runs/<shot-id>/` with panels, prompt, references, feedback, and metadata
187
+ 8. Update `.shots/manifest.json` with shot metadata (includes `provider` and `mode` fields)
188
+
189
+ ### Platform Dimensions
190
+
191
+ | Platform | Final panel | Multi-panel composite |
192
+ |----------|-------------|-----------------------|
193
+ | iphone | 1284x2778 | 1152px per panel x 2400 |
194
+ | ipad | 2048x2732 | 2048px per panel x 2732 |
195
+ | android | 1080x1920 | 1080px per panel x 1920 |
196
+
197
+ Each generate call produces at most 3 panels.
198
+
199
+ ### Multi-run generation
200
+
201
+ Each generate call produces **at most 3 panels** in a single composite. To create more screenshots, run the generate command multiple times — each run gets its own shot ID and composite.
202
+
203
+ Example: 6 panels → two runs:
204
+ ```bash
205
+ npx -y @hitslop/shots@latest generate --platform iphone --prompt "Panels 1-3: ..." --panels 3
206
+ npx -y @hitslop/shots@latest generate --platform iphone --prompt "Panels 4-6: ..." --panels 3
207
+ ```
208
+
209
+ Each run should have a focused prompt covering only that run's panels. Use `--set-name` to group related runs.
210
+
211
+ ## Style System
212
+
213
+ Styles are prompt templates that define visual direction. Each style has:
214
+ - `meta.json` — Name, description, visual tone, panel types
215
+ - `prompt.md` — Template with `{{appName}}`, `{{positioning}}`, `{{brandColors.primary}}` etc.
216
+ - `assets/` — Reference images included automatically during generation
217
+
218
+ ### Using styles
219
+
220
+ 1. Install: `npx -y @hitslop/shots@latest install bold-gradient`
221
+ 2. Generate: `npx -y @hitslop/shots@latest generate --prompt "Focus on the onboarding flow"`
222
+ 3. The style template is used as the prompt foundation, with the user's prompt appended
223
+
224
+ ### Agent behavior with styles
225
+
226
+ When generating screenshots:
227
+ 1. Check `.shots/styles/` for installed styles
228
+ 2. Check `config.activeStyle` for the currently selected style
229
+ 3. If styles exist but none is active, ask the user which to use
230
+ 4. Use the style's `prompt.md` as the prompt foundation
231
+ 5. Replace `{{placeholders}}` with config values
232
+ 6. Append the user's specific prompt instructions
233
+
234
+ ## Copywriting Principles
235
+
236
+ The existing app screenshots are reference for what the app *looks like*, not a template for the copy. Never mirror them — always improve.
237
+
238
+ ### Marketing is prediction installation
239
+
240
+ Your job isn't to describe features. It's to shift the user's self-concept so buying feels like recognition, not a gamble. Every panel should make someone stop scrolling because it reflects their identity back at them, not because it lists capabilities.
241
+
242
+ ### Three-layer system for each panel's copy
243
+
244
+ 1. **Perception** — Destabilize a cached prediction. Don't argue with "I don't need this." Show an angle they didn't consider. ("You think you're managing your time, but you're actually just reacting to it.")
245
+ 2. **Context** — Shift the frame. Make the screenshot feel like a late-night conversation, not a sales pitch. Same info, different context, different behavior.
246
+ 3. **Permission** — Remove the imagined consequence. Don't motivate — make action feel inevitable. Reflect back the version of them who already decided.
247
+
248
+ ### Headline writing recipe
249
+
250
+ For each panel, pick one of three approaches:
251
+
252
+ 1. **Paint a moment** — Capture a specific micro-moment the user will recognize. ("Check your coffee without opening the app")
253
+ 2. **State an outcome** — Describe the end state the feature achieves. ("A home for every coffee you buy")
254
+ 3. **Kill a pain** — Name the frustration the feature eliminates. ("Never waste a great bag of coffee")
255
+
256
+ ### Iron rules
257
+
258
+ - **One idea per headline** — if you need "and", it's two panels
259
+ - **3-5 words per line** — must be readable in App Store browse surfaces
260
+ - **Intentional line breaks** — break where the eye naturally pauses
261
+ - **Headlines destabilize**, subtitles grant permission or reframe
262
+ - **Don't describe features** — describe the identity shift the feature enables
263
+ - Copy that converts doesn't inform — it *recognizes*
264
+
265
+ ### Bad-to-better examples
266
+
267
+ | Bad | Better | Why |
268
+ |-----|--------|-----|
269
+ | "Track your finances" | "See exactly where $200 went" | Specific moment > generic verb |
270
+ | "AI-powered writing" | "Write the email in 30 seconds" | Outcome > technology |
271
+ | "Easy meal planning" | "Sunday dinner, planned by Friday" | Specific moment |
272
+ | "Manage your tasks" | "Done before your coffee gets cold" | Paints a moment |
273
+
274
+ ### Copy rules
275
+
276
+ - Research the app deeply — understand the emotional job-to-be-done, not just the functional one
277
+ - Read the app's reviews, description, and competitor positioning before writing a single word
278
+
279
+ ## Configuration
280
+
281
+ `.shots/config.json` contains all app metadata:
282
+
283
+ ```json
284
+ {
285
+ "appName": "My App",
286
+ "appStoreUrl": "",
287
+ "positioning": "",
288
+ "targetAudience": "",
289
+ "visualTone": "clean, modern, premium",
290
+ "brandColors": {
291
+ "primary": "#1a1a2e",
292
+ "secondary": "#16213e",
293
+ "accent": "#0f969c",
294
+ "text": "#f5f5f5"
295
+ },
296
+ "aso": {
297
+ "title": "", "subtitle": "", "description": "",
298
+ "keywords": "", "genres": [], "developer": "",
299
+ "version": "", "releaseNotes": ""
300
+ },
301
+ "ratings": { "score": 0, "reviewCount": 0 },
302
+ "scrapedAssets": { "iconUrl": "", "screenshotUrls": [], "scrapedAt": "" },
303
+ "activeStyle": ""
304
+ }
305
+ ```
306
+
307
+ ## File Structure
308
+
309
+ ```
310
+ .shots/
311
+ config.json # App metadata + ASO + style selection
312
+ manifest.json # All generated shots
313
+ inspo/ # Reference images
314
+ app-screenshots/ # App's current screenshots
315
+ runs/ # v2 generated runs with history and artifacts
316
+ shot-a1b2c3/
317
+ composite.png
318
+ screenshots/
319
+ panel-1.png
320
+ panel-2.png
321
+ panel-3.png
322
+ prompt.md
323
+ references.json
324
+ feedback.json
325
+ metadata.json
326
+ screenshots/ # Legacy final cropped panels
327
+ composites/ # Legacy full composites
328
+ styles/ # Installed style templates
329
+ bold-gradient/
330
+ meta.json
331
+ prompt.md
332
+ assets/
333
+ ```
334
+
335
+ ## Prompt Construction
336
+
337
+ ### Required Prompt Elements
338
+
339
+ Every prompt MUST specify:
340
+
341
+ 1. **Dimensions**: specify the CLI-provided composite dimensions
342
+ 2. **Panel structure**: "with three equal vertical panels"
343
+ 3. **Content centering**: "center all content (text, phone mockups, UI elements) horizontally within each panel's column"
344
+ 4. **Crop independence**: "each panel must crop cleanly into a standalone portrait App Store screenshot"
345
+ 5. **Text safety**: "keep all text legible and at least 80px away from panel edges"
346
+
347
+ ### Prompt Template
348
+
349
+ ```
350
+ Create one {compositeWidth}x{compositeHeight} App Store marketing composite for "{appName}" with three equal vertical panels and no visible seams.
351
+
352
+ Visual style: {visualTone}
353
+ Brand colors: primary {primary}, accent {accent}, text {textColor}
354
+
355
+ Panel 1 (left): {panel1Description}
356
+ Panel 2 (center): {panel2Description}
357
+ Panel 3 (right): {panel3Description}
358
+
359
+ Each panel must crop cleanly into a standalone portrait screenshot at {targetWidth}x{targetHeight}.
360
+ Keep text legible, at least 80px from panel edges.
361
+ Do not include app icons, download buttons, or platform badges.
362
+ {additionalInstructions}
363
+ ```
364
+
365
+ ### Panel Styles
366
+
367
+ Choose the right style for each panel. Not every panel needs a headline + subtitle.
368
+
369
+ | Style | When to use | Typical content |
370
+ |-------|------------|-----------------|
371
+ | BoldClaim | Opening impact | Large headline, minimal supporting text |
372
+ | QuietProof | Subtle credibility | Stats, ratings, or quiet social proof |
373
+ | BeforeAfter | Transformation | Side-by-side comparison within the panel |
374
+ | ProductTour | Feature showcase | App UI screenshots as the hero visual |
375
+ | TestimonialProof | Social proof | Quote + attribution |
376
+ | DataResult | Metrics highlight | Key numbers or outcomes |
377
+ | ProcessSteps | How-it-works | 2-3 simple steps |
378
+ | ObjectionCollapse | Address doubt | Pre-empt the most common hesitation |
379
+ | IdentityRecognition | User identity | "You already know..." framing |
380
+ | TypographicCloser | Final CTA | Clean typographic closer, one strong idea |
381
+ | FullBleedVisual | Visual impact | Edge-to-edge imagery, minimal text |
382
+
383
+ ### Narrative Arc
384
+
385
+ Order your panels to tell a story. The arc determines which benefit goes where.
386
+
387
+ #### Full arc (5+ panels)
388
+
389
+ | Position | Role | Panel Type | Purpose |
390
+ |----------|------|------------|---------|
391
+ | 1 | Hero | BoldClaim | Stop the scroll. One massive idea. |
392
+ | 2 | Differentiator | IdentityRecognition | Why THIS app, not alternatives |
393
+ | 3+ | Core Feature | ProductTour | Show it working |
394
+ | 2nd-to-last | Trust Signal | QuietProof / DataResult | Social proof or outcomes |
395
+ | Last | Closer | TypographicCloser | One strong closing idea |
396
+
397
+ #### 4-panel arc
398
+
399
+ | Position | Role | Panel Type |
400
+ |----------|------|------------|
401
+ | 1 | Hero | BoldClaim |
402
+ | 2 | Differentiator | IdentityRecognition |
403
+ | 3 | Core Feature | ProductTour |
404
+ | 4 | Closer | TypographicCloser |
405
+
406
+ #### 3-panel arc
407
+
408
+ | Position | Role | Panel Type |
409
+ |----------|------|------------|
410
+ | 1 | Hero | BoldClaim |
411
+ | 2 | Core Feature | ProductTour |
412
+ | 3 | Closer | TypographicCloser |
413
+
414
+ When `config.benefits` exists, sort benefits by `arcPosition` and assign them to panels following the arc above. The arc ensures each panel has a clear narrative role rather than listing features in arbitrary order.
415
+
416
+ ### Frutiger Aero Recipe
417
+
418
+ Use this recipe when generating FrutigerOS or another app where nostalgia,
419
+ customization, social presence, and playful self-expression are the product.
420
+
421
+ #### Visual direction
422
+
423
+ - Use glossy aqua glass, translucent rounded panels, floating bubbles, water refraction, rim highlights, saturated grass, bright sky, palm leaves, clownfish/orange accents, chrome, and lens flares.
424
+ - Typography should feel like Frutiger Aero: rounded, inflated, glossy, friendly, and high contrast. If the exact font cannot be rendered, ask for a close bubbly aqua-era imitation.
425
+ - Use reference images such as a tinted app icon and Frutiger Aero collage when available.
426
+ - Keep the real app UI readable inside a large iPhone mockup; do not let bubbles obscure primary UI.
427
+
428
+ #### Six-panel copy
429
+
430
+ | Panel | Headline | Subtitle |
431
+ |-------|----------|----------|
432
+ | Home | Your phone got glossy | A retro desktop you can actually live in |
433
+ | Theme editor | Make it yours | Swap glass, icons, wallpapers, and weird little moods |
434
+ | Chat | Chat like it's 2009 | Winks, nudges, status drama, and room energy |
435
+ | Stickers | Decorate everything | Drag in icons, stickers, widgets, and old-web flavor |
436
+ | Friends | Show off your desktop | Friend codes, global rooms, and profiles with personality |
437
+ | Apps | An OS for your aesthetic | Apps, windows, themes, and glossy nostalgia in one place |
438
+
439
+ Avoid leading with raw inventory counts such as sticker or wallpaper totals. Use
440
+ counts only as secondary proof if the visual already sells the feeling.
441
+
442
+ ### Brand Integration
443
+
444
+ When `config.json` has brand colors defined, incorporate them:
445
+
446
+ - Use `primary` for backgrounds or large color blocks
447
+ - Use `accent` for highlights, buttons, or emphasis
448
+ - Use `text` for body copy
449
+ - Maintain sufficient contrast ratios (4.5:1 for body text, 3:1 for large text)
450
+
451
+ ### Iteration Tips
452
+
453
+ When regenerating with feedback:
454
+ - Be specific about what to change: "make the headline on panel 2 larger" vs "make it better"
455
+ - Reference panel positions: "left panel", "center panel", "right panel"
456
+ - Describe the desired outcome, not just the problem
457
+ - If the layout is good but colors are wrong, say so explicitly to preserve what works
458
+
459
+ ### Anti-patterns
460
+
461
+ Avoid these common prompt mistakes:
462
+
463
+ - **Too vague**: "Make nice screenshots" — always specify visual tone and content
464
+ - **Too crowded**: Putting too much text on panels — each panel should convey one idea
465
+ - **Off-center content**: Not centering content within each panel column — elements should be horizontally centered
466
+ - **Generic stock imagery**: The screenshots should feel specific to the app, not generic marketing
467
+ - **Fake UI elements**: Don't add fake status bars, app store badges, or download buttons
468
+
469
+ ## Schema Reference
470
+
471
+ ### Config Interface
472
+
473
+ ```typescript
474
+ interface Config {
475
+ appName: string;
476
+ appStoreUrl: string;
477
+ positioning: string;
478
+ targetAudience: string;
479
+ visualTone: string;
480
+ brandColors: {
481
+ primary: string;
482
+ secondary: string;
483
+ accent: string;
484
+ text: string;
485
+ };
486
+ aso: {
487
+ title: string; // 30 chars max
488
+ subtitle: string; // 30 chars max
489
+ description: string; // 4000 chars max
490
+ keywords: string; // 100 chars, comma-separated
491
+ genres: string[];
492
+ developer: string;
493
+ version: string;
494
+ releaseNotes: string;
495
+ };
496
+ ratings: {
497
+ score: number; // 0-5
498
+ reviewCount: number;
499
+ };
500
+ scrapedAssets: {
501
+ iconUrl: string;
502
+ screenshotUrls: string[];
503
+ scrapedAt: string; // ISO 8601
504
+ };
505
+ activeStyle: string; // Currently selected style name
506
+ }
507
+ ```
508
+
509
+ ### Shot ID Format
510
+
511
+ - Prefix: `shot-`
512
+ - Suffix: 6 lowercase alphanumeric characters
513
+ - Example: `shot-a1b2c3`, `shot-x7k9m2`
514
+
515
+ IDs are unique within a manifest.
516
+
517
+ ### File Path Conventions
518
+
519
+ All paths in the manifest are **relative to `.shots/`**:
520
+
521
+ - New run composite: `runs/shot-{suffix}/composite.png`
522
+ - New run screenshots: `runs/shot-{suffix}/screenshots/panel-{panelNumber}.png` (1-indexed)
523
+ - New run prompt: `runs/shot-{suffix}/prompt.md`
524
+ - New run references: `runs/shot-{suffix}/references.json`
525
+ - New run feedback: `runs/shot-{suffix}/feedback.json`
526
+ - Legacy composites: `composites/composite-{suffix}.png`
527
+ - Legacy screenshots: `screenshots/shot-{suffix}-{panelNumber}.png`
528
+ - Reference images: usually `inspo/{filename}`, `app-screenshots/{filename}`, or `styles/{name}/assets/{filename}`
529
+
530
+ ## Requirements
531
+
532
+ - Node.js 18+
533
+ - One of the following environment variables (not needed with `--mock`):
534
+ - `OPENAI_API_KEY` — OpenAI direct (exact pixel dimensions)
535
+ - `FAL_KEY` — fal.ai proxy (exact pixel dimensions, often cheaper)
536
+ - `REPLICATE_API_TOKEN` — Replicate proxy (uses aspect ratio presets)
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env node
2
+ import { Args, Command, Options } from "@effect/cli";
3
+ import { NodeContext, NodeRuntime } from "@effect/platform-node";
4
+ import { Effect, Option } from "effect";
5
+ import { defaultCommand } from "./commands/default.js";
6
+ import { scrapeCommand } from "./commands/scrape.js";
7
+ import { generateCommand } from "./commands/generate.js";
8
+ import { feedbackCommand } from "./commands/feedback.js";
9
+ import { authLoginCommand, authLogoutCommand, authStatusCommand } from "./commands/auth.js";
10
+ import { cropCommand } from "./commands/crop.js";
11
+ import { initCommand } from "./commands/init.js";
12
+ import { installCommand } from "./commands/install.js";
13
+ import { studioCommand } from "./commands/studio.js";
14
+ import { stylesCommand } from "./commands/styles.js";
15
+ import { viewCommand } from "./commands/view.js";
16
+ // --- init ---
17
+ const initCmd = Command.make("init", {}, () => Effect.tryPromise(() => initCommand())).pipe(Command.withDescription("Create a .shots workspace"));
18
+ // --- scrape <url> ---
19
+ const scrapeCmd = Command.make("scrape", {
20
+ url: Args.text({ name: "url" }),
21
+ downloadScreenshots: Options.boolean("download-screenshots"),
22
+ country: Options.text("country").pipe(Options.withDefault("us")),
23
+ }, ({ url, downloadScreenshots, country }) => Effect.tryPromise(() => scrapeCommand(url, { downloadScreenshots, country }))).pipe(Command.withDescription("Scrape App Store metadata from a URL"));
24
+ // --- styles ---
25
+ const stylesCmd = Command.make("styles", {}, () => Effect.tryPromise(() => stylesCommand())).pipe(Command.withDescription("List bundled and installed screenshot styles"));
26
+ // --- install <style-name> ---
27
+ const installCmd = Command.make("install", {
28
+ styleName: Args.text({ name: "style-name" }),
29
+ }, ({ styleName }) => Effect.tryPromise(() => installCommand(styleName))).pipe(Command.withDescription("Install a bundled style into .shots/styles"));
30
+ // --- generate ---
31
+ const generateCmd = Command.make("generate", {
32
+ prompt: Options.text("prompt").pipe(Options.optional),
33
+ references: Options.text("references").pipe(Options.optional),
34
+ quality: Options.choice("quality", ["high", "medium", "low"]).pipe(Options.withDefault("high")),
35
+ feedbackFor: Options.text("feedback-for").pipe(Options.optional),
36
+ mock: Options.boolean("mock"),
37
+ style: Options.text("style").pipe(Options.optional),
38
+ panels: Options.integer("panels").pipe(Options.withDefault(3)),
39
+ platform: Options.choice("platform", ["iphone", "ipad", "android"]).pipe(Options.withDefault("iphone")),
40
+ setName: Options.text("set-name").pipe(Options.withDefault("default")),
41
+ provider: Options.choice("provider", ["openai", "fal", "replicate", "managed"]).pipe(Options.optional),
42
+ managedProvider: Options.choice("managed-provider", ["openai", "fal"]).pipe(Options.withDefault("openai")),
43
+ mode: Options.choice("mode", ["composite", "individual"]).pipe(Options.withDefault("composite")),
44
+ }, ({ prompt, references, quality, feedbackFor, mock, style, panels, platform, setName, provider, managedProvider, mode }) => Effect.tryPromise(() => generateCommand({
45
+ prompt: Option.getOrUndefined(prompt),
46
+ references: Option.getOrUndefined(references),
47
+ quality,
48
+ feedbackFor: Option.getOrUndefined(feedbackFor),
49
+ mock,
50
+ style: Option.getOrUndefined(style),
51
+ panels,
52
+ platform,
53
+ setName,
54
+ provider: Option.getOrUndefined(provider),
55
+ managedProvider,
56
+ mode: mode,
57
+ }))).pipe(Command.withDescription("Generate App Store screenshots"));
58
+ // --- crop <run-id> ---
59
+ const cropCmd = Command.make("crop", {
60
+ runId: Args.text({ name: "run-id" }),
61
+ platform: Options.choice("platform", ["iphone", "ipad", "android"]).pipe(Options.optional),
62
+ }, ({ runId, platform }) => Effect.tryPromise(() => cropCommand(runId, {
63
+ platform: Option.getOrUndefined(platform),
64
+ }))).pipe(Command.withDescription("Crop a run composite into exact panels"));
65
+ // --- view [run-id] ---
66
+ const viewCmd = Command.make("view", {
67
+ runId: Args.text({ name: "run-id" }).pipe(Args.optional),
68
+ }, ({ runId }) => Effect.tryPromise(() => viewCommand(Option.getOrUndefined(runId)))).pipe(Command.withDescription("Open the latest or selected run folder"));
69
+ // --- studio ---
70
+ const studioCmd = Command.make("studio", {
71
+ host: Options.text("host").pipe(Options.withDefault("127.0.0.1")),
72
+ port: Options.integer("port").pipe(Options.withDefault(4789)),
73
+ noOpen: Options.boolean("no-open"),
74
+ }, ({ host, port, noOpen }) => Effect.tryPromise(() => studioCommand({
75
+ host,
76
+ port,
77
+ open: !noOpen,
78
+ }))).pipe(Command.withDescription("Start the local Shots studio"));
79
+ // --- auth ---
80
+ const authStatusCmd = Command.make("status", {}, () => Effect.tryPromise(() => authStatusCommand())).pipe(Command.withDescription("Show BYOK and managed auth status"));
81
+ const authLoginCmd = Command.make("login", {
82
+ apiKey: Options.text("api-key").pipe(Options.optional),
83
+ apiUrl: Options.text("api-url").pipe(Options.optional),
84
+ }, ({ apiKey, apiUrl }) => Effect.tryPromise(() => authLoginCommand({
85
+ apiKey: Option.getOrUndefined(apiKey),
86
+ apiUrl: Option.getOrUndefined(apiUrl),
87
+ }))).pipe(Command.withDescription("Save a managed Shots CLI API key"));
88
+ const authLogoutCmd = Command.make("logout", {}, () => Effect.tryPromise(() => authLogoutCommand())).pipe(Command.withDescription("Clear managed auth"));
89
+ const authCmd = Command.make("auth", {}, () => Effect.sync(() => undefined)).pipe(Command.withDescription("Manage Shots authentication"), Command.withSubcommands([authStatusCmd, authLoginCmd, authLogoutCmd]));
90
+ // --- feedback <shot-id> --text "..." ---
91
+ const feedbackCmd = Command.make("feedback", {
92
+ shotId: Args.text({ name: "shot-id" }),
93
+ text: Options.text("text"),
94
+ }, ({ shotId, text }) => Effect.tryPromise(() => feedbackCommand(shotId, text))).pipe(Command.withDescription("Append feedback to an existing generated shot"));
95
+ // --- root (default — no subcommand) ---
96
+ const shots = Command.make("shots", {}, () => Effect.tryPromise(() => defaultCommand())).pipe(Command.withDescription("AI-powered App Store screenshot generator"), Command.withSubcommands([
97
+ initCmd,
98
+ scrapeCmd,
99
+ generateCmd,
100
+ cropCmd,
101
+ viewCmd,
102
+ studioCmd,
103
+ authCmd,
104
+ feedbackCmd,
105
+ stylesCmd,
106
+ installCmd,
107
+ ]));
108
+ const cli = Command.run(shots, {
109
+ name: "shots",
110
+ version: "0.1.0",
111
+ });
112
+ cli(process.argv).pipe(Effect.provide(NodeContext.layer), NodeRuntime.runMain);
113
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}