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
@@ -0,0 +1,126 @@
1
+ /**
2
+ * The category profile (#6640; starter-template contract § D) — one YAML
3
+ * document per leaf category slug at `category-profiles/<slug>.yaml`, the data
4
+ * the `fill-page` skill reads to make one starter speak for one business type.
5
+ *
6
+ * Dependency-free by design: `create-ailk` ships `tar` as its only runtime
7
+ * dependency, so this is a hand-written validator in the style of
8
+ * `resolveTemplate`/`TemplateError`, not a Zod schema. The caller parses the
9
+ * YAML and injects the registries (page types, leaf categories); nothing here
10
+ * imports a workspace package at run time.
11
+ */
12
+ import type { StarterShape } from "./template-manifest.js";
13
+ /**
14
+ * The contract's shape table, as data: each shape's primary node, the
15
+ * secondaries it allows, and whether it is reserved. Keyed by the manifest's
16
+ * own `StarterShape`, so a shape added there without a row here fails
17
+ * type-check.
18
+ */
19
+ export declare const CATEGORY_PROFILE_SHAPES: {
20
+ readonly "organization-product": {
21
+ readonly primary: "Organization";
22
+ readonly secondaries: readonly ["SoftwareApplication"];
23
+ readonly reserved: false;
24
+ };
25
+ readonly "organization-service": {
26
+ readonly primary: "Organization";
27
+ readonly secondaries: readonly [];
28
+ readonly reserved: false;
29
+ };
30
+ readonly "local-business-service": {
31
+ readonly primary: "LocalBusiness";
32
+ readonly secondaries: readonly ["Person"];
33
+ readonly reserved: false;
34
+ };
35
+ readonly "local-business-product": {
36
+ readonly primary: "LocalBusiness";
37
+ readonly secondaries: readonly [];
38
+ readonly reserved: true;
39
+ };
40
+ readonly "person-service": {
41
+ readonly primary: "Person";
42
+ readonly secondaries: readonly ["Organization"];
43
+ readonly reserved: false;
44
+ };
45
+ };
46
+ /**
47
+ * schema.org's `LocalBusiness` subtree, `LocalBusiness` itself included — the
48
+ * closure of the `LocalBusiness` union in `schema-dts`. A literal so `src/`
49
+ * needs no type package at run time; `category-profile.test.ts` proves it
50
+ * set-equal to the `schema-dts` closure, so it cannot drift silently.
51
+ */
52
+ export declare const LOCAL_BUSINESS_TYPES: ReadonlySet<string>;
53
+ export declare const OFFERING_PRICING: readonly ["quote", "fixed", "hourly", "subscription"];
54
+ export declare const PROOF_KINDS: readonly ["credential", "reviews", "before-after", "response-time", "guarantee", "experience"];
55
+ export declare const CONTENT_POSTURES: readonly ["instructions", "sample"];
56
+ export interface CategoryOffering {
57
+ name: string;
58
+ job: string;
59
+ pricing: (typeof OFFERING_PRICING)[number];
60
+ }
61
+ export interface CategoryProof {
62
+ kind: (typeof PROOF_KINDS)[number];
63
+ label: string;
64
+ }
65
+ export type CategoryRosterDelta = {
66
+ op: "add";
67
+ route: string;
68
+ pageType: string;
69
+ reason: string;
70
+ } | {
71
+ op: "drop";
72
+ route: string;
73
+ reason: string;
74
+ };
75
+ export interface CategoryIntakeItem {
76
+ id: string;
77
+ ask: string;
78
+ /** The section types an answer to this question can fill. */
79
+ fills: string[];
80
+ }
81
+ export interface CategoryProfile {
82
+ slug: string;
83
+ shape: StarterShape;
84
+ /** The schema.org subtype of the primary node; only on `local-business-*`. */
85
+ subtype?: string;
86
+ secondary?: string;
87
+ offerings: CategoryOffering[];
88
+ proof: CategoryProof[];
89
+ vocabulary: {
90
+ problem: string[];
91
+ service: string[];
92
+ outcome: string[];
93
+ };
94
+ roster: CategoryRosterDelta[];
95
+ /** Nav label → replacement label. Declared, not yet applied to a scaffold. */
96
+ nav: Record<string, string>;
97
+ content_posture: (typeof CONTENT_POSTURES)[number];
98
+ intake: CategoryIntakeItem[];
99
+ }
100
+ /** A recorded tier-2 intake — `content/_intake.yaml`. */
101
+ export interface CategoryIntake {
102
+ answers: Record<string, string | string[] | null>;
103
+ }
104
+ export interface ParseCategoryProfileOptions {
105
+ /** The file's basename without `.yaml`; the profile's `slug` must equal it. */
106
+ fileSlug: string;
107
+ /** Registered page-type keys (the schema registry). */
108
+ pageTypes: ReadonlySet<string>;
109
+ /** Leaf category slugs (the category registry). */
110
+ categories: ReadonlySet<string>;
111
+ }
112
+ /** Thrown on an invalid profile or intake; the message names the failing field path. */
113
+ export declare class CategoryProfileError extends Error {
114
+ constructor(message: string);
115
+ }
116
+ /**
117
+ * Validate a parsed category profile.
118
+ * @throws CategoryProfileError naming the failing field path.
119
+ */
120
+ export declare function parseCategoryProfile(raw: unknown, options: ParseCategoryProfileOptions): CategoryProfile;
121
+ /**
122
+ * Validate a recorded intake (`content/_intake.yaml`) against its profile.
123
+ * Unknown ids fail; an id the file omits is returned as `null` (unanswered).
124
+ * @throws CategoryProfileError naming the failing field path.
125
+ */
126
+ export declare function parseIntake(raw: unknown, profile: CategoryProfile): CategoryIntake;
@@ -0,0 +1,262 @@
1
+ /**
2
+ * The contract's shape table, as data: each shape's primary node, the
3
+ * secondaries it allows, and whether it is reserved. Keyed by the manifest's
4
+ * own `StarterShape`, so a shape added there without a row here fails
5
+ * type-check.
6
+ */
7
+ export const CATEGORY_PROFILE_SHAPES = {
8
+ "organization-product": {
9
+ primary: "Organization",
10
+ secondaries: ["SoftwareApplication"],
11
+ reserved: false,
12
+ },
13
+ "organization-service": {
14
+ primary: "Organization",
15
+ secondaries: [],
16
+ reserved: false,
17
+ },
18
+ "local-business-service": {
19
+ primary: "LocalBusiness",
20
+ secondaries: ["Person"],
21
+ reserved: false,
22
+ },
23
+ "local-business-product": {
24
+ primary: "LocalBusiness",
25
+ secondaries: [],
26
+ reserved: true,
27
+ },
28
+ "person-service": {
29
+ primary: "Person",
30
+ secondaries: ["Organization"],
31
+ reserved: false,
32
+ },
33
+ };
34
+ /**
35
+ * schema.org's `LocalBusiness` subtree, `LocalBusiness` itself included — the
36
+ * closure of the `LocalBusiness` union in `schema-dts`. A literal so `src/`
37
+ * needs no type package at run time; `category-profile.test.ts` proves it
38
+ * set-equal to the `schema-dts` closure, so it cannot drift silently.
39
+ */
40
+ export const LOCAL_BUSINESS_TYPES = new Set([
41
+ "AccountingService", "AdultEntertainment", "AmusementPark", "AnimalShelter",
42
+ "ArchiveOrganization", "ArtGallery", "Attorney", "AutoBodyShop", "AutoDealer",
43
+ "AutoPartsStore", "AutoRental", "AutoRepair", "AutoWash", "AutomatedTeller",
44
+ "AutomotiveBusiness", "Bakery", "BankOrCreditUnion", "BarOrPub", "BeautySalon",
45
+ "BedAndBreakfast", "BikeStore", "BookStore", "BowlingAlley", "Brewery",
46
+ "CafeOrCoffeeShop", "Campground", "Casino", "ChildCare", "ClothingStore",
47
+ "ComedyClub", "CommunityHealth", "ComputerStore", "ConvenienceStore",
48
+ "CovidTestingFacility", "DaySpa", "Dentist", "DepartmentStore", "Dermatology",
49
+ "DietNutrition", "Distillery", "DryCleaningOrLaundry", "Electrician",
50
+ "ElectronicsStore", "Emergency", "EmergencyService", "EmploymentAgency",
51
+ "EntertainmentBusiness", "ExerciseGym", "FastFoodRestaurant", "FinancialService",
52
+ "FireStation", "Florist", "FoodEstablishment", "FurnitureStore", "GardenStore",
53
+ "GasStation", "GeneralContractor", "Geriatric", "GolfCourse", "GovernmentOffice",
54
+ "GroceryStore", "Gynecologic", "HVACBusiness", "HairSalon", "HardwareStore",
55
+ "HealthAndBeautyBusiness", "HealthClub", "HobbyShop", "HomeAndConstructionBusiness",
56
+ "HomeGoodsStore", "Hospital", "Hostel", "Hotel", "HousePainter", "IceCreamShop",
57
+ "IndividualPhysician", "InsuranceAgency", "InternetCafe", "JewelryStore",
58
+ "LegalService", "Library", "LiquorStore", "LocalBusiness", "Locksmith",
59
+ "LodgingBusiness", "MedicalBusiness", "MedicalClinic", "MensClothingStore",
60
+ "Midwifery", "MobilePhoneStore", "Motel", "MotorcycleDealer", "MotorcycleRepair",
61
+ "MovieRentalStore", "MovieTheater", "MovingCompany", "MusicStore", "NailSalon",
62
+ "NightClub", "Notary", "Nursing", "Obstetric", "OfficeEquipmentStore", "Oncologic",
63
+ "Optician", "Optometric", "Otolaryngologic", "OutletStore", "PawnShop", "Pediatric",
64
+ "PetStore", "Pharmacy", "Physician", "PhysiciansOffice", "Physiotherapy",
65
+ "PlasticSurgery", "Plumber", "Podiatric", "PoliceStation", "PostOffice",
66
+ "PrimaryCare", "ProfessionalService", "Psychiatric", "PublicHealth",
67
+ "PublicSwimmingPool", "RadioStation", "RealEstateAgent", "RecyclingCenter",
68
+ "Resort", "Restaurant", "RoofingContractor", "SelfStorage", "ShoeStore",
69
+ "ShoppingCenter", "SkiResort", "SportingGoodsStore", "SportsActivityLocation",
70
+ "SportsClub", "StadiumOrArena", "Store", "TattooParlor", "TelevisionStation",
71
+ "TennisComplex", "TireShop", "TouristInformationCenter", "ToyStore",
72
+ "TravelAgency", "VacationRental", "WholesaleStore", "Winery",
73
+ ]);
74
+ export const OFFERING_PRICING = ["quote", "fixed", "hourly", "subscription"];
75
+ export const PROOF_KINDS = [
76
+ "credential",
77
+ "reviews",
78
+ "before-after",
79
+ "response-time",
80
+ "guarantee",
81
+ "experience",
82
+ ];
83
+ export const CONTENT_POSTURES = ["instructions", "sample"];
84
+ /** Thrown on an invalid profile or intake; the message names the failing field path. */
85
+ export class CategoryProfileError extends Error {
86
+ constructor(message) {
87
+ super(message);
88
+ this.name = "CategoryProfileError";
89
+ }
90
+ }
91
+ function fail(path, message) {
92
+ throw new CategoryProfileError(`${path}: ${message}`);
93
+ }
94
+ function object(value, path) {
95
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
96
+ fail(path, "must be a mapping");
97
+ }
98
+ return value;
99
+ }
100
+ function keys(value, path, required, optional = []) {
101
+ const allowed = new Set([...required, ...optional]);
102
+ for (const key of Object.keys(value)) {
103
+ if (!allowed.has(key))
104
+ fail(path === "" ? key : `${path}.${key}`, "unknown key");
105
+ }
106
+ for (const key of required) {
107
+ if (!(key in value))
108
+ fail(path === "" ? key : `${path}.${key}`, "is required");
109
+ }
110
+ }
111
+ function string(value, path) {
112
+ if (typeof value !== "string" || value.trim() === "")
113
+ fail(path, "must be a non-empty string");
114
+ return value;
115
+ }
116
+ function oneOf(value, allowed, path) {
117
+ if (!allowed.includes(value)) {
118
+ fail(path, `must be one of ${allowed.join(", ")} (got ${JSON.stringify(value)})`);
119
+ }
120
+ return value;
121
+ }
122
+ function list(value, path, nonEmpty) {
123
+ if (!Array.isArray(value))
124
+ fail(path, "must be a list");
125
+ if (nonEmpty && value.length === 0)
126
+ fail(path, "must not be empty");
127
+ return value;
128
+ }
129
+ function strings(value, path) {
130
+ return list(value, path, true).map((item, i) => string(item, `${path}[${i}]`));
131
+ }
132
+ /**
133
+ * Validate a parsed category profile.
134
+ * @throws CategoryProfileError naming the failing field path.
135
+ */
136
+ export function parseCategoryProfile(raw, options) {
137
+ const doc = object(raw, "(profile)");
138
+ keys(doc, "", ["slug", "shape", "offerings", "proof", "vocabulary", "roster", "nav", "content_posture", "intake"], ["subtype", "secondary"]);
139
+ const slug = string(doc.slug, "slug");
140
+ if (!options.categories.has(slug))
141
+ fail("slug", `"${slug}" is not a leaf category slug`);
142
+ if (slug !== options.fileSlug) {
143
+ fail("slug", `"${slug}" does not match its file name "${options.fileSlug}.yaml"`);
144
+ }
145
+ const shapes = Object.keys(CATEGORY_PROFILE_SHAPES);
146
+ const shape = oneOf(doc.shape, shapes, "shape");
147
+ const shapeRow = CATEGORY_PROFILE_SHAPES[shape];
148
+ if (shapeRow.reserved)
149
+ fail("shape", `"${shape}" is reserved and not yet built`);
150
+ let subtype;
151
+ if (shapeRow.primary === "LocalBusiness") {
152
+ subtype = string(doc.subtype, "subtype");
153
+ if (!LOCAL_BUSINESS_TYPES.has(subtype)) {
154
+ fail("subtype", `"${subtype}" is not a schema.org LocalBusiness subtype`);
155
+ }
156
+ }
157
+ else if (doc.subtype !== undefined) {
158
+ fail("subtype", `is only valid on a local-business-* shape, not "${shape}"`);
159
+ }
160
+ let secondary;
161
+ if (doc.secondary !== undefined) {
162
+ if (shapeRow.secondaries.length === 0)
163
+ fail("secondary", `is not allowed on "${shape}"`);
164
+ secondary = oneOf(doc.secondary, shapeRow.secondaries, "secondary");
165
+ }
166
+ const offerings = list(doc.offerings, "offerings", true).map((item, i) => {
167
+ const path = `offerings[${i}]`;
168
+ const o = object(item, path);
169
+ keys(o, path, ["name", "job", "pricing"]);
170
+ return {
171
+ name: string(o.name, `${path}.name`),
172
+ job: string(o.job, `${path}.job`),
173
+ pricing: oneOf(o.pricing, OFFERING_PRICING, `${path}.pricing`),
174
+ };
175
+ });
176
+ const proof = list(doc.proof, "proof", true).map((item, i) => {
177
+ const path = `proof[${i}]`;
178
+ const p = object(item, path);
179
+ keys(p, path, ["kind", "label"]);
180
+ return { kind: oneOf(p.kind, PROOF_KINDS, `${path}.kind`), label: string(p.label, `${path}.label`) };
181
+ });
182
+ const vocab = object(doc.vocabulary, "vocabulary");
183
+ keys(vocab, "vocabulary", ["problem", "service", "outcome"]);
184
+ const vocabulary = {
185
+ problem: strings(vocab.problem, "vocabulary.problem"),
186
+ service: strings(vocab.service, "vocabulary.service"),
187
+ outcome: strings(vocab.outcome, "vocabulary.outcome"),
188
+ };
189
+ const roster = list(doc.roster ?? [], "roster", false).map((item, i) => {
190
+ const path = `roster[${i}]`;
191
+ const r = object(item, path);
192
+ const op = oneOf(r.op, ["add", "drop"], `${path}.op`);
193
+ const route = string(r.route, `${path}.route`);
194
+ if (!route.startsWith("/"))
195
+ fail(`${path}.route`, `must start with "/"`);
196
+ if (op === "drop") {
197
+ keys(r, path, ["op", "route", "reason"]);
198
+ return { op, route, reason: string(r.reason, `${path}.reason`) };
199
+ }
200
+ keys(r, path, ["op", "route", "pageType", "reason"]);
201
+ const pageType = string(r.pageType, `${path}.pageType`);
202
+ if (!options.pageTypes.has(pageType)) {
203
+ fail(`${path}.pageType`, `"${pageType}" is not a registered page type`);
204
+ }
205
+ return { op, route, pageType, reason: string(r.reason, `${path}.reason`) };
206
+ });
207
+ const navDoc = doc.nav === null ? {} : object(doc.nav, "nav");
208
+ const nav = Object.fromEntries(Object.entries(navDoc).map(([from, to]) => [from, string(to, `nav.${from}`)]));
209
+ const ids = new Set();
210
+ const intake = list(doc.intake, "intake", true).map((item, i) => {
211
+ const path = `intake[${i}]`;
212
+ const q = object(item, path);
213
+ keys(q, path, ["id", "ask", "fills"]);
214
+ const id = string(q.id, `${path}.id`);
215
+ if (ids.has(id))
216
+ fail(`${path}.id`, `"${id}" is not unique`);
217
+ ids.add(id);
218
+ return { id, ask: string(q.ask, `${path}.ask`), fills: strings(q.fills, `${path}.fills`) };
219
+ });
220
+ return {
221
+ slug,
222
+ shape,
223
+ ...(subtype !== undefined ? { subtype } : {}),
224
+ ...(secondary !== undefined ? { secondary } : {}),
225
+ offerings,
226
+ proof,
227
+ vocabulary,
228
+ roster,
229
+ nav,
230
+ content_posture: oneOf(doc.content_posture, CONTENT_POSTURES, "content_posture"),
231
+ intake,
232
+ };
233
+ }
234
+ /**
235
+ * Validate a recorded intake (`content/_intake.yaml`) against its profile.
236
+ * Unknown ids fail; an id the file omits is returned as `null` (unanswered).
237
+ * @throws CategoryProfileError naming the failing field path.
238
+ */
239
+ export function parseIntake(raw, profile) {
240
+ const doc = object(raw, "(intake)");
241
+ keys(doc, "", ["answers"]);
242
+ const given = doc.answers === null ? {} : object(doc.answers, "answers");
243
+ const known = new Set(profile.intake.map((q) => q.id));
244
+ for (const id of Object.keys(given)) {
245
+ if (!known.has(id))
246
+ fail(`answers.${id}`, "is not an intake id of this profile");
247
+ }
248
+ const answers = {};
249
+ for (const { id } of profile.intake) {
250
+ const value = given[id];
251
+ const path = `answers.${id}`;
252
+ if (value === undefined || value === null)
253
+ answers[id] = null;
254
+ else if (typeof value === "string")
255
+ answers[id] = value.trim() === "" ? null : value;
256
+ else if (Array.isArray(value))
257
+ answers[id] = value.map((v, i) => string(v, `${path}[${i}]`));
258
+ else
259
+ fail(path, "must be a string, a list of strings, or null");
260
+ }
261
+ return { answers };
262
+ }
package/dist/cli.js CHANGED
@@ -3,8 +3,12 @@
3
3
  * `create-ailk` — entry point.
