create-ailk 0.5.0 → 0.6.1

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 (329) hide show
  1. package/README.md +55 -8
  2. package/component-catalog.json +336 -27
  3. package/component-catalog.md +25 -12
  4. package/dist/cli.js +54 -5
  5. package/dist/copy.d.ts +14 -0
  6. package/dist/copy.js +38 -1
  7. package/dist/lib/api-boot-assertion.d.ts +46 -0
  8. package/dist/lib/api-boot-assertion.js +70 -0
  9. package/dist/lib/extract-module-bundle.d.ts +20 -1
  10. package/dist/lib/extract-module-bundle.js +64 -5
  11. package/dist/lib/import-graph.d.ts +29 -0
  12. package/dist/lib/import-graph.js +140 -0
  13. package/dist/lib/module-license-gate.d.ts +6 -1
  14. package/dist/lib/module-license-gate.js +6 -1
  15. package/dist/lib/pro-callout.js +1 -1
  16. package/dist/module-architecture.d.ts +1 -0
  17. package/dist/module-architecture.js +253 -127
  18. package/dist/modules/commerce/0.6.0.tar.gz +0 -0
  19. package/dist/modules/docs-blog/0.6.0.tar.gz +0 -0
  20. package/dist/modules/multi-tenant/0.6.0.tar.gz +0 -0
  21. package/dist/modules/scheduling/0.6.0.tar.gz +0 -0
  22. package/dist/modules/signal-collectors/0.6.0.tar.gz +0 -0
  23. package/dist/modules/workspace/0.6.0.tar.gz +0 -0
  24. package/dist/programmatic.js +14 -10
  25. package/dist/surfaces.js +105 -7
  26. package/dist/template-manifest.d.ts +33 -0
  27. package/dist/template-manifest.js +99 -16
  28. package/dist/template-names.d.ts +1 -1
  29. package/dist/template-names.js +1 -0
  30. package/package.json +3 -2
  31. package/site-templates/marketing-site/DESIGN.md +148 -0
  32. package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +22 -2
  33. package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +9 -2
  34. package/site-templates/marketing-site/ailk-template.json +1 -1
  35. package/site-templates/marketing-site-free/DESIGN.md +148 -0
  36. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +2 -2
  37. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +1 -0
  38. package/site-templates/minimalism-site/DESIGN.md +148 -0
  39. package/site-templates/minimalism-site/TEMPLATE-MANIFEST.json +10 -2
  40. package/site-templates/minimalism-site/TEMPLATE-MANIFEST.md +47 -35
  41. package/site-templates/minimalism-site/ailk-template.json +2 -1
  42. package/site-templates/minimalism-site/assets/wordmark-placeholder.svg +28 -0
  43. package/site-templates/minimalism-site/content/en/HomePage/home.mdx +49 -19
  44. package/site-templates/saas-product/DESIGN.md +148 -0
  45. package/site-templates/simple-service/DESIGN.md +148 -0
  46. package/site-templates/simple-service/TEMPLATE-MANIFEST.json +240 -0
  47. package/site-templates/simple-service/TEMPLATE-MANIFEST.md +278 -0
  48. package/site-templates/simple-service/ailk-template.json +10 -0
  49. package/site-templates/simple-service/content/en/AboutPage/about.mdx +38 -0
  50. package/site-templates/simple-service/content/en/ContactPage/contact.mdx +67 -0
  51. package/site-templates/simple-service/content/en/FAQPage/faq.mdx +56 -0
  52. package/site-templates/simple-service/content/en/HomePage/home.mdx +85 -0
  53. package/site-templates/simple-service/content/en/Service/landscape-design.mdx +40 -0
  54. package/site-templates/simple-service/content/en/Service/lawn-maintenance.mdx +40 -0
  55. package/site-templates/simple-service/content/en/Service/seasonal-cleanup.mdx +40 -0
  56. package/site-templates/simple-service/content/en/ServicesList/services.mdx +72 -0
  57. package/site-templates/simple-service/provenance.json +4 -0
  58. package/templates/.env.example +72 -7
  59. package/templates/CLAUDE.md +2 -3
  60. package/templates/DESIGN.md +155 -0
  61. package/templates/README.md +42 -8
  62. package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-31.552Z-fallback-1789916251590-037e9005.json +8 -0
  63. package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-33.840Z-fallback-1789916253842-b52659e9.json +12 -0
  64. package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-33.844Z-fallback-1789916253845-6b266f8c.json +12 -0
  65. package/templates/apps/api/.env.example +13 -0
  66. package/templates/apps/api/CLAUDE.md +26 -2
  67. package/templates/apps/api/jest.config.cjs +3 -0
  68. package/templates/apps/api/package.json +1 -0
  69. package/templates/apps/api/src/__tests__/commerce-claim-registration.test.ts +407 -0
  70. package/templates/apps/api/src/__tests__/module-exclusion.test.ts +83 -7
  71. package/templates/apps/api/src/__tests__/server.test.ts +471 -0
  72. package/templates/apps/api/src/__tests__/vercel-handler.test.ts +45 -0
  73. package/templates/apps/api/src/__tests__/workspace-module-registration.test.ts +2 -0
  74. package/templates/apps/api/src/config/index.ts +62 -0
  75. package/templates/apps/api/src/config/modules.ts +7 -3
  76. package/templates/apps/api/src/lib/__mocks__/prisma.ts +238 -2
  77. package/templates/apps/api/src/lib/__tests__/auth-email-budget.test.ts +143 -0
  78. package/templates/apps/api/src/lib/__tests__/content-adapter.test.ts +23 -0
  79. package/templates/apps/api/src/lib/__tests__/deliverable-asset-store.test.ts +125 -0
  80. package/templates/apps/api/src/lib/__tests__/error-serializer.test.ts +20 -0
  81. package/templates/apps/api/src/lib/__tests__/require-commerce-claim.test.ts +317 -0
  82. package/templates/apps/api/src/lib/__tests__/site.test.ts +238 -0
  83. package/templates/apps/api/src/lib/__tests__/stripe.test.ts +75 -0
  84. package/templates/apps/api/src/lib/auth-email-budget.ts +160 -0
  85. package/templates/apps/api/src/lib/content-adapter.ts +57 -31
  86. package/templates/apps/api/src/lib/deliverable-asset-store.ts +76 -0
  87. package/templates/apps/api/src/lib/error-serializer.ts +23 -3
  88. package/templates/apps/api/src/lib/outbound-http.ts +7 -2
  89. package/templates/apps/api/src/lib/require-commerce-claim.ts +250 -0
  90. package/templates/apps/api/src/lib/site.ts +208 -9
  91. package/templates/apps/api/src/lib/stripe.ts +66 -0
  92. package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +182 -0
  93. package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +42 -0
  94. package/templates/apps/api/src/middleware/rate-limit.ts +7 -1
  95. package/templates/apps/api/src/openapi/spec.ts +543 -2
  96. package/templates/apps/api/src/routes/auth/__tests__/auth-user.functional.test.ts +48 -0
  97. package/templates/apps/api/src/routes/auth/__tests__/transactional-email.test.ts +309 -0
  98. package/templates/apps/api/src/routes/auth/__tests__/user.test.ts +123 -0
  99. package/templates/apps/api/src/routes/auth/transactional-email.ts +273 -0
  100. package/templates/apps/api/src/routes/auth/user.ts +61 -0
  101. package/templates/apps/api/src/routes/checkout/__tests__/session.test.ts +434 -0
  102. package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +888 -0
  103. package/templates/apps/api/src/routes/checkout/index.ts +34 -0
  104. package/templates/apps/api/src/routes/checkout/session.ts +139 -0
  105. package/templates/apps/api/src/routes/checkout/sessions.ts +197 -0
  106. package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +554 -0
  107. package/templates/apps/api/src/routes/content/__tests__/list-pages.test.ts +108 -0
  108. package/templates/apps/api/src/routes/content/__tests__/search.test.ts +450 -0
  109. package/templates/apps/api/src/routes/content/get-page.ts +94 -12
  110. package/templates/apps/api/src/routes/content/index.ts +13 -73
  111. package/templates/apps/api/src/routes/content/list-pages.ts +15 -3
  112. package/templates/apps/api/src/routes/content/rate-limit.ts +15 -6
  113. package/templates/apps/api/src/routes/content/search.ts +129 -0
  114. package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +447 -31
  115. package/templates/apps/api/src/routes/leads/index.ts +100 -13
  116. package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +51 -0
  117. package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +351 -0
  118. package/templates/apps/api/src/routes/project-listings/__tests__/founder-identity.test.ts +51 -0
  119. package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +5 -2
  120. package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +4 -1
  121. package/templates/apps/api/src/routes/project-listings/__tests__/resume-email.test.ts +58 -0
  122. package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +107 -6
  123. package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +87 -2
  124. package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +51 -0
  125. package/templates/apps/api/src/routes/project-listings/start.ts +5 -1
  126. package/templates/apps/api/src/routes/requests/__tests__/confirm.test.ts +686 -0
  127. package/templates/apps/api/src/routes/requests/confirm.ts +601 -0
  128. package/templates/apps/api/src/routes/requests/index.ts +54 -0
  129. package/templates/apps/api/src/routes/webhooks/README.md +265 -0
  130. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-dunning.test.ts +749 -0
  131. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +330 -0
  132. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +262 -0
  133. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +285 -0
  134. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +213 -0
  135. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-request-paid.test.ts +301 -0
  136. package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +648 -0
  137. package/templates/apps/api/src/routes/webhooks/index.ts +89 -0
  138. package/templates/apps/api/src/routes/webhooks/stripe.ts +1399 -0
  139. package/templates/apps/api/src/server.ts +439 -49
  140. package/templates/apps/api/src/services/__tests__/abandon-email.test.ts +39 -0
  141. package/templates/apps/api/src/services/__tests__/aeo-score.test.ts +217 -40
  142. package/templates/apps/api/src/services/__tests__/audit-log.test.ts +620 -0
  143. package/templates/apps/api/src/services/__tests__/consent.test.ts +2 -0
  144. package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +300 -5
  145. package/templates/apps/api/src/services/__tests__/deliverable-resend.test.ts +20 -0
  146. package/templates/apps/api/src/services/__tests__/dunning-policy.test.ts +368 -0
  147. package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +160 -0
  148. package/templates/apps/api/src/services/__tests__/listing-config.test.ts +166 -0
  149. package/templates/apps/api/src/services/__tests__/project-listings.fence.test.ts +154 -0
  150. package/templates/apps/api/src/services/__tests__/public-site-resolver.test.ts +137 -0
  151. package/templates/apps/api/src/services/__tests__/request-confirm.test.ts +444 -0
  152. package/templates/apps/api/src/services/__tests__/revalidation.test.ts +238 -0
  153. package/templates/apps/api/src/services/__tests__/verified-email.test.ts +78 -0
  154. package/templates/apps/api/src/services/abandon-email.ts +8 -2
  155. package/templates/apps/api/src/services/aeo-score.ts +49 -12
  156. package/templates/apps/api/src/services/audit-log.ts +668 -0
  157. package/templates/apps/api/src/services/consent.ts +18 -0
  158. package/templates/apps/api/src/services/default-tenant.ts +70 -31
  159. package/templates/apps/api/src/services/deliverable-fulfillment.ts +141 -13
  160. package/templates/apps/api/src/services/dunning-policy.ts +294 -0
  161. package/templates/apps/api/src/services/entitlements.ts +80 -0
  162. package/templates/apps/api/src/services/lead-promotion.ts +13 -14
  163. package/templates/apps/api/src/services/lead-routing.ts +161 -2
  164. package/templates/apps/api/src/services/leads-inbox.ts +23 -2
  165. package/templates/apps/api/src/services/listing-config.ts +27 -9
  166. package/templates/apps/api/src/services/project-listings.ts +201 -6
  167. package/templates/apps/api/src/services/public-site-resolver.ts +106 -0
  168. package/templates/apps/api/src/services/request-confirm.ts +646 -0
  169. package/templates/apps/api/src/services/revalidation.ts +173 -0
  170. package/templates/apps/api/src/services/verified-email.ts +71 -0
  171. package/templates/apps/api/src/vercel-handler.ts +75 -35
  172. package/templates/apps/mcp/.env.example +17 -0
  173. package/templates/apps/mcp/CLAUDE.md +57 -24
  174. package/templates/apps/mcp/__tests__/aeo-cli-parity.test.ts +6 -0
  175. package/templates/apps/mcp/__tests__/audit.test.ts +137 -8
  176. package/templates/apps/mcp/__tests__/catalog-drift.test.ts +79 -36
  177. package/templates/apps/mcp/__tests__/get_offerings.test.ts +111 -2
  178. package/templates/apps/mcp/__tests__/get_pricing.test.ts +82 -2
  179. package/templates/apps/mcp/__tests__/http-transport.test.ts +541 -2
  180. package/templates/apps/mcp/__tests__/index.test.ts +39 -0
  181. package/templates/apps/mcp/__tests__/lib/audit-single-page-readiness-scan.test.ts +143 -0
  182. package/templates/apps/mcp/__tests__/lib/commerce-claim.test.ts +111 -0
  183. package/templates/apps/mcp/__tests__/lib/content-adapter-parity.test.ts +65 -0
  184. package/templates/apps/mcp/__tests__/lib/content-adapter.test.ts +188 -0
  185. package/templates/apps/mcp/__tests__/lib/fail-closed-tools.test.ts +165 -0
  186. package/templates/apps/mcp/__tests__/lib/public-workspace.test.ts +124 -0
  187. package/templates/apps/mcp/__tests__/list_pages.test.ts +116 -0
  188. package/templates/apps/mcp/__tests__/module-exclusion.test.ts +7 -1
  189. package/templates/apps/mcp/__tests__/parity.test.ts +10 -7
  190. package/templates/apps/mcp/__tests__/purchase-disabled.test.ts +89 -22
  191. package/templates/apps/mcp/__tests__/read_content.test.ts +322 -1
  192. package/templates/apps/mcp/__tests__/search_content.test.ts +105 -2
  193. package/templates/apps/mcp/__tests__/server.test.ts +67 -14
  194. package/templates/apps/mcp/__tests__/start_purchase.test.ts +252 -0
  195. package/templates/apps/mcp/__tests__/tools/purchase.test.ts +300 -123
  196. package/templates/apps/mcp/jest.config.cjs +13 -0
  197. package/templates/apps/mcp/package.json +2 -0
  198. package/templates/apps/mcp/src/abuse/index.ts +159 -24
  199. package/templates/apps/mcp/src/config/modules.ts +10 -5
  200. package/templates/apps/mcp/src/http-transport.ts +102 -29
  201. package/templates/apps/mcp/src/index.ts +73 -11
  202. package/templates/apps/mcp/src/lib/audit-single-page.ts +90 -3
  203. package/templates/apps/mcp/src/lib/commerce-claim.ts +94 -0
  204. package/templates/apps/mcp/src/lib/content-adapter.ts +113 -0
  205. package/templates/apps/mcp/src/lib/empty-content-adapter.ts +139 -0
  206. package/templates/apps/mcp/src/lib/prisma.ts +39 -0
  207. package/templates/apps/mcp/src/lib/public-workspace.ts +133 -0
  208. package/templates/apps/mcp/src/lib/stripe-tax-signal.ts +59 -0
  209. package/templates/apps/mcp/src/server.ts +59 -35
  210. package/templates/apps/mcp/src/tools/audit.ts +26 -2
  211. package/templates/apps/mcp/src/tools/get_business_hours.ts +4 -3
  212. package/templates/apps/mcp/src/tools/get_faq.ts +4 -3
  213. package/templates/apps/mcp/src/tools/get_locations.ts +5 -3
  214. package/templates/apps/mcp/src/tools/get_offerings.ts +16 -4
  215. package/templates/apps/mcp/src/tools/get_pricing.ts +25 -6
  216. package/templates/apps/mcp/src/tools/get_site_info.ts +4 -3
  217. package/templates/apps/mcp/src/tools/index.ts +6 -4
  218. package/templates/apps/mcp/src/tools/list_pages.ts +4 -3
  219. package/templates/apps/mcp/src/tools/purchase.ts +128 -45
  220. package/templates/apps/mcp/src/tools/read_content.ts +20 -6
  221. package/templates/apps/mcp/src/tools/search_content.ts +11 -4
  222. package/templates/apps/web/.env.example +71 -5
  223. package/templates/apps/web/CLAUDE.md +44 -0
  224. package/templates/apps/web/PRODUCT.md +86 -0
  225. package/templates/apps/web/app/.well-known/mcp.json/build-manifest.ts +18 -2
  226. package/templates/apps/web/app/.well-known/mcp.json/route.ts +11 -3
  227. package/templates/apps/web/app/[locale]/(auth)/__tests__/auth-pages.test.tsx +282 -0
  228. package/templates/apps/web/app/[locale]/(auth)/__tests__/password-reset.test.tsx +515 -0
  229. package/templates/apps/web/app/[locale]/(auth)/forgot-password/forgot-password-card.tsx +89 -0
  230. package/templates/apps/web/app/[locale]/(auth)/forgot-password/page.tsx +109 -0
  231. package/templates/apps/web/app/[locale]/(auth)/layout.tsx +92 -0
  232. package/templates/apps/web/app/[locale]/(auth)/reset-password/page.tsx +135 -0
  233. package/templates/apps/web/app/[locale]/(auth)/reset-password/reset-password-card.tsx +109 -0
  234. package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/__tests__/page.test.tsx +48 -2
  235. package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/page.tsx +20 -6
  236. package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-card.tsx +125 -0
  237. package/templates/apps/web/app/[locale]/(auth)/sign-up/page.tsx +96 -0
  238. package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up-card.tsx +101 -0
  239. package/templates/apps/web/app/[locale]/(auth)/use-auth-card-flow.ts +102 -0
  240. package/templates/apps/web/app/[locale]/(auth)/verify-email/page.tsx +87 -0
  241. package/templates/apps/web/app/[locale]/(authed)/__tests__/gate.test.tsx +20 -10
  242. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +84 -0
  243. package/templates/apps/web/app/[locale]/(authed)/account/actions.ts +58 -0
  244. package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +63 -8
  245. package/templates/apps/web/app/[locale]/(authed)/layout.tsx +18 -1
  246. package/templates/apps/web/app/[locale]/cancellation/page.tsx +44 -0
  247. package/templates/apps/web/app/[locale]/layout.tsx +133 -87
  248. package/templates/apps/web/app/[locale]/purchase/__tests__/cancel.test.tsx +162 -0
  249. package/templates/apps/web/app/[locale]/purchase/__tests__/module-scope.test.ts +68 -0
  250. package/templates/apps/web/app/[locale]/purchase/__tests__/success.test.tsx +361 -0
  251. package/templates/apps/web/app/[locale]/purchase/cancel/page.tsx +115 -0
  252. package/templates/apps/web/app/[locale]/purchase/success/page.tsx +307 -0
  253. package/templates/apps/web/app/[locale]/returns/page.tsx +44 -0
  254. package/templates/apps/web/app/[locale]/shipping/page.tsx +44 -0
  255. package/templates/apps/web/app/api/revalidate/__tests__/route.test.ts +670 -0
  256. package/templates/apps/web/app/api/revalidate/route.ts +194 -0
  257. package/templates/apps/web/auth.ts +407 -8
  258. package/templates/apps/web/jest.config.cjs +8 -4
  259. package/templates/apps/web/lib/__tests__/auth-email.test.ts +214 -0
  260. package/templates/apps/web/lib/__tests__/auth-routes.test.ts +74 -0
  261. package/templates/apps/web/lib/__tests__/meeting-slugs.test.ts +72 -0
  262. package/templates/apps/web/lib/__tests__/site-theme.test.ts +135 -20
  263. package/templates/apps/web/lib/__tests__/structured-data.test.tsx +18 -1
  264. package/templates/apps/web/lib/auth-email.ts +318 -0
  265. package/templates/apps/web/lib/auth-env.ts +85 -0
  266. package/templates/apps/web/lib/auth-routes.ts +55 -0
  267. package/templates/apps/web/lib/meeting-slugs.ts +53 -0
  268. package/templates/apps/web/lib/site-brand.tsx +4 -8
  269. package/templates/apps/web/lib/telemetry-scrub.ts +5 -1
  270. package/templates/apps/web/messages/en.json +187 -0
  271. package/templates/apps/web/middleware.ts +1 -1
  272. package/templates/apps/web/package.json +2 -1
  273. package/templates/apps/web/src/lib/commerce-claim.ts +33 -0
  274. package/templates/apps/web/src/lib/commerce-sections.ts +93 -0
  275. package/templates/content/_site.mdx +18 -3
  276. package/templates/content/en/CancellationPolicyPage/cancellation.mdx +108 -0
  277. package/templates/content/en/ReturnsPolicyPage/returns.mdx +108 -0
  278. package/templates/content/en/ShippingPolicyPage/shipping.mdx +108 -0
  279. package/templates/database/CHANGELOG.md +149 -0
  280. package/templates/database/README.md +10 -3
  281. package/templates/database/__tests__/appointment.db.test.ts +106 -0
  282. package/templates/database/__tests__/artifact-content-store.db.test.ts +316 -0
  283. package/templates/database/__tests__/composite-fence.db.test.ts +206 -0
  284. package/templates/database/__tests__/fence-fan-out.db.test.ts +711 -0
  285. package/templates/database/__tests__/fixtures/appointment-create-probe.ts +79 -0
  286. package/templates/database/__tests__/fixtures/project-listing-consent-probe.ts +100 -0
  287. package/templates/database/__tests__/flow-workspace-key.db.test.ts +263 -0
  288. package/templates/database/__tests__/prefixed-ids.db.test.ts +85 -9
  289. package/templates/database/__tests__/project-listing-consent-at.db.test.ts +111 -0
  290. package/templates/database/__tests__/tenancy.test.ts +141 -0
  291. package/templates/database/config/schema.prisma +79 -2
  292. package/templates/database/content/schema.prisma +87 -11
  293. package/templates/database/inbox/schema.prisma +452 -9
  294. package/templates/database/jest.config.cjs +5 -3
  295. package/templates/database/migrations/20260915120000_project_listing_solution/migration.sql +15 -0
  296. package/templates/database/migrations/20260916120000_artifact_content_store/migration.sql +158 -0
  297. package/templates/database/migrations/20260917120000_callback_request_confirm/migration.sql +40 -0
  298. package/templates/database/migrations/20260918120000_add_appointment/migration.sql +67 -0
  299. package/templates/database/migrations/20260918120000_asset_metadata_columns/migration.sql +70 -0
  300. package/templates/database/migrations/20260919120000_flow_workspace_key/migration.sql +181 -0
  301. package/templates/database/migrations/20260919130000_flow_definition_admin_columns/migration.sql +172 -0
  302. package/templates/database/migrations/20260919130000_workspace_meetings/migration.sql +41 -0
  303. package/templates/database/migrations/20260919140000_inbox_workspace_key/migration.sql +185 -0
  304. package/templates/database/migrations/20260920120000_lead_handled_at/migration.sql +70 -0
  305. package/templates/database/migrations/20260920130000_lead_workspace_source_index/migration.sql +57 -0
  306. package/templates/database/migrations/20260921120000_deliverable_grant_workspace_key/migration.sql +68 -0
  307. package/templates/database/migrations/20260922120000_tenancy_fence/migration.sql +1067 -0
  308. package/templates/database/migrations/20260923120000_listing_capturing_magnet/migration.sql +210 -0
  309. package/templates/database/package.json +3 -2
  310. package/templates/database/tenancy.ts +86 -1
  311. package/templates/database/tsconfig.json +1 -0
  312. package/versions.json +34 -34
  313. package/templates/apps/api/src/routes/content/__tests__/create.test.ts +0 -253
  314. package/templates/apps/api/src/routes/content/__tests__/delete.test.ts +0 -215
  315. package/templates/apps/api/src/routes/content/__tests__/update.test.ts +0 -266
  316. package/templates/apps/api/src/routes/content/create.ts +0 -115
  317. package/templates/apps/api/src/routes/content/delete.ts +0 -82
  318. package/templates/apps/api/src/routes/content/update.ts +0 -112
  319. package/templates/apps/mcp/__tests__/create_page.test.ts +0 -202
  320. package/templates/apps/mcp/__tests__/delete_page.test.ts +0 -177
  321. package/templates/apps/mcp/__tests__/update_page.test.ts +0 -196
  322. package/templates/apps/mcp/src/tools/create_page.ts +0 -96
  323. package/templates/apps/mcp/src/tools/delete_page.ts +0 -74
  324. package/templates/apps/mcp/src/tools/update_page.ts +0 -92
  325. package/templates/apps/web/app/[locale]/sign-in/sign-in-card.tsx +0 -123
  326. package/templates/apps/web/public/logo-dark.svg +0 -21
  327. package/templates/apps/web/public/logo-light.svg +0 -21
  328. /package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/__tests__/next-path.test.ts +0 -0
  329. /package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/next-path.ts +0 -0
