create-ailk 0.4.0 → 0.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.
Files changed (396) hide show
  1. package/README.md +55 -8
  2. package/component-catalog.json +12248 -0
  3. package/component-catalog.md +193 -68
  4. package/dist/cli.js +29 -1
  5. package/dist/derive-architecture.js +5 -2
  6. package/dist/lib/api-boot-assertion.d.ts +46 -0
  7. package/dist/lib/api-boot-assertion.js +70 -0
  8. package/dist/lib/extract-module-bundle.d.ts +50 -2
  9. package/dist/lib/extract-module-bundle.js +223 -15
  10. package/dist/lib/fetch-guard.d.ts +28 -0
  11. package/dist/lib/fetch-guard.js +101 -0
  12. package/dist/lib/fetch-module.js +4 -0
  13. package/dist/lib/fetch-template.js +4 -0
  14. package/dist/lib/import-graph.d.ts +29 -0
  15. package/dist/lib/import-graph.js +140 -0
  16. package/dist/module-architecture.d.ts +60 -8
  17. package/dist/module-architecture.js +587 -242
  18. package/dist/parse-args.js +6 -2
  19. package/dist/programmatic.d.ts +10 -0
  20. package/dist/programmatic.js +134 -19
  21. package/dist/site-chrome.d.ts +80 -0
  22. package/dist/site-chrome.js +135 -0
  23. package/dist/surfaces.d.ts +21 -6
  24. package/dist/surfaces.js +209 -36
  25. package/dist/template-manifest.d.ts +88 -3
  26. package/dist/template-manifest.js +281 -29
  27. package/dist/template-names.d.ts +1 -1
  28. package/dist/template-names.js +2 -0
  29. package/package.json +10 -2
  30. package/site-templates/_base/ailk-template.json +6 -0
  31. package/site-templates/_base/content/en/Pricing/pricing.mdx +169 -0
  32. package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +351 -0
  33. package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +400 -0
  34. package/site-templates/marketing-site/ailk-template.json +11 -0
  35. package/site-templates/marketing-site/content/en/blog/05-this-vc-morning-routine-will-make-you-super-annoying.mdx +36 -0
  36. package/site-templates/marketing-site/provenance.json +4 -0
  37. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +288 -0
  38. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +286 -0
  39. package/site-templates/marketing-site-free/ailk-template.json +2 -1
  40. package/site-templates/minimalism-site/TEMPLATE-MANIFEST.json +279 -0
  41. package/site-templates/minimalism-site/TEMPLATE-MANIFEST.md +336 -0
  42. package/site-templates/minimalism-site/ailk-template.json +22 -0
  43. package/site-templates/minimalism-site/assets/avatar-placeholder-1.svg +7 -0
  44. package/site-templates/minimalism-site/assets/avatar-placeholder-2.svg +7 -0
  45. package/site-templates/minimalism-site/assets/avatar-placeholder-3.svg +7 -0
  46. package/site-templates/minimalism-site/assets/avatar-placeholder-4.svg +7 -0
  47. package/site-templates/minimalism-site/assets/avatar-placeholder-5.svg +7 -0
  48. package/site-templates/minimalism-site/assets/avatar-placeholder-6.svg +7 -0
  49. package/site-templates/minimalism-site/assets/hero-backdrop.svg +17 -0
  50. package/site-templates/minimalism-site/assets/logo-placeholder-1.svg +6 -0
  51. package/site-templates/minimalism-site/assets/logo-placeholder-2.svg +6 -0
  52. package/site-templates/minimalism-site/assets/logo-placeholder-3.svg +6 -0
  53. package/site-templates/minimalism-site/assets/wordmark-placeholder.svg +28 -0
  54. package/site-templates/minimalism-site/content/en/AboutPage/about.mdx +110 -0
  55. package/site-templates/minimalism-site/content/en/ContactPage/contact.mdx +68 -0
  56. package/site-templates/minimalism-site/content/en/FAQPage/faq.mdx +57 -0
  57. package/site-templates/minimalism-site/content/en/HomePage/home.mdx +244 -0
  58. package/site-templates/minimalism-site/content/en/LandingPage/referral.mdx +77 -0
  59. package/site-templates/minimalism-site/content/en/LandingPage/reports.mdx +73 -0
  60. package/site-templates/minimalism-site/content/en/ReportPage/annual-letter-2024.mdx +52 -0
  61. package/site-templates/minimalism-site/content/en/ReportPage/field-notes-2023.mdx +52 -0
  62. package/site-templates/minimalism-site/content/en/ReportPage/platform-shift-2025.mdx +52 -0
  63. package/site-templates/minimalism-site/content/en/ReportPage/state-of-the-market-2026.mdx +52 -0
  64. package/site-templates/minimalism-site/content/en/Service/first-engagement.mdx +38 -0
  65. package/site-templates/minimalism-site/content/en/Service/second-engagement.mdx +38 -0
  66. package/site-templates/minimalism-site/content/en/Service/third-engagement.mdx +38 -0
  67. package/site-templates/minimalism-site/content/en/ServicesList/services.mdx +73 -0
  68. package/site-templates/minimalism-site/flows/referral.flow.json +71 -0
  69. package/site-templates/minimalism-site/provenance.json +4 -0
  70. package/site-templates/saas-product/TEMPLATE-MANIFEST.md +61 -0
  71. package/site-templates/saas-product/ailk-template.json +16 -0
  72. package/site-templates/saas-product/provenance.json +10 -0
  73. package/site-templates/simple-service/TEMPLATE-MANIFEST.json +240 -0
  74. package/site-templates/simple-service/TEMPLATE-MANIFEST.md +278 -0
  75. package/site-templates/simple-service/ailk-template.json +10 -0
  76. package/site-templates/simple-service/content/en/AboutPage/about.mdx +38 -0
  77. package/site-templates/simple-service/content/en/ContactPage/contact.mdx +67 -0
  78. package/site-templates/simple-service/content/en/FAQPage/faq.mdx +56 -0
  79. package/site-templates/simple-service/content/en/HomePage/home.mdx +85 -0
  80. package/site-templates/simple-service/content/en/Service/landscape-design.mdx +40 -0
  81. package/site-templates/simple-service/content/en/Service/lawn-maintenance.mdx +40 -0
  82. package/site-templates/simple-service/content/en/Service/seasonal-cleanup.mdx +40 -0
  83. package/site-templates/simple-service/content/en/ServicesList/services.mdx +72 -0
  84. package/site-templates/simple-service/provenance.json +4 -0
  85. package/templates/.claude/skills/README.md +16 -25
  86. package/templates/.env.example +102 -12
  87. package/templates/.github/workflows/ci.yml +48 -0
  88. package/templates/README.md +3 -1
  89. package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-31.552Z-fallback-1789916251590-037e9005.json +8 -0
  90. package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-33.840Z-fallback-1789916253842-b52659e9.json +12 -0
  91. package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-33.844Z-fallback-1789916253845-6b266f8c.json +12 -0
  92. package/templates/apps/api/.env.example +13 -0
  93. package/templates/apps/api/CLAUDE.md +29 -2
  94. package/templates/apps/api/eslint.config.mjs +69 -0
  95. package/templates/apps/api/jest.config.cjs +3 -0
  96. package/templates/apps/api/package.json +7 -2
  97. package/templates/apps/api/src/__tests__/commerce-claim-registration.test.ts +407 -0
  98. package/templates/apps/api/src/__tests__/module-exclusion.test.ts +89 -9
  99. package/templates/apps/api/src/__tests__/review-surface-registration.test.ts +167 -0
  100. package/templates/apps/api/src/__tests__/server.test.ts +471 -0
  101. package/templates/apps/api/src/__tests__/vercel-handler.test.ts +45 -0
  102. package/templates/apps/api/src/__tests__/workspace-module-registration.test.ts +10 -1
  103. package/templates/apps/api/src/config/index.ts +62 -0
  104. package/templates/apps/api/src/config/modules.ts +7 -3
  105. package/templates/apps/api/src/lib/__mocks__/prisma.ts +241 -2
  106. package/templates/apps/api/src/lib/__tests__/auth-email-budget.test.ts +143 -0
  107. package/templates/apps/api/src/lib/__tests__/content-adapter.test.ts +23 -0
  108. package/templates/apps/api/src/lib/__tests__/deliverable-asset-store.test.ts +125 -0
  109. package/templates/apps/api/src/lib/__tests__/error-serializer.test.ts +20 -0
  110. package/templates/apps/api/src/lib/__tests__/require-commerce-claim.test.ts +317 -0
  111. package/templates/apps/api/src/lib/__tests__/site.test.ts +238 -0
  112. package/templates/apps/api/src/lib/__tests__/stripe.test.ts +75 -0
  113. package/templates/apps/api/src/lib/auth-email-budget.ts +160 -0
  114. package/templates/apps/api/src/lib/content-adapter.ts +57 -31
  115. package/templates/apps/api/src/lib/deliverable-asset-store.ts +76 -0
  116. package/templates/apps/api/src/lib/error-serializer.ts +23 -3
  117. package/templates/apps/api/src/lib/outbound-http.ts +7 -2
  118. package/templates/apps/api/src/lib/require-commerce-claim.ts +250 -0
  119. package/templates/apps/api/src/lib/site.ts +208 -9
  120. package/templates/apps/api/src/lib/stripe.ts +66 -0
  121. package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +521 -0
  122. package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +150 -0
  123. package/templates/apps/api/src/middleware/default-tenant.ts +136 -0
  124. package/templates/apps/api/src/middleware/rate-limit.ts +7 -1
  125. package/templates/apps/api/src/openapi/spec.ts +543 -2
  126. package/templates/apps/api/src/routes/auth/__tests__/auth-user.functional.test.ts +48 -0
  127. package/templates/apps/api/src/routes/auth/__tests__/transactional-email.test.ts +309 -0
  128. package/templates/apps/api/src/routes/auth/__tests__/user.test.ts +123 -0
  129. package/templates/apps/api/src/routes/auth/transactional-email.ts +273 -0
  130. package/templates/apps/api/src/routes/auth/user.ts +61 -0
  131. package/templates/apps/api/src/routes/checkout/__tests__/session.test.ts +434 -0
  132. package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +888 -0
  133. package/templates/apps/api/src/routes/checkout/index.ts +34 -0
  134. package/templates/apps/api/src/routes/checkout/session.ts +139 -0
  135. package/templates/apps/api/src/routes/checkout/sessions.ts +197 -0
  136. package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +554 -0
  137. package/templates/apps/api/src/routes/content/__tests__/list-pages.test.ts +108 -0
  138. package/templates/apps/api/src/routes/content/__tests__/search.test.ts +450 -0
  139. package/templates/apps/api/src/routes/content/get-page.ts +94 -12
  140. package/templates/apps/api/src/routes/content/index.ts +13 -73
  141. package/templates/apps/api/src/routes/content/list-pages.ts +15 -3
  142. package/templates/apps/api/src/routes/content/rate-limit.ts +15 -6
  143. package/templates/apps/api/src/routes/content/search.ts +129 -0
  144. package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +447 -31
  145. package/templates/apps/api/src/routes/leads/__tests__/list.test.ts +171 -0
  146. package/templates/apps/api/src/routes/leads/index.ts +100 -13
  147. package/templates/apps/api/src/routes/leads/list.ts +104 -0
  148. package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +51 -0
  149. package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +351 -0
  150. package/templates/apps/api/src/routes/project-listings/__tests__/founder-identity.test.ts +51 -0
  151. package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +5 -2
  152. package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +4 -1
  153. package/templates/apps/api/src/routes/project-listings/__tests__/resume-email.test.ts +58 -0
  154. package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +107 -6
  155. package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +87 -2
  156. package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +51 -0
  157. package/templates/apps/api/src/routes/project-listings/start.ts +5 -1
  158. package/templates/apps/api/src/routes/requests/__tests__/confirm.test.ts +686 -0
  159. package/templates/apps/api/src/routes/requests/confirm.ts +601 -0
  160. package/templates/apps/api/src/routes/requests/index.ts +54 -0
  161. package/templates/apps/api/src/routes/webhooks/README.md +265 -0
  162. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-dunning.test.ts +749 -0
  163. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +330 -0
  164. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +262 -0
  165. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +285 -0
  166. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +213 -0
  167. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-request-paid.test.ts +301 -0
  168. package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +648 -0
  169. package/templates/apps/api/src/routes/webhooks/index.ts +89 -0
  170. package/templates/apps/api/src/routes/webhooks/stripe.ts +1399 -0
  171. package/templates/apps/api/src/server.ts +521 -55
  172. package/templates/apps/api/src/services/__tests__/abandon-email.test.ts +39 -0
  173. package/templates/apps/api/src/services/__tests__/aeo-score.test.ts +217 -40
  174. package/templates/apps/api/src/services/__tests__/audit-log.test.ts +620 -0
  175. package/templates/apps/api/src/services/__tests__/consent.test.ts +2 -0
  176. package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +300 -5
  177. package/templates/apps/api/src/services/__tests__/deliverable-resend.test.ts +20 -0
  178. package/templates/apps/api/src/services/__tests__/dunning-policy.test.ts +368 -0
  179. package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +408 -0
  180. package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +160 -0
  181. package/templates/apps/api/src/services/__tests__/leads-inbox.test.ts +329 -0
  182. package/templates/apps/api/src/services/__tests__/listing-config.test.ts +166 -0
  183. package/templates/apps/api/src/services/__tests__/project-listings.fence.test.ts +154 -0
  184. package/templates/apps/api/src/services/__tests__/public-site-resolver.test.ts +137 -0
  185. package/templates/apps/api/src/services/__tests__/request-confirm.test.ts +444 -0
  186. package/templates/apps/api/src/services/__tests__/revalidation.test.ts +238 -0
  187. package/templates/apps/api/src/services/__tests__/verified-email.test.ts +78 -0
  188. package/templates/apps/api/src/services/abandon-email.ts +8 -2
  189. package/templates/apps/api/src/services/aeo-score.ts +49 -12
  190. package/templates/apps/api/src/services/audit-log.ts +668 -0
  191. package/templates/apps/api/src/services/consent.ts +18 -0
  192. package/templates/apps/api/src/services/default-tenant.ts +300 -0
  193. package/templates/apps/api/src/services/deliverable-fulfillment.ts +141 -13
  194. package/templates/apps/api/src/services/dunning-policy.ts +294 -0
  195. package/templates/apps/api/src/services/entitlements.ts +80 -0
  196. package/templates/apps/api/src/services/lead-promotion.ts +160 -0
  197. package/templates/apps/api/src/services/lead-routing.ts +161 -2
  198. package/templates/apps/api/src/services/leads-inbox.ts +284 -0
  199. package/templates/apps/api/src/services/listing-config.ts +27 -9
  200. package/templates/apps/api/src/services/owner-workspaces.ts +132 -0
  201. package/templates/apps/api/src/services/project-listings.ts +201 -6
  202. package/templates/apps/api/src/services/public-site-resolver.ts +106 -0
  203. package/templates/apps/api/src/services/request-confirm.ts +646 -0
  204. package/templates/apps/api/src/services/revalidation.ts +173 -0
  205. package/templates/apps/api/src/services/verified-email.ts +71 -0
  206. package/templates/apps/api/src/vercel-handler.ts +75 -35
  207. package/templates/apps/mcp/.env.example +17 -0
  208. package/templates/apps/mcp/CLAUDE.md +58 -25
  209. package/templates/apps/mcp/__tests__/aeo-cli-parity.test.ts +6 -0
  210. package/templates/apps/mcp/__tests__/audit.test.ts +137 -8
  211. package/templates/apps/mcp/__tests__/catalog-drift.test.ts +79 -36
  212. package/templates/apps/mcp/__tests__/get_offerings.test.ts +111 -2
  213. package/templates/apps/mcp/__tests__/get_pricing.test.ts +82 -2
  214. package/templates/apps/mcp/__tests__/http-transport.test.ts +541 -2
  215. package/templates/apps/mcp/__tests__/index.test.ts +39 -0
  216. package/templates/apps/mcp/__tests__/lib/audit-single-page-readiness-scan.test.ts +143 -0
  217. package/templates/apps/mcp/__tests__/lib/commerce-claim.test.ts +111 -0
  218. package/templates/apps/mcp/__tests__/lib/content-adapter-parity.test.ts +65 -0
  219. package/templates/apps/mcp/__tests__/lib/content-adapter.test.ts +188 -0
  220. package/templates/apps/mcp/__tests__/lib/fail-closed-tools.test.ts +165 -0
  221. package/templates/apps/mcp/__tests__/lib/public-workspace.test.ts +124 -0
  222. package/templates/apps/mcp/__tests__/list_pages.test.ts +116 -0
  223. package/templates/apps/mcp/__tests__/module-exclusion.test.ts +7 -1
  224. package/templates/apps/mcp/__tests__/parity.test.ts +12 -9
  225. package/templates/apps/mcp/__tests__/purchase-disabled.test.ts +89 -22
  226. package/templates/apps/mcp/__tests__/read_content.test.ts +322 -1
  227. package/templates/apps/mcp/__tests__/search_content.test.ts +105 -2
  228. package/templates/apps/mcp/__tests__/server.test.ts +67 -14
  229. package/templates/apps/mcp/__tests__/start_purchase.test.ts +252 -0
  230. package/templates/apps/mcp/__tests__/tools/{scaffold.test.ts → create_project.test.ts} +23 -23
  231. package/templates/apps/mcp/__tests__/tools/purchase.test.ts +300 -123
  232. package/templates/apps/mcp/eslint.config.mjs +69 -0
  233. package/templates/apps/mcp/jest.config.cjs +13 -0
  234. package/templates/apps/mcp/package.json +8 -2
  235. package/templates/apps/mcp/src/abuse/index.ts +159 -24
  236. package/templates/apps/mcp/src/config/modules.ts +10 -5
  237. package/templates/apps/mcp/src/http-transport.ts +102 -29
  238. package/templates/apps/mcp/src/index.ts +73 -11
  239. package/templates/apps/mcp/src/lib/audit-single-page.ts +90 -3
  240. package/templates/apps/mcp/src/lib/commerce-claim.ts +94 -0
  241. package/templates/apps/mcp/src/lib/content-adapter.ts +113 -0
  242. package/templates/apps/mcp/src/lib/empty-content-adapter.ts +139 -0
  243. package/templates/apps/mcp/src/lib/prisma.ts +39 -0
  244. package/templates/apps/mcp/src/lib/public-workspace.ts +133 -0
  245. package/templates/apps/mcp/src/lib/stripe-tax-signal.ts +59 -0
  246. package/templates/apps/mcp/src/server.ts +61 -37
  247. package/templates/apps/mcp/src/tools/audit.ts +26 -2
  248. package/templates/apps/mcp/src/tools/{scaffold.ts → create_project.ts} +3 -3
  249. package/templates/apps/mcp/src/tools/get_business_hours.ts +4 -3
  250. package/templates/apps/mcp/src/tools/get_faq.ts +4 -3
  251. package/templates/apps/mcp/src/tools/get_locations.ts +5 -3
  252. package/templates/apps/mcp/src/tools/get_offerings.ts +16 -4
  253. package/templates/apps/mcp/src/tools/get_pricing.ts +25 -6
  254. package/templates/apps/mcp/src/tools/get_site_info.ts +4 -3
  255. package/templates/apps/mcp/src/tools/index.ts +9 -7
  256. package/templates/apps/mcp/src/tools/list_pages.ts +4 -3
  257. package/templates/apps/mcp/src/tools/purchase.ts +128 -45
  258. package/templates/apps/mcp/src/tools/read_content.ts +20 -6
  259. package/templates/apps/mcp/src/tools/search_content.ts +11 -4
  260. package/templates/apps/web/.env.example +74 -8
  261. package/templates/apps/web/CLAUDE.md +46 -0
  262. package/templates/apps/web/app/.well-known/mcp.json/build-manifest.ts +18 -2
  263. package/templates/apps/web/app/.well-known/mcp.json/route.ts +11 -3
  264. package/templates/apps/web/app/[locale]/(auth)/__tests__/auth-pages.test.tsx +282 -0
  265. package/templates/apps/web/app/[locale]/(auth)/__tests__/password-reset.test.tsx +515 -0
  266. package/templates/apps/web/app/[locale]/(auth)/forgot-password/forgot-password-card.tsx +89 -0
  267. package/templates/apps/web/app/[locale]/(auth)/forgot-password/page.tsx +109 -0
  268. package/templates/apps/web/app/[locale]/(auth)/layout.tsx +92 -0
  269. package/templates/apps/web/app/[locale]/(auth)/reset-password/page.tsx +135 -0
  270. package/templates/apps/web/app/[locale]/(auth)/reset-password/reset-password-card.tsx +109 -0
  271. package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/__tests__/page.test.tsx +48 -2
  272. package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/page.tsx +20 -6
  273. package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-card.tsx +125 -0
  274. package/templates/apps/web/app/[locale]/(auth)/sign-up/page.tsx +96 -0
  275. package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up-card.tsx +101 -0
  276. package/templates/apps/web/app/[locale]/(auth)/use-auth-card-flow.ts +102 -0
  277. package/templates/apps/web/app/[locale]/(auth)/verify-email/page.tsx +87 -0
  278. package/templates/apps/web/app/[locale]/(authed)/__tests__/gate.test.tsx +20 -10
  279. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +84 -0
  280. package/templates/apps/web/app/[locale]/(authed)/account/actions.ts +58 -0
  281. package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +67 -14
  282. package/templates/apps/web/app/[locale]/(authed)/layout.tsx +19 -2
  283. package/templates/apps/web/app/[locale]/(authed)/leads/__tests__/page.test.tsx +181 -0
  284. package/templates/apps/web/app/[locale]/(authed)/leads/components/LeadsTable.tsx +137 -0
  285. package/templates/apps/web/app/[locale]/(authed)/leads/page.tsx +111 -0
  286. package/templates/apps/web/app/[locale]/(authed)/sign-in-gate.tsx +1 -1
  287. package/templates/apps/web/app/[locale]/[slug]/__tests__/page.test.tsx +111 -0
  288. package/templates/apps/web/app/[locale]/[slug]/page.tsx +65 -0
  289. package/templates/apps/web/app/[locale]/cancellation/page.tsx +44 -0
  290. package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +1 -1
  291. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.permalink.test.tsx +1 -1
  292. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.test.tsx +6 -5
  293. package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +1 -1
  294. package/templates/apps/web/app/[locale]/layout.tsx +308 -65
  295. package/templates/apps/web/app/[locale]/purchase/__tests__/cancel.test.tsx +162 -0
  296. package/templates/apps/web/app/[locale]/purchase/__tests__/module-scope.test.ts +68 -0
  297. package/templates/apps/web/app/[locale]/purchase/__tests__/success.test.tsx +361 -0
  298. package/templates/apps/web/app/[locale]/purchase/cancel/page.tsx +115 -0
  299. package/templates/apps/web/app/[locale]/purchase/success/page.tsx +307 -0
  300. package/templates/apps/web/app/[locale]/returns/page.tsx +44 -0
  301. package/templates/apps/web/app/[locale]/shipping/page.tsx +44 -0
  302. package/templates/apps/web/app/api/revalidate/__tests__/route.test.ts +670 -0
  303. package/templates/apps/web/app/api/revalidate/route.ts +194 -0
  304. package/templates/apps/web/auth.ts +407 -8
  305. package/templates/apps/web/components/google-analytics.tsx +18 -1
  306. package/templates/apps/web/eslint.config.mjs +69 -0
  307. package/templates/apps/web/jest.config.cjs +15 -19
  308. package/templates/apps/web/lib/__tests__/auth-email.test.ts +214 -0
  309. package/templates/apps/web/lib/__tests__/auth-routes.test.ts +74 -0
  310. package/templates/apps/web/lib/__tests__/meeting-slugs.test.ts +72 -0
  311. package/templates/apps/web/lib/__tests__/structured-data.test.tsx +50 -6
  312. package/templates/apps/web/lib/auth-email.ts +318 -0
  313. package/templates/apps/web/lib/auth-env.ts +85 -0
  314. package/templates/apps/web/lib/auth-routes.ts +55 -0
  315. package/templates/apps/web/lib/meeting-slugs.ts +53 -0
  316. package/templates/apps/web/lib/nav-config.ts +27 -7
  317. package/templates/apps/web/lib/server-api.ts +1 -1
  318. package/templates/apps/web/lib/site-chrome.ts +29 -0
  319. package/templates/apps/web/lib/telemetry-scrub.ts +50 -0
  320. package/templates/apps/web/messages/en.json +216 -2
  321. package/templates/apps/web/next.config.mjs +20 -0
  322. package/templates/apps/web/package.json +8 -3
  323. package/templates/apps/web/sentry.client.config.ts +131 -0
  324. package/templates/apps/web/sentry.edge.config.ts +72 -0
  325. package/templates/apps/web/sentry.server.config.ts +72 -0
  326. package/templates/apps/web/src/lib/commerce-claim.ts +33 -0
  327. package/templates/apps/web/src/lib/commerce-sections.ts +61 -0
  328. package/templates/apps/web/src/lib/routes.ts +13 -18
  329. package/templates/brand_assets/README.md +24 -0
  330. package/templates/content/en/AboutPage/about.mdx +160 -15
  331. package/templates/content/en/CancellationPolicyPage/cancellation.mdx +108 -0
  332. package/templates/content/en/ContactPage/contact.mdx +90 -13
  333. package/templates/content/en/FAQPage/faq.mdx +62 -22
  334. package/templates/content/en/HomePage/home.mdx +244 -20
  335. package/templates/content/en/LandingPage/blog.mdx +83 -0
  336. package/templates/content/en/LandingPage/features.mdx +103 -0
  337. package/templates/content/en/LandingPage/how-it-works.mdx +143 -0
  338. package/templates/content/en/ReturnsPolicyPage/returns.mdx +108 -0
  339. package/templates/content/en/ShippingPolicyPage/shipping.mdx +108 -0
  340. package/templates/database/CHANGELOG.md +149 -0
  341. package/templates/database/README.md +10 -3
  342. package/templates/database/__tests__/appointment.db.test.ts +106 -0
  343. package/templates/database/__tests__/artifact-content-store.db.test.ts +316 -0
  344. package/templates/database/__tests__/composite-fence.db.test.ts +206 -0
  345. package/templates/database/__tests__/fence-fan-out.db.test.ts +711 -0
  346. package/templates/database/__tests__/fixtures/appointment-create-probe.ts +79 -0
  347. package/templates/database/__tests__/fixtures/project-listing-consent-probe.ts +100 -0
  348. package/templates/database/__tests__/flow-workspace-key.db.test.ts +263 -0
  349. package/templates/database/__tests__/prefixed-ids.db.test.ts +85 -9
  350. package/templates/database/__tests__/project-listing-consent-at.db.test.ts +111 -0
  351. package/templates/database/__tests__/seed.test.ts +1 -1
  352. package/templates/database/__tests__/tenancy.test.ts +141 -0
  353. package/templates/database/config/schema.prisma +79 -2
  354. package/templates/database/content/schema.prisma +87 -11
  355. package/templates/database/eslint.config.mjs +69 -0
  356. package/templates/database/inbox/schema.prisma +452 -9
  357. package/templates/database/jest.config.cjs +5 -3
  358. package/templates/database/migrations/20260915120000_project_listing_solution/migration.sql +15 -0
  359. package/templates/database/migrations/20260916120000_artifact_content_store/migration.sql +158 -0
  360. package/templates/database/migrations/20260917120000_callback_request_confirm/migration.sql +40 -0
  361. package/templates/database/migrations/20260918120000_add_appointment/migration.sql +67 -0
  362. package/templates/database/migrations/20260918120000_asset_metadata_columns/migration.sql +70 -0
  363. package/templates/database/migrations/20260919120000_flow_workspace_key/migration.sql +181 -0
  364. package/templates/database/migrations/20260919130000_flow_definition_admin_columns/migration.sql +172 -0
  365. package/templates/database/migrations/20260919130000_workspace_meetings/migration.sql +41 -0
  366. package/templates/database/migrations/20260919140000_inbox_workspace_key/migration.sql +185 -0
  367. package/templates/database/migrations/20260920120000_lead_handled_at/migration.sql +70 -0
  368. package/templates/database/migrations/20260920130000_lead_workspace_source_index/migration.sql +57 -0
  369. package/templates/database/migrations/20260921120000_deliverable_grant_workspace_key/migration.sql +68 -0
  370. package/templates/database/migrations/20260922120000_tenancy_fence/migration.sql +1067 -0
  371. package/templates/database/migrations/20260923120000_listing_capturing_magnet/migration.sql +210 -0
  372. package/templates/database/package.json +7 -3
  373. package/templates/database/seed.ts +1 -1
  374. package/templates/database/tenancy.ts +86 -1
  375. package/templates/database/tsconfig.json +1 -0
  376. package/templates/package.json +1 -14
  377. package/versions.json +52 -0
  378. package/site-templates/marketing-site-free/content/en/Pricing/pricing.mdx +0 -103
  379. package/templates/apps/api/src/routes/content/__tests__/create.test.ts +0 -253
  380. package/templates/apps/api/src/routes/content/__tests__/delete.test.ts +0 -215
  381. package/templates/apps/api/src/routes/content/__tests__/update.test.ts +0 -266
  382. package/templates/apps/api/src/routes/content/create.ts +0 -115
  383. package/templates/apps/api/src/routes/content/delete.ts +0 -82
  384. package/templates/apps/api/src/routes/content/update.ts +0 -112
  385. package/templates/apps/mcp/__tests__/create_page.test.ts +0 -202
  386. package/templates/apps/mcp/__tests__/delete_page.test.ts +0 -177
  387. package/templates/apps/mcp/__tests__/update_page.test.ts +0 -196
  388. package/templates/apps/mcp/src/tools/create_page.ts +0 -96
  389. package/templates/apps/mcp/src/tools/delete_page.ts +0 -74
  390. package/templates/apps/mcp/src/tools/update_page.ts +0 -92
  391. package/templates/apps/web/app/[locale]/sign-in/sign-in-card.tsx +0 -123
  392. /package/site-templates/{marketing-site-free → _base}/content/en/PrivacyPage/privacy.mdx +0 -0
  393. /package/site-templates/{marketing-site-free → _base}/content/en/TermsPage/terms.mdx +0 -0
  394. /package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/__tests__/next-path.test.ts +0 -0
  395. /package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/next-path.ts +0 -0
  396. /package/templates/apps/web/{src/components → components}/SiteNav.tsx +0 -0
