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,124 @@
1
+ /**
2
+ * Tests for the MCP server's own workspace resolution (#5876 spec D2/D4/D6/D7).
3
+ *
4
+ * Drives the REAL `initMcpPublicWorkspace()` (not a mock) so its resolution
5
+ * arms are exercised for real, over an injected `WorkspacePredicateClient`
6
+ * stub — no database, no `@working-theory/database` root (client) import,
7
+ * and no internal-module mock of `./prisma.js` either: the `not-required`
8
+ * cases prove the D6 no-op by the RETURN VALUE alone. `resolvedAdapterKind()
9
+ * !== "database"` is this function's first statement and returns before any
10
+ * `await import()` — the source itself, read at `public-workspace.ts:88-91`,
11
+ * is the proof that no lazy import can fire on those two paths; a real
12
+ * unconfigured `PrismaPg({connectionString: undefined})` wouldn't throw
13
+ * synchronously anyway, so mocking the module would prove nothing further.
14
+ */
15
+
16
+ import {
17
+ DEFAULT_ORG_ID,
18
+ DEFAULT_WORKSPACE_ID,
19
+ type WorkspacePredicateClient,
20
+ } from "@working-theory/database/tenancy";
21
+
22
+ import {
23
+ initMcpPublicWorkspace,
24
+ mcpPublicWorkspace,
25
+ resetMcpPublicWorkspace,
26
+ } from "../../src/lib/public-workspace.js";
27
+
28
+ const NON_SENTINEL_ORG_ID = "org_01930000-0000-7bcd-8000-000000000002";
29
+
30
+ function makeStub(
31
+ count: number,
32
+ found: { organizationId: string } | null,
33
+ ): WorkspacePredicateClient {
34
+ return {
35
+ workspace: {
36
+ count: jest.fn().mockResolvedValue(count),
37
+ findUnique: jest.fn().mockResolvedValue(found),
38
+ },
39
+ };
40
+ }
41
+
42
+ describe("initMcpPublicWorkspace", () => {
43
+ const previous = process.env.CONTENT_ADAPTER;
44
+
45
+ beforeEach(() => {
46
+ resetMcpPublicWorkspace();
47
+ });
48
+
49
+ afterEach(() => {
50
+ if (previous === undefined) delete process.env.CONTENT_ADAPTER;
51
+ else process.env.CONTENT_ADAPTER = previous;
52
+ resetMcpPublicWorkspace();
53
+ });
54
+
55
+ it("resolves not-required on mdx", async () => {
56
+ process.env.CONTENT_ADAPTER = "mdx";
57
+
58
+ const result = await initMcpPublicWorkspace();
59
+
60
+ expect(result).toEqual({ kind: "not-required" });
61
+ expect(mcpPublicWorkspace()).toEqual({ kind: "not-required" });
62
+ });
63
+
64
+ it("resolves not-required on projection", async () => {
65
+ process.env.CONTENT_ADAPTER = "projection";
66
+
67
+ const result = await initMcpPublicWorkspace();
68
+
69
+ expect(result).toEqual({ kind: "not-required" });
70
+ });
71
+
72
+ describe("CONTENT_ADAPTER=database", () => {
73
+ beforeEach(() => {
74
+ process.env.CONTENT_ADAPTER = "database";
75
+ });
76
+
77
+ it("resolves { kind: resolved } when the sole workspace is the well-known default", async () => {
78
+ const stub = makeStub(1, { organizationId: DEFAULT_ORG_ID });
79
+
80
+ const result = await initMcpPublicWorkspace({ prisma: stub });
81
+
82
+ expect(result).toEqual({
83
+ kind: "resolved",
84
+ workspaceId: DEFAULT_WORKSPACE_ID,
85
+ prisma: stub,
86
+ });
87
+ expect(mcpPublicWorkspace()).toEqual(result);
88
+ });
89
+
90
+ it("resolves { kind: unresolved } when zero workspaces exist", async () => {
91
+ const stub = makeStub(0, null);
92
+
93
+ const result = await initMcpPublicWorkspace({ prisma: stub });
94
+
95
+ expect(result).toEqual({ kind: "unresolved", reason: "ambiguous" });
96
+ });
97
+
98
+ it("resolves { kind: unresolved } when two or more workspaces exist", async () => {
99
+ const stub = makeStub(2, null);
100
+
101
+ const result = await initMcpPublicWorkspace({ prisma: stub });
102
+
103
+ expect(result).toEqual({ kind: "unresolved", reason: "ambiguous" });
104
+ });
105
+
106
+ it("resolves { kind: unresolved } when the sole workspace is not the well-known default", async () => {
107
+ const stub = makeStub(1, { organizationId: NON_SENTINEL_ORG_ID });
108
+
109
+ const result = await initMcpPublicWorkspace({ prisma: stub });
110
+
111
+ expect(result).toEqual({ kind: "unresolved", reason: "ambiguous" });
112
+ });
113
+ });
114
+ });
115
+
116
+ describe("mcpPublicWorkspace", () => {
117
+ afterEach(() => {
118
+ resetMcpPublicWorkspace();
119
+ });
120
+
121
+ it("returns { kind: not-required } before initMcpPublicWorkspace has ever run", () => {
122
+ expect(mcpPublicWorkspace()).toEqual({ kind: "not-required" });
123
+ });
124
+ });
@@ -13,15 +13,85 @@
13
13
  import { readFileSync } from "node:fs";
