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
@@ -0,0 +1,59 @@
1
+ /**
2
+ * stripe-tax-signal — reads `commerce.stripe_tax` from project.yaml (#5949 D4).
3
+ *
4
+ * The declaration the audit's `tax-code-when-stripe-tax` rule grades against:
5
+ * "this site's Stripe account has Stripe Tax enabled, so every priced page owes
6
+ * a `stripe.taxCode`". Declarative only — nothing here calls Stripe, and no
7
+ * license claim is read.
8
+ *
9
+ * The actual YAML read + field extraction delegates to `loadCommerceConfig`
10
+ * (`@working-theory/aeo`, exported from the package root since #6012) — this
11
+ * file's own job is the directory PROBE: apps run from their own dirs in
12
+ * dev/build (`apps/mcp/`) but from repo root in some test setups, mirroring
13
+ * `apps/mcp/src/lib/locales.ts`'s own project.yaml probe (the same three
14
+ * candidates it walks; `apps/*` may not import `apps/*`, so a shared probe
15
+ * helper isn't an option either — only the parse/extract logic was the
16
+ * duplicate #6012 removes).
17
+ *
18
+ * NEVER throws. An unreadable, absent, malformed or non-boolean value all
19
+ * resolve to `undefined` — the site declares no Stripe Tax posture, and the
20
+ * rule passes saying so.
21
+ */
22
+
23
+ import { readFileSync } from "node:fs";
24
+ import { dirname, join } from "node:path";
25
+
26
+ import { loadCommerceConfig } from "@working-theory/aeo";
27
+
28
+ /**
29
+ * Probe likely locations for project.yaml. Apps run from their own dirs in
30
+ * dev/build (`apps/mcp/`) but from repo root in some test setups — the same
31
+ * three candidates `locales.ts` walks.
32
+ */
33
+ function resolveProjectYamlPath(cwd: string): string {
34
+ const candidates = [
35
+ join(cwd, "project.yaml"),
36
+ join(cwd, "..", "project.yaml"),
37
+ join(cwd, "..", "..", "project.yaml"),
38
+ ];
39
+ for (const candidate of candidates) {
40
+ try {
41
+ readFileSync(candidate, "utf8");
42
+ return candidate;
43
+ } catch {
44
+ // continue
45
+ }
46
+ }
47
+ return candidates[0] ?? join(cwd, "project.yaml");
48
+ }
49
+
50
+ /**
51
+ * `commerce.stripe_tax` at `projectRoot` (default: the process cwd), or
52
+ * `undefined` when the site declares none.
53
+ */
54
+ export function readStripeTaxSignal(
55
+ projectRoot: string = process.cwd(),
56
+ ): boolean | undefined {
57
+ const resolvedPath = resolveProjectYamlPath(projectRoot);
58
+ return loadCommerceConfig(dirname(resolvedPath)).stripeTaxEnabled;
59
+ }
@@ -13,9 +13,13 @@
13
13
  */
14
14
 
15
15
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
16
- import { getMcpToolRegistrations, OSS_DISABLED_TOOLS } from "@working-theory/metadata";
16
+ import {
17
+ getMcpToolRegistrations,
18
+ OSS_DISABLED_TOOLS,
19
+ } from "@working-theory/metadata";
17
20
 
18
21
  import { isModuleEnabled } from "./config/modules.js";
22
+ import { mcpCommerceClaim } from "./lib/commerce-claim.js";
19
23
  import { ailkAudit } from "./tools/audit.js";
20
24
  import { ailkCaptureLead } from "./tools/capture_lead.js";
21
25
  import { ailkCreateProject } from "./tools/create_project.js";
@@ -107,14 +111,6 @@ const MODULE_TOOL_ENTRIES: Record<
107
111
  specifier: "./tools/create_booking.js",
108
112
  exportName: "ailkCreateBooking",
109
113
  },
