ship-create 1.3.1 → 1.5.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/create.mjs +347 -166
- package/package.json +1 -1
- package/templates/.claude/commands/build.md +134 -0
- package/templates/.claude/skills/uiux-frontend/SKILL.md +321 -0
- package/templates/.cursorrules +41 -24
- package/templates/.windsurfrules +41 -24
- package/templates/AGENTS.md +41 -24
- package/templates/CLAUDE.md +41 -24
- package/templates/docs/DESIGN_SPEC.md +50 -0
- package/templates/docs/DESIGN_SYSTEM.md +145 -0
- package/templates/docs/tech-stack/STACK_DECISION_MATRIX.md +288 -0
- package/templates/docs/tech-stack/TECH_STACK.md +492 -0
package/create.mjs
CHANGED
|
@@ -5,18 +5,23 @@
|
|
|
5
5
|
* Why no npm dependencies: the audience is non-developers. Requiring
|
|
6
6
|
* extra packages before the very first command, or an API key before
|
|
7
7
|
* anything works, is exactly the kind of friction that loses beginners.
|
|
8
|
-
* This script uses only Node.js built-ins (fs, path, readline
|
|
9
|
-
*
|
|
10
|
-
* already use — no API key, no extra setup.
|
|
8
|
+
* This script uses only Node.js built-ins (fs, path, readline,
|
|
9
|
+
* child_process) — no API key, no extra setup.
|
|
11
10
|
*
|
|
12
11
|
* Usage:
|
|
13
12
|
* npx ship-create
|
|
14
13
|
*
|
|
15
|
-
* All templates
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
14
|
+
* All templates (starter-kit app, agent rule files, Claude skill) are
|
|
15
|
+
* bundled inside this package's own templates/ folder — nothing is read
|
|
16
|
+
* from outside, so it works standalone via npx.
|
|
17
|
+
*
|
|
18
|
+
* What changed from v1.3.1 → v1.4.0:
|
|
19
|
+
* - Removed the AI-tool picker (no longer needed)
|
|
20
|
+
* - Added 4 idea questions so PROJECT.md + HUMAN_FLOW.md are pre-filled
|
|
21
|
+
* with real content (no [bracket placeholders] in the core sections)
|
|
22
|
+
* - Runs `npm install` automatically so the project is ready to open
|
|
23
|
+
* - End message tells the user exactly how to start building in their
|
|
24
|
+
* AI coding tool — no intermediate manual steps
|
|
20
25
|
*/
|
|
21
26
|
|
|
22
27
|
import fs from "node:fs";
|
|
@@ -24,28 +29,76 @@ import path from "node:path";
|
|
|
24
29
|
import readline from "node:readline";
|
|
25
30
|
import { stdin as input, stdout as output } from "node:process";
|
|
26
31
|
import { fileURLToPath } from "node:url";
|
|
32
|
+
import { spawnSync } from "node:child_process";
|
|
27
33
|
|
|
28
34
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
29
35
|
const TEMPLATES_DIR = path.join(__dirname, "templates");
|
|
30
36
|
|
|
31
37
|
const PRODUCT_TYPES = [
|
|
32
|
-
{ key: "SAAS",
|
|
33
|
-
{ key: "CRM",
|
|
34
|
-
{ key: "MEMBERSHIP",
|
|
35
|
-
{ key: "LEADGEN",
|
|
36
|
-
{ key: "DIRECTORY",
|
|
37
|
-
{ key: "DASHBOARD",
|
|
38
|
-
{ key: "INTERNAL_TOOL", label: "Internal tool",
|
|
39
|
-
{ key: "MARKETPLACE",
|
|
38
|
+
{ key: "SAAS", label: "SaaS product", file: "SAAS_TEMPLATE.md" },
|
|
39
|
+
{ key: "CRM", label: "CRM system", file: "CRM_TEMPLATE.md" },
|
|
40
|
+
{ key: "MEMBERSHIP", label: "Membership site", file: "MEMBERSHIP_TEMPLATE.md" },
|
|
41
|
+
{ key: "LEADGEN", label: "Lead generation website", file: "LEADGEN_TEMPLATE.md" },
|
|
42
|
+
{ key: "DIRECTORY", label: "Directory website", file: "DIRECTORY_TEMPLATE.md" },
|
|
43
|
+
{ key: "DASHBOARD", label: "Dashboard / internal analytics", file: "DASHBOARD_TEMPLATE.md" },
|
|
44
|
+
{ key: "INTERNAL_TOOL", label: "Internal tool", file: "INTERNAL_TOOL_TEMPLATE.md" },
|
|
45
|
+
{ key: "MARKETPLACE", label: "Marketplace", file: "MARKETPLACE_TEMPLATE.md" },
|
|
40
46
|
];
|
|
41
47
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
// Pre-seeded journey stages per product type so HUMAN_FLOW.md has real
|
|
49
|
+
// content the moment the project is created.
|
|
50
|
+
const JOURNEY_SEEDS = {
|
|
51
|
+
SAAS: {
|
|
52
|
+
discovery: "Googles a pain they have, or sees a post about the tool from someone they follow",
|
|
53
|
+
firstUse: "Signs up, sets up their workspace, completes onboarding in under 5 minutes",
|
|
54
|
+
habitual: "Comes back daily — it's now the default tool for this part of their work",
|
|
55
|
+
advocacy: "Invites teammates, shares a screenshot publicly, or recommends it unprompted",
|
|
56
|
+
},
|
|
57
|
+
CRM: {
|
|
58
|
+
discovery: "Frustrated with spreadsheets or outgrowing their current CRM",
|
|
59
|
+
firstUse: "Imports contacts, creates their first deal, moves it through the pipeline",
|
|
60
|
+
habitual: "Logs every interaction, tracks deals from lead to close without thinking about it",
|
|
61
|
+
advocacy: "Gets their whole sales team on it, pushes for it in team meetings",
|
|
62
|
+
},
|
|
63
|
+
MEMBERSHIP: {
|
|
64
|
+
discovery: "Finds the creator/brand through content, a recommendation, or an ad",
|
|
65
|
+
firstUse: "Signs up for free or paid tier, accesses their first piece of content",
|
|
66
|
+
habitual: "Returns regularly for new content and engages in the community",
|
|
67
|
+
advocacy: "Refers friends, shares content, or upgrades to a higher tier",
|
|
68
|
+
},
|
|
69
|
+
LEADGEN: {
|
|
70
|
+
discovery: "Finds the page via search, ad, or referral while actively looking for a solution",
|
|
71
|
+
firstUse: "Reads the page, fills out a form or books a call",
|
|
72
|
+
habitual: "Becomes a paying customer after the follow-up sequence",
|
|
73
|
+
advocacy: "Refers others in their network who need the same service",
|
|
74
|
+
},
|
|
75
|
+
DIRECTORY: {
|
|
76
|
+
discovery: "Searches for a specific provider or category in the niche",
|
|
77
|
+
firstUse: "Browses listings, contacts or books a provider directly",
|
|
78
|
+
habitual: "Returns when they need another provider, or to leave a review",
|
|
79
|
+
advocacy: "Shares the directory with their network when someone asks for a recommendation",
|
|
80
|
+
},
|
|
81
|
+
DASHBOARD: {
|
|
82
|
+
discovery: "Team lead or ops person is tired of copy-pasting data into spreadsheets",
|
|
83
|
+
firstUse: "Connects data sources, sees the first charts populate automatically",
|
|
84
|
+
habitual: "Checks the dashboard every morning as the first thing in their routine",
|
|
85
|
+
advocacy: "Shares views with stakeholders, adds more team members as viewers",
|
|
86
|
+
},
|
|
87
|
+
INTERNAL_TOOL: {
|
|
88
|
+
discovery: "Team identifies a manual, repetitive process that's slowing them down",
|
|
89
|
+
firstUse: "First team member completes the core task in the tool, faster than before",
|
|
90
|
+
habitual: "Whole team uses it as the default — the old process is gone",
|
|
91
|
+
advocacy: "Other teams ask if they can get a version for their workflow too",
|
|
92
|
+
},
|
|
93
|
+
MARKETPLACE: {
|
|
94
|
+
discovery: "Buyer searches for a product or service and lands on a listing",
|
|
95
|
+
firstUse: "Browses listings, makes first purchase or sends first inquiry",
|
|
96
|
+
habitual: "Returns to buy again, or becomes a repeat seller",
|
|
97
|
+
advocacy: "Refers buyers and sellers in their network",
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// ─── helpers ────────────────────────────────────────────────────────────────
|
|
49
102
|
|
|
50
103
|
function toKebabCase(str) {
|
|
51
104
|
return str
|
|
@@ -55,11 +108,9 @@ function toKebabCase(str) {
|
|
|
55
108
|
.replace(/(^-|-$)/g, "") || "my-product";
|
|
56
109
|
}
|
|
57
110
|
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
// first question() call resolves. The async-iterator pattern reliably
|
|
62
|
-
// delivers every line regardless of how stdin is supplied.
|
|
111
|
+
// Use the readline async iterator rather than the `question()` API to avoid
|
|
112
|
+
// a Node.js quirk where piped / non-TTY stdin stops delivering buffered
|
|
113
|
+
// lines after the first question() call resolves.
|
|
63
114
|
function makeLineReader(rl) {
|
|
64
115
|
const it = rl[Symbol.asyncIterator]();
|
|
65
116
|
return async function nextLine(promptText) {
|
|
@@ -101,31 +152,203 @@ function copyRecursiveExcluding(src, dest, excludeNames) {
|
|
|
101
152
|
}
|
|
102
153
|
}
|
|
103
154
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
155
|
+
// ─── doc builders ───────────────────────────────────────────────────────────
|
|
156
|
+
|
|
157
|
+
function buildProjectMd(projectName, productTypeLabel, { targetUser, problem, valueProp, idea }) {
|
|
158
|
+
return `# PROJECT.md
|
|
159
|
+
|
|
160
|
+
**Phase:** S — Structure
|
|
161
|
+
**Purpose:** Single source of truth for *what* you're building and *why*. Every AI build prompt should point here instead of re-explaining the product from scratch.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## 1. Product Vision
|
|
166
|
+
|
|
167
|
+
${projectName} is a ${productTypeLabel.toLowerCase()} that helps **${targetUser}** ${valueProp}.
|
|
168
|
+
|
|
169
|
+
> *Expand this into a fuller paragraph before moving to HUMAN_FLOW.md — what does this product become in 2 years if it succeeds?*
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## 2. Problem Statement
|
|
174
|
+
|
|
175
|
+
- **Who has this problem:** ${targetUser}
|
|
176
|
+
- **What is the problem, specifically:** ${problem}
|
|
177
|
+
- **How do they solve it today** (workarounds, competitors, spreadsheets, manual labor): [fill in]
|
|
178
|
+
- **Why do existing solutions fail them:** [fill in]
|
|
179
|
+
- **Cost of the problem** (time, money, missed opportunity, stress — quantify if possible): [fill in]
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 3. Opportunity
|
|
184
|
+
|
|
185
|
+
- **Why now** (market shift, new tech, behavior change, AI cost drop, etc.): [fill in]
|
|
186
|
+
- **Market size / addressable audience** (rough is fine): [fill in]
|
|
187
|
+
- **Unfair advantage** (your access, audience, data, speed, niche knowledge): [fill in]
|
|
188
|
+
- **Why AI makes this newly buildable by you, solo or with a small team:** [fill in]
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## 4. Target Audience
|
|
193
|
+
|
|
194
|
+
| Attribute | Description |
|
|
195
|
+
|---|---|
|
|
196
|
+
| Primary segment | ${targetUser} |
|
|
197
|
+
| Secondary segment | [fill in] |
|
|
198
|
+
| Demographics / firmographics | [fill in] |
|
|
199
|
+
| Where they hang out (channels) | [fill in] |
|
|
200
|
+
| What they currently pay for adjacent solutions | [fill in] |
|
|
201
|
+
| Buying trigger (what makes them search for this *today*) | [fill in] |
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## 5. User Personas
|
|
206
|
+
|
|
207
|
+
### Persona 1: Primary User
|
|
208
|
+
- **Goal:** ${valueProp}
|
|
209
|
+
- **Frustration:** ${problem}
|
|
210
|
+
- **Tech comfort level:** [fill in]
|
|
211
|
+
- **Quote that sounds like them:** "[fill in]"
|
|
212
|
+
- **What "success" looks like for them in this product:** [fill in]
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## 6. MVP Scope
|
|
217
|
+
|
|
218
|
+
> Smallest version that delivers real value to Persona 1. 3–5 features max.
|
|
219
|
+
|
|
220
|
+
- [fill in]
|
|
221
|
+
- [fill in]
|
|
222
|
+
- [fill in]
|
|
223
|
+
|
|
224
|
+
### Out of scope for MVP
|
|
225
|
+
- [fill in — things you explicitly will NOT build yet]
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## 7. Success Metrics
|
|
230
|
+
|
|
231
|
+
| Metric | Target (Month 1) | Target (Month 3) |
|
|
232
|
+
|---|---|---|
|
|
233
|
+
| [fill in] | | |
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## 8. Revenue Model
|
|
238
|
+
|
|
239
|
+
- **Pricing model:** [subscription / one-time / freemium / marketplace fee / etc.]
|
|
240
|
+
- **Price point:** [fill in]
|
|
241
|
+
- **Revenue goal (Month 3):** [fill in]
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## 9. Original Idea (keep for reference)
|
|
246
|
+
|
|
247
|
+
> *Your words from when you scaffolded this project.*
|
|
248
|
+
|
|
249
|
+
${idea || "(not provided)"}
|
|
250
|
+
`;
|
|
114
251
|
}
|
|
115
252
|
|
|
253
|
+
function buildHumanFlowMd(projectName, productType, { targetUser, problem, valueProp }) {
|
|
254
|
+
const seed = JOURNEY_SEEDS[productType.key] || JOURNEY_SEEDS.SAAS;
|
|
255
|
+
|
|
256
|
+
return `# HUMAN_FLOW.md
|
|
257
|
+
|
|
258
|
+
**Phase:** H — Human Flow
|
|
259
|
+
**Purpose:** Maps how a real ${targetUser} moves through ${projectName} — screen by screen, decision by decision — before any code is written. Skipping this is the #1 reason AI-generated apps feel disjointed.
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## 1. User Journey
|
|
264
|
+
|
|
265
|
+
| Stage | What the user is trying to do | Where they are emotionally |
|
|
266
|
+
|---|---|---|
|
|
267
|
+
| Discovery | ${seed.discovery} | Curious but skeptical — "does this actually work?" |
|
|
268
|
+
| First use | ${seed.firstUse} | Evaluating — will bounce at the first confusing screen |
|
|
269
|
+
| Habitual use | ${seed.habitual} | Comfortable and trusting |
|
|
270
|
+
| Advocacy | ${seed.advocacy} | Proud to recommend it |
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## 2. User Flow — Core Task
|
|
275
|
+
|
|
276
|
+
> Map the step-by-step sequence for the most important action a user takes. Fill in the blanks.
|
|
277
|
+
|
|
278
|
+
\`\`\`
|
|
279
|
+
[Entry point] → [Step 1: action] → [Step 2: action] → [Decision point?]
|
|
280
|
+
├─ Yes → [Step 3a]
|
|
281
|
+
└─ No → [Step 3b]
|
|
282
|
+
→ [Outcome / success state]
|
|
283
|
+
\`\`\`
|
|
284
|
+
|
|
285
|
+
- **Flow name:** [e.g. "New user signs up and reaches first value"]
|
|
286
|
+
- **Entry point(s):** [homepage CTA / email link / referral / etc.]
|
|
287
|
+
- **Steps to reach value:** [count them — every extra step is a drop-off risk]
|
|
288
|
+
- **Exit points:** [where can they bail, and is that OK?]
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## 3. Core Screens
|
|
293
|
+
|
|
294
|
+
> One section per screen. Every screen needs a happy path and at least one error/empty state before it gets built.
|
|
295
|
+
|
|
296
|
+
### Screen 1: [Name — e.g. Landing page]
|
|
297
|
+
- **Purpose:** [what does the user do or decide here?]
|
|
298
|
+
- **Happy path:** [what happens when everything works]
|
|
299
|
+
- **Empty state:** [what the user sees on first visit with no data]
|
|
300
|
+
- **Error state:** [what happens if something goes wrong]
|
|
301
|
+
|
|
302
|
+
### Screen 2: [Name — e.g. Dashboard]
|
|
303
|
+
- **Purpose:**
|
|
304
|
+
- **Happy path:**
|
|
305
|
+
- **Empty state:**
|
|
306
|
+
- **Error state:**
|
|
307
|
+
|
|
308
|
+
### Screen 3: [Name — e.g. Settings]
|
|
309
|
+
- **Purpose:**
|
|
310
|
+
- **Happy path:**
|
|
311
|
+
- **Empty state:**
|
|
312
|
+
- **Error state:**
|
|
313
|
+
|
|
314
|
+
> Add more screens until every route in Section 4 has a matching entry here.
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## 4. Information Architecture
|
|
319
|
+
|
|
320
|
+
| Route | Screen name | Who can access |
|
|
321
|
+
|---|---|---|
|
|
322
|
+
| / | Landing / Home | Public |
|
|
323
|
+
| /dashboard | Main app view | Logged-in user |
|
|
324
|
+
| /settings | Account settings | Logged-in user |
|
|
325
|
+
| [fill in] | | |
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
## 5. Key UX Decisions
|
|
330
|
+
|
|
331
|
+
> Record decisions that affect the whole product so the AI agent doesn't re-litigate them each session.
|
|
332
|
+
|
|
333
|
+
- Auth method: [magic link / OAuth / password — pick one]
|
|
334
|
+
- Mobile-first or desktop-first: [fill in]
|
|
335
|
+
- [fill in any other non-obvious decisions]
|
|
336
|
+
`;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// ─── main ───────────────────────────────────────────────────────────────────
|
|
340
|
+
|
|
116
341
|
async function main() {
|
|
117
342
|
const rl = readline.createInterface({ input, terminal: false });
|
|
118
343
|
const nextLine = makeLineReader(rl);
|
|
119
344
|
|
|
120
345
|
console.log("===========================================");
|
|
121
|
-
console.log(" SHIP CLI —
|
|
122
|
-
console.log("
|
|
123
|
-
console.log(
|
|
124
|
-
"\nNo API key needed. This just sets up your files and gives you a\nready-to-paste prompt for ChatGPT, Claude, Gemini, Cursor, or Windsurf.\n"
|
|
125
|
-
);
|
|
346
|
+
console.log(" SHIP CLI — scaffold your project in 60 s");
|
|
347
|
+
console.log("===========================================\n");
|
|
126
348
|
|
|
127
|
-
|
|
128
|
-
const
|
|
349
|
+
// ── 1. Core questions ──────────────────────────────────────────────────
|
|
350
|
+
const projectNameRaw = await ask(nextLine, "Project name?", "My Product");
|
|
351
|
+
const projectSlug = toKebabCase(projectNameRaw);
|
|
129
352
|
|
|
130
353
|
const productType = await pickFromList(
|
|
131
354
|
nextLine,
|
|
@@ -134,178 +357,136 @@ async function main() {
|
|
|
134
357
|
(t) => t.label
|
|
135
358
|
);
|
|
136
359
|
|
|
137
|
-
|
|
360
|
+
// ── 2. Idea questions (fills the docs without needing a separate AI step)
|
|
361
|
+
console.log("\n── About your idea ─────────────────────────");
|
|
362
|
+
const idea = await ask(
|
|
138
363
|
nextLine,
|
|
139
|
-
"
|
|
140
|
-
|
|
141
|
-
(t) => t.key
|
|
364
|
+
"Describe it in 1-3 sentences (who it's for and what it does)",
|
|
365
|
+
""
|
|
142
366
|
);
|
|
367
|
+
const targetUser = await ask(
|
|
368
|
+
nextLine,
|
|
369
|
+
"Who is your primary user? (e.g. 'freelance designers', 'gym owners')",
|
|
370
|
+
"small business owners"
|
|
371
|
+
);
|
|
372
|
+
const problem = await ask(
|
|
373
|
+
nextLine,
|
|
374
|
+
"What is the #1 problem they face today?",
|
|
375
|
+
""
|
|
376
|
+
);
|
|
377
|
+
const valueProp = await ask(
|
|
378
|
+
nextLine,
|
|
379
|
+
"What makes them say 'I need this'? (the aha moment)",
|
|
380
|
+
""
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
// Close readline before any child processes touch stdin
|
|
384
|
+
rl.close();
|
|
143
385
|
|
|
144
|
-
//
|
|
145
|
-
// (process.cwd()) — not inside the SHIP Method OS repo itself. This way
|
|
146
|
-
// it behaves like `npx create-next-app`: open any empty folder, run the
|
|
147
|
-
// command, get your project right there.
|
|
386
|
+
// ── 3. Guard: don't overwrite an existing folder ───────────────────────
|
|
148
387
|
const outDir = path.join(process.cwd(), projectSlug);
|
|
149
388
|
if (fs.existsSync(outDir)) {
|
|
150
|
-
console.log(`\n
|
|
151
|
-
|
|
152
|
-
return;
|
|
389
|
+
console.log(`\n Folder ./${projectSlug} already exists — pick a different name and run again.`);
|
|
390
|
+
process.exit(1);
|
|
153
391
|
}
|
|
154
392
|
|
|
155
|
-
console.log(`\
|
|
393
|
+
console.log(`\nScaffolding ./${projectSlug} ...`);
|
|
156
394
|
|
|
157
|
-
// 1. Copy the working app shell (sale/member/backoffice UI on mock data)
|
|
158
|
-
// from this package's bundled templates — nothing is read from outside
|
|
159
|
-
// this package.
|
|
160
395
|
const starterKitSrc = path.join(TEMPLATES_DIR, "starter-kit");
|
|
161
396
|
if (!fs.existsSync(starterKitSrc) || !fs.existsSync(path.join(TEMPLATES_DIR, "docs"))) {
|
|
162
|
-
console.log(
|
|
163
|
-
"\nThis package's bundled templates are missing or incomplete.\n" +
|
|
164
|
-
"Try reinstalling: npx ship-create@latest\n"
|
|
165
|
-
);
|
|
166
|
-
rl.close();
|
|
397
|
+
console.log("\nBundled templates missing. Try: npx ship-create@latest\n");
|
|
167
398
|
process.exit(1);
|
|
168
399
|
}
|
|
400
|
+
|
|
401
|
+
// ── 4. Copy app shell ──────────────────────────────────────────────────
|
|
169
402
|
copyRecursiveExcluding(
|
|
170
403
|
starterKitSrc,
|
|
171
404
|
outDir,
|
|
172
|
-
new Set([
|
|
173
|
-
"node_modules",
|
|
174
|
-
".next",
|
|
175
|
-
"package-lock.json",
|
|
176
|
-
"next-env.d.ts",
|
|
177
|
-
"tsconfig.tsbuildinfo",
|
|
178
|
-
])
|
|
405
|
+
new Set(["node_modules", ".next", "package-lock.json", "next-env.d.ts", "tsconfig.tsbuildinfo"])
|
|
179
406
|
);
|
|
180
407
|
|
|
181
|
-
//
|
|
408
|
+
// ── 5. Copy agent rule files (CLAUDE.md, AGENTS.md, .cursorrules, etc.)
|
|
182
409
|
for (const ruleFile of ["AGENTS.md", "CLAUDE.md", ".cursorrules", ".windsurfrules"]) {
|
|
183
410
|
const src = path.join(TEMPLATES_DIR, ruleFile);
|
|
184
|
-
if (fs.existsSync(src))
|
|
185
|
-
fs.copyFileSync(src, path.join(outDir, ruleFile));
|
|
186
|
-
}
|
|
411
|
+
if (fs.existsSync(src)) fs.copyFileSync(src, path.join(outDir, ruleFile));
|
|
187
412
|
}
|
|
188
413
|
|
|
189
|
-
//
|
|
190
|
-
// Claude Code can explicitly invoke the SHIP workflow as a skill, not
|
|
191
|
-
// just follow it as a passive rules file.
|
|
414
|
+
// ── 6. Copy Claude Code skill so agents can invoke it as a skill ───────
|
|
192
415
|
const claudeSkillSrc = path.join(TEMPLATES_DIR, ".claude");
|
|
193
416
|
if (fs.existsSync(claudeSkillSrc)) {
|
|
194
417
|
copyRecursiveExcluding(claudeSkillSrc, path.join(outDir, ".claude"), new Set());
|
|
195
418
|
}
|
|
196
419
|
|
|
197
|
-
//
|
|
420
|
+
// ── 7. Write pre-filled docs (no bracket placeholders in core sections)
|
|
198
421
|
const docsDir = path.join(outDir, "docs");
|
|
199
422
|
fs.mkdirSync(docsDir, { recursive: true });
|
|
200
423
|
|
|
201
|
-
const projectMdSrc = path.join(TEMPLATES_DIR, "docs", "PROJECT.md");
|
|
202
|
-
const rawProjectMd = fs.readFileSync(projectMdSrc, "utf8");
|
|
203
424
|
fs.writeFileSync(
|
|
204
425
|
path.join(docsDir, "PROJECT.md"),
|
|
205
|
-
|
|
426
|
+
buildProjectMd(projectNameRaw, productType.label, { targetUser, problem, valueProp, idea })
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
fs.writeFileSync(
|
|
430
|
+
path.join(docsDir, "HUMAN_FLOW.md"),
|
|
431
|
+
buildHumanFlowMd(projectNameRaw, productType, { targetUser, problem, valueProp })
|
|
206
432
|
);
|
|
207
433
|
|
|
434
|
+
// Product-type feature checklist
|
|
208
435
|
const templateSrc = path.join(TEMPLATES_DIR, "docs", "product-types", productType.file);
|
|
209
436
|
if (fs.existsSync(templateSrc)) {
|
|
210
437
|
fs.copyFileSync(templateSrc, path.join(docsDir, productType.file));
|
|
211
438
|
}
|
|
212
439
|
|
|
213
|
-
|
|
214
|
-
if (fs.existsSync(humanFlowSrc)) {
|
|
215
|
-
fs.copyFileSync(humanFlowSrc, path.join(docsDir, "HUMAN_FLOW.md"));
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Copy the full prompt chain too, so this project is self-contained.
|
|
440
|
+
// Full prompt chain (Stages 3-6) for users who want it
|
|
219
441
|
const promptsSrc = path.join(TEMPLATES_DIR, "docs", "PROMPTS.md");
|
|
220
442
|
if (fs.existsSync(promptsSrc)) {
|
|
221
443
|
fs.copyFileSync(promptsSrc, path.join(docsDir, "PROMPTS.md"));
|
|
222
444
|
}
|
|
223
445
|
|
|
224
|
-
//
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
I'm building a ${productType.label.toLowerCase()} called "${projectNameRaw}".
|
|
230
|
-
|
|
231
|
-
I want you to fill out the following template completely. Do not skip
|
|
232
|
-
sections. If you don't have enough information for a section, ask me a
|
|
233
|
-
specific clarifying question instead of writing a generic placeholder.
|
|
234
|
-
|
|
235
|
-
Push back on weak answers — if my problem statement is vague ("things are
|
|
236
|
-
hard"), ask me to quantify it. If my target audience is "everyone," force me
|
|
237
|
-
to pick a primary segment.
|
|
238
|
-
|
|
239
|
-
Here is the template to fill:
|
|
240
|
-
|
|
241
|
-
${rawProjectMd}
|
|
242
|
-
|
|
243
|
-
Output the completed template in the same markdown format, ready to save
|
|
244
|
-
back into docs/PROJECT.md.`;
|
|
245
|
-
|
|
246
|
-
fs.writeFileSync(path.join(docsDir, "FIRST_PROMPT.txt"), prompt);
|
|
247
|
-
|
|
248
|
-
const toolNote = aiTool.note;
|
|
249
|
-
const nextSteps = `# Next Steps for ${projectNameRaw}
|
|
250
|
-
|
|
251
|
-
You're building: **${productType.label}**
|
|
252
|
-
Your AI tool: **${aiTool.key}**
|
|
253
|
-
|
|
254
|
-
## 1. Run the app shell (optional, do this anytime)
|
|
255
|
-
|
|
256
|
-
\`\`\`
|
|
257
|
-
cd ${projectSlug}
|
|
258
|
-
npm install
|
|
259
|
-
npm run dev
|
|
260
|
-
\`\`\`
|
|
261
|
-
|
|
262
|
-
Then open http://localhost:3000 — you'll see the sale page, member area, and
|
|
263
|
-
backoffice already wired up with placeholder/mock data.
|
|
264
|
-
|
|
265
|
-
## 2. Fill in your product spec (do this first, before changing code)
|
|
266
|
-
|
|
267
|
-
Open \`docs/PROJECT.md\`. It's pre-filled with your project name and product
|
|
268
|
-
type. Fill in sections 1-2 (Vision, Problem Statement) yourself, by hand —
|
|
269
|
-
this is the thinking only you can do.
|
|
270
|
-
|
|
271
|
-
## 3. Let AI fill in the rest
|
|
272
|
-
|
|
273
|
-
${toolNote}
|
|
274
|
-
|
|
275
|
-
The exact prompt to paste is saved in \`docs/FIRST_PROMPT.txt\` — open it,
|
|
276
|
-
fill in the [raw idea] bracket, and paste the whole thing into ${aiTool.key}.
|
|
446
|
+
// Tech-stack reference — agent reads this when making stack decisions.
|
|
447
|
+
const techStackSrc = path.join(TEMPLATES_DIR, "docs", "tech-stack");
|
|
448
|
+
if (fs.existsSync(techStackSrc)) {
|
|
449
|
+
copyRecursiveExcluding(techStackSrc, path.join(docsDir, "tech-stack"), new Set());
|
|
450
|
+
}
|
|
277
451
|
|
|
278
|
-
|
|
452
|
+
// Design system + spec templates — filled by /build after user picks theme/font/trend.
|
|
453
|
+
for (const f of ["DESIGN_SYSTEM.md", "DESIGN_SPEC.md"]) {
|
|
454
|
+
const src = path.join(TEMPLATES_DIR, "docs", f);
|
|
455
|
+
if (fs.existsSync(src)) fs.copyFileSync(src, path.join(docsDir, f));
|
|
456
|
+
}
|
|
279
457
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
458
|
+
// ── 8. npm install ─────────────────────────────────────────────────────
|
|
459
|
+
console.log("\nInstalling packages (this takes ~30 seconds)...");
|
|
460
|
+
const install = spawnSync("npm", ["install"], {
|
|
461
|
+
cwd: outDir,
|
|
462
|
+
stdio: "inherit",
|
|
463
|
+
shell: true,
|
|
464
|
+
});
|
|
465
|
+
if (install.status !== 0) {
|
|
466
|
+
console.log(`\n npm install failed. Run it manually:\n cd ${projectSlug} && npm install`);
|
|
467
|
+
}
|
|
285
468
|
|
|
286
|
-
|
|
469
|
+
// ── 9. Done ────────────────────────────────────────────────────────────
|
|
470
|
+
console.log(`
|
|
471
|
+
Done! Your project is at: ./${projectSlug}/
|
|
287
472
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
473
|
+
docs/PROJECT.md — product spec (pre-filled)
|
|
474
|
+
docs/HUMAN_FLOW.md — UX flow (pre-filled)
|
|
475
|
+
docs/DESIGN_SYSTEM.md — design tokens & decisions (filled by /build)
|
|
476
|
+
docs/DESIGN_SPEC.md — screen-by-screen design spec (filled by /build)
|
|
477
|
+
docs/tech-stack/STACK_DECISION_MATRIX.md — stack choices reference
|
|
292
478
|
|
|
293
|
-
|
|
294
|
-
— Claude can invoke it directly as a skill (not just a passive rule file)
|
|
295
|
-
whenever you ask it to build a feature or review whether something is
|
|
296
|
-
ready to ship.
|
|
297
|
-
`;
|
|
479
|
+
── Open in your AI coding tool and type /build ─────────────────
|
|
298
480
|
|
|
299
|
-
|
|
481
|
+
Claude Code → claude ./${projectSlug}
|
|
482
|
+
Cursor → cursor ./${projectSlug}
|
|
483
|
+
Windsurf → windsurf ./${projectSlug}
|
|
300
484
|
|
|
301
|
-
|
|
302
|
-
console.log(` ./${projectSlug}/`);
|
|
303
|
-
console.log("\nOpen this file for what to do next:");
|
|
304
|
-
console.log(` ${projectSlug}/NEXT_STEPS.md`);
|
|
305
|
-
console.log("\nOr just run:");
|
|
306
|
-
console.log(` cat "${projectSlug}/NEXT_STEPS.md"`);
|
|
485
|
+
Then type: /build
|
|
307
486
|
|
|
308
|
-
|
|
487
|
+
The agent will read your docs, create the build spec, pick a theme,
|
|
488
|
+
and start coding the MVP — no copy-paste, no extra setup.
|
|
489
|
+
`);
|
|
309
490
|
}
|
|
310
491
|
|
|
311
492
|
main().catch((err) => {
|
package/package.json
CHANGED