14
14
  import { resolve, join } from "node:path";
15
15
 
16
+ import {
17
+ resetResolvedContentAdapter,
18
+ type ArtifactPrismaClient,
19
+ } from "@working-theory/content-adapters";
20
+
16
21
  import {
17
22
  ailkListPages,
18
23
  listPagesInputSchema,
19
24
  listPagesOutputSchema,
20
25
  } from "../src/tools/list_pages.js";
26
+ import {
27
+ __setMcpPublicWorkspaceForTest,
28
+ resetMcpPublicWorkspace,
29
+ } from "../src/lib/public-workspace.js";
21
30
 
22
31
  // Content root for real-filesystem tests — repo-relative
23
32
  const REPO_ROOT = resolve(__dirname, "../../..");
24
33
  const CONTENT_ROOT = join(REPO_ROOT, "content");
34
+ const WORKSPACE_ID = "wksp_00000000-0000-7000-8000-000000000001";
35
+
36
+ /**
37
+ * A structural `ArtifactPrismaClient` stub (#5876 spec D11) — a published
38
+ * HomePage plus a draft-only HomePage that must never surface (AC-1's
39
+ * published-only half is a reach test — the invariant is the adapter's).
40
+ */
41
+ function makeArtifactPrismaClientStub(): ArtifactPrismaClient {
42
+ return {
43
+ artifact: {
44
+ findFirst: jest.fn(),
45
+ findMany: jest.fn().mockResolvedValue([
46
+ {
47
+ id: "artifact_published",
48
+ workspaceId: WORKSPACE_ID,
49
+ contentType: "text/page",
50
+ locale: "en",
51
+ slug: "home",
52
+ title: "Home",
53
+ publishedVersionId: "version_published",
54
+ },
55
+ {
56
+ id: "artifact_draft",
57
+ workspaceId: WORKSPACE_ID,
58
+ contentType: "text/page",
59
+ locale: "en",
60
+ slug: "draft-page",
61
+ title: "Draft Page",
62
+ publishedVersionId: null,
63
+ },
64
+ ]),
65
+ create: jest.fn(),
66
+ updateMany: jest.fn(),
67
+ deleteMany: jest.fn(),
68
+ },
69
+ artifactVersion: {
70
+ findFirst: jest.fn(),
71
+ // Only the PUBLISHED version's row is ever returned — the adapter
72
+ // filters publishedIds to non-null before this query, so a draft-only
73
+ // artifact's version id never appears in the `id: { in: [...] }` set.
74
+ findMany: jest.fn().mockResolvedValue([
75
+ {
76
+ id: "version_published",
77
+ artifactId: "artifact_published",
78
+ workspaceId: WORKSPACE_ID,
79
+ content: {
80
+ type: "HomePage",
81
+ slug: "home",
82
+ title: "Home",
83
+ description: "The homepage.",
84
+ body: "# Hello",
85
+ },
86
+ isCurrent: false,
87
+ },
88
+ ]),
89
+ create: jest.fn(),
90
+ updateMany: jest.fn(),
91
+ },
92
+ $transaction: jest.fn(),
93
+ };
94
+ }
25
95
 
