create-ailk 0.6.1 → 0.8.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 (324) hide show
  1. package/README.md +87 -14
  2. package/component-catalog.json +735 -95
  3. package/component-catalog.md +13 -7
  4. package/dist/add-module-core.d.ts +22 -0
  5. package/dist/add-module-core.js +50 -0
  6. package/dist/add-module.d.ts +89 -0
  7. package/dist/add-module.js +334 -0
  8. package/dist/category-profile.d.ts +126 -0
  9. package/dist/category-profile.js +262 -0
  10. package/dist/cli.js +89 -29
  11. package/dist/index.d.ts +4 -2
  12. package/dist/index.js +6 -1
  13. package/dist/lib/extract-module-bundle.d.ts +56 -1
  14. package/dist/lib/extract-module-bundle.js +170 -14
  15. package/dist/lib/license-key-files.d.ts +15 -0
  16. package/dist/lib/license-key-files.js +102 -0
  17. package/dist/lib/module-license-gate.d.ts +79 -0
  18. package/dist/lib/module-license-gate.js +160 -12
  19. package/dist/lib/module-tier.d.ts +45 -16
  20. package/dist/lib/module-tier.js +76 -0
  21. package/dist/lib/pro-callout.d.ts +16 -10
  22. package/dist/lib/pro-callout.js +34 -3
  23. package/dist/license-set.d.ts +34 -0
  24. package/dist/license-set.js +194 -0
  25. package/dist/module-architecture.d.ts +20 -0
  26. package/dist/module-architecture.js +77 -19
  27. package/dist/modules/commerce/0.6.1.tar.gz +0 -0
  28. package/dist/modules/docs-blog/0.6.1.tar.gz +0 -0
  29. package/dist/modules/marketing-site/0.6.1.tar.gz +0 -0
  30. package/dist/modules/minimalism-site/0.6.1.tar.gz +0 -0
  31. package/dist/modules/multi-tenant/0.6.1.tar.gz +0 -0
  32. package/dist/modules/saas-product/0.6.1.tar.gz +0 -0
  33. package/dist/modules/scheduling/0.6.1.tar.gz +0 -0
  34. package/dist/modules/signal-collectors/0.6.1.tar.gz +0 -0
  35. package/dist/modules/simple-service/0.6.1.tar.gz +0 -0
  36. package/dist/modules/workspace/0.6.1.tar.gz +0 -0
  37. package/dist/parse-args.d.ts +85 -10
  38. package/dist/parse-args.js +227 -15
  39. package/dist/programmatic.d.ts +81 -5
  40. package/dist/programmatic.js +291 -32
  41. package/dist/site-chrome.d.ts +18 -7
  42. package/dist/site-chrome.js +32 -14
  43. package/dist/site-roster.d.ts +127 -0
  44. package/dist/site-roster.js +499 -0
  45. package/dist/starter-grid.d.ts +216 -0
  46. package/dist/starter-grid.js +449 -0
  47. package/dist/surfaces.d.ts +20 -7
  48. package/dist/surfaces.js +204 -24
  49. package/dist/sync-routes.d.ts +9 -0
  50. package/dist/sync-routes.js +2 -2
  51. package/dist/template-manifest.d.ts +94 -19
  52. package/dist/template-manifest.js +170 -289
  53. package/dist/template-names.d.ts +14 -12
  54. package/dist/template-names.js +13 -17
  55. package/package.json +6 -4
  56. package/site-templates/_base/content/en/BookCall/book-call.mdx +52 -0
  57. package/site-templates/_base/content/en/Feature/feature-1.mdx +69 -0
  58. package/site-templates/_base/content/en/Feature/feature-2.mdx +69 -0
  59. package/site-templates/_base/content/en/Feature/feature-3.mdx +69 -0
  60. package/site-templates/_base/content/en/FeaturesList/features.mdx +91 -0
  61. package/site-templates/_base/content/en/GlossaryList/glossary.mdx +65 -0
  62. package/site-templates/_base/content/en/NewsletterSignup/newsletter.mdx +67 -0
  63. package/site-templates/_base/content/en/PortfolioItem/sample-four.mdx +105 -0
  64. package/site-templates/_base/content/en/PortfolioItem/sample-one.mdx +34 -0
  65. package/site-templates/_base/content/en/PortfolioItem/sample-three.mdx +94 -0
  66. package/site-templates/_base/content/en/PortfolioItem/sample-two.mdx +45 -0
  67. package/site-templates/_base/content/en/PortfolioList/portfolio.mdx +169 -0
  68. package/site-templates/_base/content/en/Pricing/pricing.mdx +71 -62
  69. package/site-templates/_base/content/en/PrivacyPage/privacy.mdx +17 -3
  70. package/site-templates/_base/content/en/TermsPage/terms.mdx +2 -3
  71. package/site-templates/_base/content/en/UseCase/persona-1.mdx +69 -0
  72. package/site-templates/_base/content/en/UseCase/persona-2.mdx +69 -0
  73. package/site-templates/_base/content/en/UseCase/persona-3.mdx +69 -0
  74. package/site-templates/designs/minimalism/DESIGN.md +162 -0
  75. package/site-templates/designs/standard/DESIGN.md +167 -0
  76. package/site-templates/marketing-site/DESIGN.md +59 -40
  77. package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +376 -57
  78. package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +230 -90
  79. package/site-templates/marketing-site/ailk-template.json +1 -0
  80. package/site-templates/marketing-site-free/DESIGN.md +59 -40
  81. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +323 -63
  82. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +323 -113
  83. package/site-templates/marketing-site-free/ailk-template.json +3 -2
  84. package/site-templates/marketing-site-free/content/en/NewsletterSignup/newsletter.mdx +59 -0
  85. package/site-templates/marketing-site-free/content/en/Pricing/pricing.mdx +199 -0
  86. package/site-templates/minimalism-site/DESIGN.md +54 -40
  87. package/site-templates/minimalism-site/TEMPLATE-MANIFEST.json +246 -19
  88. package/site-templates/minimalism-site/TEMPLATE-MANIFEST.md +173 -51
  89. package/site-templates/minimalism-site/ailk-template.json +3 -2
  90. package/site-templates/minimalism-site/content/en/BookCall/book-call.mdx +52 -0
  91. package/site-templates/minimalism-site/content/en/ContactPage/contact.mdx +3 -37
  92. package/site-templates/minimalism-site/content/en/PurchasePolicyPage/purchase-policy.mdx +123 -0
  93. package/site-templates/saas-product/DESIGN.md +59 -40
  94. package/site-templates/saas-product/TEMPLATE-MANIFEST.md +9 -1
  95. package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.json +369 -0
  96. package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.md +302 -0
  97. package/site-templates/shapes/local-business-service/ailk-template.json +10 -0
  98. package/site-templates/{simple-service → shapes/local-business-service}/content/en/AboutPage/about.mdx +4 -4
  99. package/site-templates/shapes/local-business-service/content/en/HomePage/home.mdx +70 -0
  100. package/site-templates/shapes/local-business-service/content/en/NewsletterSignup/newsletter.mdx +59 -0
  101. package/site-templates/shapes/local-business-service/content/en/PortfolioList/portfolio.mdx +171 -0
  102. package/site-templates/shapes/local-business-service/content/en/PurchasePolicyPage/purchase-policy.mdx +123 -0
  103. package/site-templates/shapes/local-business-service/content/en/Service/first-job.mdx +39 -0
  104. package/site-templates/shapes/local-business-service/content/en/Service/second-job.mdx +39 -0
  105. package/site-templates/shapes/local-business-service/content/en/Service/third-job.mdx +39 -0
  106. package/site-templates/shapes/local-business-service/content/en/ServicesList/services.mdx +61 -0
  107. package/site-templates/shapes/local-business-service/provenance.json +4 -0
  108. package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.json +362 -0
  109. package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.md +300 -0
  110. package/site-templates/shapes/organization-service/ailk-template.json +10 -0
  111. package/site-templates/shapes/organization-service/content/en/AboutPage/about.mdx +38 -0
  112. package/site-templates/shapes/organization-service/content/en/HomePage/home.mdx +85 -0
  113. package/site-templates/shapes/organization-service/content/en/NewsletterSignup/newsletter.mdx +59 -0
  114. package/site-templates/shapes/organization-service/content/en/PortfolioList/portfolio.mdx +171 -0
  115. package/site-templates/shapes/organization-service/content/en/PurchasePolicyPage/purchase-policy.mdx +123 -0
  116. package/site-templates/shapes/organization-service/content/en/Service/first-engagement.mdx +38 -0
  117. package/site-templates/shapes/organization-service/content/en/Service/second-engagement.mdx +38 -0
  118. package/site-templates/shapes/organization-service/content/en/Service/third-engagement.mdx +38 -0
  119. package/site-templates/shapes/organization-service/content/en/ServicesList/services.mdx +72 -0
  120. package/site-templates/shapes/organization-service/provenance.json +4 -0
  121. package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.json +369 -0
  122. package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.md +299 -0
  123. package/site-templates/shapes/person-service/ailk-template.json +10 -0
  124. package/site-templates/shapes/person-service/content/_site.mdx +85 -0
  125. package/site-templates/shapes/person-service/content/en/AboutPage/about.mdx +39 -0
  126. package/site-templates/shapes/person-service/content/en/HomePage/home.mdx +97 -0
  127. package/site-templates/shapes/person-service/content/en/NewsletterSignup/newsletter.mdx +59 -0
  128. package/site-templates/shapes/person-service/content/en/PortfolioList/portfolio.mdx +169 -0
  129. package/site-templates/shapes/person-service/content/en/PurchasePolicyPage/purchase-policy.mdx +123 -0
  130. package/site-templates/shapes/person-service/content/en/Service/first-engagement.mdx +38 -0
  131. package/site-templates/shapes/person-service/content/en/Service/second-engagement.mdx +38 -0
  132. package/site-templates/shapes/person-service/content/en/Service/third-engagement.mdx +38 -0
  133. package/site-templates/{simple-service → shapes/person-service}/content/en/ServicesList/services.mdx +16 -16
  134. package/site-templates/shapes/person-service/provenance.json +4 -0
  135. package/templates/.claude/settings.json +15 -0
  136. package/templates/.claude/skills/README.md +10 -9
  137. package/templates/.claude/skills/{scaffold-auth → add-auth}/SKILL.md +7 -7
  138. package/templates/.claude/skills/{scaffold-auth → add-auth}/references/env-vars.md +2 -2
  139. package/templates/.claude/skills/{scaffold-auth → add-auth}/references/oauth-app-setup.md +3 -3
  140. package/templates/.claude/skills/{scaffold-page → add-page}/SKILL.md +76 -62
  141. package/templates/.claude/skills/add-page/references/page-types.md +115 -0
  142. package/templates/.claude/skills/{scaffold-page → add-page}/templates/content.mdx.tmpl +4 -4
  143. package/templates/.claude/skills/{scaffold-page → add-page}/templates/route.tsx.tmpl +1 -1
  144. package/templates/.claude/skills/{audit-site → check-site}/SKILL.md +16 -17
  145. package/templates/.claude/skills/{audit-site → check-site}/references/common-errors.md +3 -3
  146. package/templates/.claude/skills/{audit-site → check-site}/references/findings-glossary.md +16 -14
  147. package/templates/.claude/skills/{audit-site → check-site}/references/severity-levels.md +1 -1
  148. package/templates/.claude/skills/fill-page/SKILL.md +180 -0
  149. package/templates/.claude/skills/fill-page/profiles/plumbing.yaml +80 -0
  150. package/templates/.claude/skills/fill-page/references/section-sources.md +41 -0
  151. package/templates/.claude/skills/setup-database/SKILL.md +156 -0
  152. package/templates/.claude/skills/setup-database/scripts/verify-inbox.ts +154 -0
  153. package/templates/.claude/skills/{provision-config → setup-deploy}/SKILL.md +73 -41
  154. package/templates/.claude/skills/{provision-config → setup-deploy}/references/env-vars-template.md +21 -30
  155. package/templates/.claude/skills/setup-deploy/references/runbook-template.md +204 -0
  156. package/templates/.claude/skills/{provision-config → setup-deploy}/references/vercel-json-template.md +1 -1
  157. package/templates/.claude/skills/upgrade-plan/SKILL.md +117 -0
  158. package/templates/.claude/skills/upgrade-plan/references/options.md +24 -0
  159. package/templates/.claude/skills/upgrade-plan/scripts/upgrade-plan.ts +527 -0
  160. package/templates/.github/workflows/ci.yml +5 -0
  161. package/templates/CLAUDE.md +8 -2
  162. package/templates/DESIGN.md +56 -44
  163. package/templates/README.md +116 -46
  164. package/templates/apps/api/.env.example +2 -1
  165. package/templates/apps/api/CLAUDE.md +3 -2
  166. package/templates/apps/api/package.json +1 -1
  167. package/templates/apps/api/src/config/__tests__/index.test.ts +146 -10
  168. package/templates/apps/api/src/config/index.ts +46 -14
  169. package/templates/apps/api/src/lib/__tests__/lead-capture-store.test.ts +30 -0
  170. package/templates/apps/api/src/lib/__tests__/lead-delivery.test.ts +96 -0
  171. package/templates/apps/api/src/lib/lead-capture-store.ts +12 -0
  172. package/templates/apps/api/src/lib/lead-delivery.ts +52 -0
  173. package/templates/apps/api/src/lib/license-key-store.ts +45 -0
  174. package/templates/apps/api/src/lib/site.ts +5 -3
  175. package/templates/apps/api/src/middleware/__tests__/auth.database-not-configured.test.ts +97 -0
  176. package/templates/apps/api/src/middleware/auth.ts +9 -0
  177. package/templates/apps/api/src/openapi/spec.ts +63 -12
  178. package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +127 -5
  179. package/templates/apps/api/src/routes/content/get-page.ts +86 -2
  180. package/templates/apps/api/src/routes/leads/__tests__/capture.test.ts +88 -0
  181. package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +100 -0
  182. package/templates/apps/api/src/routes/leads/index.ts +46 -1
  183. package/templates/apps/api/src/server.ts +13 -0
  184. package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +194 -0
  185. package/templates/apps/api/src/services/__tests__/leads-inbox.test.ts +124 -0
  186. package/templates/apps/api/src/services/lead-routing.ts +140 -30
  187. package/templates/apps/api/src/services/leads-inbox.ts +48 -11
  188. package/templates/apps/mcp/__tests__/audit-single-page.test.ts +75 -1
  189. package/templates/apps/mcp/__tests__/lib/audit-single-page-readiness-scan.test.ts +57 -9
  190. package/templates/apps/mcp/__tests__/parity.test.ts +5 -4
  191. package/templates/apps/mcp/package.json +2 -1
  192. package/templates/apps/mcp/src/index.ts +7 -0
  193. package/templates/apps/mcp/src/lib/audit-single-page.ts +36 -10
  194. package/templates/apps/mcp/src/lib/license-key-store.ts +45 -0
  195. package/templates/apps/mcp/src/tools/create_project.ts +21 -6
  196. package/templates/apps/web/__tests__/setup.ts +18 -0
  197. package/templates/apps/web/app/[locale]/(auth)/forgot-password/forgot-password-card.tsx +10 -2
  198. package/templates/apps/web/app/[locale]/(auth)/layout.tsx +2 -2
  199. package/templates/apps/web/app/[locale]/(auth)/reset-password/reset-password-card.tsx +10 -4
  200. package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-card.tsx +22 -14
  201. package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up-card.tsx +26 -18
  202. package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +12 -4
  203. package/templates/apps/web/app/[locale]/[slug]/__tests__/page.test.tsx +35 -4
  204. package/templates/apps/web/app/[locale]/[slug]/page.tsx +49 -9
  205. package/templates/apps/web/app/[locale]/book-a-call/page.tsx +50 -0
  206. package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +3 -3
  207. package/templates/apps/web/app/[locale]/case-studies/__tests__/page-renderer-migration.test.tsx +3 -3
  208. package/templates/apps/web/app/[locale]/clients/__tests__/page-renderer-migration.test.tsx +3 -4
  209. package/templates/apps/web/app/[locale]/contact/ConversationalIntakeWrapper.tsx +67 -29
  210. package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +94 -11
  211. package/templates/apps/web/app/[locale]/contact/__tests__/contact-form-text.test.ts +32 -0
  212. package/templates/apps/web/app/[locale]/contact/__tests__/contact.actions.test.ts +129 -0
  213. package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +228 -86
  214. package/templates/apps/web/app/[locale]/contact/contact-form-text.ts +50 -0
  215. package/templates/apps/web/app/[locale]/contact/contact.actions.ts +73 -20
  216. package/templates/apps/web/app/[locale]/contact/page.tsx +115 -7
  217. package/templates/apps/web/app/[locale]/contact/site-contact-email.ts +54 -0
  218. package/templates/apps/web/app/[locale]/courses/__tests__/page-renderer-migration.test.tsx +3 -3
  219. package/templates/apps/web/app/[locale]/downloads/__tests__/page-renderer-migration.test.tsx +3 -4
  220. package/templates/apps/web/app/[locale]/events/__tests__/page-renderer-migration.test.tsx +4 -5
  221. package/templates/apps/web/app/[locale]/features/[slug]/page.tsx +64 -0
  222. package/templates/apps/web/app/[locale]/features/page.tsx +52 -0
  223. package/templates/apps/web/app/[locale]/glossary/page.tsx +50 -0
  224. package/templates/apps/web/app/[locale]/how-to/__tests__/page-renderer-migration.test.tsx +1 -1
  225. package/templates/apps/web/app/[locale]/how-to/page.tsx +25 -15
  226. package/templates/apps/web/app/[locale]/integrations/__tests__/page-renderer-migration.test.tsx +3 -3
  227. package/templates/apps/web/app/[locale]/layout.tsx +146 -52
  228. package/templates/apps/web/app/[locale]/newsletter/__tests__/newsletter.actions.test.ts +75 -0
  229. package/templates/apps/web/app/[locale]/newsletter/__tests__/page.test.tsx +163 -0
  230. package/templates/apps/web/app/[locale]/newsletter/newsletter.actions.ts +66 -0
  231. package/templates/apps/web/app/[locale]/newsletter/page.tsx +142 -0
  232. package/templates/apps/web/app/[locale]/partners/__tests__/page-renderer-migration.test.tsx +3 -3
  233. package/templates/apps/web/app/[locale]/portfolio/[slug]/page.tsx +59 -0
  234. package/templates/apps/web/app/[locale]/portfolio/page.tsx +49 -0
  235. package/templates/apps/web/app/[locale]/products/__tests__/page-renderer-migration.test.tsx +3 -3
  236. package/templates/apps/web/app/[locale]/projects/__tests__/page-renderer-migration.test.tsx +4 -5
  237. package/templates/apps/web/app/[locale]/{cancellation → purchase-policy}/page.tsx +15 -10
  238. package/templates/apps/web/app/[locale]/resources/__tests__/page-renderer-migration.test.tsx +4 -5
  239. package/templates/apps/web/app/[locale]/services/__tests__/page-renderer-migration.test.tsx +3 -3
  240. package/templates/apps/web/app/[locale]/software/[slug]/page.tsx +14 -1
  241. package/templates/apps/web/app/[locale]/software/__tests__/page-renderer-migration.test.tsx +1 -1
  242. package/templates/apps/web/app/[locale]/software/page.tsx +25 -15
  243. package/templates/apps/web/app/[locale]/tech-articles/__tests__/page-renderer-migration.test.tsx +1 -1
  244. package/templates/apps/web/app/[locale]/tech-articles/page.tsx +25 -15
  245. package/templates/apps/web/app/[locale]/testimonials/__tests__/page-renderer-migration.test.tsx +3 -4
  246. package/templates/apps/web/app/[locale]/use-cases/[slug]/page.tsx +63 -0
  247. package/templates/apps/web/app/[locale]/use-cases/__tests__/page-renderer-migration.test.tsx +3 -4
  248. package/templates/apps/web/app/[locale]/use-cases/page.tsx +15 -2
  249. package/templates/apps/web/app/[locale]/whitepapers/__tests__/page-renderer-migration.test.tsx +1 -1
  250. package/templates/apps/web/app/[locale]/whitepapers/page.tsx +25 -15
  251. package/templates/apps/web/app/error-fallback.tsx +17 -45
  252. package/templates/apps/web/app/not-found.tsx +14 -38
  253. package/templates/apps/web/app/og/route.tsx +6 -0
  254. package/templates/apps/web/eslint.config.mjs +3 -0
  255. package/templates/apps/web/instrumentation.ts +14 -0
  256. package/templates/apps/web/lib/__tests__/structured-data.test.tsx +38 -0
  257. package/templates/apps/web/lib/license-key-store.ts +52 -0
  258. package/templates/apps/web/lib/nav-config.ts +82 -18
  259. package/templates/apps/web/lib/prisma-for-auth.ts +9 -6
  260. package/templates/apps/web/lib/site-brand.tsx +1 -1
  261. package/templates/apps/web/lib/site-chrome.ts +3 -3
  262. package/templates/apps/web/lib/site-identity.ts +148 -0
  263. package/templates/apps/web/lib/site-theme.ts +11 -0
  264. package/templates/apps/web/messages/en.json +78 -37
  265. package/templates/apps/web/public/android-chrome-192x192.png +0 -0
  266. package/templates/apps/web/public/android-chrome-512x512.png +0 -0
  267. package/templates/apps/web/public/apple-touch-icon.png +0 -0
  268. package/templates/apps/web/public/favicon-16x16.png +0 -0
  269. package/templates/apps/web/public/favicon-32x32.png +0 -0
  270. package/templates/apps/web/public/favicon.ico +0 -0
  271. package/templates/apps/web/public/favicon.svg +3 -3
  272. package/templates/apps/web/public/logomark.svg +6 -3
  273. package/templates/apps/web/public/logos/ai-launch-kit-light.svg +4 -0
  274. package/templates/apps/web/public/logos/ai-launch-kit.svg +4 -0
  275. package/templates/apps/web/public/logos/atomic-agent-light.svg +4 -0
  276. package/templates/apps/web/public/logos/atomic-agent.svg +4 -0
  277. package/templates/apps/web/public/logos/salessmyth-light.svg +4 -0
  278. package/templates/apps/web/public/logos/salessmyth.svg +4 -0
  279. package/templates/apps/web/public/site.webmanifest +4 -4
  280. package/templates/apps/web/public/wordmark-light.svg +4 -0
  281. package/templates/apps/web/public/wordmark.svg +3 -3
  282. package/templates/apps/web/security-headers.config.ts +5 -1
  283. package/templates/content/_site.mdx +25 -0
  284. package/templates/content/en/AboutPage/about.mdx +150 -144
  285. package/templates/content/en/BlogList/blog.mdx +126 -0
  286. package/templates/content/en/ContactPage/contact.mdx +21 -92
  287. package/templates/content/en/FAQPage/faq.mdx +86 -57
  288. package/templates/content/en/HomePage/home.mdx +268 -217
  289. package/templates/content/en/PurchasePolicyPage/purchase-policy.mdx +181 -0
  290. package/templates/content/en/UseCasesList/use-cases.mdx +170 -0
  291. package/templates/database/CHANGELOG.md +14 -0
  292. package/templates/database/package.json +1 -1
  293. package/templates/package.json +3 -1
  294. package/templates/tools/lint-architecture.ts +315 -0
  295. package/templates/tools/tsconfig.json +14 -0
  296. package/versions.json +33 -33
  297. package/site-templates/simple-service/DESIGN.md +0 -148
  298. package/site-templates/simple-service/TEMPLATE-MANIFEST.json +0 -240
  299. package/site-templates/simple-service/TEMPLATE-MANIFEST.md +0 -278
  300. package/site-templates/simple-service/ailk-template.json +0 -10
  301. package/site-templates/simple-service/content/en/ContactPage/contact.mdx +0 -67
  302. package/site-templates/simple-service/content/en/FAQPage/faq.mdx +0 -56
  303. package/site-templates/simple-service/content/en/HomePage/home.mdx +0 -85
  304. package/site-templates/simple-service/content/en/Service/landscape-design.mdx +0 -40
  305. package/site-templates/simple-service/content/en/Service/lawn-maintenance.mdx +0 -40
  306. package/site-templates/simple-service/content/en/Service/seasonal-cleanup.mdx +0 -40
  307. package/site-templates/simple-service/provenance.json +0 -4
  308. package/templates/.claude/skills/add-schema/SKILL.md +0 -277
  309. package/templates/.claude/skills/add-schema/references/conflict-handling.md +0 -200
  310. package/templates/.claude/skills/add-schema/references/file-finding.md +0 -123
  311. package/templates/.claude/skills/add-schema/references/schema-types.md +0 -234
  312. package/templates/.claude/skills/provision-config/references/runbook-template.md +0 -248
  313. package/templates/.claude/skills/scaffold-page/references/page-types.md +0 -169
  314. package/templates/.claude/skills/suggest-site-pages/SKILL.md +0 -242
  315. package/templates/.claude/skills/suggest-site-pages/references/categories.md +0 -287
  316. package/templates/apps/web/app/[locale]/contact/ContactForm.tsx +0 -150
  317. package/templates/apps/web/app/[locale]/returns/page.tsx +0 -44
  318. package/templates/apps/web/app/[locale]/shipping/page.tsx +0 -44
  319. package/templates/content/en/CancellationPolicyPage/cancellation.mdx +0 -108
  320. package/templates/content/en/LandingPage/blog.mdx +0 -83
  321. package/templates/content/en/LandingPage/features.mdx +0 -103
  322. package/templates/content/en/LandingPage/how-it-works.mdx +0 -143
  323. package/templates/content/en/ReturnsPolicyPage/returns.mdx +0 -108
  324. package/templates/content/en/ShippingPolicyPage/shipping.mdx +0 -108
