create-ailk 0.4.0 → 0.6.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 (396) hide show
  1. package/README.md +55 -8
  2. package/component-catalog.json +12248 -0
  3. package/component-catalog.md +193 -68
  4. package/dist/cli.js +29 -1
  5. package/dist/derive-architecture.js +5 -2
  6. package/dist/lib/api-boot-assertion.d.ts +46 -0
  7. package/dist/lib/api-boot-assertion.js +70 -0
  8. package/dist/lib/extract-module-bundle.d.ts +50 -2
  9. package/dist/lib/extract-module-bundle.js +223 -15
  10. package/dist/lib/fetch-guard.d.ts +28 -0
  11. package/dist/lib/fetch-guard.js +101 -0
  12. package/dist/lib/fetch-module.js +4 -0
  13. package/dist/lib/fetch-template.js +4 -0
  14. package/dist/lib/import-graph.d.ts +29 -0
  15. package/dist/lib/import-graph.js +140 -0
  16. package/dist/module-architecture.d.ts +60 -8
  17. package/dist/module-architecture.js +587 -242
  18. package/dist/parse-args.js +6 -2
  19. package/dist/programmatic.d.ts +10 -0
  20. package/dist/programmatic.js +134 -19
  21. package/dist/site-chrome.d.ts +80 -0
  22. package/dist/site-chrome.js +135 -0
  23. package/dist/surfaces.d.ts +21 -6
  24. package/dist/surfaces.js +209 -36
  25. package/dist/template-manifest.d.ts +88 -3
  26. package/dist/template-manifest.js +281 -29
  27. package/dist/template-names.d.ts +1 -1
  28. package/dist/template-names.js +2 -0
  29. package/package.json +10 -2
  30. package/site-templates/_base/ailk-template.json +6 -0
  31. package/site-templates/_base/content/en/Pricing/pricing.mdx +169 -0
  32. package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +351 -0
  33. package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +400 -0
  34. package/site-templates/marketing-site/ailk-template.json +11 -0
  35. package/site-templates/marketing-site/content/en/blog/05-this-vc-morning-routine-will-make-you-super-annoying.mdx +36 -0
  36. package/site-templates/marketing-site/provenance.json +4 -0
  37. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +288 -0
  38. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +286 -0
  39. package/site-templates/marketing-site-free/ailk-template.json +2 -1
  40. package/site-templates/minimalism-site/TEMPLATE-MANIFEST.json +279 -0
  41. package/site-templates/minimalism-site/TEMPLATE-MANIFEST.md +336 -0
  42. package/site-templates/minimalism-site/ailk-template.json +22 -0
  43. package/site-templates/minimalism-site/assets/avatar-placeholder-1.svg +7 -0
  44. package/site-templates/minimalism-site/assets/avatar-placeholder-2.svg +7 -0
  45. package/site-templates/minimalism-site/assets/avatar-placeholder-3.svg +7 -0
  46. package/site-templates/minimalism-site/assets/avatar-placeholder-4.svg +7 -0
  47. package/site-templates/minimalism-site/assets/avatar-placeholder-5.svg +7 -0
  48. package/site-templates/minimalism-site/assets/avatar-placeholder-6.svg +7 -0
  49. package/site-templates/minimalism-site/assets/hero-backdrop.svg +17 -0
  50. package/site-templates/minimalism-site/assets/logo-placeholder-1.svg +6 -0
  51. package/site-templates/minimalism-site/assets/logo-placeholder-2.svg +6 -0
  52. package/site-templates/minimalism-site/assets/logo-placeholder-3.svg +6 -0
  53. package/site-templates/minimalism-site/assets/wordmark-placeholder.svg +28 -0
  54. package/site-templates/minimalism-site/content/en/AboutPage/about.mdx +110 -0
  55. package/site-templates/minimalism-site/content/en/ContactPage/contact.mdx +68 -0
  56. package/site-templates/minimalism-site/content/en/FAQPage/faq.mdx +57 -0
  57. package/site-templates/minimalism-site/content/en/HomePage/home.mdx +244 -0
  58. package/site-templates/minimalism-site/content/en/LandingPage/referral.mdx +77 -0
  59. package/site-templates/minimalism-site/content/en/LandingPage/reports.mdx +73 -0
  60. package/site-templates/minimalism-site/content/en/ReportPage/annual-letter-2024.mdx +52 -0
  61. package/site-templates/minimalism-site/content/en/ReportPage/field-notes-2023.mdx +52 -0
  62. package/site-templates/minimalism-site/content/en/ReportPage/platform-shift-2025.mdx +52 -0
  63. package/site-templates/minimalism-site/content/en/ReportPage/state-of-the-market-2026.mdx +52 -0
  64. package/site-templates/minimalism-site/content/en/Service/first-engagement.mdx +38 -0
  65. package/site-templates/minimalism-site/content/en/Service/second-engagement.mdx +38 -0
  66. package/site-templates/minimalism-site/content/en/Service/third-engagement.mdx +38 -0
  67. package/site-templates/minimalism-site/content/en/ServicesList/services.mdx +73 -0
  68. package/site-templates/minimalism-site/flows/referral.flow.json +71 -0
  69. package/site-templates/minimalism-site/provenance.json +4 -0
  70. package/site-templates/saas-product/TEMPLATE-MANIFEST.md +61 -0
  71. package/site-templates/saas-product/ailk-template.json +16 -0
  72. package/site-templates/saas-product/provenance.json +10 -0
  73. package/site-templates/simple-service/TEMPLATE-MANIFEST.json +240 -0
  74. package/site-templates/simple-service/TEMPLATE-MANIFEST.md +278 -0
  75. package/site-templates/simple-service/ailk-template.json +10 -0
  76. package/site-templates/simple-service/content/en/AboutPage/about.mdx +38 -0
  77. package/site-templates/simple-service/content/en/ContactPage/contact.mdx +67 -0
  78. package/site-templates/simple-service/content/en/FAQPage/faq.mdx +56 -0
  79. package/site-templates/simple-service/content/en/HomePage/home.mdx +85 -0
  80. package/site-templates/simple-service/content/en/Service/landscape-design.mdx +40 -0
  81. package/site-templates/simple-service/content/en/Service/lawn-maintenance.mdx +40 -0
  82. package/site-templates/simple-service/content/en/Service/seasonal-cleanup.mdx +40 -0
  83. package/site-templates/simple-service/content/en/ServicesList/services.mdx +72 -0
  84. package/site-templates/simple-service/provenance.json +4 -0
  85. package/templates/.claude/skills/README.md +16 -25
  86. package/templates/.env.example +102 -12
  87. package/templates/.github/workflows/ci.yml +48 -0
  88. package/templates/README.md +3 -1
  89. package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-31.552Z-fallback-1789916251590-037e9005.json +8 -0
  90. package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-33.840Z-fallback-1789916253842-b52659e9.json +12 -0
  91. package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-33.844Z-fallback-1789916253845-6b266f8c.json +12 -0
  92. package/templates/apps/api/.env.example +13 -0
  93. package/templates/apps/api/CLAUDE.md +29 -2
  94. package/templates/apps/api/eslint.config.mjs +69 -0
  95. package/templates/apps/api/jest.config.cjs +3 -0
  96. package/templates/apps/api/package.json +7 -2
  97. package/templates/apps/api/src/__tests__/commerce-claim-registration.test.ts +407 -0
  98. package/templates/apps/api/src/__tests__/module-exclusion.test.ts +89 -9
  99. package/templates/apps/api/src/__tests__/review-surface-registration.test.ts +167 -0
  100. package/templates/apps/api/src/__tests__/server.test.ts +471 -0
  101. package/templates/apps/api/src/__tests__/vercel-handler.test.ts +45 -0
  102. package/templates/apps/api/src/__tests__/workspace-module-registration.test.ts +10 -1
  103. package/templates/apps/api/src/config/index.ts +62 -0
  104. package/templates/apps/api/src/config/modules.ts +7 -3
  105. package/templates/apps/api/src/lib/__mocks__/prisma.ts +241 -2
  106. package/templates/apps/api/src/lib/__tests__/auth-email-budget.test.ts +143 -0
  107. package/templates/apps/api/src/lib/__tests__/content-adapter.test.ts +23 -0
  108. package/templates/apps/api/src/lib/__tests__/deliverable-asset-store.test.ts +125 -0
  109. package/templates/apps/api/src/lib/__tests__/error-serializer.test.ts +20 -0
  110. package/templates/apps/api/src/lib/__tests__/require-commerce-claim.test.ts +317 -0
  111. package/templates/apps/api/src/lib/__tests__/site.test.ts +238 -0
  112. package/templates/apps/api/src/lib/__tests__/stripe.test.ts +75 -0
  113. package/templates/apps/api/src/lib/auth-email-budget.ts +160 -0
  114. package/templates/apps/api/src/lib/content-adapter.ts +57 -31
  115. package/templates/apps/api/src/lib/deliverable-asset-store.ts +76 -0
  116. package/templates/apps/api/src/lib/error-serializer.ts +23 -3
  117. package/templates/apps/api/src/lib/outbound-http.ts +7 -2
  118. package/templates/apps/api/src/lib/require-commerce-claim.ts +250 -0
  119. package/templates/apps/api/src/lib/site.ts +208 -9
  120. package/templates/apps/api/src/lib/stripe.ts +66 -0
  121. package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +521 -0
  122. package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +150 -0
  123. package/templates/apps/api/src/middleware/default-tenant.ts +136 -0
  124. package/templates/apps/api/src/middleware/rate-limit.ts +7 -1
  125. package/templates/apps/api/src/openapi/spec.ts +543 -2
  126. package/templates/apps/api/src/routes/auth/__tests__/auth-user.functional.test.ts +48 -0
  127. package/templates/apps/api/src/routes/auth/__tests__/transactional-email.test.ts +309 -0
  128. package/templates/apps/api/src/routes/auth/__tests__/user.test.ts +123 -0
  129. package/templates/apps/api/src/routes/auth/transactional-email.ts +273 -0
  130. package/templates/apps/api/src/routes/auth/user.ts +61 -0
  131. package/templates/apps/api/src/routes/checkout/__tests__/session.test.ts +434 -0
  132. package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +888 -0
  133. package/templates/apps/api/src/routes/checkout/index.ts +34 -0
  134. package/templates/apps/api/src/routes/checkout/session.ts +139 -0
  135. package/templates/apps/api/src/routes/checkout/sessions.ts +197 -0
  136. package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +554 -0
  137. package/templates/apps/api/src/routes/content/__tests__/list-pages.test.ts +108 -0
  138. package/templates/apps/api/src/routes/content/__tests__/search.test.ts +450 -0
  139. package/templates/apps/api/src/routes/content/get-page.ts +94 -12
  140. package/templates/apps/api/src/routes/content/index.ts +13 -73
  141. package/templates/apps/api/src/routes/content/list-pages.ts +15 -3
  142. package/templates/apps/api/src/routes/content/rate-limit.ts +15 -6
  143. package/templates/apps/api/src/routes/content/search.ts +129 -0
  144. package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +447 -31
  145. package/templates/apps/api/src/routes/leads/__tests__/list.test.ts +171 -0
  146. package/templates/apps/api/src/routes/leads/index.ts +100 -13
  147. package/templates/apps/api/src/routes/leads/list.ts +104 -0
  148. package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +51 -0
  149. package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +351 -0
  150. package/templates/apps/api/src/routes/project-listings/__tests__/founder-identity.test.ts +51 -0
  151. package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +5 -2
  152. package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +4 -1
  153. package/templates/apps/api/src/routes/project-listings/__tests__/resume-email.test.ts +58 -0
  154. package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +107 -6
  155. package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +87 -2
  156. package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +51 -0
  157. package/templates/apps/api/src/routes/project-listings/start.ts +5 -1
  158. package/templates/apps/api/src/routes/requests/__tests__/confirm.test.ts +686 -0
  159. package/templates/apps/api/src/routes/requests/confirm.ts +601 -0
  160. package/templates/apps/api/src/routes/requests/index.ts +54 -0
  161. package/templates/apps/api/src/routes/webhooks/README.md +265 -0
  162. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-dunning.test.ts +749 -0
  163. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +330 -0
  164. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +262 -0
  165. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +285 -0
  166. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +213 -0
  167. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-request-paid.test.ts +301 -0
  168. package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +648 -0
  169. package/templates/apps/api/src/routes/webhooks/index.ts +89 -0
  170. package/templates/apps/api/src/routes/webhooks/stripe.ts +1399 -0
  171. package/templates/apps/api/src/server.ts +521 -55
  172. package/templates/apps/api/src/services/__tests__/abandon-email.test.ts +39 -0
  173. package/templates/apps/api/src/services/__tests__/aeo-score.test.ts +217 -40
  174. package/templates/apps/api/src/services/__tests__/audit-log.test.ts +620 -0
  175. package/templates/apps/api/src/services/__tests__/consent.test.ts +2 -0
  176. package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +300 -5
  177. package/templates/apps/api/src/services/__tests__/deliverable-resend.test.ts +20 -0
  178. package/templates/apps/api/src/services/__tests__/dunning-policy.test.ts +368 -0
  179. package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +408 -0
  180. package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +160 -0
  181. package/templates/apps/api/src/services/__tests__/leads-inbox.test.ts +329 -0
  182. package/templates/apps/api/src/services/__tests__/listing-config.test.ts +166 -0
  183. package/templates/apps/api/src/services/__tests__/project-listings.fence.test.ts +154 -0
  184. package/templates/apps/api/src/services/__tests__/public-site-resolver.test.ts +137 -0
  185. package/templates/apps/api/src/services/__tests__/request-confirm.test.ts +444 -0
  186. package/templates/apps/api/src/services/__tests__/revalidation.test.ts +238 -0
  187. package/templates/apps/api/src/services/__tests__/verified-email.test.ts +78 -0
  188. package/templates/apps/api/src/services/abandon-email.ts +8 -2
  189. package/templates/apps/api/src/services/aeo-score.ts +49 -12
  190. package/templates/apps/api/src/services/audit-log.ts +668 -0
  191. package/templates/apps/api/src/services/consent.ts +18 -0
  192. package/templates/apps/api/src/services/default-tenant.ts +300 -0
  193. package/templates/apps/api/src/services/deliverable-fulfillment.ts +141 -13
  194. package/templates/apps/api/src/services/dunning-policy.ts +294 -0
  195. package/templates/apps/api/src/services/entitlements.ts +80 -0
  196. package/templates/apps/api/src/services/lead-promotion.ts +160 -0
  197. package/templates/apps/api/src/services/lead-routing.ts +161 -2
  198. package/templates/apps/api/src/services/leads-inbox.ts +284 -0
  199. package/templates/apps/api/src/services/listing-config.ts +27 -9
  200. package/templates/apps/api/src/services/owner-workspaces.ts +132 -0
  201. package/templates/apps/api/src/services/project-listings.ts +201 -6
  202. package/templates/apps/api/src/services/public-site-resolver.ts +106 -0
  203. package/templates/apps/api/src/services/request-confirm.ts +646 -0
  204. package/templates/apps/api/src/services/revalidation.ts +173 -0
  205. package/templates/apps/api/src/services/verified-email.ts +71 -0
  206. package/templates/apps/api/src/vercel-handler.ts +75 -35
  207. package/templates/apps/mcp/.env.example +17 -0
  208. package/templates/apps/mcp/CLAUDE.md +58 -25
  209. package/templates/apps/mcp/__tests__/aeo-cli-parity.test.ts +6 -0
  210. package/templates/apps/mcp/__tests__/audit.test.ts +137 -8
  211. package/templates/apps/mcp/__tests__/catalog-drift.test.ts +79 -36
  212. package/templates/apps/mcp/__tests__/get_offerings.test.ts +111 -2
  213. package/templates/apps/mcp/__tests__/get_pricing.test.ts +82 -2
  214. package/templates/apps/mcp/__tests__/http-transport.test.ts +541 -2
  215. package/templates/apps/mcp/__tests__/index.test.ts +39 -0
  216. package/templates/apps/mcp/__tests__/lib/audit-single-page-readiness-scan.test.ts +143 -0
  217. package/templates/apps/mcp/__tests__/lib/commerce-claim.test.ts +111 -0
  218. package/templates/apps/mcp/__tests__/lib/content-adapter-parity.test.ts +65 -0
  219. package/templates/apps/mcp/__tests__/lib/content-adapter.test.ts +188 -0
  220. package/templates/apps/mcp/__tests__/lib/fail-closed-tools.test.ts +165 -0
  221. package/templates/apps/mcp/__tests__/lib/public-workspace.test.ts +124 -0
  222. package/templates/apps/mcp/__tests__/list_pages.test.ts +116 -0
  223. package/templates/apps/mcp/__tests__/module-exclusion.test.ts +7 -1
  224. package/templates/apps/mcp/__tests__/parity.test.ts +12 -9
  225. package/templates/apps/mcp/__tests__/purchase-disabled.test.ts +89 -22
  226. package/templates/apps/mcp/__tests__/read_content.test.ts +322 -1
  227. package/templates/apps/mcp/__tests__/search_content.test.ts +105 -2
  228. package/templates/apps/mcp/__tests__/server.test.ts +67 -14
  229. package/templates/apps/mcp/__tests__/start_purchase.test.ts +252 -0
  230. package/templates/apps/mcp/__tests__/tools/{scaffold.test.ts → create_project.test.ts} +23 -23
  231. package/templates/apps/mcp/__tests__/tools/purchase.test.ts +300 -123
  232. package/templates/apps/mcp/eslint.config.mjs +69 -0
  233. package/templates/apps/mcp/jest.config.cjs +13 -0
  234. package/templates/apps/mcp/package.json +8 -2
  235. package/templates/apps/mcp/src/abuse/index.ts +159 -24
  236. package/templates/apps/mcp/src/config/modules.ts +10 -5
  237. package/templates/apps/mcp/src/http-transport.ts +102 -29
  238. package/templates/apps/mcp/src/index.ts +73 -11
  239. package/templates/apps/mcp/src/lib/audit-single-page.ts +90 -3
  240. package/templates/apps/mcp/src/lib/commerce-claim.ts +94 -0
  241. package/templates/apps/mcp/src/lib/content-adapter.ts +113 -0
  242. package/templates/apps/mcp/src/lib/empty-content-adapter.ts +139 -0
  243. package/templates/apps/mcp/src/lib/prisma.ts +39 -0
  244. package/templates/apps/mcp/src/lib/public-workspace.ts +133 -0
  245. package/templates/apps/mcp/src/lib/stripe-tax-signal.ts +59 -0
  246. package/templates/apps/mcp/src/server.ts +61 -37
  247. package/templates/apps/mcp/src/tools/audit.ts +26 -2
  248. package/templates/apps/mcp/src/tools/{scaffold.ts → create_project.ts} +3 -3
  249. package/templates/apps/mcp/src/tools/get_business_hours.ts +4 -3
  250. package/templates/apps/mcp/src/tools/get_faq.ts +4 -3
  251. package/templates/apps/mcp/src/tools/get_locations.ts +5 -3
  252. package/templates/apps/mcp/src/tools/get_offerings.ts +16 -4
  253. package/templates/apps/mcp/src/tools/get_pricing.ts +25 -6
  254. package/templates/apps/mcp/src/tools/get_site_info.ts +4 -3
  255. package/templates/apps/mcp/src/tools/index.ts +9 -7
  256. package/templates/apps/mcp/src/tools/list_pages.ts +4 -3
  257. package/templates/apps/mcp/src/tools/purchase.ts +128 -45
  258. package/templates/apps/mcp/src/tools/read_content.ts +20 -6
  259. package/templates/apps/mcp/src/tools/search_content.ts +11 -4
  260. package/templates/apps/web/.env.example +74 -8
  261. package/templates/apps/web/CLAUDE.md +46 -0
  262. package/templates/apps/web/app/.well-known/mcp.json/build-manifest.ts +18 -2
  263. package/templates/apps/web/app/.well-known/mcp.json/route.ts +11 -3
  264. package/templates/apps/web/app/[locale]/(auth)/__tests__/auth-pages.test.tsx +282 -0
  265. package/templates/apps/web/app/[locale]/(auth)/__tests__/password-reset.test.tsx +515 -0
  266. package/templates/apps/web/app/[locale]/(auth)/forgot-password/forgot-password-card.tsx +89 -0
  267. package/templates/apps/web/app/[locale]/(auth)/forgot-password/page.tsx +109 -0
  268. package/templates/apps/web/app/[locale]/(auth)/layout.tsx +92 -0
  269. package/templates/apps/web/app/[locale]/(auth)/reset-password/page.tsx +135 -0
  270. package/templates/apps/web/app/[locale]/(auth)/reset-password/reset-password-card.tsx +109 -0
  271. package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/__tests__/page.test.tsx +48 -2
  272. package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/page.tsx +20 -6
  273. package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-card.tsx +125 -0
  274. package/templates/apps/web/app/[locale]/(auth)/sign-up/page.tsx +96 -0
  275. package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up-card.tsx +101 -0
  276. package/templates/apps/web/app/[locale]/(auth)/use-auth-card-flow.ts +102 -0
  277. package/templates/apps/web/app/[locale]/(auth)/verify-email/page.tsx +87 -0
  278. package/templates/apps/web/app/[locale]/(authed)/__tests__/gate.test.tsx +20 -10
  279. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +84 -0
  280. package/templates/apps/web/app/[locale]/(authed)/account/actions.ts +58 -0
  281. package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +67 -14
  282. package/templates/apps/web/app/[locale]/(authed)/layout.tsx +19 -2
  283. package/templates/apps/web/app/[locale]/(authed)/leads/__tests__/page.test.tsx +181 -0
  284. package/templates/apps/web/app/[locale]/(authed)/leads/components/LeadsTable.tsx +137 -0
  285. package/templates/apps/web/app/[locale]/(authed)/leads/page.tsx +111 -0
  286. package/templates/apps/web/app/[locale]/(authed)/sign-in-gate.tsx +1 -1
  287. package/templates/apps/web/app/[locale]/[slug]/__tests__/page.test.tsx +111 -0
  288. package/templates/apps/web/app/[locale]/[slug]/page.tsx +65 -0
  289. package/templates/apps/web/app/[locale]/cancellation/page.tsx +44 -0
  290. package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +1 -1
  291. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.permalink.test.tsx +1 -1
  292. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.test.tsx +6 -5
  293. package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +1 -1
  294. package/templates/apps/web/app/[locale]/layout.tsx +308 -65
  295. package/templates/apps/web/app/[locale]/purchase/__tests__/cancel.test.tsx +162 -0
  296. package/templates/apps/web/app/[locale]/purchase/__tests__/module-scope.test.ts +68 -0
  297. package/templates/apps/web/app/[locale]/purchase/__tests__/success.test.tsx +361 -0
  298. package/templates/apps/web/app/[locale]/purchase/cancel/page.tsx +115 -0
  299. package/templates/apps/web/app/[locale]/purchase/success/page.tsx +307 -0
  300. package/templates/apps/web/app/[locale]/returns/page.tsx +44 -0
  301. package/templates/apps/web/app/[locale]/shipping/page.tsx +44 -0
  302. package/templates/apps/web/app/api/revalidate/__tests__/route.test.ts +670 -0
  303. package/templates/apps/web/app/api/revalidate/route.ts +194 -0
  304. package/templates/apps/web/auth.ts +407 -8
  305. package/templates/apps/web/components/google-analytics.tsx +18 -1
  306. package/templates/apps/web/eslint.config.mjs +69 -0
  307. package/templates/apps/web/jest.config.cjs +15 -19
  308. package/templates/apps/web/lib/__tests__/auth-email.test.ts +214 -0
  309. package/templates/apps/web/lib/__tests__/auth-routes.test.ts +74 -0
  310. package/templates/apps/web/lib/__tests__/meeting-slugs.test.ts +72 -0
  311. package/templates/apps/web/lib/__tests__/structured-data.test.tsx +50 -6
  312. package/templates/apps/web/lib/auth-email.ts +318 -0
  313. package/templates/apps/web/lib/auth-env.ts +85 -0
  314. package/templates/apps/web/lib/auth-routes.ts +55 -0
  315. package/templates/apps/web/lib/meeting-slugs.ts +53 -0
  316. package/templates/apps/web/lib/nav-config.ts +27 -7
  317. package/templates/apps/web/lib/server-api.ts +1 -1
  318. package/templates/apps/web/lib/site-chrome.ts +29 -0
  319. package/templates/apps/web/lib/telemetry-scrub.ts +50 -0
  320. package/templates/apps/web/messages/en.json +216 -2
  321. package/templates/apps/web/next.config.mjs +20 -0
  322. package/templates/apps/web/package.json +8 -3
  323. package/templates/apps/web/sentry.client.config.ts +131 -0
  324. package/templates/apps/web/sentry.edge.config.ts +72 -0
  325. package/templates/apps/web/sentry.server.config.ts +72 -0
  326. package/templates/apps/web/src/lib/commerce-claim.ts +33 -0
  327. package/templates/apps/web/src/lib/commerce-sections.ts +61 -0
  328. package/templates/apps/web/src/lib/routes.ts +13 -18
  329. package/templates/brand_assets/README.md +24 -0
  330. package/templates/content/en/AboutPage/about.mdx +160 -15
  331. package/templates/content/en/CancellationPolicyPage/cancellation.mdx +108 -0
  332. package/templates/content/en/ContactPage/contact.mdx +90 -13
  333. package/templates/content/en/FAQPage/faq.mdx +62 -22
  334. package/templates/content/en/HomePage/home.mdx +244 -20
  335. package/templates/content/en/LandingPage/blog.mdx +83 -0
  336. package/templates/content/en/LandingPage/features.mdx +103 -0
  337. package/templates/content/en/LandingPage/how-it-works.mdx +143 -0
  338. package/templates/content/en/ReturnsPolicyPage/returns.mdx +108 -0
  339. package/templates/content/en/ShippingPolicyPage/shipping.mdx +108 -0
  340. package/templates/database/CHANGELOG.md +149 -0
  341. package/templates/database/README.md +10 -3
  342. package/templates/database/__tests__/appointment.db.test.ts +106 -0
  343. package/templates/database/__tests__/artifact-content-store.db.test.ts +316 -0
  344. package/templates/database/__tests__/composite-fence.db.test.ts +206 -0
  345. package/templates/database/__tests__/fence-fan-out.db.test.ts +711 -0
  346. package/templates/database/__tests__/fixtures/appointment-create-probe.ts +79 -0
  347. package/templates/database/__tests__/fixtures/project-listing-consent-probe.ts +100 -0
  348. package/templates/database/__tests__/flow-workspace-key.db.test.ts +263 -0
  349. package/templates/database/__tests__/prefixed-ids.db.test.ts +85 -9
  350. package/templates/database/__tests__/project-listing-consent-at.db.test.ts +111 -0
  351. package/templates/database/__tests__/seed.test.ts +1 -1
  352. package/templates/database/__tests__/tenancy.test.ts +141 -0
  353. package/templates/database/config/schema.prisma +79 -2
  354. package/templates/database/content/schema.prisma +87 -11
  355. package/templates/database/eslint.config.mjs +69 -0
  356. package/templates/database/inbox/schema.prisma +452 -9
  357. package/templates/database/jest.config.cjs +5 -3
  358. package/templates/database/migrations/20260915120000_project_listing_solution/migration.sql +15 -0
  359. package/templates/database/migrations/20260916120000_artifact_content_store/migration.sql +158 -0
  360. package/templates/database/migrations/20260917120000_callback_request_confirm/migration.sql +40 -0
  361. package/templates/database/migrations/20260918120000_add_appointment/migration.sql +67 -0
  362. package/templates/database/migrations/20260918120000_asset_metadata_columns/migration.sql +70 -0
  363. package/templates/database/migrations/20260919120000_flow_workspace_key/migration.sql +181 -0
  364. package/templates/database/migrations/20260919130000_flow_definition_admin_columns/migration.sql +172 -0
  365. package/templates/database/migrations/20260919130000_workspace_meetings/migration.sql +41 -0
  366. package/templates/database/migrations/20260919140000_inbox_workspace_key/migration.sql +185 -0
  367. package/templates/database/migrations/20260920120000_lead_handled_at/migration.sql +70 -0
  368. package/templates/database/migrations/20260920130000_lead_workspace_source_index/migration.sql +57 -0
  369. package/templates/database/migrations/20260921120000_deliverable_grant_workspace_key/migration.sql +68 -0
  370. package/templates/database/migrations/20260922120000_tenancy_fence/migration.sql +1067 -0
  371. package/templates/database/migrations/20260923120000_listing_capturing_magnet/migration.sql +210 -0
  372. package/templates/database/package.json +7 -3
  373. package/templates/database/seed.ts +1 -1
  374. package/templates/database/tenancy.ts +86 -1
  375. package/templates/database/tsconfig.json +1 -0
  376. package/templates/package.json +1 -14
  377. package/versions.json +52 -0
  378. package/site-templates/marketing-site-free/content/en/Pricing/pricing.mdx +0 -103
  379. package/templates/apps/api/src/routes/content/__tests__/create.test.ts +0 -253
  380. package/templates/apps/api/src/routes/content/__tests__/delete.test.ts +0 -215
  381. package/templates/apps/api/src/routes/content/__tests__/update.test.ts +0 -266
  382. package/templates/apps/api/src/routes/content/create.ts +0 -115
  383. package/templates/apps/api/src/routes/content/delete.ts +0 -82
  384. package/templates/apps/api/src/routes/content/update.ts +0 -112
  385. package/templates/apps/mcp/__tests__/create_page.test.ts +0 -202
  386. package/templates/apps/mcp/__tests__/delete_page.test.ts +0 -177
  387. package/templates/apps/mcp/__tests__/update_page.test.ts +0 -196
  388. package/templates/apps/mcp/src/tools/create_page.ts +0 -96
  389. package/templates/apps/mcp/src/tools/delete_page.ts +0 -74
  390. package/templates/apps/mcp/src/tools/update_page.ts +0 -92
  391. package/templates/apps/web/app/[locale]/sign-in/sign-in-card.tsx +0 -123
  392. /package/site-templates/{marketing-site-free → _base}/content/en/PrivacyPage/privacy.mdx +0 -0
  393. /package/site-templates/{marketing-site-free → _base}/content/en/TermsPage/terms.mdx +0 -0
  394. /package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/__tests__/next-path.test.ts +0 -0
  395. /package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/next-path.ts +0 -0
  396. /package/templates/apps/web/{src/components → components}/SiteNav.tsx +0 -0