110
- // D14a Stage 1 Unit C (#55): Paid-tier content write surface. Handlers
111
- // self-gate via requirePaidTier and return canonical error bodies (no throw)
112
- // for license / validation / conflict / not-found cases — see
113
- // apps/mcp/src/lib/errors.ts. Catalog entries carry tier: "pro" so agents
114
- // see the gating before invoking via list_tools.
115
- create_page: { specifier: "./tools/create_page.js", exportName: "ailkCreatePage" },
116
- update_page: { specifier: "./tools/update_page.js", exportName: "ailkUpdatePage" },
117
- delete_page: { specifier: "./tools/delete_page.js", exportName: "ailkDeletePage" },
118
114
  // D-48 signal-collectors — the flow engine's MCP twins of GET
119
115
  // /v1/flows/:slug and POST /v1/flows/:slug/steps. Anonymous, identity-blind;
120
116
  // free-anonymous status is now module-gated rather than unconditional.
@@ -140,7 +136,10 @@ async function resolveHandler(name: string): Promise<ToolHandler> {
140
136
  if (!entry) {
141
137
  throw new Error(`MCP catalog drift: no handler for tool '${name}'`);
142
138
  }
143
- const mod = (await import(entry.specifier)) as Record<string, ToolHandler | undefined>;
139
+ const mod = (await import(entry.specifier)) as Record<
140
+ string,
141
+ ToolHandler | undefined
142
+ >;
144
143
  const fn = mod[entry.exportName];
145
144
  if (!fn) {
146
145
  throw new Error(
@@ -163,47 +162,72 @@ export interface RegisteredToolDescriptor {
163
162
 
164
163
  /**
165
164
  * Whether a catalog registration entry should register on this build/boot —
166
- * the OSS-disabled filter (static per-name set) AND the module-presence
167
- * filter (S1, #5088): a tool tagged with `module` (e.g. `create_booking`
168
- * carrying `module: "scheduling"`) registers only when
169
- * {@link isModuleEnabled} says its module is present, the same way its
170
- * `apps/api` HTTP route is module-conditional in `server.ts` there — so a
171
- * tool and its route appear or vanish together, holding API↔MCP parity.
165
+ * three filters:
166
+ *
167
+ * 1. the OSS-disabled filter (static per-name set; empty since #5971);
168
+ * 2. the module-presence filter (S1, #5088): a tool tagged with `module`
169
+ * (e.g. `create_booking` carrying `module: "scheduling"`) registers only
170
+ * when {@link isModuleEnabled} says its module is present, the same way
171
+ * its `apps/api` HTTP route is module-conditional in `server.ts` there —
172
+ * so a tool and its route appear or vanish together, holding API↔MCP
173
+ * parity;
174
+ * 3. the ADD-ON CLAIM filter (#5971): a tool tagged `addon: "commerce"`
175
+ * registers only on a deployment that positively holds the `commerce`
176
+ * licence claim. Deliberately NOT the module filter — D-64/#5796 moved
177
+ * the commerce primitives off module gating precisely so that adding
178
+ * commerce to a live site is a licence change that lands no files on its
179
+ * repo, and "keyless site" / "keyed site" name the LICENCE, not an env
180
+ * allowlist.
181
+ *
182
+ * The claim is the boot-memoised `mcpCommerceClaim()`, so this stays
183
+ * synchronous — see `lib/commerce-claim.ts`.
172
184
  */
173
- function shouldRegister(reg: { name: string; module?: string }): boolean {
185
+ function shouldRegister(reg: {
186
+ name: string;
187
+ module?: string;
188
+ addon?: "commerce";
189
+ }): boolean {
174
190
  if (OSS_DISABLED_TOOLS.has(reg.name)) return false;
175
191
  if (reg.module && !isModuleEnabled(reg.module)) return false;
192
+ if (reg.addon === "commerce" && mcpCommerceClaim() !== "claimed")
193
+ return false;
176
194
  return true;
177
195
  }
178
196
 
197
+ /**
198
+ * The {@link McpCatalogOptions} this boot's claims imply — passed to
199
+ * `getMcpToolRegistrations()` so the upstream catalog filter and
200
+ * {@link shouldRegister} agree by construction rather than by coincidence.
201
+ */
202
+ function catalogOptionsForThisBoot(): { addons: { commerce: boolean } } {
203
+ return { addons: { commerce: mcpCommerceClaim() === "claimed" } };
204
+ }
205
+
179
206
  /**
180
207
  * Returns the list of tools that {@link createMcpServer} will register, after
181
- * the OSS catalog filter ({@link OSS_DISABLED_TOOLS}) and the module-presence
182
- * filter are applied. Pure — no server instance is constructed.
208
+ * the OSS catalog filter ({@link OSS_DISABLED_TOOLS}), the module-presence
209
+ * filter and the add-on claim filter are applied. Pure — no server instance
210
+ * is constructed.
183
211
  *
184
- * Consumed by `apps/mcp/__tests__/purchase-disabled.test.ts` to assert that
185
- * disabled tools are absent from the registered catalog. If a future change
186
- * removes a name from `OSS_DISABLED_TOOLS` without satisfying the Phase 3
187
- * re-enable criteria, that test fails RED.
212
+ * Consumed by `apps/mcp/__tests__/purchase-disabled.test.ts`, which drives
213
+ * both halves of the claim matrix: `start_purchase` absent on a keyless boot,
214
+ * present on a claimed one.
188
215
  */
189
216
  export function listRegisteredTools(): RegisteredToolDescriptor[] {
190
- return getMcpToolRegistrations()
217
+ return getMcpToolRegistrations(catalogOptionsForThisBoot())
191
218
  .filter(shouldRegister)
192
219
  .map((reg) => ({ name: reg.name, description: reg.description }));
193
220
  }
194
221
 
195
222
  /**
196
223
  * Creates a configured McpServer with all AILK tools registered, less any
197
- * tool name in {@link OSS_DISABLED_TOOLS} and less any tool whose `module`
198
- * is not enabled on this build (S1, #5088; see {@link shouldRegister}). The
199
- * disabled filter is applied defensively here even though the upstream
200
- * `getMcpToolRegistrations()` already excludes them — a belt-and-braces
201
- * guard so a future regression in the metadata package can't accidentally
202
- * re-register a disabled tool.
203
- *
204
- * Re-enable criteria for each disabled tool live at
205
- * `docs/internal/roadmaps/phase-3-readiness.md`. S15a (issue #610) is the
206
- * source of the disabled-tool filter.
224
+ * tool name in {@link OSS_DISABLED_TOOLS}, less any tool whose `module` is
225
+ * not enabled on this build (S1, #5088), and less any `addon`-tagged tool
226
+ * whose claim this deployment does not hold (#5971) — see
227
+ * {@link shouldRegister}. Every filter is applied defensively here even
228
+ * though the upstream `getMcpToolRegistrations()` already excludes the same
229
+ * entries: a belt-and-braces guard so a regression in the metadata package
230
+ * can't accidentally register something this boot should not serve.
207
231
  */
208
232
  export function createMcpServer(): McpServer {
209
233
  const server = new McpServer({
@@ -211,7 +235,7 @@ export function createMcpServer(): McpServer {
211
235
  version: "0.1.0",
212
236
  });
213
237
 
214
- for (const reg of getMcpToolRegistrations()) {
238
+ for (const reg of getMcpToolRegistrations(catalogOptionsForThisBoot())) {
215
239
  if (!shouldRegister(reg)) {
216
240
  continue;
217
241
  }
@@ -26,7 +26,10 @@ import {
26
26
  entityMarkup,
27
27
  faqHowtoIncentive,
28
28
  jsonldValidity,
29
+ offerOnPricedPage,
30
+ policiesPresent,
29
31
  schemaCoverage,
32
+ taxCodeWhenStripeTax,
30
33
  type Rule,
31
34
  } from "@working-theory/aeo";
32
35
  import {
@@ -45,8 +48,21 @@ export { auditInputSchema, auditOutputSchema };
45
48
  export type { AuditInput, AuditOutput };
46
49
 
47
50
  /**
48
- * Canonical rule set — mirrors packages/aeo/src/cli/index.ts RULES array.
49
- * Imported from @working-theory/aeo public API; must not be re-implemented here.
51
+ * The MCP audit's rule set. Imported from the @working-theory/aeo public API;
52
+ * must not be re-implemented here.
53
+ *
54
+ * It is NOT identical to `packages/aeo/src/cli/index.ts`'s RULES array, and
55
+ * this comment used to claim it was. The two differ by exactly one member:
56
+ * the CLI carries `schemaCorrectness` (10 rules) and this set does not
57
+ * (9 rules). Everything else — including the two `always-*` example rules —
58
+ * is shared.
59
+ *
60
+ * The cause is mechanical, not a choice: `packages/aeo/src/index.ts` — the
61
+ * package's PUBLIC barrel — does not re-export `schemaCorrectness`, while
62
+ * `rules/index.ts` (the internal barrel the CLI imports from) exports all of
63
+ * them. A rule absent from the public barrel is unreachable from any app.
64
+ * Closing that gap would change this tool's `score` on every page, which is
65
+ * why #5949 left it alone; it is recorded as that spec's residual 1.
50
66
  */
51
67
  const RULES: Rule[] = [
52
68
  alwaysPasses,
@@ -55,6 +71,9 @@ const RULES: Rule[] = [
55
71
  jsonldValidity,
56
72
  faqHowtoIncentive,
57
73
  entityMarkup,
74
+ policiesPresent,
75
+ offerOnPricedPage,
76
+ taxCodeWhenStripeTax,
58
77
  ];
59
78
 
60
79
  /**
@@ -62,10 +81,14 @@ const RULES: Rule[] = [
62
81
  *
63
82
  * Accepts an optional `contentRoot` override for testing. When omitted,
64
83
  * resolves the content root relative to cwd (supports ESM runtime).
84
+ *
85
+ * `projectRoot` is the matching seam for the project.yaml the readiness rules'
86
+ * Stripe-Tax signal is read from (#5949 D9); it likewise defaults to cwd.
65
87
  */
66
88
  export async function ailkAudit(
67
89
  input: AuditInput,
68
90
  contentRoot?: string,
91
+ projectRoot?: string,
69
92
  ): Promise<AuditOutput> {
70
93
  auditInputSchema.parse(input);
71
94
 
@@ -75,6 +98,7 @@ export async function ailkAudit(
75
98
  const auditInput = await buildSinglePageAuditInput(
76
99
  input as unknown as SinglePageInput,
77
100
  contentRoot,
101
+ projectRoot,
78
102
  );
79
103
  const report = runAudit(auditInput, RULES);
80
104
 
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * get_business_hours — returns business hours from LocalBusiness structured data.
3
3
  *
4
- * Pure read: imports the MDX adapter from @working-theory/content-adapters (ADR 0011).
4
+ * Pure read: resolves its adapter through `getMcpContentAdapter()` — the one
5
+ * resolver seam (#5805, ADR 0011), MDX by default.
5
6
  * Resolves a page by slug (default: "home") and extracts LocalBusiness schema.
6
7
  * Returns null when the page is not found or has no openingHoursSpecification.
7
8
  *
@@ -16,7 +17,6 @@
16
17
 
17
18
  import { join } from "node:path";
18
19
 
19
- import { createMdxAdapter } from "@working-theory/content-adapters";
20
20
  import {
21
21
  getBusinessHoursInputSchema,
22
22
  getBusinessHoursOutputSchema,
@@ -24,6 +24,7 @@ import {
24
24
  type GetBusinessHoursOutput,
25
25
  } from "@working-theory/validation";
26
26
 
27
+ import { getMcpContentAdapter } from "../lib/content-adapter.js";
27
28
  import { resolveLocale } from "../lib/resolve-locale.js";
28
29
 
29
30
  export { getBusinessHoursInputSchema, getBusinessHoursOutputSchema };
@@ -45,7 +46,7 @@ export async function ailkGetBusinessHours(
45
46
 
46
47
  const root = contentRoot ?? join(process.cwd(), "content");
47
48
  const locale = resolveLocale(input.locale);
48
- const adapter = createMdxAdapter({ contentRoot: root, defaultLocale: locale });
49
+ const adapter = getMcpContentAdapter({ contentRoot: root, locale });
49
50
 
50
51
  const slug = input.slug ?? DEFAULT_SLUG;
51
52
  const data = await adapter.extractStructuredData("LocalBusiness", slug, locale);
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * get_faq — returns FAQ question/answer pairs from FAQPage structured data.
3
3
  *
4
- * Pure read: imports the MDX adapter from @working-theory/content-adapters (ADR 0011).
4
+ * Pure read: resolves its adapter through `getMcpContentAdapter()` — the one
5
+ * resolver seam (#5805, ADR 0011), MDX by default.
5
6
  * Resolves a page by slug (default: "faq") and extracts FAQPage schema.
6
7
  * Returns null when the page is not found or has no mainEntity questions.
7
8
  *
@@ -16,7 +17,6 @@
16
17
 
17
18
  import { join } from "node:path";
18
19
 
19
- import { createMdxAdapter } from "@working-theory/content-adapters";
20
20
  import {
21
21
  getFaqInputSchema,
22
22
  getFaqOutputSchema,
@@ -24,6 +24,7 @@ import {
24
24
  type GetFaqOutput,
25
25
  } from "@working-theory/validation";
26
26
 
27
+ import { getMcpContentAdapter } from "../lib/content-adapter.js";
27
28
  import { resolveLocale } from "../lib/resolve-locale.js";
28
29
 
29
30
  export { getFaqInputSchema, getFaqOutputSchema };
@@ -45,7 +46,7 @@ export async function ailkGetFaq(
45
46
 
46
47
  const root = contentRoot ?? join(process.cwd(), "content");
47
48
  const locale = resolveLocale(input.locale);
48
- const adapter = createMdxAdapter({ contentRoot: root, defaultLocale: locale });
49
+ const adapter = getMcpContentAdapter({ contentRoot: root, locale });
49
50
 
50
51
  const slug = input.slug ?? DEFAULT_SLUG;
51
52
  const data = await adapter.extractStructuredData("FAQPage", slug, locale);
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * get_locations — returns all Place locations from structured data site-wide.
3
3
  *
4
- * Pure read: imports the MDX adapter from @working-theory/content-adapters (ADR 0011).
4
+ * Pure read: resolves its adapter through `getMcpContentAdapter()` — the one
5
+ * resolver seam (#5805, ADR 0011), MDX by default.
5
6
  * Lists all pages of type "Place" and extracts Place schema from each.
6
7
  * Returns an empty array when no Place pages exist.
7
8
  *
@@ -16,7 +17,7 @@
16
17
 
17
18
  import { join } from "node:path";
18
19
 
19
- import { createMdxAdapter, SCHEMA_ORG_TYPES } from "@working-theory/content-adapters";
20
+ import { SCHEMA_ORG_TYPES } from "@working-theory/content-adapters";
20
21
  import {
21
22
  getLocationsInputSchema,
22
23
  getLocationsOutputSchema,
@@ -24,6 +25,7 @@ import {
24
25
  type GetLocationsOutput,
25
26
  } from "@working-theory/validation";
26
27
 
28
+ import { getMcpContentAdapter } from "../lib/content-adapter.js";
27
29
  import { resolveLocale } from "../lib/resolve-locale.js";
28
30
 
29
31
  export { getLocationsInputSchema, getLocationsOutputSchema };
@@ -43,7 +45,7 @@ export async function ailkGetLocations(
43
45
 
44
46
  const root = contentRoot ?? join(process.cwd(), "content");
45
47
  const locale = resolveLocale(input.locale);
46
- const adapter = createMdxAdapter({ contentRoot: root, defaultLocale: locale });
48
+ const adapter = getMcpContentAdapter({ contentRoot: root, locale });
47
49
 
48
50
  // Place is not a page-level SchemaOrgType, so we iterate all types to find
49
51
  // pages whose schema field has Place structured data.
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * get_offerings — returns Services and/or Products as a unified offerings array.
3
3
  *
4
- * Pure read: imports the MDX adapter from @working-theory/content-adapters (ADR 0011).
4
+ * Pure read: resolves its adapter through `getMcpContentAdapter()` — the one
5
+ * resolver seam (#5805, ADR 0011), MDX by default.
5
6
  * Lists pages of type "Service" and/or "Product" and shapes them into a
6
7
  * unified offerings array. Optionally filtered by type.
7
8
  *
@@ -9,14 +10,20 @@
9
10
  * project's allowlist; invalid or unknown values silently fall back to the
10
11
  * default locale (AC-i18n-07). Lists are locale-scoped.
11
12
  *
13
+ * The offering fields (#5838) — `provider`, `categories`, `price` and
14
+ * `priceCurrency` — come off the page's own frontmatter, the same shape the
15
+ * listing card renders from. `price` and `priceCurrency` are `null` (never a
16
+ * fabricated zero) for a listing that declares no price, and `categories` is
17
+ * `[]` for one that is untagged.
18
+ *
12
19
  * Input: { type?: 'service' | 'product', locale?: string }
13
- * Output: { offerings: Array<{ name, description, category?, image? }> }
20
+ * Output: { offerings: Array<{ name, description, category?, image?, provider, categories, price, priceCurrency }> }
14
21
  * Session/scope: none — read-only public content.
15
22
  */
16
23
 
17
24
  import { join } from "node:path";
18
25
 
19
- import { createMdxAdapter, type SchemaOrgType } from "@working-theory/content-adapters";
26
+ import { type SchemaOrgType } from "@working-theory/content-adapters";
20
27
  import {
21
28
  getOfferingsInputSchema,
22
29
  getOfferingsOutputSchema,
@@ -24,6 +31,7 @@ import {
24
31
  type GetOfferingsOutput,
25
32
  } from "@working-theory/validation";
26
33
 
34
+ import { getMcpContentAdapter } from "../lib/content-adapter.js";
27
35
  import { resolveLocale } from "../lib/resolve-locale.js";
28
36
 
29
37
  export { getOfferingsInputSchema, getOfferingsOutputSchema };
@@ -43,7 +51,7 @@ export async function ailkGetOfferings(
43
51
 
44
52
  const root = contentRoot ?? join(process.cwd(), "content");
45
53
  const locale = resolveLocale(input.locale);
46
- const adapter = createMdxAdapter({ contentRoot: root, defaultLocale: locale });
54
+ const adapter = getMcpContentAdapter({ contentRoot: root, locale });
47
55
 
48
56
  const typesToFetch: SchemaOrgType[] = [];
49
57
  if (!input.type || input.type === "service") typesToFetch.push("Service");
@@ -60,6 +68,10 @@ export async function ailkGetOfferings(
60
68
  description: page.description,
61
69
  category: page.schema?.["category"] as string | undefined,
62
70
  image: page.image,
71
+ provider: page.provider ?? null,
72
+ categories: page.categories ?? [],
73
+ price: page.price ?? null,
74
+ priceCurrency: page.priceCurrency ?? null,
63
75
  }));
64
76
 
65
77
  return getOfferingsOutputSchema.parse({ offerings });
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * get_pricing — returns pricing tiers from Offer structured data.
3
3
  *
4
- * Pure read: imports the MDX adapter from @working-theory/content-adapters (ADR 0011).
4
+ * Pure read: resolves its adapter through `getMcpContentAdapter()` — the one
5
+ * resolver seam (#5805, ADR 0011), MDX by default.
5
6
  * Resolves a page by slug (default: "pricing") and extracts Offer schema.
6
7
  * Returns null when the page is not found or has no offer data.
7
8
  *
@@ -9,6 +10,12 @@
9
10
  * project's allowlist; invalid or unknown values silently fall back to the
10
11
  * default locale (AC-i18n-07).
11
12
  *
13
+ * Fallback (#5838): when the page authors no `offers` in its `schema`, the
14
+ * offering fields on its own frontmatter answer instead — a listing that
15
+ * declares `price` + `priceCurrency` yields one tier named for the page. A
16
+ * listing with neither an authored offer nor a price still returns `null`: no
17
+ * price means no tier, never a fabricated zero.
18
+ *
12
19
  * Input: { slug?: string, locale?: string }
13
20
  * Output: { tiers: Array<{ name, price, priceCurrency, description? }> } | null
14
21
  * Session/scope: none — read-only public content.
@@ -16,7 +23,6 @@
16
23
 
17
24
  import { join } from "node:path";
18
25
 
19
- import { createMdxAdapter } from "@working-theory/content-adapters";
20
26
  import {
21
27
  getPricingInputSchema,
22
28
  getPricingOutputSchema,
@@ -24,6 +30,7 @@ import {
24
30
  type GetPricingOutput,
25
31
  } from "@working-theory/validation";
26
32
 
33
+ import { getMcpContentAdapter } from "../lib/content-adapter.js";
27
34
  import { resolveLocale } from "../lib/resolve-locale.js";
28
35
 
29
36
  export { getPricingInputSchema, getPricingOutputSchema };
@@ -45,13 +52,12 @@ export async function ailkGetPricing(
45
52
 
46
53
  const root = contentRoot ?? join(process.cwd(), "content");
47
54
  const locale = resolveLocale(input.locale);
48
- const adapter = createMdxAdapter({ contentRoot: root, defaultLocale: locale });
55
+ const adapter = getMcpContentAdapter({ contentRoot: root, locale });
49
56
 
50
57
  const slug = input.slug ?? DEFAULT_SLUG;
51
58
  const data = await adapter.extractStructuredData("Product", slug, locale);
52
- if (!data) return null;
53
59
 
54
- const schema = data as Record<string, unknown>;
60
+ const schema = (data ?? {}) as Record<string, unknown>;
55
61
 
56
62
  // Support both a top-level `offers` array and a single offer object.
57
63
  const offersRaw = schema["offers"];
@@ -61,7 +67,20 @@ export async function ailkGetPricing(
61
67
  ? [offersRaw as Record<string, unknown>]
62
68
  : [];
63
69
 
64
- if (offersArr.length === 0) return null;
70
+ if (offersArr.length === 0) {
71
+ const page = await adapter.getPageBySlug("Product", slug, locale);
72
+ if (!page || page.price === undefined) return null;
73
+ return getPricingOutputSchema.parse({
74
+ tiers: [
75
+ {
76
+ name: page.title,
77
+ price: page.price,
78
+ priceCurrency: page.priceCurrency ?? "USD",
79
+ ...(page.summary !== undefined && { description: page.summary }),
80
+ },
81
+ ],
82
+ });
83
+ }
65
84
 
66
85
  const tiers = offersArr.map((offer) => ({
67
86
  name: (offer["name"] as string) ?? "",
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * get_site_info — returns structured site identity from the HomePage.
3
3
  *
4
- * Pure read: imports the MDX adapter from @working-theory/content-adapters (ADR 0011).
4
+ * Pure read: resolves its adapter through `getMcpContentAdapter()` — the one
5
+ * resolver seam (#5805, ADR 0011), MDX by default.
5
6
  * Resolves the HomePage by slug (default: "home") and extracts the `schema`
6
7
  * frontmatter field. Returns null when the page is not found or has no schema.
7
8
  *
@@ -16,7 +17,6 @@
16
17
 
17
18
  import { join } from "node:path";
18
19
 
19
- import { createMdxAdapter } from "@working-theory/content-adapters";
20
20
  import {
21
21
  getSiteInfoInputSchema,
22
22
  getSiteInfoOutputSchema,
@@ -24,6 +24,7 @@ import {
24
24
  type GetSiteInfoOutput,
25
25
  } from "@working-theory/validation";
26
26
 
27
+ import { getMcpContentAdapter } from "../lib/content-adapter.js";
27
28
  import { resolveLocale } from "../lib/resolve-locale.js";
28
29
 
29
30
  export { getSiteInfoInputSchema, getSiteInfoOutputSchema };
@@ -45,7 +46,7 @@ export async function ailkGetSiteInfo(
45
46
 
46
47
  const root = contentRoot ?? join(process.cwd(), "content");
47
48
  const locale = resolveLocale(input.locale);
48
- const adapter = createMdxAdapter({ contentRoot: root, defaultLocale: locale });
49
+ const adapter = getMcpContentAdapter({ contentRoot: root, locale });
49
50
 
50
51
  const slug = input.slug ?? DEFAULT_SLUG;
51
52
  const data = await adapter.extractStructuredData("HomePage", slug, locale);
@@ -124,10 +124,12 @@ export {
124
124
  type ScaffoldOutput,
125
125
  } from "./create_project.js";
126
126
 
127
- // The scheduling tools (list_availability, get_event_meta, create_booking),
128
- // the content-write tools (create_page, update_page, delete_page), and the
129
- // signal-collectors flow tools (get_flow, submit_flow_step) are deliberately
130
- // NOT re-exported here. All three groups are module-conditional slice files
127
+ // The scheduling tools (list_availability, get_event_meta, create_booking)
128
+ // and the signal-collectors flow tools (get_flow, submit_flow_step) are
129
+ // deliberately NOT re-exported here. (The content-write tools that used to sit
130
+ // in this list — create_page, update_page, delete_page — no longer exist at
131
+ // all: #5853 retired them with their HTTP twins.)
132
+ // Both remaining groups are module-conditional slice files
131
133
  // (#5182): a re-export from this barrel is a static path into a file the OSS
132
134
  // strip removes, which reds the public tree's type-check. They are reached
133
135
  // only through `createMcpServer`'s per-module loader in ../server.ts.
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * list_pages — returns published content pages, optionally filtered by type.
3
3
  *
4
- * Pure read: imports the MDX adapter from @working-theory/content-adapters (ADR 0011).
4
+ * Pure read: resolves its adapter through `getMcpContentAdapter()` — the one
5
+ * resolver seam (#5805, ADR 0011), MDX by default.
5
6
  * No auth, no side effects, no network. The adapter reads from the filesystem.
6
7
  *
7
8
  * Phase 3 S3: optional `locale` parameter. Validated against `readLocales()`
@@ -17,7 +18,6 @@
17
18
  import { join } from "node:path";
18
19
 
19
20
  import {
20
- createMdxAdapter,
21
21
  SCHEMA_ORG_TYPES,
22
22
  type SchemaOrgType,
23
23
  } from "@working-theory/content-adapters";
@@ -28,6 +28,7 @@ import {
28
28
  type ListPagesOutput,
29
29
  } from "@working-theory/validation";
30
30
 
31
+ import { getMcpContentAdapter } from "../lib/content-adapter.js";
31
32
  import { resolveLocale } from "../lib/resolve-locale.js";
32
33
 
33
34
  export { listPagesInputSchema, listPagesOutputSchema };
@@ -49,7 +50,7 @@ export async function ailkListPages(
49
50
  // Default: resolve content/ relative to cwd (works in ESM and jest/commonjs).
50
51
  const root = contentRoot ?? join(process.cwd(), "content");
51
52
  const locale = resolveLocale(input.locale);
52
- const adapter = createMdxAdapter({ contentRoot: root, defaultLocale: locale });
53
+ const adapter = getMcpContentAdapter({ contentRoot: root, locale });
53
54
 
54
55
  const types: readonly SchemaOrgType[] = input.type
55
56
  ? [input.type]