@@ -1,21 +1,17 @@
1
- /**
2
- * Site-template registry, and the roster merge that composes a template with
3
- * the modules it takes (spec 5478, unit A of the site-templates mechanism;
4
- * design record docs/specs/5462-site-templates-module-bundles-spec.md).
5
- *
6
- * A site template is DATA AND CONTENT, NOT CODE: a page roster, seed content
7
- * and config — never a `page.tsx` (parent § D16). This file declares what a
8
- * template IS. It declares no delivery channel, reads no licence, and touches
9
- * no disk: the `--template` flag, the bundle fetch and the landing order are
10
- * #5481's, and `GET /v1/templates/:name/:version` is #5480's.
11
- *
12
- * A CODE registry, not per-template JSON (parent § D2): in TypeScript,
13
- * `requires` is checked against `RECOGNIZED_MODULES` at compile time, and this
14
- * package already twice records that a fixed template FILE is the drift a
15
- * registry removes.
16
- */
17
1
  import { classifyModules } from "./lib/module-tier.js";
18
2
  import { MODULE_SLICES, normalizeModules, } from "./module-architecture.js";
3
+ /**
4
+ * The base layer's directory under `site-templates/` (#5618 D1) — the seed
5
+ * files every template reaches by declaring `extends`, installed by
6
+ * `installBundleTree` before any module bundle and only when `--template`
7
+ * is named, so a scaffold naming no template is byte-identical to before it
8
+ * existed. It is NOT a template: it has no `TEMPLATE_MANIFESTS` entry (so
9
+ * `--template _base` stays unrecognized — `RECOGNIZED_TEMPLATES` is exactly
10
+ * that object's key set) and is never published as a registry bundle. The
11
+ * leading underscore is what `build-module-bundles.mjs`'s template loop and
12
+ * the directory-scanning tests key their skip on.
13
+ */
14
+ export const BASE_LAYER_DIR = "_base";
19
15
  /** Thrown on an undeclared template name or a route claimed twice. Mirrors `SurfaceError`'s shape. */