26
96
  describe("listPagesInputSchema", () => {
27
97
  it("accepts empty input (type is optional)", () => {
@@ -101,6 +171,52 @@ describe("listPagesInputSchema — Zod boundary security", () => {
101
171
  });
102
172
  });
103
173
 
174
+ describe("ailkListPages — CONTENT_ADAPTER=database (#5876)", () => {
175
+ const previous = process.env.CONTENT_ADAPTER;
176
+
177
+ beforeEach(() => {
178
+ process.env.CONTENT_ADAPTER = "database";
179
+ resetResolvedContentAdapter();
180
+ });
181
+
182
+ afterEach(() => {
183
+ if (previous === undefined) delete process.env.CONTENT_ADAPTER;
184
+ else process.env.CONTENT_ADAPTER = previous;
185
+ resetMcpPublicWorkspace();
186
+ resetResolvedContentAdapter();
187
+ });
188
+
189
+ it("AC-1: lists only published pages", async () => {
190
+ const stub = makeArtifactPrismaClientStub();
191
+ __setMcpPublicWorkspaceForTest({
192
+ kind: "resolved",
193
+ workspaceId: WORKSPACE_ID,
194
+ prisma: stub,
195
+ });
196
+
197
+ const result = await ailkListPages({ type: "HomePage" });
198
+
199
+ expect(result.pages).toEqual([
200
+ expect.objectContaining({ slug: "home", type: "HomePage" }),
201
+ ]);
202
+ expect(stub.artifact.findMany).toHaveBeenCalledWith(
203
+ expect.objectContaining({
204
+ where: expect.objectContaining({ workspaceId: WORKSPACE_ID }),
205
+ }),
206
+ );
207
+ });
208
+
209
+ it("AC-2: returns { pages: [] } when the workspace is unresolved, with no query", async () => {
210
+ const stub = makeArtifactPrismaClientStub();
211
+ __setMcpPublicWorkspaceForTest({ kind: "unresolved", reason: "ambiguous" });
212
+
213
+ const result = await ailkListPages({ type: "HomePage" });
214
+
215
+ expect(result.pages).toEqual([]);
216
+ expect(stub.artifact.findMany).not.toHaveBeenCalled();
217
+ });
218
+ });
219
+
104
220
  describe("architectural guard — no console calls", () => {
105
221
  it("apps/mcp/src/tools/list_pages.ts does not call console.*", () => {
106
222
  const src = readFileSync(
@@ -18,7 +18,13 @@ import { getMcpToolRegistrations } from "@working-theory/metadata";
18
18
 
19
19
  import { createMcpServer, listRegisteredTools } from "../src/server.js";
20
20
 
21
- const PAID_MODULES = ["scheduling", "content-write", "signal-collectors"] as const;
21
+ // `content-write` left this list in #5853: its three tools (create_page /
22
+ // update_page / delete_page) were deleted with the untenanted HTTP routes they
23
+ // twinned, so the module now owns NO catalog tool at all and the
24
+ // "owns at least one" fixture check below would fail for it. The flag itself
25
+ // is unchanged — it still gates the tenant-guarded editor routes in apps/api
26
+ // (D8), which this MCP-side matrix has never covered.
27
+ const PAID_MODULES = ["scheduling", "signal-collectors"] as const;
22
28
 
23
29
  function withEnabledModules<T>(enabled: string, fn: () => T): T {
24
30
  const prior = process.env.AILK_ENABLED_MODULES;
@@ -106,9 +106,6 @@ const MCP_ONLY_TOOLS = new Set([
106
106
  "ailkSubscribeNewsletter", // subscribe_newsletter — anonymous write via MCP; no Server Action (B7)
107
107
  "ailkRequestCallback", // request_callback — anonymous write via MCP; no Server Action (B7)
108
108
  "ailkCreateProject", // create_project — agent-only project creator; no Server Action equivalent (#299)
109
- "ailkCreatePage", // create_page — agent-first Pro content write; HTTP twin is apps/api, not a Server Action (D14a #55)
110
- "ailkUpdatePage", // update_page — agent-first Pro content write; HTTP twin is apps/api, not a Server Action (D14a #55)
111
- "ailkDeletePage", // delete_page — agent-first Pro content write; HTTP twin is apps/api, not a Server Action (D14a #55)
112
109
  "ailkGetFlow", // get_flow — agent-callable flow-engine read; no web Server Action (#3956 D7)
113
110
  "ailkListAvailability", // list_availability — anonymous scheduling read via MCP; the booker section is a client island calling apps/api directly, not a Server Action (#4416)
114
111
  "ailkGetEventMeta", // get_event_meta — meta-only scheduling read via MCP; a tile-rendering page is an RSC calling apps/api via api-client, not a Server Action (#4633)
@@ -247,8 +244,14 @@ describe("MCP ↔ Server-Action parity (filesystem)", () => {
247
244
  // - ailkSubscribeNewsletter, ailkRequestCallback:
248
245
  // MCP-only anonymous-write tools (B7; in MCP_ONLY_TOOLS allow-list).
249
246
  // - ailkCreateProject: MCP-only agent project creator (#299; in MCP_ONLY_TOOLS).
250
- // - ailkCreatePage, ailkUpdatePage, ailkDeletePage: MCP-only agent-first
251
- // Pro content writes; HTTP twin is apps/api, not Server Actions (D14a #55).
247
+ // - ailkCreatePage, ailkUpdatePage, ailkDeletePage: RETIRED by #5853
248
+ // (D7). Their HTTP twins — the untenanted /v1/content writes — were
249
+ // deleted in the same PR, so keeping the tools would have left three
250
+ // MCP tools with no HTTP equivalent, a direct breach of the parity rule
251
+ // this file enforces. Their replacement is identity-BOUND
252
+ // (/v1/workspaces/:workspaceId/content), which the identity-blind
253
+ // anonymous MCP surface can never carry (D15); the authed MCP surface
254
+ // is #3591.
252
255
  // - ailkGetFlow: MCP-only agent-callable flow-engine read; HTTP twin is
253
256
  // apps/api, not a Server Action (#3956 D7) — the page fetch in
254
257
  // apps/web/app/[locale]/flows/[slug]/page.tsx is a direct api-client
@@ -257,12 +260,12 @@ describe("MCP ↔ Server-Action parity (filesystem)", () => {
257
260
  // GET /v1/schedule/availability and POST /v1/schedule/book (#4416). The
258
261
  // `booker` section is a "use client" island that fetches those routes
259
262
  // directly (the ContactForm/StepFlow pattern) — no Server Action, so
260
- // the HTTP twin is apps/api, as with the create/update/delete_page trio.
263
+ // the HTTP twin is apps/api.
261
264
  // - ailkGetEventMeta: MCP-only meta-only scheduling read, twin of
262
265
  // GET /v1/schedule/event-meta (#4633) — same posture as the other
263
266
  // scheduling tools.
264
267
  expect(actions.size).toBe(5);
265
- expect(tools.size).toBe(23);
268
+ expect(tools.size).toBe(20);
266
269
  expect(r.missingTools).toEqual([]);
267
270
  expect(r.orphanTools).toEqual([]);
268
271
  });
@@ -1,34 +1,101 @@
1
1
  /**
2
- * Tests for S15a — purchase tool disabled at OSS catalog.
2
+ * `start_purchase` visibility — the commerce add-on claim matrix (#5971).
3
3
  *
4
- * Issue #610 / spec
5
- * `docs/internal/specs/ailk-pre-oss-security-phase-3-s15a-disable-purchase-spec.md`.
4
+ * Supersedes S15a (#610), which asserted blanket absence while
5
+ * `OSS_DISABLED_TOOLS` carried the name. The gate is now the deployment's
6
+ * `commerce` licence claim, so the assertion is a MATRIX, not an absence:
6
7
  *
7
- * Acceptance criterion verified here: `start_purchase` is NOT in the
8
- * registered MCP tool catalog when the server is constructed by
9
- * `createMcpServer()`. If a future PR re-registers the tool (by removing
10
- * `'start_purchase'` from `OSS_DISABLED_TOOLS` in `apps/mcp/src/server.ts`),
11
- * this test fails RED, forcing a deliberate review of the Phase 3
12
- * re-enablement criteria in
13
- * `docs/internal/roadmaps/phase-3-readiness.md`.
8
+ * keyless (no claim) → absent from `listRegisteredTools()` and from
9
+ * `createMcpServer()._registeredTools`, and calling `ailkPurchase` BY
10
+ * NAME returns the module-disabled body with no `checkout.createSession`
11
+ * call;
12
+ * keyed (claim held) → present in both surfaces.
14
13
  *
15
- * `purchase.ts` source is preserved — only the registration is filtered.
14
+ * Both halves are driven through `__setMcpCommerceClaimForTest()`, the same
15
+ * injector idiom `lib/public-workspace.ts` established (#5876). No test
16
+ * reaches a licence server.
16
17
  */
17
18
 
19
+ import { checkout } from "@working-theory/api-client";
20
+
21
+ import {
22
+ __setMcpCommerceClaimForTest,
23
+ resetMcpCommerceClaim,
24
+ } from "../src/lib/commerce-claim.js";
18
25
  import { createMcpServer, listRegisteredTools } from "../src/server.js";
26
+ import { ailkPurchase } from "../src/tools/purchase.js";
27
+
28
+ jest.mock("@working-theory/api-client", () => ({
29
+ checkout: { createSession: jest.fn() },
30
+ }));
31
+
32
+ const createSession = checkout.createSession as jest.MockedFunction<
33
+ typeof checkout.createSession
34
+ >;
35
+
36
+ function registeredNames(): string[] {
37
+ const server = createMcpServer();
38
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- #506/#507: pre-existing test-hygiene violation
39
+ const tools = (server as any)._registeredTools as Record<string, unknown>;
40
+ return Object.keys(tools);
41
+ }
42
+
43
+ afterEach(() => {
44
+ jest.clearAllMocks();
45
+ resetMcpCommerceClaim();
46
+ });
47
+
48
+ describe("start_purchase — keyless site (no commerce claim)", () => {
49
+ beforeEach(() => {
50
+ __setMcpCommerceClaimForTest("unclaimed");
51
+ });
52
+
53
+ it("is NOT in listRegisteredTools()", () => {
54
+ expect(listRegisteredTools().map((t) => t.name)).not.toContain(
55
+ "start_purchase",
56
+ );
57
+ });
58
+
59
+ it("is NOT in createMcpServer()._registeredTools", () => {
60
+ expect(registeredNames()).not.toContain("start_purchase");
61
+ });
19
62
 
20
- describe("purchase tool — disabled at OSS catalog (S15a)", () => {
21
- it("start_purchase is NOT in listRegisteredTools()", () => {
22
- const tools = listRegisteredTools();
23
- const names = tools.map((t) => t.name);
24
- expect(names).not.toContain("start_purchase");
63
+ it("calling it by name returns the module-disabled body and creates no session", async () => {
64
+ const result = await ailkPurchase({ productCode: "pro-plan" });
65
+
66
+ expect(result).toEqual({ error: "not_found", message: "Not found" });
67
+ expect(createSession).not.toHaveBeenCalled();
68
+ });
69
+
70
+ it("the module-disabled body does not hint that a licence gate exists", async () => {
71
+ const result = await ailkPurchase({ productCode: "pro-plan" });
72
+
73
+ expect(JSON.stringify(result)).not.toMatch(/commerce|licen[cs]e|claim/i);
74
+ });
75
+ });
76
+
77
+ describe("start_purchase — keyed site (commerce claim held)", () => {
78
+ beforeEach(() => {
79
+ __setMcpCommerceClaimForTest("claimed");
80
+ });
81
+
82
+ it("IS in listRegisteredTools()", () => {
83
+ expect(listRegisteredTools().map((t) => t.name)).toContain(
84
+ "start_purchase",
85
+ );
25
86
  });
26
87
 
27
- it("start_purchase is NOT in createMcpServer()._registeredTools", () => {
28
- const server = createMcpServer();
29
- // _registeredTools is a plain object (Record<string, ...>) in SDK v1.x.
30
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- mirrors apps/mcp/__tests__/server.test.ts
31
- const tools = (server as any)._registeredTools as Record<string, unknown>;
32
- expect("start_purchase" in tools).toBe(false);
88
+ it("IS in createMcpServer()._registeredTools", () => {
89
+ expect(registeredNames()).toContain("start_purchase");
90
+ });
91
+ });
92
+
93
+ describe("fail-closed default", () => {
94
+ it("the un-initialised module state is unclaimed", () => {
95
+ // No injector call — this is the state a boot that never resolved the
96
+ // licence (or resolved it to a rejection) leaves behind.
97
+ expect(listRegisteredTools().map((t) => t.name)).not.toContain(
98
+ "start_purchase",
99
+ );
33
100
  });
34
101
  });