@@ -0,0 +1,252 @@
1
+ /**
2
+ * `start_purchase` — the MCP half of the API↔MCP parity proof (#5971 D7),
3
+ * plus FR-1 and FR-2, rewritten for #6001's redirect fix.
4
+ *
5
+ * `apps/api` cannot import `apps/mcp` and vice versa, so neither side can
6
+ * call the other in a test. The evidence is the SHARED SEAM asserted from
7
+ * both ends — `CreateSessionPayload` / `CheckoutSessionResponse` in
8
+ * `packages/api-client/src/checkout.ts`:
9
+ *
10
+ * here — the tool calls `checkout.createSession` with EXACTLY the
11
+ * payload object the route validates, and its return value
12
+ * deep-equals `{ url }` from the seam;
13
+ * the API end — `apps/api/src/routes/checkout/__tests__/sessions.test.ts`
14
+ * § "parity with the payload the MCP tool builds (#5971)"
15
+ * feeds that same payload to the route and pins its reply.
16
+ *
17
+ * Idiom: `apps/mcp/__tests__/search_content.test.ts`, the worked example root
18
+ * `CLAUDE.md` § API ↔ MCP parity names.
19
+ *
20
+ * #6001: three security reviews on PR #5991 (two High, one Medium) found
21
+ * that an anonymous MCP caller could forward its own successUrl/cancelUrl to
22
+ * `checkout.createSession` — the payment succeeds so no fraud signal fires,
23
+ * making it a phishing-amplification open redirect off the seller's real
24
+ * checkout — and that `allowPromotionCodes` had no reason to be
25
+ * caller-controlled on an anonymous surface either. The tool now validates
26
+ * against `purchaseMcpInputSchema` (a strict productCode+quantity-only pick
27
+ * of the shared schema), so it can never build a payload containing a
28
+ * caller-supplied redirect: both `successUrl` and `cancelUrl` are ALWAYS
29
+ * derived from `AILK_BASE_URL`, and `allowPromotionCodes` is never forwarded.
30
+ *
31
+ * Nothing here reaches Stripe or a licence server (NFR-6).
32
+ */
33
+
34
+ import { checkout } from "@working-theory/api-client";
35
+
36
+ import {
37
+ __setMcpCommerceClaimForTest,
38
+ resetMcpCommerceClaim,
39
+ } from "../src/lib/commerce-claim.js";
40
+ import { ailkPurchase } from "../src/tools/purchase.js";
41
+
42
+ jest.mock("@working-theory/api-client", () => ({
43
+ checkout: { createSession: jest.fn() },
44
+ }));
45
+
46
+ const createSession = checkout.createSession as jest.MockedFunction<
47
+ typeof checkout.createSession
48
+ >;
49
+
50
+ const HOSTED_URL = "https://checkout.stripe.com/c/pay/cs_test_abc123";
51
+
52
+ function sessionCreated() {
53
+ return {
54
+ ok: true as const,
55
+ body: { sessionId: "cs_test_abc123", url: HOSTED_URL },
56
+ };
57
+ }
58
+
59
+ beforeEach(() => {
60
+ __setMcpCommerceClaimForTest("claimed");
61
+ process.env.AILK_BASE_URL = "https://example.com";
62
+ });
63
+
64
+ afterEach(() => {
65
+ jest.clearAllMocks();
66
+ resetMcpCommerceClaim();
67
+ delete process.env.AILK_BASE_URL;
68
+ });
69
+
70
+ describe("start_purchase — parity with the payload POST /v1/checkout/sessions takes (#5971, #6001)", () => {
71
+ it("calls checkout.createSession with EXACTLY the route's payload shape, redirects derived — never caller-supplied", async () => {
72
+ createSession.mockResolvedValueOnce(sessionCreated());
73
+
74
+ await ailkPurchase({
75
+ productCode: "pro-plan",
76
+ quantity: 2,
77
+ });
78
+
79
+ expect(createSession).toHaveBeenCalledTimes(1);
80
+ expect(createSession).toHaveBeenCalledWith({
81
+ productCode: "pro-plan",
82
+ quantity: 2,
83
+ successUrl:
84
+ "https://example.com/purchase/success?session_id={CHECKOUT_SESSION_ID}",
85
+ cancelUrl: "https://example.com/purchase/cancel",
86
+ });
87
+ });
88
+
89
+ it("omits quantity entirely when it is not supplied", async () => {
90
+ createSession.mockResolvedValueOnce(sessionCreated());
91
+
92
+ await ailkPurchase({ productCode: "pro-plan" });
93
+
94
+ const payload = createSession.mock.calls[0]![0];
95
+ expect(payload).not.toHaveProperty("quantity");
96
+ expect(payload).not.toHaveProperty("allowPromotionCodes");
97
+ });
98
+
99
+ it("derives both redirect URLs from AILK_BASE_URL — always, not only when omitted (#6001)", async () => {
100
+ createSession.mockResolvedValueOnce(sessionCreated());
101
+
102
+ await ailkPurchase({ productCode: "pro-plan" });
103
+
104
+ expect(createSession).toHaveBeenCalledWith({
105
+ productCode: "pro-plan",
106
+ successUrl:
107
+ "https://example.com/purchase/success?session_id={CHECKOUT_SESSION_ID}",
108
+ cancelUrl: "https://example.com/purchase/cancel",
109
+ });
110
+ });
111
+ });
112
+
113
+ describe("start_purchase — FR-1: the hosted Checkout URL", () => {
114
+ it("returns { url } deep-equal to the seam's url, and nothing else", async () => {
115
+ createSession.mockResolvedValueOnce(sessionCreated());
116
+
117
+ const result = await ailkPurchase({ productCode: "pro-plan" });
118
+
119
+ expect(result).toEqual({ url: HOSTED_URL });
120
+ });
121
+
122
+ it("does NOT surface the route's sessionId (D5 — it identifies a buyer's session)", async () => {
123
+ createSession.mockResolvedValueOnce(sessionCreated());
124
+
125
+ const result = await ailkPurchase({ productCode: "pro-plan" });
126
+
127
+ expect(result).not.toHaveProperty("sessionId");
128
+ });
129
+
130
+ it("the URL it returns is Stripe-hosted — nothing is charged in-tool", async () => {
131
+ createSession.mockResolvedValueOnce(sessionCreated());
132
+
133
+ const result = await ailkPurchase({ productCode: "pro-plan" });
134
+
135
+ expect((result as { url: string }).url).toMatch(
136
+ /^https:\/\/checkout\.stripe\.com\//,
137
+ );
138
+ });
139
+ });
140
+
141
+ describe("start_purchase — FR-2: an unknown offering", () => {
142
+ it("does NOT echo the caller's product code back (route invariant parity)", async () => {
143
+ createSession.mockResolvedValueOnce({
144
+ ok: false as const,
145
+ status: 404,
146
+ body: { error: "unknown-product-code" },
147
+ });
148
+
149
+ const result = await ailkPurchase({ productCode: "no-such-offering" });
150
+
151
+ // The HTTP twin states and tests "Error responses never echo user input";
152
+ // parity means the pair answers alike (#5971 security review, Low).
153
+ expect(JSON.stringify(result)).not.toContain("no-such-offering");
154
+ });
155
+
156
+ it("returns a typed not_found body rather than throwing", async () => {
157
+ createSession.mockResolvedValueOnce({
158
+ ok: false as const,
159
+ status: 404,
160
+ body: { error: "unknown-product-code" },
161
+ });
162
+
163
+ const result = await ailkPurchase({ productCode: "no-such-offering" });
164
+
165
+ expect(result).toMatchObject({ error: "not_found" });
166
+ });
167
+
168
+ it("makes exactly one call — createSession — and reaches no Stripe stub", async () => {
169
+ createSession.mockResolvedValueOnce({
170
+ ok: false as const,
171
+ status: 404,
172
+ body: { error: "unknown-product-code" },
173
+ });
174
+
175
+ await ailkPurchase({ productCode: "no-such-offering" });
176
+
177
+ expect(createSession).toHaveBeenCalledTimes(1);
178
+ });
179
+
180
+ it("maps a non-404 route failure to a typed body too", async () => {
181
+ createSession.mockResolvedValueOnce({
182
+ ok: false as const,
183
+ status: 502,
184
+ body: { error: "checkout-unavailable" },
185
+ });
186
+
187
+ const result = await ailkPurchase({ productCode: "pro-plan" });
188
+
189
+ expect(result).toMatchObject({ error: "validation_failed" });
190
+ });
191
+ });
192
+
193
+ describe("start_purchase — invalid input never reaches the seam", () => {
194
+ it("rejects a product code the route's regex would reject", async () => {
195
+ const result = await ailkPurchase({ productCode: "has spaces" });
196
+
197
+ expect(result).toMatchObject({ error: "validation_failed" });
198
+ expect(createSession).not.toHaveBeenCalled();
199
+ });
200
+
201
+ it("rejects a quantity above the route's maximum", async () => {
202
+ const result = await ailkPurchase({
203
+ productCode: "pro-plan",
204
+ quantity: 1000,
205
+ });
206
+
207
+ expect(result).toMatchObject({ error: "validation_failed" });
208
+ expect(createSession).not.toHaveBeenCalled();
209
+ });
210
+ });
211
+
212
+ describe("start_purchase — the redirect fix (#6001): a caller-supplied redirect fails validation, not merely gets ignored", () => {
213
+ it("rejects a call carrying a caller-supplied successUrl — even a plausible-looking one, not just javascript:", async () => {
214
+ const result = await ailkPurchase({
215
+ productCode: "pro-plan",
216
+ successUrl: "https://attacker.example/phish",
217
+ } as never);
218
+
219
+ expect(result).toMatchObject({ error: "validation_failed" });
220
+ expect(createSession).not.toHaveBeenCalled();
221
+ });
222
+
223
+ it("rejects a javascript: redirect URL exactly as the route would — via the same rejection path as any other successUrl", async () => {
224
+ const result = await ailkPurchase({
225
+ productCode: "pro-plan",
226
+ successUrl: "javascript:alert(1)",
227
+ } as never);
228
+
229
+ expect(result).toMatchObject({ error: "validation_failed" });
230
+ expect(createSession).not.toHaveBeenCalled();
231
+ });
232
+
233
+ it("rejects a call carrying a caller-supplied cancelUrl", async () => {
234
+ const result = await ailkPurchase({
235
+ productCode: "pro-plan",
236
+ cancelUrl: "https://attacker.example/cancelled",
237
+ } as never);
238
+
239
+ expect(result).toMatchObject({ error: "validation_failed" });
240
+ expect(createSession).not.toHaveBeenCalled();
241
+ });
242
+
243
+ it("rejects a call carrying allowPromotionCodes", async () => {
244
+ const result = await ailkPurchase({
245
+ productCode: "pro-plan",
246
+ allowPromotionCodes: true,
247
+ } as never);
248
+
249
+ expect(result).toMatchObject({ error: "validation_failed" });
250
+ expect(createSession).not.toHaveBeenCalled();
251
+ });
252
+ });
@@ -1,16 +1,16 @@
1
1
  /**
2
- * Tests for the ailkScaffold MCP tool handler.
2
+ * Tests for the ailkCreateProject MCP tool handler.
3
3
  *
4
4
  * Acceptance criteria verified here:
5
5
  * - scaffoldInputSchema validates correct inputs
6
6
  * - scaffoldInputSchema rejects invalid inputs (empty projectName, missing targetDir)
7
- * - ailkScaffold scaffolds into a tmp dir: package.json + ≥1 file created
8
- * - ailkScaffold fails cleanly when targetDir is non-empty (rejects with Error)
7
+ * - ailkCreateProject scaffolds into a tmp dir: package.json + ≥1 file created
8
+ * - ailkCreateProject fails cleanly when targetDir is non-empty (rejects with Error)
9
9
  * - Output validates against scaffoldOutputSchema
10
10
  * - No direct create-ailk import in apps/mcp (architectural guard)
11
11
  *
12
12
  * Hermetic: uses os.tmpdir() + unique subdir. No network calls.
13
- * Integration tests pass CLI_PATH explicitly to ailkScaffold (second arg)
13
+ * Integration tests pass CLI_PATH explicitly to ailkCreateProject (second arg)
14
14
  * so they don't depend on process.cwd() being the repo root in Jest.
15
15
  */