@@ -1,5 +1,46 @@
1
1
  import { classifyModules } from "./lib/module-tier.js";
2
2
  import { MODULE_SLICES, normalizeModules, } from "./module-architecture.js";
3
+ import { ALIASES, SHAPE_ROSTERS, SHAPE_STARTER_ALIASES, } from "./starter-grid.js";
4
+ import { RECOGNIZED_TEMPLATES } from "./template-names.js";
5
+ /**
6
+ * The closed **shape** enum (starter-template contract § The three axes,
7
+ * operator-disposed 2026-09-24) — `<primary node>-<offering node>` in
8
+ * schema.org terms, kebab-cased. Declared beside `TemplateManifest`, the same
9
+ * pattern `ModuleName` uses in `parse-args.ts`: a `readonly [...] as const`
10
+ * array plus its derived literal-union type, so an unrecognized value fails
11
+ * type-check rather than a runtime validation.
12
+ */
13
+ export const STARTER_SHAPES = [
14
+ "organization-product",
15
+ "organization-service",
16
+ "local-business-service",
17
+ "local-business-product",
18
+ "person-service",
19
+ ];
20
+ /**
21
+ * The closed **design** enum (same contract section) — the look applied to
22
+ * any shape; orthogonal to it by construction.
23
+ */
24
+ export const STARTER_DESIGNS = ["standard", "minimalism"];
25
+ /**
26
+ * Each design's layout knobs (#6638) — the part of a design that is chrome,
27
+ * so a template names `design:` instead of re-typing a `layout` block, and any
28
+ * shape can wear any design. `resolveSiteChrome(layout, design)` reads it:
29
+ * an explicit `TemplateManifest.layout` wins field by field, then this, then
30
+ * `DEFAULT_SITE_CHROME`. `standard` declares nothing — it IS the defaults, so
31
+ * spelling them out here would be a second copy of them to drift. A plain
32
+ * object, not a file: this module touches no disk. The design's documented
33
+ * look lives in `site-templates/designs/<design>/DESIGN.md`.
34
+ */
35
+ export const DESIGN_LAYOUTS = {
36
+ standard: {},
37
+ minimalism: {
38
+ nav: "mark-only",
39
+ footer: "centered",
40
+ announcementBanner: false,
41
+ home: "deck",
42
+ },
43
+ };
3
44
  /**
4
45
  * The base layer's directory under `site-templates/` (#5618 D1) — the seed
5
46
  * files every template reaches by declaring `extends`, installed by
@@ -12,6 +53,16 @@ import { MODULE_SLICES, normalizeModules, } from "./module-architecture.js";
12
53
  * the directory-scanning tests key their skip on.
13
54
  */
