create-ailk 0.6.0 → 0.7.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 (248) hide show
  1. package/README.md +44 -5
  2. package/component-catalog.json +146 -21
  3. package/component-catalog.md +9 -6
  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 +124 -13
  11. package/dist/copy.d.ts +14 -0
  12. package/dist/copy.js +38 -1
  13. package/dist/index.d.ts +4 -2
  14. package/dist/index.js +6 -1
  15. package/dist/lib/extract-module-bundle.d.ts +76 -2
  16. package/dist/lib/extract-module-bundle.js +219 -8
  17. package/dist/lib/license-key-files.d.ts +15 -0
  18. package/dist/lib/license-key-files.js +102 -0
  19. package/dist/lib/module-license-gate.d.ts +6 -1
  20. package/dist/lib/module-license-gate.js +6 -1
  21. package/dist/lib/pro-callout.d.ts +16 -10
  22. package/dist/lib/pro-callout.js +27 -4
  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.0.tar.gz +0 -0
  28. package/dist/modules/commerce/0.6.1.tar.gz +0 -0
  29. package/dist/modules/docs-blog/0.6.0.tar.gz +0 -0
  30. package/dist/modules/docs-blog/0.6.1.tar.gz +0 -0
  31. package/dist/modules/marketing-site/0.6.1.tar.gz +0 -0
  32. package/dist/modules/minimalism-site/0.6.1.tar.gz +0 -0
  33. package/dist/modules/multi-tenant/0.6.0.tar.gz +0 -0
  34. package/dist/modules/multi-tenant/0.6.1.tar.gz +0 -0
  35. package/dist/modules/saas-product/0.6.1.tar.gz +0 -0
  36. package/dist/modules/scheduling/0.6.0.tar.gz +0 -0
  37. package/dist/modules/scheduling/0.6.1.tar.gz +0 -0
  38. package/dist/modules/signal-collectors/0.6.0.tar.gz +0 -0
  39. package/dist/modules/signal-collectors/0.6.1.tar.gz +0 -0
  40. package/dist/modules/simple-service/0.6.1.tar.gz +0 -0
  41. package/dist/modules/workspace/0.6.0.tar.gz +0 -0
  42. package/dist/modules/workspace/0.6.1.tar.gz +0 -0
  43. package/dist/parse-args.d.ts +7 -0
  44. package/dist/parse-args.js +23 -1
  45. package/dist/programmatic.d.ts +46 -0
  46. package/dist/programmatic.js +144 -19
  47. package/dist/site-chrome.d.ts +18 -7
  48. package/dist/site-chrome.js +32 -14
  49. package/dist/site-roster.d.ts +127 -0
  50. package/dist/site-roster.js +494 -0
  51. package/dist/surfaces.d.ts +20 -7
  52. package/dist/surfaces.js +149 -28
  53. package/dist/sync-routes.d.ts +9 -0
  54. package/dist/sync-routes.js +2 -2
  55. package/dist/template-manifest.d.ts +58 -2
  56. package/dist/template-manifest.js +196 -69
  57. package/package.json +6 -4
  58. package/site-templates/_base/content/en/BookCall/book-call.mdx +53 -0
  59. package/site-templates/_base/content/en/GlossaryList/glossary.mdx +66 -0
  60. package/site-templates/_base/content/en/NewsletterSignup/newsletter.mdx +68 -0
  61. package/site-templates/_base/content/en/Pricing/pricing.mdx +71 -62
  62. package/site-templates/_base/content/en/PrivacyPage/privacy.mdx +2 -3
  63. package/site-templates/_base/content/en/TermsPage/terms.mdx +2 -3
  64. package/site-templates/_base/content/en/UseCase/first-use-case.mdx +23 -0
  65. package/site-templates/_base/content/en/UseCase/second-use-case.mdx +23 -0
  66. package/site-templates/_base/content/en/UseCase/third-use-case.mdx +23 -0
  67. package/site-templates/designs/minimalism/DESIGN.md +162 -0
  68. package/site-templates/designs/standard/DESIGN.md +162 -0
  69. package/site-templates/marketing-site/DESIGN.md +162 -0
  70. package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +312 -57
  71. package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +209 -90
  72. package/site-templates/marketing-site/ailk-template.json +1 -0
  73. package/site-templates/marketing-site-free/DESIGN.md +162 -0
  74. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +260 -66
  75. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +294 -114
  76. package/site-templates/marketing-site-free/ailk-template.json +3 -2
  77. package/site-templates/minimalism-site/DESIGN.md +162 -0
  78. package/site-templates/minimalism-site/TEMPLATE-MANIFEST.json +246 -19
  79. package/site-templates/minimalism-site/TEMPLATE-MANIFEST.md +185 -51
  80. package/site-templates/minimalism-site/ailk-template.json +3 -2
  81. package/site-templates/minimalism-site/content/en/BookCall/book-call.mdx +53 -0
  82. package/site-templates/minimalism-site/content/en/ContactPage/contact.mdx +3 -37
  83. package/site-templates/minimalism-site/content/en/PurchasePolicyPage/purchase-policy.mdx +124 -0
  84. package/site-templates/saas-product/DESIGN.md +162 -0
  85. package/site-templates/simple-service/DESIGN.md +162 -0
  86. package/site-templates/simple-service/TEMPLATE-MANIFEST.json +135 -26
  87. package/site-templates/simple-service/TEMPLATE-MANIFEST.md +114 -59
  88. package/site-templates/simple-service/ailk-template.json +4 -3
  89. package/site-templates/simple-service/content/en/BookCall/book-call.mdx +53 -0
  90. package/site-templates/simple-service/content/en/ContactPage/contact.mdx +1 -36
  91. package/site-templates/simple-service/content/en/PurchasePolicyPage/purchase-policy.mdx +124 -0
  92. package/templates/.claude/settings.json +15 -0
  93. package/templates/.claude/skills/README.md +8 -8
  94. package/templates/.claude/skills/{scaffold-auth → add-auth}/SKILL.md +7 -7
  95. package/templates/.claude/skills/{scaffold-auth → add-auth}/references/env-vars.md +2 -2
  96. package/templates/.claude/skills/{scaffold-auth → add-auth}/references/oauth-app-setup.md +3 -3
  97. package/templates/.claude/skills/{scaffold-page → add-page}/SKILL.md +76 -62
  98. package/templates/.claude/skills/add-page/references/page-types.md +111 -0
  99. package/templates/.claude/skills/{scaffold-page → add-page}/templates/content.mdx.tmpl +4 -4
  100. package/templates/.claude/skills/{scaffold-page → add-page}/templates/route.tsx.tmpl +1 -1
  101. package/templates/.claude/skills/{audit-site → check-site}/SKILL.md +16 -17
  102. package/templates/.claude/skills/{audit-site → check-site}/references/common-errors.md +3 -3
  103. package/templates/.claude/skills/{audit-site → check-site}/references/findings-glossary.md +16 -14
  104. package/templates/.claude/skills/{audit-site → check-site}/references/severity-levels.md +1 -1
  105. package/templates/.claude/skills/fill-page/SKILL.md +180 -0
  106. package/templates/.claude/skills/fill-page/profiles/plumbing.yaml +80 -0
  107. package/templates/.claude/skills/fill-page/references/section-sources.md +41 -0
  108. package/templates/.claude/skills/{provision-config → setup-deploy}/SKILL.md +8 -8
  109. package/templates/.claude/skills/{provision-config → setup-deploy}/references/env-vars-template.md +2 -2
  110. package/templates/.claude/skills/{provision-config → setup-deploy}/references/runbook-template.md +8 -8
  111. package/templates/.claude/skills/{provision-config → setup-deploy}/references/vercel-json-template.md +1 -1
  112. package/templates/.claude/skills/upgrade-plan/SKILL.md +117 -0
  113. package/templates/.claude/skills/upgrade-plan/references/options.md +24 -0
  114. package/templates/.claude/skills/upgrade-plan/scripts/upgrade-plan.ts +527 -0
  115. package/templates/.github/workflows/ci.yml +5 -0
  116. package/templates/CLAUDE.md +10 -5
  117. package/templates/DESIGN.md +162 -0
  118. package/templates/README.md +134 -38
  119. package/templates/apps/api/.env.example +2 -1
  120. package/templates/apps/api/package.json +1 -1
  121. package/templates/apps/api/src/config/__tests__/index.test.ts +37 -2
  122. package/templates/apps/api/src/config/index.ts +19 -6
  123. package/templates/apps/api/src/lib/license-key-store.ts +45 -0
  124. package/templates/apps/api/src/lib/site.ts +5 -3
  125. package/templates/apps/api/src/middleware/__tests__/auth.database-not-configured.test.ts +97 -0
  126. package/templates/apps/api/src/middleware/auth.ts +9 -0
  127. package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +127 -5
  128. package/templates/apps/api/src/routes/content/get-page.ts +86 -2
  129. package/templates/apps/api/src/server.ts +6 -0
  130. package/templates/apps/mcp/__tests__/audit-single-page.test.ts +75 -1
  131. package/templates/apps/mcp/__tests__/lib/audit-single-page-readiness-scan.test.ts +57 -9
  132. package/templates/apps/mcp/package.json +2 -1
  133. package/templates/apps/mcp/src/index.ts +7 -0
  134. package/templates/apps/mcp/src/lib/audit-single-page.ts +36 -10
  135. package/templates/apps/mcp/src/lib/license-key-store.ts +45 -0
  136. package/templates/apps/web/PRODUCT.md +86 -0
  137. package/templates/apps/web/__tests__/setup.ts +18 -0
  138. package/templates/apps/web/app/[locale]/(auth)/forgot-password/forgot-password-card.tsx +10 -2
  139. package/templates/apps/web/app/[locale]/(auth)/layout.tsx +2 -2
  140. package/templates/apps/web/app/[locale]/(auth)/reset-password/reset-password-card.tsx +10 -4
  141. package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-card.tsx +22 -14
  142. package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up-card.tsx +26 -18
  143. package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +12 -4
  144. package/templates/apps/web/app/[locale]/[slug]/__tests__/page.test.tsx +35 -4
  145. package/templates/apps/web/app/[locale]/[slug]/page.tsx +49 -9
  146. package/templates/apps/web/app/[locale]/book-a-call/page.tsx +50 -0
  147. package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +3 -3
  148. package/templates/apps/web/app/[locale]/case-studies/__tests__/page-renderer-migration.test.tsx +3 -3
  149. package/templates/apps/web/app/[locale]/clients/__tests__/page-renderer-migration.test.tsx +3 -4
  150. package/templates/apps/web/app/[locale]/contact/ContactForm.tsx +94 -33
  151. package/templates/apps/web/app/[locale]/contact/ConversationalIntakeWrapper.tsx +53 -23
  152. package/templates/apps/web/app/[locale]/contact/__tests__/ContactForm.test.tsx +96 -0
  153. package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +84 -2
  154. package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +5 -6
  155. package/templates/apps/web/app/[locale]/contact/page.tsx +25 -7
  156. package/templates/apps/web/app/[locale]/courses/__tests__/page-renderer-migration.test.tsx +3 -3
  157. package/templates/apps/web/app/[locale]/downloads/__tests__/page-renderer-migration.test.tsx +3 -4
  158. package/templates/apps/web/app/[locale]/events/__tests__/page-renderer-migration.test.tsx +4 -5
  159. package/templates/apps/web/app/[locale]/glossary/page.tsx +50 -0
  160. package/templates/apps/web/app/[locale]/how-to/__tests__/page-renderer-migration.test.tsx +1 -1
  161. package/templates/apps/web/app/[locale]/how-to/page.tsx +25 -15
  162. package/templates/apps/web/app/[locale]/integrations/__tests__/page-renderer-migration.test.tsx +3 -3
  163. package/templates/apps/web/app/[locale]/layout.tsx +113 -51
  164. package/templates/apps/web/app/[locale]/newsletter/page.tsx +50 -0
  165. package/templates/apps/web/app/[locale]/partners/__tests__/page-renderer-migration.test.tsx +3 -3
  166. package/templates/apps/web/app/[locale]/products/__tests__/page-renderer-migration.test.tsx +3 -3
  167. package/templates/apps/web/app/[locale]/projects/__tests__/page-renderer-migration.test.tsx +4 -5
  168. package/templates/apps/web/app/[locale]/{shipping → purchase-policy}/page.tsx +15 -10
  169. package/templates/apps/web/app/[locale]/resources/__tests__/page-renderer-migration.test.tsx +4 -5
  170. package/templates/apps/web/app/[locale]/services/__tests__/page-renderer-migration.test.tsx +3 -3
  171. package/templates/apps/web/app/[locale]/software/[slug]/page.tsx +14 -1
  172. package/templates/apps/web/app/[locale]/software/__tests__/page-renderer-migration.test.tsx +1 -1
  173. package/templates/apps/web/app/[locale]/software/page.tsx +25 -15
  174. package/templates/apps/web/app/[locale]/tech-articles/__tests__/page-renderer-migration.test.tsx +1 -1
  175. package/templates/apps/web/app/[locale]/tech-articles/page.tsx +25 -15
  176. package/templates/apps/web/app/[locale]/testimonials/__tests__/page-renderer-migration.test.tsx +3 -4
  177. package/templates/apps/web/app/[locale]/use-cases/[slug]/page.tsx +48 -0
  178. package/templates/apps/web/app/[locale]/use-cases/__tests__/page-renderer-migration.test.tsx +3 -4
  179. package/templates/apps/web/app/[locale]/use-cases/page.tsx +15 -2
  180. package/templates/apps/web/app/[locale]/whitepapers/__tests__/page-renderer-migration.test.tsx +1 -1
  181. package/templates/apps/web/app/[locale]/whitepapers/page.tsx +25 -15
  182. package/templates/apps/web/app/error-fallback.tsx +17 -45
  183. package/templates/apps/web/app/not-found.tsx +14 -38
  184. package/templates/apps/web/app/og/route.tsx +6 -0
  185. package/templates/apps/web/eslint.config.mjs +3 -0
  186. package/templates/apps/web/instrumentation.ts +14 -0
  187. package/templates/apps/web/lib/__tests__/site-theme.test.ts +135 -20
  188. package/templates/apps/web/lib/__tests__/structured-data.test.tsx +38 -0
  189. package/templates/apps/web/lib/license-key-store.ts +52 -0
  190. package/templates/apps/web/lib/nav-config.ts +64 -12
  191. package/templates/apps/web/lib/prisma-for-auth.ts +9 -6
  192. package/templates/apps/web/lib/site-brand.tsx +4 -8
  193. package/templates/apps/web/lib/site-chrome.ts +3 -3
  194. package/templates/apps/web/lib/site-identity.ts +134 -0
  195. package/templates/apps/web/lib/site-theme.ts +11 -0
  196. package/templates/apps/web/messages/en.json +52 -37
  197. package/templates/apps/web/middleware.ts +1 -1
  198. package/templates/apps/web/public/android-chrome-192x192.png +0 -0
  199. package/templates/apps/web/public/android-chrome-512x512.png +0 -0
  200. package/templates/apps/web/public/apple-touch-icon.png +0 -0
  201. package/templates/apps/web/public/favicon-16x16.png +0 -0
  202. package/templates/apps/web/public/favicon-32x32.png +0 -0
  203. package/templates/apps/web/public/favicon.ico +0 -0
  204. package/templates/apps/web/public/favicon.svg +3 -3
  205. package/templates/apps/web/public/logomark.svg +6 -3
  206. package/templates/apps/web/public/logos/ai-launch-kit-light.svg +4 -0
  207. package/templates/apps/web/public/logos/ai-launch-kit.svg +4 -0
  208. package/templates/apps/web/public/logos/atomic-agent-light.svg +4 -0
  209. package/templates/apps/web/public/logos/atomic-agent.svg +4 -0
  210. package/templates/apps/web/public/logos/salessmyth-light.svg +4 -0
  211. package/templates/apps/web/public/logos/salessmyth.svg +4 -0
  212. package/templates/apps/web/public/site.webmanifest +4 -4
  213. package/templates/apps/web/public/wordmark-light.svg +4 -0
  214. package/templates/apps/web/public/wordmark.svg +3 -3
  215. package/templates/apps/web/security-headers.config.ts +5 -1
  216. package/templates/apps/web/src/lib/commerce-sections.ts +35 -3
  217. package/templates/content/_site.mdx +41 -3
  218. package/templates/content/en/AboutPage/about.mdx +150 -144
  219. package/templates/content/en/BlogList/blog.mdx +105 -0
  220. package/templates/content/en/ContactPage/contact.mdx +44 -85
  221. package/templates/content/en/FAQPage/faq.mdx +86 -57
  222. package/templates/content/en/HomePage/home.mdx +276 -217
  223. package/templates/content/en/PurchasePolicyPage/purchase-policy.mdx +182 -0
  224. package/templates/content/en/SoftwareProduct/product.mdx +175 -0
  225. package/templates/content/en/UseCasesList/use-cases.mdx +170 -0
  226. package/templates/database/CHANGELOG.md +14 -0
  227. package/templates/database/package.json +3 -2
  228. package/templates/package.json +3 -1
  229. package/templates/tools/lint-architecture.ts +315 -0
  230. package/templates/tools/tsconfig.json +14 -0
  231. package/versions.json +33 -33
  232. package/templates/.claude/skills/add-schema/SKILL.md +0 -277
  233. package/templates/.claude/skills/add-schema/references/conflict-handling.md +0 -200
  234. package/templates/.claude/skills/add-schema/references/file-finding.md +0 -123
  235. package/templates/.claude/skills/add-schema/references/schema-types.md +0 -234
  236. package/templates/.claude/skills/scaffold-page/references/page-types.md +0 -169
  237. package/templates/.claude/skills/suggest-site-pages/SKILL.md +0 -242
  238. package/templates/.claude/skills/suggest-site-pages/references/categories.md +0 -287
  239. package/templates/apps/web/app/[locale]/cancellation/page.tsx +0 -44
  240. package/templates/apps/web/app/[locale]/returns/page.tsx +0 -44
  241. package/templates/apps/web/public/logo-dark.svg +0 -21
  242. package/templates/apps/web/public/logo-light.svg +0 -21
  243. package/templates/content/en/CancellationPolicyPage/cancellation.mdx +0 -108
  244. package/templates/content/en/LandingPage/blog.mdx +0 -83
  245. package/templates/content/en/LandingPage/features.mdx +0 -103
  246. package/templates/content/en/LandingPage/how-it-works.mdx +0 -143
  247. package/templates/content/en/ReturnsPolicyPage/returns.mdx +0 -108
  248. package/templates/content/en/ShippingPolicyPage/shipping.mdx +0 -108
