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
@@ -15,21 +15,24 @@
15
15
  * import { scaffoldSync } from './programmatic.js';
16
16
  * const result = scaffoldSync({ targetDir: '/tmp/my-site', projectName: 'my-site' });
17
17
  */
18
- import { existsSync, readdirSync, readFileSync, statSync, writeFileSync, } from "node:fs";
19
- import { basename, dirname, join, resolve } from "node:path";
18
+ import { existsSync, readdirSync, readFileSync, rmdirSync, rmSync, statSync, writeFileSync, } from "node:fs";
19
+ import { basename, dirname, join, relative, resolve, sep } from "node:path";
20
20
  import { fileURLToPath } from "node:url";
21
+ import { AddModuleRefusedError, applyModuleToScaffold, } from "./add-module.js";
21
22
  import { CATALOG_TARGET_PATH, writeComponentCatalog, } from "./component-catalog.js";
22
23
  import { buildScaffoldMarkers, copyTemplate } from "./copy.js";
23
24
  import { deriveArchitecture } from "./derive-architecture.js";
24
25
  import { extractModuleBundle, installBundleTree, } from "./lib/extract-module-bundle.js";
25
26
  import { fetchTemplate } from "./lib/fetch-template.js";
26
- import { resolvePaidModules } from "./lib/module-license-gate.js";
27
+ import { assertLicenseKeyShape, writeLicenseKeyFiles, } from "./lib/license-key-files.js";
28
+ import { FREE_DESIGNS, assertDesignAllowed, deriveLevel, resolveModuleSet, resolvePaidModules, } from "./lib/module-license-gate.js";
27
29
  import { classifyModules } from "./lib/module-tier.js";
28
30
  import { normalizeModules } from "./module-architecture.js";
31
+ import { RECOGNIZED_SHAPES, RESERVED_SHAPES, } from "./parse-args.js";
29
32
  import { SITE_CHROME_PATH, renderSiteChrome, resolveSiteChrome, } from "./site-chrome.js";
30
33
  import { GATEABLE_SURFACES, MODULE_SURFACES, NAV_CONFIG_PATH, excludedRoutePaths, renderNavConfig, resolveSurfaces, } from "./surfaces.js";
31
34
  import { syncRoutes } from "./sync-routes.js";
32
- import { BASE_LAYER_DIR, mergeRoster, resolveTemplate, resolveTemplateLineage, templateDelivery, } from "./template-manifest.js";
35
+ import { BASE_LAYER_DIR, SHAPE_STARTERS, STARTER_DESIGNS, TEMPLATE_MANIFESTS, TemplateError, mergeRoster, resolveTemplate, resolveTemplateLineage, rosterDeclares, templateDelivery, templateTreeDir, } from "./template-manifest.js";
33
36
  import { validateTarget } from "./validate.js";
34
37
  const PKG_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
35
38
  const TEMPLATES_DIR = resolve(PKG_ROOT, "templates");
@@ -73,6 +76,37 @@ function countFiles(dir) {
73
76
  }
74
77
  return count;
75
78
  }
