ship-create 1.5.0 → 1.6.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 CHANGED
@@ -10,18 +10,6 @@
10
10
  *
11
11
  * Usage:
12
12
  * npx ship-create
13
- *
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
25
13
  */
26
14
 
27
15
  import fs from "node:fs";
@@ -34,19 +22,105 @@ import { spawnSync } from "node:child_process";
34
22
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
35
23
  const TEMPLATES_DIR = path.join(__dirname, "templates");
36
24
 
25
+ // ─── ANSI helpers ────────────────────────────────────────────────────────────
26
+
27
+ const C = {
28
+ reset: "\x1b[0m",
29
+ bold: "\x1b[1m",
30
+ dim: "\x1b[2m",
31
+ cyan: "\x1b[36m",
32
+ green: "\x1b[32m",
33
+ white: "\x1b[37m",
34
+ hideCursor: "\x1b[?25l",
35
+ showCursor: "\x1b[?25h",
36
+ clearLine: "\x1b[2K",
37
+ up: (n) => `\x1b[${n}A`,
38
+ };
39
+
40
+ // ─── Data ─────────────────────────────────────────────────────────────────────
41
+
37
42
  const PRODUCT_TYPES = [
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" },
43
+ { key: "SAAS", name: "Web app / SaaS", desc: "users sign up and use a tool online", file: "SAAS_TEMPLATE.md" },
44
+ { key: "CRM", name: "CRM / Sales pipeline", desc: "track leads, deals, and customers", file: "CRM_TEMPLATE.md" },
45
+ { key: "MEMBERSHIP", name: "Membership / community", desc: "paid access to content or a community", file: "MEMBERSHIP_TEMPLATE.md" },
46
+ { key: "LEADGEN", name: "Landing page / lead capture", desc: "get visitors to fill a form or book a call", file: "LEADGEN_TEMPLATE.md" },
47
+ { key: "DIRECTORY", name: "Directory / listing site", desc: "browse and find providers, products, or places", file: "DIRECTORY_TEMPLATE.md" },
48
+ { key: "DASHBOARD", name: "Dashboard / analytics", desc: "visualise data and KPIs for a team or business", file: "DASHBOARD_TEMPLATE.md" },
49
+ { key: "INTERNAL_TOOL", name: "Internal tool", desc: "replaces a manual or spreadsheet-based process inside a company", file: "INTERNAL_TOOL_TEMPLATE.md" },
50
+ { key: "MARKETPLACE", name: "Marketplace", desc: "connects buyers and sellers; transactions happen on the platform", file: "MARKETPLACE_TEMPLATE.md" },
51
+ ];
52
+
53
+ const LANGUAGES = [
54
+ { code: "en", name: "English", desc: "UI text, labels, and copy in English" },
55
+ { code: "th", name: "Thai (ภาษาไทย)", desc: "ข้อความ label และ copy ในระบบเป็นภาษาไทย" },
46
56
  ];
47
57
 
