screencraft 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/.claude/settings.local.json +30 -0
- package/.env.example +3 -0
- package/MCP_README.md +200 -0
- package/README.md +148 -0
- package/bin/screencraft.js +61 -0
- package/package.json +31 -0
- package/src/auth/keystore.js +148 -0
- package/src/commands/init.js +119 -0
- package/src/commands/launch.js +405 -0
- package/src/detectors/detectBrand.js +1222 -0
- package/src/detectors/simulator.js +317 -0
- package/src/generators/analyzeStyleReference.js +471 -0
- package/src/generators/compositePSD.js +682 -0
- package/src/generators/copy.js +147 -0
- package/src/mcp/index.js +394 -0
- package/src/pipeline/aeSwap.js +369 -0
- package/src/pipeline/download.js +32 -0
- package/src/pipeline/queue.js +101 -0
- package/src/server/index.js +627 -0
- package/src/server/public/app.js +738 -0
- package/src/server/public/index.html +255 -0
- package/src/server/public/style.css +751 -0
- package/src/server/session.js +36 -0
- package/templates/ae/(Footage)/Assets/This Hip-Hop Upbeat (Short version).wav +0 -0
- package/templates/ae/(Footage)/Assets/screen_01_raw.png +0 -0
- package/templates/ae/(Footage)/Assets/screen_02_raw.png +0 -0
- package/templates/ae/(Footage)/Assets/screen_03_raw.png +0 -0
- package/templates/ae/(Footage)/Assets/screen_04_raw.png +0 -0
- package/templates/ae/(Footage)/Assets/screen_05_raw.png +0 -0
- package/templates/ae/(Footage)/Assets/screen_06_raw.png +0 -0
- package/templates/ae/Motion Forge Test 1.0 (converted).aep +0 -0
- package/templates/ae_swap.jsx +284 -0
- package/templates/layouts/minimal.psd +0 -0
- package/templates/screencraft.config.example.js +165 -0
- package/test/output/layout_test.png +0 -0
- package/test/output/style_profile.json +64 -0
- package/test/reference.png +0 -0
- package/test/test_brand.js +69 -0
- package/test/test_psd.js +83 -0
- package/test/test_style_analysis.js +114 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(node -e:*)",
|
|
5
|
+
"Bash(node:*)",
|
|
6
|
+
"Bash(npm install:*)",
|
|
7
|
+
"Bash(xcrun simctl:*)",
|
|
8
|
+
"Bash(ls:*)",
|
|
9
|
+
"Bash('/Applications/Adobe After Effects 2026/aerender' -help)",
|
|
10
|
+
"Bash('/Applications/Adobe After Effects 2026/aerender' -project '/tmp/test-ae-output/source/app-launch.aep' -comp \"Screenshot_scene_1\" -RStemplate \"Best Settings\" -output '/tmp/test-ae-output/video/preview.mov' -v ERRORS_AND_PROGRESS -continueOnMissingFootage)",
|
|
11
|
+
"Bash(open:*)",
|
|
12
|
+
"Bash(find:*)",
|
|
13
|
+
"Bash('/Applications/Adobe After Effects 2026/aerender' -project '/tmp/test-ae-wild/source/app-launch.aep' -s '/tmp/test-ae-wild/source/ae_swap.jsx' -close SAVE_CHANGES -v ERRORS_AND_PROGRESS)",
|
|
14
|
+
"Bash('/Applications/Adobe After Effects 2026/aerender' -project '/tmp/test-ae-wild/source/app-launch.aep' -comp \"MASTER_app_launch\" -s '/tmp/test-ae-wild/source/ae_swap.jsx' -e 1 -close SAVE_CHANGES -v ERRORS_AND_PROGRESS)",
|
|
15
|
+
"Bash('/Applications/Adobe After Effects 2026/aerender' -project '/tmp/test-ae-wild/source/app-launch.aep' -comp \"MASTER_app_launch\" -s '/tmp/test-ae-wild/source/ae_swap.jsx' -output '/tmp/test-ae-wild/video/preview.mp4' -RStemplate \"Best Settings\" -v ERRORS_AND_PROGRESS -continueOnMissingFootage)",
|
|
16
|
+
"Bash(osascript:*)",
|
|
17
|
+
"Bash(kill:*)",
|
|
18
|
+
"Bash(pkill:*)",
|
|
19
|
+
"Bash('/Applications/Adobe After Effects 2026/aerender' -project '/tmp/test-ae-wild/source/app-launch.aep' -comp \"MASTER_app_launch\" -output '/tmp/test-ae-wild/video/preview.mp4' -RStemplate \"Best Settings\" -v ERRORS_AND_PROGRESS -continueOnMissingFootage)",
|
|
20
|
+
"Bash('/Applications/Adobe After Effects 2026/aerender' -project '/tmp/test-ae-final/source/app-launch.aep' -comp \"MASTER_app_launch\" -output '/tmp/test-ae-final/video/preview.mp4' -RStemplate \"Best Settings\" -v ERRORS_AND_PROGRESS -continueOnMissingFootage)",
|
|
21
|
+
"Bash(ffmpeg:*)",
|
|
22
|
+
"Bash(for:*)",
|
|
23
|
+
"Bash(do)",
|
|
24
|
+
"Bash(base=\"$f%.mp4\")",
|
|
25
|
+
"Bash(echo:*)",
|
|
26
|
+
"Bash(done)",
|
|
27
|
+
"Bash(sips -g hasAlpha:*)"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
}
|
package/.env.example
ADDED
package/MCP_README.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# ScreenCraft MCP Server
|
|
2
|
+
|
|
3
|
+
ScreenCraft plugs directly into **Claude Code**, **Claude Desktop**, and **Claude Cowork** as an MCP (Model Context Protocol) server. This means your AI assistant can generate your entire App Store launch kit — screenshots, headlines, device mockups, PSDs, and preview video — without you leaving your coding session.
|
|
4
|
+
|
|
5
|
+
The key insight: **Claude already understands your app**. It's read your codebase, knows your features, and understands your users. ScreenCraft lets Claude turn that understanding into App Store assets, with zero additional API costs.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## What It Does
|
|
10
|
+
|
|
11
|
+
When ScreenCraft is connected as an MCP server, your AI assistant gets these tools:
|
|
12
|
+
|
|
13
|
+
| Tool | What It Does |
|
|
14
|
+
|------|-------------|
|
|
15
|
+
| `detect_brand` | Scans your project for brand colors, app icon, fonts, logo, and framework |
|
|
16
|
+
| `set_brand_overrides` | Lets Claude fix or refine colors/name based on what it knows from your code |
|
|
17
|
+
| `suggest_screenshots` | Claude recommends which screens to capture based on your app's routes and features |
|
|
18
|
+
| `set_headlines` | Claude writes App Store headline copy — the white text + accent word for each screen |
|
|
19
|
+
| `list_templates` | Shows available PSD layout templates |
|
|
20
|
+
| `set_template` | Picks a template for screenshot compositing |
|
|
21
|
+
| `open_ui` | Launches the visual web UI at localhost:3141, pre-filled with everything Claude set up |
|
|
22
|
+
| `generate_launch_kit` | Runs the full pipeline headlessly — no UI needed |
|
|
23
|
+
| `get_session_status` | Checks what's been configured and what's still needed |
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Setup (One Time)
|
|
28
|
+
|
|
29
|
+
### Option 1: Claude Code CLI
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
claude mcp add --transport stdio screencraft -- npx screencraft --mcp
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Option 2: Project config file
|
|
36
|
+
|
|
37
|
+
Add a `.mcp.json` file to your project root:
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"mcpServers": {
|
|
42
|
+
"screencraft": {
|
|
43
|
+
"type": "stdio",
|
|
44
|
+
"command": "npx",
|
|
45
|
+
"args": ["screencraft", "--mcp"]
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
This gets committed to git — anyone on your team who uses Claude Code gets ScreenCraft tools automatically.
|
|
52
|
+
|
|
53
|
+
### Option 3: Claude Desktop
|
|
54
|
+
|
|
55
|
+
Add to your Claude Desktop config (`claude_desktop_config.json`):
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"mcpServers": {
|
|
60
|
+
"screencraft": {
|
|
61
|
+
"command": "npx",
|
|
62
|
+
"args": ["screencraft", "--mcp"]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## How It Works
|
|
71
|
+
|
|
72
|
+
### The Flow
|
|
73
|
+
|
|
74
|
+
1. You're coding with Claude (Code, Desktop, or Cowork)
|
|
75
|
+
2. You say: **"Generate App Store screenshots for my app"**
|
|
76
|
+
3. Claude reads your codebase — it already knows your app's features, colors, and purpose
|
|
77
|
+
4. Claude calls ScreenCraft tools:
|
|
78
|
+
- `detect_brand` → finds your colors, icon, font from the project
|
|
79
|
+
- `set_headlines` → writes headline copy for each screen (no API call — Claude IS the AI)
|
|
80
|
+
- `suggest_screenshots` → recommends which screens to capture
|
|
81
|
+
- `open_ui` → opens the web UI with everything pre-filled
|
|
82
|
+
5. You review in the browser, capture screenshots from the Simulator, and click Generate
|
|
83
|
+
6. Done — composited App Store PNGs, layered PSDs, AE project, and preview video
|
|
84
|
+
|
|
85
|
+
### Why This Is Different
|
|
86
|
+
|
|
87
|
+
Traditional approach: Screenshot tool asks you to type headlines, pick colors, configure everything manually. Or it calls an AI API (costing tokens) to analyze your app from scratch.
|
|
88
|
+
|
|
89
|
+
ScreenCraft + MCP approach: Claude already has full context on your app from your coding session. It generates the headlines, suggests the right screens, and knows your brand — then pushes all of that into ScreenCraft. **Zero extra AI cost. Zero manual config.**
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Example Prompts
|
|
94
|
+
|
|
95
|
+
Once MCP is connected, try these in Claude Code or Claude Desktop:
|
|
96
|
+
|
|
97
|
+
### Full launch kit
|
|
98
|
+
> Generate my App Store launch kit. Detect my brand, write headlines for 6 key screens, and open the ScreenCraft UI so I can capture screenshots.
|
|
99
|
+
|
|
100
|
+
### Just headlines
|
|
101
|
+
> Write App Store headline copy for my app. I need a short white text phrase and an accent word for each of these screens: home, settings, onboarding, profile, analytics, pricing.
|
|
102
|
+
|
|
103
|
+
### Brand check
|
|
104
|
+
> Run ScreenCraft brand detection on my project and tell me what colors, icon, and font it found. Fix anything that looks wrong.
|
|
105
|
+
|
|
106
|
+
### Headless generation
|
|
107
|
+
> I already have screenshots in my screenshots/ folder. Detect my brand, write headlines, and generate the launch kit without opening the UI.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## What Gets Generated
|
|
112
|
+
|
|
113
|
+
| Output | Free Tier | Licensed |
|
|
114
|
+
|--------|-----------|----------|
|
|
115
|
+
| Composited App Store PNGs (1290x2796) | Yes | Yes |
|
|
116
|
+
| Raw simulator screenshots | Yes | Yes |
|
|
117
|
+
| Brand assets (icon, logo, font) | Yes | Yes |
|
|
118
|
+
| Layered PSD files | — | Yes |
|
|
119
|
+
| After Effects project (.aep) | — | Yes |
|
|
120
|
+
| App Store preview video (.mp4) | — | Yes |
|
|
121
|
+
|
|
122
|
+
Output lands in `your-project/launch-kit/`.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Prerequisites
|
|
127
|
+
|
|
128
|
+
- **Node.js 18+**
|
|
129
|
+
- **ScreenCraft installed**: `npm install -g screencraft`
|
|
130
|
+
- **For screenshot capture**: Xcode with your app running in the iOS Simulator (build with Cmd+R before generating)
|
|
131
|
+
- **For video rendering** (optional): Adobe After Effects 2024-2026 with scripting enabled
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Architecture
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
Your AI Session (Claude Code / Desktop / Cowork)
|
|
139
|
+
│
|
|
140
|
+
│ MCP tools (stdio)
|
|
141
|
+
▼
|
|
142
|
+
ScreenCraft MCP Server (src/mcp/index.js)
|
|
143
|
+
│
|
|
144
|
+
│ Shared session state
|
|
145
|
+
▼
|
|
146
|
+
ScreenCraft Web UI (localhost:3141)
|
|
147
|
+
│
|
|
148
|
+
│ Calls existing pipeline
|
|
149
|
+
▼
|
|
150
|
+
┌─────────────────────────────────┐
|
|
151
|
+
│ Brand Detection │
|
|
152
|
+
│ Screenshot Capture (Simulator) │
|
|
153
|
+
│ PSD Compositing (sharp/ag-psd)│
|
|
154
|
+
│ AE Project Prep + Render │
|
|
155
|
+
└─────────────────────────────────┘
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The MCP server and web UI share the same session. MCP tools populate brand data, headlines, and preferences. The web UI reads that state and presents it visually. The user reviews, captures screenshots, and triggers generation.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Registering as an Official MCP Server
|
|
163
|
+
|
|
164
|
+
ScreenCraft can be published to the [Official MCP Registry](https://registry.modelcontextprotocol.io/) so users can discover it alongside 19,000+ other MCP servers.
|
|
165
|
+
|
|
166
|
+
Steps:
|
|
167
|
+
1. Publish the npm package (`npm publish`)
|
|
168
|
+
2. Create a `server.json` with registry metadata
|
|
169
|
+
3. Authenticate namespace via GitHub or DNS verification for screencraft.dev
|
|
170
|
+
4. Submit via the MCP publisher CLI
|
|
171
|
+
|
|
172
|
+
Once registered, ScreenCraft appears in the registry and downstream aggregators like mcp.so and mcpservers.org.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Troubleshooting
|
|
177
|
+
|
|
178
|
+
**"Tools not showing up in Claude Code"**
|
|
179
|
+
- Run `/mcp` in Claude Code to check server status
|
|
180
|
+
- Make sure `npx screencraft --mcp` works standalone (should produce no output — it's waiting for stdio messages)
|
|
181
|
+
|
|
182
|
+
**"Brand detection found wrong colors"**
|
|
183
|
+
- Use `set_brand_overrides` to fix them, or edit in the web UI
|
|
184
|
+
- Add a `screencraft.config.js` to your project root for permanent overrides
|
|
185
|
+
|
|
186
|
+
**"Screenshots fail to capture"**
|
|
187
|
+
- Build and run your app in the Simulator (Xcode Cmd+R) before capturing
|
|
188
|
+
- ScreenCraft detects the running simulator — it won't boot a blank one
|
|
189
|
+
|
|
190
|
+
**GNotificationCenterDelegate warning in terminal**
|
|
191
|
+
- Harmless. Caused by canvas and sharp both bundling libgio. Does not affect functionality.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Links
|
|
196
|
+
|
|
197
|
+
- [ScreenCraft Website](https://screencraft.dev)
|
|
198
|
+
- [Get a License Key](https://screencraft.dev/activate)
|
|
199
|
+
- [MCP Protocol Docs](https://modelcontextprotocol.io)
|
|
200
|
+
- [MCP Registry](https://registry.modelcontextprotocol.io)
|
package/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# ScreenCraft CLI
|
|
2
|
+
|
|
3
|
+
> App Store launch kit from one terminal command.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx screencraft launch
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Generates App Store screenshots, layered PSDs, and a preview video — automatically branded from your codebase.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## What it does
|
|
14
|
+
|
|
15
|
+
1. **Detects** your framework (Expo, React Native, Flutter, Swift, Android)
|
|
16
|
+
2. **Reads** brand colors + app name from your theme files — no config needed
|
|
17
|
+
3. **Captures** screenshots from the iOS/Android simulator (or uses `./screenshots/` folder)
|
|
18
|
+
4. **Suggests** App Store headline text via Claude AI — you approve each one
|
|
19
|
+
5. **Composites** screenshots into device mockups with your headline text baked in
|
|
20
|
+
6. **Renders** a 30s vertical preview video using your branded After Effects template
|
|
21
|
+
7. **Outputs** a complete `/launch-kit/` folder in your project root
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Output
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
your-app/
|
|
29
|
+
launch-kit/
|
|
30
|
+
screenshots/
|
|
31
|
+
screen_01.png ← App Store-ready PNG, 1290×2796px
|
|
32
|
+
screen_02.png
|
|
33
|
+
screen_03.png
|
|
34
|
+
screen_04.png
|
|
35
|
+
screen_05.png
|
|
36
|
+
video/
|
|
37
|
+
preview.mp4 ← 30s vertical preview (watermarked until activated)
|
|
38
|
+
source/
|
|
39
|
+
app-launch.aep ← Editable After Effects project
|
|
40
|
+
screen_01.psd ← Layered PSD (Pro tier)
|
|
41
|
+
...
|
|
42
|
+
README.md ← App Store submission checklist
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Tiers
|
|
48
|
+
|
|
49
|
+
| | Free | Launch ($49) | Pro ($99) |
|
|
50
|
+
|---|---|---|---|
|
|
51
|
+
| App Store PNGs | ✓ | ✓ | ✓ |
|
|
52
|
+
| Preview video (clean) | watermarked | ✓ | ✓ |
|
|
53
|
+
| After Effects file | — | ✓ | ✓ |
|
|
54
|
+
| Layered PSDs | — | — | ✓ |
|
|
55
|
+
| Social cut (1:1) | — | — | ✓ |
|
|
56
|
+
| App Store copy | — | — | ✓ |
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Quick Start
|
|
61
|
+
|
|
62
|
+
### Option A — Automated (with simulator)
|
|
63
|
+
|
|
64
|
+
Add routes to `screencraft.config.js` in your project root:
|
|
65
|
+
|
|
66
|
+
```js
|
|
67
|
+
module.exports = {
|
|
68
|
+
app: {
|
|
69
|
+
name: "YourApp",
|
|
70
|
+
tagline: "Your tagline here.",
|
|
71
|
+
cta: "Download Free",
|
|
72
|
+
},
|
|
73
|
+
screenshots: [
|
|
74
|
+
{ route: "/home", label: "Everything at a glance" },
|
|
75
|
+
{ route: "/features", label: "Built for how you work" },
|
|
76
|
+
{ route: "/profile", label: "Your space, your way" },
|
|
77
|
+
{ route: "/onboard", label: "Get started in seconds" },
|
|
78
|
+
{ route: "/settings", label: "Simple and powerful" },
|
|
79
|
+
],
|
|
80
|
+
};
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Then run:
|
|
84
|
+
```bash
|
|
85
|
+
npx screencraft launch
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Option B — Manual screenshots (fastest for testing)
|
|
89
|
+
|
|
90
|
+
1. Take screenshots on your phone or simulator
|
|
91
|
+
2. Place them in `./screenshots/` in your project root
|
|
92
|
+
3. Run `npx screencraft launch`
|
|
93
|
+
|
|
94
|
+
The CLI will pick them up automatically.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Commands
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npx screencraft launch # Full pipeline
|
|
102
|
+
npx screencraft launch --screenshots-only # Free tier (no video)
|
|
103
|
+
npx screencraft activate MF-XXXX-XXXX # Store license key
|
|
104
|
+
npx screencraft status # Check credits
|
|
105
|
+
npx screencraft init # Generate config file
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Testing
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# Test brand detection on your app project
|
|
114
|
+
node test/test_brand.js /path/to/your/app
|
|
115
|
+
|
|
116
|
+
# Test PSD compositor (add a screenshot first)
|
|
117
|
+
cp /path/to/screenshot.png test/sample_screenshot.png
|
|
118
|
+
node test/test_psd.js
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Environment
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
ANTHROPIC_API_KEY=sk-ant-... # For AI headline suggestions
|
|
127
|
+
SCREENCRAFT_API=https://... # Override API endpoint
|
|
128
|
+
NODE_ENV=development # Dev mode (accepts MF-* test keys)
|
|
129
|
+
SCREENCRAFT_DEV=1 # Also enables dev mode
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Template Files (for motion designer)
|
|
135
|
+
|
|
136
|
+
Place these in `templates/`:
|
|
137
|
+
|
|
138
|
+
| File | Description |
|
|
139
|
+
|------|-------------|
|
|
140
|
+
| `templates/device_frame.png` | Transparent phone mockup overlay, 1130×1900px |
|
|
141
|
+
| `templates/screen_mask.png` | White shape of screen area (for clipping) |
|
|
142
|
+
| `templates/app-launch.aep` | After Effects template with swap layer naming |
|
|
143
|
+
|
|
144
|
+
See `TEMPLATE_SPEC.md` for the full AE build specification.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
screencraft.dev
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* bin/screencraft.js
|
|
4
|
+
* CLI entry point. Routes commands.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npx screencraft launch — full launch kit pipeline
|
|
8
|
+
* npx screencraft launch --screenshots-only — free tier, no video
|
|
9
|
+
* npx screencraft activate <key> — store license key
|
|
10
|
+
* npx screencraft status — show credits remaining
|
|
11
|
+
* npx screencraft serve — open web UI at localhost:3141
|
|
12
|
+
* npx screencraft init — generate screencraft.config.js
|
|
13
|
+
* npx screencraft --mcp — start MCP server (stdio)
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const path = require('path');
|
|
17
|
+
|
|
18
|
+
const [,, command, ...args] = process.argv;
|
|
19
|
+
|
|
20
|
+
// ── MCP mode — must run before banner (stdout is protocol) ───────
|
|
21
|
+
if (command === '--mcp' || process.argv.includes('--mcp')) {
|
|
22
|
+
require('../src/mcp').startMCP();
|
|
23
|
+
} else {
|
|
24
|
+
// Normal CLI mode
|
|
25
|
+
const chalk = require('chalk');
|
|
26
|
+
|
|
27
|
+
// ── Banner ──────────────────────────────────────────────────────
|
|
28
|
+
console.log('');
|
|
29
|
+
console.log(chalk.hex('#A78BFA')('◆ ScreenCraft') + chalk.dim(' v0.1.0'));
|
|
30
|
+
console.log(chalk.dim('─────────────────────────────────────'));
|
|
31
|
+
console.log('');
|
|
32
|
+
|
|
33
|
+
// ── Route ───────────────────────────────────────────────────────
|
|
34
|
+
switch (command) {
|
|
35
|
+
case 'launch':
|
|
36
|
+
require('../src/commands/launch')(args);
|
|
37
|
+
break;
|
|
38
|
+
case 'activate':
|
|
39
|
+
require('../src/auth/keystore').activate(args[0]);
|
|
40
|
+
break;
|
|
41
|
+
case 'status':
|
|
42
|
+
require('../src/auth/keystore').status();
|
|
43
|
+
break;
|
|
44
|
+
case 'serve':
|
|
45
|
+
require('../src/server').startServer();
|
|
46
|
+
break;
|
|
47
|
+
case 'init':
|
|
48
|
+
require('../src/commands/init')();
|
|
49
|
+
break;
|
|
50
|
+
default:
|
|
51
|
+
console.log(chalk.dim('Commands:'));
|
|
52
|
+
console.log(' ' + chalk.white('launch') + chalk.dim(' — generate launch kit'));
|
|
53
|
+
console.log(' ' + chalk.white('launch --screenshots-only') + chalk.dim(' — free tier'));
|
|
54
|
+
console.log(' ' + chalk.white('activate <key>') + chalk.dim(' — activate license'));
|
|
55
|
+
console.log(' ' + chalk.white('status') + chalk.dim(' — check credits'));
|
|
56
|
+
console.log(' ' + chalk.white('serve') + chalk.dim(' — open web UI'));
|
|
57
|
+
console.log(' ' + chalk.white('init') + chalk.dim(' — create config file'));
|
|
58
|
+
console.log(' ' + chalk.white('--mcp') + chalk.dim(' — start MCP server'));
|
|
59
|
+
console.log('');
|
|
60
|
+
}
|
|
61
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "screencraft",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "App launch kit generator — screenshots, PSDs, and preview video from one CLI command",
|
|
5
|
+
"main": "src/commands/launch.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"screencraft": "./bin/screencraft.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test:brand": "node test/test_brand.js",
|
|
11
|
+
"test:psd": "node test/test_psd.js",
|
|
12
|
+
"test:pipeline": "node test/test_pipeline.js",
|
|
13
|
+
"launch": "node bin/screencraft.js launch"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
17
|
+
"ag-psd": "^30.1.0",
|
|
18
|
+
"archiver": "^6.0.1",
|
|
19
|
+
"canvas": "^3.2.1",
|
|
20
|
+
"chalk": "^4.1.2",
|
|
21
|
+
"dotenv": "^16.0.3",
|
|
22
|
+
"express": "^5.2.1",
|
|
23
|
+
"inquirer": "^8.2.6",
|
|
24
|
+
"node-fetch": "^2.6.9",
|
|
25
|
+
"ora": "^5.4.1",
|
|
26
|
+
"sharp": "^0.33.0"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18.0.0"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/auth/keystore.js
|
|
3
|
+
* --------------------
|
|
4
|
+
* Manages license key storage in ~/.screencraft/config.json
|
|
5
|
+
* and validates keys against the ScreenCraft API (screencraft.dev).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const os = require('os');
|
|
11
|
+
const chalk = require('chalk');
|
|
12
|
+
|
|
13
|
+
const CONFIG_DIR = path.join(os.homedir(), '.screencraft');
|
|
14
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
15
|
+
const API_BASE = process.env.SCREENCRAFT_API || 'https://screencraft.dev/api';
|
|
16
|
+
|
|
17
|
+
// ── Read / Write ──────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
function readConfig() {
|
|
20
|
+
try {
|
|
21
|
+
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
22
|
+
} catch {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function writeConfig(data) {
|
|
28
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
29
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(data, null, 2));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function getStoredKey() {
|
|
33
|
+
return readConfig().licenseKey || null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ── Activate ──────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
async function activate(key) {
|
|
39
|
+
if (!key) {
|
|
40
|
+
console.log(chalk.hex('#C9A84C')(' Usage: screencraft activate <license-key>'));
|
|
41
|
+
console.log(chalk.dim(' Get a key at screencraft.dev'));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
console.log(chalk.dim(' Validating key...'));
|
|
46
|
+
const result = await validateKey(key);
|
|
47
|
+
|
|
48
|
+
if (result) {
|
|
49
|
+
const config = readConfig();
|
|
50
|
+
config.licenseKey = key;
|
|
51
|
+
config.tier = result.tier;
|
|
52
|
+
config.activatedAt = new Date().toISOString();
|
|
53
|
+
writeConfig(config);
|
|
54
|
+
|
|
55
|
+
console.log(chalk.hex('#6BC46A')(` ✓ Activated — ${result.tier} tier`));
|
|
56
|
+
console.log(chalk.dim(` ${result.generationsRemaining} generation${result.generationsRemaining !== 1 ? 's' : ''} remaining`));
|
|
57
|
+
console.log(chalk.dim(' Key saved to ~/.screencraft/config.json'));
|
|
58
|
+
} else {
|
|
59
|
+
console.log(chalk.hex('#C9A84C')(' ✗ Invalid or expired key'));
|
|
60
|
+
console.log(chalk.dim(' Visit screencraft.dev to get a valid key'));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ── Status ────────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
async function status() {
|
|
67
|
+
const config = readConfig();
|
|
68
|
+
|
|
69
|
+
if (!config.licenseKey) {
|
|
70
|
+
console.log(chalk.dim(' No license key found.'));
|
|
71
|
+
console.log(chalk.dim(' Run: screencraft activate <key>'));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
console.log(chalk.dim(' Key: ') + chalk.white(maskKey(config.licenseKey)));
|
|
76
|
+
console.log(chalk.dim(' Tier: ') + chalk.white(config.tier || 'unknown'));
|
|
77
|
+
console.log(chalk.dim(' Activated: ') + chalk.white(config.activatedAt?.split('T')[0] || 'unknown'));
|
|
78
|
+
|
|
79
|
+
// Check remaining generations
|
|
80
|
+
const result = await validateKey(config.licenseKey);
|
|
81
|
+
if (result) {
|
|
82
|
+
console.log(chalk.dim(' Remaining: ') + chalk.white(`${result.generationsRemaining} generation${result.generationsRemaining !== 1 ? 's' : ''}`));
|
|
83
|
+
} else {
|
|
84
|
+
console.log(chalk.dim(' Status: ') + chalk.hex('#C9A84C')('expired or all generations used'));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ── Validate key against API ──────────────────────────────────────
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Returns { tier, generationsRemaining } or null if invalid.
|
|
92
|
+
*/
|
|
93
|
+
async function validateKey(key) {
|
|
94
|
+
if (!key) return null;
|
|
95
|
+
|
|
96
|
+
// Dev mode: accept SC- prefixed keys for testing
|
|
97
|
+
const isDev = process.env.NODE_ENV === 'development' || process.env.SCREENCRAFT_DEV;
|
|
98
|
+
if (isDev && key.startsWith('SC-')) {
|
|
99
|
+
const tier = key.includes('PRO') ? 'pro' : 'launch';
|
|
100
|
+
return { tier, generationsRemaining: tier === 'pro' ? 10 : 1 };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
const res = await fetch(`${API_BASE}/keys/validate`, {
|
|
105
|
+
method: 'POST',
|
|
106
|
+
headers: { 'Content-Type': 'application/json' },
|
|
107
|
+
body: JSON.stringify({ key }),
|
|
108
|
+
signal: AbortSignal.timeout(5000),
|
|
109
|
+
});
|
|
110
|
+
if (!res.ok) return null;
|
|
111
|
+
const data = await res.json();
|
|
112
|
+
return data.valid ? { tier: data.tier, generationsRemaining: data.generationsRemaining } : null;
|
|
113
|
+
} catch {
|
|
114
|
+
if (isDev) {
|
|
115
|
+
console.log(chalk.dim(' (dev mode: API unreachable, key accepted)'));
|
|
116
|
+
return { tier: 'launch', generationsRemaining: 1 };
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Consume one generation after a successful render.
|
|
124
|
+
*/
|
|
125
|
+
async function useGeneration(key) {
|
|
126
|
+
if (!key) return false;
|
|
127
|
+
try {
|
|
128
|
+
const res = await fetch(`${API_BASE}/keys/use`, {
|
|
129
|
+
method: 'POST',
|
|
130
|
+
headers: { 'Content-Type': 'application/json' },
|
|
131
|
+
body: JSON.stringify({ key }),
|
|
132
|
+
signal: AbortSignal.timeout(5000),
|
|
133
|
+
});
|
|
134
|
+
const data = await res.json();
|
|
135
|
+
return data.ok;
|
|
136
|
+
} catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ── Helpers ───────────────────────────────────────────────────────
|
|
142
|
+
|
|
143
|
+
function maskKey(key) {
|
|
144
|
+
if (!key || key.length < 8) return '****';
|
|
145
|
+
return key.slice(0, 6) + '****' + key.slice(-4);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
module.exports = { activate, status, getStoredKey, validateKey, useGeneration };
|