14
55
  export const BASE_LAYER_DIR = "_base";
56
+ /**
57
+ * Whether a merged roster declares one scaffold-relative content file
58
+ * (#6646 D1): named by a row's `content`, or under a row's `contentDir`.
59
+ * The one predicate the scaffold's per-row install and the manifest
60
+ * generator's install-diff both read, so the two cannot disagree.
61
+ */
62
+ export function rosterDeclares(roster, path) {
63
+ return roster.some((row) => (row.content ?? []).includes(path) ||
64
+ (row.contentDir !== undefined && path.startsWith(`${row.contentDir}/`)));
65
+ }
15
66
  /** Thrown on an undeclared template name or a route claimed twice. Mirrors `SurfaceError`'s shape. */
16
67
  export class TemplateError extends Error {
17
68
  constructor(message) {
@@ -20,308 +71,138 @@ export class TemplateError extends Error {
20
71
  }
21
72
  }
22
73
  /**
23
- * The ten rows every declared template brings itself — one declared page set,
24
- * whichever mechanism scaffolds each (the Free manifest § 3, #5469 D4).
74
+ * Compose one alias into its manifest (#6753 D5, extended #6757 D7): the
75
+ * shape's standard roster, minus the alias's `drop` routes, with its `add`
76
+ * rows inserted before the `before` route (appended when there is none),
77
+ * then each `insert` group's rows spliced before ITS OWN `before` route, in
78
+ * array order — the one alias with more than one anchor point
79
+ * (`minimalism-site`) uses this to interleave its own rows into the shape's
80
+ * row order. Rows are pushed by reference, never cloned, so
81
+ * `manifest.roster.includes(row)` identity checks downstream keep working.
82
+ * Every refusal names the alias, so a malformed table fails at load — the
83
+ * shipped one — or in the test that injected it.
25
84
  *
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.
85
+ * @throws TemplateError on a `drop`, `before` or an `insert` group's `before`
86
+ * naming a route the shape (or the roster built so far) does not carry, a
87
+ * `before` with nothing to add, an `insert` group with empty `rows`, an
88
+ * `add`/`insert` row whose route a kept shape row already claims, or two
89
+ * `add`/`insert` rows naming one route.
54
90
  */
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"] },
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
- ];
91
+ function composeManifest(name, alias) {
92
+ const rows = SHAPE_ROSTERS[alias.shape];
93
+ const { drop = [], add = [], before, insert = [] } = alias.differences ?? {};
94
+ for (const route of drop) {
95
+ if (!rows.some((row) => row.route === route)) {
96
+ throw new TemplateError(`alias "${name}": differences.drop names "${route}", which shape "${alias.shape}" does not carry`);
97
+ }
98
+ }
99
+ const kept = rows.filter((row) => !drop.includes(row.route));
100
+ let roster;
101
+ if (before === undefined) {
102
+ roster = [...kept, ...add];
103
+ }
104
+ else {
105
+ const at = kept.findIndex((row) => row.route === before);
106
+ if (at === -1) {
107
+ throw new TemplateError(`alias "${name}": differences.before names "${before}", which shape "${alias.shape}" does not carry after its drops`);
108
+ }
109
+ if (add.length === 0) {
110
+ throw new TemplateError(`alias "${name}": differences.before names "${before}" but differences.add is empty`);
111
+ }
112
+ roster = [...kept.slice(0, at), ...add, ...kept.slice(at)];
113
+ }
114
+ for (const group of insert) {
115
+ const at = roster.findIndex((row) => row.route === group.before);
116
+ if (at === -1) {
117
+ throw new TemplateError(`alias "${name}": differences.insert names "${group.before}", which shape "${alias.shape}" does not carry after its drops`);
118
+ }
119
+ if (group.rows.length === 0) {
120
+ throw new TemplateError(`alias "${name}": differences.insert names "${group.before}" but its rows is empty`);
121
+ }
122
+ roster = [...roster.slice(0, at), ...group.rows, ...roster.slice(at)];
123
+ }
124
+ const shapeRoutes = new Set(kept.map((row) => row.route));
125
+ const added = new Set();
126
+ for (const row of [...add, ...insert.flatMap((group) => group.rows)]) {
127
+ if (shapeRoutes.has(row.route)) {
128
+ throw new TemplateError(`route "${row.route}" is claimed by both shape "${alias.shape}" and alias "${name}"`);
129
+ }
130
+ if (added.has(row.route)) {
131
+ throw new TemplateError(`alias "${name}": differences.add names route "${row.route}" twice`);
132
+ }
133
+ added.add(row.route);
134
+ }
135
+ return {
136
+ name,
137
+ requires: alias.modules,
138
+ ...(alias.layout && { layout: alias.layout }),
139
+ categories: alias.categories,
140
+ shape: alias.shape,
141
+ design: alias.design,
142
+ extends: BASE_LAYER_DIR,
143
+ roster,
144
+ };
145
+ }
70
146
  /**
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.
147
+ * The declared templates, each composed once at load from its `ALIASES` row
148
+ * (#6753) — no manifest here is hand-typed, so a template's shape, design,
149
+ * modules and roster have one authority: `starter-grid.ts`. Declaration order
150
+ * is `ALIASES`'s, which is `RECOGNIZED_TEMPLATES`'s.
151
+ *
152
+ * Frozen (#5507): this registry is also the issuer's authorization allowlist
153
+ * (spec 5480 § C7) — a mutable global there could be monkey-patched at
154
+ * runtime. A caller needing a different registry for a test injects its own
155
+ * fixture rather than mutating this one.
80
156
  */