79
+ /**
80
+ * The template a `shape` scaffolds (#6754 D5, extended #6757 D6 row 1): the
81
+ * one FREE-delivery manifest of that shape — a declared `--template` alias
82
+ * (`TEMPLATE_MANIFESTS`) or a bare-shape starter with no alias of its own
83
+ * (`SHAPE_STARTERS`), the union the candidate set draws from. Reads manifest
84
+ * fields only — no roster.
85
+ *
86
+ * @throws TemplateError on a reserved or unknown shape, a shape with no Free
87
+ * starter yet (D6), or more than one (never picked silently).
88
+ */
89
+ function resolveShapeTemplate(shape) {
90
+ if (!RECOGNIZED_SHAPES.includes(shape)) {
91
+ const known = RECOGNIZED_SHAPES.join(", ");
92
+ throw new TemplateError(RESERVED_SHAPES.includes(shape)
93
+ ? `shape "${shape}" is not available yet. Choose one of: ${known}.`
94
+ : `unknown shape "${shape}". Recognized: ${known}`);
95
+ }
96
+ const ofShape = [
97
+ ...Object.values(TEMPLATE_MANIFESTS),
98
+ ...Object.values(SHAPE_STARTERS),
99
+ ].filter((manifest) => manifest.shape === shape);
100
+ const free = ofShape.filter((manifest) => templateDelivery(manifest) === "free");
101
+ if (free.length === 0) {
102
+ const names = ofShape.map((manifest) => manifest.name).join(", ");
103
+ throw new TemplateError(`shape "${shape}" has no Free starter yet — its page list lands with #6743. Templates built on this shape: ${names || "none"}.`);
104
+ }
105
+ if (free.length > 1) {
106
+ throw new TemplateError(`shape "${shape}" has more than one Free starter: ${free.map((manifest) => manifest.name).join(", ")}`);
107
+ }
108
+ return free[0];
109
+ }
76
110
  /**
77
111
  * Scaffold an AI Launch Kit starter project into targetDir (synchronous).
78
112
  *
@@ -110,13 +144,18 @@ export function scaffoldSync(opts) {
110
144
  // template's `layout` block; a bare `scaffoldSync` call (every existing
111
145
  // synchronous caller) writes DEFAULT_SITE_CHROME, byte-identical to the
112
146
  // monorepo's own committed apps/web/lib/site-chrome.ts.
113
- writeFileSync(join(targetAbs, SITE_CHROME_PATH), renderSiteChrome(resolveSiteChrome(opts.layout)));
147
+ writeFileSync(join(targetAbs, SITE_CHROME_PATH), renderSiteChrome(resolveSiteChrome(opts.layout, opts.design)));
114
148
  // Opt-in only (#4636). Every write above this line is unconditional, so a
115
149
  // scaffold with `withCatalog` unset touches exactly the files it did before.
116
150
  if (opts.withCatalog === true)
117
151
  writeComponentCatalog(targetAbs);
118
152
  const filesCreated = countFiles(targetAbs);
119
- return { targetDir: targetAbs, filesCreated, surfaces: enabledSurfaces };
153
+ return {
154
+ targetDir: targetAbs,
155
+ filesCreated,
156
+ surfaces: enabledSurfaces,
157
+ modules: normalizeModules(opts.modules ?? ["marketing"]),
158
+ };
120
159
  }
121
160
  /**
122
161
  * Scaffold an AI Launch Kit starter project into targetDir (async wrapper).
@@ -136,8 +175,14 @@ export function scaffoldSync(opts) {
136
175
  * its fetched bundle or — when it requires only free modules — from the
137
176
  * `site-templates/` tree this package ships.
138
177
  *
139
- * And, ahead of all five when `opts.template` is set (0): resolve the
140
- * template and union its `requires` with `opts.modules`, so every step above
178
+ * Between 2 and 3, `assertDesignAllowed` (#6760) refuses a design the caller
179
+ * CHOSE (`opts.design`) for a level below Pro, HERE, also before anything is
180
+ * written to disk. A template's own declared design is never gated (#6778
181
+ * option (c)): the template's `requires` already decide its level.
182
+ *
183
+ * And, ahead of all five when `opts.template` (or `opts.shape`, #6754 D5) is
184
+ * set (0): resolve the template and union its `requires` with `opts.modules`,
185
+ * so every step above
141
186
  * runs over the EFFECTIVE module set rather than the requested one.
142
187
  *
143
188
  * `scaffoldSync` itself stays licensing-oblivious on purpose: every existing
@@ -146,35 +191,134 @@ export function scaffoldSync(opts) {
146
191
  * with a paid module in `opts.modules` exercises the gate at all.
147
192
  */
