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
@@ -57,6 +57,14 @@ export declare const RECOGNIZED_SURFACES: readonly string[];
57
57
  * comment on that row. Resolving through an alias prints a deprecation notice.
58
58
  */
59
59
  export declare const SURFACE_ALIASES: Readonly<Record<string, string>>;
60
+ /**
61
+ * Surface ids that became core routes (#6658 D3: `software` — the product's
62
+ * own page is in every scaffold). Accepted and dropped wherever a surface list
63
+ * is read — a `--surfaces` value or an existing site's `# Surfaces:` header —
64
+ * so a site scaffolded with `--surfaces software` keeps working: its route
65
+ * is copied regardless.
66
+ */
67
+ export declare const CORE_PROMOTED_SURFACES: ReadonlySet<string>;
60
68
  /**
61
69
  * Surfaces each recognized module contributes.
62
70
  *
@@ -183,19 +191,24 @@ export declare const NAV_CONFIG_PATH = "apps/web/lib/nav-config.ts";
183
191
  * surface set for the same reason the route tree does.
184
192
  *
185
193
  * The base items are the Free marketing-site chrome (#5469 D5, the approved
186
- * chrome diff recorded on that card):
187
- * a Product flyout (How it works · Features · FAQ), then How it works, Pricing
188
- * and About. Every one of those pages is in EVERY scaffold — the four core
189
- * routes plus the `[slug]` route over the overlay's LandingPage MDX — except
190
- * Pricing, which is a gateable surface: its item is emitted only when the
191
- * `pricing` surface is enabled (a template roster enables it, #5469 D10), so
192
- * the nav still never links a page the scaffold does not carry.
194
+ * chrome diff recorded on that card, re-pointed by #6658 D8):
195
+ * a Product flyout in two columns (Features · Use Cases, #6721), then Blog,
196
+ * Pricing and About. The feature and use-case links ride the `features` and
197
+ * `use-cases` surfaces; `/blog` is always linked (the teaser or the docs-blog
198
+ * module serves it). Pricing is gateable
199
+ * surfaces: each item is emitted only when its surface is enabled (a template
200
+ * roster enables it, #5469 D10), so the nav still never links a page the
201
+ * scaffold does not carry.
193
202
  *
194
203
  * Only gateable surfaces carrying a `navLabelKey` are linked after the base
195
204
  * items, because a nav entry needs a translated label. Emitting an entry whose
196
205
  * `labelKey` has no translation would trade a dead link for a missing-message
197
206
  * error.
198
207
  *
208
+ * The same file carries `footerColumns`, the footer's column groups (#6468):
209
+ * label keys and paths only, under the same gates, so the footer links every
210
+ * seed page the header does not.
211
+ *
199
212
  * Deterministic: gateable entries follow GATEABLE_SURFACES order (alphabetical
200
213
  * by id), after the fixed base items.
201
214
  */
package/dist/surfaces.js CHANGED
@@ -20,8 +20,8 @@
20
20
  *
21
21
  * The default (`--modules marketing`) enables NO gateable surface: every one of
22
22
  * them is a claim about the business that the scaffolder cannot make. The path
23
- * from there is the `suggest-site-pages` → `scaffold-page` skill pair that
24
- * already ships in `templates/.claude/skills/`.
23
+ * from there is the `add-page` skill that already ships in
24
+ * `templates/.claude/skills/`.
25
25
  *
26
26
  * Zero runtime dependencies — create-ailk is a published, standalone CLI.
27
27
  *