81
- const FREE_BLOG_TEASER_ROW = {
82
- route: "/blog",
83
- content: ["content/en/LandingPage/blog.mdx"],
84
- };
157
+ export const TEMPLATE_MANIFESTS = Object.freeze(Object.fromEntries(RECOGNIZED_TEMPLATES.map((name) => [
158
+ name,
159
+ composeManifest(name, ALIASES[name]),
160
+ ])));
85
161
  /**
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).
162
+ * The composed FREE manifest per shape with no `--template` alias of its own
163
+ * (#6757 D5) — the coordinates a keyless `--shape S` builds. `Object.freeze`d,
164
+ * composed once at load by the same module-private `composeManifest` over
165
+ * `SHAPE_STARTER_ALIASES`, each manifest's `name` the shape. Data holding
166
+ * composed manifests, the status D5 gives `TEMPLATE_MANIFESTS`;
167
+ * `resolveTemplate` stays the only exported function that yields a
168
+ * template's roster, and its behavior is unchanged — it does NOT resolve a
169
+ * shape name (NFR-3, the issuer allowlist stays `TEMPLATE_MANIFESTS`-only).
99
170
  */
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
- ];
171
+ export const SHAPE_STARTERS = Object.freeze(Object.fromEntries(Object.entries(SHAPE_STARTER_ALIASES).map(([name, alias]) => [
172
+ name,
173
+ composeManifest(name, alias),
174
+ ])));
112
175
  /**
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.
176
+ * Where a starter's files live under `site-templates/` (#6757 D5) — the one
177
+ * place that says so. A `SHAPE_STARTERS` key (a bare-shape starter with no
178
+ * `--template` alias) lives at `shapes/<name>`, out of the template showcase
179
+ * (§ 1.6); every other name (a declared `--template` alias) is unchanged.
143
180
  */
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
- },
160
- { route: "/privacy", content: ["content/en/PrivacyPage/privacy.mdx"] },
161
- { route: "/terms", content: ["content/en/TermsPage/terms.mdx"] },
162
- { route: "/account" },
163
- { route: "/purchase/success" },
164
- { route: "/purchase/cancel" },
165
- ];
181
+ export function templateTreeDir(name) {
182
+ return Object.prototype.hasOwnProperty.call(SHAPE_STARTERS, name)
183
+ ? `shapes/${name}`
184
+ : name;
185
+ }
166
186
  /**
167
- * The three settled templates and their fixed `requires` lists — settled by
168
- * Sam, 2026-09-14 (parent § D12). The plain name is the Pro one: it is the
169
- * flagship and the demo.
187
+ * Look up a declared template — the one exported function that yields a
188
+ * template's roster (#6753 AC-1); `TEMPLATE_MANIFESTS`, the frozen record it
189
+ * reads, holds only manifests composed from `ALIASES`.
170
190
  *
171
- * `workspace` appears in none of them. It is a Pro module, but an authed app
172
- * surface contributing no PUBLIC page to a marketing site, and `requires`
173
- * names what the roster uses (parent § D15). A Pro key unlocks it regardless
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.
191
+ * With no `aliases`, returns the frozen `TEMPLATE_MANIFESTS` entry itself.
192
+ * With an injected alias table — the `resolveTemplateLineage` precedent
193
+ * (#5507) — composes `aliases[name]` against the shipped shape rosters, so a
194
+ * test exercises the composer's refusals without touching the shipped table.
177
195
  *
178
- * `commerce` is likewise in none of them: it is a per-site add-on available at
179
- * any level, so a template for it would contradict the offering model and
180
- * multiply the set combinatorially (parent § D13). It still contributes rows
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.
196
+ * @throws TemplateError on an undeclared name, or a malformed injected alias.
187
197
  */