4
4
  *
5
5
  * Orchestration:
6
- * 0. on a `sync-routes` subcommand: parse its own argv, run syncRoutesSync(),
6
+ * 0. on an `add-module` / `upgrade` subcommand: parse its own argv, run
7
+ * addModule(), print the report, exit (2 on a refusal it names)
8
+ * on a `sync-routes` subcommand: parse its own argv, run syncRoutesSync(),
7
9
  * print the report, exit (1 iff routes are missing in check mode)
10
+ * on a `license set` subcommand: write the key into the scaffold, push it
11
+ * to a linked Vercel project and redeploy, exit (#6683 D9)
8
12
  * 1. parse argv → CliOptions
9
13
  * 2. on --help: print helpText, exit 0
10
14
  * 3. on --version: print version, exit 0
@@ -30,9 +34,11 @@ import { spawnSync } from "node:child_process";
30
34
  import { readFileSync } from "node:fs";
31
35
  import { dirname, resolve } from "node:path";
32
36
  import { fileURLToPath } from "node:url";
33
- import { proCallout } from "./lib/pro-callout.js";
34
- import { helpText, parseCliArgs, parseModules } from "./parse-args.js";
35
- import { formatSurfaceDrift, scaffold, syncArchitectureSync, syncCatalogSync, syncRoutesSync, } from "./programmatic.js";
37
+ import { ADD_MODULE_HELP, AddModuleRefusedError, formatAddModuleReport, parseAddModuleArgs, } from "./add-module.js";
38
+ import { FREE_PREREQUISITES_NOTE, LICENSED_PREREQUISITES_NOTE, isLicensedInstall, proCallout, } from "./lib/pro-callout.js";
39
+ import { LICENSE_SET_HELP, runLicenseSet } from "./license-set.js";
40
+ import { helpText, parseCliArgs, parseModules, resolveCliShape, toScaffoldOptions, } from "./parse-args.js";
41
+ import { addModule, formatSurfaceDrift, scaffold, syncArchitectureSync, syncCatalogSync, syncRoutesSync, } from "./programmatic.js";
36
42
  import { CORE_SURFACE_PATHS, SURFACE_ALIASES } from "./surfaces.js";
37
43
  import { SYNC_ROUTES_HELP, formatSyncRoutesReport, parseSyncRoutesArgs, } from "./sync-routes.js";
38
44
  const PKG_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
@@ -83,6 +89,32 @@ function readScaffoldPrerequisites(targetAbs) {
83
89
  }
84
90
  async function main() {
85
91
  const argv = process.argv.slice(2);
92
+ // `add-module` (alias `upgrade`, #6653): apply a purchased module to an
93
+ // existing site. Its own argv shape, like `sync-routes` below. A refusal it
94
+ // names (a free module, a patch outside the config allowlist, an unreadable
95
+ // architecture.yaml) exits 2; anything else — the licence gate included —
96
+ // falls through to the top-level catch, exit 1, as a scaffold would.
97
+ if (argv[0] === "add-module" || argv[0] === "upgrade") {
98
+ const args = parseAddModuleArgs(argv.slice(1));
99
+ if (args.help || args.module === undefined) {
100
+ process.stdout.write(ADD_MODULE_HELP);
101
+ process.exit(0);
102
+ }
103
+ try {
104
+ const result = await addModule({
105
+ targetDir: args.target ?? process.cwd(),
106
+ moduleName: args.module,
107
+ });
108
+ process.stdout.write(formatAddModuleReport(result, args.module));
109
+ }
110
+ catch (err) {
111
+ if (!(err instanceof AddModuleRefusedError))
112
+ throw err;
113
+ process.stderr.write(`[create-ailk] ${err.message}\n`);
114
+ process.exit(err.exitCode);
115
+ }
116
+ process.exit(0);
117
+ }
86
118
  // Subcommand dispatch, ahead of the flag parser: `sync-routes` has its own
87
119
  // argv shape (#4986). Check mode exits 1 iff a template route is missing;
88
120
  // locally modified routes are reported but do not fail; --apply exits 0.
@@ -99,7 +131,19 @@ async function main() {
99
131
  process.stdout.write(formatSyncRoutesReport(result, args.apply));
100
132
  process.exit(!args.apply && result.missing.length > 0 ? 1 : 0);
101
133
  }
102
- const opts = parseCliArgs(argv);
134
+ // `license set <key|-> [dir]` (#6683 D9): its own argv shape, ahead of the
135
+ // flag parser like `sync-routes`. The key reaches `vercel` on stdin only.
136
+ if (argv[0] === "license" && argv[1] === "set") {
137
+ process.exit(runLicenseSet(argv.slice(2), {
138
+ cwd: process.cwd(),
139
+ stdout: (text) => process.stdout.write(text),
140
+ stderr: (text) => process.stderr.write(text),
141
+ readStdin: () => readFileSync(0, "utf8"),
142
+ }));
143
+ }
144
+ // No silent default (#6754 D2/D3): a scaffold run naming neither --shape
145
+ // nor --template asks for a shape on a terminal and is refused without one.
146
+ const opts = await resolveCliShape(parseCliArgs(argv, { interactive: process.stdin.isTTY === true }), { input: process.stdin, output: process.stdout });
103
147
  // Deprecation notice for a retired `--surfaces` id (#5563 D2). `surfaces.ts`
104
148
  // stays pure — `canonicalizeSurface` only resolves the alias — so this is
105
149
  // the one place that prints: compare the raw `--surfaces` argv token
@@ -118,7 +162,13 @@ async function main() {
118
162
  }
119
163
  }
120
164
  if (opts.help) {
121
- process.stdout.write(helpText + "\n" + SYNC_ROUTES_HELP);
165
+ process.stdout.write(helpText +
166
+ "\n" +
167
+ SYNC_ROUTES_HELP +
168
+ "\n" +
169
+ ADD_MODULE_HELP +
170
+ "\n" +
171
+ LICENSE_SET_HELP);
122
172
  process.exit(0);
123
173
  }
124
174
  if (opts.version) {
@@ -161,22 +211,16 @@ async function main() {
161
211
  // scaffold() gates + fetches any requested paid module (K1, #5089) and any
162
212
  // named site template's own bundle (#5481), then validates and copies the
163
213
  // local template — throws ValidationError or LicenseRequiredError on
164
- // rejection (caught at top level below). Neither AILK_LICENSE_KEY nor
165
- // either registry URL is read, and no network call is made, when the
166
- // effective module set names no paid module — the regression floor, which
167
- // `--template marketing-site-free` deliberately rides (record D11): its
214
+ // rejection (caught at top level below). The network invariant (#6759 D6):
215
+ // no key → no request at all; a key with no --modules → the D4 level probe
216
+ // (3-4 requests); an explicit --modules list → requests only for the paid
217
+ // modules it names, exactly as before this card. `--template
218
+ // marketing-site-free` still rides the no-key case (record D11): its
168
219
  // `requires` is all-free, so it installs from the shipped
169
220
  // `site-templates/` tree with no key and no request.
170
- const { targetDir: targetAbs, surfaces } = await scaffold({
171
- targetDir: opts.target,
172
- modules: opts.modules,
173
- surfaces: opts.surfaces,
174
- withCatalog: opts.withCatalog,
175
- // Spread rather than passed as `undefined` so a run naming no template
176
- // hands `scaffold()` exactly the option object it got before the flag
177
- // existed (spec 5481 § 3 DD3's byte-identity floor).
178
- ...(opts.template !== undefined ? { template: opts.template } : {}),
179
- });
221
+ const { targetDir: targetAbs, surfaces, starter, modules: effectiveModules, } = await scaffold(toScaffoldOptions(opts));
222
+ // The starter scaffold() resolved — from --template, or from --shape (#6754 D4).
223
+ const templateName = starter?.template ?? opts.template;
180
224
  if (opts.install) {
181
225
  // Hard-coded argv. User input only flows through `cwd`, never the command.
182
226
  const result = spawnSync("pnpm", ["install"], {
@@ -188,19 +232,28 @@ async function main() {
188
232
  throw new Error(`pnpm install failed in ${targetAbs} (exit ${result.status}). Run it manually after fixing the issue.`);
189
233
  }
190
234
  }
191
- // Next-steps message.
235
+ // Next-steps message. A licensed install (#6480) is not told it needs no
236
+ // licence key, and is not pitched the Pro tier it already owns. Reads the
237
+ // EFFECTIVE module set (#6759 D6) — result.modules already carries any
238
+ // template's requires and the key's derived level, so it stays correct for
239
+ // a keyed default scaffold, which opts.modules alone would not have shown.
240
+ const licensed = isLicensedInstall({ modules: effectiveModules });
192
241
  const lines = [
193
242
  "",
194
- opts.template !== undefined
195
- ? `Scaffolded AI Launch Kit starter from the "${opts.template}" template.`
243
+ templateName !== undefined
244
+ ? `Scaffolded AI Launch Kit starter from the "${templateName}" template.`
196
245
  : "Scaffolded AI Launch Kit starter.",
246
+ ...(starter !== undefined
247
+ ? [`Starter: shape ${starter.shape}, design ${starter.design}.`]
248
+ : []),
197
249
  "",
198
250
  ];
199
251
  // Prerequisites (#6440 AC4) — every one the scaffold enforces, and nothing
200
252
  // it does not. A starter reaches `pnpm dev` AND `pnpm build` on a toolchain
201
253
  // alone: the Prisma client is generated by `database`'s own `postinstall`,
202
- // so there is no generator step to remember, and no database, licence key or
203
- // vendor API key gates either command. Saying so is the point — the previous
254
+ // so there is no generator step to remember, and no database or vendor API
255
+ // key gates either command (a free install also says no licence key does;
256
+ // a licensed one, #6480, does not). Saying so is the point — the previous
204
257
  // output named no prerequisites at all, which left a customer to discover
205
258
  // the toolchain floor from a failure.
206
259
  const prereqs = readScaffoldPrerequisites(targetAbs);
@@ -210,7 +263,12 @@ async function main() {
210
263
  ].filter((part) => part !== undefined);
211
264
  if (toolchain.length > 0) {
212
265
  lines.push(`Prerequisites: ${toolchain.join(" and ")} — that is all.`);
213
- lines.push("No database, no licence key and no vendor API key are needed to run or build this site.");
266
+ lines.push(licensed ? LICENSED_PREREQUISITES_NOTE : FREE_PREREQUISITES_NOTE);
267
+ lines.push("");
268
+ }
269
+ // Only when asked for (#6683 D9) — a run without --license is unchanged.
270
+ if (opts.license !== undefined) {
271
+ lines.push("Licence key written to apps/api/.env and ailk.key (owner-only, gitignored).");
214
272
  lines.push("");
215
273
  }
216
274
  lines.push("Next steps:");
@@ -226,15 +284,17 @@ async function main() {
226
284
  lines.push("");
227
285
  lines.push(`Route surfaces: ${CORE_SURFACE_PATHS.join(", ")}` +
228
286
  (surfaces.length > 0 ? ` + ${surfaces.join(", ")}` : ""));
229
- lines.push("Add another with the `scaffold-page` skill, or re-run with --surfaces <id>.");
287
+ lines.push("Add another with the `add-page` skill, or re-run with --surfaces <id>.");
230
288
  // Only when asked for. The default scaffold's output is unchanged.
231
289
  if (opts.withCatalog) {
232
290
  lines.push("Component catalog: COMPONENT-CATALOG.md — search it by capability before building a component.");
233
291
  }
234
292
  lines.push("Reference: ADR 0007 — pnpm create ailk@latest is the canonical install path.");
235
293
  lines.push("");
236
- lines.push(proCallout());
237
- lines.push("");
294
+ if (!licensed) {
295
+ lines.push(proCallout());
296
+ lines.push("");
297
+ }
238
298
  process.stdout.write(lines.join("\n"));
239
299
  }
240
300
  main().catch((err) => {
package/dist/index.d.ts CHANGED
@@ -5,9 +5,11 @@
5
5
  */
6
6
  export { copyTemplate, type CopyTemplateOptions } from "./copy.js";
7
7
  export { parseCliArgs, parseModules, ParseError, helpText, RECOGNIZED_MODULES, type ModuleName, type CliOptions, } from "./parse-args.js";
8
- export { CATALOG_TARGET_PATH, FREE_MODULES, LicenseRequiredError, PAID_MODULES, classifyModules, formatSurfaceDrift, scaffold, scaffoldSync, syncArchitectureSync, syncCatalogSync, syncRoutesSync, type ResolvePaidModulesOptions, type ScaffoldOptions, type ScaffoldResult, type SyncArchitectureOptions, type SyncArchitectureResult, type SyncCatalogResult, type SyncRoutesSyncOptions, } from "./programmatic.js";
8
+ export { CATALOG_TARGET_PATH, FREE_MODULES, addModule, LicenseRequiredError, PAID_MODULES, classifyModules, formatSurfaceDrift, scaffold, scaffoldSync, syncArchitectureSync, syncCatalogSync, syncRoutesSync, type AddModuleOptions, type ResolvePaidModulesOptions, type ScaffoldOptions, type ScaffoldResult, type SyncArchitectureOptions, type SyncArchitectureResult, type SyncCatalogResult, type SyncRoutesSyncOptions, } from "./programmatic.js";
9
9
  export { DEFAULT_AILK_MODULE_REGISTRY_URL, FetchModuleError, fetchModule, type FetchModuleOptions, } from "./lib/fetch-module.js";
10
- export { extractModuleBundle, type ExtractModuleBundleOptions, } from "./lib/extract-module-bundle.js";
10
+ export { extractModuleBundle, type ExistingFilePolicy, type ExtractModuleBundleOptions, type InstallReportRow, } from "./lib/extract-module-bundle.js";
11
+ export { ADD_MODULE_HELP, AddModuleRefusedError, applyModuleToScaffold, formatAddModuleReport, parseAddModuleArgs, type AddModuleReportRow, type AddModuleResult, type ApplyModuleOptions, } from "./add-module.js";
12
+ export { SITE_ROSTER_PATH, readSiteRoster, siteRosterProblems, } from "./site-roster.js";
11
13
  export { SYNC_ROUTES_HELP, formatSyncRoutesReport, listTemplateRouteDirs, parseSyncRoutesArgs, syncRoutes, type RouteReport, type SyncRoutesOptions, type SyncRoutesResult, } from "./sync-routes.js";
12
14
  export { deriveArchitecture } from "./derive-architecture.js";
13
15
  export { CORE_ROUTE_DIRS, CORE_SURFACE_PATHS, NAV_CONFIG_PATH, GATEABLE_SURFACES, MODULE_SURFACES, RECOGNIZED_SURFACES, ROUTE_SINCE, SurfaceError, excludedContentPaths, excludedRoutePaths, parseSurfaces, renderNavConfig, resolveSurfaces, surfaceForRouteDir, type Surface, } from "./surfaces.js";
package/dist/index.js CHANGED
@@ -5,12 +5,17 @@
5
5
  */
6
6
  export { copyTemplate } from "./copy.js";
7
7
  export { parseCliArgs, parseModules, ParseError, helpText, RECOGNIZED_MODULES, } from "./parse-args.js";
8
- export { CATALOG_TARGET_PATH, FREE_MODULES, LicenseRequiredError, PAID_MODULES, classifyModules, formatSurfaceDrift, scaffold, scaffoldSync, syncArchitectureSync, syncCatalogSync, syncRoutesSync, } from "./programmatic.js";
8
+ export { CATALOG_TARGET_PATH, FREE_MODULES, addModule, LicenseRequiredError, PAID_MODULES, classifyModules, formatSurfaceDrift, scaffold, scaffoldSync, syncArchitectureSync, syncCatalogSync, syncRoutesSync, } from "./programmatic.js";
9
9
  // The paid-module fetch/extract primitives (K1, #5089 — Flavor 3). Exported
10
10
  // so tooling (and this repo's own evidence-run scripts) can drive the fetch
11
11
  // path directly without re-implementing its shape.
12
12
  export { DEFAULT_AILK_MODULE_REGISTRY_URL, FetchModuleError, fetchModule, } from "./lib/fetch-module.js";
13
13
  export { extractModuleBundle, } from "./lib/extract-module-bundle.js";
14
+ // `create-ailk add-module` (#6653) — apply a purchased module to an existing
15
+ // site in place. The pure core, beside `sync-routes`'s.
16
+ export { ADD_MODULE_HELP, AddModuleRefusedError, applyModuleToScaffold, formatAddModuleReport, parseAddModuleArgs, } from "./add-module.js";
17
+ // The site's page roster of record and its install-diff check (#6682).
18
+ export { SITE_ROSTER_PATH, readSiteRoster, siteRosterProblems, } from "./site-roster.js";
14
19
  // `create-ailk sync-routes` (#4986) — the template-route drift check + copy.
15
20
  // The pure module is exported so tooling can run it against any template dir.
16
21
  export { SYNC_ROUTES_HELP, formatSyncRoutesReport, listTemplateRouteDirs, parseSyncRoutesArgs, syncRoutes, } from "./sync-routes.js";