create-ailk 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (620) hide show
  1. package/README.md +124 -3
  2. package/component-catalog.md +456 -0
  3. package/dist/cli.js +92 -16
  4. package/dist/component-catalog.d.ts +11 -0
  5. package/dist/component-catalog.js +39 -0
  6. package/dist/copy.d.ts +7 -0
  7. package/dist/copy.js +24 -0
  8. package/dist/derive-architecture.d.ts +13 -0
  9. package/dist/derive-architecture.js +238 -0
  10. package/dist/index.d.ts +7 -2
  11. package/dist/index.js +19 -2
  12. package/dist/lib/extract-module-bundle.d.ts +16 -0
  13. package/dist/lib/extract-module-bundle.js +184 -0
  14. package/dist/lib/fetch-module.d.ts +55 -0
  15. package/dist/lib/fetch-module.js +125 -0
  16. package/dist/lib/module-license-gate.d.ts +48 -0
  17. package/dist/lib/module-license-gate.js +67 -0
  18. package/dist/lib/module-tier.d.ts +35 -0
  19. package/dist/lib/module-tier.js +37 -0
  20. package/dist/module-architecture.d.ts +175 -0
  21. package/dist/module-architecture.js +663 -0
  22. package/dist/parse-args.d.ts +47 -1
  23. package/dist/parse-args.js +143 -2
  24. package/dist/programmatic.d.ts +160 -3
  25. package/dist/programmatic.js +196 -7
  26. package/dist/surfaces.d.ts +211 -0
  27. package/dist/surfaces.js +532 -0
  28. package/dist/sync-routes.d.ts +92 -0
  29. package/dist/sync-routes.js +350 -0
  30. package/package.json +12 -5
  31. package/templates/.claude/rules/architecture.md +6 -2
  32. package/templates/.claude/skills/README.md +9 -9
  33. package/templates/.claude/skills/add-schema/SKILL.md +14 -7
  34. package/templates/.claude/skills/add-schema/references/schema-types.md +109 -6
  35. package/templates/.claude/skills/provision-config/SKILL.md +1 -1
  36. package/templates/.claude/skills/scaffold-commerce/SKILL.md +498 -1
  37. package/templates/.claude/skills/scaffold-commerce/templates/checkout-route.template.ts +370 -0
  38. package/templates/.claude/skills/scaffold-commerce/templates/invoice-route.template.ts +376 -0
  39. package/templates/.claude/skills/scaffold-commerce/templates/payment-link-route.template.ts +471 -0
  40. package/templates/.claude/skills/scaffold-commerce/templates/portal-route.template.ts +365 -0
  41. package/templates/.claude/skills/scaffold-commerce/templates/stripe-config.template.ts +60 -0
  42. package/templates/.claude/skills/scaffold-commerce/templates/subscription-route.template.ts +446 -0
  43. package/templates/.claude/skills/suggest-site-pages/references/categories.md +4 -4
  44. package/templates/.env.example +126 -2
  45. package/templates/CLAUDE.md +11 -5
  46. package/templates/README.md +1 -1
  47. package/templates/apps/api/.env.example +38 -0
  48. package/templates/apps/api/CLAUDE.md +78 -1
  49. package/templates/apps/api/LICENSE +201 -0
  50. package/templates/apps/api/api/index.ts +6 -141
  51. package/templates/apps/api/jest.config.cjs +16 -0
  52. package/templates/apps/api/package.json +22 -6
  53. package/templates/apps/api/src/__tests__/module-exclusion.test.ts +141 -0
  54. package/templates/apps/api/src/__tests__/server.test.ts +46 -0
  55. package/templates/apps/api/src/__tests__/vercel-handler.test.ts +211 -0
  56. package/templates/apps/api/src/bin/abandon-sweep.ts +42 -0
  57. package/templates/apps/api/src/bin/deliverable-resend.ts +144 -0
  58. package/templates/apps/api/src/bin/deliverable-upload.ts +114 -0
  59. package/templates/apps/api/src/bin/followup-sweep.ts +44 -0
  60. package/templates/apps/api/src/bin/listing-csv-sweep.ts +46 -0
  61. package/templates/apps/api/src/bin/seed-presets.ts +58 -0
  62. package/templates/apps/api/src/bin/seed-waitlist-experiments.ts +104 -0
  63. package/templates/apps/api/src/bin/session-retention-sweep.ts +46 -0
  64. package/templates/apps/api/src/cli.ts +5 -11
  65. package/templates/apps/api/src/config/index.ts +35 -0
  66. package/templates/apps/api/src/config/modules.ts +43 -0
  67. package/templates/apps/api/src/lib/__mocks__/prisma-client.js +11 -0
  68. package/templates/apps/api/src/lib/__mocks__/prisma.ts +241 -0
  69. package/templates/apps/api/src/lib/__tests__/lead-capture-store.test.ts +2 -2
  70. package/templates/apps/api/src/lib/site.ts +16 -0
  71. package/templates/apps/api/src/lib/stripe.ts +22 -0
  72. package/templates/apps/api/src/lib/tenant-db.ts +225 -0
  73. package/templates/apps/api/src/lib/ws-token.ts +91 -0
  74. package/templates/apps/api/src/middleware/__tests__/adversarial/session-fixation.test.ts +7 -1
  75. package/templates/apps/api/src/middleware/__tests__/auth.cookie-path.test.ts +7 -1
  76. package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +91 -4
  77. package/templates/apps/api/src/middleware/auth.ts +43 -8
  78. package/templates/apps/api/src/middleware/rate-limit.ts +78 -3
  79. package/templates/apps/api/src/middleware/tenant.ts +99 -0
  80. package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +92 -0
  81. package/templates/apps/api/src/openapi/spec.ts +312 -3
  82. package/templates/apps/api/src/routes/aeo/__tests__/index.test.ts +175 -0
  83. package/templates/apps/api/src/routes/aeo/index.ts +107 -0
  84. package/templates/apps/api/src/routes/auth/__tests__/auth-tokens.functional.test.ts +5 -2
  85. package/templates/apps/api/src/routes/auth/__tests__/tokens.test.ts +22 -3
  86. package/templates/apps/api/src/routes/auth/tokens.ts +13 -1
  87. package/templates/apps/api/src/routes/billing/__tests__/portal.test.ts +450 -0
  88. package/templates/apps/api/src/routes/billing/__tests__/read.test.ts +262 -0
  89. package/templates/apps/api/src/routes/billing/__tests__/usage.test.ts +436 -0
  90. package/templates/apps/api/src/routes/billing/index.ts +36 -0
  91. package/templates/apps/api/src/routes/billing/portal.ts +182 -0
  92. package/templates/apps/api/src/routes/billing/read.ts +75 -0
  93. package/templates/apps/api/src/routes/billing/usage.ts +153 -0
  94. package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +99 -0
  95. package/templates/apps/api/src/routes/checkout/sessions.ts +20 -8
  96. package/templates/apps/api/src/routes/content/create.ts +1 -0
  97. package/templates/apps/api/src/routes/content/delete.ts +1 -0
  98. package/templates/apps/api/src/routes/content/index.ts +42 -6
  99. package/templates/apps/api/src/routes/content/update.ts +1 -0
  100. package/templates/apps/api/src/routes/deliverables/__tests__/index.test.ts +393 -0
  101. package/templates/apps/api/src/routes/deliverables/index.ts +200 -0
  102. package/templates/apps/api/src/routes/flow-checkouts/__tests__/index.test.ts +443 -0
  103. package/templates/apps/api/src/routes/flow-checkouts/index.ts +82 -0
  104. package/templates/apps/api/src/routes/flows/README.md +147 -0
  105. package/templates/apps/api/src/routes/flows/__tests__/index.test.ts +752 -0
  106. package/templates/apps/api/src/routes/flows/__tests__/recommender.test.ts +671 -0
  107. package/templates/apps/api/src/routes/flows/index.ts +202 -0
  108. package/templates/apps/api/src/routes/flows/recommender.ts +443 -0
  109. package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +438 -31
  110. package/templates/apps/api/src/routes/leads/__tests__/track.test.ts +10 -10
  111. package/templates/apps/api/src/routes/leads/index.ts +43 -9
  112. package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +558 -0
  113. package/templates/apps/api/src/routes/project-listings/__tests__/copy-edit.test.ts +815 -0
  114. package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +869 -0
  115. package/templates/apps/api/src/routes/project-listings/__tests__/me-route-precedence.test.ts +221 -0
  116. package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +468 -0
  117. package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +424 -0
  118. package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +707 -0
  119. package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +375 -0
  120. package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +467 -0
  121. package/templates/apps/api/src/routes/project-listings/__tests__/tenant-isolation.test.ts +566 -0
  122. package/templates/apps/api/src/routes/project-listings/get.ts +70 -0
  123. package/templates/apps/api/src/routes/project-listings/index.ts +120 -0
  124. package/templates/apps/api/src/routes/project-listings/list.ts +47 -0
  125. package/templates/apps/api/src/routes/project-listings/me.ts +76 -0
  126. package/templates/apps/api/src/routes/project-listings/patch-copy.ts +222 -0
  127. package/templates/apps/api/src/routes/project-listings/patch-draft.ts +97 -0
  128. package/templates/apps/api/src/routes/project-listings/patch.ts +136 -0
  129. package/templates/apps/api/src/routes/project-listings/public.ts +52 -0
  130. package/templates/apps/api/src/routes/project-listings/respond.ts +37 -0
  131. package/templates/apps/api/src/routes/project-listings/resume-token.ts +22 -0
  132. package/templates/apps/api/src/routes/project-listings/start.ts +106 -0
  133. package/templates/apps/api/src/routes/project-listings/submit.ts +122 -0
  134. package/templates/apps/api/src/routes/schedule/__tests__/index.test.ts +490 -0
  135. package/templates/apps/api/src/routes/schedule/index.ts +249 -0
  136. package/templates/apps/api/src/routes/slack/__tests__/actions.test.ts +385 -0
  137. package/templates/apps/api/src/routes/slack/actions.ts +177 -0
  138. package/templates/apps/api/src/routes/slack/index.ts +38 -0
  139. package/templates/apps/api/src/routes/test/auth/__tests__/session.test.ts +16 -6
  140. package/templates/apps/api/src/routes/test/auth/session.ts +8 -6
  141. package/templates/apps/api/src/routes/waitlist-experiments/__tests__/tenant-isolation.test.ts +577 -0
  142. package/templates/apps/api/src/routes/waitlist-experiments/comparison.ts +78 -0
  143. package/templates/apps/api/src/routes/waitlist-experiments/index.ts +41 -0
  144. package/templates/apps/api/src/routes/waitlist-experiments/list.ts +64 -0
  145. package/templates/apps/api/src/routes/waitlist-experiments/respond.ts +42 -0
  146. package/templates/apps/api/src/routes/waitlist-experiments/signup.ts +84 -0
  147. package/templates/apps/api/src/routes/waitlist-experiments/signups.ts +119 -0
  148. package/templates/apps/api/src/routes/waitlist-signups/__tests__/index.test.ts +238 -0
  149. package/templates/apps/api/src/routes/waitlist-signups/index.ts +101 -0
  150. package/templates/apps/api/src/routes/webhooks/README.md +35 -12
  151. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +315 -0
  152. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +270 -0
  153. package/templates/apps/api/src/routes/webhooks/stripe.ts +248 -20
  154. package/templates/apps/api/src/routes/workspaces/__tests__/config-tenant-isolation.test.ts +356 -0
  155. package/templates/apps/api/src/routes/workspaces/__tests__/tenant-isolation.test.ts +671 -0
  156. package/templates/apps/api/src/routes/workspaces/config.ts +125 -0
  157. package/templates/apps/api/src/routes/workspaces/index.ts +194 -0
  158. package/templates/apps/api/src/routes/workspaces/sessions.ts +218 -0
  159. package/templates/apps/api/src/server.ts +307 -7
  160. package/templates/apps/api/src/services/__tests__/abandon-email.test.ts +489 -0
  161. package/templates/apps/api/src/services/__tests__/aeo-score.test.ts +475 -0
  162. package/templates/apps/api/src/services/__tests__/answer-distributions.test.ts +132 -0
  163. package/templates/apps/api/src/services/__tests__/consent-migration.test.ts +65 -0
  164. package/templates/apps/api/src/services/__tests__/consent.test.ts +282 -0
  165. package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +523 -0
  166. package/templates/apps/api/src/services/__tests__/deliverable-resend.test.ts +359 -0
  167. package/templates/apps/api/src/services/__tests__/entitlements.test.ts +142 -0
  168. package/templates/apps/api/src/services/__tests__/flow-aggregate.test.ts +425 -0
  169. package/templates/apps/api/src/services/__tests__/flow-engine.test.ts +2840 -0
  170. package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +393 -0
  171. package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +8 -8
  172. package/templates/apps/api/src/services/__tests__/listing-csv-sweep.test.ts +560 -0
  173. package/templates/apps/api/src/services/__tests__/listing-promotion.test.ts +684 -0
  174. package/templates/apps/api/src/services/__tests__/playbook-compile.test.ts +406 -0
  175. package/templates/apps/api/src/services/__tests__/playbook-render.test.ts +290 -0
  176. package/templates/apps/api/src/services/__tests__/project-listing-decision.test.ts +736 -0
  177. package/templates/apps/api/src/services/__tests__/project-listing-flow.test.ts +475 -0
  178. package/templates/apps/api/src/services/__tests__/project-listing-issue.test.ts +340 -0
  179. package/templates/apps/api/src/services/__tests__/recommender-capture.test.ts +983 -0
  180. package/templates/apps/api/src/services/__tests__/slack-notify.test.ts +278 -0
  181. package/templates/apps/api/src/services/__tests__/tenant-context-cascade.test.ts +71 -0
  182. package/templates/apps/api/src/services/__tests__/tenant-context.test.ts +379 -0
  183. package/templates/apps/api/src/services/__tests__/usage-aggregation.test.ts +941 -0
  184. package/templates/apps/api/src/services/__tests__/usage-credits.test.ts +588 -0
  185. package/templates/apps/api/src/services/__tests__/usage-metering.test.ts +768 -0
  186. package/templates/apps/api/src/services/__tests__/waitlist-dashboard.test.ts +1314 -0
  187. package/templates/apps/api/src/services/__tests__/waitlist-experiments.test.ts +341 -0
  188. package/templates/apps/api/src/services/__tests__/waitlist-followup.test.ts +567 -0
  189. package/templates/apps/api/src/services/__tests__/waitlist-scoring.test.ts +474 -0
  190. package/templates/apps/api/src/services/__tests__/waitlist-signups.test.ts +354 -0
  191. package/templates/apps/api/src/services/abandon-email.ts +307 -0
  192. package/templates/apps/api/src/services/aeo-score.ts +288 -0
  193. package/templates/apps/api/src/services/answer-distributions.ts +138 -0
  194. package/templates/apps/api/src/services/consent.ts +236 -0
  195. package/templates/apps/api/src/services/deliverable-fulfillment.ts +523 -0
  196. package/templates/apps/api/src/services/entitlements.ts +102 -0
  197. package/templates/apps/api/src/services/flow-aggregate.ts +232 -0
  198. package/templates/apps/api/src/services/flow-checkouts.ts +123 -0
  199. package/templates/apps/api/src/services/flow-engine.ts +1296 -0
  200. package/templates/apps/api/src/services/lead-promotion.ts +176 -0
  201. package/templates/apps/api/src/services/lead-routing.ts +3 -3
  202. package/templates/apps/api/src/services/listing-config.ts +102 -0
  203. package/templates/apps/api/src/services/listing-csv-sweep.ts +455 -0
  204. package/templates/apps/api/src/services/listing-promotion.ts +342 -0
  205. package/templates/apps/api/src/services/playbook-compile.ts +398 -0
  206. package/templates/apps/api/src/services/playbook-render.ts +263 -0
  207. package/templates/apps/api/src/services/project-listing-decision.ts +510 -0
  208. package/templates/apps/api/src/services/project-listing-flow.ts +426 -0
  209. package/templates/apps/api/src/services/project-listing-issue.ts +251 -0
  210. package/templates/apps/api/src/services/project-listings.ts +1472 -0
  211. package/templates/apps/api/src/services/recommender-capture.ts +835 -0
  212. package/templates/apps/api/src/services/scheduling/cal-provider.ts +392 -0
  213. package/templates/apps/api/src/services/scheduling/index.ts +63 -0
  214. package/templates/apps/api/src/services/scheduling/types.ts +88 -0
  215. package/templates/apps/api/src/services/slack-notify.ts +240 -0
  216. package/templates/apps/api/src/services/tenant-context.ts +451 -0
  217. package/templates/apps/api/src/services/usage-aggregation.ts +516 -0
  218. package/templates/apps/api/src/services/usage-credits.ts +340 -0
  219. package/templates/apps/api/src/services/usage-metering.ts +699 -0
  220. package/templates/apps/api/src/services/waitlist-dashboard.ts +947 -0
  221. package/templates/apps/api/src/services/waitlist-experiments.ts +213 -0
  222. package/templates/apps/api/src/services/waitlist-followup.ts +486 -0
  223. package/templates/apps/api/src/services/waitlist-scoring.ts +166 -0
  224. package/templates/apps/api/src/services/waitlist-signups.ts +165 -0
  225. package/templates/apps/api/src/vercel-handler.ts +165 -0
  226. package/templates/apps/api/tsconfig.json +0 -12
  227. package/templates/apps/mcp/CLAUDE.md +19 -9
  228. package/templates/apps/mcp/LICENSE +201 -0
  229. package/templates/apps/mcp/__tests__/create_page.test.ts +1 -0
  230. package/templates/apps/mcp/__tests__/delete_page.test.ts +1 -0
  231. package/templates/apps/mcp/__tests__/module-exclusion.test.ts +105 -0
  232. package/templates/apps/mcp/__tests__/parity.test.ts +70 -6
  233. package/templates/apps/mcp/__tests__/request_callback.test.ts +4 -4
  234. package/templates/apps/mcp/__tests__/schedule_tools.test.ts +242 -0
  235. package/templates/apps/mcp/__tests__/submit_lead.test.ts +4 -4
  236. package/templates/apps/mcp/__tests__/subscribe_newsletter.test.ts +4 -4
  237. package/templates/apps/mcp/__tests__/tools/purchase.test.ts +47 -0
  238. package/templates/apps/mcp/__tests__/update_page.test.ts +1 -0
  239. package/templates/apps/mcp/package.json +6 -4
  240. package/templates/apps/mcp/src/config/modules.ts +37 -0
  241. package/templates/apps/mcp/src/server.ts +109 -18
  242. package/templates/apps/mcp/src/tools/capture_lead.ts +1 -1
  243. package/templates/apps/mcp/src/tools/create_booking.ts +58 -0
  244. package/templates/apps/mcp/src/tools/create_page.ts +1 -0
  245. package/templates/apps/mcp/src/tools/delete_page.ts +1 -0
  246. package/templates/apps/mcp/src/tools/get_event_meta.ts +55 -0
  247. package/templates/apps/mcp/src/tools/get_flow.ts +61 -0
  248. package/templates/apps/mcp/src/tools/index.ts +25 -0
  249. package/templates/apps/mcp/src/tools/list_availability.ts +67 -0
  250. package/templates/apps/mcp/src/tools/purchase.ts +3 -0
  251. package/templates/apps/mcp/src/tools/submit_flow_step.ts +83 -0
  252. package/templates/apps/mcp/src/tools/update_page.ts +1 -0
  253. package/templates/apps/mcp/tsconfig.json +1 -9
  254. package/templates/apps/web/.env.example +44 -0
  255. package/templates/apps/web/CLAUDE.md +28 -0
  256. package/templates/apps/web/LICENSE +201 -0
  257. package/templates/apps/web/__tests__/__mocks__/code-highlight.jest-stub.ts +24 -0
  258. package/templates/apps/web/__tests__/__mocks__/next-intl-server.ts +33 -0
  259. package/templates/apps/web/__tests__/__mocks__/next-intl.ts +18 -0
  260. package/templates/apps/web/__tests__/__mocks__/server-only.ts +13 -0
  261. package/templates/apps/web/__tests__/__mocks__/style-mock.js +4 -0
  262. package/templates/apps/web/__tests__/__mocks__/vercel-analytics-server.ts +9 -0
  263. package/templates/apps/web/__tests__/jest-support/import-meta-transformer.cjs +90 -0
  264. package/templates/apps/web/__tests__/setup.ts +22 -32
  265. package/templates/apps/web/app/.well-known/ai-plugin.json/route.ts +6 -3
  266. package/templates/apps/web/app/.well-known/mcp.json/build-manifest.ts +8 -2
  267. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +260 -0
  268. package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +263 -0
  269. package/templates/apps/web/app/[locale]/(authed)/account/sign-out-button.tsx +39 -0
  270. package/templates/apps/web/app/[locale]/(authed)/layout.tsx +54 -0
  271. package/templates/apps/web/app/[locale]/(authed)/sign-in-gate.tsx +96 -0
  272. package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/page.tsx +309 -0
  273. package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/signups/[leadId]/page.tsx +149 -0
  274. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/comparison.test.tsx +273 -0
  275. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/gate.test.tsx +172 -0
  276. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/list.test.tsx +171 -0
  277. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/signup.test.tsx +179 -0
  278. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ExperimentsTable.tsx +116 -0
  279. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ForbiddenState.tsx +24 -0
  280. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/OfferFunnelTable.tsx +66 -0
  281. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/RollupTicker.tsx +61 -0
  282. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ScoreTrace.tsx +114 -0
  283. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/SignupsTable.tsx +139 -0
  284. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/WaitlistComparisonTabs.tsx +85 -0
  285. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/status-badges.tsx +67 -0
  286. package/templates/apps/web/app/[locale]/(authed)/waitlist/format.ts +111 -0
  287. package/templates/apps/web/app/[locale]/(authed)/waitlist/page.tsx +179 -0
  288. package/templates/apps/web/app/[locale]/about/page.tsx +10 -1
  289. package/templates/apps/web/app/[locale]/blog/[[...slug]]/page.tsx +6 -1
  290. package/templates/apps/web/app/[locale]/careers/[slug]/page.tsx +9 -0
  291. package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +3 -3
  292. package/templates/apps/web/app/[locale]/careers/page.tsx +9 -1
  293. package/templates/apps/web/app/[locale]/case-studies/[slug]/page.tsx +9 -0
  294. package/templates/apps/web/app/[locale]/case-studies/__tests__/page-renderer-migration.test.tsx +3 -3
  295. package/templates/apps/web/app/[locale]/case-studies/page.tsx +13 -1
  296. package/templates/apps/web/app/[locale]/clients/__tests__/page-renderer-migration.test.tsx +11 -1
  297. package/templates/apps/web/app/[locale]/clients/page.tsx +9 -1
  298. package/templates/apps/web/app/[locale]/contact/ContactForm.tsx +21 -1
  299. package/templates/apps/web/app/[locale]/contact/ConversationalIntakeWrapper.tsx +188 -0
  300. package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +242 -0
  301. package/templates/apps/web/app/[locale]/contact/__tests__/contact.actions.test.ts +29 -0
  302. package/templates/apps/web/app/[locale]/contact/__tests__/intake-analytics.test.ts +51 -0
  303. package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +39 -15
  304. package/templates/apps/web/app/[locale]/contact/contact.actions.ts +21 -3
  305. package/templates/apps/web/app/[locale]/contact/intake-analytics.ts +49 -0
  306. package/templates/apps/web/app/[locale]/contact/intake-script.ts +66 -0
  307. package/templates/apps/web/app/[locale]/contact/page.tsx +14 -4
  308. package/templates/apps/web/app/[locale]/courses/[slug]/page.tsx +13 -1
  309. package/templates/apps/web/app/[locale]/courses/__tests__/page-renderer-migration.test.tsx +3 -3
  310. package/templates/apps/web/app/[locale]/courses/page.tsx +9 -1
  311. package/templates/apps/web/app/[locale]/docs/[[...slug]]/page.tsx +1 -0
  312. package/templates/apps/web/app/[locale]/downloads/__tests__/page-renderer-migration.test.tsx +11 -1
  313. package/templates/apps/web/app/[locale]/downloads/page.tsx +13 -1
  314. package/templates/apps/web/app/[locale]/events/[slug]/page.tsx +9 -0
  315. package/templates/apps/web/app/[locale]/events/__tests__/page-renderer-migration.test.tsx +12 -2
  316. package/templates/apps/web/app/[locale]/events/page.tsx +9 -1
  317. package/templates/apps/web/app/[locale]/faq/page.tsx +10 -1
  318. package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +265 -0
  319. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.permalink.test.tsx +445 -0
  320. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.test.tsx +341 -0
  321. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/checkout.actions.test.ts +287 -0
  322. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/flow.actions.test.ts +302 -0
  323. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/two-route-tool.test.tsx +128 -0
  324. package/templates/apps/web/app/[locale]/flows/[slug]/checkout.actions.ts +254 -0
  325. package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +144 -0
  326. package/templates/apps/web/app/[locale]/flows/[slug]/page.tsx +108 -0
  327. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsClient.tsx +532 -0
  328. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsEmailStep.tsx +121 -0
  329. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/ResultsClient.checkout.test.tsx +457 -0
  330. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/page.test.tsx +396 -0
  331. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/page.tsx +114 -0
  332. package/templates/apps/web/app/[locale]/flows/[slug]/start/page.tsx +52 -0
  333. package/templates/apps/web/app/[locale]/how-to/[slug]/page.tsx +13 -1
  334. package/templates/apps/web/app/[locale]/how-to/__tests__/page-renderer-migration.test.tsx +1 -1
  335. package/templates/apps/web/app/[locale]/integrations/[slug]/page.tsx +9 -0
  336. package/templates/apps/web/app/[locale]/integrations/__tests__/page-renderer-migration.test.tsx +3 -3
  337. package/templates/apps/web/app/[locale]/integrations/page.tsx +13 -1
  338. package/templates/apps/web/app/[locale]/launch-service/page.tsx +14 -1
  339. package/templates/apps/web/app/[locale]/layout.tsx +127 -16
  340. package/templates/apps/web/app/[locale]/page.tsx +10 -1
  341. package/templates/apps/web/app/[locale]/partners/[slug]/page.tsx +9 -0
  342. package/templates/apps/web/app/[locale]/partners/__tests__/page-renderer-migration.test.tsx +3 -3
  343. package/templates/apps/web/app/[locale]/partners/page.tsx +9 -1
  344. package/templates/apps/web/app/[locale]/products/[slug]/page.tsx +9 -0
  345. package/templates/apps/web/app/[locale]/products/__tests__/page-renderer-migration.test.tsx +3 -3
  346. package/templates/apps/web/app/[locale]/products/page.tsx +9 -1
  347. package/templates/apps/web/app/[locale]/projects/[slug]/page.tsx +13 -1
  348. package/templates/apps/web/app/[locale]/projects/__tests__/page-renderer-migration.test.tsx +10 -0
  349. package/templates/apps/web/app/[locale]/projects/apply/ListingApplicationClient.tsx +110 -0
  350. package/templates/apps/web/app/[locale]/projects/apply/__tests__/ListingApplicationClient.test.tsx +96 -0
  351. package/templates/apps/web/app/[locale]/projects/page.tsx +13 -1
  352. package/templates/apps/web/app/[locale]/resources/[slug]/page.tsx +9 -0
  353. package/templates/apps/web/app/[locale]/resources/__tests__/page-renderer-migration.test.tsx +12 -2
  354. package/templates/apps/web/app/[locale]/resources/page.tsx +13 -1
  355. package/templates/apps/web/app/[locale]/schedule/__tests__/page.test.tsx +117 -0
  356. package/templates/apps/web/app/[locale]/schedule/confirmed/__tests__/page.test.tsx +96 -0
  357. package/templates/apps/web/app/[locale]/schedule/confirmed/page.tsx +92 -0
  358. package/templates/apps/web/app/[locale]/schedule/page.tsx +82 -0
  359. package/templates/apps/web/app/[locale]/services/[slug]/page.tsx +9 -0
  360. package/templates/apps/web/app/[locale]/services/__tests__/page-renderer-migration.test.tsx +3 -3
  361. package/templates/apps/web/app/[locale]/services/page.tsx +9 -1
  362. package/templates/apps/web/app/[locale]/sign-in/__tests__/next-path.test.ts +31 -0
  363. package/templates/apps/web/app/[locale]/sign-in/__tests__/page.test.tsx +152 -0
  364. package/templates/apps/web/app/[locale]/sign-in/next-path.ts +0 -0
  365. package/templates/apps/web/app/[locale]/sign-in/page.tsx +83 -0
  366. package/templates/apps/web/app/[locale]/sign-in/sign-in-card.tsx +123 -0
  367. package/templates/apps/web/app/[locale]/software/[slug]/page.tsx +13 -1
  368. package/templates/apps/web/app/[locale]/software/__tests__/page-renderer-migration.test.tsx +1 -1
  369. package/templates/apps/web/app/[locale]/tech-articles/[slug]/page.tsx +13 -1
  370. package/templates/apps/web/app/[locale]/tech-articles/__tests__/page-renderer-migration.test.tsx +1 -1
  371. package/templates/apps/web/app/[locale]/testimonials/__tests__/page-renderer-migration.test.tsx +11 -1
  372. package/templates/apps/web/app/[locale]/testimonials/page.tsx +13 -1
  373. package/templates/apps/web/app/[locale]/thanks/[slug]/__tests__/page.test.tsx +93 -0
  374. package/templates/apps/web/app/[locale]/thanks/[slug]/page.tsx +48 -0
  375. package/templates/apps/web/app/[locale]/use-cases/__tests__/page-renderer-migration.test.tsx +11 -1
  376. package/templates/apps/web/app/[locale]/use-cases/page.tsx +13 -1
  377. package/templates/apps/web/app/[locale]/whitepapers/[slug]/page.tsx +13 -1
  378. package/templates/apps/web/app/[locale]/whitepapers/__tests__/page-renderer-migration.test.tsx +1 -1
  379. package/templates/apps/web/app/globals.css +35 -0
  380. package/templates/apps/web/app/llms.txt/route.ts +2 -1
  381. package/templates/apps/web/app/og/route.tsx +10 -2
  382. package/templates/apps/web/auth.ts +8 -0
  383. package/templates/apps/web/components/sign-out-trigger.tsx +42 -0
  384. package/templates/apps/web/content/en/blog/README.txt +25 -0
  385. package/templates/apps/web/content/en/docs/index.mdx +28 -0
  386. package/templates/apps/web/jest.config.cjs +90 -27
  387. package/templates/apps/web/lib/__tests__/site-brand.test.ts +119 -0
  388. package/templates/apps/web/lib/__tests__/site-theme.test.ts +112 -0
  389. package/templates/apps/web/lib/__tests__/structured-data.test.tsx +499 -0
  390. package/templates/apps/web/lib/account-config.ts +13 -0
  391. package/templates/apps/web/lib/server-api.ts +85 -0
  392. package/templates/apps/web/lib/site-brand.tsx +121 -0
  393. package/templates/apps/web/lib/site-theme.ts +74 -0
  394. package/templates/apps/web/messages/en.json +275 -7
  395. package/templates/apps/web/middleware.ts +11 -3
  396. package/templates/apps/web/next.config.mjs +8 -2
  397. package/templates/apps/web/package.json +17 -7
  398. package/templates/apps/web/postcss.config.mjs +23 -0
  399. package/templates/apps/web/public/.well-known/security.txt +3 -3
  400. package/templates/apps/web/src/lib/routes.ts +15 -2
  401. package/templates/apps/web/tsconfig.json +1 -16
  402. package/templates/content/_site.mdx +36 -25
  403. package/templates/content/en/AboutPage/about.mdx +24 -14
  404. package/templates/content/en/ContactPage/contact.mdx +23 -14
  405. package/templates/content/en/FAQPage/faq.mdx +10 -392
  406. package/templates/content/en/HomePage/home.mdx +14 -285
  407. package/templates/database/CHANGELOG.md +443 -0
  408. package/templates/database/LICENSE +201 -0
  409. package/templates/database/README.md +3 -1
  410. package/templates/database/__tests__/flows-listing-diagnostic.test.ts +392 -0
  411. package/templates/database/__tests__/flows-presets.test.ts +173 -0
  412. package/templates/database/__tests__/prefixed-ids.db.test.ts +146 -0
  413. package/templates/database/__tests__/seed.test.ts +88 -0
  414. package/templates/database/__tests__/user-cascade.test.ts +19 -4
  415. package/templates/database/auth/schema.prisma +521 -0
  416. package/templates/database/config/schema.prisma +234 -0
  417. package/templates/database/content/schema.prisma +263 -0
  418. package/templates/database/flows/listing-diagnostic.ts +182 -0
  419. package/templates/database/flows/presets.ts +315 -0
  420. package/templates/database/inbox/__tests__/schema.test.ts +4 -0
  421. package/templates/database/inbox/schema.prisma +574 -3
  422. package/templates/database/jest.config.cjs +8 -0
  423. package/templates/database/migrations/20260712215647_add_multi_tenant_tenancy/migration.sql +201 -0
  424. package/templates/database/migrations/20260713143320_reanchor_site_config_to_workspace/migration.sql +67 -0
  425. package/templates/database/migrations/20260713174000_add_audit_logs/migration.sql +46 -0
  426. package/templates/database/migrations/20260713190000_drop_telemetry_records/migration.sql +25 -0
  427. package/templates/database/migrations/20260713192000_add_uuidv7_function/migration.sql +49 -0
  428. package/templates/database/migrations/20260713192429_prefixed_uuid_entity_ids/migration.sql +197 -0
  429. package/templates/database/migrations/20260713200000_add_org_billing/migration.sql +32 -0
  430. package/templates/database/migrations/20260714000000_add_lead_promotion_fields/migration.sql +22 -0
  431. package/templates/database/migrations/20260714010000_add_usage_billing_content_models/migration.sql +151 -0
  432. package/templates/database/migrations/20260714020000_add_usage_billing_auth_config_models/migration.sql +223 -0
  433. package/templates/database/migrations/20260714030000_credit_balance_non_negative/migration.sql +22 -0
  434. package/templates/database/migrations/20260714030000_widen_usage_billing_report_aggregates/migration.sql +32 -0
  435. package/templates/database/migrations/20260810233516_add_flow_engine_models/migration.sql +40 -0
  436. package/templates/database/migrations/20260811020000_add_flow_session_abandon_email_columns/migration.sql +4 -0
  437. package/templates/database/migrations/20260811030000_add_flow_session_abandon_email_index/migration.sql +2 -0
  438. package/templates/database/migrations/20260811040000_add_flow_session_segment/migration.sql +2 -0
  439. package/templates/database/migrations/20260811050000_add_deliverable_models/migration.sql +29 -0
  440. package/templates/database/migrations/20260812000000_add_deliverable_grant_send_columns/migration.sql +6 -0
  441. package/templates/database/migrations/20260902222948_add_waitlist_experiment_models/migration.sql +39 -0
  442. package/templates/database/migrations/20260902224500_add_waitlist_id_format_checks/migration.sql +24 -0
  443. package/templates/database/migrations/20260903004112_add_flow_session_flags_aeo_score/migration.sql +3 -0
  444. package/templates/database/migrations/20260903010000_add_playbook_instance/migration.sql +33 -0
  445. package/templates/database/migrations/20260904000000_add_waitlist_followup_send/migration.sql +30 -0
  446. package/templates/database/migrations/20260905230000_add_flow_checkout/migration.sql +47 -0
  447. package/templates/database/migrations/20260906000000_add_flow_checkout_idempotency/migration.sql +16 -0
  448. package/templates/database/migrations/20260907120000_add_project_listing/migration.sql +47 -0
  449. package/templates/database/migrations/20260907130000_add_project_listing_slack_columns/migration.sql +10 -0
  450. package/templates/database/migrations/20260907180000_add_listing_csv_send/migration.sql +26 -0
  451. package/templates/database/migrations/20260909120000_project_listings_platform/migration.sql +90 -0
  452. package/templates/database/migrations/20260909180000_listing_draft_and_fields/migration.sql +174 -0
  453. package/templates/database/migrations/20260910120000_listing_public_site_key/migration.sql +14 -0
  454. package/templates/database/migrations/20260911120000_listing_copy_edit_audit/migration.sql +25 -0
  455. package/templates/database/migrations/20260911140000_listing_structured_address/migration.sql +32 -0
  456. package/templates/database/migrations/20260911180000_consent_grants/migration.sql +71 -0
  457. package/templates/database/migrations/20260911200000_listing_site_answers/migration.sql +30 -0
  458. package/templates/database/migrations/20260912120000_listing_owner_link/migration.sql +49 -0
  459. package/templates/database/ops/schema.prisma +27 -0
  460. package/templates/database/package.json +42 -4
  461. package/templates/database/prisma.config.ts +1 -1
  462. package/templates/database/schema.prisma +19 -435
  463. package/templates/database/seed-test.ts +14 -4
  464. package/templates/database/seed.ts +148 -0
  465. package/templates/database/tenancy.ts +45 -0
  466. package/templates/database/tsconfig.flows.json +15 -0
  467. package/templates/database/tsconfig.json +7 -1
  468. package/templates/database/tsconfig.tenancy.json +14 -0
  469. package/templates/package.json +30 -55
  470. package/templates/pnpm-workspace.yaml +0 -3
  471. package/templates/project.yaml +1 -1
  472. package/templates/tsconfig.json +1 -37
  473. package/templates/apps/web/__tests__/auth/adversarial/cookie-attributes.test.ts +0 -94
  474. package/templates/apps/web/__tests__/auth/adversarial/oauth-state-parameter.test.ts +0 -75
  475. package/templates/apps/web/__tests__/auth/adversarial/redirect-uri-allowlist.test.ts +0 -82
  476. package/templates/apps/web/__tests__/auth/env-helpers.test.ts +0 -168
  477. package/templates/apps/web/__tests__/authority-list-routes.test.tsx +0 -208
  478. package/templates/apps/web/__tests__/better-stack.test.ts +0 -66
  479. package/templates/apps/web/__tests__/blog-dir-resolution.test.ts +0 -87
  480. package/templates/apps/web/__tests__/blog-source-populated.test.ts +0 -130
  481. package/templates/apps/web/__tests__/data-testid-coverage.test.tsx +0 -60
  482. package/templates/apps/web/__tests__/google-analytics.test.tsx +0 -104
  483. package/templates/apps/web/__tests__/i18n/middleware.test.ts +0 -119
  484. package/templates/apps/web/__tests__/i18n/routing.test.ts +0 -114
  485. package/templates/apps/web/__tests__/i18n/translations.test.ts +0 -191
  486. package/templates/apps/web/__tests__/json-ld.test.ts +0 -98
  487. package/templates/apps/web/__tests__/knowledge-events-routes.test.tsx +0 -580
  488. package/templates/apps/web/__tests__/layout-stealth.test.ts +0 -39
  489. package/templates/apps/web/__tests__/lighthouse-fixtures/improvement-image-shrink.tsx +0 -103
  490. package/templates/apps/web/__tests__/lighthouse-fixtures/override-fixture.md +0 -114
  491. package/templates/apps/web/__tests__/lighthouse-fixtures/regression-bloat.tsx +0 -94
  492. package/templates/apps/web/__tests__/page.test.tsx +0 -230
  493. package/templates/apps/web/__tests__/products-services-phase1.test.tsx +0 -321
  494. package/templates/apps/web/__tests__/projects-routes.test.tsx +0 -392
  495. package/templates/apps/web/__tests__/resources-downloads-integrations-phase2.test.tsx +0 -475
  496. package/templates/apps/web/__tests__/routes/ai-plugin-json.test.ts +0 -53
  497. package/templates/apps/web/__tests__/routes/blog-route.test.tsx +0 -191
  498. package/templates/apps/web/__tests__/routes/llms-txt.test.ts +0 -123
  499. package/templates/apps/web/__tests__/routes/robots-txt.test.ts +0 -52
  500. package/templates/apps/web/__tests__/routes/sitemap-xml.test.ts +0 -78
  501. package/templates/apps/web/__tests__/routes/well-known-mcp.test.ts +0 -203
  502. package/templates/apps/web/__tests__/section-routes.test.tsx +0 -693
  503. package/templates/apps/web/__tests__/security-headers.test.ts +0 -201
  504. package/templates/apps/web/__tests__/sentry-config.test.ts +0 -116
  505. package/templates/apps/web/__tests__/sitemap-locales.test.ts +0 -415
  506. package/templates/apps/web/__tests__/standalone-informational-routes.test.tsx +0 -844
  507. package/templates/apps/web/app/[locale]/__tests__/layout.test.tsx +0 -120
  508. package/templates/apps/web/app/[locale]/__tests__/page-renderer-migration.test.tsx +0 -202
  509. package/templates/apps/web/app/__tests__/error-boundaries.test.tsx +0 -152
  510. package/templates/apps/web/content/en/blog/index.mdx +0 -15
  511. package/templates/apps/web/content/en/blog/launching-ailk.mdx +0 -20
  512. package/templates/apps/web/content/en/docs/getting-started.mdx +0 -30
  513. package/templates/content/.gitkeep +0 -0
  514. package/templates/content/CLAUDE.md +0 -85
  515. package/templates/content/ar/HomePage/home.mdx +0 -123
  516. package/templates/content/ar/_site.mdx +0 -35
  517. package/templates/content/de/_site.mdx +0 -29
  518. package/templates/content/en/.gitkeep +0 -0
  519. package/templates/content/en/AboutPage/about.schema.json +0 -82
  520. package/templates/content/en/BookCall/book-call.mdx +0 -19
  521. package/templates/content/en/BookCall/book-call.schema.json +0 -121
  522. package/templates/content/en/BreadcrumbList/site-breadcrumbs.mdx +0 -20
  523. package/templates/content/en/CareersList/careers.mdx +0 -24
  524. package/templates/content/en/CaseStudiesList/case-studies.mdx +0 -22
  525. package/templates/content/en/CaseStudiesList/showcase.mdx +0 -8
  526. package/templates/content/en/CaseStudy/ailk-aeo-rollout.mdx +0 -32
  527. package/templates/content/en/CaseStudy/schema-builder-migration.mdx +0 -41
  528. package/templates/content/en/ClientsList/clients.mdx +0 -38
  529. package/templates/content/en/ContactPage/contact.schema.json +0 -100
  530. package/templates/content/en/ContactPoint/primary.mdx +0 -14
  531. package/templates/content/en/Course/aeo-fundamentals.mdx +0 -37
  532. package/templates/content/en/Course/schema-builder-workshop.mdx +0 -62
  533. package/templates/content/en/CoursesList/courses.mdx +0 -22
  534. package/templates/content/en/DownloadsList/downloads.mdx +0 -22
  535. package/templates/content/en/Event/aeo-office-hours-may-2026.mdx +0 -39
  536. package/templates/content/en/Event/ailk-launch-day-2026.mdx +0 -35
  537. package/templates/content/en/EventsList/events.mdx +0 -26
  538. package/templates/content/en/FAQPage/faq.schema.json +0 -231
  539. package/templates/content/en/GlossaryList/glossary.mdx +0 -19
  540. package/templates/content/en/GlossaryList/glossary.schema.json +0 -300
  541. package/templates/content/en/HomePage/home.schema.json +0 -88
  542. package/templates/content/en/HomePage/launch-service.mdx +0 -475
  543. package/templates/content/en/HomePage/launch-service.schema.json +0 -138
  544. package/templates/content/en/HowTo/add-mdx-content.mdx +0 -38
  545. package/templates/content/en/HowTo/configure-aeo-scoring.mdx +0 -35
  546. package/templates/content/en/HowTo/set-up-json-ld.mdx +0 -36
  547. package/templates/content/en/Integration/github-actions.mdx +0 -31
  548. package/templates/content/en/Integration/openai-api.mdx +0 -26
  549. package/templates/content/en/Integration/vercel.mdx +0 -32
  550. package/templates/content/en/IntegrationsList/integrations.mdx +0 -22
  551. package/templates/content/en/JobPosting/content-strategist.mdx +0 -46
  552. package/templates/content/en/JobPosting/developer-advocate.mdx +0 -47
  553. package/templates/content/en/JobPosting/senior-fullstack-engineer.mdx +0 -57
  554. package/templates/content/en/LandingPage/aeo-audit.mdx +0 -19
  555. package/templates/content/en/LandingPage/aeo-audit.schema.json +0 -92
  556. package/templates/content/en/LandingPage/agency-waitlist.mdx +0 -340
  557. package/templates/content/en/LandingPage/agency-waitlist.schema.json +0 -152
  558. package/templates/content/en/LandingPage/developers.mdx +0 -19
  559. package/templates/content/en/LandingPage/developers.schema.json +0 -109
  560. package/templates/content/en/LandingPage/for-agencies.mdx +0 -390
  561. package/templates/content/en/LandingPage/for-agencies.schema.json +0 -107
  562. package/templates/content/en/LandingPage/for-ai-search-agencies.mdx +0 -417
  563. package/templates/content/en/LandingPage/for-ai-search-agencies.schema.json +0 -105
  564. package/templates/content/en/LandingPage/for-businesses.mdx +0 -19
  565. package/templates/content/en/LandingPage/for-businesses.schema.json +0 -96
  566. package/templates/content/en/LandingPage/platform.mdx +0 -369
  567. package/templates/content/en/LandingPage/platform.schema.json +0 -166
  568. package/templates/content/en/LandingPage/vs-ai-website-builders.mdx +0 -19
  569. package/templates/content/en/LandingPage/vs-ai-website-builders.schema.json +0 -102
  570. package/templates/content/en/LandingPage/vs-wordpress.mdx +0 -19
  571. package/templates/content/en/LandingPage/vs-wordpress.schema.json +0 -53
  572. package/templates/content/en/NewsletterSignup/newsletter.mdx +0 -236
  573. package/templates/content/en/NewsletterSignup/newsletter.schema.json +0 -91
  574. package/templates/content/en/Organization/site-owner.mdx +0 -16
  575. package/templates/content/en/Partner/prisma.mdx +0 -31
  576. package/templates/content/en/Partner/supabase.mdx +0 -30
  577. package/templates/content/en/Partner/vercel.mdx +0 -28
  578. package/templates/content/en/PartnersList/partners.mdx +0 -22
  579. package/templates/content/en/Person/founder.mdx +0 -18
  580. package/templates/content/en/Pricing/pricing.mdx +0 -538
  581. package/templates/content/en/Pricing/pricing.schema.json +0 -209
  582. package/templates/content/en/PrivacyPage/privacy.mdx +0 -165
  583. package/templates/content/en/PrivacyPage/privacy.schema.json +0 -64
  584. package/templates/content/en/Product/ailk.mdx +0 -19
  585. package/templates/content/en/Product/ailk.schema.json +0 -94
  586. package/templates/content/en/Product/api-launch-kit.mdx +0 -26
  587. package/templates/content/en/Product/launch-kit.mdx +0 -28
  588. package/templates/content/en/ProductsList/products.mdx +0 -26
  589. package/templates/content/en/Project/ailk-phase-2.mdx +0 -54
  590. package/templates/content/en/ProjectsList/projects.mdx +0 -25
  591. package/templates/content/en/ResourceDetail/aeo-scoring-guide.mdx +0 -27
  592. package/templates/content/en/ResourceDetail/content-model-whitepaper.mdx +0 -28
  593. package/templates/content/en/ResourceDetail/schema-org-quick-reference.mdx +0 -39
  594. package/templates/content/en/ResourcesHub/resources.mdx +0 -22
  595. package/templates/content/en/Service/consulting.mdx +0 -22
  596. package/templates/content/en/Service/implementation.mdx +0 -30
  597. package/templates/content/en/Service/launch-service.mdx +0 -232
  598. package/templates/content/en/Service/launch.mdx +0 -228
  599. package/templates/content/en/ServicesList/services.mdx +0 -84
  600. package/templates/content/en/SoftwareProduct/ailk-aeo.mdx +0 -44
  601. package/templates/content/en/SoftwareProduct/ailk-content-adapters.mdx +0 -47
  602. package/templates/content/en/SoftwareProduct/ailk-schema.mdx +0 -49
  603. package/templates/content/en/TeamList/team.mdx +0 -63
  604. package/templates/content/en/TeamMember/founder.mdx +0 -72
  605. package/templates/content/en/TermsPage/terms.mdx +0 -19
  606. package/templates/content/en/TermsPage/terms.schema.json +0 -99
  607. package/templates/content/en/TestimonialsList/testimonials.mdx +0 -44
  608. package/templates/content/en/UseCasesList/use-cases.mdx +0 -30
  609. package/templates/content/en/Whitepaper/ax-first-product-development.mdx +0 -27
  610. package/templates/content/en/Whitepaper/open-core-commercial-strategy.mdx +0 -27
  611. package/templates/content/en/Whitepaper/structured-data-for-answer-engines.mdx +0 -31
  612. package/templates/content/en-XA/AboutPage/about.mdx +0 -8
  613. package/templates/content/en-XA/FAQPage/faq.mdx +0 -8
  614. package/templates/content/en-XA/HomePage/home.mdx +0 -8
  615. package/templates/content/es/_site.mdx +0 -30
  616. package/templates/content/fr/_site.mdx +0 -30
  617. package/templates/content/ja/_site.mdx +0 -30
  618. package/templates/content/pt-BR/AboutPage/about.mdx +0 -8
  619. package/templates/content/pt-BR/FAQPage/faq.mdx +0 -8
  620. package/templates/content/pt-BR/HomePage/home.mdx +0 -23