@@ -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";
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";
34
40
  import { helpText, parseCliArgs, parseModules } from "./parse-args.js";
35
- import { formatSurfaceDrift, scaffold, syncArchitectureSync, syncCatalogSync, syncRoutesSync, } from "./programmatic.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)), "..");
@@ -52,8 +58,63 @@ function readRawFlagValue(argv, flag) {
52
58
  }
53
59
  return undefined;
54
60
  }
61
+ /**
62
+ * The prerequisites a freshly-scaffolded starter ACTUALLY has before
63
+ * `pnpm dev` / `pnpm build` will run (#6440 AC4).
64
+ *
65
+ * Read back off the scaffold that was just written rather than hard-coded
66
+ * here, so the printed floors can never drift from the ones the scaffold's own
67
+ * `engines` / `packageManager` fields enforce. Both fields are optional in
68
+ * principle; a missing one is simply not named, since naming a prerequisite
69
+ * the scaffold does not enforce is the other half of the defect this fixes.
70
+ */
71
+ function readScaffoldPrerequisites(targetAbs) {
72
+ try {
73
+ const pkg = JSON.parse(readFileSync(resolve(targetAbs, "package.json"), "utf8"));
74
+ const node = pkg.engines?.node;
75
+ // `packageManager` is "pnpm@<version>[+<integrity>]" — the version only.
76
+ const pnpm = pkg.packageManager?.startsWith("pnpm@")
77
+ ? pkg.packageManager.slice("pnpm@".length).split("+")[0]
78
+ : undefined;
79
+ return {
80
+ ...(node !== undefined ? { node } : {}),
81
+ ...(pnpm !== undefined ? { pnpm } : {}),
82
+ };
83
+ }
84
+ catch {
85
+ // A scaffold with no readable root package.json is a bug the copy step
86
+ // would already have thrown on; next-steps is not the place to fail.
87
+ return {};
88
+ }
89
+ }
55
90
  async function main() {
56
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
+ }
57
118
  // Subcommand dispatch, ahead of the flag parser: `sync-routes` has its own
