@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.
- package/SKILL.md +536 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +113 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/auth.d.ts +7 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +106 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/crop.d.ts +5 -0
- package/dist/commands/crop.d.ts.map +1 -0
- package/dist/commands/crop.js +32 -0
- package/dist/commands/crop.js.map +1 -0
- package/dist/commands/default.d.ts +2 -0
- package/dist/commands/default.d.ts.map +1 -0
- package/dist/commands/default.js +142 -0
- package/dist/commands/default.js.map +1 -0
- package/dist/commands/feedback.d.ts +2 -0
- package/dist/commands/feedback.d.ts.map +1 -0
- package/dist/commands/feedback.js +6 -0
- package/dist/commands/feedback.js.map +1 -0
- package/dist/commands/generate.d.ts +3 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +5 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +5 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/install.d.ts +2 -0
- package/dist/commands/install.d.ts.map +1 -0
- package/dist/commands/install.js +13 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/scrape.d.ts +5 -0
- package/dist/commands/scrape.d.ts.map +1 -0
- package/dist/commands/scrape.js +5 -0
- package/dist/commands/scrape.js.map +1 -0
- package/dist/commands/studio.d.ts +7 -0
- package/dist/commands/studio.d.ts.map +1 -0
- package/dist/commands/studio.js +351 -0
- package/dist/commands/studio.js.map +1 -0
- package/dist/commands/styles.d.ts +2 -0
- package/dist/commands/styles.d.ts.map +1 -0
- package/dist/commands/styles.js +25 -0
- package/dist/commands/styles.js.map +1 -0
- package/dist/commands/view.d.ts +2 -0
- package/dist/commands/view.d.ts.map +1 -0
- package/dist/commands/view.js +25 -0
- package/dist/commands/view.js.map +1 -0
- package/dist/core/crop-pipeline.d.ts +28 -0
- package/dist/core/crop-pipeline.d.ts.map +1 -0
- package/dist/core/crop-pipeline.js +82 -0
- package/dist/core/crop-pipeline.js.map +1 -0
- package/dist/core/generate.d.ts +20 -0
- package/dist/core/generate.d.ts.map +1 -0
- package/dist/core/generate.js +388 -0
- package/dist/core/generate.js.map +1 -0
- package/dist/core/init.d.ts +3 -0
- package/dist/core/init.d.ts.map +1 -0
- package/dist/core/init.js +77 -0
- package/dist/core/init.js.map +1 -0
- package/dist/core/mock-generator.d.ts +5 -0
- package/dist/core/mock-generator.d.ts.map +1 -0
- package/dist/core/mock-generator.js +42 -0
- package/dist/core/mock-generator.js.map +1 -0
- package/dist/core/prompt-builder.d.ts +15 -0
- package/dist/core/prompt-builder.d.ts.map +1 -0
- package/dist/core/prompt-builder.js +195 -0
- package/dist/core/prompt-builder.js.map +1 -0
- package/dist/core/providers/fal.d.ts +3 -0
- package/dist/core/providers/fal.d.ts.map +1 -0
- package/dist/core/providers/fal.js +53 -0
- package/dist/core/providers/fal.js.map +1 -0
- package/dist/core/providers/index.d.ts +3 -0
- package/dist/core/providers/index.d.ts.map +1 -0
- package/dist/core/providers/index.js +2 -0
- package/dist/core/providers/index.js.map +1 -0
- package/dist/core/providers/managed.d.ts +3 -0
- package/dist/core/providers/managed.d.ts.map +1 -0
- package/dist/core/providers/managed.js +132 -0
- package/dist/core/providers/managed.js.map +1 -0
- package/dist/core/providers/openai.d.ts +3 -0
- package/dist/core/providers/openai.d.ts.map +1 -0
- package/dist/core/providers/openai.js +40 -0
- package/dist/core/providers/openai.js.map +1 -0
- package/dist/core/providers/replicate.d.ts +3 -0
- package/dist/core/providers/replicate.d.ts.map +1 -0
- package/dist/core/providers/replicate.js +87 -0
- package/dist/core/providers/replicate.js.map +1 -0
- package/dist/core/providers/resolve.d.ts +3 -0
- package/dist/core/providers/resolve.d.ts.map +1 -0
- package/dist/core/providers/resolve.js +41 -0
- package/dist/core/providers/resolve.js.map +1 -0
- package/dist/core/providers/types.d.ts +29 -0
- package/dist/core/providers/types.d.ts.map +1 -0
- package/dist/core/providers/types.js +2 -0
- package/dist/core/providers/types.js.map +1 -0
- package/dist/core/scraper.d.ts +22 -0
- package/dist/core/scraper.d.ts.map +1 -0
- package/dist/core/scraper.js +138 -0
- package/dist/core/scraper.js.map +1 -0
- package/dist/core/walkthrough.d.ts +10 -0
- package/dist/core/walkthrough.d.ts.map +1 -0
- package/dist/core/walkthrough.js +116 -0
- package/dist/core/walkthrough.js.map +1 -0
- package/dist/schema/config.d.ts +45 -0
- package/dist/schema/config.d.ts.map +1 -0
- package/dist/schema/config.js +36 -0
- package/dist/schema/config.js.map +1 -0
- package/dist/schema/manifest.d.ts +43 -0
- package/dist/schema/manifest.d.ts.map +1 -0
- package/dist/schema/manifest.js +5 -0
- package/dist/schema/manifest.js.map +1 -0
- package/dist/schema/style-meta.d.ts +8 -0
- package/dist/schema/style-meta.d.ts.map +1 -0
- package/dist/schema/style-meta.js +2 -0
- package/dist/schema/style-meta.js.map +1 -0
- package/dist/styles/installer.d.ts +2 -0
- package/dist/styles/installer.d.ts.map +1 -0
- package/dist/styles/installer.js +43 -0
- package/dist/styles/installer.js.map +1 -0
- package/dist/styles/registry.d.ts +20 -0
- package/dist/styles/registry.d.ts.map +1 -0
- package/dist/styles/registry.js +96 -0
- package/dist/styles/registry.js.map +1 -0
- package/dist/util/auth-store.d.ts +12 -0
- package/dist/util/auth-store.d.ts.map +1 -0
- package/dist/util/auth-store.js +46 -0
- package/dist/util/auth-store.js.map +1 -0
- package/dist/util/id.d.ts +2 -0
- package/dist/util/id.d.ts.map +1 -0
- package/dist/util/id.js +14 -0
- package/dist/util/id.js.map +1 -0
- package/dist/util/json.d.ts +3 -0
- package/dist/util/json.d.ts.map +1 -0
- package/dist/util/json.js +9 -0
- package/dist/util/json.js.map +1 -0
- package/dist/util/open.d.ts +2 -0
- package/dist/util/open.d.ts.map +1 -0
- package/dist/util/open.js +21 -0
- package/dist/util/open.js.map +1 -0
- package/dist/util/paths.d.ts +9 -0
- package/dist/util/paths.d.ts.map +1 -0
- package/dist/util/paths.js +21 -0
- package/dist/util/paths.js.map +1 -0
- package/package.json +37 -0
- package/studio/assets/index-BgiuT_Mv.css +1 -0
- package/studio/assets/index-DnN633_x.js +9 -0
- package/studio/index.html +13 -0
- package/styles/clean-premium/meta.json +7 -0
- 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 @@
|
|
|
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
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5F,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,eAAe;AACf,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,CAC5C,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,CACvC,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,2BAA2B,CAAC,CAAC,CAAC;AAE7D,uBAAuB;AACvB,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAC5B,QAAQ,EACR;IACE,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IAC/B,mBAAmB,EAAE,OAAO,CAAC,OAAO,CAAC,sBAAsB,CAAC;IAC5D,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;CACjE,EACD,CAAC,EAAE,GAAG,EAAE,mBAAmB,EAAE,OAAO,EAAE,EAAE,EAAE,CACxC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,CACrB,aAAa,CAAC,GAAG,EAAE,EAAE,mBAAmB,EAAE,OAAO,EAAE,CAAC,CACrD,CACJ,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,sCAAsC,CAAC,CAAC,CAAC;AAExE,iBAAiB;AACjB,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,EAAE,GAAG,EAAE,CAChD,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,aAAa,EAAE,CAAC,CACzC,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,8CAA8C,CAAC,CAAC,CAAC;AAEhF,+BAA+B;AAC/B,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAC7B,SAAS,EACT;IACE,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;CAC7C,EACD,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,CACtE,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,4CAA4C,CAAC,CAAC,CAAC;AAE9E,mBAAmB;AACnB,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAC9B,UAAU,EACV;IACE,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;IACrD,UAAU,EAAE,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;IAC7D,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAChE,OAAO,CAAC,WAAW,CAAC,MAAe,CAAC,CACrC;IACD,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;IAChE,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC;IAC7B,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;IACnD,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAC9D,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CACtE,OAAO,CAAC,WAAW,CAAC,QAAiB,CAAC,CACvC;IACD,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;IACtE,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAClF,OAAO,CAAC,QAAQ,CACjB;IACD,eAAe,EAAE,OAAO,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CACzE,OAAO,CAAC,WAAW,CAAC,QAAiB,CAAC,CACvC;IACD,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,CAAC,IAAI,CAC5D,OAAO,CAAC,WAAW,CAAC,WAAoB,CAAC,CAC1C;CACF,EACD,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,EAAE,EAAE,CACxH,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,CACrB,eAAe,CAAC;IACd,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC;IACrC,UAAU,EAAE,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC;IAC7C,OAAO;IACP,WAAW,EAAE,MAAM,CAAC,cAAc,CAAC,WAAW,CAAC;IAC/C,IAAI;IACJ,KAAK,EAAE,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC;IACnC,MAAM;IACN,QAAQ;IACR,OAAO;IACP,QAAQ,EAAE,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC;IACzC,eAAe;IACf,IAAI,EAAE,IAAkC;CACzC,CAAC,CACH,CACJ,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,gCAAgC,CAAC,CAAC,CAAC;AAElE,wBAAwB;AACxB,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAC1B,MAAM,EACN;IACE,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IACpC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;CAC3F,EACD,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE;IAClE,QAAQ,EAAE,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC;CAC1C,CAAC,CAAC,CACJ,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,wCAAwC,CAAC,CAAC,CAAC;AAE1E,wBAAwB;AACxB,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAC1B,MAAM,EACN;IACE,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;CACzD,EACD,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAClF,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,wCAAwC,CAAC,CAAC,CAAC;AAE1E,iBAAiB;AACjB,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAC5B,QAAQ,EACR;IACE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;IACjE,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAC7D,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;CACnC,EACD,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CACzB,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,CACrB,aAAa,CAAC;IACZ,IAAI;IACJ,IAAI;IACJ,IAAI,EAAE,CAAC,MAAM;CACd,CAAC,CACH,CACJ,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,8BAA8B,CAAC,CAAC,CAAC;AAEhE,eAAe;AACf,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,EAAE,GAAG,EAAE,CACpD,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,iBAAiB,EAAE,CAAC,CAC7C,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,mCAAmC,CAAC,CAAC,CAAC;AAErE,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAC/B,OAAO,EACP;IACE,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;IACtD,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;CACvD,EACD,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,CACrB,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,CACrB,gBAAgB,CAAC;IACf,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC;IACrC,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC;CACtC,CAAC,CACH,CACJ,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,kCAAkC,CAAC,CAAC,CAAC;AAEpE,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,EAAE,GAAG,EAAE,CACpD,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,iBAAiB,EAAE,CAAC,CAC7C,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,oBAAoB,CAAC,CAAC,CAAC;AAEtD,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,CAC5C,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAC7B,CAAC,IAAI,CACJ,OAAO,CAAC,eAAe,CAAC,6BAA6B,CAAC,EACtD,OAAO,CAAC,eAAe,CAAC,CAAC,aAAa,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC,CACtE,CAAC;AAEF,0CAA0C;AAC1C,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAC9B,UAAU,EACV;IACE,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IACtC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC;CAC3B,EACD,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,CACnB,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CACzD,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,+CAA+C,CAAC,CAAC,CAAC;AAEjF,yCAAyC;AACzC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,CAC3C,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,cAAc,EAAE,CAAC,CAC1C,CAAC,IAAI,CACJ,OAAO,CAAC,eAAe,CAAC,2CAA2C,CAAC,EACpE,OAAO,CAAC,eAAe,CAAC;IACtB,OAAO;IACP,SAAS;IACT,WAAW;IACX,OAAO;IACP,OAAO;IACP,SAAS;IACT,OAAO;IACP,WAAW;IACX,SAAS;IACT,UAAU;CACX,CAAC,CACH,CAAC;AAEF,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE;IAC7B,IAAI,EAAE,OAAO;IACb,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC"}
|