48
- // Pre-seeded journey stages per product type so HUMAN_FLOW.md has real
49
- // content the moment the project is created.
58
+ const I18N = {
59
+ en: {
60
+ sectionIdea: "── About your idea ────────────────────────────",
61
+ qName: "Project name?",
62
+ qNameDefault: "My Product",
63
+ qIdea: "Describe it in 1–3 sentences (who it's for and what it does)",
64
+ qUser: "Who is your primary user? (e.g. 'freelance designers', 'gym owners')",
65
+ qUserDefault: "small business owners",
66
+ qProblem: "What is the #1 problem they face today?",
67
+ qValue: "What makes them say 'I need this'? (the aha moment)",
68
+ scaffolding: (slug) => `Scaffolding ./${slug} ...`,
69
+ installing: "Installing packages (this takes ~30 seconds)...",
70
+ installFail: (slug) => `npm install failed. Run it manually:\n cd ${slug} && npm install`,
71
+ folderExists: (slug) => `Folder ./${slug} already exists — pick a different name and run again.`,
72
+ templatesMissing:"Bundled templates missing. Try: npx ship-create@latest",
73
+ done: (slug) => `
74
+ ✔ Done! Your project is at ./${slug}/
75
+
76
+ docs/PROJECT.md — product spec (pre-filled)
77
+ docs/HUMAN_FLOW.md — UX flow (pre-filled)
78
+ docs/DESIGN_SYSTEM.md — design tokens (filled by /build)
79
+
80
+ ──────────────────────────────────────────
81
+ Open in your AI coding tool and type /build
82
+
83
+ Claude Code → claude ./${slug}
84
+ Cursor → cursor ./${slug}
85
+ Windsurf → windsurf ./${slug}
86
+
87
+ Then type: /build
88
+ `,
89
+ },
90
+ th: {
91
+ sectionIdea: "── เกี่ยวกับ idea ของคุณ ─────────────────────",
92
+ qName: "ชื่อโปรเจค?",
93
+ qNameDefault: "สินค้าของฉัน",
94
+ qIdea: "อธิบายในไม่เกิน 1–3 ประโยค (สำหรับใคร และทำอะไร)",
95
+ qUser: "ผู้ใช้หลักของคุณคือใคร? (เช่น 'ฟรีแลนซ์ดีไซเนอร์', 'เจ้าของยิม')",
96
+ qUserDefault: "เจ้าของธุรกิจขนาดเล็ก",
97
+ qProblem: "ปัญหาอันดับ 1 ที่พวกเขาเจออยู่ทุกวันคืออะไร?",
98
+ qValue: "อะไรทำให้พวกเขาบอกว่า 'ฉันต้องการสิ่งนี้!'? (จุด aha moment)",
99
+ scaffolding: (slug) => `กำลัง scaffold ./${slug} ...`,
100
+ installing: "กำลังติดตั้ง packages (ใช้เวลาประมาณ 30 วินาที)...",
101
+ installFail: (slug) => `npm install ล้มเหลว รันเองได้ที่:\n cd ${slug} && npm install`,
102
+ folderExists: (slug) => `โฟลเดอร์ ./${slug} มีอยู่แล้ว — เลือกชื่ออื่นแล้วรันใหม่`,
103
+ templatesMissing:"ไม่พบ template ที่ bundle ไว้ ลองรัน: npx ship-create@latest",
104
+ done: (slug) => `
105
+ ✔ เสร็จแล้ว! โปรเจคของคุณอยู่ที่ ./${slug}/
106
+
107
+ docs/PROJECT.md — spec สินค้า (กรอกข้อมูลแล้ว)
108
+ docs/HUMAN_FLOW.md — UX flow (กรอกข้อมูลแล้ว)
109
+ docs/DESIGN_SYSTEM.md — design tokens (เติมโดย /build)
110
+
111
+ ──────────────────────────────────────────
112
+ เปิดใน AI coding tool แล้วพิมพ์ /build
113
+
114
+ Claude Code → claude ./${slug}
115
+ Cursor → cursor ./${slug}
116
+ Windsurf → windsurf ./${slug}
117
+
118
+ จากนั้นพิมพ์: /build
119
+ `,
120
+ },
121
+ };
122
+
123
+ // Pre-seeded journey stages per product type so HUMAN_FLOW.md has real content.
50
124
  const JOURNEY_SEEDS = {
51
125
  SAAS: {
52
126
  discovery: "Googles a pain they have, or sees a post about the tool from someone they follow",
@@ -98,19 +172,87 @@ const JOURNEY_SEEDS = {
98
172
  },
99
173
  };
100
174
 
101
- // ─── helpers ────────────────────────────────────────────────────────────────
175
+ // ─── Interactive selector (arrow keys) ───────────────────────────────────────
176
+
177
+ function selectInteractive(title, items) {
178
+ return new Promise((resolve) => {
179
+ let idx = 0;
180
+
181
+ const printItems = () => {
182
+ items.forEach((item, i) => {
183
+ const selected = i === idx;
184
+ process.stdout.write(C.clearLine + "\r");
185
+ if (selected) {
186
+ process.stdout.write(
187
+ ` ${C.cyan}${C.bold}❯ ${item.name}${C.reset}` +
188
+ ` ${C.dim}${item.desc}${C.reset}\n`
189
+ );
190
+ } else {
191
+ process.stdout.write(` ${C.dim} ${item.name}${C.reset}\n`);
192
+ }
193
+ });
194
+ // hint line (no trailing newline so cursor stays here)
195
+ process.stdout.write(
196
+ C.clearLine + `\r ${C.dim}↑↓ navigate · Enter to select${C.reset}`
197
+ );
198
+ };
199
+
200
+ // initial render
201
+ process.stdout.write(`\n ${C.bold}${title}${C.reset}\n\n`);
202
+ process.stdout.write(C.hideCursor);
203
+ printItems();
204
+
205
+ process.stdin.setRawMode(true);
206
+ process.stdin.resume();
207
+ process.stdin.setEncoding("utf8");
208
+
209
+ const onData = (key) => {
210
+ if (key === "\x03") { // Ctrl+C
211
+ process.stdout.write(C.showCursor + "\n");
212
+ process.exit(0);
213
+ }
214
+ if (key === "\r" || key === "\n") { // Enter
215
+ process.stdin.removeListener("data", onData);
216
+ process.stdin.setRawMode(false);
217
+ process.stdin.pause();
218
+ // replace hint line with confirmed selection
219
+ process.stdout.write(
220
+ "\r" + C.clearLine +
221
+ ` ${C.green}${C.bold}✔ ${items[idx].name}${C.reset}\n`
222
+ );
223
+ process.stdout.write(C.showCursor);
224
+ resolve(items[idx]);
225
+ return;
226
+ }
227
+ const prev = idx;
228
+ if (key === "\x1b[A") idx = Math.max(0, idx - 1); // up
229
+ if (key === "\x1b[B") idx = Math.min(items.length - 1, idx + 1); // down
230
+ if (idx !== prev) {
231
+ process.stdout.write(C.up(items.length));
232
+ printItems();
233
+ }
234
+ };
235
+
236
+ process.stdin.on("data", onData);
237
+ });
238
+ }
102
239
 
103
- function toKebabCase(str) {
104
- return str
105
- .trim()
106
- .toLowerCase()
107
- .replace(/[^a-z0-9]+/g, "-")
108
- .replace(/(^-|-$)/g, "") || "my-product";
240
+ // Fallback for non-TTY environments (CI, piped input)
241
+ async function selectFallback(title, items, nextLine) {
242
+ console.log(`\n ${title}`);
243
+ items.forEach((item, i) =>
244
+ console.log(` ${i + 1}. ${item.name} — ${item.desc}`)
245
+ );
246
+ while (true) {
247
+ const raw = await nextLine(` Pick a number (1-${items.length}): `);
248
+ const n = parseInt(raw, 10) - 1;
249
+ if (n >= 0 && n < items.length) return items[n];
250
+ console.log(" Not a valid number, try again.");
251
+ }
109
252
  }
110
253
 
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.
254
+ // ─── Text question helper ─────────────────────────────────────────────────────
255
+
114
256
  function makeLineReader(rl) {
115
257
  const it = rl[Symbol.asyncIterator]();
116
258
  return async function nextLine(promptText) {
@@ -122,22 +264,12 @@ function makeLineReader(rl) {
122
264
  }
123
265
 
124
266
  async function ask(nextLine, question, defaultValue) {
125
- const suffix = defaultValue ? ` (${defaultValue})` : "";
126
- const answer = await nextLine(`${question}${suffix}: `);
267
+ const suffix = defaultValue ? ` ${C.dim}(${defaultValue})${C.reset}` : "";
268
+ output.write(` ${question}${suffix}\n ${C.cyan}›${C.reset} `);
269
+ const answer = await nextLine("");
127
270
  return answer || defaultValue || "";
128
271
  }
129
272
 
130
- async function pickFromList(nextLine, title, items, labelFn) {
131
- console.log(`\n${title}`);
132
- items.forEach((item, i) => console.log(` ${i + 1}. ${labelFn(item)}`));
133
- while (true) {
134
- const raw = await nextLine(`Pick a number (1-${items.length}): `);
135
- const idx = parseInt(raw, 10) - 1;
136
- if (idx >= 0 && idx < items.length) return items[idx];
137
- console.log(" Not a valid number, try again.");
138
- }
139
- }
140
-
141
273
  function copyRecursiveExcluding(src, dest, excludeNames) {
142
274
  fs.mkdirSync(dest, { recursive: true });
143
275
  for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
@@ -152,9 +284,17 @@ function copyRecursiveExcluding(src, dest, excludeNames) {
152
284
  }
153
285
  }
154
286
 
155
- // ─── doc builders ───────────────────────────────────────────────────────────
287
+ function toKebabCase(str) {
288
+ return str
289
+ .trim()
290
+ .toLowerCase()
291
+ .replace(/[^a-z0-9]+/g, "-")
292
+ .replace(/(^-|-$)/g, "") || "my-product";
293
+ }
156
294
 
157
- function buildProjectMd(projectName, productTypeLabel, { targetUser, problem, valueProp, idea }) {
295
+ // ─── Doc builders ─────────────────────────────────────────────────────────────
296
+
297
+ function buildProjectMd(projectName, productTypeName, { targetUser, problem, valueProp, idea, uiLanguage }) {
158
298
  return `# PROJECT.md
159
299
 
160
300
  **Phase:** S — Structure
@@ -164,7 +304,7 @@ function buildProjectMd(projectName, productTypeLabel, { targetUser, problem, va
164
304
 
165
305
  ## 1. Product Vision
166
306
 
167
- ${projectName} is a ${productTypeLabel.toLowerCase()} that helps **${targetUser}** ${valueProp}.
307
+ ${projectName} is a ${productTypeName.toLowerCase()} that helps **${targetUser}** ${valueProp}.
168
308
 
169
309
  > *Expand this into a fuller paragraph before moving to HUMAN_FLOW.md — what does this product become in 2 years if it succeeds?*
170
310
 
@@ -242,7 +382,15 @@ ${projectName} is a ${productTypeLabel.toLowerCase()} that helps **${targetUser}
242
382
 
243
383
  ---
244
384
 
245
- ## 9. Original Idea (keep for reference)
385
+ ## 9. Technical Decisions
386
+
387
+ - **UI language:** ${uiLanguage || "English"}
388
+ - **Auth method:** [magic link / OAuth / password — fill in]
389
+ - **Hosting:** [Vercel / Railway / other — fill in]
390
+
391
+ ---
392
+
393
+ ## 10. Original Idea (keep for reference)
246
394
 
247
395
  > *Your words from when you scaffolded this project.*
248
396
 
@@ -336,94 +484,94 @@ function buildHumanFlowMd(projectName, productType, { targetUser, problem, value
336
484
  `;
337
485
  }
338
486
 
339
- // ─── main ───────────────────────────────────────────────────────────────────
487
+ // ─── Main ──────────────────────────────────────────────────────────────────────
340
488
 
341
489
  async function main() {
490
+ const isTTY = process.stdin.isTTY;
491
+
492
+ // ── Banner ─────────────────────────────────────────────────────────────────
493
+ console.log("");
494
+ console.log(`${C.bold}${C.cyan} ███████╗██╗ ██╗██╗██████╗ ${C.reset}`);
495
+ console.log(`${C.bold}${C.cyan} ██╔════╝██║ ██║██║██╔══██╗${C.reset}`);
496
+ console.log(`${C.bold}${C.cyan} ███████╗███████║██║██████╔╝${C.reset}`);
497
+ console.log(`${C.bold}${C.cyan} ╚════██║██╔══██║██║██╔═══╝ ${C.reset}`);
498
+ console.log(`${C.bold}${C.cyan} ███████║██║ ██║██║██║ ${C.reset}`);
499
+ console.log(`${C.bold}${C.cyan} ╚══════╝╚═╝ ╚═╝╚═╝╚═╝ ${C.reset}`);
500
+ console.log("");
501
+ console.log(` ${C.bold}The SHIP Method${C.reset} ${C.dim}· scaffold your project${C.reset}`);
502
+ console.log(` ${C.dim}──────────────────────────────────────────${C.reset}`);
503
+ console.log("");
504
+
505
+ // ── Interactive selections (no readline needed) ────────────────────────────
506
+ let productType, uiLanguage;
507
+
508
+ if (isTTY) {
509
+ productType = await selectInteractive("What type of system are you building?", PRODUCT_TYPES);
510
+ uiLanguage = await selectInteractive("UI language — what will your app display to users?", LANGUAGES);
511
+ }
512
+
513
+ // ── Text questions (readline) — switch language after uiLanguage is known ──
342
514
  const rl = readline.createInterface({ input, terminal: false });
343
515
  const nextLine = makeLineReader(rl);
344
516
 
345
- console.log("===========================================");
346
- console.log(" SHIP CLI scaffold your project in 60 s");
347
- console.log("===========================================\n");
348
-
349
- // ── 1. Core questions ──────────────────────────────────────────────────
350
- const projectNameRaw = await ask(nextLine, "Project name?", "My Product");
351
- const projectSlug = toKebabCase(projectNameRaw);
517
+ if (!isTTY) {
518
+ productType = await selectFallback("What type of system are you building?", PRODUCT_TYPES, nextLine);
519
+ uiLanguage = await selectFallback("UI language — what will your app display to users?", LANGUAGES, nextLine);
520
+ }
352
521
 
353
- const productType = await pickFromList(
354
- nextLine,
355
- "What are you building?",
356
- PRODUCT_TYPES,
357
- (t) => t.label
358
- );
522
+ const t = I18N[uiLanguage.code] ?? I18N.en;
359
523
 
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(
363
- nextLine,
364
- "Describe it in 1-3 sentences (who it's for and what it does)",
365
- ""
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
- );
524
+ console.log(`\n ${C.dim}${t.sectionIdea}${C.reset}`);
525
+ const projectNameRaw = await ask(nextLine, t.qName, t.qNameDefault);
526
+ const projectSlug = toKebabCase(projectNameRaw);
527
+ const idea = await ask(nextLine, t.qIdea, "");
528
+ const targetUser = await ask(nextLine, t.qUser, t.qUserDefault);
529
+ const problem = await ask(nextLine, t.qProblem, "");
530
+ const valueProp = await ask(nextLine, t.qValue, "");
382
531
 
383
- // Close readline before any child processes touch stdin
384
532
  rl.close();
385
533
 
386
- // ── 3. Guard: don't overwrite an existing folder ───────────────────────
534
+ // ── Guard: don't overwrite an existing folder ──────────────────────────────
387
535
  const outDir = path.join(process.cwd(), projectSlug);
388
536
  if (fs.existsSync(outDir)) {
389
- console.log(`\n Folder ./${projectSlug} already exists — pick a different name and run again.`);
537
+ console.log(`\n ${C.dim}${t.folderExists(projectSlug)}${C.reset}`);
390
538
  process.exit(1);
391
539
  }
392
540
 
393
- console.log(`\nScaffolding ./${projectSlug} ...`);
541
+ console.log(`\n ${C.dim}${t.scaffolding(projectSlug)}${C.reset}\n`);
394
542
 
395
543
  const starterKitSrc = path.join(TEMPLATES_DIR, "starter-kit");
396
544
  if (!fs.existsSync(starterKitSrc) || !fs.existsSync(path.join(TEMPLATES_DIR, "docs"))) {
397
- console.log("\nBundled templates missing. Try: npx ship-create@latest\n");
545
+ console.log(`\n ${t.templatesMissing}\n`);
398
546
  process.exit(1);
399
547
  }
400
548
 
401
- // ── 4. Copy app shell ──────────────────────────────────────────────────
549
+ // ── Copy app shell ─────────────────────────────────────────────────────────
402
550
  copyRecursiveExcluding(
403
551
  starterKitSrc,
404
552
  outDir,
405
553
  new Set(["node_modules", ".next", "package-lock.json", "next-env.d.ts", "tsconfig.tsbuildinfo"])
406
554
  );
407
555
 
408
- // ── 5. Copy agent rule files (CLAUDE.md, AGENTS.md, .cursorrules, etc.)
556
+ // ── Copy agent rule files ──────────────────────────────────────────────────
409
557
  for (const ruleFile of ["AGENTS.md", "CLAUDE.md", ".cursorrules", ".windsurfrules"]) {
410
558
  const src = path.join(TEMPLATES_DIR, ruleFile);
411
559
  if (fs.existsSync(src)) fs.copyFileSync(src, path.join(outDir, ruleFile));
412
560
  }
413
561
 
414
- // ── 6. Copy Claude Code skill so agents can invoke it as a skill ───────
562
+ // ── Copy Claude Code skill ─────────────────────────────────────────────────
415
563
  const claudeSkillSrc = path.join(TEMPLATES_DIR, ".claude");
416
564
  if (fs.existsSync(claudeSkillSrc)) {
417
565
  copyRecursiveExcluding(claudeSkillSrc, path.join(outDir, ".claude"), new Set());
418
566
  }
419
567
 
420
- // ── 7. Write pre-filled docs (no bracket placeholders in core sections)
568
+ // ── Write pre-filled docs ──────────────────────────────────────────────────
421
569
  const docsDir = path.join(outDir, "docs");
422
570
  fs.mkdirSync(docsDir, { recursive: true });
423
571
 
424
572
  fs.writeFileSync(
425
573
  path.join(docsDir, "PROJECT.md"),
426
- buildProjectMd(projectNameRaw, productType.label, { targetUser, problem, valueProp, idea })
574
+ buildProjectMd(projectNameRaw, productType.name, { targetUser, problem, valueProp, idea, uiLanguage: uiLanguage.name })
427
575
  );
428
576
 
429
577
  fs.writeFileSync(
@@ -431,65 +579,49 @@ async function main() {
431
579
  buildHumanFlowMd(projectNameRaw, productType, { targetUser, problem, valueProp })
432
580
  );
433
581
 
434
- // Product-type feature checklist
435
582
  const templateSrc = path.join(TEMPLATES_DIR, "docs", "product-types", productType.file);
436
583
  if (fs.existsSync(templateSrc)) {
437
584
  fs.copyFileSync(templateSrc, path.join(docsDir, productType.file));
438
585
  }
439
586
 
440
- // Full prompt chain (Stages 3-6) for users who want it
441
587
  const promptsSrc = path.join(TEMPLATES_DIR, "docs", "PROMPTS.md");
442
588
  if (fs.existsSync(promptsSrc)) {
443
589
  fs.copyFileSync(promptsSrc, path.join(docsDir, "PROMPTS.md"));
444
590
  }
445
591
 
446
- // Tech-stack reference — agent reads this when making stack decisions.
447
592
  const techStackSrc = path.join(TEMPLATES_DIR, "docs", "tech-stack");
448
593
  if (fs.existsSync(techStackSrc)) {
449
594
  copyRecursiveExcluding(techStackSrc, path.join(docsDir, "tech-stack"), new Set());
450
595
  }
451
596
 
452
- // Design system + spec templates — filled by /build after user picks theme/font/trend.
453
597
  for (const f of ["DESIGN_SYSTEM.md", "DESIGN_SPEC.md"]) {
454
598
  const src = path.join(TEMPLATES_DIR, "docs", f);
455
599
  if (fs.existsSync(src)) fs.copyFileSync(src, path.join(docsDir, f));
456
600
  }
457
601
 
458
- // ── 8. npm install ─────────────────────────────────────────────────────
459
- console.log("\nInstalling packages (this takes ~30 seconds)...");
602
+ // ── npm install ────────────────────────────────────────────────────────────
603
+ console.log(` ${C.dim}${t.installing}${C.reset}\n`);
460
604
  const install = spawnSync("npm", ["install"], {
461
605
  cwd: outDir,
462
606
  stdio: "inherit",
463
607
  shell: true,
464
608
  });
465
609
  if (install.status !== 0) {
466
- console.log(`\n npm install failed. Run it manually:\n cd ${projectSlug} && npm install`);
610
+ console.log(`\n ${t.installFail(projectSlug)}`);
467
611
  }
468
612
 
469
- // ── 9. Done ────────────────────────────────────────────────────────────
470
- console.log(`
471
- Done! Your project is at: ./${projectSlug}/
472
-
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
478
-
479
- ── Open in your AI coding tool and type /build ─────────────────
480
-
481
- Claude Code → claude ./${projectSlug}
482
- Cursor → cursor ./${projectSlug}
483
- Windsurf → windsurf ./${projectSlug}
484
-
485
- Then type: /build
486
-
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
- `);
613
+ // ── Done ───────────────────────────────────────────────────────────────────
614
+ const doneLines = t.done(projectSlug).split("\n");
615
+ const styledDone = doneLines.map((line, i) => {
616
+ if (i === 1) return ` ${C.green}${C.bold}${line.trim()}${C.reset}`;
617
+ if (line.includes("/build")) return line.replace("/build", `${C.cyan}${C.bold}/build${C.reset}`);
618
+ return `${C.dim}${line}${C.reset}`;
619
+ }).join("\n");
620
+ console.log(styledDone);
490
621
  }
491
622
 
492
623
  main().catch((err) => {
493
- console.error("\nSomething went wrong:", err.message);
624
+ process.stdout.write(C.showCursor);
625
+ console.error(`\n ${C.dim}Something went wrong:${C.reset}`, err.message);
494
626
  process.exit(1);
495
627
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ship-create",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Scaffold a new project the SHIP Method way — Structure, Human Flow, Instruction, Publish. No git clone, no API key, just one command.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,75 +1,48 @@
1
1
  ---
2
2
  name: ship-method
3
- description: Use when starting any new product, feature, or "build me an app" request — before writing any application code. Walks through Structure, Human Flow, Instruction, Publish in order, using this repo's templates. Also use when asked to review whether a project is "ready to build" or "ready to ship."
3
+ description: Use when starting any new product, feature, or "build me an app" request — before writing any application code. Walks through Structure, Theme, Instruction, Publish in order, using this repo's templates. Also use when asked to review whether a project is "ready to build" or "ready to ship."
4
4
  ---
5
5
 
6
6
  # The SHIP Method
7
7
 
8
- You are operating inside a project built with **The SHIP Method OS**. The method has four phases, run strictly in order:
9
-
10
8
  ```
11
- S — STRUCTURE H — HUMAN FLOW I — INSTRUCTION P — PUBLISH
12
- (what & why) (the experience) (AI-ready specs) (ship it)
9
+ S — STRUCTURE H — HUMAN FLOW I — INSTRUCTION P — PUBLISH
13
10
  ```
14
11
 
15
- The single most common reason AI-built products end up broken, scope-creeped, or unshippable: someone skips Structure and Human Flow and goes straight to "build me an app." This skill exists to stop that.
16
-
17
- ## When to Use This Skill
18
-
19
- - The user describes a new product/feature idea and wants something built
20
- - The user asks you to generate code for a feature that doesn't have a filled spec yet
21
- - The user asks "is this ready to build?" or "is this ready to ship?"
22
- - You're about to scaffold, design a schema, or write business logic and no `PROJECT.md` / `HUMAN_FLOW.md` exists yet for it
23
- - The user runs the `/ship` shortcut — drive them through the gates below one phase at a time, drafting their answers into the docs
12
+ ## When `/build` is invoked do this immediately, in this order
24
13
 
25
- ## The Checklist
14
+ **Do not create a task list. Do not plan. Act.**
26
15
 
27
- Work through these gates in order. Do not let scope, urgency, or the user saying "just build it fast" skip a gate without at least naming what's being skipped.
16
+ ### 1. Read (no output, no tasks)
17
+ Silently read `docs/PROJECT.md` and `docs/HUMAN_FLOW.md`. If core sections are empty, ask the user one question: *"Describe your idea in one sentence."* Fill the docs from their answer, then continue.
28
18
 
29
- - [ ] **Gate 1 — Structure exists.** Find or create `PROJECT.md` (template: `01-STRUCTURE/PROJECT.md` in this repo, or `docs/PROJECT.md` if this is a `ship-create`-generated project). Vision, Problem Statement, Target Audience, and MVP Scope must be filled not placeholder brackets.
30
- - [ ] **Gate 2 Human Flow exists.** Find or create `HUMAN_FLOW.md`. Every core screen needs a happy path and at least one error/empty state defined before it gets built.
31
- - [ ] **Gate 3 — Instruction exists.** Functional requirements, data model, and API contract are written somewhere concrete (`AI_BUILD_SPEC.md` / `docs/PROJECT.md`'s feature section) before you generate feature code.
32
- - [ ] **Gate 4 — Theme & First Screen.** Once Gates 1-3 pass and before final polish/ship: derive 2-3 business-appropriate themes from `PROJECT.md`, let the user pick one, apply it (`app/globals.css`, `app/layout.tsx`), and record it in the design system; then read `HUMAN_FLOW.md` and replace the starter's generic `app/page.tsx` ("Pick your area") with the real entry point. See `theme-guide.md` in this skill folder.
33
- - [ ] **Gate 5 — Publish readiness.** Before telling the user something is "done," check it against the relevant checklist (`QA_CHECKLIST.md`, `LAUNCH_CHECKLIST.md`) rather than declaring success from a clean build alone.
19
+ ### 2. Themefirst visible output
20
+ Present 2–3 theme options (palette name + font, one line each). Wait for the user to pick. Apply the chosen theme to `app/globals.css` and `app/layout.tsx`. Record in `docs/DESIGN_SYSTEM.md`. **This is the first thing the user sees it must happen before any feature code.**
34
21
 
35
- ## How to Apply This
22
+ ### 3. Home screen — second visible output
23
+ Read `HUMAN_FLOW.md`. Replace `app/page.tsx` with the real entry point for this product. The user should be able to run `npm run dev` and see their product, not the starter template.
36
24
 
37
- 1. **If Gate 1 or 2 is missing or full of `[bracket placeholders]`:** stop, don't generate feature code. Instead, ask the user one specific question at a time to fill the gap — pull questions directly from the relevant template's section headers. Scaffolding, config, and boilerplate are fine to write anytime; business logic and feature code are gated.
38
- 2. **If the user insists on skipping a gate** ("just build it, skip the docs"): comply, but say once, briefly, what's being skipped and the most likely thing that breaks later as a result. Don't refuse repeatedly or lecture.
39
- 3. **If all gates are filled:** proceed normally — generate code straight from the spec, and flag if a code request contradicts what's already written in `PROJECT.md` or `HUMAN_FLOW.md` rather than silently overriding it.
40
- 4. **When asked "is this ready to build/ship?":** walk the checklist above explicitly and report which gates pass/fail, rather than giving a vague yes/no.
25
+ ### 4. Ask one question
26
+ *"Which feature do you want to build first?"* Then build just that one.
41
27
 
42
- ## Theme & First Screen (Gate 4)
28
+ ---
43
29
 
44
- Run this once Gates 1-3 pass, before final polish or shipping. The agent already
45
- knows the business from `PROJECT.md`, so it can theme and build the front door
46
- accurately.
30
+ **That's the entire flow.** Steps 13 are one session. Features are added one at a time after that.
47
31
 
48
- 1. **Derive & choose a theme.** From `PROJECT.md`, produce 2-3 candidate themes
49
- (palette as HSL token values + a font pairing). Present them and let the user
50
- pick — never pick silently, never require brand assets the user didn't give.
51
- 2. **Apply it.** Write the chosen tokens into `app/globals.css` (`:root` and
52
- `.dark`), set fonts in `app/layout.tsx`, and record the choice in
53
- `12-DESIGN-SYSTEM/DESIGN_SYSTEM.md` (or `docs/DESIGN_SYSTEM.md`).
54
- 3. **Build the first screen.** Read `HUMAN_FLOW.md`, decide the real entry point
55
- for this business, and replace the starter's `app/page.tsx` ("Pick your
56
- area") with it — landing/sale for marketing-led products, app home/dashboard
57
- redirect for tools. Adjust each area's main page to match.
32
+ ## Hard rules
58
33
 
59
- Full procedure and the business-type palette/font table: `theme-guide.md` in
60
- this skill folder. (In this OS repo the app lives under `starter-kit/`.)
34
+ - **No task list at start.** Create tasks only when building a specific feature, and only 1–2 at a time.
35
+ - **No spec before theme.** `docs/AI_BUILD_SPEC.md` is written after the home screen exists, not before.
36
+ - **No gates that block visible output.** The only thing that can pause the flow is a missing answer to a direct question.
37
+ - **If the user says "skip docs":** comply. Say once what's skipped. Never repeat.
61
38
 
62
- ## Reference Files (when present in this repo)
39
+ ## Reference files
63
40
 
64
41
  | Need | File |
65
42
  |---|---|
66
- | Business goals, scope, MVP | `01-STRUCTURE/PROJECT.md`, `01-STRUCTURE/MVP_SCOPE.md` |
67
- | User journeys, screens | `02-HUMAN-FLOW/HUMAN_FLOW.md`, `02-HUMAN-FLOW/SCREEN_PLANNING.md` |
68
- | Specs, prompt chain | `03-INSTRUCTION/AI_BUILD_SPEC.md`, `03-INSTRUCTION/PROMPTS.md` |
69
- | Pre-launch checks | `04-PUBLISH/QA_CHECKLIST.md`, `04-PUBLISH/LAUNCH_CHECKLIST.md` |
70
- | Product-type starter pack | `06-TEMPLATES/<TYPE>_TEMPLATE.md` |
71
- | Design consistency | `12-DESIGN-SYSTEM/DESIGN_SYSTEM.md` |
72
- | Business-type → palette/font theme guide | `theme-guide.md` (this skill folder) |
73
- | Stack decisions | `13-TECH-STACK/STACK_DECISION_MATRIX.md` |
74
-
75
- If this is a project generated by `ship-create`, the same files exist under `docs/` instead of the numbered folders above.
43
+ | Vision, audience, MVP | `docs/PROJECT.md` |
44
+ | User flow, screens | `docs/HUMAN_FLOW.md` |
45
+ | Functional spec | `docs/AI_BUILD_SPEC.md` (written after home screen) |
46
+ | Stack choices | `docs/tech-stack/STACK_DECISION_MATRIX.md` |
47
+ | UI guidance | invoke `ui-ux-pro-max` skill |
48
+ | Theme palette table | `theme-guide.md` (this skill folder) |
@@ -51,10 +51,12 @@ All docs live under `docs/`:
51
51
 
52
52
  6. **Never invent business facts** (market size, pricing, real metrics, real user quotes). Draft clearly-labeled `[TBD: ...]` placeholders or ask the user.
53
53
 
54
- 7. **Once the spec is complete, apply white theme and replace the home screen — do this first, before any features.**
55
- When `docs/PROJECT.md` and `docs/HUMAN_FLOW.md` are filled and `docs/AI_BUILD_SPEC.md` exists:
56
- - **Home first:** Replace the starter kit's generic `app/page.tsx` immediately with the real entry point from `docs/HUMAN_FLOW.md`. This is always the first build output so the user sees something real right away.
57
- - **Theme (shadcn/ui):** Use Tailwind CSS v4 + shadcn/ui. Apply the user's chosen shadcn color palette (zinc/slate/blue/green/orange/rose/violet) via its CSS variable block in `app/globals.css`. Always white background. Font: modern (Inter/Geist) or luxury (Playfair Display + DM Sans). Do not write custom HSL tokens manually.
54
+ 7. **When `/build` is invoked: act immediately, no planning, no task list.**
55
+ - **First:** read `docs/PROJECT.md` + `docs/HUMAN_FLOW.md` silently. If empty, ask one question: *"Describe your idea in one sentence."* Fill docs, then continue.
56
+ - **Second:** propose 2–3 theme options (one line each), let the user pick, apply to `app/globals.css` + `app/layout.tsx`. This is the first visible output.
57
+ - **Third:** replace `app/page.tsx` with the real home screen from `HUMAN_FLOW.md`. User runs `npm run dev` and sees their product.
58
+ - **Then:** ask "which feature first?" and build just that one.
59
+ - **No task list at start.** No spec before theme. No gate that blocks visible output.
58
60
  - See `.claude/skills/ship-method/theme-guide.md` for product-type → palette reference.
59
61
 
60
62
  ## Quick Orientation for a New Agent Session
@@ -51,10 +51,12 @@ All docs live under `docs/`:
51
51
 
52
52
  6. **Never invent business facts** (market size, pricing, real metrics, real user quotes). Draft clearly-labeled `[TBD: ...]` placeholders or ask the user.
53
53
 
54
- 7. **Once the spec is complete, apply white theme and replace the home screen — do this first, before any features.**
55
- When `docs/PROJECT.md` and `docs/HUMAN_FLOW.md` are filled and `docs/AI_BUILD_SPEC.md` exists:
56
- - **Home first:** Replace the starter kit's generic `app/page.tsx` immediately with the real entry point from `docs/HUMAN_FLOW.md`. This is always the first build output so the user sees something real right away.
57
- - **Theme (shadcn/ui):** Use Tailwind CSS v4 + shadcn/ui. Apply the user's chosen shadcn color palette (zinc/slate/blue/green/orange/rose/violet) via its CSS variable block in `app/globals.css`. Always white background. Font: modern (Inter/Geist) or luxury (Playfair Display + DM Sans). Do not write custom HSL tokens manually.
54
+ 7. **When `/build` is invoked: act immediately, no planning, no task list.**
55
+ - **First:** read `docs/PROJECT.md` + `docs/HUMAN_FLOW.md` silently. If empty, ask one question: *"Describe your idea in one sentence."* Fill docs, then continue.
56
+ - **Second:** propose 2–3 theme options (one line each), let the user pick, apply to `app/globals.css` + `app/layout.tsx`. This is the first visible output.
57
+ - **Third:** replace `app/page.tsx` with the real home screen from `HUMAN_FLOW.md`. User runs `npm run dev` and sees their product.
58
+ - **Then:** ask "which feature first?" and build just that one.
59
+ - **No task list at start.** No spec before theme. No gate that blocks visible output.
58
60
  - See `.claude/skills/ship-method/theme-guide.md` for product-type → palette reference.
59
61
 
60
62
  ## Quick Orientation for a New Agent Session
@@ -51,10 +51,12 @@ All docs live under `docs/`:
51
51
 
52
52
  6. **Never invent business facts** (market size, pricing, real metrics, real user quotes). Draft clearly-labeled `[TBD: ...]` placeholders or ask the user.
53
53
 
54
- 7. **Once the spec is complete, apply white theme and replace the home screen — do this first, before any features.**
55
- When `docs/PROJECT.md` and `docs/HUMAN_FLOW.md` are filled and `docs/AI_BUILD_SPEC.md` exists:
56
- - **Home first:** Replace the starter kit's generic `app/page.tsx` immediately with the real entry point from `docs/HUMAN_FLOW.md`. This is always the first build output so the user sees something real right away.
57
- - **Theme (shadcn/ui):** Use Tailwind CSS v4 + shadcn/ui. Apply the user's chosen shadcn color palette (zinc/slate/blue/green/orange/rose/violet) via its CSS variable block in `app/globals.css`. Always white background. Font: modern (Inter/Geist) or luxury (Playfair Display + DM Sans). Do not write custom HSL tokens manually.
54
+ 7. **When `/build` is invoked: act immediately, no planning, no task list.**
55
+ - **First:** read `docs/PROJECT.md` + `docs/HUMAN_FLOW.md` silently. If empty, ask one question: *"Describe your idea in one sentence."* Fill docs, then continue.
56
+ - **Second:** propose 2–3 theme options (one line each), let the user pick, apply to `app/globals.css` + `app/layout.tsx`. This is the first visible output.
57
+ - **Third:** replace `app/page.tsx` with the real home screen from `HUMAN_FLOW.md`. User runs `npm run dev` and sees their product.
58
+ - **Then:** ask "which feature first?" and build just that one.
59
+ - **No task list at start.** No spec before theme. No gate that blocks visible output.
58
60
  - See `.claude/skills/ship-method/theme-guide.md` for product-type → palette reference.
59
61
 
60
62
  ## Quick Orientation for a New Agent Session
@@ -51,10 +51,12 @@ All docs live under `docs/`:
51
51
 
52
52
  6. **Never invent business facts** (market size, pricing, real metrics, real user quotes). Draft clearly-labeled `[TBD: ...]` placeholders or ask the user.
53
53
 
54
- 7. **Once the spec is complete, apply white theme and replace the home screen — do this first, before any features.**
55
- When `docs/PROJECT.md` and `docs/HUMAN_FLOW.md` are filled and `docs/AI_BUILD_SPEC.md` exists:
56
- - **Home first:** Replace the starter kit's generic `app/page.tsx` immediately with the real entry point from `docs/HUMAN_FLOW.md`. This is always the first build output so the user sees something real right away.
57
- - **Theme (shadcn/ui):** Use Tailwind CSS v4 + shadcn/ui. Apply the user's chosen shadcn color palette (zinc/slate/blue/green/orange/rose/violet) via its CSS variable block in `app/globals.css`. Always white background. Font: modern (Inter/Geist) or luxury (Playfair Display + DM Sans). Do not write custom HSL tokens manually.
54
+ 7. **When `/build` is invoked: act immediately, no planning, no task list.**
55
+ - **First:** read `docs/PROJECT.md` + `docs/HUMAN_FLOW.md` silently. If empty, ask one question: *"Describe your idea in one sentence."* Fill docs, then continue.
56
+ - **Second:** propose 2–3 theme options (one line each), let the user pick, apply to `app/globals.css` + `app/layout.tsx`. This is the first visible output.
57
+ - **Third:** replace `app/page.tsx` with the real home screen from `HUMAN_FLOW.md`. User runs `npm run dev` and sees their product.
58
+ - **Then:** ask "which feature first?" and build just that one.
59
+ - **No task list at start.** No spec before theme. No gate that blocks visible output.
58
60
  - See `.claude/skills/ship-method/theme-guide.md` for product-type → palette reference.
59
61
 
60
62
  ## Quick Orientation for a New Agent Session