148
193
  export async function scaffold(opts) {
194
+ // #6683 D9 — refused before anything is fetched or written, like the
195
+ // template and design refusals below. The message never repeats the value.
196
+ if (opts.license !== undefined)
197
+ assertLicenseKeyShape(opts.license);
198
+ const licenseKey = opts.license ?? opts.licenseKey;
149
199
  // Resolved first, and by the registry rather than re-validated here: the
150
200
  // name already passed `parse-args.ts`, and `resolveTemplate` throws a
151
- // `TemplateError` for a programmatic caller that skipped the parser.
152
- const template = opts.template !== undefined ? resolveTemplate(opts.template) : undefined;
201
+ // `TemplateError` for a programmatic caller that skipped the parser. A
202
+ // `shape` resolves to its one Free starter and from there is the template
203
+ // path verbatim (#6754 D5); neither leaves the bare overlay untouched.
204
+ if (opts.template !== undefined && opts.shape !== undefined) {
205
+ throw new TemplateError("pass a template or a shape, not both");
206
+ }
207
+ const template = opts.template !== undefined
208
+ ? resolveTemplate(opts.template)
209
+ : opts.shape !== undefined
210
+ ? resolveShapeTemplate(opts.shape)
211
+ : undefined;
212
+ // #6638 — the design this scaffold renders: the caller's override, else the
213
+ // template's own. Unset for a scaffold naming neither.
214
+ const design = opts.design ?? template?.design;
215
+ if (design !== undefined && !STARTER_DESIGNS.includes(design)) {
216
+ throw new TemplateError(`unknown design "${design}". Declared: ${STARTER_DESIGNS.join(", ")}`);
217
+ }
153
218
  // #5618 D2 — the `extends` chain is resolved in the client registry before
154
219
  // any fetch: an unknown parent or a cycle is refused here, with the target
155
- // still non-existent, like the four refusals below.
156
- if (template !== undefined)
157
- resolveTemplateLineage(template.name);
220
+ // still non-existent, like the four refusals below. The registry is the
221
+ // union of TEMPLATE_MANIFESTS and SHAPE_STARTERS (#6757 D6 row 1): a
222
+ // `--shape` run's resolved template may be a bare-shape starter, whose name
223
+ // (the shape itself) `TEMPLATE_MANIFESTS` alone does not carry.
224
+ if (template !== undefined) {
225
+ resolveTemplateLineage(template.name, {
226
+ ...TEMPLATE_MANIFESTS,
227
+ ...SHAPE_STARTERS,
228
+ });
229
+ }
158
230
  // The effective module set: union(template.requires, parsed --modules),
159
231
  // normalized through `normalizeModules` — the single ordering authority
160
232
  // (parent record D7). Order-independence falls out rather than being
161
233
  // engineered: it returns `RECOGNIZED_MODULES.filter(...)`, declaration-
162
234
  // ordered and deduped, so the same SET in any argument order is the same
163
235
  // list.
164
- const modules = normalizeModules([
165
- ...(template?.requires ?? []),
166
- ...(opts.modules ?? ["marketing"]),
167
- ]);
168
- const { paid } = classifyModules(modules);
169
- const bundles = await resolvePaidModules({
170
- paidModules: paid,
171
- licenseKey: opts.licenseKey,
172
- ...(template !== undefined ? { templateName: template.name } : {}),
173
- ...(opts.moduleRegistryUrl !== undefined
174
- ? { registryUrl: opts.moduleRegistryUrl }
175
- : {}),
176
- ...(opts.fetchImpl !== undefined ? { fetchImpl: opts.fetchImpl } : {}),
177
- });
236
+ let modules;
237
+ let bundles;
238
+ // The run's derived level (#6759), for `assertDesignAllowed` below.
239
+ // Undefined on the alias branch and on a `--modules`-keep run (D2/D4 in
240
+ // module-license-gate.ts — the level plays no part in composing either),
241
+ // so a non-`standard` design on either path derives it separately, below.
242
+ let level;
243
+ if (opts.template !== undefined) {
244
+ // Alias run — untouched (NFR-4): the union line and its
245
+ // resolvePaidModules({ …, templateName }) call stay exactly as they were.
246
+ modules = normalizeModules([
247
+ ...(template?.requires ?? []),
248
+ ...(opts.modules ?? ["marketing"]),
249
+ ]);
250
+ const { paid } = classifyModules(modules);
251
+ bundles = await resolvePaidModules({
252
+ paidModules: paid,
253
+ licenseKey,
254
+ ...(template !== undefined ? { templateName: template.name } : {}),
255
+ ...(opts.moduleRegistryUrl !== undefined
256
+ ? { registryUrl: opts.moduleRegistryUrl }
257
+ : {}),
258
+ ...(opts.fetchImpl !== undefined ? { fetchImpl: opts.fetchImpl } : {}),
259
+ });
260
+ // #6760 — `resolveModuleSet` never runs on this branch (NFR-4), so no
261
+ // level was derived above. Derive one — from the registry's per-module
262
+ // answers, never a key claim (D2) — only when the caller CHOSE a
263
+ // non-`standard` design alongside the template (`opts.design`). An
264
+ // alias's own declared design is never gated (#6778 option (c)), so a
265
+ // plain `--template minimalism-site` run makes no extra network call and
266
+ // is never refused on its design.
267
+ if (opts.design !== undefined && !FREE_DESIGNS.includes(opts.design)) {
268
+ ({ level } = await deriveLevel({
269
+ licenseKey,
270
+ ...(opts.moduleRegistryUrl !== undefined
271
+ ? { registryUrl: opts.moduleRegistryUrl }
272
+ : {}),
273
+ ...(opts.fetchImpl !== undefined ? { fetchImpl: opts.fetchImpl } : {}),
274
+ }));
275
+ }
276
+ }
277
+ else {
278
+ // Shape and bare runs — the key's level decides (#6759 D6), never a flag,
279
+ // a manifest or a key claim.
280
+ const chosen = await resolveModuleSet({
281
+ named: opts.modules,
282
+ licenseKey,
283
+ ...(opts.moduleRegistryUrl !== undefined
284
+ ? { registryUrl: opts.moduleRegistryUrl }
285
+ : {}),
286
+ ...(opts.fetchImpl !== undefined ? { fetchImpl: opts.fetchImpl } : {}),
287
+ });
288
+ bundles = chosen.bundles;
289
+ level = chosen.level;
290
+ modules =
291
+ template !== undefined
292
+ ? normalizeModules([...template.requires, ...chosen.modules])
293
+ : chosen.modules;
294
+ // A paid member of the shape's own starter `requires` not already
295
+ // fetched during the level probe (D6) — every buildable shape's starter
296
+ // requires only `marketing` today, so this is presently a no-op branch.
297
+ if (template !== undefined) {
298
+ const templatePaid = classifyModules(template.requires).paid;
299
+ const stillMissing = templatePaid.filter((m) => !bundles.has(m));
300
+ if (stillMissing.length > 0) {
301
+ const extra = await resolvePaidModules({
302
+ paidModules: stillMissing,
303
+ licenseKey,
304
+ ...(opts.moduleRegistryUrl !== undefined
305
+ ? { registryUrl: opts.moduleRegistryUrl }
306
+ : {}),
307
+ ...(opts.fetchImpl !== undefined
308
+ ? { fetchImpl: opts.fetchImpl }
309
+ : {}),
310
+ });
311
+ for (const [k, v] of extra)
312
+ bundles.set(k, v);
313
+ }
314
+ }
315
+ }
316
+ // #6760 D4 — after the level is resolved, before any fetch or write below.
317
+ // Only the caller's chosen design is gated (#6778 option (c)); a template's
318
+ // own declared design is not. `level ?? "free"` fails closed: a
319
+ // `--modules`-keep run under-reports its level by design (NFR-2), so a
320
+ // chosen non-`standard` design on that path is refused rather than trusted.
321
+ assertDesignAllowed(opts.design, level ?? "free");
178
322
  // The template's OWN bundle, fetched here — before `scaffoldSync` — so the
179
323
  // fourth refusal (the template's bundle unpublished, parent record D17 /
180
324
  // spec 5481 § 3 DD5 row 4) throws with the target still non-existent, like
@@ -189,7 +333,7 @@ export async function scaffold(opts) {
189
333
  const templateBundle = template !== undefined && templateDelivery(template) === "paid"
190
334
  ? await fetchTemplate({
191
335
  templateName: template.name,
192
- token: opts.licenseKey ?? process.env["AILK_LICENSE_KEY"] ?? "",
336
+ token: licenseKey ?? process.env["AILK_LICENSE_KEY"] ?? "",
193
337
  ...(opts.templateRegistryUrl !== undefined
194
338
  ? { registryUrl: opts.templateRegistryUrl }
195
339
  : {}),
@@ -224,9 +368,11 @@ export async function scaffold(opts) {
224
368
  ...(surfaces !== undefined ? { surfaces } : {}),
225
369
  // #5612 D1/D2 — the code registry's own `layout` block, known
226
370
  // synchronously (unlike a bundle's `ailk-template.json`, only readable
227
- // after install below). A template declaring none resolves to the same
228
- // defaults `scaffoldSync` already writes for a templateless scaffold.
371
+ // after install below). A template declaring none resolves to its
372
+ // design's knobs (#6638), and a `standard` one to the same defaults
373
+ // `scaffoldSync` already writes for a templateless scaffold.
229
374
  ...(template?.layout !== undefined ? { layout: template.layout } : {}),
375
+ ...(design !== undefined ? { design } : {}),
230
376
  });
231
377
  // Threaded to extractModuleBundle for `package.json` patch targets'
232
378
  // `workspace:*` substitution (spec 5418 § 3 D2) — the same version map
@@ -311,7 +457,7 @@ export async function scaffold(opts) {
311
457
  ownership,
312
458
  })
313
459
  : (() => {
314
- const sourceRoot = join(SITE_TEMPLATES_DIR, template.name);
460
+ const sourceRoot = join(SITE_TEMPLATES_DIR, templateTreeDir(template.name));
315
461
  if (!existsSync(sourceRoot)) {
316
462
  throw new Error(`template "${template.name}": ${sourceRoot} is missing — the published package must carry site-templates/ in its "files" list`);
317
463
  }
@@ -327,11 +473,73 @@ export async function scaffold(opts) {
327
473
  });
328
474
  })();
329
475
  if (installed.layout !== undefined) {
330
- writeFileSync(join(result.targetDir, SITE_CHROME_PATH), renderSiteChrome(resolveSiteChrome(installed.layout)));
476
+ writeFileSync(join(result.targetDir, SITE_CHROME_PATH), renderSiteChrome(resolveSiteChrome(installed.layout, design)));
331
477
  }
478
+ pruneUnrosteredSeeds(result.targetDir, mergeRoster(template, template.requires), result.surfaces, ownership);
479
+ }
480
+ // #6683 D9 — persisted only after every layer landed, and only when the
481
+ // caller passed `license`: a scaffold without it writes nothing new.
482
+ if (opts.license !== undefined) {
483
+ writeLicenseKeyFiles(result.targetDir, opts.license);
484
+ }
485
+ // #6754 D4/D5 — the resolved starter, so the CLI names what it built.
486
+ if (template !== undefined && design !== undefined) {
487
+ return {
488
+ ...result,
489
+ starter: { template: template.name, shape: template.shape, design },
490
+ };
332
491
  }