58
119
  // argv shape (#4986). Check mode exits 1 iff a template route is missing;
59
120
  // locally modified routes are reported but do not fail; --apply exits 0.
@@ -70,6 +131,16 @@ async function main() {
70
131
  process.stdout.write(formatSyncRoutesReport(result, args.apply));
71
132
  process.exit(!args.apply && result.missing.length > 0 ? 1 : 0);
72
133
  }
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
+ }
73
144
  const opts = parseCliArgs(argv);
74
145
  // Deprecation notice for a retired `--surfaces` id (#5563 D2). `surfaces.ts`
75
146
  // stays pure — `canonicalizeSurface` only resolves the alias — so this is
@@ -89,7 +160,13 @@ async function main() {
89
160
  }
90
161
  }
91
162
  if (opts.help) {
92
- process.stdout.write(helpText + "\n" + SYNC_ROUTES_HELP);
163
+ process.stdout.write(helpText +
164
+ "\n" +
165
+ SYNC_ROUTES_HELP +
166
+ "\n" +
167
+ ADD_MODULE_HELP +
168
+ "\n" +
169
+ LICENSE_SET_HELP);
93
170
  process.exit(0);
94
171
  }
95
172
  if (opts.version) {
@@ -147,6 +224,7 @@ async function main() {
147
224
  // hands `scaffold()` exactly the option object it got before the flag
148
225
  // existed (spec 5481 § 3 DD3's byte-identity floor).
149
226
  ...(opts.template !== undefined ? { template: opts.template } : {}),
227
+ ...(opts.license !== undefined ? { license: opts.license } : {}),
150
228
  });