16
16
 
@@ -26,7 +26,7 @@ import { join, resolve } from "node:path";
26
26
 
27
27
  import { scaffoldInputSchema, scaffoldOutputSchema } from "@working-theory/validation";
28
28
 
29
- import { ailkScaffold } from "../../src/tools/scaffold.js";
29
+ import { ailkCreateProject } from "../../src/tools/create_project.js";
30
30
 
31
31
  // ── Input schema tests ────────────────────────────────────────────────────────
32
32
 
@@ -103,13 +103,13 @@ describe("scaffoldOutputSchema", () => {
103
103
  const REPO_ROOT = resolve(__dirname, "../../../..");
104
104
  const CLI_PATH = join(REPO_ROOT, "packages/create-ailk/dist/cli.js");
105
105
 
106
- describe("ailkScaffold handler", () => {
106
+ describe("ailkCreateProject handler", () => {
107
107
  let scratch: string;
108
108
 
109
109
  beforeAll(() => {
110
110
  if (!existsSync(CLI_PATH)) {
111
111
  console.warn(
112
- `[scaffold.test] create-ailk CLI not built: ${CLI_PATH} missing. ` +
112
+ `[create_project.test] create-ailk CLI not built: ${CLI_PATH} missing. ` +
113
113
  "Run `pnpm --filter create-ailk build` first.",
114
114
  );
115
115
  }
@@ -125,11 +125,11 @@ describe("ailkScaffold handler", () => {
125
125
 
126
126
  it("scaffolds at least one file into a fresh tmp dir and reports targetDir", async () => {
127
127
  if (!existsSync(CLI_PATH)) {
128
- console.warn("[scaffold.test] skipping integration test — CLI not built");
128
+ console.warn("[create_project.test] skipping integration test — CLI not built");
129
129
  return;
130
130
  }
131
131
  const target = join(scratch, "my-site");
132
- const output = await ailkScaffold(
132
+ const output = await ailkCreateProject(
133
133
  { targetDir: target, projectName: "my-site" },
134
134
  CLI_PATH,
135
135
  );
@@ -139,37 +139,37 @@ describe("ailkScaffold handler", () => {
139
139
 
140
140
  it("scaffolds package.json when full templates are built (build-templates required)", async () => {
141
141
  if (!existsSync(CLI_PATH)) {
142
- console.warn("[scaffold.test] skipping integration test — CLI not built");
142
+ console.warn("[create_project.test] skipping integration test — CLI not built");
143
143
  return;
144
144
  }
145
145
  const TEMPLATES_DIR = join(REPO_ROOT, "packages/create-ailk/templates");
146
146
  const PKG_JSON_IN_TEMPLATES = join(TEMPLATES_DIR, "package.json");
147
147
  if (!existsSync(PKG_JSON_IN_TEMPLATES)) {
148
148
  console.warn(
149
- "[scaffold.test] skipping package.json assertion — run `pnpm --filter create-ailk run build-templates` first",
149
+ "[create_project.test] skipping package.json assertion — run `pnpm --filter create-ailk run build-templates` first",
150
150
  );
151
151
  return;
152
152
  }
153
153
  const target = join(scratch, "my-site");
154
- await ailkScaffold({ targetDir: target, projectName: "my-site" }, CLI_PATH);
154
+ await ailkCreateProject({ targetDir: target, projectName: "my-site" }, CLI_PATH);
155
155
  expect(existsSync(join(target, "package.json"))).toBe(true);
156
156
  });
157
157
 
158
158
  it("scaffolds app dir when full templates are built (build-templates required)", async () => {
159
159
  if (!existsSync(CLI_PATH)) {
160
- console.warn("[scaffold.test] skipping integration test — CLI not built");
160
+ console.warn("[create_project.test] skipping integration test — CLI not built");
161
161
  return;
162
162
  }
163
163
  const TEMPLATES_DIR = join(REPO_ROOT, "packages/create-ailk/templates");
164
164
  const PKG_JSON_IN_TEMPLATES = join(TEMPLATES_DIR, "package.json");
165
165
  if (!existsSync(PKG_JSON_IN_TEMPLATES)) {
166
166
  console.warn(
167
- "[scaffold.test] skipping app dir assertion — run `pnpm --filter create-ailk run build-templates` first",
167
+ "[create_project.test] skipping app dir assertion — run `pnpm --filter create-ailk run build-templates` first",
168
168
  );
169
169
  return;
170
170
  }
171
171
  const target = join(scratch, "my-site");
172
- await ailkScaffold({ targetDir: target, projectName: "my-site" }, CLI_PATH);
172
+ await ailkCreateProject({ targetDir: target, projectName: "my-site" }, CLI_PATH);
173
173
  // The template produces apps/web/app/ from the monorepo template snapshot.
174
174
  const hasApp =
175
175
  existsSync(join(target, "app")) ||
@@ -179,22 +179,22 @@ describe("ailkScaffold handler", () => {
179
179
 
180
180
  it("returns at least one file (schema-typed page seed or README.md)", async () => {
181
181
  if (!existsSync(CLI_PATH)) {
182
- console.warn("[scaffold.test] skipping integration test — CLI not built");
182
+ console.warn("[create_project.test] skipping integration test — CLI not built");
183
183
  return;
184
184
  }
185
185
  const target = join(scratch, "my-site");
186
- await ailkScaffold({ targetDir: target, projectName: "my-site" }, CLI_PATH);
186
+ await ailkCreateProject({ targetDir: target, projectName: "my-site" }, CLI_PATH);
187
187
  // At minimum README.md is always emitted by the template.
188
188
  expect(existsSync(join(target, "README.md"))).toBe(true);
189
189
  });
190
190
 
191
191
  it("returns output conforming to scaffoldOutputSchema", async () => {
192
192
  if (!existsSync(CLI_PATH)) {
193
- console.warn("[scaffold.test] skipping integration test — CLI not built");
193
+ console.warn("[create_project.test] skipping integration test — CLI not built");
194
194
  return;
195
195
  }
196
196
  const target = join(scratch, "my-site");
197
- const output = await ailkScaffold(
197
+ const output = await ailkCreateProject(
198
198
  { targetDir: target, projectName: "my-site" },
199
199
  CLI_PATH,
200
200
  );
@@ -204,7 +204,7 @@ describe("ailkScaffold handler", () => {
204
204
 
205
205
  it("rejects with a clear error when targetDir is non-empty", async () => {
206
206
  if (!existsSync(CLI_PATH)) {
207
- console.warn("[scaffold.test] skipping integration test — CLI not built");
207
+ console.warn("[create_project.test] skipping integration test — CLI not built");
208
208
  return;
209
209
  }
210
210
  const target = join(scratch, "populated");
@@ -215,7 +215,7 @@ describe("ailkScaffold handler", () => {
215
215
  // the target exists and is non-empty. The MCP handler rejects with a
216
216
  // descriptive Error whose message matches the CLI stderr output.
217
217
  await expect(
218
- ailkScaffold({ targetDir: target, projectName: "populated" }, CLI_PATH),
218
+ ailkCreateProject({ targetDir: target, projectName: "populated" }, CLI_PATH),
219
219
  ).rejects.toThrow(/non-empty|exists/i);
220
220
  });
221
221
  });
@@ -223,11 +223,11 @@ describe("ailkScaffold handler", () => {
223
223
  // ── Architectural guard ───────────────────────────────────────────────────────
224
224
 
225
225
  describe("architectural guard — no create-ailk direct import", () => {
226
- it("apps/mcp/src/tools/scaffold.ts does not import create-ailk directly", async () => {
226
+ it("apps/mcp/src/tools/create_project.ts does not import create-ailk directly", async () => {
227
227
  const { readFileSync } = await import("node:fs");
228
228
  const { resolve: res } = await import("node:path");
229
229
  const src = readFileSync(
230
- res(__dirname, "../../src/tools/scaffold.ts"),
230
+ res(__dirname, "../../src/tools/create_project.ts"),
231
231
  "utf-8",
232
232
  );
233
233
  // Must not have a static import of create-ailk or its dist