333
492
  return result;
334
493
  }
494
+ /**
495
+ * The per-row install of the two SHARED seed layers (#6646 D1, disposition
496
+ * (b)): with every layer landed, remove each `content/en/**.mdx` that the
497
+ * `templates/` snapshot (the overlay — no `ownership` entry) or the `_base`
498
+ * layer wrote and no merged roster row names. Those two layers serve every
499
+ * template, so each carries seeds some templates do not roster (`/pricing`
500
+ * on a services site); a template's own tree and its modules' bundles are
501
+ * never pruned — a seed THEY ship without a row is drift, which the manifest
502
+ * generator's `--check` fails on instead.
503
+ *
504
+ * Merged over the template's own `requires`, as `rosterSurfaces` is: the
505
+ * rows that name shared-layer seeds are the template's own. A seed inside
506
+ * the content directory of a surface this scaffold ENABLES is kept too, so
507
+ * an explicit `--surfaces pricing` on a template that does not roster
508
+ * `/pricing` gets its route and its seed together.
509
+ */
510
+ function pruneUnrosteredSeeds(targetDir, roster, surfaces, ownership) {
511
+ // `installBundleTree`'s ownership label for the base layer: it installs
512
+ // with `templateRequires: []`, so it records as a template. The overlay
513
+ // (the `templates/` snapshot) records no owner at all.
514
+ const shared = new Set([undefined, `template "${BASE_LAYER_DIR}"`]);
515
+ const enabledDirs = GATEABLE_SURFACES.filter((surface) => surfaces.includes(surface.id)).flatMap((surface) => surface.content.map((dir) => `${dir.replace("<locale>", "en")}/`));
516
+ const root = join(targetDir, "content", "en");
517
+ if (!existsSync(root))
518
+ return;
519
+ const stack = [root];
520
+ while (stack.length > 0) {
521
+ const dir = stack.pop();
522
+ if (dir === undefined)
523
+ break;
524
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
525
+ const abs = join(dir, entry.name);
526
+ if (entry.isDirectory()) {
527
+ stack.push(abs);
528
+ continue;
529
+ }
530
+ if (!entry.name.endsWith(".mdx"))
531
+ continue;
532
+ const path = relative(targetDir, abs).split(sep).join("/");
533
+ if (shared.has(ownership.get(path)) &&
534
+ !rosterDeclares(roster, path) &&
535
+ !enabledDirs.some((prefix) => path.startsWith(prefix))) {
536
+ rmSync(abs);
537
+ if (readdirSync(dir).length === 0)
538
+ rmdirSync(dir);
539
+ }
540
+ }
541
+ }
542
+ }
335
543
  /**
336
544
  * The MODULE-LESS gateable-surface ids a template's merged roster names
337
545
  * (#5469 D10): every `GATEABLE_SURFACES` entry no module contributes
@@ -478,6 +686,57 @@ export function syncRoutesSync(opts) {
478
686
  versions: readVersionMap(),
479
687
  });
480
688
  }
689
+ /**
690
+ * Apply a purchased module to an already-scaffolded site in place
691
+ * (`create-ailk add-module`, #6653). The thin package-rooted wrapper around
692
+ * `add-module.ts`'s `applyModuleToScaffold`, the `syncRoutesSync` split:
693
+ *
694
+ * 1. the target must be an existing scaffold — the same check as the sync
695
+ * subcommands above;
696
+ * 2. a free module is refused before any key read or network call — it
697
+ * ships with every scaffold, there is no bundle to add;
698
+ * 3. `resolvePaidModules` — the SAME gate `scaffold()` runs, so a missing
699
+ * key or a key that does not cover the module is refused with the
700
+ * scaffold-time message, before anything is written;
701
+ * 4. the fetched bundle is merged under the plus-config policy.
702
+ */
703
+ export async function addModule(opts) {
704
+ const targetAbs = resolve(opts.targetDir);
705
+ if (!existsSync(targetAbs)) {
706
+ throw new Error(`add-module target does not exist: ${targetAbs}. Point add-module at an already-scaffolded repo.`);
707
+ }
708
+ if (!statSync(targetAbs).isDirectory()) {
709
+ throw new Error(`add-module target is not a directory: ${targetAbs}`);
710
+ }
711
+ if (!existsSync(join(targetAbs, "architecture.yaml"))) {
712
+ throw new Error(`not a scaffolded repo: no architecture.yaml at ${targetAbs}. Run \`create-ailk <target-dir>\` to scaffold first.`);
713
+ }
714
+ const { paid } = classifyModules([opts.moduleName]);
715
+ if (paid.length === 0) {
716
+ throw new AddModuleRefusedError(`"${opts.moduleName}" ships with every scaffold — there is no module bundle to add`);
717
+ }
718
+ const bundles = await resolvePaidModules({
719
+ paidModules: paid,
720
+ licenseKey: opts.licenseKey,
721
+ ...(opts.moduleRegistryUrl !== undefined
722
+ ? { registryUrl: opts.moduleRegistryUrl }
723
+ : {}),
724
+ ...(opts.fetchImpl !== undefined ? { fetchImpl: opts.fetchImpl } : {}),
725
+ });
726
+ const bundleBytes = bundles.get(opts.moduleName);
727
+ if (bundleBytes === undefined) {
728
+ throw new Error(`module "${opts.moduleName}": no bundle was resolved`);
729
+ }
730
+ return applyModuleToScaffold({
731
+ targetDir: targetAbs,
732
+ moduleName: opts.moduleName,
733
+ bundleBytes,
734
+ versions: readVersionMap(),
735
+ markers: buildScaffoldMarkers(basename(targetAbs)),
736
+ templatesDir: TEMPLATES_DIR,
737
+ siteTemplatesDir: SITE_TEMPLATES_DIR,
738
+ });
739
+ }
481
740
  /** Re-exported so the CLI can name the file it wrote without duplicating it. */