151
229
  if (opts.install) {
152
230
  // Hard-coded argv. User input only flows through `cwd`, never the command.
@@ -159,33 +237,66 @@ async function main() {
159
237
  throw new Error(`pnpm install failed in ${targetAbs} (exit ${result.status}). Run it manually after fixing the issue.`);
160
238
  }
161
239
  }
162
- // Next-steps message.
240
+ // Next-steps message. A licensed install (#6480) is not told it needs no
241
+ // licence key, and is not pitched the Pro tier it already owns.
242
+ const licensed = isLicensedInstall({
243
+ ...(opts.template !== undefined ? { template: opts.template } : {}),
244
+ modules: opts.modules,
245
+ });
163
246
  const lines = [
164
247
  "",
165
248
  opts.template !== undefined
166
249
  ? `Scaffolded AI Launch Kit starter from the "${opts.template}" template.`
167
250
  : "Scaffolded AI Launch Kit starter.",
168
251
  "",
169
- "Next steps:",
170
- ` cd ${targetAbs}`,
171
252
  ];
172
- if (!opts.install)
173
- lines.push(" pnpm install");
174
- lines.push(" pnpm dev");
253
+ // Prerequisites (#6440 AC4) — every one the scaffold enforces, and nothing
254
+ // it does not. A starter reaches `pnpm dev` AND `pnpm build` on a toolchain
255
+ // alone: the Prisma client is generated by `database`'s own `postinstall`,
256
+ // so there is no generator step to remember, and no database or vendor API
257
+ // key gates either command (a free install also says no licence key does;
258
+ // a licensed one, #6480, does not). Saying so is the point — the previous
259
+ // output named no prerequisites at all, which left a customer to discover
260
+ // the toolchain floor from a failure.
261
+ const prereqs = readScaffoldPrerequisites(targetAbs);
262
+ const toolchain = [
263
+ prereqs.node !== undefined ? `Node.js ${prereqs.node}` : undefined,
264
+ prereqs.pnpm !== undefined ? `pnpm ${prereqs.pnpm}` : "pnpm",
265
+ ].filter((part) => part !== undefined);
266
+ if (toolchain.length > 0) {
267
+ lines.push(`Prerequisites: ${toolchain.join(" and ")} — that is all.`);
268
+ lines.push(licensed ? LICENSED_PREREQUISITES_NOTE : FREE_PREREQUISITES_NOTE);
269
+ lines.push("");
270
+ }
271
+ // Only when asked for (#6683 D9) — a run without --license is unchanged.
272
+ if (opts.license !== undefined) {
273
+ lines.push("Licence key written to apps/api/.env and ailk.key (owner-only, gitignored).");
274
+ lines.push("");
275
+ }
276
+ lines.push("Next steps:");
277
+ lines.push(` cd ${targetAbs}`);
278
+ if (!opts.install) {
279
+ lines.push(" pnpm install # also generates the Prisma client");
280
+ }
281
+ lines.push(" pnpm dev # http://localhost:3000");
282
+ lines.push(" pnpm build # production build, when you are ready to deploy");
175
283
  lines.push("");