@@ -16,15 +16,26 @@
16
16
  * shape the inline schema parsed pre-relocation.
17
17
  */
18
18
 
19
- import { readFileSync } from "node:fs";
19
+ import {
20
+ mkdirSync,
21
+ mkdtempSync,
22
+ readFileSync,
23
+ rmSync,
24
+ writeFileSync,
25
+ } from "node:fs";
26
+ import { tmpdir } from "node:os";
20
27
  import { resolve, join } from "node:path";
21
28
 
29
+ import { READINESS_SCAN_TYPES } from "@working-theory/aeo";
30
+
22
31
  import {
23
32
  auditInputSchema,
24
33
  auditOutputSchema,
25
34
  purchaseInputSchema,
26
35
  } from "@working-theory/validation";
27
36
 
37
+ import { buildSinglePageAuditInput } from "../src/lib/audit-single-page.js";
38
+ import { readStripeTaxSignal } from "../src/lib/stripe-tax-signal.js";
28
39
  import { ailkAudit } from "../src/tools/audit.js";
29
40
 
30
41
  const REPO_ROOT = resolve(__dirname, "../../..");
@@ -152,10 +163,7 @@ describe("ailkAudit handler — slug input", () => {
152
163
  describe("ailkAudit handler — url input (same-origin)", () => {
153
164
  it("returns audit result for a same-origin URL", async () => {
154
165
  const result = (await withBaseUrl(TEST_BASE_URL, () =>
155
- ailkAudit(
156
- { url: `${TEST_BASE_URL}/HomePage/home` },
157
- CONTENT_ROOT,
158
- ),
166
+ ailkAudit({ url: `${TEST_BASE_URL}/HomePage/home` }, CONTENT_ROOT),
159
167
  )) as Awaited<ReturnType<typeof ailkAudit>>;
160
168
  expect(typeof result.score).toBe("number");
161
169
  expect(result.perPage[0].slug).toBe("home");
@@ -204,14 +212,135 @@ describe("structural guards", () => {
204
212
  describe("purchaseInputSchema relocated to @working-theory/validation", () => {
205
213
  it("parses the same shape the inline schema parsed pre-relocation", () => {
206
214
  const result = purchaseInputSchema.safeParse({
207
- priceId: "price_test_123",
215
+ productCode: "price_test_123",
208
216
  quantity: 2,
209
217
  });
210
218
  expect(result.success).toBe(true);
211
219
  });
212
220
 
213
- it("rejects an empty priceId", () => {
214
- const result = purchaseInputSchema.safeParse({ priceId: "" });
221
+ it("rejects an empty productCode", () => {
222
+ const result = purchaseInputSchema.safeParse({ productCode: "" });
215
223
  expect(result.success).toBe(false);
216
224
  });
217
225
  });
226
+
227
+ // ── #5949 — the agent-readiness rules on the MCP audit path ───────────────────
228
+
229
+ const T1_ROOT = resolve(
230
+ __dirname,
231
+ "../../../packages/aeo/__tests__/fixtures/t1-site",
232
+ );
233
+ const T1_CONTENT_ROOT = join(T1_ROOT, "content");
234
+
235
+ const READINESS_RULE_IDS = [
236
+ "policies-present",
237
+ "offer-on-priced-page",
238
+ "tax-code-when-stripe-tax",
239
+ ];
240
+
241
+ describe("audit — readiness rules (#5949)", () => {
242
+ it("AC-1: the T1 fixture site passes all three through the MCP tool", async () => {
243
+ const result = await ailkAudit(
244
+ { slug: "widget", type: "Product" },
245
+ T1_CONTENT_ROOT,
246
+ T1_ROOT,
247
+ );
248
+ for (const id of READINESS_RULE_IDS) {
249
+ const row = result.perRule.find((r) => r.ruleId === id);
250
+ expect(row).toBeDefined();
251
+ expect(row!.status).toBe("pass");
252
+ }
253
+ });
254
+
255
+ it("the tool's rule set is 9 rows and still validates against auditOutputSchema", async () => {
256
+ const result = await ailkAudit(
257
+ { slug: "widget", type: "Product" },
258
+ T1_CONTENT_ROOT,
259
+ T1_ROOT,
260
+ );
261
+ expect(result.perRule).toHaveLength(9);
262
+ expect(auditOutputSchema.safeParse(result).success).toBe(true);
263
+ });
264
+
265
+ it("D5: the site scan is the five READINESS_SCAN_TYPES and no more", async () => {
266
+ const input = await buildSinglePageAuditInput(
267
+ { slug: "widget", type: "Product" },
268
+ T1_CONTENT_ROOT,
269
+ T1_ROOT,
270
+ );
271
+ const scanned = new Set(input.readiness!.sitePages.map((p) => p.type));
272
+ expect([...scanned].sort()).toEqual([...READINESS_SCAN_TYPES].sort());
273
+ // The fixture publishes a HomePage; it is deliberately outside the universe.
274
+ expect(scanned.has("HomePage")).toBe(false);
275
+ expect(input.readiness!.sitePages.find((p) => p.slug === "widget")).toEqual(
276
+ {
277
+ slug: "widget",
278
+ type: "Product",
279
+ price: 49,
280
+ taxCode: "txcd_99999999",
281
+ },
282
+ );
283
+ });
284
+
285
+ it("D5 fail-open-to-pass: a site the scan finds nothing in yields sitePages: [] and three passes", async () => {
286
+ const dir = mkdtempSync(join(tmpdir(), "ailk-mcp-readiness-"));
287
+ try {
288
+ mkdirSync(join(dir, "content", "en", "HomePage"), { recursive: true });
289
+ writeFileSync(
290
+ join(dir, "content", "en", "HomePage", "index.mdx"),
291
+ [
292
+ "---",
293
+ 'type: "HomePage"',
294
+ 'slug: "index"',
295
+ 'title: "Nothing for sale"',
296
+ 'description: "A site the readiness scan finds no priced page in."',
297
+ "---",
298
+ "",
299
+ "Body.",
300
+ ].join("\n"),
301
+ "utf8",
302
+ );
303
+
304
+ const input = await buildSinglePageAuditInput(
305
+ { slug: "index", type: "HomePage" },
306
+ join(dir, "content"),
307
+ dir,
308
+ );
309
+ expect(input.readiness!.sitePages).toEqual([]);
310
+
311
+ const result = await ailkAudit(
312
+ { slug: "index", type: "HomePage" },
313
+ join(dir, "content"),
314
+ dir,
315
+ );
316
+ for (const id of READINESS_RULE_IDS) {
317
+ expect(result.perRule.find((r) => r.ruleId === id)!.status).toBe(
318
+ "pass",
319
+ );
320
+ }
321
+ } finally {
322
+ rmSync(dir, { recursive: true, force: true });
323
+ }
324
+ });
325
+
326
+ it("the projectRoot seam defaults to cwd when omitted (existing call sites unchanged)", async () => {
327
+ const result = await ailkAudit(
328
+ { slug: "home", type: "HomePage" },
329
+ CONTENT_ROOT,
330
+ );
331
+ expect(result.perRule).toHaveLength(9);
332
+ expect(auditOutputSchema.safeParse(result).success).toBe(true);
333
+ });
334
+ });
335
+
336
+ describe("readStripeTaxSignal — wiring smoke test (#6012)", () => {
337
+ // Not a duplicate of packages/aeo's own loadCommerceConfig suite (which
338
+ // covers the reader's parse/extract arms in isolation, against synthetic
339
+ // tmpdirs): this proves the EXPORTED apps/mcp wrapper itself is wired
340
+ // correctly — the directory probe delegating to loadCommerceConfig — against
341
+ // a real fixture file, since #6012 deleted the rest of this describe block
342
+ // as duplicate coverage but nothing else here exercises readStripeTaxSignal.
343
+ it("reads the T1 fixture's own project.yaml — Stripe Tax on", () => {
344
+ expect(readStripeTaxSignal(T1_ROOT)).toBe(true);
345
+ });
346
+ });
@@ -9,6 +9,10 @@
9
9
  * What this asserts:
10
10
  * - Tool names registered on the McpServer == catalog tool names
11
11
  * - Tool descriptions registered on the McpServer == catalog descriptions
12
+ * - …UNDER BOTH commerce-claim states (#5971). The claim is what makes
13
+ * `start_purchase` visible, so comparing a claimed server against an
14
+ * unclaimed catalog (or vice versa) would compare unlike surfaces and
15
+ * report drift that isn't there — or, worse, miss drift that is.
12
16
  *
13
17
  * What this does NOT assert:
14
18
  * - inputSchema equality — the server registers Zod schemas; the catalog
@@ -16,8 +20,14 @@
16
20
  * (packages/validation), but distinct shapes downstream by design.
17
21
  */
18
22
 
23
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
19
24
  import { getMcpToolCatalog } from "@working-theory/metadata";
20
25
 
26
+ import {
27
+ __setMcpCommerceClaimForTest,
28
+ resetMcpCommerceClaim,
29
+ type McpCommerceClaim,
30
+ } from "../src/lib/commerce-claim.js";
21
31
  import { createMcpServer } from "../src/server.js";
22
32
 
23
33
  interface RegisteredTool {
@@ -31,43 +41,76 @@ interface RegisteredTool {
31
41
  // registers.
32
42
  const PAID_MODULES = "scheduling,content-write,signal-collectors";
33
43
 
34
- describe("MCP catalog ↔ server drift", () => {
35
- const priorEnabledModules = process.env.AILK_ENABLED_MODULES;
44
+ /**
45
+ * The SDK exposes no public API for listing a server's tools without a live
46
+ * transport, so this reads the `_registeredTools` field directly. Pinned to
47
+ * @modelcontextprotocol/sdk ^1.29.0 — if the SDK renames the field, change it
48
+ * here once rather than at each call site.
49
+ */
50
+ function registeredToolsOf(
51
+ server: McpServer,
52
+ ): Record<string, RegisteredTool> {
53
+ return (
54
+ // eslint-disable-next-line no-restricted-syntax -- #506/#507: the SDK exposes no public accessor; the double assertion is confined to this one helper
55
+ server as unknown as { _registeredTools: Record<string, RegisteredTool> }
56
+ )._registeredTools;
57
+ }
58
+
59
+ /** The catalog options a given claim state implies — the same mapping
60
+ * `server.ts` applies internally, so both surfaces are read like for like. */
61
+ function catalogOptionsFor(claim: McpCommerceClaim) {
62
+ return { addons: { commerce: claim === "claimed" } };
63
+ }
64
+
65
+ describe.each<McpCommerceClaim>(["unclaimed", "claimed"])(
66
+ "MCP catalog ↔ server drift — commerce claim %s",
67
+ (claim) => {
68
+ const priorEnabledModules = process.env.AILK_ENABLED_MODULES;
69
+
70
+ beforeEach(() => {
71
+ process.env.AILK_ENABLED_MODULES = PAID_MODULES;
72
+ __setMcpCommerceClaimForTest(claim);
73
+ });
74
+
75
+ afterEach(() => {
76
+ resetMcpCommerceClaim();
77
+ });
78
+
79
+ afterAll(() => {
80
+ if (priorEnabledModules === undefined) {
81
+ delete process.env.AILK_ENABLED_MODULES;
82
+ } else {
83
+ process.env.AILK_ENABLED_MODULES = priorEnabledModules;
84
+ }
85
+ });
36
86
 
37
- beforeEach(() => {
38
- process.env.AILK_ENABLED_MODULES = PAID_MODULES;
39
- });
87
+ it("registered tool names equal catalog tool names", () => {
88
+ const registered = registeredToolsOf(createMcpServer());
89
+ const registeredNames = Object.keys(registered).sort();
90
+ const catalogNames = getMcpToolCatalog(catalogOptionsFor(claim))
91
+ .map((t) => t.name)
92
+ .sort();
93
+ expect(registeredNames).toEqual(catalogNames);
94
+ });
40
95
 
41
- afterAll(() => {
42
- if (priorEnabledModules === undefined) {
43
- delete process.env.AILK_ENABLED_MODULES;
44
- } else {
45
- process.env.AILK_ENABLED_MODULES = priorEnabledModules;
46
- }
47
- });
96
+ it("registered tool descriptions equal catalog descriptions", () => {
97
+ const registered = registeredToolsOf(createMcpServer());
98
+ for (const entry of getMcpToolCatalog(catalogOptionsFor(claim))) {
99
+ expect(registered[entry.name]).toBeDefined();
100
+ expect(registered[entry.name]!.description).toBe(entry.description);
101
+ }
102
+ });
48
103
 
49
- it("registered tool names equal catalog tool names", () => {
50
- const server = createMcpServer();
51
- const registered = (
52
- // eslint-disable-next-line no-restricted-syntax -- #506/#507: pre-existing test-hygiene violation
53
- server as unknown as { _registeredTools: Record<string, RegisteredTool> }
54
- )._registeredTools;
55
- const registeredNames = Object.keys(registered).sort();
56
- const catalogNames = getMcpToolCatalog()
57
- .map((t) => t.name)
58
- .sort();
59
- expect(registeredNames).toEqual(catalogNames);
60
- });
104
+ it("start_purchase appears in both surfaces exactly when the claim is held", () => {
105
+ const registered = registeredToolsOf(createMcpServer());
106
+ const expected = claim === "claimed";
61
107
 
62
- it("registered tool descriptions equal catalog descriptions", () => {
63
- const server = createMcpServer();
64
- const registered = (
65
- // eslint-disable-next-line no-restricted-syntax -- #506/#507: pre-existing test-hygiene violation
66
- server as unknown as { _registeredTools: Record<string, RegisteredTool> }
67
- )._registeredTools;
68
- for (const entry of getMcpToolCatalog()) {
69
- expect(registered[entry.name]).toBeDefined();
70
- expect(registered[entry.name]!.description).toBe(entry.description);
71
- }
72
- });
73
- });
108
+ expect("start_purchase" in registered).toBe(expected);
109
+ expect(
110
+ getMcpToolCatalog(catalogOptionsFor(claim)).some(
111
+ (t) => t.name === "start_purchase",
112
+ ),
113
+ ).toBe(expected);
114
+ });
115
+ },
116
+ );
@@ -9,8 +9,15 @@
9
9
  * - Output validates against getOfferingsOutputSchema
10
10
  */
11
11
 
12
- import { readFileSync } from "node:fs";
13
- import { resolve } from "node:path";
12
+ import {
13
+ mkdirSync,
14
+ mkdtempSync,
15
+ readFileSync,
16
+ rmSync,
17
+ writeFileSync,
18
+ } from "node:fs";
19
+ import { tmpdir } from "node:os";
20
+ import { join, resolve } from "node:path";
14
21
 
15
22
  import {
16
23
  ailkGetOfferings,
@@ -88,3 +95,105 @@ describe("architectural guard — no console calls", () => {
88
95
  expect(src).not.toMatch(/\bconsole\.(log|warn|error|info|debug)\b/);
89
96
  });
90
97
  });
98
+
99
+ /**
100
+ * The offering fields on the catalog surface (#5838, spec 5465 D13/FR14).
101
+ *
102
+ * `get_offerings` is the one place an agent reads every Product and Service
103
+ * listing, so it returns the listing card's own fields — the provider, the
104
+ * categories, and the price when there is one — rather than leaving a caller to
105
+ * parse body copy for them. A listing with no price returns `null` for both
106
+ * money fields; it never fabricates a zero.
107
+ */
108
+ describe("ailkGetOfferings — offering fields (#5838)", () => {
109
+ const mkdtemp = (): string =>
110
+ mkdtempSync(join(tmpdir(), "ailk-offerings-"));
111
+
112
+ function writeFixture(root: string): void {
113
+ mkdirSync(join(root, "en", "Product"), { recursive: true });
114
+ mkdirSync(join(root, "en", "Service"), { recursive: true });
115
+ writeFileSync(
116
+ join(root, "en", "Product", "standard.mdx"),
117
+ [
118
+ "---",
119
+ "type: Product",
120
+ "slug: standard",
121
+ "title: Standard",
122
+ "description: The flagship marketing template.",
123
+ "summary: A marketing site with docs, scheduling and flows.",
124
+ "thumbnail: /templates/standard.png",
125
+ "template: marketing-site",
126
+ "provider:",
127
+ " name: SalesSmyth",
128
+ " logo: /logomark.svg",
129
+ "categories:",
130
+ " - technology",
131
+ " - legal",
132
+ "---",
133
+ "",
134
+ "Body copy.",
135
+ "",
136
+ ].join("\n"),
137
+ );
138
+ writeFileSync(
139
+ join(root, "en", "Service", "launch.mdx"),
140
+ [
141
+ "---",
142
+ "type: Service",
143
+ "slug: launch",
144
+ "title: Launch Service",
145
+ "description: A productized two-week deploy.",
146
+ "provider:",
147
+ " name: SalesSmyth",
148
+ "price: 2495",
149
+ "priceCurrency: USD",
150
+ "categories:",
151
+ " - technology",
152
+ "---",
153
+ "",
154
+ "Body copy.",
155
+ "",
156
+ ].join("\n"),
157
+ );
158
+ }
159
+
160
+ let root: string;
161
+
162
+ beforeAll(() => {
163
+ root = mkdtemp();
164
+ writeFixture(root);
165
+ });
166
+
167
+ afterAll(() => {
168
+ rmSync(root, { recursive: true, force: true });
169
+ });
170
+
171
+ it("returns provider, categories and a null price for a listing with no price", async () => {
172
+ const result = await ailkGetOfferings({ type: "product" }, root);
173
+ const standard = result.offerings.find((o) => o.name === "Standard");
174
+ expect(standard).toBeDefined();
175
+ expect(standard?.provider).toEqual({
176
+ name: "SalesSmyth",
177
+ logo: "/logomark.svg",
178
+ });
179
+ expect(standard?.categories).toEqual(["technology", "legal"]);
180
+ expect(standard?.price).toBeNull();
181
+ expect(standard?.priceCurrency).toBeNull();
182
+ });
183
+
184
+ it("returns the price and currency for a listing that carries them", async () => {
185
+ const result = await ailkGetOfferings({ type: "service" }, root);
186
+ const launch = result.offerings.find((o) => o.name === "Launch Service");
187
+ expect(launch?.price).toBe(2495);
188
+ expect(launch?.priceCurrency).toBe("USD");
189
+ expect(launch?.provider).toEqual({ name: "SalesSmyth" });
190
+ });
191
+
192
+ it("returns a null provider and an empty categories array for a page carrying neither", async () => {
193
+ const result = await ailkGetOfferings({}, CONTENT_ROOT);
194
+ for (const offering of result.offerings) {
195
+ expect(offering.provider === null || typeof offering.provider === "object").toBe(true);
196
+ expect(Array.isArray(offering.categories)).toBe(true);
197
+ }
198
+ });
199
+ });
@@ -9,8 +9,15 @@
9
9
  * - Output validates against getPricingOutputSchema
10
10
  */
11
11
 
12
- import { readFileSync } from "node:fs";
13
- import { resolve } from "node:path";
12
+ import {
13
+ mkdirSync,
14
+ mkdtempSync,
15
+ readFileSync,
16
+ rmSync,
17
+ writeFileSync,
18
+ } from "node:fs";
19
+ import { tmpdir } from "node:os";
20
+ import { join, resolve } from "node:path";
14
21
 
15
22
  import {
16
23
  ailkGetPricing,
@@ -78,3 +85,76 @@ describe("architectural guard — no console calls", () => {
78
85
  expect(src).not.toMatch(/\bconsole\.(log|warn|error|info|debug)\b/);
79
86
  });
80
87
  });
88
+
89
+ /**
90
+ * The offering-field fallback (#5838, spec 5465 D13/FR14).
91
+ *
92
+ * A listing page carries its price in frontmatter rather than hand-rolling an
93
+ * `Offer` into `schema`, so `get_pricing` reads that shape when the page
94
+ * authors no offer of its own. An authored offer still wins — this is a
95
+ * fallback, not a replacement — and a listing with no price at all still
96
+ * answers `null`.
97
+ */
98
+ describe("ailkGetPricing — offering-field fallback (#5838)", () => {
99
+ let root: string;
100
+
101
+ beforeAll(() => {
102
+ root = mkdtempSync(join(tmpdir(), "ailk-pricing-"));
103
+ mkdirSync(join(root, "en", "Product"), { recursive: true });
104
+ writeFileSync(
105
+ join(root, "en", "Product", "priced.mdx"),
106
+ [
107
+ "---",
108
+ "type: Product",
109
+ "slug: priced",
110
+ "title: Standard",
111
+ "description: The flagship marketing template.",
112
+ "summary: A marketing site with docs, scheduling and flows.",
113
+ "price: 249",
114
+ "priceCurrency: USD",
115
+ "---",
116
+ "",
117
+ "Body copy.",
118
+ "",
119
+ ].join("\n"),
120
+ );
121
+ writeFileSync(
122
+ join(root, "en", "Product", "free.mdx"),
123
+ [
124
+ "---",
125
+ "type: Product",
126
+ "slug: free",
127
+ "title: Standard (Free)",
128
+ "description: The free marketing template.",
129
+ "summary: A marketing site, free forever.",
130
+ "---",
131
+ "",
132
+ "Body copy.",
133
+ "",
134
+ ].join("\n"),
135
+ );
136
+ });
137
+
138
+ afterAll(() => {
139
+ rmSync(root, { recursive: true, force: true });
140
+ });
141
+
142
+ it("returns one tier from the page's own price and priceCurrency", async () => {
143
+ const result = await ailkGetPricing({ slug: "priced" }, root);
144
+ expect(result).toEqual({
145
+ tiers: [
146
+ {
147
+ name: "Standard",
148
+ price: 249,
149
+ priceCurrency: "USD",
150
+ description: "A marketing site with docs, scheduling and flows.",
151
+ },
152
+ ],
153
+ });
154
+ });
155
+
156
+ it("returns null for a listing that declares no price — never a fabricated zero", async () => {
157
+ const result = await ailkGetPricing({ slug: "free" }, root);
158
+ expect(result).toBeNull();
159
+ });
160
+ });