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.
- package/README.md +55 -8
- package/component-catalog.json +12248 -0
- package/component-catalog.md +193 -68
- package/dist/cli.js +29 -1
- package/dist/derive-architecture.js +5 -2
- package/dist/lib/api-boot-assertion.d.ts +46 -0
- package/dist/lib/api-boot-assertion.js +70 -0
- package/dist/lib/extract-module-bundle.d.ts +50 -2
- package/dist/lib/extract-module-bundle.js +223 -15
- package/dist/lib/fetch-guard.d.ts +28 -0
- package/dist/lib/fetch-guard.js +101 -0
- package/dist/lib/fetch-module.js +4 -0
- package/dist/lib/fetch-template.js +4 -0
- package/dist/lib/import-graph.d.ts +29 -0
- package/dist/lib/import-graph.js +140 -0
- package/dist/module-architecture.d.ts +60 -8
- package/dist/module-architecture.js +587 -242
- package/dist/parse-args.js +6 -2
- package/dist/programmatic.d.ts +10 -0
- package/dist/programmatic.js +134 -19
- package/dist/site-chrome.d.ts +80 -0
- package/dist/site-chrome.js +135 -0
- package/dist/surfaces.d.ts +21 -6
- package/dist/surfaces.js +209 -36
- package/dist/template-manifest.d.ts +88 -3
- package/dist/template-manifest.js +281 -29
- package/dist/template-names.d.ts +1 -1
- package/dist/template-names.js +2 -0
- package/package.json +10 -2
- package/site-templates/_base/ailk-template.json +6 -0
- package/site-templates/_base/content/en/Pricing/pricing.mdx +169 -0
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +351 -0
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +400 -0
- package/site-templates/marketing-site/ailk-template.json +11 -0
- package/site-templates/marketing-site/content/en/blog/05-this-vc-morning-routine-will-make-you-super-annoying.mdx +36 -0
- package/site-templates/marketing-site/provenance.json +4 -0
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +288 -0
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +286 -0
- package/site-templates/marketing-site-free/ailk-template.json +2 -1
- package/site-templates/minimalism-site/TEMPLATE-MANIFEST.json +279 -0
- package/site-templates/minimalism-site/TEMPLATE-MANIFEST.md +336 -0
- package/site-templates/minimalism-site/ailk-template.json +22 -0
- package/site-templates/minimalism-site/assets/avatar-placeholder-1.svg +7 -0
- package/site-templates/minimalism-site/assets/avatar-placeholder-2.svg +7 -0
- package/site-templates/minimalism-site/assets/avatar-placeholder-3.svg +7 -0
- package/site-templates/minimalism-site/assets/avatar-placeholder-4.svg +7 -0
- package/site-templates/minimalism-site/assets/avatar-placeholder-5.svg +7 -0
- package/site-templates/minimalism-site/assets/avatar-placeholder-6.svg +7 -0
- package/site-templates/minimalism-site/assets/hero-backdrop.svg +17 -0
- package/site-templates/minimalism-site/assets/logo-placeholder-1.svg +6 -0
- package/site-templates/minimalism-site/assets/logo-placeholder-2.svg +6 -0
- package/site-templates/minimalism-site/assets/logo-placeholder-3.svg +6 -0
- package/site-templates/minimalism-site/assets/wordmark-placeholder.svg +28 -0
- package/site-templates/minimalism-site/content/en/AboutPage/about.mdx +110 -0
- package/site-templates/minimalism-site/content/en/ContactPage/contact.mdx +68 -0
- package/site-templates/minimalism-site/content/en/FAQPage/faq.mdx +57 -0
- package/site-templates/minimalism-site/content/en/HomePage/home.mdx +244 -0
- package/site-templates/minimalism-site/content/en/LandingPage/referral.mdx +77 -0
- package/site-templates/minimalism-site/content/en/LandingPage/reports.mdx +73 -0
- package/site-templates/minimalism-site/content/en/ReportPage/annual-letter-2024.mdx +52 -0
- package/site-templates/minimalism-site/content/en/ReportPage/field-notes-2023.mdx +52 -0
- package/site-templates/minimalism-site/content/en/ReportPage/platform-shift-2025.mdx +52 -0
- package/site-templates/minimalism-site/content/en/ReportPage/state-of-the-market-2026.mdx +52 -0
- package/site-templates/minimalism-site/content/en/Service/first-engagement.mdx +38 -0
- package/site-templates/minimalism-site/content/en/Service/second-engagement.mdx +38 -0
- package/site-templates/minimalism-site/content/en/Service/third-engagement.mdx +38 -0
- package/site-templates/minimalism-site/content/en/ServicesList/services.mdx +73 -0
- package/site-templates/minimalism-site/flows/referral.flow.json +71 -0
- package/site-templates/minimalism-site/provenance.json +4 -0
- package/site-templates/saas-product/TEMPLATE-MANIFEST.md +61 -0
- package/site-templates/saas-product/ailk-template.json +16 -0
- package/site-templates/saas-product/provenance.json +10 -0
- package/site-templates/simple-service/TEMPLATE-MANIFEST.json +240 -0
- package/site-templates/simple-service/TEMPLATE-MANIFEST.md +278 -0
- package/site-templates/simple-service/ailk-template.json +10 -0
- package/site-templates/simple-service/content/en/AboutPage/about.mdx +38 -0
- package/site-templates/simple-service/content/en/ContactPage/contact.mdx +67 -0
- package/site-templates/simple-service/content/en/FAQPage/faq.mdx +56 -0
- package/site-templates/simple-service/content/en/HomePage/home.mdx +85 -0
- package/site-templates/simple-service/content/en/Service/landscape-design.mdx +40 -0
- package/site-templates/simple-service/content/en/Service/lawn-maintenance.mdx +40 -0
- package/site-templates/simple-service/content/en/Service/seasonal-cleanup.mdx +40 -0
- package/site-templates/simple-service/content/en/ServicesList/services.mdx +72 -0
- package/site-templates/simple-service/provenance.json +4 -0
- package/templates/.claude/skills/README.md +16 -25
- package/templates/.env.example +102 -12
- package/templates/.github/workflows/ci.yml +48 -0
- package/templates/README.md +3 -1
- package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-31.552Z-fallback-1789916251590-037e9005.json +8 -0
- package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-33.840Z-fallback-1789916253842-b52659e9.json +12 -0
- package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-33.844Z-fallback-1789916253845-6b266f8c.json +12 -0
- package/templates/apps/api/.env.example +13 -0
- package/templates/apps/api/CLAUDE.md +29 -2
- package/templates/apps/api/eslint.config.mjs +69 -0
- package/templates/apps/api/jest.config.cjs +3 -0
- package/templates/apps/api/package.json +7 -2
- package/templates/apps/api/src/__tests__/commerce-claim-registration.test.ts +407 -0
- package/templates/apps/api/src/__tests__/module-exclusion.test.ts +89 -9
- package/templates/apps/api/src/__tests__/review-surface-registration.test.ts +167 -0
- package/templates/apps/api/src/__tests__/server.test.ts +471 -0
- package/templates/apps/api/src/__tests__/vercel-handler.test.ts +45 -0
- package/templates/apps/api/src/__tests__/workspace-module-registration.test.ts +10 -1
- package/templates/apps/api/src/config/index.ts +62 -0
- package/templates/apps/api/src/config/modules.ts +7 -3
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +241 -2
- package/templates/apps/api/src/lib/__tests__/auth-email-budget.test.ts +143 -0
- package/templates/apps/api/src/lib/__tests__/content-adapter.test.ts +23 -0
- package/templates/apps/api/src/lib/__tests__/deliverable-asset-store.test.ts +125 -0
- package/templates/apps/api/src/lib/__tests__/error-serializer.test.ts +20 -0
- package/templates/apps/api/src/lib/__tests__/require-commerce-claim.test.ts +317 -0
- package/templates/apps/api/src/lib/__tests__/site.test.ts +238 -0
- package/templates/apps/api/src/lib/__tests__/stripe.test.ts +75 -0
- package/templates/apps/api/src/lib/auth-email-budget.ts +160 -0
- package/templates/apps/api/src/lib/content-adapter.ts +57 -31
- package/templates/apps/api/src/lib/deliverable-asset-store.ts +76 -0
- package/templates/apps/api/src/lib/error-serializer.ts +23 -3
- package/templates/apps/api/src/lib/outbound-http.ts +7 -2
- package/templates/apps/api/src/lib/require-commerce-claim.ts +250 -0
- package/templates/apps/api/src/lib/site.ts +208 -9
- package/templates/apps/api/src/lib/stripe.ts +66 -0
- package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +521 -0
- package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +150 -0
- package/templates/apps/api/src/middleware/default-tenant.ts +136 -0
- package/templates/apps/api/src/middleware/rate-limit.ts +7 -1
- package/templates/apps/api/src/openapi/spec.ts +543 -2
- package/templates/apps/api/src/routes/auth/__tests__/auth-user.functional.test.ts +48 -0
- package/templates/apps/api/src/routes/auth/__tests__/transactional-email.test.ts +309 -0
- package/templates/apps/api/src/routes/auth/__tests__/user.test.ts +123 -0
- package/templates/apps/api/src/routes/auth/transactional-email.ts +273 -0
- package/templates/apps/api/src/routes/auth/user.ts +61 -0
- package/templates/apps/api/src/routes/checkout/__tests__/session.test.ts +434 -0
- package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +888 -0
- package/templates/apps/api/src/routes/checkout/index.ts +34 -0
- package/templates/apps/api/src/routes/checkout/session.ts +139 -0
- package/templates/apps/api/src/routes/checkout/sessions.ts +197 -0
- package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +554 -0
- package/templates/apps/api/src/routes/content/__tests__/list-pages.test.ts +108 -0
- package/templates/apps/api/src/routes/content/__tests__/search.test.ts +450 -0
- package/templates/apps/api/src/routes/content/get-page.ts +94 -12
- package/templates/apps/api/src/routes/content/index.ts +13 -73
- package/templates/apps/api/src/routes/content/list-pages.ts +15 -3
- package/templates/apps/api/src/routes/content/rate-limit.ts +15 -6
- package/templates/apps/api/src/routes/content/search.ts +129 -0
- package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +447 -31
- package/templates/apps/api/src/routes/leads/__tests__/list.test.ts +171 -0
- package/templates/apps/api/src/routes/leads/index.ts +100 -13
- package/templates/apps/api/src/routes/leads/list.ts +104 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +51 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +351 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/founder-identity.test.ts +51 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +5 -2
- package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +4 -1
- package/templates/apps/api/src/routes/project-listings/__tests__/resume-email.test.ts +58 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +107 -6
- package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +87 -2
- package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +51 -0
- package/templates/apps/api/src/routes/project-listings/start.ts +5 -1
- package/templates/apps/api/src/routes/requests/__tests__/confirm.test.ts +686 -0
- package/templates/apps/api/src/routes/requests/confirm.ts +601 -0
- package/templates/apps/api/src/routes/requests/index.ts +54 -0
- package/templates/apps/api/src/routes/webhooks/README.md +265 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-dunning.test.ts +749 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +330 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +262 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +285 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +213 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-request-paid.test.ts +301 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +648 -0
- package/templates/apps/api/src/routes/webhooks/index.ts +89 -0
- package/templates/apps/api/src/routes/webhooks/stripe.ts +1399 -0
- package/templates/apps/api/src/server.ts +521 -55
- package/templates/apps/api/src/services/__tests__/abandon-email.test.ts +39 -0
- package/templates/apps/api/src/services/__tests__/aeo-score.test.ts +217 -40
- package/templates/apps/api/src/services/__tests__/audit-log.test.ts +620 -0
- package/templates/apps/api/src/services/__tests__/consent.test.ts +2 -0
- package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +300 -5
- package/templates/apps/api/src/services/__tests__/deliverable-resend.test.ts +20 -0
- package/templates/apps/api/src/services/__tests__/dunning-policy.test.ts +368 -0
- package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +408 -0
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +160 -0
- package/templates/apps/api/src/services/__tests__/leads-inbox.test.ts +329 -0
- package/templates/apps/api/src/services/__tests__/listing-config.test.ts +166 -0
- package/templates/apps/api/src/services/__tests__/project-listings.fence.test.ts +154 -0
- package/templates/apps/api/src/services/__tests__/public-site-resolver.test.ts +137 -0
- package/templates/apps/api/src/services/__tests__/request-confirm.test.ts +444 -0
- package/templates/apps/api/src/services/__tests__/revalidation.test.ts +238 -0
- package/templates/apps/api/src/services/__tests__/verified-email.test.ts +78 -0
- package/templates/apps/api/src/services/abandon-email.ts +8 -2
- package/templates/apps/api/src/services/aeo-score.ts +49 -12
- package/templates/apps/api/src/services/audit-log.ts +668 -0
- package/templates/apps/api/src/services/consent.ts +18 -0
- package/templates/apps/api/src/services/default-tenant.ts +300 -0
- package/templates/apps/api/src/services/deliverable-fulfillment.ts +141 -13
- package/templates/apps/api/src/services/dunning-policy.ts +294 -0
- package/templates/apps/api/src/services/entitlements.ts +80 -0
- package/templates/apps/api/src/services/lead-promotion.ts +160 -0
- package/templates/apps/api/src/services/lead-routing.ts +161 -2
- package/templates/apps/api/src/services/leads-inbox.ts +284 -0
- package/templates/apps/api/src/services/listing-config.ts +27 -9
- package/templates/apps/api/src/services/owner-workspaces.ts +132 -0
- package/templates/apps/api/src/services/project-listings.ts +201 -6
- package/templates/apps/api/src/services/public-site-resolver.ts +106 -0
- package/templates/apps/api/src/services/request-confirm.ts +646 -0
- package/templates/apps/api/src/services/revalidation.ts +173 -0
- package/templates/apps/api/src/services/verified-email.ts +71 -0
- package/templates/apps/api/src/vercel-handler.ts +75 -35
- package/templates/apps/mcp/.env.example +17 -0
- package/templates/apps/mcp/CLAUDE.md +58 -25
- package/templates/apps/mcp/__tests__/aeo-cli-parity.test.ts +6 -0
- package/templates/apps/mcp/__tests__/audit.test.ts +137 -8
- package/templates/apps/mcp/__tests__/catalog-drift.test.ts +79 -36
- package/templates/apps/mcp/__tests__/get_offerings.test.ts +111 -2
- package/templates/apps/mcp/__tests__/get_pricing.test.ts +82 -2
- package/templates/apps/mcp/__tests__/http-transport.test.ts +541 -2
- package/templates/apps/mcp/__tests__/index.test.ts +39 -0
- package/templates/apps/mcp/__tests__/lib/audit-single-page-readiness-scan.test.ts +143 -0
- package/templates/apps/mcp/__tests__/lib/commerce-claim.test.ts +111 -0
- package/templates/apps/mcp/__tests__/lib/content-adapter-parity.test.ts +65 -0
- package/templates/apps/mcp/__tests__/lib/content-adapter.test.ts +188 -0
- package/templates/apps/mcp/__tests__/lib/fail-closed-tools.test.ts +165 -0
- package/templates/apps/mcp/__tests__/lib/public-workspace.test.ts +124 -0
- package/templates/apps/mcp/__tests__/list_pages.test.ts +116 -0
- package/templates/apps/mcp/__tests__/module-exclusion.test.ts +7 -1
- package/templates/apps/mcp/__tests__/parity.test.ts +12 -9
- package/templates/apps/mcp/__tests__/purchase-disabled.test.ts +89 -22
- package/templates/apps/mcp/__tests__/read_content.test.ts +322 -1
- package/templates/apps/mcp/__tests__/search_content.test.ts +105 -2
- package/templates/apps/mcp/__tests__/server.test.ts +67 -14
- package/templates/apps/mcp/__tests__/start_purchase.test.ts +252 -0
- package/templates/apps/mcp/__tests__/tools/{scaffold.test.ts → create_project.test.ts} +23 -23
- package/templates/apps/mcp/__tests__/tools/purchase.test.ts +300 -123
- package/templates/apps/mcp/eslint.config.mjs +69 -0
- package/templates/apps/mcp/jest.config.cjs +13 -0
- package/templates/apps/mcp/package.json +8 -2
- package/templates/apps/mcp/src/abuse/index.ts +159 -24
- package/templates/apps/mcp/src/config/modules.ts +10 -5
- package/templates/apps/mcp/src/http-transport.ts +102 -29
- package/templates/apps/mcp/src/index.ts +73 -11
- package/templates/apps/mcp/src/lib/audit-single-page.ts +90 -3
- package/templates/apps/mcp/src/lib/commerce-claim.ts +94 -0
- package/templates/apps/mcp/src/lib/content-adapter.ts +113 -0
- package/templates/apps/mcp/src/lib/empty-content-adapter.ts +139 -0
- package/templates/apps/mcp/src/lib/prisma.ts +39 -0
- package/templates/apps/mcp/src/lib/public-workspace.ts +133 -0
- package/templates/apps/mcp/src/lib/stripe-tax-signal.ts +59 -0
- package/templates/apps/mcp/src/server.ts +61 -37
- package/templates/apps/mcp/src/tools/audit.ts +26 -2
- package/templates/apps/mcp/src/tools/{scaffold.ts → create_project.ts} +3 -3
- package/templates/apps/mcp/src/tools/get_business_hours.ts +4 -3
- package/templates/apps/mcp/src/tools/get_faq.ts +4 -3
- package/templates/apps/mcp/src/tools/get_locations.ts +5 -3
- package/templates/apps/mcp/src/tools/get_offerings.ts +16 -4
- package/templates/apps/mcp/src/tools/get_pricing.ts +25 -6
- package/templates/apps/mcp/src/tools/get_site_info.ts +4 -3
- package/templates/apps/mcp/src/tools/index.ts +9 -7
- package/templates/apps/mcp/src/tools/list_pages.ts +4 -3
- package/templates/apps/mcp/src/tools/purchase.ts +128 -45
- package/templates/apps/mcp/src/tools/read_content.ts +20 -6
- package/templates/apps/mcp/src/tools/search_content.ts +11 -4
- package/templates/apps/web/.env.example +74 -8
- package/templates/apps/web/CLAUDE.md +46 -0
- package/templates/apps/web/app/.well-known/mcp.json/build-manifest.ts +18 -2
- package/templates/apps/web/app/.well-known/mcp.json/route.ts +11 -3
- package/templates/apps/web/app/[locale]/(auth)/__tests__/auth-pages.test.tsx +282 -0
- package/templates/apps/web/app/[locale]/(auth)/__tests__/password-reset.test.tsx +515 -0
- package/templates/apps/web/app/[locale]/(auth)/forgot-password/forgot-password-card.tsx +89 -0
- package/templates/apps/web/app/[locale]/(auth)/forgot-password/page.tsx +109 -0
- package/templates/apps/web/app/[locale]/(auth)/layout.tsx +92 -0
- package/templates/apps/web/app/[locale]/(auth)/reset-password/page.tsx +135 -0
- package/templates/apps/web/app/[locale]/(auth)/reset-password/reset-password-card.tsx +109 -0
- package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/__tests__/page.test.tsx +48 -2
- package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/page.tsx +20 -6
- package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-card.tsx +125 -0
- package/templates/apps/web/app/[locale]/(auth)/sign-up/page.tsx +96 -0
- package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up-card.tsx +101 -0
- package/templates/apps/web/app/[locale]/(auth)/use-auth-card-flow.ts +102 -0
- package/templates/apps/web/app/[locale]/(auth)/verify-email/page.tsx +87 -0
- package/templates/apps/web/app/[locale]/(authed)/__tests__/gate.test.tsx +20 -10
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +84 -0
- package/templates/apps/web/app/[locale]/(authed)/account/actions.ts +58 -0
- package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +67 -14
- package/templates/apps/web/app/[locale]/(authed)/layout.tsx +19 -2
- package/templates/apps/web/app/[locale]/(authed)/leads/__tests__/page.test.tsx +181 -0
- package/templates/apps/web/app/[locale]/(authed)/leads/components/LeadsTable.tsx +137 -0
- package/templates/apps/web/app/[locale]/(authed)/leads/page.tsx +111 -0
- package/templates/apps/web/app/[locale]/(authed)/sign-in-gate.tsx +1 -1
- package/templates/apps/web/app/[locale]/[slug]/__tests__/page.test.tsx +111 -0
- package/templates/apps/web/app/[locale]/[slug]/page.tsx +65 -0
- package/templates/apps/web/app/[locale]/cancellation/page.tsx +44 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +1 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.permalink.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.test.tsx +6 -5
- package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +1 -1
- package/templates/apps/web/app/[locale]/layout.tsx +308 -65
- package/templates/apps/web/app/[locale]/purchase/__tests__/cancel.test.tsx +162 -0
- package/templates/apps/web/app/[locale]/purchase/__tests__/module-scope.test.ts +68 -0
- package/templates/apps/web/app/[locale]/purchase/__tests__/success.test.tsx +361 -0
- package/templates/apps/web/app/[locale]/purchase/cancel/page.tsx +115 -0
- package/templates/apps/web/app/[locale]/purchase/success/page.tsx +307 -0
- package/templates/apps/web/app/[locale]/returns/page.tsx +44 -0
- package/templates/apps/web/app/[locale]/shipping/page.tsx +44 -0
- package/templates/apps/web/app/api/revalidate/__tests__/route.test.ts +670 -0
- package/templates/apps/web/app/api/revalidate/route.ts +194 -0
- package/templates/apps/web/auth.ts +407 -8
- package/templates/apps/web/components/google-analytics.tsx +18 -1
- package/templates/apps/web/eslint.config.mjs +69 -0
- package/templates/apps/web/jest.config.cjs +15 -19
- package/templates/apps/web/lib/__tests__/auth-email.test.ts +214 -0
- package/templates/apps/web/lib/__tests__/auth-routes.test.ts +74 -0
- package/templates/apps/web/lib/__tests__/meeting-slugs.test.ts +72 -0
- package/templates/apps/web/lib/__tests__/structured-data.test.tsx +50 -6
- package/templates/apps/web/lib/auth-email.ts +318 -0
- package/templates/apps/web/lib/auth-env.ts +85 -0
- package/templates/apps/web/lib/auth-routes.ts +55 -0
- package/templates/apps/web/lib/meeting-slugs.ts +53 -0
- package/templates/apps/web/lib/nav-config.ts +27 -7
- package/templates/apps/web/lib/server-api.ts +1 -1
- package/templates/apps/web/lib/site-chrome.ts +29 -0
- package/templates/apps/web/lib/telemetry-scrub.ts +50 -0
- package/templates/apps/web/messages/en.json +216 -2
- package/templates/apps/web/next.config.mjs +20 -0
- package/templates/apps/web/package.json +8 -3
- package/templates/apps/web/sentry.client.config.ts +131 -0
- package/templates/apps/web/sentry.edge.config.ts +72 -0
- package/templates/apps/web/sentry.server.config.ts +72 -0
- package/templates/apps/web/src/lib/commerce-claim.ts +33 -0
- package/templates/apps/web/src/lib/commerce-sections.ts +61 -0
- package/templates/apps/web/src/lib/routes.ts +13 -18
- package/templates/brand_assets/README.md +24 -0
- package/templates/content/en/AboutPage/about.mdx +160 -15
- package/templates/content/en/CancellationPolicyPage/cancellation.mdx +108 -0
- package/templates/content/en/ContactPage/contact.mdx +90 -13
- package/templates/content/en/FAQPage/faq.mdx +62 -22
- package/templates/content/en/HomePage/home.mdx +244 -20
- package/templates/content/en/LandingPage/blog.mdx +83 -0
- package/templates/content/en/LandingPage/features.mdx +103 -0
- package/templates/content/en/LandingPage/how-it-works.mdx +143 -0
- package/templates/content/en/ReturnsPolicyPage/returns.mdx +108 -0
- package/templates/content/en/ShippingPolicyPage/shipping.mdx +108 -0
- package/templates/database/CHANGELOG.md +149 -0
- package/templates/database/README.md +10 -3
- package/templates/database/__tests__/appointment.db.test.ts +106 -0
- package/templates/database/__tests__/artifact-content-store.db.test.ts +316 -0
- package/templates/database/__tests__/composite-fence.db.test.ts +206 -0
- package/templates/database/__tests__/fence-fan-out.db.test.ts +711 -0
- package/templates/database/__tests__/fixtures/appointment-create-probe.ts +79 -0
- package/templates/database/__tests__/fixtures/project-listing-consent-probe.ts +100 -0
- package/templates/database/__tests__/flow-workspace-key.db.test.ts +263 -0
- package/templates/database/__tests__/prefixed-ids.db.test.ts +85 -9
- package/templates/database/__tests__/project-listing-consent-at.db.test.ts +111 -0
- package/templates/database/__tests__/seed.test.ts +1 -1
- package/templates/database/__tests__/tenancy.test.ts +141 -0
- package/templates/database/config/schema.prisma +79 -2
- package/templates/database/content/schema.prisma +87 -11
- package/templates/database/eslint.config.mjs +69 -0
- package/templates/database/inbox/schema.prisma +452 -9
- package/templates/database/jest.config.cjs +5 -3
- package/templates/database/migrations/20260915120000_project_listing_solution/migration.sql +15 -0
- package/templates/database/migrations/20260916120000_artifact_content_store/migration.sql +158 -0
- package/templates/database/migrations/20260917120000_callback_request_confirm/migration.sql +40 -0
- package/templates/database/migrations/20260918120000_add_appointment/migration.sql +67 -0
- package/templates/database/migrations/20260918120000_asset_metadata_columns/migration.sql +70 -0
- package/templates/database/migrations/20260919120000_flow_workspace_key/migration.sql +181 -0
- package/templates/database/migrations/20260919130000_flow_definition_admin_columns/migration.sql +172 -0
- package/templates/database/migrations/20260919130000_workspace_meetings/migration.sql +41 -0
- package/templates/database/migrations/20260919140000_inbox_workspace_key/migration.sql +185 -0
- package/templates/database/migrations/20260920120000_lead_handled_at/migration.sql +70 -0
- package/templates/database/migrations/20260920130000_lead_workspace_source_index/migration.sql +57 -0
- package/templates/database/migrations/20260921120000_deliverable_grant_workspace_key/migration.sql +68 -0
- package/templates/database/migrations/20260922120000_tenancy_fence/migration.sql +1067 -0
- package/templates/database/migrations/20260923120000_listing_capturing_magnet/migration.sql +210 -0
- package/templates/database/package.json +7 -3
- package/templates/database/seed.ts +1 -1
- package/templates/database/tenancy.ts +86 -1
- package/templates/database/tsconfig.json +1 -0
- package/templates/package.json +1 -14
- package/versions.json +52 -0
- package/site-templates/marketing-site-free/content/en/Pricing/pricing.mdx +0 -103
- package/templates/apps/api/src/routes/content/__tests__/create.test.ts +0 -253
- package/templates/apps/api/src/routes/content/__tests__/delete.test.ts +0 -215
- package/templates/apps/api/src/routes/content/__tests__/update.test.ts +0 -266
- package/templates/apps/api/src/routes/content/create.ts +0 -115
- package/templates/apps/api/src/routes/content/delete.ts +0 -82
- package/templates/apps/api/src/routes/content/update.ts +0 -112
- package/templates/apps/mcp/__tests__/create_page.test.ts +0 -202
- package/templates/apps/mcp/__tests__/delete_page.test.ts +0 -177
- package/templates/apps/mcp/__tests__/update_page.test.ts +0 -196
- package/templates/apps/mcp/src/tools/create_page.ts +0 -96
- package/templates/apps/mcp/src/tools/delete_page.ts +0 -74
- package/templates/apps/mcp/src/tools/update_page.ts +0 -92
- package/templates/apps/web/app/[locale]/sign-in/sign-in-card.tsx +0 -123
- /package/site-templates/{marketing-site-free → _base}/content/en/PrivacyPage/privacy.mdx +0 -0
- /package/site-templates/{marketing-site-free → _base}/content/en/TermsPage/terms.mdx +0 -0
- /package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/__tests__/next-path.test.ts +0 -0
- /package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/next-path.ts +0 -0
- /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
|
|
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
|
-
* -
|
|
8
|
-
* -
|
|
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
|
|
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 {
|
|
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("
|
|
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
|
-
`[
|
|
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("[
|
|
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
|
|
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("[
|
|
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
|
-
"[
|
|
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
|
|
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("[
|
|
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
|
-
"[
|
|
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
|
|
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("[
|
|
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
|
|
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("[
|
|
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
|
|
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("[
|
|
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
|
-
|
|
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/
|
|
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/
|
|
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
|