@@ -75,9 +75,11 @@ export const CORE_ROUTE_DIRS = [
75
75
  // registering the group keeps `sign-in`'s existing `core` identity exactly.
76
76
  `${ROUTE_ROOT}/(auth)`,
77
77
  // #5469 D2 — always copied: it is what serves the Free template's
78
- // /how-it-works, /features and /blog pages, and it is inert for every slug
79
- // that has no LandingPage MDX (notFound), so a scaffold carrying it and no
80
- // content is byte-for-byte a scaffold with three more 404s, nothing else.
78
+ // /blog teaser page (its former /how-it-works and /features siblings
79
+ // moved to the /use-cases list and the product's own page), and it is
80
+ // inert for every slug that has no LandingPage MDX (notFound), so a
81
+ // scaffold carrying it and no content is byte-for-byte a scaffold with one
82
+ // more 404, nothing else.
81
83
  `${ROUTE_ROOT}/[slug]`,
82
84
  // #6084 D12 (the operator's shape-2 disposition) — the checkout return
83
85
  // pair. The launchers pointing at them (`apps/mcp/src/tools/purchase.ts`,
@@ -118,6 +120,13 @@ export const GATEABLE_SURFACES = [
118
120
  content: ["apps/web/content/<locale>/blog"],
119
121
  navLabelKey: "blog",
120
122
  },
123
+ {
124
+ // Module-less (#6658 D5), like `pricing`: every starter's roster names
125
+ // it. No `navLabelKey` — the footer links it, gated on this surface.
126
+ id: "book-a-call",
127
+ routes: [`${ROUTE_ROOT}/book-a-call`],
128
+ content: ["content/<locale>/BookCall"],
129
+ },
121
130
  {
122
131
  id: "careers",
123
132
  routes: [`${ROUTE_ROOT}/careers`],
@@ -164,12 +173,25 @@ export const GATEABLE_SURFACES = [
164
173
  routes: [`${ROUTE_ROOT}/events`],
165
174
  content: ["content/<locale>/EventsList", "content/<locale>/Event"],
166
175
  },
176
+ {
177
+ // #6721 — the product's feature pages: a FeaturesList hub over one
178
+ // Feature page per feature. The Free roster enables it by its rows.
179
+ id: "features",
180
+ routes: [`${ROUTE_ROOT}/features`],
181
+ content: ["content/<locale>/FeaturesList", "content/<locale>/Feature"],
182
+ },
167
183
  {
168
184
  // API-backed (the flow engine); no MDX of its own.
169
185
  id: "flows",
170
186
  routes: [`${ROUTE_ROOT}/flows`],
171
187
  content: [],
172
188
  },
189
+ {
190
+ // Module-less (#6658 D5) — see `book-a-call`.
191
+ id: "glossary",
192
+ routes: [`${ROUTE_ROOT}/glossary`],
193
+ content: ["content/<locale>/GlossaryList"],
194
+ },
173
195
  {
174
196
  id: "how-to",
175
197
  routes: [`${ROUTE_ROOT}/how-to`],
@@ -206,11 +228,23 @@ export const GATEABLE_SURFACES = [
206
228
  // functional gain, since no route derives from a message key.
207
229
  navLabelKey: "schedule",
208
230
  },
231
+ {
232
+ // Module-less (#6658 D5) — see `book-a-call`. The page's form posts to
233
+ // lead capture and sends no verification email.
234
+ id: "newsletter",
235
+ routes: [`${ROUTE_ROOT}/newsletter`],
236
+ content: ["content/<locale>/NewsletterSignup"],
237
+ },
209
238
  {
210
239
  id: "partners",
211
240
  routes: [`${ROUTE_ROOT}/partners`],
212
241
  content: ["content/<locale>/PartnersList", "content/<locale>/Partner"],
213
242
  },
243
+ {
244
+ id: "portfolio",
245
+ routes: [`${ROUTE_ROOT}/portfolio`],
246
+ content: ["content/<locale>/PortfolioList", "content/<locale>/PortfolioItem"],
247
+ },
214
248
  {
215
249
  // Module-LESS by construction (#5479 D16): no MODULE_SURFACES value names
216
250
  // it, so a site template may enable it by roster alone. Same for
@@ -248,6 +282,8 @@ export const GATEABLE_SURFACES = [
248
282
  content: ["content/<locale>/ServicesList", "content/<locale>/Service"],
249
283
  },
250
284
  {
285
+ // #6721 — the product's own page (#6658 D3), a gateable surface again now
286
+ // that the Free roster links /features instead of /software/product.
251
287
  id: "software",
252
288
  routes: [`${ROUTE_ROOT}/software`],
253
289
  content: ["content/<locale>/SoftwareProduct"],
@@ -270,7 +306,7 @@ export const GATEABLE_SURFACES = [
270
306
  {
271
307
  id: "use-cases",
272
308
  routes: [`${ROUTE_ROOT}/use-cases`],
273
- content: ["content/<locale>/UseCasesList"],
309
+ content: ["content/<locale>/UseCasesList", "content/<locale>/UseCase"],
274
310
  },
275
311
  {
276
312
  id: "whitepapers",
@@ -288,6 +324,17 @@ export const RECOGNIZED_SURFACES = GATEABLE_SURFACES.map((s) => s.id);
288
324
  export const SURFACE_ALIASES = {
289
325
  schedule: "meet",
290
326
  };
327
+ /**
328
+ * Surface ids that became core routes (#6658 D3: `software` — the product's
329
+ * own page is in every scaffold). Accepted and dropped wherever a surface list
330
+ * is read — a `--surfaces` value or an existing site's `# Surfaces:` header —
331
+ * so a site scaffolded with `--surfaces software` keeps working: its route
332
+ * is copied regardless.
333
+ */
334
+ // #6721 — `software` is a gateable surface again (the Free roster now carries
335
+ // /features + /features/[slug] instead); the set is kept, empty, so the
336
+ // accept-and-drop read sites stay in place for the next promotion.
337
+ export const CORE_PROMOTED_SURFACES = new Set([]);
291
338
  /** Canonical id for a `--surfaces` value, resolving a retired alias. */
292
339
  function canonicalizeSurface(s) {
293
340
  return SURFACE_ALIASES[s] ?? s;
@@ -346,7 +393,7 @@ export function parseSurfaces(raw) {
346
393
  const requested = raw
347
394
  .split(",")
348
395
  .map((s) => canonicalizeSurface(s.trim().toLowerCase()))
349
- .filter(Boolean);
396
+ .filter((s) => s !== "" && !CORE_PROMOTED_SURFACES.has(s));
350
397
  for (const s of requested) {
351
398
  if (!RECOGNIZED_SURFACES.includes(s)) {
352
399
  throw new SurfaceError(`unknown surface "${s}". Recognized: ${RECOGNIZED_SURFACES.join(", ")}`);
@@ -376,6 +423,8 @@ export function resolveSurfaces(modules = ["marketing"], explicit = []) {
376
423
  const selected = new Set(modules.map((m) => m.trim().toLowerCase()));
377
424
  for (const raw of explicit) {
378
425
  const s = canonicalizeSurface(raw);
426
+ if (CORE_PROMOTED_SURFACES.has(s))
427
+ continue;
379
428
  if (!RECOGNIZED_SURFACES.includes(s)) {
380
429
  throw new SurfaceError(`unknown surface "${s}". Recognized: ${RECOGNIZED_SURFACES.join(", ")}`);
381
430
  }
@@ -458,6 +507,10 @@ export function excludedContentPaths(enabled) {
458
507
  export const ROUTE_SINCE = {
459
508
  about: "0.0.0",
460
509
  "blog/[[...slug]]": "0.0.0",
510
+ // #6658 — four genuinely new route directories (D4, D5), attaching at
511
+ // today's fixed-group version, the same "attach at today's version" rule
512
+ // #6084, #5977, #5678, #5479, #5371 and #5048 applied.
513
+ "book-a-call": "0.26.0",
461
514
  careers: "0.0.0",
462
515
  "careers/[slug]": "0.0.0",
463
516
  "case-studies": "0.0.0",
@@ -471,10 +524,14 @@ export const ROUTE_SINCE = {
471
524
  downloads: "0.0.0",
472
525
  events: "0.0.0",
473
526
  "events/[slug]": "0.0.0",
527
+ // #6721 — the feature pages, a new route pair at today's line.
528
+ features: "0.27.0",
529
+ "features/[slug]": "0.27.0",
474
530
  faq: "0.0.0",
475
531
  "flows/[slug]": "0.0.0",
476
532
  "flows/[slug]/results/[payload]": "0.14.0",
477
533
  "flows/[slug]/start": "0.18.0",
534
+ glossary: "0.26.0",
478
535
  "how-to": "0.0.0",
479
536
  "how-to/[slug]": "0.0.0",
480
537
  integrations: "0.0.0",
@@ -486,11 +543,15 @@ export const ROUTE_SINCE = {
486
543
  // in rather than the "predates the manifest" 0.0.0 above.
487
544
  "meet/[slug]": "0.5.0",
488
545
  "meet/confirmed": "0.0.0",
546
+ newsletter: "0.26.0",
489
547
  partners: "0.0.0",
490
548
  "partners/[slug]": "0.0.0",
491
549
  // #5479 — three genuinely new route directories, so they attach at today's
492
550
  // fixed-group version rather than the "predates the manifest" 0.0.0 above,
493
551
  // the same rule #5048 ("projects/apply") and #5014 ("(authed)") applied.
552
+ // #6687 — the before/after portfolio, a new route pair at today's line.
553
+ portfolio: "0.27.0",
554
+ "portfolio/[slug]": "0.27.0",
494
555
  pricing: "0.22.0",
495
556
  privacy: "0.22.0",
496
557
  products: "0.0.0",
@@ -519,6 +580,7 @@ export const ROUTE_SINCE = {
519
580
  testimonials: "0.0.0",
520
581
  "thanks/[slug]": "0.14.0",
521
582
  "use-cases": "0.0.0",
583
+ "use-cases/[slug]": "0.26.0",
522
584
  whitepapers: "0.0.0",
523
585
  "whitepapers/[slug]": "0.0.0",
524
586
  // #5014 — the (authed) route GROUP: layout.tsx + sign-in-gate.tsx sit
@@ -708,44 +770,152 @@ export const NAV_CONFIG_PATH = "apps/web/lib/nav-config.ts";
708
770
  * surface set for the same reason the route tree does.
709
771
  *
710
772
  * The base items are the Free marketing-site chrome (#5469 D5, the approved
711
- * chrome diff recorded on that card):
712
- * a Product flyout (How it works · Features · FAQ), then How it works, Pricing
713
- * and About. Every one of those pages is in EVERY scaffold — the four core
714
- * routes plus the `[slug]` route over the overlay's LandingPage MDX — except
715
- * Pricing, which is a gateable surface: its item is emitted only when the
716
- * `pricing` surface is enabled (a template roster enables it, #5469 D10), so
717
- * the nav still never links a page the scaffold does not carry.
773
+ * chrome diff recorded on that card, re-pointed by #6658 D8):
774
+ * a Product flyout in two columns (Features · Use Cases, #6721), then Blog,
775
+ * Pricing and About. The feature and use-case links ride the `features` and
776
+ * `use-cases` surfaces; `/blog` is always linked (the teaser or the docs-blog
777
+ * module serves it). Pricing is gateable
778
+ * surfaces: each item is emitted only when its surface is enabled (a template
779
+ * roster enables it, #5469 D10), so the nav still never links a page the
780
+ * scaffold does not carry.
718
781
  *
719
782
  * Only gateable surfaces carrying a `navLabelKey` are linked after the base
720
783
  * items, because a nav entry needs a translated label. Emitting an entry whose
721
784
  * `labelKey` has no translation would trade a dead link for a missing-message
722
785
  * error.
723
786
  *
787
+ * The same file carries `footerColumns`, the footer's column groups (#6468):
788
+ * label keys and paths only, under the same gates, so the footer links every
789
+ * seed page the header does not.
790
+ *
724
791
  * Deterministic: gateable entries follow GATEABLE_SURFACES order (alphabetical
725
792
  * by id), after the fixed base items.
726
793
  */
727
794
  export function renderNavConfig(enabled) {
728
795
  const on = new Set(enabled);
729
- const linked = GATEABLE_SURFACES.filter((s) => s.navLabelKey !== undefined && on.has(s.id));
796
+ // `blog` is linked unconditionally below (operator ruling 2026-09-23), so
797
+ // it is excluded here; `use-cases` is the Free roster's own page (a
798
+ // module-less surface the roster enables, like `pricing`), linked in its
799
+ // fixed positions below rather than appended.
800
+ const linked = GATEABLE_SURFACES.filter((s) => s.navLabelKey !== undefined &&
801
+ on.has(s.id) &&
802
+ s.id !== "blog" &&
803
+ s.id !== "use-cases");
804
+ const useCases = on.has("use-cases");
805
+ const features = on.has("features");
806
+ // #6721 (operator ruling 2026-09-25): the Product flyout is two columns —
807
+ // Features and Use Cases — each a heading over three label-only links. The
808
+ // `groups` render the desktop panel; `features` carries the same links for
809
+ // the mobile disclosure and the active-path match. "Use cases" left the top
810
+ // nav (it lives in the flyout and its own footer column).
811
+ const featureLinks = features
812
+ ? [
813
+ ' { labelKey: "feature1", path: "/features/feature-1" },',
814
+ ' { labelKey: "feature2", path: "/features/feature-2" },',
815
+ ' { labelKey: "feature3", path: "/features/feature-3" },',
816
+ ]
817
+ : [];
818
+ const useCaseLinks = useCases
819
+ ? [
820
+ ' { labelKey: "useCase1", path: "/use-cases/persona-1" },',
821
+ ' { labelKey: "useCase2", path: "/use-cases/persona-2" },',
822
+ ' { labelKey: "useCase3", path: "/use-cases/persona-3" },',
823
+ ]
824
+ : [];
825
+ const indent = (line) => ` ${line}`;
826
+ const group = (headingKey, links) => links.length === 0
827
+ ? []
828
+ : [
829
+ " {",
830
+ ` headingKey: "${headingKey}",`,
831
+ " items: [",
832
+ ...links.map(indent),
833
+ " ],",
834
+ " },",
835
+ ];
730
836
  const entries = [
731
837
  " {",
732
838
  ' labelKey: "product",',
733
839
  " flyout: {",
734
- ' presentation: "list",',
735
- ' width: "trigger",',
840
+ // SOFT_LOCK_OK: #6508 — width "panel" (D4/D5): a fixed 640px panel.
841
+ ' width: "panel",',
736
842
  " features: [",
737
- ' { labelKey: "howItWorks", descKey: "howItWorksDesc", icon: Layers, path: "/how-it-works" },',
738
- ' { labelKey: "features", descKey: "featuresDesc", icon: Sparkles, path: "/features" },',
739
- ' { labelKey: "faq", descKey: "faqDesc", icon: HelpCircle, path: "/faq" },',
843
+ ...featureLinks,
844
+ ...useCaseLinks,
845
+ " ],",
846
+ " groups: [",
847
+ ...group("flyoutFeaturesHeading", featureLinks),
848
+ ...group("flyoutUseCasesHeading", useCaseLinks),
740
849
  " ],",
741
- ' actions: [{ labelKey: "docs", icon: BookOpen, path: "/how-it-works" }],',
850
+ " actions: [],",
742
851
  " },",
743
852
  " },",
744
- ' { labelKey: "howItWorks", path: "/how-it-works" },',
853
+ // Operator ruling 2026-09-25: Product · Blog · Pricing · About.
854
+ // `/blog` is always linked — the `blog` surface serves the real route, and
855
+ // with it off the `[slug]` route serves the overlay's module page.
856
+ ' { labelKey: "blog", path: "/blog" },',
745
857
  ...(on.has("pricing") ? [' { labelKey: "pricing", path: "/pricing" },'] : []),
746
858
  ' { labelKey: "about", path: "/about" },',
747
859
  ...linked.map((s) => ` { labelKey: "${s.navLabelKey}", path: "/${s.id}" },`),
748
860
  ];
861
+ // The footer's column groups (operator rulings 2026-09-23 + 2026-09-25):
862
+ // Site pages, Features, Use Cases, Resources. Pricing rides the same
863
+ // `pricing` gate as the header item; `/blog` is always linked (above). The
864
+ // Features column links the three feature pages and the /features hub; the
865
+ // Use Cases column the three use cases and the /use-cases hub — each rides
866
+ // its surface's gate. Book a call, Glossary and Newsletter (#6658 D8) each
867
+ // ride their own surface's gate.
868
+ const footerEntries = [
869
+ " {",
870
+ ' headingKey: "footerSitePagesHeading",',
871
+ " items: [",
872
+ ...(features ? [' { labelKey: "features", path: "/features" },'] : []),
873
+ ...(on.has("pricing")
874
+ ? [' { labelKey: "pricing", path: "/pricing" },']
875
+ : []),
876
+ ' { labelKey: "about", path: "/about" },',
877
+ ' { labelKey: "contact", path: "/contact" },',
878
+ ...(on.has("book-a-call")
879
+ ? [' { labelKey: "bookACall", path: "/book-a-call" },']
880
+ : []),
881
+ " ],",
882
+ " },",
883
+ ...(features
884
+ ? [
885
+ " {",
886
+ ' headingKey: "footerFeaturesHeading",',
887
+ " items: [",
888
+ ...featureLinks.map((l) => l.slice(2)),
889
+ ' { labelKey: "allFeatures", path: "/features" },',
890
+ " ],",
891
+ " },",
892
+ ]
893
+ : []),
894
+ ...(useCases
895
+ ? [
896
+ " {",
897
+ ' headingKey: "footerUseCasesHeading",',
898
+ " items: [",
899
+ ...useCaseLinks.map((l) => l.slice(2)),
900
+ ' { labelKey: "allUseCases", path: "/use-cases" },',
901
+ " ],",
902
+ " },",
903
+ ]
904
+ : []),
905
+ " {",
906
+ ' headingKey: "footerResourcesHeading",',
907
+ " items: [",
908
+ ' { labelKey: "blog", path: "/blog" },',
909
+ ...(on.has("glossary")
910
+ ? [' { labelKey: "glossary", path: "/glossary" },']
911
+ : []),
912
+ ...(on.has("newsletter")
913
+ ? [' { labelKey: "newsletter", path: "/newsletter" },']
914
+ : []),
915
+ ' { labelKey: "faq", path: "/faq" },',
916
+ " ],",
917
+ " },",
918
+ ];
749
919
  // The emitted file imports the consumer's `NavItem` type from the UI package.
750
920
  // Assembled from fragments so THIS source file never contains the literal
751
921
  // import string: `tools/lint-architecture.ts` matches on source text, so a
@@ -763,15 +933,25 @@ export function renderNavConfig(enabled) {
763
933
  // points at a route this site actually carries — the nav is regenerated with
764
934
  // the surface set so it can never link a page that was not scaffolded.
765
935
  //
766
- // Adding a link: scaffold the page (the \`scaffold-page\` skill), then add an
936
+ // Adding a link: scaffold the page (the \`add-page\` skill), then add an
767
937
  // entry below with a \`labelKey\` that exists in the \`nav\` namespace of
768
938
  // \`apps/web/messages/<locale>.json\`. Paths are locale-agnostic — Nav prefixes
769
939
  // them with the active locale at render.
770
940
  ${navItemImport}
771
- import { BookOpen, HelpCircle, Layers, Sparkles } from "lucide-react";
772
941
 
773
942
  export const navItems: NavItem[] = [
774
943
  ${entries.join("\n")}
775
944
  ];
945
+
946
+ // Footer column groups. Keys only — the layout resolves each \`headingKey\`
947
+ // and \`labelKey\` through the same \`nav\` labels as the header.
948
+ export interface FooterColumnConfig {
949
+ headingKey: string;
950
+ items: { labelKey: string; path: string }[];
951
+ }
952
+
953
+ export const footerColumns: FooterColumnConfig[] = [
954
+ ${footerEntries.join("\n")}
955
+ ];
776
956
  `;
777
957
  }
@@ -15,6 +15,15 @@ export interface SyncRoutesOptions {
15
15
  versions: WorkspaceVersionMap;
16
16
  /** Copy the missing routes into the target. Default false = check only. */
17
17
  apply?: boolean;
18
+ /**
19
+ * Surface ids to treat as carried whether or not any of their route dirs
20
+ * exists yet (#6653 D8) — `add-module` passes the added module's
21
+ * `MODULE_SURFACES`, since a surface whose routes ship from `templates/`
22
+ * would otherwise read as not-carried on first add and never be copied.
23
+ * Undefined for every other caller: the carried set is then exactly the
24
+ * presence-derived one.
25
+ */
26
+ forceSurfaces?: readonly string[];
18
27
  }
19
28
  /** One route directory the report names. */
20
29
  export interface RouteReport {
@@ -141,10 +141,10 @@ export function syncRoutes(opts) {
141
141
  // unrelated "projects" CONTENT surface is separately requested. Without
142
142
  // this exclusion, a signal-collectors-only scaffold would falsely read as
143
143
  // "carries projects" the moment its bundle lands that one file.
144
- const carried = new Set();
144
+ const carried = new Set(opts.forceSurfaces ?? []);
145
145
  const skippedSurfaces = [];
146
146
  for (const surface of GATEABLE_SURFACES) {
147
- const present = surface.routes.some((r) => {
147
+ const present = carried.has(surface.id) || surface.routes.some((r) => {
148
148
  const p = join(targetAbs, r);
149
149
  if (!existsSync(p) || !statSync(p).isDirectory())
150
150
  return false;
@@ -18,6 +18,34 @@ import type { CategorySlug } from "@working-theory/schema/categories";
18
18
  import { type PageRosterRow } from "./module-architecture.js";
19
19
  import type { ModuleName } from "./parse-args.js";
20
20
  import type { TemplateLayout } from "./site-chrome.js";
21
+ import { type BuildableShape, type TemplateAlias } from "./starter-grid.js";
22
+ /**
23
+ * The closed **shape** enum (starter-template contract § The three axes,
24
+ * operator-disposed 2026-09-24) — `<primary node>-<offering node>` in
25
+ * schema.org terms, kebab-cased. Declared beside `TemplateManifest`, the same
26
+ * pattern `ModuleName` uses in `parse-args.ts`: a `readonly [...] as const`
27
+ * array plus its derived literal-union type, so an unrecognized value fails
28
+ * type-check rather than a runtime validation.
29
+ */
30
+ export declare const STARTER_SHAPES: readonly ["organization-product", "organization-service", "local-business-service", "local-business-product", "person-service"];
31
+ export type StarterShape = (typeof STARTER_SHAPES)[number];
32
+ /**
33
+ * The closed **design** enum (same contract section) — the look applied to
34
+ * any shape; orthogonal to it by construction.
35
+ */
36
+ export declare const STARTER_DESIGNS: readonly ["standard", "minimalism"];
37
+ export type StarterDesign = (typeof STARTER_DESIGNS)[number];
38
+ /**
39
+ * Each design's layout knobs (#6638) — the part of a design that is chrome,
40
+ * so a template names `design:` instead of re-typing a `layout` block, and any
41
+ * shape can wear any design. `resolveSiteChrome(layout, design)` reads it:
42
+ * an explicit `TemplateManifest.layout` wins field by field, then this, then
43
+ * `DEFAULT_SITE_CHROME`. `standard` declares nothing — it IS the defaults, so
44
+ * spelling them out here would be a second copy of them to drift. A plain
45
+ * object, not a file: this module touches no disk. The design's documented
46
+ * look lives in `site-templates/designs/<design>/DESIGN.md`.
47
+ */
48
+ export declare const DESIGN_LAYOUTS: Readonly<Record<StarterDesign, TemplateLayout>>;
21
49
  /**
22
50
  * One site template: its fixed module list and the page rows it brings itself.
23
51
  *
@@ -57,6 +85,20 @@ export interface TemplateManifest {
57
85
  * registry — never a group here.
58
86
  */
59
87
  categories: readonly CategorySlug[];
88
+ /**
89
+ * The shape this template's roster renders (starter-template contract § The
90
+ * three axes) — `<primary node>-<offering node>`, from the closed
91
+ * {@link STARTER_SHAPES} enum. Owns the entity preset, the roster and each
92
+ * page's section stack; the machine truth the showcase, the build-starter
93
+ * skill and the render gate read — never the directory name.
94
+ */
95
+ shape: StarterShape;
96
+ /**
97
+ * The design this template renders (same contract section) — the look,
98
+ * orthogonal to `shape` by construction: any design applies to any shape.
99
+ * From the closed {@link STARTER_DESIGNS} enum.
100
+ */
101
+ design: StarterDesign;
60
102
  /**
61
103
  * The template this one EXTENDS — its lineage, never its level (#5618 D2).
62
104
  * A single parent, not a list: "later layers win file by file" needs a
@@ -70,7 +112,7 @@ export interface TemplateManifest {
70
112
  */
71
113
  extends: string;
72
114
  /** Base rows, before any module's contribution is merged. */
73
- roster: readonly PageRosterRow[];
115
+ roster: readonly TemplateRosterRow[];
74
116
  /** Bundle-relative source → scaffold-relative destination. */
75
117
  installMap?: Record<string, string>;
76
118
  /**
@@ -94,26 +136,30 @@ export interface TemplateManifest {
94
136
  * the directory-scanning tests key their skip on.
95
137
  */
96
138
  export declare const BASE_LAYER_DIR = "_base";
139
+ /**
140
+ * One row of a template's own roster — the same shape as a module's
141
+ * `PageRosterRow`, `contentDir` included (#6646 disposition (a)): a module's
142
+ * contributed detail row (`/reports/[slug]`) serves a seed directory exactly
143
+ * as a template's own does. Where `routes-map.ts` records the route's
144
+ * `contentPath`, the generator refuses a `contentDir` that disagrees.
145
+ */
146
+ export type TemplateRosterRow = PageRosterRow;
147
+ /**
148
+ * Whether a merged roster declares one scaffold-relative content file
149
+ * (#6646 D1): named by a row's `content`, or under a row's `contentDir`.
150
+ * The one predicate the scaffold's per-row install and the manifest
151
+ * generator's install-diff both read, so the two cannot disagree.
152
+ */
153
+ export declare function rosterDeclares(roster: readonly TemplateRosterRow[], path: string): boolean;
97
154
  /** Thrown on an undeclared template name or a route claimed twice. Mirrors `SurfaceError`'s shape. */
98
155
  export declare class TemplateError extends Error {
99
156
  constructor(message: string);
100
157
  }
101
158
  /**
102
- * The three settled templates and their fixed `requires` lists — settled by
103
- * Sam, 2026-09-14 (parent § D12). The plain name is the Pro one: it is the
104
- * flagship and the demo.
105
- *
106
- * `workspace` appears in none of them. It is a Pro module, but an authed app
107
- * surface contributing no PUBLIC page to a marketing site, and `requires`
108
- * names what the roster uses (parent § D15). A Pro key unlocks it regardless
109
- * of any template. `ModuleName` could express it either way — `workspace` is
110
- * one of the seven `RECOGNIZED_MODULES` names; it is simply not one any
111
- * template's fixed `requires` list names.
112
- *
113
- * `commerce` is likewise in none of them: it is a per-site add-on available at
114
- * any level, so a template for it would contradict the offering model and
115
- * multiply the set combinatorially (parent § D13). It still contributes rows
116
- * to whichever template takes it, through `mergeRoster` below.
159
+ * The declared templates, each composed once at load from its `ALIASES` row
160
+ * (#6753) — no manifest here is hand-typed, so a template's shape, design,
161
+ * modules and roster have one authority: `starter-grid.ts`. Declaration order
162
+ * is `ALIASES`'s, which is `RECOGNIZED_TEMPLATES`'s.
117
163
  *
118
164
  * Frozen (#5507): this registry is also the issuer's authorization allowlist
119
165
  * (spec 5480 § C7) — a mutable global there could be monkey-patched at
@@ -121,8 +167,37 @@ export declare class TemplateError extends Error {
121
167
  * fixture rather than mutating this one.
122
168
  */
123
169
  export declare const TEMPLATE_MANIFESTS: Readonly<Record<string, TemplateManifest>>;
124
- /** Look up a declared template. @throws TemplateError on an undeclared name. */
125
- export declare function resolveTemplate(name: string): TemplateManifest;
170
+ /**
171
+ * The composed FREE manifest per shape with no `--template` alias of its own
172
+ * (#6757 D5) — the coordinates a keyless `--shape S` builds. `Object.freeze`d,
173
+ * composed once at load by the same module-private `composeManifest` over
174
+ * `SHAPE_STARTER_ALIASES`, each manifest's `name` the shape. Data holding
175
+ * composed manifests, the status D5 gives `TEMPLATE_MANIFESTS`;
176
+ * `resolveTemplate` stays the only exported function that yields a
177
+ * template's roster, and its behavior is unchanged — it does NOT resolve a
178
+ * shape name (NFR-3, the issuer allowlist stays `TEMPLATE_MANIFESTS`-only).
179
+ */
180
+ export declare const SHAPE_STARTERS: Readonly<Partial<Record<BuildableShape, TemplateManifest>>>;
181
+ /**
182
+ * Where a starter's files live under `site-templates/` (#6757 D5) — the one
183
+ * place that says so. A `SHAPE_STARTERS` key (a bare-shape starter with no
184
+ * `--template` alias) lives at `shapes/<name>`, out of the template showcase
185
+ * (§ 1.6); every other name (a declared `--template` alias) is unchanged.
186
+ */
187
+ export declare function templateTreeDir(name: string): string;
188
+ /**
189
+ * Look up a declared template — the one exported function that yields a
190
+ * template's roster (#6753 AC-1); `TEMPLATE_MANIFESTS`, the frozen record it
191
+ * reads, holds only manifests composed from `ALIASES`.
192
+ *
193
+ * With no `aliases`, returns the frozen `TEMPLATE_MANIFESTS` entry itself.
194
+ * With an injected alias table — the `resolveTemplateLineage` precedent
195
+ * (#5507) — composes `aliases[name]` against the shipped shape rosters, so a
196
+ * test exercises the composer's refusals without touching the shipped table.
197
+ *
198
+ * @throws TemplateError on an undeclared name, or a malformed injected alias.
199
+ */
200
+ export declare function resolveTemplate(name: string, aliases?: Readonly<Record<string, TemplateAlias>>): TemplateManifest;
126
201
  /**
127
202
  * Resolve a template's `extends` chain — `[name, …ancestors, "_base"]` — in
128
203
  * the client registry, before any fetch (#5618 D2). `_base` is the only
@@ -161,7 +236,7 @@ export declare function resolveTemplateLineage(name: string, registry?: Readonly
161
236
  * runtime by a base-wins or module-wins precedence rule, either of which
162
237
  * silently produces a site missing a page someone declared.
163
238
  */
164
- export declare function mergeRoster(manifest: TemplateManifest, modules: readonly string[]): PageRosterRow[];
239
+ export declare function mergeRoster(manifest: TemplateManifest, modules: readonly string[]): TemplateRosterRow[];
165
240
  /**
166
241
  * Whether a template installs on the keyless path or needs a verified
167
242
  * `AILK_LICENSE_KEY` — derived from `requires`, never declared (parent § D3).