482
741
  export { CATALOG_TARGET_PATH };
483
742
  /**
@@ -15,11 +15,17 @@
15
15
  * `apps/web/lib/site-chrome.ts` every scaffold gets — beside `nav-config.ts`,
16
16
  * the same GENERATED-header idiom (`surfaces.ts` `renderNavConfig`).
17
17
  *
18
- * Every field is optional and DEFAULTS to today's render (`full` / `columns`
19
- * / `true` / `default`) — a scaffold naming no template, or a template naming
20
- * no `layout` block, gets these defaults written, byte-identical to the
21
- * monorepo's own committed `apps/web/lib/site-chrome.ts`.
18
+ * Every field is optional and DEFAULTS to `full` / `columns` / `false` /
19
+ * `default` — the announcement banner is off unless a template turns it on
20
+ * (#6469). A scaffold naming no template, or a template naming no `layout`
21
+ * block, gets these defaults written, byte-identical to the monorepo's own
22
+ * committed `apps/web/lib/site-chrome.ts`.
23
+ *
24
+ * A template's DESIGN supplies layout knobs too (#6638): `DESIGN_LAYOUTS` in
25
+ * `template-manifest.ts`, read by `resolveSiteChrome`'s optional `design`
26
+ * argument between an explicit `layout` and these defaults.
22
27
  */
