create-ailk 0.1.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/LICENSE +201 -0
- package/README.md +39 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +80 -0
- package/dist/copy.d.ts +8 -0
- package/dist/copy.js +109 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/lib/pro-callout.d.ts +12 -0
- package/dist/lib/pro-callout.js +20 -0
- package/dist/parse-args.d.ts +13 -0
- package/dist/parse-args.js +81 -0
- package/dist/programmatic.d.ts +49 -0
- package/dist/programmatic.js +89 -0
- package/dist/substitute.d.ts +31 -0
- package/dist/substitute.js +45 -0
- package/dist/validate.d.ts +8 -0
- package/dist/validate.js +70 -0
- package/package.json +47 -0
- package/templates/.claude/.gitkeep +0 -0
- package/templates/.claude/agents/web.md +80 -0
- package/templates/.claude/rules/architecture.md +62 -0
- package/templates/.claude/rules/preserve-existing-style.md +51 -0
- package/templates/.claude/skills/README.md +43 -0
- package/templates/.claude/skills/add-schema/SKILL.md +270 -0
- package/templates/.claude/skills/add-schema/references/conflict-handling.md +200 -0
- package/templates/.claude/skills/add-schema/references/file-finding.md +123 -0
- package/templates/.claude/skills/add-schema/references/schema-types.md +131 -0
- package/templates/.claude/skills/audit-site/SKILL.md +213 -0
- package/templates/.claude/skills/audit-site/references/common-errors.md +113 -0
- package/templates/.claude/skills/audit-site/references/findings-glossary.md +138 -0
- package/templates/.claude/skills/audit-site/references/severity-levels.md +114 -0
- package/templates/.claude/skills/provision-config/SKILL.md +243 -0
- package/templates/.claude/skills/provision-config/references/env-vars-template.md +184 -0
- package/templates/.claude/skills/provision-config/references/runbook-template.md +248 -0
- package/templates/.claude/skills/provision-config/references/vercel-json-template.md +98 -0
- package/templates/.claude/skills/scaffold-auth/SKILL.md +261 -0
- package/templates/.claude/skills/scaffold-auth/references/env-vars.md +91 -0
- package/templates/.claude/skills/scaffold-auth/references/oauth-app-setup.md +171 -0
- package/templates/.claude/skills/scaffold-commerce/SKILL.md +310 -0
- package/templates/.claude/skills/scaffold-commerce/references/lookup-keys-template.md +71 -0
- package/templates/.claude/skills/scaffold-commerce/references/marketplace-brand-pages.md +83 -0
- package/templates/.claude/skills/scaffold-commerce/templates/marketplace-cta-route.tsx.tmpl +72 -0
- package/templates/.claude/skills/scaffold-page/SKILL.md +243 -0
- package/templates/.claude/skills/scaffold-page/references/page-types.md +169 -0
- package/templates/.claude/skills/scaffold-page/templates/content.mdx.tmpl +28 -0
- package/templates/.claude/skills/scaffold-page/templates/route.tsx.tmpl +28 -0
- package/templates/.claude/skills/suggest-site-pages/SKILL.md +242 -0
- package/templates/.claude/skills/suggest-site-pages/references/categories.md +287 -0
- package/templates/.env.example +205 -0
- package/templates/.gitkeep +2 -0
- package/templates/CLAUDE.md +142 -0
- package/templates/CONVENTIONS.md +222 -0
- package/templates/README.md +110 -0
- package/templates/apps/api/.env.example +40 -0
- package/templates/apps/api/CLAUDE.md +63 -0
- package/templates/apps/api/api/index.ts +144 -0
- package/templates/apps/api/jest.config.cjs +74 -0
- package/templates/apps/api/package.json +54 -0
- package/templates/apps/api/src/__tests__/factories/content-fixture.ts +138 -0
- package/templates/apps/api/src/__tests__/factories/index.ts +70 -0
- package/templates/apps/api/src/__tests__/factories/lead-fixture.ts +82 -0
- package/templates/apps/api/src/__tests__/index.test.ts +118 -0
- package/templates/apps/api/src/__tests__/server.test.ts +207 -0
- package/templates/apps/api/src/__tests__/services/enrichment-shim.test.ts +126 -0
- package/templates/apps/api/src/__tests__/setup.ts +24 -0
- package/templates/apps/api/src/cli.ts +30 -0
- package/templates/apps/api/src/config/__tests__/index.test.ts +97 -0
- package/templates/apps/api/src/config/index.ts +56 -0
- package/templates/apps/api/src/index.ts +32 -0
- package/templates/apps/api/src/lib/__mocks__/prisma-client.js +15 -0
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +45 -0
- package/templates/apps/api/src/lib/__tests__/content-adapter.test.ts +178 -0
- package/templates/apps/api/src/lib/__tests__/error-serializer.test.ts +100 -0
- package/templates/apps/api/src/lib/__tests__/errors.test.ts +94 -0
- package/templates/apps/api/src/lib/__tests__/file-fallback.test.ts +144 -0
- package/templates/apps/api/src/lib/__tests__/fixtures/HomePage/home.mdx +8 -0
- package/templates/apps/api/src/lib/__tests__/lead-capture-store.test.ts +227 -0
- package/templates/apps/api/src/lib/__tests__/outbound-http.test.ts +125 -0
- package/templates/apps/api/src/lib/__tests__/prisma.test.ts +165 -0
- package/templates/apps/api/src/lib/__tests__/require-paid-tier.test.ts +129 -0
- package/templates/apps/api/src/lib/__tests__/stripe.test.ts +74 -0
- package/templates/apps/api/src/lib/content-adapter.ts +86 -0
- package/templates/apps/api/src/lib/error-serializer.ts +77 -0
- package/templates/apps/api/src/lib/errors.ts +95 -0
- package/templates/apps/api/src/lib/file-fallback.ts +87 -0
- package/templates/apps/api/src/lib/lead-capture-store.ts +110 -0
- package/templates/apps/api/src/lib/locales.ts +201 -0
- package/templates/apps/api/src/lib/outbound-http.ts +122 -0
- package/templates/apps/api/src/lib/prisma.ts +21 -0
- package/templates/apps/api/src/lib/require-paid-tier.ts +53 -0
- package/templates/apps/api/src/lib/stripe.ts +30 -0
- package/templates/apps/api/src/lib/webhook-idempotency.ts +92 -0
- package/templates/apps/api/src/middleware/__tests__/adversarial/bearer-token-format.test.ts +111 -0
- package/templates/apps/api/src/middleware/__tests__/adversarial/cookie-or-bearer-precedence.test.ts +172 -0
- package/templates/apps/api/src/middleware/__tests__/adversarial/csrf-bearer-skip.test.ts +119 -0
- package/templates/apps/api/src/middleware/__tests__/adversarial/csrf-cookie-path.test.ts +138 -0
- package/templates/apps/api/src/middleware/__tests__/adversarial/session-fixation.test.ts +149 -0
- package/templates/apps/api/src/middleware/__tests__/adversarial/session-timeout.test.ts +139 -0
- package/templates/apps/api/src/middleware/__tests__/auth.bearer-path.test.ts +175 -0
- package/templates/apps/api/src/middleware/__tests__/auth.both-present-precedence.test.ts +76 -0
- package/templates/apps/api/src/middleware/__tests__/auth.cookie-path.test.ts +153 -0
- package/templates/apps/api/src/middleware/__tests__/auth.neither-present.test.ts +72 -0
- package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +292 -0
- package/templates/apps/api/src/middleware/auth.ts +246 -0
- package/templates/apps/api/src/middleware/rate-limit.ts +132 -0
- package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +129 -0
- package/templates/apps/api/src/openapi/index.ts +12 -0
- package/templates/apps/api/src/openapi/spec.ts +408 -0
- package/templates/apps/api/src/routes/auth/__tests__/auth-tokens.functional.test.ts +193 -0
- package/templates/apps/api/src/routes/auth/__tests__/auth-user.functional.test.ts +201 -0
- package/templates/apps/api/src/routes/auth/__tests__/tokens.test.ts +294 -0
- package/templates/apps/api/src/routes/auth/__tests__/user.test.ts +486 -0
- package/templates/apps/api/src/routes/auth/tokens.ts +199 -0
- package/templates/apps/api/src/routes/auth/user.ts +244 -0
- package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +588 -0
- package/templates/apps/api/src/routes/checkout/index.ts +13 -0
- package/templates/apps/api/src/routes/checkout/sessions.ts +185 -0
- package/templates/apps/api/src/routes/content/__tests__/create.test.ts +239 -0
- package/templates/apps/api/src/routes/content/__tests__/delete.test.ts +201 -0
- package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +141 -0
- package/templates/apps/api/src/routes/content/__tests__/list-pages.test.ts +122 -0
- package/templates/apps/api/src/routes/content/__tests__/list-types.test.ts +44 -0
- package/templates/apps/api/src/routes/content/__tests__/locale.test.ts +118 -0
- package/templates/apps/api/src/routes/content/__tests__/rate-limit.test.ts +51 -0
- package/templates/apps/api/src/routes/content/__tests__/update.test.ts +252 -0
- package/templates/apps/api/src/routes/content/create.ts +114 -0
- package/templates/apps/api/src/routes/content/delete.ts +81 -0
- package/templates/apps/api/src/routes/content/get-page.ts +98 -0
- package/templates/apps/api/src/routes/content/index.ts +32 -0
- package/templates/apps/api/src/routes/content/list-pages.ts +83 -0
- package/templates/apps/api/src/routes/content/list-types.ts +19 -0
- package/templates/apps/api/src/routes/content/rate-limit.ts +13 -0
- package/templates/apps/api/src/routes/content/slug.ts +9 -0
- package/templates/apps/api/src/routes/content/update.ts +111 -0
- package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +415 -0
- package/templates/apps/api/src/routes/leads/__tests__/track.test.ts +165 -0
- package/templates/apps/api/src/routes/leads/index.ts +130 -0
- package/templates/apps/api/src/routes/test/auth/__tests__/session.test.ts +296 -0
- package/templates/apps/api/src/routes/test/auth/session.ts +141 -0
- package/templates/apps/api/src/routes/webhooks/README.md +57 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +247 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +198 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +633 -0
- package/templates/apps/api/src/routes/webhooks/index.ts +41 -0
- package/templates/apps/api/src/routes/webhooks/stripe.ts +253 -0
- package/templates/apps/api/src/server.ts +157 -0
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +395 -0
- package/templates/apps/api/src/services/enrichment-shim.ts +53 -0
- package/templates/apps/api/src/services/lead-routing.ts +338 -0
- package/templates/apps/api/tsconfig.json +34 -0
- package/templates/apps/api/vercel.json +17 -0
- package/templates/apps/mcp/.env.example +52 -0
- package/templates/apps/mcp/CLAUDE.md +84 -0
- package/templates/apps/mcp/__tests__/abuse/daily-cap.test.ts +52 -0
- package/templates/apps/mcp/__tests__/abuse/email-validator.test.ts +46 -0
- package/templates/apps/mcp/__tests__/abuse/honeypot.test.ts +36 -0
- package/templates/apps/mcp/__tests__/abuse/user-agent-deny.test.ts +54 -0
- package/templates/apps/mcp/__tests__/aeo-cli-parity.test.ts +110 -0
- package/templates/apps/mcp/__tests__/audit-single-page.test.ts +213 -0
- package/templates/apps/mcp/__tests__/audit.test.ts +217 -0
- package/templates/apps/mcp/__tests__/catalog-drift.test.ts +52 -0
- package/templates/apps/mcp/__tests__/create_page.test.ts +201 -0
- package/templates/apps/mcp/__tests__/delete_page.test.ts +176 -0
- package/templates/apps/mcp/__tests__/get_business_hours.test.ts +82 -0
- package/templates/apps/mcp/__tests__/get_faq.test.ts +80 -0
- package/templates/apps/mcp/__tests__/get_locations.test.ts +65 -0
- package/templates/apps/mcp/__tests__/get_offerings.test.ts +90 -0
- package/templates/apps/mcp/__tests__/get_pricing.test.ts +80 -0
- package/templates/apps/mcp/__tests__/get_site_info.test.ts +81 -0
- package/templates/apps/mcp/__tests__/http-transport.test.ts +323 -0
- package/templates/apps/mcp/__tests__/lib/errors.test.ts +75 -0
- package/templates/apps/mcp/__tests__/lib/require-paid-tier.test.ts +75 -0
- package/templates/apps/mcp/__tests__/list_pages.test.ts +112 -0
- package/templates/apps/mcp/__tests__/locale.test.ts +135 -0
- package/templates/apps/mcp/__tests__/parity.test.ts +205 -0
- package/templates/apps/mcp/__tests__/purchase-disabled.test.ts +34 -0
- package/templates/apps/mcp/__tests__/read_content.test.ts +153 -0
- package/templates/apps/mcp/__tests__/request_callback.test.ts +163 -0
- package/templates/apps/mcp/__tests__/search_content.test.ts +153 -0
- package/templates/apps/mcp/__tests__/server.test.ts +115 -0
- package/templates/apps/mcp/__tests__/submit_lead.test.ts +165 -0
- package/templates/apps/mcp/__tests__/subscribe_newsletter.test.ts +161 -0
- package/templates/apps/mcp/__tests__/tools/purchase.test.ts +227 -0
- package/templates/apps/mcp/__tests__/tools/scaffold.test.ts +237 -0
- package/templates/apps/mcp/__tests__/update_page.test.ts +195 -0
- package/templates/apps/mcp/jest.config.cjs +82 -0
- package/templates/apps/mcp/package.json +37 -0
- package/templates/apps/mcp/src/abuse/daily-cap.ts +47 -0
- package/templates/apps/mcp/src/abuse/email-validator.ts +25 -0
- package/templates/apps/mcp/src/abuse/honeypot.ts +21 -0
- package/templates/apps/mcp/src/abuse/index.ts +111 -0
- package/templates/apps/mcp/src/abuse/user-agent-deny.ts +58 -0
- package/templates/apps/mcp/src/http-transport.ts +303 -0
- package/templates/apps/mcp/src/index.ts +67 -0
- package/templates/apps/mcp/src/lib/audit-single-page.ts +194 -0
- package/templates/apps/mcp/src/lib/errors.ts +61 -0
- package/templates/apps/mcp/src/lib/locales.ts +171 -0
- package/templates/apps/mcp/src/lib/require-paid-tier.ts +44 -0
- package/templates/apps/mcp/src/lib/resolve-locale.ts +30 -0
- package/templates/apps/mcp/src/server.ts +139 -0
- package/templates/apps/mcp/src/tools/audit.ts +103 -0
- package/templates/apps/mcp/src/tools/capture_lead.ts +52 -0
- package/templates/apps/mcp/src/tools/create_page.ts +95 -0
- package/templates/apps/mcp/src/tools/delete_page.ts +73 -0
- package/templates/apps/mcp/src/tools/get_business_hours.ts +66 -0
- package/templates/apps/mcp/src/tools/get_faq.ts +69 -0
- package/templates/apps/mcp/src/tools/get_locations.ts +85 -0
- package/templates/apps/mcp/src/tools/get_offerings.ts +66 -0
- package/templates/apps/mcp/src/tools/get_pricing.ts +74 -0
- package/templates/apps/mcp/src/tools/get_site_info.ts +84 -0
- package/templates/apps/mcp/src/tools/index.ts +125 -0
- package/templates/apps/mcp/src/tools/list_pages.ts +72 -0
- package/templates/apps/mcp/src/tools/purchase.ts +89 -0
- package/templates/apps/mcp/src/tools/read_content.ts +61 -0
- package/templates/apps/mcp/src/tools/request_callback.ts +51 -0
- package/templates/apps/mcp/src/tools/scaffold.ts +119 -0
- package/templates/apps/mcp/src/tools/search_content.ts +59 -0
- package/templates/apps/mcp/src/tools/subscribe_newsletter.ts +47 -0
- package/templates/apps/mcp/src/tools/update_page.ts +91 -0
- package/templates/apps/mcp/tsconfig.json +19 -0
- package/templates/apps/web/.env.example +47 -0
- package/templates/apps/web/CLAUDE.md +81 -0
- package/templates/apps/web/__tests__/auth/adversarial/cookie-attributes.test.ts +94 -0
- package/templates/apps/web/__tests__/auth/adversarial/oauth-state-parameter.test.ts +75 -0
- package/templates/apps/web/__tests__/auth/adversarial/redirect-uri-allowlist.test.ts +82 -0
- package/templates/apps/web/__tests__/auth/env-helpers.test.ts +168 -0
- package/templates/apps/web/__tests__/authority-list-routes.test.tsx +208 -0
- package/templates/apps/web/__tests__/better-stack.test.ts +66 -0
- package/templates/apps/web/__tests__/blog-dir-resolution.test.ts +87 -0
- package/templates/apps/web/__tests__/blog-source-populated.test.ts +130 -0
- package/templates/apps/web/__tests__/data-testid-coverage.test.tsx +60 -0
- package/templates/apps/web/__tests__/google-analytics.test.tsx +104 -0
- package/templates/apps/web/__tests__/i18n/middleware.test.ts +119 -0
- package/templates/apps/web/__tests__/i18n/routing.test.ts +114 -0
- package/templates/apps/web/__tests__/i18n/translations.test.ts +191 -0
- package/templates/apps/web/__tests__/json-ld.test.ts +98 -0
- package/templates/apps/web/__tests__/knowledge-events-routes.test.tsx +580 -0
- package/templates/apps/web/__tests__/layout-stealth.test.ts +39 -0
- package/templates/apps/web/__tests__/lighthouse-fixtures/improvement-image-shrink.tsx +103 -0
- package/templates/apps/web/__tests__/lighthouse-fixtures/override-fixture.md +114 -0
- package/templates/apps/web/__tests__/lighthouse-fixtures/regression-bloat.tsx +94 -0
- package/templates/apps/web/__tests__/page.test.tsx +230 -0
- package/templates/apps/web/__tests__/products-services-phase1.test.tsx +321 -0
- package/templates/apps/web/__tests__/projects-routes.test.tsx +392 -0
- package/templates/apps/web/__tests__/resources-downloads-integrations-phase2.test.tsx +475 -0
- package/templates/apps/web/__tests__/routes/ai-plugin-json.test.ts +53 -0
- package/templates/apps/web/__tests__/routes/blog-route.test.tsx +191 -0
- package/templates/apps/web/__tests__/routes/llms-txt.test.ts +123 -0
- package/templates/apps/web/__tests__/routes/robots-txt.test.ts +52 -0
- package/templates/apps/web/__tests__/routes/sitemap-xml.test.ts +78 -0
- package/templates/apps/web/__tests__/routes/well-known-mcp.test.ts +203 -0
- package/templates/apps/web/__tests__/section-routes.test.tsx +693 -0
- package/templates/apps/web/__tests__/security-headers.test.ts +201 -0
- package/templates/apps/web/__tests__/sentry-config.test.ts +116 -0
- package/templates/apps/web/__tests__/setup.ts +82 -0
- package/templates/apps/web/__tests__/sitemap-locales.test.ts +415 -0
- package/templates/apps/web/__tests__/standalone-informational-routes.test.tsx +844 -0
- package/templates/apps/web/app/.well-known/ai-plugin.json/route.ts +81 -0
- package/templates/apps/web/app/.well-known/mcp.json/build-manifest.ts +110 -0
- package/templates/apps/web/app/.well-known/mcp.json/route.ts +58 -0
- package/templates/apps/web/app/[locale]/__tests__/layout.test.tsx +120 -0
- package/templates/apps/web/app/[locale]/__tests__/page-renderer-migration.test.tsx +202 -0
- package/templates/apps/web/app/[locale]/about/page.tsx +31 -0
- package/templates/apps/web/app/[locale]/blog/[[...slug]]/page.tsx +173 -0
- package/templates/apps/web/app/[locale]/careers/[slug]/page.tsx +45 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +117 -0
- package/templates/apps/web/app/[locale]/careers/page.tsx +25 -0
- package/templates/apps/web/app/[locale]/case-studies/[slug]/page.tsx +44 -0
- package/templates/apps/web/app/[locale]/case-studies/__tests__/page-renderer-migration.test.tsx +113 -0
- package/templates/apps/web/app/[locale]/case-studies/page.tsx +25 -0
- package/templates/apps/web/app/[locale]/clients/__tests__/page-renderer-migration.test.tsx +63 -0
- package/templates/apps/web/app/[locale]/clients/page.tsx +27 -0
- package/templates/apps/web/app/[locale]/contact/ContactForm.tsx +130 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/contact.actions.test.ts +152 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +92 -0
- package/templates/apps/web/app/[locale]/contact/contact.actions.ts +79 -0
- package/templates/apps/web/app/[locale]/contact/error.tsx +24 -0
- package/templates/apps/web/app/[locale]/contact/page.tsx +38 -0
- package/templates/apps/web/app/[locale]/courses/[slug]/page.tsx +34 -0
- package/templates/apps/web/app/[locale]/courses/__tests__/page-renderer-migration.test.tsx +119 -0
- package/templates/apps/web/app/[locale]/courses/page.tsx +25 -0
- package/templates/apps/web/app/[locale]/dev/purchase/__tests__/actions.test.ts +160 -0
- package/templates/apps/web/app/[locale]/dev/purchase/error.tsx +25 -0
- package/templates/apps/web/app/[locale]/dev/purchase/page.tsx +117 -0
- package/templates/apps/web/app/[locale]/dev/purchase/purchase.actions.ts +58 -0
- package/templates/apps/web/app/[locale]/docs/[[...slug]]/page.tsx +118 -0
- package/templates/apps/web/app/[locale]/downloads/__tests__/page-renderer-migration.test.tsx +72 -0
- package/templates/apps/web/app/[locale]/downloads/page.tsx +25 -0
- package/templates/apps/web/app/[locale]/events/[slug]/page.tsx +47 -0
- package/templates/apps/web/app/[locale]/events/__tests__/page-renderer-migration.test.tsx +130 -0
- package/templates/apps/web/app/[locale]/events/page.tsx +25 -0
- package/templates/apps/web/app/[locale]/faq/page.tsx +36 -0
- package/templates/apps/web/app/[locale]/how-to/[slug]/page.tsx +34 -0
- package/templates/apps/web/app/[locale]/how-to/__tests__/page-renderer-migration.test.tsx +78 -0
- package/templates/apps/web/app/[locale]/how-to/page.tsx +40 -0
- package/templates/apps/web/app/[locale]/integrations/[slug]/page.tsx +43 -0
- package/templates/apps/web/app/[locale]/integrations/__tests__/page-renderer-migration.test.tsx +129 -0
- package/templates/apps/web/app/[locale]/integrations/page.tsx +25 -0
- package/templates/apps/web/app/[locale]/launch-service/page.tsx +31 -0
- package/templates/apps/web/app/[locale]/layout.tsx +241 -0
- package/templates/apps/web/app/[locale]/page.tsx +41 -0
- package/templates/apps/web/app/[locale]/partners/[slug]/page.tsx +44 -0
- package/templates/apps/web/app/[locale]/partners/__tests__/page-renderer-migration.test.tsx +113 -0
- package/templates/apps/web/app/[locale]/partners/page.tsx +25 -0
- package/templates/apps/web/app/[locale]/products/[slug]/page.tsx +43 -0
- package/templates/apps/web/app/[locale]/products/__tests__/page-renderer-migration.test.tsx +129 -0
- package/templates/apps/web/app/[locale]/products/page.tsx +25 -0
- package/templates/apps/web/app/[locale]/projects/[slug]/page.tsx +39 -0
- package/templates/apps/web/app/[locale]/projects/__tests__/page-renderer-migration.test.tsx +138 -0
- package/templates/apps/web/app/[locale]/projects/page.tsx +30 -0
- package/templates/apps/web/app/[locale]/resources/[slug]/page.tsx +51 -0
- package/templates/apps/web/app/[locale]/resources/__tests__/page-renderer-migration.test.tsx +117 -0
- package/templates/apps/web/app/[locale]/resources/page.tsx +27 -0
- package/templates/apps/web/app/[locale]/services/[slug]/page.tsx +43 -0
- package/templates/apps/web/app/[locale]/services/__tests__/page-renderer-migration.test.tsx +129 -0
- package/templates/apps/web/app/[locale]/services/page.tsx +25 -0
- package/templates/apps/web/app/[locale]/software/[slug]/page.tsx +34 -0
- package/templates/apps/web/app/[locale]/software/__tests__/page-renderer-migration.test.tsx +81 -0
- package/templates/apps/web/app/[locale]/software/page.tsx +39 -0
- package/templates/apps/web/app/[locale]/tech-articles/[slug]/page.tsx +34 -0
- package/templates/apps/web/app/[locale]/tech-articles/__tests__/page-renderer-migration.test.tsx +82 -0
- package/templates/apps/web/app/[locale]/tech-articles/page.tsx +39 -0
- package/templates/apps/web/app/[locale]/testimonials/__tests__/page-renderer-migration.test.tsx +63 -0
- package/templates/apps/web/app/[locale]/testimonials/page.tsx +27 -0
- package/templates/apps/web/app/[locale]/use-cases/__tests__/page-renderer-migration.test.tsx +63 -0
- package/templates/apps/web/app/[locale]/use-cases/page.tsx +27 -0
- package/templates/apps/web/app/[locale]/whitepapers/[slug]/page.tsx +34 -0
- package/templates/apps/web/app/[locale]/whitepapers/__tests__/page-renderer-migration.test.tsx +82 -0
- package/templates/apps/web/app/[locale]/whitepapers/page.tsx +39 -0
- package/templates/apps/web/app/__tests__/error-boundaries.test.tsx +152 -0
- package/templates/apps/web/app/api/auth/[...all]/route.ts +16 -0
- package/templates/apps/web/app/error-fallback.tsx +75 -0
- package/templates/apps/web/app/error.tsx +23 -0
- package/templates/apps/web/app/layout.tsx +18 -0
- package/templates/apps/web/app/llms.txt/route.ts +101 -0
- package/templates/apps/web/app/not-found.tsx +48 -0
- package/templates/apps/web/app/og/__tests__/route.test.ts +164 -0
- package/templates/apps/web/app/og/route.tsx +135 -0
- package/templates/apps/web/app/robots.txt/route.ts +36 -0
- package/templates/apps/web/app/sitemap.xml/route.ts +146 -0
- package/templates/apps/web/auth.ts +141 -0
- package/templates/apps/web/components/ThemeToggle.tsx +60 -0
- package/templates/apps/web/components/google-analytics.tsx +34 -0
- package/templates/apps/web/content/en/blog/index.mdx +15 -0
- package/templates/apps/web/content/en/blog/launching-ailk.mdx +20 -0
- package/templates/apps/web/content/en/docs/getting-started.mdx +30 -0
- package/templates/apps/web/i18n/request.ts +36 -0
- package/templates/apps/web/jest.config.cjs +100 -0
- package/templates/apps/web/lib/__mocks__/next-mdx-remote-rsc.js +12 -0
- package/templates/apps/web/lib/__mocks__/source-server.js +31 -0
- package/templates/apps/web/lib/__tests__/og.test.ts +65 -0
- package/templates/apps/web/lib/auth-env.ts +66 -0
- package/templates/apps/web/lib/blog-list-data.ts +95 -0
- package/templates/apps/web/lib/blog-post-data.ts +79 -0
- package/templates/apps/web/lib/nav-config.ts +13 -0
- package/templates/apps/web/lib/observability/better-stack.ts +27 -0
- package/templates/apps/web/lib/og.ts +23 -0
- package/templates/apps/web/lib/prisma-for-auth.ts +34 -0
- package/templates/apps/web/lib/source.ts +34 -0
- package/templates/apps/web/messages/en.json +131 -0
- package/templates/apps/web/middleware.ts +80 -0
- package/templates/apps/web/next.config.mjs +167 -0
- package/templates/apps/web/package.json +62 -0
- package/templates/apps/web/public/.well-known/security.txt +17 -0
- package/templates/apps/web/public/android-chrome-192x192.png +0 -0
- package/templates/apps/web/public/android-chrome-512x512.png +0 -0
- package/templates/apps/web/public/apple-touch-icon.png +0 -0
- package/templates/apps/web/public/favicon-16x16.png +0 -0
- package/templates/apps/web/public/favicon-32x32.png +0 -0
- package/templates/apps/web/public/favicon.ico +0 -0
- package/templates/apps/web/public/favicon.svg +4 -0
- package/templates/apps/web/public/logo-dark.svg +21 -0
- package/templates/apps/web/public/logo-light.svg +21 -0
- package/templates/apps/web/public/logomark-dark.svg +4 -0
- package/templates/apps/web/public/logomark-light.svg +4 -0
- package/templates/apps/web/public/og-default.png +0 -0
- package/templates/apps/web/public/site.webmanifest +19 -0
- package/templates/apps/web/scripts/assert-content-bundled.ts +55 -0
- package/templates/apps/web/security-headers.config.ts +132 -0
- package/templates/apps/web/sentry.client.config.ts +27 -0
- package/templates/apps/web/sentry.edge.config.ts +15 -0
- package/templates/apps/web/sentry.server.config.ts +20 -0
- package/templates/apps/web/source.config.ts +78 -0
- package/templates/apps/web/src/components/SiteNav.tsx +21 -0
- package/templates/apps/web/src/lib/routes.ts +37 -0
- package/templates/apps/web/tsconfig.json +35 -0
- package/templates/apps/web/types/messages.d.ts +19 -0
- package/templates/content/.gitkeep +0 -0
- package/templates/content/CLAUDE.md +85 -0
- package/templates/content/_site.mdx +38 -0
- package/templates/content/ar/HomePage/home.mdx +123 -0
- package/templates/content/ar/_site.mdx +35 -0
- package/templates/content/de/_site.mdx +29 -0
- package/templates/content/en/.gitkeep +0 -0
- package/templates/content/en/AboutPage/about.mdx +19 -0
- package/templates/content/en/AboutPage/about.schema.json +82 -0
- package/templates/content/en/BookCall/book-call.mdx +19 -0
- package/templates/content/en/BookCall/book-call.schema.json +121 -0
- package/templates/content/en/BreadcrumbList/site-breadcrumbs.mdx +20 -0
- package/templates/content/en/CareersList/careers.mdx +24 -0
- package/templates/content/en/CaseStudiesList/case-studies.mdx +22 -0
- package/templates/content/en/CaseStudiesList/showcase.mdx +8 -0
- package/templates/content/en/CaseStudy/ailk-aeo-rollout.mdx +32 -0
- package/templates/content/en/CaseStudy/schema-builder-migration.mdx +41 -0
- package/templates/content/en/ClientsList/clients.mdx +38 -0
- package/templates/content/en/ContactPage/contact.mdx +19 -0
- package/templates/content/en/ContactPage/contact.schema.json +100 -0
- package/templates/content/en/ContactPoint/primary.mdx +14 -0
- package/templates/content/en/Course/aeo-fundamentals.mdx +37 -0
- package/templates/content/en/Course/schema-builder-workshop.mdx +62 -0
- package/templates/content/en/CoursesList/courses.mdx +22 -0
- package/templates/content/en/DownloadsList/downloads.mdx +22 -0
- package/templates/content/en/Event/aeo-office-hours-may-2026.mdx +39 -0
- package/templates/content/en/Event/ailk-launch-day-2026.mdx +35 -0
- package/templates/content/en/EventsList/events.mdx +26 -0
- package/templates/content/en/FAQPage/faq.mdx +420 -0
- package/templates/content/en/FAQPage/faq.schema.json +231 -0
- package/templates/content/en/GlossaryList/glossary.mdx +19 -0
- package/templates/content/en/GlossaryList/glossary.schema.json +300 -0
- package/templates/content/en/HomePage/home.mdx +312 -0
- package/templates/content/en/HomePage/home.schema.json +88 -0
- package/templates/content/en/HomePage/launch-service.mdx +475 -0
- package/templates/content/en/HomePage/launch-service.schema.json +138 -0
- package/templates/content/en/HowTo/add-mdx-content.mdx +38 -0
- package/templates/content/en/HowTo/configure-aeo-scoring.mdx +35 -0
- package/templates/content/en/HowTo/set-up-json-ld.mdx +36 -0
- package/templates/content/en/Integration/github-actions.mdx +31 -0
- package/templates/content/en/Integration/openai-api.mdx +26 -0
- package/templates/content/en/Integration/vercel.mdx +32 -0
- package/templates/content/en/IntegrationsList/integrations.mdx +22 -0
- package/templates/content/en/JobPosting/content-strategist.mdx +46 -0
- package/templates/content/en/JobPosting/developer-advocate.mdx +47 -0
- package/templates/content/en/JobPosting/senior-fullstack-engineer.mdx +57 -0
- package/templates/content/en/LandingPage/aeo-audit.mdx +19 -0
- package/templates/content/en/LandingPage/aeo-audit.schema.json +92 -0
- package/templates/content/en/LandingPage/agency-waitlist.mdx +340 -0
- package/templates/content/en/LandingPage/agency-waitlist.schema.json +152 -0
- package/templates/content/en/LandingPage/developers.mdx +19 -0
- package/templates/content/en/LandingPage/developers.schema.json +109 -0
- package/templates/content/en/LandingPage/for-agencies.mdx +390 -0
- package/templates/content/en/LandingPage/for-agencies.schema.json +107 -0
- package/templates/content/en/LandingPage/for-ai-search-agencies.mdx +417 -0
- package/templates/content/en/LandingPage/for-ai-search-agencies.schema.json +105 -0
- package/templates/content/en/LandingPage/for-businesses.mdx +19 -0
- package/templates/content/en/LandingPage/for-businesses.schema.json +96 -0
- package/templates/content/en/LandingPage/platform.mdx +369 -0
- package/templates/content/en/LandingPage/platform.schema.json +166 -0
- package/templates/content/en/LandingPage/vs-ai-website-builders.mdx +19 -0
- package/templates/content/en/LandingPage/vs-ai-website-builders.schema.json +102 -0
- package/templates/content/en/LandingPage/vs-wordpress.mdx +19 -0
- package/templates/content/en/LandingPage/vs-wordpress.schema.json +53 -0
- package/templates/content/en/NewsletterSignup/newsletter.mdx +236 -0
- package/templates/content/en/NewsletterSignup/newsletter.schema.json +91 -0
- package/templates/content/en/Organization/site-owner.mdx +16 -0
- package/templates/content/en/Partner/prisma.mdx +31 -0
- package/templates/content/en/Partner/supabase.mdx +30 -0
- package/templates/content/en/Partner/vercel.mdx +28 -0
- package/templates/content/en/PartnersList/partners.mdx +22 -0
- package/templates/content/en/Person/founder.mdx +18 -0
- package/templates/content/en/Pricing/pricing.mdx +538 -0
- package/templates/content/en/Pricing/pricing.schema.json +209 -0
- package/templates/content/en/PrivacyPage/privacy.mdx +165 -0
- package/templates/content/en/PrivacyPage/privacy.schema.json +64 -0
- package/templates/content/en/Product/ailk.mdx +19 -0
- package/templates/content/en/Product/ailk.schema.json +94 -0
- package/templates/content/en/Product/api-launch-kit.mdx +26 -0
- package/templates/content/en/Product/launch-kit.mdx +28 -0
- package/templates/content/en/ProductsList/products.mdx +26 -0
- package/templates/content/en/Project/ailk-phase-2.mdx +54 -0
- package/templates/content/en/ProjectsList/projects.mdx +25 -0
- package/templates/content/en/ResourceDetail/aeo-scoring-guide.mdx +27 -0
- package/templates/content/en/ResourceDetail/content-model-whitepaper.mdx +28 -0
- package/templates/content/en/ResourceDetail/schema-org-quick-reference.mdx +39 -0
- package/templates/content/en/ResourcesHub/resources.mdx +22 -0
- package/templates/content/en/Service/consulting.mdx +22 -0
- package/templates/content/en/Service/implementation.mdx +30 -0
- package/templates/content/en/Service/launch-service.mdx +232 -0
- package/templates/content/en/Service/launch.mdx +228 -0
- package/templates/content/en/ServicesList/services.mdx +84 -0
- package/templates/content/en/SoftwareProduct/ailk-aeo.mdx +44 -0
- package/templates/content/en/SoftwareProduct/ailk-content-adapters.mdx +47 -0
- package/templates/content/en/SoftwareProduct/ailk-schema.mdx +49 -0
- package/templates/content/en/TeamList/team.mdx +63 -0
- package/templates/content/en/TeamMember/founder.mdx +72 -0
- package/templates/content/en/TermsPage/terms.mdx +19 -0
- package/templates/content/en/TermsPage/terms.schema.json +99 -0
- package/templates/content/en/TestimonialsList/testimonials.mdx +44 -0
- package/templates/content/en/UseCasesList/use-cases.mdx +30 -0
- package/templates/content/en/Whitepaper/ax-first-product-development.mdx +27 -0
- package/templates/content/en/Whitepaper/open-core-commercial-strategy.mdx +27 -0
- package/templates/content/en/Whitepaper/structured-data-for-answer-engines.mdx +31 -0
- package/templates/content/en-XA/AboutPage/about.mdx +8 -0
- package/templates/content/en-XA/FAQPage/faq.mdx +8 -0
- package/templates/content/en-XA/HomePage/home.mdx +8 -0
- package/templates/content/es/_site.mdx +30 -0
- package/templates/content/fr/_site.mdx +30 -0
- package/templates/content/ja/_site.mdx +30 -0
- package/templates/content/pt-BR/AboutPage/about.mdx +8 -0
- package/templates/content/pt-BR/FAQPage/faq.mdx +8 -0
- package/templates/content/pt-BR/HomePage/home.mdx +23 -0
- package/templates/database/CHANGELOG.md +5 -0
- package/templates/database/README.md +234 -0
- package/templates/database/__tests__/user-cascade.test.ts +125 -0
- package/templates/database/auth/.gitkeep +0 -0
- package/templates/database/config/.gitkeep +0 -0
- package/templates/database/content/.gitkeep +0 -0
- package/templates/database/inbox/.gitkeep +0 -0
- package/templates/database/inbox/README.md +3 -0
- package/templates/database/inbox/__tests__/schema.test.ts +40 -0
- package/templates/database/inbox/schema.prisma +52 -0
- package/templates/database/jest.config.cjs +29 -0
- package/templates/database/migrations/20260508000000_add_inbox_namespace/migration.sql +328 -0
- package/templates/database/migrations/20260513000000_better_auth_migration/migration.sql +100 -0
- package/templates/database/migrations/20260515000000_add_user_billing_fields/migration.sql +28 -0
- package/templates/database/migrations/20260516000000_add_user_tier_care_plan_seat/migration.sql +25 -0
- package/templates/database/migrations/20260518000000_add_telemetry_records/migration.sql +42 -0
- package/templates/database/migrations/20260525000000_theme_wiring/migration.sql +67 -0
- package/templates/database/migrations/20260710000000_retire_commercial_tier/migration.sql +21 -0
- package/templates/database/migrations/migration_lock.toml +3 -0
- package/templates/database/ops/.gitkeep +0 -0
- package/templates/database/package.json +45 -0
- package/templates/database/prisma.config.ts +20 -0
- package/templates/database/schema.prisma +487 -0
- package/templates/database/scripts/db-generate-locked.sh +80 -0
- package/templates/database/seed-test.ts +85 -0
- package/templates/database/tsconfig.build.json +16 -0
- package/templates/database/tsconfig.json +9 -0
- package/templates/package.json +125 -0
- package/templates/pnpm-workspace.yaml +6 -0
- package/templates/project.yaml +59 -0
- package/templates/tsconfig.base.json +18 -0
- package/templates/tsconfig.json +45 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# AI Launch Kit — root environment template
|
|
2
|
+
# Copy to .env (gitignored) and fill in values.
|
|
3
|
+
# Per-app .env files live alongside their app (apps/api/.env, etc.).
|
|
4
|
+
|
|
5
|
+
# ── Marketing / canonical domain ─────────────────────────────────────────────
|
|
6
|
+
AILK_PUBLIC_DOMAIN=ailaunchkit.ai
|
|
7
|
+
|
|
8
|
+
# ── Launch posture ───────────────────────────────────────────────────────────
|
|
9
|
+
# STEALTH defaults to "true" for new installs so a freshly-deployed instance
|
|
10
|
+
# isn't indexed by crawlers or unfurled by Slack/Twitter/LinkedIn before the
|
|
11
|
+
# operator is ready. Three layers gate on this single flag:
|
|
12
|
+
# 1. apps-paid/marketing-web/public/robots.txt (always-blocking static file)
|
|
13
|
+
# 2. X-Robots-Tag: noindex, nofollow, noarchive HTTP header (next.config.ts)
|
|
14
|
+
# 3. <meta name="robots" content="noindex,nofollow"> tag (app/layout.tsx)
|
|
15
|
+
# At launch, set STEALTH=false on the hosting platform's env vars and redeploy.
|
|
16
|
+
# See README.md § "Going live" for the full flip checklist.
|
|
17
|
+
STEALTH=true
|
|
18
|
+
|
|
19
|
+
# Blog-OFF launch gate (#3413) — SITE-ARCHITECTURE §4.1 parks the blog at
|
|
20
|
+
# launch (SITE-PLAN derived.blog_enabled=false). While unset/false, every
|
|
21
|
+
# public blog surface is gated: /blog + posts 404, /blog/rss.xml and
|
|
22
|
+
# /blog/<slug>.md 404, sitemap/llms.txt omit blog entries, and the nav/footer
|
|
23
|
+
# hide the Blog link. Set BLOG_ENABLED=true and redeploy to launch the blog.
|
|
24
|
+
# BLOG_ENABLED=true
|
|
25
|
+
|
|
26
|
+
# ── Content backend ──────────────────────────────────────────────────────────
|
|
27
|
+
# Default: mdx — reads from /content/*.mdx at the repo root.
|
|
28
|
+
# Set to "pro-cms" to use the paid Pro CMS adapter (separate package, not
|
|
29
|
+
# bundled with the OSS template).
|
|
30
|
+
CONTENT_BACKEND=mdx
|
|
31
|
+
# Override the content directory (default: <repo-root>/content). Useful for tests.
|
|
32
|
+
# AILK_CONTENT_ROOT=
|
|
33
|
+
|
|
34
|
+
# ── Database (optional in standalone mode) ───────────────────────────────────
|
|
35
|
+
# DATABASE_URL=postgres://user:pass@host:5432/db
|
|
36
|
+
|
|
37
|
+
# ── Stripe (sandbox keys only — never sk_live_/pk_live_) ─────────────────────
|
|
38
|
+
# STRIPE_SECRET_KEY=sk_test_...
|
|
39
|
+
# Stripe webhook signing secret — used by POST /api/webhooks/stripe to
|
|
40
|
+
# verify event payloads. Get from the Stripe Dashboard:
|
|
41
|
+
# Developers → Webhooks → <your endpoint> → Signing secret.
|
|
42
|
+
# Format: whsec_*. Server-only — must NOT be prefixed with NEXT_PUBLIC_.
|
|
43
|
+
# STRIPE_WEBHOOK_SECRET=
|
|
44
|
+
# Internal URL of the billing service — used by marketing-api's webhook
|
|
45
|
+
# proxy (POST /v1/webhooks/stripe) to forward verified deliveries. Format:
|
|
46
|
+
# https://<vercel-deployment>.vercel.app (no trailing slash, no path).
|
|
47
|
+
# BILLING_SERVICE_URL=
|
|
48
|
+
|
|
49
|
+
# ── Auth (Better Auth — apps/web only) ───────────────────────────────────────
|
|
50
|
+
# BETTER_AUTH_URL=http://localhost:3000
|
|
51
|
+
# BETTER_AUTH_SECRET=
|
|
52
|
+
|
|
53
|
+
# ── Email provider (Resend default) ──────────────────────────────────────────
|
|
54
|
+
# RESEND_API_KEY=
|
|
55
|
+
|
|
56
|
+
# ── Analytics ────────────────────────────────────────────────────────────────
|
|
57
|
+
# All providers off by default. Set the env var(s) for any provider(s) you want.
|
|
58
|
+
# Each is independent — set one, two, all three, or none.
|
|
59
|
+
#
|
|
60
|
+
# Vercel Analytics — built-in dashboard for Vercel deploys. Requires Vercel hosting.
|
|
61
|
+
# Set to "true" to enable. Requires redeploy after change (NEXT_PUBLIC_* baking).
|
|
62
|
+
# NEXT_PUBLIC_VERCEL_ANALYTICS_ENABLED=
|
|
63
|
+
#
|
|
64
|
+
# Google Analytics 4 — gtag.js measurement. Requires consent in EU/UK/CA.
|
|
65
|
+
# Get the measurement ID from analytics.google.com → Admin → Data Streams.
|
|
66
|
+
# Format: G-XXXXXXXXXX. Requires redeploy after change (NEXT_PUBLIC_* baking).
|
|
67
|
+
# NEXT_PUBLIC_GA_MEASUREMENT_ID=
|
|
68
|
+
#
|
|
69
|
+
# Google Analytics tag — env-driven gtag.js wiring used by the three Next.js
|
|
70
|
+
# apps (apps/web, apps-paid/marketing-web, apps-paid/dashboard-web). When set,
|
|
71
|
+
# every page emits the canonical gtag.js loader + inline init in <head>.
|
|
72
|
+
# Empty by default so new AILK installs do NOT pre-configure a GA property —
|
|
73
|
+
# customers set their own measurement ID after registering a GA4 stream.
|
|
74
|
+
# Format: G-XXXXXXXXXX (Google Analytics 4 measurement ID). See README.md
|
|
75
|
+
# § "Going live" → "Analytics" for the configured wire-up flow. Distinct from
|
|
76
|
+
# NEXT_PUBLIC_GA_MEASUREMENT_ID above — that var drives the consent-gated
|
|
77
|
+
# @working-theory/analytics adapter; this one drives the always-on env-presence path
|
|
78
|
+
# (Sam's call per issue #886). Most installs will set ONE of the two.
|
|
79
|
+
# NEXT_PUBLIC_GOOGLE_TAG_ID=
|
|
80
|
+
#
|
|
81
|
+
# Google Search Console — verification meta tag. No script, no consent needed.
|
|
82
|
+
# Get the token from search.google.com/search-console → Settings → Ownership
|
|
83
|
+
# verification → HTML tag. Paste only the content="..." value (not the full tag).
|
|
84
|
+
# GOOGLE_SITE_VERIFICATION=
|
|
85
|
+
#
|
|
86
|
+
# GA4 Measurement Protocol — server-side analytics secret.
|
|
87
|
+
# Required for `lead-captured` and `purchase-completed` events to reach
|
|
88
|
+
# Google Analytics 4 from apps/api. Get from analytics.google.com →
|
|
89
|
+
# Admin → Data Streams → API secrets. Server-only — must NOT be prefixed
|
|
90
|
+
# with NEXT_PUBLIC_.
|
|
91
|
+
# GA_API_SECRET=
|
|
92
|
+
|
|
93
|
+
# ── Error monitoring (Sentry) ────────────────────────────────────────────────
|
|
94
|
+
# All Sentry env vars empty by default — a fresh AILK install sends ZERO
|
|
95
|
+
# traffic to Sentry until you plug in your own DSN. Same OSS-default-on
|
|
96
|
+
# pattern as #884 (stealth) and #886 (Google Analytics).
|
|
97
|
+
#
|
|
98
|
+
# The five vars below cover both runtime (DSN) and build-time (auth token +
|
|
99
|
+
# org/project for source-map upload). When SENTRY_DSN is unset, every
|
|
100
|
+
# Sentry.init() call resolves to `enabled: false` and the SDK is a no-op —
|
|
101
|
+
# no network, no captures, no console noise. When SENTRY_AUTH_TOKEN +
|
|
102
|
+
# SENTRY_ORG + SENTRY_PROJECT are unset, the build-time source-map upload
|
|
103
|
+
# step skips silently — `next build` still succeeds.
|
|
104
|
+
#
|
|
105
|
+
# To turn Sentry on:
|
|
106
|
+
# 1. Create a Sentry project at https://sentry.io.
|
|
107
|
+
# 2. Copy the project's DSN (Settings → Projects → <project> → Client Keys).
|
|
108
|
+
# 3. Mint a build-time auth token at https://sentry.io/settings/auth-tokens/
|
|
109
|
+
# with scopes: project:releases, org:read.
|
|
110
|
+
# 4. Set all five vars on your hosting platform's env config.
|
|
111
|
+
# 5. Redeploy. See README.md § "Going live" → "Error monitoring (Sentry)".
|
|
112
|
+
#
|
|
113
|
+
# Server-runtime DSN — read by sentry.server.config.ts / sentry.edge.config.ts
|
|
114
|
+
# in each Next.js app and by src/lib/sentry-init.ts in each Fastify app.
|
|
115
|
+
# Format: https://<key>@<org>.ingest.sentry.io/<project-id>. Server-only.
|
|
116
|
+
# SENTRY_DSN=
|
|
117
|
+
#
|
|
118
|
+
# Browser DSN — read by sentry.client.config.ts in each Next.js app. Must
|
|
119
|
+
# carry the NEXT_PUBLIC_ prefix so Next.js bakes it into the browser bundle.
|
|
120
|
+
# Usually the same value as SENTRY_DSN; safe to expose because Sentry DSNs
|
|
121
|
+
# are public by design.
|
|
122
|
+
# NEXT_PUBLIC_SENTRY_DSN=
|
|
123
|
+
#
|
|
124
|
+
# Build-time auth token — read by withSentryConfig() in each Next.js app's
|
|
125
|
+
# next.config.{ts,mjs}. Uploads source maps at build time so production stack
|
|
126
|
+
# traces are de-minified. Server-only. Format: sntrys_*.
|
|
127
|
+
# SENTRY_AUTH_TOKEN=
|
|
128
|
+
#
|
|
129
|
+
# Sentry organisation slug — read by withSentryConfig() alongside auth token.
|
|
130
|
+
# Match the slug from your Sentry URL (https://sentry.io/organizations/<slug>/).
|
|
131
|
+
# SENTRY_ORG=
|
|
132
|
+
#
|
|
133
|
+
# Sentry project slug — read by withSentryConfig() alongside auth token.
|
|
134
|
+
# Match the slug from your Sentry project URL.
|
|
135
|
+
# SENTRY_PROJECT=
|
|
136
|
+
|
|
137
|
+
# ── License (JWT model — ADR 0014) ───────────────────────────────────────────
|
|
138
|
+
# The JWT issued by ailaunchkit.ai. When set, @working-theory/license verifies it locally
|
|
139
|
+
# against a bundled public key and unlocks Pro features. When unset, deploy
|
|
140
|
+
# runs in free tier — no network, no parse attempt.
|
|
141
|
+
# AILK_LICENSE_KEY=
|
|
142
|
+
# Override the daily refresh upstream (self-hosters point at their own server
|
|
143
|
+
# implementing the @working-theory/validation refresh contract).
|
|
144
|
+
# Default: https://ailaunchkit.ai/api/license/refresh
|
|
145
|
+
# AILK_LICENSE_REFRESH_URL=
|
|
146
|
+
# Self-host override — replaces the bundled public key with a PEM-encoded one.
|
|
147
|
+
# Only set this if you operate your own license-signing infrastructure.
|
|
148
|
+
# AILK_LICENSE_PUBLIC_KEY_PEM=
|
|
149
|
+
# Filesystem path where the daily-refreshed JWT is written. Read-only
|
|
150
|
+
# filesystems silently no-op; in-process JWT continues to serve until restart.
|
|
151
|
+
# Default: ${cwd}/.ailk-license.jwt
|
|
152
|
+
# AILK_LICENSE_STORAGE_PATH=
|
|
153
|
+
|
|
154
|
+
# ── E2E determinism (clock + randomness) ─────────────────────────────────────
|
|
155
|
+
# These three env vars let end-to-end test harnesses freeze the wall clock and
|
|
156
|
+
# seed the PRNG so flake-prone time- or random-dependent assertions become
|
|
157
|
+
# deterministic. All three default UNSET to real platform behavior (Date.now,
|
|
158
|
+
# crypto.randomUUID); production deployments leave them unset.
|
|
159
|
+
#
|
|
160
|
+
# Wired through @working-theory/common (getClock / getRandomness). Migrated call sites:
|
|
161
|
+
# rate-limit window, API token expiry, session-expiry check, JWT exp comparison,
|
|
162
|
+
# request-id generator. Read at call time, not module load.
|
|
163
|
+
#
|
|
164
|
+
# AILK_CLOCK_MODE — unset (real) | "frozen" (clock anchored at a known
|
|
165
|
+
# epoch; same value returned on every call)
|
|
166
|
+
# AILK_RANDOMNESS_MODE — unset (real) | "seeded" (deterministic PRNG)
|
|
167
|
+
# AILK_RANDOMNESS_SEED — required when MODE=seeded; numeric string. Non-numeric
|
|
168
|
+
# / unset triggers a stderr warn and falls back to real.
|
|
169
|
+
# AILK_CLOCK_MODE=
|
|
170
|
+
# AILK_RANDOMNESS_MODE=
|
|
171
|
+
# AILK_RANDOMNESS_SEED=
|
|
172
|
+
|
|
173
|
+
# ── E2E test-auth seam (pre-OSS — never set in production) ───────────────────
|
|
174
|
+
# These two env vars enable the test-auth route at POST /api/test/auth/session
|
|
175
|
+
# (apps/api/src/routes/test/auth/session.ts) so an e2e harness can mint a
|
|
176
|
+
# Better Auth session for a seeded test user WITHOUT going through Google
|
|
177
|
+
# OAuth, GitHub OAuth, or magic-link email.
|
|
178
|
+
#
|
|
179
|
+
# TRIPLE-GATED — the route is only mounted when ALL three of these hold:
|
|
180
|
+
# 1. NODE_ENV !== "production"
|
|
181
|
+
# 2. AILK_TEST_AUTH_ENABLED === "true" (literal string)
|
|
182
|
+
# 3. AILK_TEST_AUTH_USER_ALLOWLIST is non-empty
|
|
183
|
+
# If any gate fails, Fastify returns its default 404 for the route — the
|
|
184
|
+
# surface NEVER advertises itself. PRODUCTION DEPLOYMENTS MUST LEAVE BOTH
|
|
185
|
+
# UNSET. The startup log emits "test-auth route MOUNTED" when the route is
|
|
186
|
+
# active so an accidental prod-enable is visible.
|
|
187
|
+
#
|
|
188
|
+
# Allowlist format: comma-separated user IDs (exact-match strings; no regex,
|
|
189
|
+
# no wildcard). Empty entries between commas are trimmed.
|
|
190
|
+
# Run `pnpm --filter @working-theory/database db:seed:test` to seed three test users
|
|
191
|
+
# (test-e2e-user / test-e2e-admin / test-e2e-owner — distinguished by the
|
|
192
|
+
# Better Auth `role` field). The seed script itself refuses to run unless
|
|
193
|
+
# AILK_TEST_AUTH_ENABLED === "true".
|
|
194
|
+
#
|
|
195
|
+
# AILK_TEST_AUTH_ENABLED=
|
|
196
|
+
# AILK_TEST_AUTH_USER_ALLOWLIST=
|
|
197
|
+
|
|
198
|
+
# ── Lead capture file store ───────────────────────────────────────────────────
|
|
199
|
+
# Always-on JSONL write path — every successful submission appends a line.
|
|
200
|
+
# Default output: apps/api/leads.jsonl (relative to process cwd).
|
|
201
|
+
# LEAD_CAPTURE_FILE=apps/api/leads.jsonl
|
|
202
|
+
# Set to "true" to disable file writes (leads go to DB only; requires DATABASE_URL).
|
|
203
|
+
# LEAD_CAPTURE_FILE_DISABLED=false
|
|
204
|
+
# Set to "true" to include the submitter's IP in metadata (default: off for privacy).
|
|
205
|
+
# LEAD_CAPTURE_INCLUDE_IP=false
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# {{PROJECT_NAME}} — Agent Operating Manual
|
|
2
|
+
|
|
3
|
+
You are Claude, working in the `{{PROJECT_NAME}}` repo. Read this file at session start.
|
|
4
|
+
|
|
5
|
+
## Repo shape
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
apps/web/ Next.js 16 app — visitor-facing UX
|
|
9
|
+
apps/api/ Fastify HTTP API — checkout, lead capture, webhooks (optional)
|
|
10
|
+
apps/mcp/ MCP server — agent-callable tools (optional)
|
|
11
|
+
content/ MDX content for typed page surfaces (HomePage, BlogPosting, ...)
|
|
12
|
+
.claude/ Rules, skills, and agent definitions for Claude Code
|
|
13
|
+
docs/ Project notes (ADRs, architecture, planning)
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
You consume `@working-theory/*` packages from npm. Source for those packages is at
|
|
17
|
+
<https://github.com/tailwind-ai/ai-launch-kit>; you don't have it locally.
|
|
18
|
+
Treat each `@working-theory/*` package as a third-party dependency — read its README
|
|
19
|
+
or its types, don't go looking for the source.
|
|
20
|
+
|
|
21
|
+
## Architecture invariants
|
|
22
|
+
|
|
23
|
+
The full rule lives at `.claude/rules/architecture.md`. Summary:
|
|
24
|
+
|
|
25
|
+
- `apps/*` are peer apps. They do not import from each other.
|
|
26
|
+
- `apps/web` does not read the database — it goes through `apps/api` via
|
|
27
|
+
the published HTTP client.
|
|
28
|
+
- Vendor SDKs (Stripe, Resend, NextAuth, Prisma) are wrapped in their
|
|
29
|
+
respective `@working-theory/*` packages. Don't import them directly in your code.
|
|
30
|
+
- New page types live under `content/<TypeName>/` and consume the schema
|
|
31
|
+
builders from `@working-theory/schema`.
|
|
32
|
+
|
|
33
|
+
Crossing one of these is an architecture decision, not a workaround. If
|
|
34
|
+
you think you need to, stop and write a short note in `docs/` explaining
|
|
35
|
+
why before you change the boundary.
|
|
36
|
+
|
|
37
|
+
## Resource map
|
|
38
|
+
|
|
39
|
+
Where to find the rule, surface, or fact you need.
|
|
40
|
+
|
|
41
|
+
| For… | Look at |
|
|
42
|
+
| ---------------------------------------------- | -------------------------------------------------------------- |
|
|
43
|
+
| Per-issue workflow (pick → branch → TDD → PR) | `.claude/rules/loop-contract.md` |
|
|
44
|
+
| Architecture boundaries (the why + escalation) | `.claude/rules/architecture.md` |
|
|
45
|
+
| Diff hygiene when editing existing files | `.claude/rules/preserve-existing-style.md` |
|
|
46
|
+
| Skills available to invoke | `.claude/skills/README.md` |
|
|
47
|
+
| Domain agents (sample web agent) | `.claude/agents/web.md` |
|
|
48
|
+
| Code style, testing, security, content schema | `CONVENTIONS.md` |
|
|
49
|
+
| Environment variables and what they unlock | `.env.example` |
|
|
50
|
+
| Per-page-type content shape | `content/<TypeName>/*.mdx` frontmatter |
|
|
51
|
+
| Deploy targets | `apps/web/vercel.json`, `apps/api/vercel.json` |
|
|
52
|
+
| Verification before push | `pnpm preflight` (architecture + types + lint + tests + build) |
|
|
53
|
+
|
|
54
|
+
If a question doesn't fit any row above, add the answer to this table
|
|
55
|
+
when you find it. Future-you will thank present-you.
|
|
56
|
+
|
|
57
|
+
## Workflow
|
|
58
|
+
|
|
59
|
+
The single-developer flow is described fully in
|
|
60
|
+
`.claude/rules/loop-contract.md`. The short version:
|
|
61
|
+
|
|
62
|
+
1. Pick or open an issue with a `## Acceptance Criteria` section.
|
|
63
|
+
2. Branch from your default branch (typically `main`).
|
|
64
|
+
3. Write tests against the acceptance criteria first (RED).
|
|
65
|
+
4. Implement the smallest change that makes them pass (GREEN).
|
|
66
|
+
5. Run `pnpm preflight` — must be green before you push.
|
|
67
|
+
6. Open a PR; reference the issue with `Closes #N`.
|
|
68
|
+
|
|
69
|
+
Skip the test-first step only for: pure documentation, single-line typo
|
|
70
|
+
fixes, or config-only changes already covered by an existing test.
|
|
71
|
+
|
|
72
|
+
## Project-specific MCP servers
|
|
73
|
+
|
|
74
|
+
Configured in `.claude/settings.json`. This project doesn't ship any MCP
|
|
75
|
+
server connections by default — add the ones you actually use (your
|
|
76
|
+
database, your deployment platform, etc.) and keep the tokens in
|
|
77
|
+
`.env.local`, never in the repo.
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
// example shape — add only what you have credentials for
|
|
81
|
+
{
|
|
82
|
+
"mcpServers": {
|
|
83
|
+
"your-database": { "command": "...", "args": ["..."] }
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Content schema discipline
|
|
89
|
+
|
|
90
|
+
This project's content is typed against schema.org via `@working-theory/schema`.
|
|
91
|
+
Every MDX file under `content/` has frontmatter that the build validates:
|
|
92
|
+
|
|
93
|
+
```yaml
|
|
94
|
+
---
|
|
95
|
+
type: BlogPosting # one of the supported page types
|
|
96
|
+
slug: my-post # URL-safe identifier
|
|
97
|
+
title: My Post
|
|
98
|
+
description: "Short summary used for the page meta tag and JSON-LD."
|
|
99
|
+
---
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
When in doubt, copy an existing file in the same folder, then change the
|
|
103
|
+
slug and the body. Don't invent a new `type` value without first
|
|
104
|
+
checking what `@working-theory/schema` exports.
|
|
105
|
+
|
|
106
|
+
## Hard floors
|
|
107
|
+
|
|
108
|
+
These are non-negotiable in this repo. Don't ask, don't try to route
|
|
109
|
+
around them.
|
|
110
|
+
|
|
111
|
+
- **No live Stripe keys in code.** `sk_live_*` and `pk_live_*` belong in
|
|
112
|
+
the deployment platform's secrets store, never in `.env`, source files,
|
|
113
|
+
or commit messages. Use Stripe sandbox keys for everything in source.
|
|
114
|
+
- **No secrets in commits.** API keys, license keys, database URLs — all
|
|
115
|
+
go through `.env.local` (gitignored) for local dev and the deployment
|
|
116
|
+
platform's secrets store for prod.
|
|
117
|
+
- **Validate every webhook.** Stripe sends signed payloads; reject any
|
|
118
|
+
request whose HMAC doesn't match before you do any work on it.
|
|
119
|
+
- **No production database access from a dev session.** If you need to
|
|
120
|
+
inspect prod, do it through your hosting platform's read-only console,
|
|
121
|
+
not from your laptop with a copied connection string.
|
|
122
|
+
|
|
123
|
+
## When to stop and ask
|
|
124
|
+
|
|
125
|
+
You have wide latitude on most things, but stop and surface the question
|
|
126
|
+
when:
|
|
127
|
+
|
|
128
|
+
- An architecture invariant would have to bend.
|
|
129
|
+
- A change to a shared `@working-theory/*` package would be needed (file an issue
|
|
130
|
+
upstream at <https://github.com/tailwind-ai/ai-launch-kit>; don't fork).
|
|
131
|
+
- You're about to delete or move >5 files at once.
|
|
132
|
+
- The acceptance criteria contradict each other or the existing code.
|
|
133
|
+
|
|
134
|
+
A 30-second clarifying question beats two hours of work down the wrong
|
|
135
|
+
branch.
|
|
136
|
+
|
|
137
|
+
## Inherited rules
|
|
138
|
+
|
|
139
|
+
If you have global rules in `~/.claude/rules/` they still apply. This
|
|
140
|
+
file describes only what's specific to `{{PROJECT_NAME}}`. Where the two
|
|
141
|
+
disagree, the global rule wins unless you're explicitly editing this
|
|
142
|
+
file to override it.
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# {{PROJECT_NAME}} — Coding Conventions
|
|
2
|
+
|
|
3
|
+
`CLAUDE.md` answers "how does this repo work?" — this file answers "what's
|
|
4
|
+
the right way to write the code?" Read both before non-trivial work.
|
|
5
|
+
|
|
6
|
+
A new convention is a PR + a short note in `docs/` explaining the change,
|
|
7
|
+
not a unilateral introduction.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Module boundaries
|
|
12
|
+
|
|
13
|
+
The boundary rule lives at `.claude/rules/architecture.md`. The minimum
|
|
14
|
+
you need to remember:
|
|
15
|
+
|
|
16
|
+
- `apps/web`, `apps/api`, `apps/mcp` are peer apps. They do not import
|
|
17
|
+
from each other.
|
|
18
|
+
- `apps/web` does not import `@working-theory/database`. It speaks to `apps/api`
|
|
19
|
+
via the published HTTP client.
|
|
20
|
+
- Packages from npm (`@working-theory/*` and others) are upstream — your code
|
|
21
|
+
consumes them, never the other way around.
|
|
22
|
+
- Vendor SDKs (`stripe`, `resend`, `next-auth`, `@prisma/client`) are
|
|
23
|
+
imported only inside the `@working-theory/*` package that wraps them. If you
|
|
24
|
+
need them elsewhere, route through the wrapper.
|
|
25
|
+
|
|
26
|
+
If you genuinely need to cross a boundary, that's an architecture
|
|
27
|
+
decision — write a short rationale in `docs/` first.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## TypeScript
|
|
32
|
+
|
|
33
|
+
- **Strict mode.** No bare `any`. Use `unknown` and narrow.
|
|
34
|
+
- **`import type` for type-only imports.** Reduces bundle size and
|
|
35
|
+
signals intent.
|
|
36
|
+
- **Annotate return types on exported functions.** Inferred types are
|
|
37
|
+
fine for internal helpers; exports declare explicitly.
|
|
38
|
+
- **No `// @ts-ignore` without an explanation.** Prefer
|
|
39
|
+
`// @ts-expect-error` so unused suppressions surface as errors.
|
|
40
|
+
- **Discriminated unions over enums** for shapes shared across modules.
|
|
41
|
+
Enums don't tree-shake cleanly across boundaries.
|
|
42
|
+
- **Zod for runtime validation** of anything coming from outside your
|
|
43
|
+
process — request bodies, env vars, fetched JSON.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## File sizing and naming
|
|
48
|
+
|
|
49
|
+
- **Filenames are kebab-case.** `user-profile.ts`, not `userProfile.ts`.
|
|
50
|
+
- **React component files are PascalCase `.tsx`.** `Button.tsx`,
|
|
51
|
+
`UserCard.tsx`. Tests next to them under `__tests__/`.
|
|
52
|
+
- **Directories are kebab-case.** `lead-capture/`, not `leadCapture/`.
|
|
53
|
+
- **Forbidden filenames:** `utils.ts`, `helpers.ts`, `common.ts`,
|
|
54
|
+
`misc.ts`. Use a name that says what's inside.
|
|
55
|
+
- **One exported entity per file** is the default. If a file exports a
|
|
56
|
+
component plus seven helpers, split the helpers out.
|
|
57
|
+
- **Files should fit in your head.** No hard line limit, but a single
|
|
58
|
+
file growing past ~400 lines and not being one cohesive thing is a
|
|
59
|
+
signal to split.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## React + Next.js
|
|
64
|
+
|
|
65
|
+
- **Server Components by default.** Only add `'use client'` when the
|
|
66
|
+
component uses browser-only APIs (`useState`, `useEffect`, refs,
|
|
67
|
+
click handlers).
|
|
68
|
+
- **Server actions** are fine for form-submit flows that live entirely
|
|
69
|
+
in `apps/web`. For anything reused or agent-callable, put it in
|
|
70
|
+
`apps/api` instead.
|
|
71
|
+
- **`apps/web` does not own the database.** Even in server components,
|
|
72
|
+
`apps/web` reads from `apps/api`. The boundary is real.
|
|
73
|
+
- **Mobile-first.** Default Tailwind utilities target mobile; use
|
|
74
|
+
`md:`/`lg:` modifiers for larger viewports.
|
|
75
|
+
- **MDX rendering** uses `next-mdx-remote/rsc`. Don't introduce
|
|
76
|
+
`@next/mdx` — it conflicts with Server Components.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Tailwind + design tokens
|
|
81
|
+
|
|
82
|
+
- **Semantic tokens only.** `bg-canvas`, `text-fg`, `border-border`,
|
|
83
|
+
`bg-accent`. The palette comes from `@working-theory/theme`.
|
|
84
|
+
- **No raw hex, rgb, or arbitrary colors.** `bg-[#abc]` is forbidden.
|
|
85
|
+
If a token you need doesn't exist, add it to your theme extension.
|
|
86
|
+
- **No inline `style={{ ... }}`.** Reserve it for genuinely dynamic
|
|
87
|
+
values (computed positions, animations).
|
|
88
|
+
- **Spacing uses Tailwind's scale.** `p-4`, `gap-2`, `mt-8`. Custom
|
|
89
|
+
values go in `tailwind.config.ts`.
|
|
90
|
+
- **Dark mode** uses the `dark:` modifier with tokens that resolve in
|
|
91
|
+
both modes. Don't hardcode `text-white`.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Database conventions
|
|
96
|
+
|
|
97
|
+
If you've enabled the optional database layer:
|
|
98
|
+
|
|
99
|
+
- **Prisma is the source of truth.** Schema lives in
|
|
100
|
+
`prisma/schema.prisma`; migrations are generated with
|
|
101
|
+
`pnpm db:migrate`.
|
|
102
|
+
- **Migrations are forward-only.** Never edit a migration that has
|
|
103
|
+
already been applied. Write a corrective migration instead.
|
|
104
|
+
- **No raw SQL outside the migrations folder.** All runtime data
|
|
105
|
+
access goes through Prisma.
|
|
106
|
+
- **`@map` for snake_case ↔ camelCase.** DB columns are snake_case;
|
|
107
|
+
TS field names are camelCase.
|
|
108
|
+
- **Index every query you ship.** Add `@@index([...])` for any field
|
|
109
|
+
combination read in a route handler.
|
|
110
|
+
- **Idempotent seeds.** Your seed script must run safely twice. Use
|
|
111
|
+
`prisma.upsert` keyed on a stable unique field.
|
|
112
|
+
- **Standalone-aware code.** This project boots without
|
|
113
|
+
`DATABASE_URL`. Code that requires the DB must handle the unset case
|
|
114
|
+
(return 501, fall through to MDX content, etc.).
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Testing
|
|
119
|
+
|
|
120
|
+
- **Jest + ts-jest** for unit tests, co-located. Each app and `database/`
|
|
121
|
+
ships its own Jest config; the root `pnpm test` fans out via
|
|
122
|
+
`pnpm -r test`.
|
|
123
|
+
- Source: `Foo.tsx` → tests: `__tests__/Foo.test.tsx`.
|
|
124
|
+
- Run: `pnpm --filter <pkg> test`.
|
|
125
|
+
- **Playwright** for end-to-end (root `e2e/` once you add it).
|
|
126
|
+
- **TDD: RED → GREEN.** Write the failing test from the acceptance
|
|
127
|
+
criteria first. Implement to green. Refactor with tests still green.
|
|
128
|
+
- **Skip TDD only for** config-only changes, pure documentation, or
|
|
129
|
+
single-line fixes already covered by an existing test.
|
|
130
|
+
- **Happy-path + failure-path** is the minimum for new behavior. One
|
|
131
|
+
test of each.
|
|
132
|
+
- **Don't mock code you own.** Mock external services (Stripe,
|
|
133
|
+
Resend, the HTTP layer to `apps/api` from `apps/web` tests). If
|
|
134
|
+
you're mocking your own service, the test is at the wrong layer.
|
|
135
|
+
- **Existing tests pass unchanged on cleanup PRs.** A behavior change
|
|
136
|
+
needs explicit acceptance criteria.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Security
|
|
141
|
+
|
|
142
|
+
- **Env vars only — never hardcoded secrets.** `.env.local`
|
|
143
|
+
(gitignored) for local dev; production secrets via your deployment
|
|
144
|
+
platform's secrets store.
|
|
145
|
+
- **HMAC-verify every webhook.** Stripe, license callbacks, anything
|
|
146
|
+
signed. Reject before any side effect; log the rejection; return 401.
|
|
147
|
+
- **Input validation at the boundary.** Every route validates request
|
|
148
|
+
bodies via Zod. Reject with 422 on schema failure.
|
|
149
|
+
- **No secrets in logs.** Redact tokens, API keys, and personal data
|
|
150
|
+
before logging. The wrapper packages handle this for you when you
|
|
151
|
+
use them.
|
|
152
|
+
- **Stripe sandbox keys only in source.** `sk_live_*`/`pk_live_*` live
|
|
153
|
+
in the production secrets store. They never appear in `.env`,
|
|
154
|
+
source files, or commit messages.
|
|
155
|
+
- **License key handling** uses the `@working-theory/license` verifier. License
|
|
156
|
+
keys are bearer tokens — treat them with the same care as API keys.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Content schema
|
|
161
|
+
|
|
162
|
+
- **Validate at MDX build time.** The build step parses every file
|
|
163
|
+
under `content/` and rejects malformed frontmatter.
|
|
164
|
+
- **Frontmatter is the contract.** `type`, `slug`, plus the fields
|
|
165
|
+
that match the type's schema, in YAML at the top of every MDX file.
|
|
166
|
+
- **Single source of truth** for content shape lives in `@working-theory/schema`
|
|
167
|
+
(schema.org-compatible types). Don't redefine the same shape inline.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Cross-cutting wrappers
|
|
172
|
+
|
|
173
|
+
Vendor SDKs are imported only inside their `@working-theory/*` wrapper. The
|
|
174
|
+
wrapper exports a typed, project-flavored interface; your code imports
|
|
175
|
+
from the wrapper.
|
|
176
|
+
|
|
177
|
+
| Vendor | Wrapper package | Where you use it |
|
|
178
|
+
| ------------------- | --------------------- | ---------------------------- |
|
|
179
|
+
| Stripe | `@working-theory/stripe-client` | `apps/api` checkout/webhooks |
|
|
180
|
+
| NextAuth | `@working-theory/auth-client` | `apps/api`, `apps/web` |
|
|
181
|
+
| Resend / Nodemailer | `@working-theory/email` | `apps/api` |
|
|
182
|
+
| Analytics | `@working-theory/analytics` | `apps/web`, `apps/api` |
|
|
183
|
+
| Prisma | `@working-theory/database` | `apps/api`, `apps/mcp` |
|
|
184
|
+
|
|
185
|
+
The wrapper is where retries, signature verification, error mapping,
|
|
186
|
+
and config defaults live. Don't reinvent them per consumer.
|
|
187
|
+
|
|
188
|
+
**Why bother:** swapping a vendor (Resend → SendGrid; NextAuth → Clerk;
|
|
189
|
+
Prisma → Drizzle) becomes a one-package change. Your app code doesn't
|
|
190
|
+
move.
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Commit + diff hygiene
|
|
195
|
+
|
|
196
|
+
- **One PR, one task.** No "while I was in there" changes. Drive-by
|
|
197
|
+
cleanup goes in its own follow-up PR.
|
|
198
|
+
- **Diff is the contract.** A 20-line behavioral change should produce
|
|
199
|
+
a ~20-line diff. If your editor reformatted unrelated lines, revert
|
|
200
|
+
the formatting before committing. See
|
|
201
|
+
`.claude/rules/preserve-existing-style.md`.
|
|
202
|
+
- **Commit message format:** `feat(area): description (#N)`. Include
|
|
203
|
+
the issue number when one exists.
|
|
204
|
+
- **Squash merge only.** Multiple commits on the branch are fine; the
|
|
205
|
+
merge collapses them.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## When this document changes
|
|
210
|
+
|
|
211
|
+
A change to `CONVENTIONS.md` should be:
|
|
212
|
+
|
|
213
|
+
1. Reviewed in a PR by you (or by an agent that's been told the
|
|
214
|
+
change is intentional).
|
|
215
|
+
2. Accompanied by a short rationale in the PR body — what changed,
|
|
216
|
+
what motivated it, what's now different in practice.
|
|
217
|
+
3. Reflected in any related rule under `.claude/rules/` if the
|
|
218
|
+
change implies an enforcement adjustment.
|
|
219
|
+
|
|
220
|
+
If you find an existing convention conflicts with how the code is
|
|
221
|
+
actually written: stop and decide which is correct before unifying.
|
|
222
|
+
The fix might be the convention, the code, or both.
|