188
- export const TEMPLATE_MANIFESTS = Object.freeze({
189
- /**
190
- * The Free template, and the mechanism's own proof (parent § D11): it
191
- * requires only `marketing`, so the keyless merge path is exercised by a
192
- * template we ship anyway rather than by a throwaway fixture.
193
- */
194
- "marketing-site-free": {
195
- name: "marketing-site-free",
196
- requires: ["marketing"],
197
- categories: ["accounting", "legal", "technology"],
198
- extends: BASE_LAYER_DIR,
199
- roster: [...TEMPLATE_BASE_ROSTER, FREE_BLOG_TEASER_ROW],
200
- },
201
- /**
202
- * The Pro flagship. Its module list is OFFERINGS § 4 offering 3's tiers line
203
- * — "docs, scheduling and flows modules" — where "flows" is
204
- * `signal-collectors` (`MODULE_SURFACES` maps it to `["flows"]`).
205
- */
206
- "marketing-site": {
207
- name: "marketing-site",
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,
225
- roster: TEMPLATE_BASE_ROSTER,
226
- },
227
- /**
228
- * Pro Plus — the Pro list plus `multi-tenant`, which is correctly in the
229
- * list because it DOES contribute pages (the org and workspace surfaces).
230
- * Its `pageContributions` is nonetheless unset today, deferred to its own
231
- * instance, so this template's merged roster carries the same contributed
232
- * rows as `marketing-site`'s and no more. That is a module which has
233
- * declared no rows yet — visible in the diff — not a merge that dropped
234
- * declared rows.
235
- */
236
- "saas-product": {
237
- name: "saas-product",
238
- requires: [
239
- "marketing",
240
- "docs-blog",
241
- "scheduling",
242
- "signal-collectors",
243
- "multi-tenant",
244
- ],
245
- categories: ["technology", "medical-specialty"],
246
- extends: BASE_LAYER_DIR,
247
- roster: TEMPLATE_BASE_ROSTER,
248
- },
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
- });
318
- /** Look up a declared template. @throws TemplateError on an undeclared name. */
319
- export function resolveTemplate(name) {
320
- const manifest = TEMPLATE_MANIFESTS[name];
321
- if (!manifest) {
322
- throw new TemplateError(`unknown template "${name}". Declared: ${Object.keys(TEMPLATE_MANIFESTS).join(", ")}`);
198
+ export function resolveTemplate(name, aliases) {
199
+ const declared = aliases ?? TEMPLATE_MANIFESTS;
200
+ if (!Object.prototype.hasOwnProperty.call(declared, name)) {
201
+ throw new TemplateError(`unknown template "${name}". Declared: ${Object.keys(declared).join(", ")}`);
323
202
  }
324
- return manifest;
203
+ return aliases
204
+ ? composeManifest(name, aliases[name])
205
+ : TEMPLATE_MANIFESTS[name];
325
206
  }
326
207
  /**
327
208
  * Resolve a template's `extends` chain — `[name, …ancestors, "_base"]` — in
@@ -20,22 +20,24 @@
20
20
  * ("Deliberately imports nothing from `parse-args.ts`: that module imports
21
21
  * THIS one … so a back-import would close a cycle"), and `surfaces.ts` is the
22
22
  * precedent this file follows: the flag's vocabulary lives in a module the
23
- * parser can import because it imports nothing at all.
23
+ * parser can import because it reaches nothing that imports the parser.
24
24
  *
25
- * THE REGISTRY IS STILL `template-manifest.ts` (parent record D2). This file
26
- * declares NAMES; that one declares what a template IS — `requires`, the
27
- * roster, the `installMap`, the free-vs-paid derivation — and it stays the
28
- * single place a template is defined. The two cannot drift: the names here
29
- * are asserted equal to `Object.keys(TEMPLATE_MANIFESTS)` by
30
- * `__tests__/template-scaffold.test.ts`, so adding a template without listing
31
- * it here (or the reverse) fails the suite rather than shipping a flag that
32
- * accepts a name the registry does not know.
25
+ * THE NAMES ARE DERIVED, NOT TYPED (#6753 D1). They are the key set of
26
+ * `ALIASES` in `starter-grid.ts` — the alias table `template-manifest.ts`
27
+ * composes every manifest from — so the flag's vocabulary and the registry
28
+ * cannot drift. `starter-grid.ts` is the one file this module imports, and it
29
+ * imports nothing at runtime (every import there is `import type`), so
30
+ * `parse-args.ts` → here → `starter-grid.ts` still reaches no module that
31
+ * imports `parse-args.ts` back. `ALIASES` does not live in
32
+ * `template-manifest.ts` for exactly that reason: importing it from there
33
+ * would re-open the cycle above.
33
34
  *
34
35
  * Rejected: lazifying `module-tier.ts`'s `PAID_MODULES` to break the cycle at
35
36
  * its source. It is the correct structural fix and it is not this unit's to
36
37
  * make — `module-tier.ts` is a READ row in spec 5481 § 4, and `PAID_MODULES`
37
38
  * is consumed as a plain array across the package.
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", "minimalism-site", "simple-service"];
41
- export type TemplateName = (typeof RECOGNIZED_TEMPLATES)[number];
40
+ import { ALIASES } from "./starter-grid.js";
41
+ export type TemplateName = keyof typeof ALIASES;
42
+ /** The declared site templates, in `ALIASES` (and so `TEMPLATE_MANIFESTS`) declaration order. */
43
+ export declare const RECOGNIZED_TEMPLATES: readonly TemplateName[];
@@ -20,27 +20,23 @@
20
20
  * ("Deliberately imports nothing from `parse-args.ts`: that module imports
21
21
  * THIS one … so a back-import would close a cycle"), and `surfaces.ts` is the
22
22
  * precedent this file follows: the flag's vocabulary lives in a module the
23
- * parser can import because it imports nothing at all.
23
+ * parser can import because it reaches nothing that imports the parser.
24
24
  *
25
- * THE REGISTRY IS STILL `template-manifest.ts` (parent record D2). This file
26
- * declares NAMES; that one declares what a template IS — `requires`, the
27
- * roster, the `installMap`, the free-vs-paid derivation — and it stays the
28
- * single place a template is defined. The two cannot drift: the names here
29
- * are asserted equal to `Object.keys(TEMPLATE_MANIFESTS)` by
30
- * `__tests__/template-scaffold.test.ts`, so adding a template without listing
31
- * it here (or the reverse) fails the suite rather than shipping a flag that
32
- * accepts a name the registry does not know.
25
+ * THE NAMES ARE DERIVED, NOT TYPED (#6753 D1). They are the key set of
26
+ * `ALIASES` in `starter-grid.ts` — the alias table `template-manifest.ts`
27
+ * composes every manifest from — so the flag's vocabulary and the registry
28
+ * cannot drift. `starter-grid.ts` is the one file this module imports, and it
29
+ * imports nothing at runtime (every import there is `import type`), so
30
+ * `parse-args.ts` → here → `starter-grid.ts` still reaches no module that
31
+ * imports `parse-args.ts` back. `ALIASES` does not live in
32
+ * `template-manifest.ts` for exactly that reason: importing it from there
33
+ * would re-open the cycle above.
33
34
  *
34
35
  * Rejected: lazifying `module-tier.ts`'s `PAID_MODULES` to break the cycle at
35
36
  * its source. It is the correct structural fix and it is not this unit's to
36
37
  * make — `module-tier.ts` is a READ row in spec 5481 § 4, and `PAID_MODULES`
37
38
  * is consumed as a plain array across the package.
38
39
  */
39
- /** The declared site templates, in `TEMPLATE_MANIFESTS` declaration order. */
40
- export const RECOGNIZED_TEMPLATES = [
41
- "marketing-site-free",
42
- "marketing-site",
43
- "saas-product",
44
- "minimalism-site",
45
- "simple-service",
46
- ];
40
+ import { ALIASES } from "./starter-grid.js";
41
+ /** The declared site templates, in `ALIASES` (and so `TEMPLATE_MANIFESTS`) declaration order. */
42
+ export const RECOGNIZED_TEMPLATES = Object.keys(ALIASES);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-ailk",
3
- "version": "0.6.1",
3
+ "version": "0.8.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",
@@ -25,8 +25,8 @@
25
25
  "tar": "^7.5.21"
26
26
  },
27
27
  "devDependencies": {
28
- "@working-theory/schema": "0.26.0",
29
- "@working-theory/templates": "0.26.0"
28
+ "@working-theory/templates": "0.28.0",
29
+ "@working-theory/schema": "0.28.0"
30
30
  },
31
31
  "repository": {
32
32
  "type": "git",
@@ -58,6 +58,8 @@
58
58
  "build-catalog": "node scripts/build-component-catalog.mjs",
59
59
  "check-catalog": "node scripts/build-component-catalog.mjs --check",
60
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"
61
+ "manifest:check": "node scripts/build-template-manifest.mjs --template marketing-site-free --check && node scripts/build-template-manifest.mjs --template marketing-site --check",
62
+ "page-types:build": "tsx scripts/generate-page-types-reference.ts",
63
+ "page-types:check": "tsx scripts/generate-page-types-reference.ts --check"
62
64
  }
63
65
  }