28
+ import { type StarterDesign } from "./template-manifest.js";
23
29
  /** The `nav` knob's vocabulary. `mark-only` → the layout passes Nav an empty
24
30
  * `navItems` (brand mark + the existing actions/theme/locale controls only). */
25
31
  export type SiteChromeNav = "full" | "mark-only";
@@ -47,7 +53,7 @@ export interface ResolvedSiteChrome {
47
53
  announcementBanner: boolean;
48
54
  home: SiteChromeHome;
49
55
  }
50
- /** Byte-identical-to-today defaults (#5612 D1). */
56
+ /** Defaults (#5612 D1; banner off since #6469). */
51
57
  export declare const DEFAULT_SITE_CHROME: ResolvedSiteChrome;
52
58
  /** Template-relative path of the generated site-chrome registry — beside `NAV_CONFIG_PATH`. */
53
59
  export declare const SITE_CHROME_PATH = "apps/web/lib/site-chrome.ts";
@@ -68,8 +74,13 @@ export declare class TemplateLayoutError extends Error {
68
74
  * @param subject Caller-supplied context for the error message (e.g. `template "foo"`).
69
75
  */
70
76
  export declare function validateTemplateLayout(value: unknown, subject: string): TemplateLayout;
71
- /** Fill every field a `TemplateLayout` leaves unset with `DEFAULT_SITE_CHROME`'s value. */
72
- export declare function resolveSiteChrome(layout?: TemplateLayout): ResolvedSiteChrome;
77
+ /**
78
+ * Fill every field a `TemplateLayout` leaves unset — from `design`'s
79
+ * `DESIGN_LAYOUTS` entry when one is named (#6638), then from
80
+ * `DEFAULT_SITE_CHROME`. The explicit `layout` wins field by field, so a
81
+ * template can wear a design and still override one knob of it.
82
+ */
83
+ export declare function resolveSiteChrome(layout?: TemplateLayout, design?: StarterDesign): ResolvedSiteChrome;
73
84
  /**
74
85
  * Render `apps/web/lib/site-chrome.ts` for a resolved layout — the same
75
86
  * GENERATED-header idiom `renderNavConfig` uses. `layout.tsx` imports
@@ -15,16 +15,22 @@
15
15
  * `apps/web/lib/site-chrome.ts` every scaffold gets — beside `nav-config.ts`,
16
16
  * the same GENERATED-header idiom (`surfaces.ts` `renderNavConfig`).
17
17
  *
18
- * Every field is optional and DEFAULTS to today's render (`full` / `columns`
19
- * / `true` / `default`) — a scaffold naming no template, or a template naming
20
- * no `layout` block, gets these defaults written, byte-identical to the
21
- * monorepo's own committed `apps/web/lib/site-chrome.ts`.
18
+ * Every field is optional and DEFAULTS to `full` / `columns` / `false` /
19
+ * `default` — the announcement banner is off unless a template turns it on
20
+ * (#6469). A scaffold naming no template, or a template naming no `layout`
21
+ * block, gets these defaults written, byte-identical to the monorepo's own
22
+ * committed `apps/web/lib/site-chrome.ts`.
23
+ *
24
+ * A template's DESIGN supplies layout knobs too (#6638): `DESIGN_LAYOUTS` in
25
+ * `template-manifest.ts`, read by `resolveSiteChrome`'s optional `design`
26
+ * argument between an explicit `layout` and these defaults.
22
27
  */
23
- /** Byte-identical-to-today defaults (#5612 D1). */
28
+ import { DESIGN_LAYOUTS, TemplateError, } from "./template-manifest.js";
29
+ /** Defaults (#5612 D1; banner off since #6469). */
24
30
  export const DEFAULT_SITE_CHROME = {
25
31
  nav: "full",
26
32
  footer: "columns",
27
- announcementBanner: true,
33
+ announcementBanner: false,
28
34
  home: "default",
29
35
  };
30
36
  /** Template-relative path of the generated site-chrome registry — beside `NAV_CONFIG_PATH`. */
@@ -85,13 +91,25 @@ export function validateTemplateLayout(value, subject) {
85
91
  }
86
92
  return result;
87
93
  }