176
284
  lines.push("Open http://localhost:3000 to see the home page.");
285
+ lines.push("Adding auth, saved flows, checkout or database-backed content later? README.md \u00a7 \"When you need a database\" has the one-command setup.");
177
286
  lines.push("");
178
287
  lines.push(`Route surfaces: ${CORE_SURFACE_PATHS.join(", ")}` +
179
288
  (surfaces.length > 0 ? ` + ${surfaces.join(", ")}` : ""));
180
- lines.push("Add another with the `scaffold-page` skill, or re-run with --surfaces <id>.");
289
+ lines.push("Add another with the `add-page` skill, or re-run with --surfaces <id>.");
181
290
  // Only when asked for. The default scaffold's output is unchanged.
182
291
  if (opts.withCatalog) {
183
292
  lines.push("Component catalog: COMPONENT-CATALOG.md — search it by capability before building a component.");
184
293
  }
185
294
  lines.push("Reference: ADR 0007 — pnpm create ailk@latest is the canonical install path.");
186
295
  lines.push("");
187
- lines.push(proCallout());
188
- lines.push("");
296
+ if (!licensed) {
297
+ lines.push(proCallout());
298
+ lines.push("");
299
+ }
189
300
  process.stdout.write(lines.join("\n"));
190
301
  }