20
16
  export class TemplateError extends Error {
21
17
  constructor(message) {
@@ -24,19 +20,148 @@ export class TemplateError extends Error {
24
20
  }
25
21
  }
26
22
  /**
27
- * `/pricing`, `/privacy` and `/terms` — the three module-less rows every
28
- * declared template brings itself.
29
- *
30
- * Module-less by construction (parent § D16): no module owns these surfaces,
31
- * their route files live in `apps/web` and are snapshotted into the starter
32
- * like every other route, and a template enables them by NAMING them in its
33
- * roster. Registering the three as `GATEABLE_SURFACES` entries is #5479's; the
34
- * rows are declared here because the roster is what a template is.
23
+ * The ten rows every declared template brings itself — one declared page set,
24
+ * whichever mechanism scaffolds each (the Free manifest § 3, #5469 D4).
25
+ *
26
+ * Two mechanisms, one roster:
27
+ *
28
+ * - `/`, `/how-it-works`, `/features`, `/about`, `/faq`, `/contact` and
29
+ * `/sign-in` are in EVERY scaffold, template or not: their content is the
30
+ * always-copied overlay (`template-overlay/content/en/**`, copied whole by
31
+ * `build-templates.mjs`) and their routes are core (`CORE_ROUTE_DIRS` —
32
+ * the three LandingPage pages render through the generic `[slug]` route,
33
+ * #5469 D2). They are declared here anyway, so the manifest a template
34
+ * publishes (`build-template-manifest.mjs`) lists the whole page set and
35
+ * the roster is what a template IS, not merely what it adds.
36
+ * - `/pricing`, `/privacy` and `/terms` are module-less by construction
37
+ * (parent § D16): no module owns these surfaces, their route files live in
38
+ * `apps/web` and are snapshotted into the starter like every other route,
39
+ * and a template enables them by NAMING them in its roster — `scaffold()`
40
+ * selects their `GATEABLE_SURFACES` entries from the merged roster's
41
+ * routes (#5469 D10). Their seed content ships ONCE, in the base layer
42
+ * (`site-templates/_base/content/**`, #5618 D1), which every template
43
+ * reaches by declaring `extends` — installed before the module bundles
44
+ * and the template's own tree, so a template may override a base page
45
+ * by shipping the same path.
46
+ *
47
+ * `/sign-in` carries no `content`: it is a code route (the SignInCard island),
48
+ * like the API-backed rows the scheduling module contributes.
49
+ *
50
+ * `/blog` is deliberately NOT here (#5469 D3): the docs-blog module
51
+ * contributes its own `/blog` row, so a base row would make `mergeRoster`
52
+ * refuse every template that takes that module. The Free teaser row is
53
+ * declared on `marketing-site-free` alone, below.
35
54
  */
36
55
  const TEMPLATE_BASE_ROSTER = [
56
+ { route: "/", content: ["content/en/HomePage/home.mdx"] },
57
+ {
58
+ route: "/how-it-works",
59
+ content: ["content/en/LandingPage/how-it-works.mdx"],
60
+ },
61
+ { route: "/features", content: ["content/en/LandingPage/features.mdx"] },
37
62
  { route: "/pricing", content: ["content/en/Pricing/pricing.mdx"] },
63
+ { route: "/about", content: ["content/en/AboutPage/about.mdx"] },
64
+ { route: "/faq", content: ["content/en/FAQPage/faq.mdx"] },
65
+ { route: "/contact", content: ["content/en/ContactPage/contact.mdx"] },
66
+ { route: "/sign-in" },
67
+ { route: "/privacy", content: ["content/en/PrivacyPage/privacy.mdx"] },
68
+ { route: "/terms", content: ["content/en/TermsPage/terms.mdx"] },
69
+ ];
70
+ /**
71
+ * The Free `/blog` teaser (#5469 D3) — one static LandingPage (`schema: Blog`)
72
+ * that says what the Pro docs-blog module gives you. It is served by the
73
+ * generic `[slug]` route over `content/en/LandingPage/blog.mdx`, which ships
74
+ * in the always-copied overlay. `/blog` degrades by DIRECTORY PRECEDENCE, not
75
+ * by a module gate: in a keyed docs-blog scaffold the module's own
76
+ * `blog/[[...slug]]/page.tsx` directory exists and Next.js resolves the static
77
+ * segment first, so the teaser file is inert; keyless, no `blog/` directory
78
+ * exists and the teaser renders. Declared on the Free template only — on
79
+ * `marketing-site` the docs-blog contribution claims the route.
80
+ */
81
+ const FREE_BLOG_TEASER_ROW = {
82
+ route: "/blog",
83
+ content: ["content/en/LandingPage/blog.mdx"],
84
+ };
85
+ /**
86
+ * `minimalism-site`'s own rows (#5589) — the one declared template that does
87
+ * not reuse `TEMPLATE_BASE_ROSTER`. Its page set is the issue's ROSTER, and
88
+ * the rows it does NOT carry are as deliberate as the ones it does: no
89
+ * `/how-it-works`, `/features` or `/pricing` (a reports-first site sells
90
+ * nothing from a pricing table), and no `/sign-in` (a code route every
91
+ * scaffold carries regardless — `CORE_ROUTE_DIRS` — which this template
92
+ * simply does not advertise as one of its pages).
93
+ *
94
+ * `/meet`, `/meet/[slug]`, `/meet/confirmed`, `/reports/[slug]` and the three
95
+ * `/flows/*` rows are absent here on purpose: they are the `scheduling` and
96
+ * `signal-collectors` modules' own `pageContributions`, merged in by
97
+ * `mergeRoster`. Declaring one here would make that merge refuse the
98
+ * template outright (§ D8's both-claimants refusal).
99
+ */
100
+ const MINIMALISM_ROSTER = [
101
+ { route: "/", content: ["content/en/HomePage/home.mdx"] },
102
+ { route: "/reports", content: ["content/en/LandingPage/reports.mdx"] },
103
+ { route: "/about", content: ["content/en/AboutPage/about.mdx"] },
104
+ { route: "/services", content: ["content/en/ServicesList/services.mdx"] },
105
+ { route: "/services/[slug]" },
106
+ { route: "/contact", content: ["content/en/ContactPage/contact.mdx"] },
107
+ { route: "/referral", content: ["content/en/LandingPage/referral.mdx"] },
108
+ { route: "/faq", content: ["content/en/FAQPage/faq.mdx"] },
109
+ { route: "/privacy", content: ["content/en/PrivacyPage/privacy.mdx"] },
110
+ { route: "/terms", content: ["content/en/TermsPage/terms.mdx"] },
111
+ ];
112
+ /**
113
+ * `simple-service`'s own rows (#6087) — the T1 lawn-care roster of Epic #5778.
114
+ * The second declared template that does not reuse `TEMPLATE_BASE_ROSTER`: a
115
+ * lawn-care site sells three named services from `/services`, not from a
116
+ * pricing table, so `/how-it-works`, `/features` and `/pricing` are absent —
117
+ * `/pricing`'s `_base` seed still lands on disk, it simply has no rostered
118
+ * route, exactly as it does for `minimalism-site`.
119
+ *
120
+ * `/meet`, `/meet/[slug]`, `/meet/confirmed` and `/appointments` are absent
121
+ * here on purpose: they are the `scheduling` module's own
122
+ * `pageContributions`, merged in by `mergeRoster`. Declaring one here would
123
+ * make that merge refuse the template outright (§ D8's both-claimants
124
+ * refusal).
125
+ *
126
+ * `/purchase/success` and `/purchase/cancel` ARE declared here, and that is a
127
+ * documented divergence from spec § D3. The spec was written at a pin where
128
+ * #6084 was expected to add the pair to `MODULE_SLICES.commerce`'s
129
+ * `pageContributions`. It did not: the operator's shape-2 disposition
130
+ * (#6084 D12) ships the return pair BASE TREE — `CORE_ROUTE_DIRS` carries
131
+ * `[locale]/purchase`, and neither `GATEABLE_SURFACES` nor
132
+ * `MODULE_SURFACES.commerce` gained an entry — so the `commerce` slice
133
+ * contributes no rows at all and nothing else claims these two routes. The
134
+ * issue's AC-1 requires the merged roster to carry them; with no module
135
+ * claimant, the template's own roster is the only place that can declare
136
+ * them, and doing so trips no collision.
137
+ *
138
+ * `/account` carries no `content`: it is a code route (the `(authed)` group),
139
+ * like `/sign-in` on the base roster and like the API-backed rows the
140
+ * scheduling module contributes. `/services/[slug]` likewise declares none —
141
+ * its seed is a whole DIRECTORY (`content/en/Service`), and a roster row's
142
+ * `content` names files the manifest generator reads frontmatter from.
143
+ */
144
+ const SIMPLE_SERVICE_ROSTER = [
145
+ { route: "/", content: ["content/en/HomePage/home.mdx"] },
146
+ { route: "/services", content: ["content/en/ServicesList/services.mdx"] },
147
+ { route: "/services/[slug]" },
148
+ { route: "/about", content: ["content/en/AboutPage/about.mdx"] },
149
+ { route: "/contact", content: ["content/en/ContactPage/contact.mdx"] },
150
+ { route: "/faq", content: ["content/en/FAQPage/faq.mdx"] },
151
+ { route: "/returns", content: ["content/en/ReturnsPolicyPage/returns.mdx"] },
152
+ {
153
+ route: "/cancellation",
154
+ content: ["content/en/CancellationPolicyPage/cancellation.mdx"],
155
+ },
156
+ {
157
+ route: "/shipping",
158
+ content: ["content/en/ShippingPolicyPage/shipping.mdx"],
159
+ },
38
160
  { route: "/privacy", content: ["content/en/PrivacyPage/privacy.mdx"] },
39
161
  { route: "/terms", content: ["content/en/TermsPage/terms.mdx"] },
162
+ { route: "/account" },
163
+ { route: "/purchase/success" },
164
+ { route: "/purchase/cancel" },
40
165
  ];
41
166
  /**
42
167
  * The three settled templates and their fixed `requires` lists — settled by
@@ -46,15 +171,21 @@ const TEMPLATE_BASE_ROSTER = [
46
171
  * `workspace` appears in none of them. It is a Pro module, but an authed app
47
172
  * surface contributing no PUBLIC page to a marketing site, and `requires`
48
173
  * names what the roster uses (parent § D15). A Pro key unlocks it regardless
49
- * of any template. `ModuleName` could not express it in any case —
50
- * `RECOGNIZED_MODULES` is the six names and `workspace` is not among them.
174
+ * of any template. `ModuleName` could express it either way — `workspace` is
175
+ * one of the seven `RECOGNIZED_MODULES` names; it is simply not one any
176
+ * template's fixed `requires` list names.
51
177
  *
52
178
  * `commerce` is likewise in none of them: it is a per-site add-on available at
53
179
  * any level, so a template for it would contradict the offering model and
54
180
  * multiply the set combinatorially (parent § D13). It still contributes rows
55
181
  * to whichever template takes it, through `mergeRoster` below.
182
+ *
183
+ * Frozen (#5507): this registry is also the issuer's authorization allowlist
184
+ * (spec 5480 § C7) — a mutable global there could be monkey-patched at
185
+ * runtime. A caller needing a different registry for a test injects its own
186
+ * fixture rather than mutating this one.
56
187
  */
57
- export const TEMPLATE_MANIFESTS = {
188
+ export const TEMPLATE_MANIFESTS = Object.freeze({
58
189
  /**
59
190
  * The Free template, and the mechanism's own proof (parent § D11): it
60
191
  * requires only `marketing`, so the keyless merge path is exercised by a
@@ -63,7 +194,9 @@ export const TEMPLATE_MANIFESTS = {
63
194
  "marketing-site-free": {
64
195
  name: "marketing-site-free",
65
196
  requires: ["marketing"],
66
- roster: TEMPLATE_BASE_ROSTER,
197
+ categories: ["accounting", "legal", "technology"],
198
+ extends: BASE_LAYER_DIR,
199
+ roster: [...TEMPLATE_BASE_ROSTER, FREE_BLOG_TEASER_ROW],
67
200
  },
68
201
  /**
69
202
  * The Pro flagship. Its module list is OFFERINGS § 4 offering 3's tiers line
@@ -73,6 +206,22 @@ export const TEMPLATE_MANIFESTS = {
73
206
  "marketing-site": {
74
207
  name: "marketing-site",
75
208
  requires: ["marketing", "docs-blog", "scheduling", "signal-collectors"],
209
+ // The flagship is the general-purpose template: it carries the whole
210
+ // registry, so every group tile on the hub has at least one template.
211
+ categories: [
212
+ "accounting",
213
+ "legal",
214
+ "hvac",
215
+ "roofing",
216
+ "plumbing",
217
+ "electrical",
218
+ "landscaping",
219
+ "auto-repair",
220
+ "real-estate",
221
+ "technology",
222
+ "medical-specialty",
223
+ ],
224
+ extends: BASE_LAYER_DIR,
76
225
  roster: TEMPLATE_BASE_ROSTER,
77
226
  },
78
227
  /**
@@ -93,9 +242,79 @@ export const TEMPLATE_MANIFESTS = {
93
242
  "signal-collectors",
94
243
  "multi-tenant",
95
244
  ],
245
+ categories: ["technology", "medical-specialty"],
246
+ extends: BASE_LAYER_DIR,
96
247
  roster: TEMPLATE_BASE_ROSTER,
97
248
  },
98
- };
249
+ /**
250
+ * The second Pro template (#5589) — the reports-first, scroll-snap deck
251
+ * site. NOT `marketing-site` plus a module: its `requires` omits
252
+ * `docs-blog`, so the two Pro templates are siblings under `_base`
253
+ * (#5618 D2), never a lineage. Level still derives from `requires`:
254
+ * `scheduling` and `signal-collectors` are paid, so `templateDelivery`
255
+ * returns `"paid"` exactly as it does for `marketing-site`.
256
+ *
257
+ * It declares its OWN roster rather than reusing `TEMPLATE_BASE_ROSTER`
258
+ * (the only declared template that does): the deck home replaces the
259
+ * document-scroll home, and `/how-it-works`, `/features`, `/pricing` and
260
+ * `/sign-in` are not pages this site has. `/pricing`'s absence is not a
261
+ * missing file — the `_base` layer still lands its seed content, and the
262
+ * roster is what the template IS, so an unrostered page simply has no
263
+ * route directory copied (`rosterSurfaces`).
264
+ *
265
+ * `/reports` and `/referral` carry no `GATEABLE_SURFACES` entry and need
266
+ * none: both are single-segment `LandingPage` slugs served by the generic
267
+ * `[slug]` route (#5469 D2), the same mechanism Free's `/how-it-works`
268
+ * uses. `/reports/[slug]` (the reader) is the signal-collectors module's
269
+ * own contributed row.
270
+ *
271
+ * `/services/[slug]` declares no `content`: its seed is a whole DIRECTORY
272
+ * (`content/en/Service`, as `routes-map.ts` records it), and a roster
273
+ * row's `content` names files the manifest generator reads frontmatter
274
+ * from. The three samples ride the template tree unmapped, landing at
275
+ * that directory.
276
+ */
277
+ "minimalism-site": {
278
+ name: "minimalism-site",
279
+ requires: ["marketing", "scheduling", "signal-collectors"],
280
+ categories: ["real-estate", "technology", "legal"],
281
+ extends: BASE_LAYER_DIR,
282
+ roster: MINIMALISM_ROSTER,
283
+ layout: {
284
+ nav: "mark-only",
285
+ footer: "centered",
286
+ announcementBanner: false,
287
+ home: "deck",
288
+ },
289
+ },
290
+ /**
291
+ * The T1 lawn-care template (#6087, Epic #5778) — the first site shape
292
+ * that composes `scheduling` and `commerce`: three priced services, a
293
+ * booker, a checkout return pair and the three #5947 policy pages.
294
+ *
295
+ * `requires` lists `marketing` for the registry convention every other
296
+ * manifest keeps: it is free (`module-tier.ts`'s `FREE_MODULES`), so it
297
+ * cannot change `templateDelivery`'s verdict, and listing it keeps the
298
+ * template's own declaration — not `DEFAULT_MODULES` — the authority for
299
+ * what a bare `--template simple-service` run installs. The order is
300
+ * `RECOGNIZED_MODULES` order, which is also `normalizeModules`' output
301
+ * order, so the declared list and the merge order read the same.
302
+ *
303
+ * `scheduling` and `commerce` are both paid, so `templateDelivery`
304
+ * returns `"paid"` from either one alone.
305
+ *
306
+ * It declares NO `layout` block: the default marketing-site chrome is the
307
+ * one a lawn-care site wants, and the issue's STOP boundary excludes
308
+ * layout knobs the spec does not name.
309
+ */
310
+ "simple-service": {
311
+ name: "simple-service",
312
+ requires: ["marketing", "scheduling", "commerce"],
313
+ categories: ["landscaping"],
314
+ extends: BASE_LAYER_DIR,
315
+ roster: SIMPLE_SERVICE_ROSTER,
316
+ },
317
+ });
99
318
  /** Look up a declared template. @throws TemplateError on an undeclared name. */
100
319
  export function resolveTemplate(name) {
101
320
  const manifest = TEMPLATE_MANIFESTS[name];
@@ -104,6 +323,39 @@ export function resolveTemplate(name) {
104
323
  }
105
324
  return manifest;
106
325
  }
326
+ /**
327
+ * Resolve a template's `extends` chain — `[name, …ancestors, "_base"]` — in
328
+ * the client registry, before any fetch (#5618 D2). `_base` is the only
329
+ * valid terminus: an `extends` naming neither it nor a declared template is
330
+ * refused naming the field, the value and the recognized set, in
331
+ * `resolveTemplate`'s message shape; a cycle is refused naming the whole
332
+ * chain. The registry is a parameter for the same reason `TEMPLATE_MANIFESTS`
333
+ * is frozen (#5507): a test needing a cyclic or dangling fixture injects its
334
+ * own rather than mutating the shipped one.
335
+ *
336
+ * @throws TemplateError on an undeclared name, an unknown parent or a cycle.
337
+ */
338
+ export function resolveTemplateLineage(name, registry = TEMPLATE_MANIFESTS) {
339
+ const chain = [];
340
+ let current = name;
341
+ for (;;) {
342
+ if (chain.includes(current)) {
343
+ throw new TemplateError(`template "${name}": cyclic "extends" chain ${[...chain, current].join(" → ")}`);
344
+ }
345
+ chain.push(current);
346
+ if (current === BASE_LAYER_DIR)
347
+ return chain;
348
+ const manifest = registry[current];
349
+ if (!manifest) {
350
+ const declared = [...Object.keys(registry), BASE_LAYER_DIR].join(", ");
351
+ if (chain.length === 1) {
352
+ throw new TemplateError(`unknown template "${current}". Declared: ${declared}`);
353
+ }
354
+ throw new TemplateError(`template "${chain[chain.length - 2]}": "extends" names unknown template "${current}". Declared: ${declared}`);
355
+ }
356
+ current = manifest.extends;
357
+ }
358
+ }
107
359
  /**
108
360
  * Compose a template's roster: its base rows, then each taken module's
109
361
  * contributed rows in `normalizeModules` order (parent § D7).
@@ -37,5 +37,5 @@
37
37
  * is consumed as a plain array across the package.
38
38
  */
39
39
  /** The declared site templates, in `TEMPLATE_MANIFESTS` declaration order. */
40
- export declare const RECOGNIZED_TEMPLATES: readonly ["marketing-site-free", "marketing-site", "saas-product"];
40
+ export declare const RECOGNIZED_TEMPLATES: readonly ["marketing-site-free", "marketing-site", "saas-product", "minimalism-site", "simple-service"];
41
41
  export type TemplateName = (typeof RECOGNIZED_TEMPLATES)[number];
@@ -41,4 +41,6 @@ export const RECOGNIZED_TEMPLATES = [
41
41
  "marketing-site-free",
42
42
  "marketing-site",
43
43
  "saas-product",
44
+ "minimalism-site",
45
+ "simple-service",
44
46
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-ailk",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Scaffolder for AI Launch Kit — `pnpm create ailk@latest <dir>` produces a starter repo consuming @working-theory/* from npm (per ADR 0007).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -12,6 +12,8 @@
12
12
  "dist",
13
13
  "templates",
14
14
  "site-templates",
15
+ "versions.json",
16
+ "component-catalog.json",
15
17
  "component-catalog.md",
16
18
  "README.md",
17
19
  "LICENSE"
@@ -22,6 +24,10 @@
22
24
  "dependencies": {
23
25
  "tar": "^7.5.21"
24
26
  },
27
+ "devDependencies": {
28
+ "@working-theory/schema": "0.25.0",
29
+ "@working-theory/templates": "0.25.0"
30
+ },
25
31
  "repository": {
26
32
  "type": "git",
27
33
  "url": "git+https://github.com/working-theory-labs/ai-launch-kit.git",
@@ -50,6 +56,8 @@
50
56
  "test:coverage": "jest --coverage",
51
57
  "build-templates": "tsc -b && node scripts/build-templates.mjs",
52
58
  "build-catalog": "node scripts/build-component-catalog.mjs",
53
- "check-catalog": "node scripts/build-component-catalog.mjs --check"
59
+ "check-catalog": "node scripts/build-component-catalog.mjs --check",
60
+ "manifest:build": "node scripts/build-template-manifest.mjs --template marketing-site-free",
61
+ "manifest:check": "node scripts/build-template-manifest.mjs --template marketing-site-free --check && node scripts/build-template-manifest.mjs --template marketing-site --check"
54
62
  }
55
63
  }
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "_base",
3
+ "requires": [],
4
+ "$comment": "The BASE LAYER (#5618 D1) — not a template. Every declared template reaches this tree by `extends: \"_base\"`; scaffold() installs it through installBundleTree before any module bundle and before the template's own tree, only when --template is named. It carries the seed content every template brings itself and no module owns: /pricing, /privacy and /terms (spec 5462 D16). No installMap: every file here is unmapped and lands at its own path under the scaffold root's content/, so a template overrides a base page by shipping the same path. Never published as a registry bundle; the leading underscore is the skip signal for the bundle producer and the site-templates/ directory scans.",
5
+ "postInstall": "Base layer installed: seed copy for /pricing, /privacy and /terms is in content/en/ - replace the placeholder copy before you launch."
6
+ }
@@ -0,0 +1,169 @@
1
+ ---
2
+ type: "Pricing"
3
+ slug: "pricing"
4
+ title: "Pays for itself in {tangible value or outcome}"
5
+ description: "This is a great place to communicate any important details you want everyone to know about how your pricing works."
6
+ sections:
7
+ [
8
+ {
9
+ "id": "hero",
10
+ "type": "hero",
11
+ "data":
12
+ {
13
+ "eyebrow": "Pricing",
14
+ "headline": "Pays for itself in {tangible value or outcome}",
15
+ "subheadline": "This is a great place to communicate any important details you want everyone to know about how your pricing works.",
16
+ "primaryCta": { "label": "{Primary CTA}", "href": "/contact" },
17
+ "secondaryCta":
18
+ {
19
+ "label": "{Secondary CTA}",
20
+ "href": "/how-it-works",
21
+ "variant": "outline",
22
+ },
23
+ },
24
+ },
25
+ {
26
+ "id": "plans",
27
+ "type": "pricing",
28
+ "data":
29
+ {
30
+ "variant": "three-tiers-with-toggle",
31
+ "eyebrow": "Pricing",
32
+ "headline": "Pays for itself in {tangible value or outcome}",
33
+ "description": "This is a great place to communicate any important details you want everyone to know about how your pricing works.",
34
+ "billingToggle": true,
35
+ "plans":
36
+ [
37
+ {
38
+ "name": "Basic plan",
39
+ "price": { "monthly": "$19/mo", "annual": "$180/yr" },
40
+ "features":
41
+ [
42
+ "Feature text goes here",
43
+ "Feature text goes here",
44
+ "Feature text goes here",
45
+ ],
46
+ "cta": { "label": "{Primary CTA}", "href": "/contact" },
47
+ },
48
+ {
49
+ "name": "Business plan",
50
+ "price": { "monthly": "$29/mo", "annual": "$280/yr" },
51
+ "features":
52
+ [
53
+ "New feature text goes here",
54
+ "Feature text goes here",
55
+ "Feature text goes here",
56
+ "Feature text goes here",
57
+ ],
58
+ "cta": { "label": "{Primary CTA}", "href": "/contact" },
59
+ },
60
+ {
61
+ "name": "Enterprise plan",
62
+ "price": { "monthly": "$49/mo", "annual": "$480/yr" },
63
+ "features":
64
+ [
65
+ "New feature text goes here",
66
+ "Feature text goes here",
67
+ "Feature text goes here",
68
+ "Feature text goes here",
69
+ "Feature text goes here",
70
+ ],
71
+ "cta": { "label": "{Primary CTA}", "href": "/contact" },
72
+ },
73
+ ],
74
+ },
75
+ },
76
+ {
77
+ "id": "logo-cloud",
78
+ "type": "logoCloud",
79
+ "data":
80
+ {
81
+ "headline": "Don’t just take our word for it",
82
+ "logos":
83
+ [
84
+ { "name": "Placeholder logo", "src": "/logomark.svg" },
85
+ { "name": "Placeholder logo", "src": "/logomark.svg" },
86
+ { "name": "Placeholder logo", "src": "/logomark.svg" },
87
+ { "name": "Placeholder logo", "src": "/logomark.svg" },
88
+ { "name": "Placeholder logo", "src": "/logomark.svg" },
89
+ { "name": "Placeholder logo", "src": "/logomark.svg" },
90
+ ],
91
+ "layout": "inline",
92
+ "treatment": "monochrome",
93
+ },
94
+ },
95
+ {
96
+ "id": "testimonial",
97
+ "type": "testimonials",
98
+ "data":
99
+ {
100
+ "variant": "featured",
101
+ "items":
102
+ [
103
+ {
104
+ "quote": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat.",
105
+ "author": "John Smith",
106
+ "role": "CEO, Google",
107
+ },
108
+ ],
109
+ },
110
+ },
111
+ {
112
+ "id": "switch",
113
+ "type": "cta",
114
+ "data":
115
+ {
116
+ "variant": "box",
117
+ "headline": "Switch to {your product} in {time amount} or less",
118
+ "description": "You shouldn't have to {experience a painful problem}. With {your product}, you can finally {achieve a value outcome or get a big improvement}.",
119
+ "chips":
120
+ ["No credit card required", "Cancel any time", "Live chat support"],
121
+ "primaryCta": { "label": "{Primary CTA}", "href": "/contact" },
122
+ "secondaryCta":
123
+ {
124
+ "label": "{Secondary CTA}",
125
+ "href": "/pricing",
126
+ "variant": "outline",
127
+ },
128
+ "closingLine": "{Social Proof goes here}",
129
+ },
130
+ },
131
+ {
132
+ "id": "faq",
133
+ "type": "faq",
134
+ "data":
135
+ {
136
+ "headline": "FAQs",
137
+ "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique.",
138
+ "supportLink": { "label": "Contact", "href": "/contact" },
139
+ "presentation": "accordion",
140
+ "initialOpen": "first",
141
+ "items":
142
+ [
143
+ {
144
+ "question": "Vel odio odit voluptatum ut veritatis. Ex rerum aspernatur voluptas unde officia. Dolorem officiis laboriosa?",
145
+ "answer": "Vel odio odit voluptatum ut veritatis. Ex rerum aspernatur voluptas unde officia. Dolorem officiis laboriosa odio odit voluptatum ut veritatis. Ex rerum aspernatur voluptas unde officia. Dolorem officiis laboriosa.",
146
+ },
147
+ {
148
+ "question": "Dolorem consectetur quia maxime sunt ut sint. Fuga optio dolorem animi voluptatem laudantium error. Quis accusamus deleniti rer?",
149
+ "answer": "Vel odio odit voluptatum ut veritatis. Ex rerum aspernatur voluptas unde officia. Dolorem officiis laboriosa odio odit voluptatum ut veritatis. Ex rerum aspernatur voluptas unde officia. Dolorem officiis laboriosa.",
150
+ },
151
+ {
152
+ "question": "Totam nihil esse. Itaque optio temporibus adipisci. Rerum nisi aperiam aliquid doloremque aliquam?",
153
+ "answer": "Vel odio odit voluptatum ut veritatis. Ex rerum aspernatur voluptas unde officia. Dolorem officiis laboriosa odio odit voluptatum ut veritatis. Ex rerum aspernatur voluptas unde officia. Dolorem officiis laboriosa.",
154
+ },
155
+ {
156
+ "question": "Delectus minus officiis et. Perspiciatis illum nisi vitae. Sed consequuntur animi corrupti distinctio ipsam accusamus?",
157
+ "answer": "Vel odio odit voluptatum ut veritatis. Ex rerum aspernatur voluptas unde officia. Dolorem officiis laboriosa odio odit voluptatum ut veritatis. Ex rerum aspernatur voluptas unde officia. Dolorem officiis laboriosa.",
158
+ },
159
+ {
160
+ "question": "Aperiam perferendis voluptatem est sit enim sed eaque perspiciatis enim. Est explicabo ea explicabo et et dignissimos aliquid?",
161
+ "answer": "Vel odio odit voluptatum ut veritatis. Ex rerum aspernatur voluptas unde officia. Dolorem officiis laboriosa odio odit voluptatum ut veritatis. Ex rerum aspernatur voluptas unde officia. Dolorem officiis laboriosa.",
162
+ },
163
+ ],
164
+ },
165
+ },
166
+ ]
167
+ ---
168
+
169
+ {/* Rendered from frontmatter `sections[]`. Seed content — replace the plan names, prices and features above with your own before you launch. */}