@@ -0,0 +1,242 @@
1
+ // oss: false
2
+ /**
3
+ * Tests for the list_availability + get_event_meta + create_booking MCP
4
+ * tools (#4416, #4633).
5
+ *
6
+ * The claim these tools make is PARITY OF CODE, not just of endpoint: an
7
+ * agent that hits a taken slot must read `slot_unavailable`, the same word
8
+ * the route emitted and the same word the booker section branches on. Most of
9
+ * what follows pins that mapping.
10
+ */
11
+
12
+ import { ailkCreateBooking } from "../src/tools/create_booking.js";
13
+ import { ailkGetEventMeta } from "../src/tools/get_event_meta.js";
14
+ import {
15
+ ailkListAvailability,
16
+ schedulingKindFromStatus,
17
+ } from "../src/tools/list_availability.js";
18
+
19
+ jest.mock("@working-theory/api-client", () => ({
20
+ apiClient: {
21
+ schedule: {
22
+ availability: jest.fn(),
23
+ eventMeta: jest.fn(),
24
+ book: jest.fn(),
25
+ },
26
+ },
27
+ }));
28
+
29
+ const { apiClient } = require("@working-theory/api-client") as {
30
+ apiClient: {
31
+ schedule: { availability: jest.Mock; eventMeta: jest.Mock; book: jest.Mock };
32
+ };
33
+ };
34
+
35
+ const VALID_RANGE = {
36
+ calLink: "sam/fit-check",
37
+ from: "2026-08-26T00:00:00.000Z",
38
+ to: "2026-09-02T00:00:00.000Z",
39
+ };
40
+
41
+ const AVAILABILITY = {
42
+ event: {
43
+ title: "15-Minute Call with Sam",
44
+ durationMinutes: 15,
45
+ meetingType: "Google Meet",
46
+ timeZone: "America/Denver",
47
+ },
48
+ slots: [
49
+ { start: "2026-08-27T16:00:00.000Z", end: "2026-08-27T16:15:00.000Z" },
50
+ ],
51
+ };
52
+
53
+ beforeEach(() => {
54
+ jest.clearAllMocks();
55
+ });
56
+
57
+ describe("schedulingKindFromStatus", () => {
58
+ it.each([
59
+ [400, "invalid_input"],
60
+ [404, "event_not_found"],
61
+ [409, "slot_unavailable"],
62
+ [503, "not_configured"],
63
+ [502, "provider_error"],
64
+ [500, "provider_error"],
65
+ ])("maps %i to %s", (status, kind) => {
66
+ expect(schedulingKindFromStatus(status as number)).toBe(kind);
67
+ });
68
+ });
69
+
70
+ describe("ailkListAvailability", () => {
71
+ it("rejects a malformed calLink without calling api-client", async () => {
72
+ const result = await ailkListAvailability({
73
+ ...VALID_RANGE,
74
+ calLink: "https://cal.com/sam/fit-check",
75
+ });
76
+
77
+ expect(result.ok).toBe(false);
78
+ if (!result.ok) expect(result.error.kind).toBe("invalid_input");
79
+ expect(apiClient.schedule.availability).not.toHaveBeenCalled();
80
+ });
81
+
82
+ it("rejects a range wider than the 60-day cap without calling api-client", async () => {
83
+ const result = await ailkListAvailability({
84
+ ...VALID_RANGE,
85
+ to: "2027-08-26T00:00:00.000Z",
86
+ });
87
+
88
+ expect(result.ok).toBe(false);
89
+ expect(apiClient.schedule.availability).not.toHaveBeenCalled();
90
+ });
91
+
92
+ it("forwards the validated range and returns the derived event meta", async () => {
93
+ apiClient.schedule.availability.mockResolvedValueOnce({
94
+ ok: true,
95
+ body: AVAILABILITY,
96
+ });
97
+
98
+ const result = await ailkListAvailability(VALID_RANGE);
99
+
100
+ expect(apiClient.schedule.availability).toHaveBeenCalledWith(VALID_RANGE);
101
+ expect(result.ok).toBe(true);
102
+ if (result.ok) {
103
+ expect(result.data.event.durationMinutes).toBe(15);
104
+ expect(result.data.slots).toHaveLength(1);
105
+ }
106
+ });
107
+
108
+ it("surfaces an unconfigured deploy as not_configured, not a bare status", async () => {
109
+ apiClient.schedule.availability.mockResolvedValueOnce({
110
+ ok: false,
111
+ status: 503,
112
+ body: null,
113
+ });
114
+
115
+ const result = await ailkListAvailability(VALID_RANGE);
116
+
117
+ expect(result.ok).toBe(false);
118
+ if (!result.ok) expect(result.error.kind).toBe("not_configured");
119
+ });
120
+ });
121
+
122
+ describe("ailkGetEventMeta", () => {
123
+ const EVENTS = {
124
+ events: [
125
+ {
126
+ calLink: "sam/fit-check",
127
+ event: {
128
+ title: "15-Minute Call with Sam",
129
+ durationMinutes: 15,
130
+ price: { amountMinor: 0, currency: "usd" },
131
+ timeZone: "America/Denver",
132
+ },
133
+ },
134
+ {
135
+ calLink: "sam/power-hour",
136
+ event: {
137
+ title: "Power Hour",
138
+ durationMinutes: 60,
139
+ description: "Sixty focused minutes on your positioning.",
140
+ price: { amountMinor: 25000, currency: "usd" },
141
+ timeZone: "America/Denver",
142
+ },
143
+ },
144
+ ],
145
+ };
146
+
147
+ it("rejects a malformed link in the list without calling api-client", async () => {
148
+ const result = await ailkGetEventMeta({
149
+ calLinks: "sam/fit-check,https://cal.com/sam/power-hour",
150
+ });
151
+
152
+ expect(result.ok).toBe(false);
153
+ if (!result.ok) expect(result.error.kind).toBe("invalid_input");
154
+ expect(apiClient.schedule.eventMeta).not.toHaveBeenCalled();
155
+ });
156
+
157
+ it("forwards the links and returns meta only — a free and a priced event", async () => {
158
+ apiClient.schedule.eventMeta.mockResolvedValueOnce({
159
+ ok: true,
160
+ body: EVENTS,
161
+ });
162
+
163
+ const result = await ailkGetEventMeta({
164
+ calLinks: "sam/fit-check,sam/power-hour",
165
+ });
166
+
167
+ expect(apiClient.schedule.eventMeta).toHaveBeenCalledWith({
168
+ calLinks: "sam/fit-check,sam/power-hour",
169
+ });
170
+ expect(result.ok).toBe(true);
171
+ if (result.ok) {
172
+ expect(result.data.events).toHaveLength(2);
173
+ expect(result.data.events[0]?.event.price?.amountMinor).toBe(0);
174
+ expect(result.data.events[1]?.event.price?.amountMinor).toBe(25000);
175
+ expect(result.data.events[1]?.event.description).toBe(
176
+ "Sixty focused minutes on your positioning.",
177
+ );
178
+ }
179
+ });
180
+
181
+ it("surfaces an unconfigured deploy as not_configured, not a bare status", async () => {
182
+ apiClient.schedule.eventMeta.mockResolvedValueOnce({
183
+ ok: false,
184
+ status: 503,
185
+ body: null,
186
+ });
187
+
188
+ const result = await ailkGetEventMeta({ calLinks: "sam/fit-check" });
189
+
190
+ expect(result.ok).toBe(false);
191
+ if (!result.ok) expect(result.error.kind).toBe("not_configured");
192
+ });
193
+ });
194
+
195
+ describe("ailkCreateBooking", () => {
196
+ const VALID_BOOKING = {
197
+ calLink: "sam/fit-check",
198
+ start: "2026-08-27T16:00:00.000Z",
199
+ attendee: { name: "Sam Rivera", email: "sam@example.com" },
200
+ };
201
+
202
+ it("rejects a malformed attendee email without calling api-client", async () => {
203
+ const result = await ailkCreateBooking({
204
+ ...VALID_BOOKING,
205
+ attendee: { name: "Sam Rivera", email: "sam@" },
206
+ });
207
+
208
+ expect(result.ok).toBe(false);
209
+ if (!result.ok) expect(result.error.kind).toBe("invalid_input");
210
+ expect(apiClient.schedule.book).not.toHaveBeenCalled();
211
+ });
212
+
213
+ it("forwards the booking and returns the provider's identifier", async () => {
214
+ apiClient.schedule.book.mockResolvedValueOnce({
215
+ ok: true,
216
+ body: {
217
+ bookingId: "bkg_9",
218
+ start: "2026-08-27T16:00:00.000Z",
219
+ end: "2026-08-27T16:15:00.000Z",
220
+ },
221
+ });
222
+
223
+ const result = await ailkCreateBooking(VALID_BOOKING);
224
+
225
+ expect(apiClient.schedule.book).toHaveBeenCalledWith(VALID_BOOKING);
226
+ expect(result.ok).toBe(true);
227
+ if (result.ok) expect(result.data.bookingId).toBe("bkg_9");
228
+ });
229
+
230
+ it("surfaces a lost race as slot_unavailable so the caller can retry another slot", async () => {
231
+ apiClient.schedule.book.mockResolvedValueOnce({
232
+ ok: false,
233
+ status: 409,
234
+ body: null,
235
+ });
236
+
237
+ const result = await ailkCreateBooking(VALID_BOOKING);
238
+
239
+ expect(result.ok).toBe(false);
240
+ if (!result.ok) expect(result.error.kind).toBe("slot_unavailable");
241
+ });
242
+ });
@@ -55,7 +55,7 @@ describe("ailkCaptureLead — happy path", () => {
55
55
  it("forwards canonical body to api-client and returns ok:true with id and persisted", async () => {
56
56
  apiClient.leads.submit.mockResolvedValueOnce({
57
57
  ok: true,
58
- body: { id: "lead_abc123", persisted: "db" },
58
+ body: { id: "lead_00000000-0000-7000-8000-0000000000d1", persisted: "db" },
59
59
  });
60
60
 
61
61
  const result = await ailkCaptureLead(VALID_INPUT);
@@ -68,7 +68,7 @@ describe("ailkCaptureLead — happy path", () => {
68
68
  });
69
69
  expect(result.ok).toBe(true);
70
70
  if (result.ok) {
71
- expect(result.id).toBe("lead_abc123");
71
+ expect(result.id).toBe("lead_00000000-0000-7000-8000-0000000000d1");
72
72
  expect(result.persisted).toBe("db");
73
73
  }
74
74
  });
@@ -135,7 +135,7 @@ describe("ailkCaptureLead — siteId default", () => {
135
135
  delete process.env.AILK_SITE_ID;
136
136
  apiClient.leads.submit.mockResolvedValueOnce({
137
137
  ok: true,
138
- body: { id: "lead_default", persisted: "db" },
138
+ body: { id: "lead_00000000-0000-7000-8000-0000000000d2", persisted: "db" },
139
139
  });
140
140
 
141
141
  await ailkCaptureLead(VALID_INPUT);
@@ -154,7 +154,7 @@ describe("ailkCaptureLead — siteId override", () => {
154
154
  process.env.AILK_SITE_ID = "site-abc";
155
155
  apiClient.leads.submit.mockResolvedValueOnce({
156
156
  ok: true,
157
- body: { id: "lead_override", persisted: "db" },
157
+ body: { id: "lead_00000000-0000-7000-8000-0000000000d3", persisted: "db" },
158
158
  });
159
159
 
160
160
  await ailkCaptureLead(VALID_INPUT);
@@ -52,7 +52,7 @@ describe("ailkSubscribeNewsletter — happy path", () => {
52
52
  it("forwards canonical body to api-client and returns ok:true with id and persisted", async () => {
53
53
  apiClient.leads.subscribe.mockResolvedValueOnce({
54
54
  ok: true,
55
- body: { id: "sub_xyz789", persisted: "db" },
55
+ body: { id: "subscr_00000000-0000-7000-8000-0000000000d1", persisted: "db" },
56
56
  });
57
57
 
58
58
  const result = await ailkSubscribeNewsletter(VALID_INPUT);
@@ -64,7 +64,7 @@ describe("ailkSubscribeNewsletter — happy path", () => {
64
64
  });
65
65
  expect(result.ok).toBe(true);
66
66
  if (result.ok) {
67
- expect(result.id).toBe("sub_xyz789");
67
+ expect(result.id).toBe("subscr_00000000-0000-7000-8000-0000000000d1");
68
68
  expect(result.persisted).toBe("db");
69
69
  }
70
70
  });
@@ -131,7 +131,7 @@ describe("ailkSubscribeNewsletter — siteId default", () => {
131
131
  delete process.env.AILK_SITE_ID;
132
132
  apiClient.leads.subscribe.mockResolvedValueOnce({
133
133
  ok: true,
134
- body: { id: "sub_default", persisted: "db" },
134
+ body: { id: "subscr_00000000-0000-7000-8000-0000000000d2", persisted: "db" },
135
135
  });
136
136
 
137
137
  await ailkSubscribeNewsletter(VALID_INPUT);
@@ -150,7 +150,7 @@ describe("ailkSubscribeNewsletter — siteId override", () => {
150
150
  process.env.AILK_SITE_ID = "site-abc";
151
151
  apiClient.leads.subscribe.mockResolvedValueOnce({
152
152
  ok: true,
153
- body: { id: "sub_override", persisted: "db" },
153
+ body: { id: "subscr_00000000-0000-7000-8000-0000000000d3", persisted: "db" },
154
154
  });
155
155
 
156
156
  await ailkSubscribeNewsletter(VALID_INPUT);
@@ -54,6 +54,22 @@ describe("PurchaseInputSchema", () => {
54
54
  expect(result.success).toBe(true);
55
55
  });
56
56
 
57
+ it("accepts an optional allowPromotionCodes boolean", () => {
58
+ const result = PurchaseInputSchema.safeParse({
59
+ priceId: "price_test_123",
60
+ allowPromotionCodes: true,
61
+ });
62
+ expect(result.success).toBe(true);
63
+ });
64
+
65
+ it("rejects a non-boolean allowPromotionCodes", () => {
66
+ const result = PurchaseInputSchema.safeParse({
67
+ priceId: "price_test_123",
68
+ allowPromotionCodes: "yes",
69
+ });
70
+ expect(result.success).toBe(false);
71
+ });
72
+
57
73
  it("rejects input missing priceId", () => {
58
74
  const result = PurchaseInputSchema.safeParse({ quantity: 1 });
59
75
  expect(result.success).toBe(false);
@@ -132,6 +148,37 @@ describe("ailkPurchase — valid input", () => {
132
148
  expect(body.quantity).toBe(1);
133
149
  });
134
150
 
151
+ it("forwards allowPromotionCodes when supplied, omits it otherwise", async () => {
152
+ const okResponse = () => ({
153
+ ok: true,
154
+ status: 201,
155
+ json: () =>
156
+ Promise.resolve({
157
+ sessionId: "cs_test_promo",
158
+ url: "https://checkout.stripe.com/pay/cs_test_promo",
159
+ }),
160
+ });
161
+ mockFetch.mockResolvedValueOnce(okResponse());
162
+
163
+ await ailkPurchase({
164
+ priceId: "price_test_123",
165
+ allowPromotionCodes: true,
166
+ });
167
+
168
+ const withFlag = JSON.parse(
169
+ (mockFetch.mock.calls[0] as [string, RequestInit])[1].body as string,
170
+ ) as Record<string, unknown>;
171
+ expect(withFlag.allowPromotionCodes).toBe(true);
172
+
173
+ mockFetch.mockResolvedValueOnce(okResponse());
174
+ await ailkPurchase({ priceId: "price_test_123" });
175
+
176
+ const withoutFlag = JSON.parse(
177
+ (mockFetch.mock.calls[1] as [string, RequestInit])[1].body as string,
178
+ ) as Record<string, unknown>;
179
+ expect(withoutFlag).not.toHaveProperty("allowPromotionCodes");
180
+ });
181
+
135
182
  it("returns { url } on success", async () => {
136
183
  mockFetch.mockResolvedValueOnce({
137
184
  ok: true,
@@ -1,3 +1,4 @@
1
+ // oss: false
1
2
  /**
2
3
  * Tests for the update_page MCP tool handler.
3
4
  *
@@ -6,6 +6,7 @@
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
8
8
  "scripts": {
9
+ "lint": "tsx ../../scripts/lint-package.ts",
9
10
  "build": "tsc -b",
10
11
  "type-check": "tsc --noEmit",
11
12
  "dev": "node --experimental-vm-modules dist/index.js",
@@ -14,6 +15,9 @@
14
15
  "test:coverage": "jest --coverage"
15
16
  },
16
17
  "dependencies": {
18
+ "@fastify/cors": "^11.2.0",
19
+ "@fastify/rate-limit": "^10.3.0",
20
+ "@modelcontextprotocol/sdk": "^1.29.0",
17
21
  "@working-theory/aeo": "workspace:*",
18
22
  "@working-theory/api-client": "workspace:*",
19
23
  "@working-theory/content-adapters": "workspace:*",
@@ -21,10 +25,8 @@
21
25
  "@working-theory/metadata": "workspace:*",
22
26
  "@working-theory/schema": "workspace:*",
23
27
  "@working-theory/validation": "workspace:*",
24
- "@fastify/cors": "^11.2.0",
25
- "@fastify/rate-limit": "^10.3.0",
26
- "@modelcontextprotocol/sdk": "^1.29.0",
27
- "fastify": "^5.7.2",
28
+ "fastify": "^5.12.1",
29
+ "yaml": "^2.9.0",
28
30
  "zod": "^4.2.1"
29
31
  },
30
32
  "devDependencies": {
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Paid-module presence — the registration-grain signal used by `server.ts`
3
+ * to decide whether a paid module's tools are wired into this build
4
+ * (S1, #5088). Mirrors `apps/api/src/config/modules.ts` — apps/* may not
5
+ * import apps/*, so this tiny reader is duplicated rather than shared.
6
+ *
7
+ * `AILK_ENABLED_MODULES` is a comma-separated allowlist (e.g.
8
+ * `"multi-tenant,scheduling"`). Unset means "every module on" — today's
9
+ * behavior, unchanged for every existing deployment; an explicit empty
10
+ * string (`""`) means "no paid modules" (the OSS-strip build target).
11
+ *
12
+ * Recognized module keys: `docs`, `multi-tenant`, `scheduling` (the
13
+ * `ModuleName` vocabulary `packages/create-ailk/src/module-architecture.ts`
14
+ * already models via `MODULE_SLICES`) plus `content-write` — the Pro
15
+ * content-write surface (`create_page`/`update_page`/`delete_page`).
16
+ * `content-write` is intentionally NOT a `MODULE_SLICES` entry: it is not
17
+ * (yet) a create-ailk `--modules` selector, so it stays a local flag here
18
+ * rather than widening the create-ailk module vocabulary.
19
+ */
20
+
21
+ function enabledModules(): Set<string> | undefined {
22
+ const raw = process.env.AILK_ENABLED_MODULES;
23
+ if (raw === undefined) return undefined; // undefined sentinel = "all enabled"
24
+ return new Set(
25
+ raw
26
+ .split(",")
27
+ .map((m) => m.trim())
28
+ .filter(Boolean),
29
+ );
30
+ }
31
+
32
+ /** Whether the named paid module's tools should register on this build/boot. */
33
+ export function isModuleEnabled(moduleName: string): boolean {
34
+ const enabled = enabledModules();
35
+ if (enabled === undefined) return true;
36
+ return enabled.has(moduleName);
37
+ }
@@ -12,15 +12,15 @@
12
12
  * each catalog entry to its handler.
13
13
  */
14
14
 
15
- import { getMcpToolRegistrations, OSS_DISABLED_TOOLS } from "@working-theory/metadata";
16
15
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
16
+ import { getMcpToolRegistrations, OSS_DISABLED_TOOLS } from "@working-theory/metadata";
17
17
 
18
+ import { isModuleEnabled } from "./config/modules.js";
18
19
  import { ailkAudit } from "./tools/audit.js";
19
20
  import { ailkCaptureLead } from "./tools/capture_lead.js";
20
- import { ailkCreatePage } from "./tools/create_page.js";
21
- import { ailkDeletePage } from "./tools/delete_page.js";
22
21
  import { ailkGetBusinessHours } from "./tools/get_business_hours.js";
23
22
  import { ailkGetFaq } from "./tools/get_faq.js";
23
+ import { ailkGetFlow } from "./tools/get_flow.js";
24
24
  import { ailkGetLocations } from "./tools/get_locations.js";
25
25
  import { ailkGetOfferings } from "./tools/get_offerings.js";
26
26
  import { ailkGetPricing } from "./tools/get_pricing.js";
@@ -31,8 +31,8 @@ import { ailkReadContent } from "./tools/read_content.js";
31
31
  import { ailkRequestCallback } from "./tools/request_callback.js";
32
32
  import { ailkScaffold } from "./tools/scaffold.js";
33
33
  import { ailkSearchContent } from "./tools/search_content.js";
34
+ import { ailkSubmitFlowStep } from "./tools/submit_flow_step.js";
34
35
  import { ailkSubscribeNewsletter } from "./tools/subscribe_newsletter.js";
35
- import { ailkUpdatePage } from "./tools/update_page.js";
36
36
 
37
37
  /**
38
38
  * Tool-name → handler map. Each handler accepts the parsed input the SDK
@@ -65,16 +65,87 @@ const HANDLERS: Record<string, (input: unknown) => Promise<unknown>> = {
65
65
  subscribe_newsletter: (input) => ailkSubscribeNewsletter(input as never),
66
66
  request_callback: (input) => ailkRequestCallback(input as never),
67
67
  scaffold: (input) => ailkScaffold(input as never),
68
+ get_flow: (input) => ailkGetFlow(input as never),
69
+ submit_flow_step: (input) => ailkSubmitFlowStep(input as never),
70
+ };
71
+
72
+ type ToolHandler = (input: unknown) => Promise<unknown>;
73
+
74
+ /**
75
+ * Module-conditional tool handlers — the FILE-grain half of S1's registration
76
+ * gate (#5182).
77
+ *
78
+ * `shouldRegister` already keeps a paid module's tools out of the registered
79
+ * catalog when the module is absent, but that is a RUNTIME filter: a static
80
+ * `import` of the handler still pins the file into the TYPE graph, so an OSS
81
+ * strip that deletes it reds `tsc`. A literal `await import()` does not help —
82
+ * `import()` defers evaluation, not resolution, so `tsc` resolves the specifier
83
+ * either way and reports TS2307 once the file is gone.
84
+ *
85
+ * The specifier therefore comes from this table rather than being written
86
+ * inline, which is what makes it non-literal at the call site and so
87
+ * unresolvable by `tsc`. The cast in {@link resolveHandler} is the load-bearing
88
+ * line: `import(nonLiteral)` yields `any`, and the cast is the only thing
89
+ * restoring a type at the module boundary.
90
+ *
91
+ * Every entry here is a slice file the strip deletes; every entry's catalog
92
+ * row carries the matching `module` tag, so `shouldRegister` and this table
93
+ * appear or vanish together.
94
+ */
95
+ const MODULE_TOOL_ENTRIES: Record<
96
+ string,
97
+ { readonly specifier: string; readonly exportName: string }
98
+ > = {
99
+ // #4416 — the scheduling twins of GET /v1/schedule/availability and
100
+ // POST /v1/schedule/book. Anonymous, identity-blind, credential-free: the
101
+ // scheduling credential lives in apps/api, which these reach via api-client.
102
+ list_availability: {
103
+ specifier: "./tools/list_availability.js",
104
+ exportName: "ailkListAvailability",
105
+ },
106
+ get_event_meta: {
107
+ specifier: "./tools/get_event_meta.js",
108
+ exportName: "ailkGetEventMeta",
109
+ },
110
+ create_booking: {
111
+ specifier: "./tools/create_booking.js",
112
+ exportName: "ailkCreateBooking",
113
+ },
68
114
  // D14a Stage 1 Unit C (#55): Paid-tier content write surface. Handlers
69
115
  // self-gate via requirePaidTier and return canonical error bodies (no throw)
70
116
  // for license / validation / conflict / not-found cases — see
71
117
  // apps/mcp/src/lib/errors.ts. Catalog entries carry tier: "pro" so agents
72
118
  // see the gating before invoking via list_tools.
73
- create_page: (input) => ailkCreatePage(input as never),
74
- update_page: (input) => ailkUpdatePage(input as never),
75
- delete_page: (input) => ailkDeletePage(input as never),
119
+ create_page: { specifier: "./tools/create_page.js", exportName: "ailkCreatePage" },
120
+ update_page: { specifier: "./tools/update_page.js", exportName: "ailkUpdatePage" },
121
+ delete_page: { specifier: "./tools/delete_page.js", exportName: "ailkDeletePage" },
76
122
  };
77
123
 
124
+ /**
125
+ * Resolves a tool name to its handler: the statically-imported map first, then
126
+ * the module-conditional table. Called from inside the already-`async`
127
+ * `server.tool` callback so {@link createMcpServer} stays synchronous — seven
128
+ * files call it that way, one of them production (`src/index.ts`), and making
129
+ * it async would rewrite all seven for no gain.
130
+ */
131
+ async function resolveHandler(name: string): Promise<ToolHandler> {
132
+ const direct = HANDLERS[name];
133
+ if (direct) return direct;
134
+
135
+ const entry = MODULE_TOOL_ENTRIES[name];
136
+ if (!entry) {
137
+ throw new Error(`MCP catalog drift: no handler for tool '${name}'`);
138
+ }
139
+ const mod = (await import(entry.specifier)) as Record<string, ToolHandler | undefined>;
140
+ const fn = mod[entry.exportName];
141
+ if (!fn) {
142
+ throw new Error(
143
+ `MCP slice drift: '${entry.specifier}' does not export '${entry.exportName}'`,
144
+ );
145
+ }
146
+ return fn;
147
+ }
148
+
78
149
  /**
79
150
  * Tool descriptor returned by {@link listRegisteredTools}. A minimal shape
80
151
  * sufficient for callers that need to assert which tools are registered
@@ -86,10 +157,25 @@ export interface RegisteredToolDescriptor {
86
157
  description: string;
87
158
  }
88
159
 
160
+ /**
161
+ * Whether a catalog registration entry should register on this build/boot —
162
+ * the OSS-disabled filter (static per-name set) AND the module-presence
163
+ * filter (S1, #5088): a tool tagged with `module` (e.g. `create_booking`
164
+ * carrying `module: "scheduling"`) registers only when
165
+ * {@link isModuleEnabled} says its module is present, the same way its
166
+ * `apps/api` HTTP route is module-conditional in `server.ts` there — so a
167
+ * tool and its route appear or vanish together, holding API↔MCP parity.
168
+ */
169
+ function shouldRegister(reg: { name: string; module?: string }): boolean {
170
+ if (OSS_DISABLED_TOOLS.has(reg.name)) return false;
171
+ if (reg.module && !isModuleEnabled(reg.module)) return false;
172
+ return true;
173
+ }
174
+
89
175
  /**
90
176
  * Returns the list of tools that {@link createMcpServer} will register, after
91
- * the OSS catalog filter ({@link OSS_DISABLED_TOOLS}) is applied. Pure — no
92
- * server instance is constructed.
177
+ * the OSS catalog filter ({@link OSS_DISABLED_TOOLS}) and the module-presence
178
+ * filter are applied. Pure — no server instance is constructed.
93
179
  *
94
180
  * Consumed by `apps/mcp/__tests__/purchase-disabled.test.ts` to assert that
95
181
  * disabled tools are absent from the registered catalog. If a future change
@@ -98,20 +184,22 @@ export interface RegisteredToolDescriptor {
98
184
  */
99
185
  export function listRegisteredTools(): RegisteredToolDescriptor[] {
100
186
  return getMcpToolRegistrations()
101
- .filter((reg) => !OSS_DISABLED_TOOLS.has(reg.name))
187
+ .filter(shouldRegister)
102
188
  .map((reg) => ({ name: reg.name, description: reg.description }));
103
189
  }
104
190
 
105
191
  /**
106
192
  * Creates a configured McpServer with all AILK tools registered, less any
107
- * tool name in {@link OSS_DISABLED_TOOLS}. The disabled filter is applied
108
- * defensively here even though the upstream `getMcpToolRegistrations()`
109
- * already excludes them a belt-and-braces guard so a future regression in
110
- * the metadata package can't accidentally re-register a disabled tool.
193
+ * tool name in {@link OSS_DISABLED_TOOLS} and less any tool whose `module`
194
+ * is not enabled on this build (S1, #5088; see {@link shouldRegister}). The
195
+ * disabled filter is applied defensively here even though the upstream
196
+ * `getMcpToolRegistrations()` already excludes them a belt-and-braces
197
+ * guard so a future regression in the metadata package can't accidentally
198
+ * re-register a disabled tool.
111
199
  *
112
200
  * Re-enable criteria for each disabled tool live at
113
201
  * `docs/internal/roadmaps/phase-3-readiness.md`. S15a (issue #610) is the
114
- * source of this filter.
202
+ * source of the disabled-tool filter.
115
203
  */
116
204
  export function createMcpServer(): McpServer {
117
205
  const server = new McpServer({
@@ -120,14 +208,17 @@ export function createMcpServer(): McpServer {
120
208
  });
121
209
 
122
210
  for (const reg of getMcpToolRegistrations()) {
123
- if (OSS_DISABLED_TOOLS.has(reg.name)) {
211
+ if (!shouldRegister(reg)) {
124
212
  continue;
125
213
  }
126
- const handler = HANDLERS[reg.name];
127
- if (!handler) {
214
+ // Boot-time drift detection is preserved: a registered name must be
215
+ // resolvable from ONE of the two tables, checked here rather than at first
216
+ // invocation, so a catalog/handler mismatch still fails at startup.
217
+ if (!HANDLERS[reg.name] && !MODULE_TOOL_ENTRIES[reg.name]) {
128
218
  throw new Error(`MCP catalog drift: no handler for tool '${reg.name}'`);
129
219
  }
130
220
  server.tool(reg.name, reg.description, reg.inputShape, async (input) => {
221
+ const handler = await resolveHandler(reg.name);
131
222
  const result = await handler(input);
132
223
  return {
133
224
  content: [{ type: "text", text: JSON.stringify(result) }],
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * capture_lead — capture a contact-form lead.
3
3
  *
4
- * Validates input via Zod, forwards to POST /api/leads/submit via
4
+ * Validates input via Zod, forwards to POST /v1/leads/submit via
5
5
  * @working-theory/api-client, and shapes the response into the MCP output schema.
6
6
  * No business logic — orchestration lives in apps/api B6.
7
7
  *