88
- /** Fill every field a `TemplateLayout` leaves unset with `DEFAULT_SITE_CHROME`'s value. */
89
- export function resolveSiteChrome(layout) {
94
+ /**
95
+ * Fill every field a `TemplateLayout` leaves unset — from `design`'s
96
+ * `DESIGN_LAYOUTS` entry when one is named (#6638), then from
97
+ * `DEFAULT_SITE_CHROME`. The explicit `layout` wins field by field, so a
98
+ * template can wear a design and still override one knob of it.
99
+ */
100
+ export function resolveSiteChrome(layout, design) {
101
+ const fromDesign = design !== undefined ? DESIGN_LAYOUTS[design] : {};
102
+ // A JS caller (or a cast) can reach here with an undeclared design.
103
+ if (fromDesign === undefined) {
104
+ throw new TemplateError(`unknown design "${design}". Declared: ${Object.keys(DESIGN_LAYOUTS).join(", ")}`);
105
+ }
90
106
  return {
91
- nav: layout?.nav ?? DEFAULT_SITE_CHROME.nav,
92
- footer: layout?.footer ?? DEFAULT_SITE_CHROME.footer,
93
- announcementBanner: layout?.announcementBanner ?? DEFAULT_SITE_CHROME.announcementBanner,
94
- home: layout?.home ?? DEFAULT_SITE_CHROME.home,
107
+ nav: layout?.nav ?? fromDesign.nav ?? DEFAULT_SITE_CHROME.nav,
108
+ footer: layout?.footer ?? fromDesign.footer ?? DEFAULT_SITE_CHROME.footer,
109
+ announcementBanner: layout?.announcementBanner ??
110
+ fromDesign.announcementBanner ??
111
+ DEFAULT_SITE_CHROME.announcementBanner,
112
+ home: layout?.home ?? fromDesign.home ?? DEFAULT_SITE_CHROME.home,
95
113
  };
96
114
  }
97
115
  /**
@@ -106,8 +124,8 @@ export function renderSiteChrome(resolved) {
106
124
  //
107
125
  // GENERATED by create-ailk from the declared template's \`layout\` block
108
126
  // (#5612). A scaffold with no template, or a template with no \`layout\`
109
- // block, gets these defaults — byte-identical to every scaffold before this
110
- // file existed. \`apps/web/app/[locale]/layout.tsx\` reads this to decide:
127
+ // block, gets the defaults below (the announcement banner is off unless a
128
+ // template turns it on, #6469). \`apps/web/app/[locale]/layout.tsx\` reads this to decide:
111
129
  // - nav: "mark-only" → Nav renders the brand mark + right-hand controls
112
130
  // only (no nav links).
113
131
  // - footer: "centered" → Footer renders its existing centered variant.