191
302
  main().catch((err) => {
package/dist/copy.d.ts CHANGED
@@ -1,4 +1,12 @@
1
1
  import { type MarkerMap, type WorkspaceVersionMap } from "./substitute.js";
2
+ /**
3
+ * THE marker map every layer of a scaffold substitutes with (#6460) — the base
4
+ * `templates/` copy below, and (threaded through `installBundleTree`) the base
5
+ * layer and site-template trees that land on top of it. One definition, so a
6
+ * `{{MARKER}}` resolves to the same value whichever layer authored the file it
7
+ * sits in; a second copy would let the two drift silently.
8
+ */
9
+ export declare function buildScaffoldMarkers(projectName: string): MarkerMap;
2
10
  export interface CopyTemplateOptions {
3
11
  templatesDir: string;
4
12
  targetDir: string;
@@ -12,4 +20,10 @@ export interface CopyTemplateOptions {
12
20
  */
13
21
  excludePaths?: string[];
14
22
  }
23
+ /**
24
+ * Whether a scaffolded path's content is text we run markers over. Exported
25
+ * because the template/base layer (`installBundleTree`) substitutes with the
26
+ * same classifier — one definition, not two (#6460).
27
+ */
28
+ export declare function isTextFile(relativePath: string): boolean;
15
29
  export declare function copyTemplate(opts: CopyTemplateOptions): void;
package/dist/copy.js CHANGED
@@ -20,7 +20,39 @@
20
20
  */
21
21
  import { copyFileSync, mkdirSync, readFileSync, readdirSync, writeFileSync, } from "node:fs";
22
22
  import { extname, join, relative, resolve, sep } from "node:path";
23
+ import pkg from "../package.json" with { type: "json" };
23
24
  import { substituteMarkers, substituteWorkspaceVersions, } from "./substitute.js";
25
+ /**
26
+ * This package's own `package.json` `"version"` — the value the
27
+ * `{{CREATE_AILK_VERSION}}` marker is stamped with at scaffold time (#6460).
28
+ * Deliberately a static JSON import rather than `import.meta.url` +
29
+ * `fileURLToPath`, for the reason `lib/fetch-module.ts`'s own
30
+ * `CREATE_AILK_VERSION` doc records: `copy.ts` is loaded directly by
31
+ * `copy.test.ts`, and `import.meta` is a SyntaxError under ts-jest's CommonJS
32
+ * transform.
33
+ */
34
+ const CREATE_AILK_VERSION = pkg.version;
35
+ /**
36
+ * THE marker map every layer of a scaffold substitutes with (#6460) — the base
37
+ * `templates/` copy below, and (threaded through `installBundleTree`) the base
38
+ * layer and site-template trees that land on top of it. One definition, so a
39
+ * `{{MARKER}}` resolves to the same value whichever layer authored the file it
40
+ * sits in; a second copy would let the two drift silently.
41
+ */
42
+ export function buildScaffoldMarkers(projectName) {
43
+ return {
44
+ PROJECT_NAME: projectName,
45
+ COMPANY_NAME: "Your Company",
46
+ COMPANY_DOMAIN: "example.com",
47
+ // The social-profile handle the Free chrome's footer links carry
48
+ // (`https://x.com/{{COMPANY_HANDLE}}`, #5469 D5) — a placeholder the
49
+ // site owner replaces, never the kit vendor's own handle.
50
+ COMPANY_HANDLE: "yourcompany",
51
+ // Stamped into each DESIGN.md header so an installed scaffold records the
52
+ // create-ailk it was generated by (#6460).
53
+ CREATE_AILK_VERSION,
54
+ };
55
+ }
24
56
  /**
25
57
  * Whether a walked template-relative path is inside (or equal to) an excluded
26
58
  * directory. Compares `/`-normalized paths so the check holds on Windows, and
@@ -65,7 +97,12 @@ const DOTFILES_AS_TEXT = new Set([
65
97
  ".prettierrc",
66
98
  ".eslintrc",
67
99
  ]);
68
- function isTextFile(relativePath) {
100
+ /**
101
+ * Whether a scaffolded path's content is text we run markers over. Exported
102
+ * because the template/base layer (`installBundleTree`) substitutes with the
103
+ * same classifier — one definition, not two (#6460).
104
+ */
105
+ export function isTextFile(relativePath) {
69
106
  const ext = extname(relativePath);
70
107
  if (TEXT_EXTS.has(ext))
71
108
  return true;
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";