create-ailk 0.6.1 → 0.8.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 +87 -14
- package/component-catalog.json +735 -95
- package/component-catalog.md +13 -7
- package/dist/add-module-core.d.ts +22 -0
- package/dist/add-module-core.js +50 -0
- package/dist/add-module.d.ts +89 -0
- package/dist/add-module.js +334 -0
- package/dist/category-profile.d.ts +126 -0
- package/dist/category-profile.js +262 -0
- package/dist/cli.js +89 -29
- package/dist/index.d.ts +4 -2
- package/dist/index.js +6 -1
- package/dist/lib/extract-module-bundle.d.ts +56 -1
- package/dist/lib/extract-module-bundle.js +170 -14
- package/dist/lib/license-key-files.d.ts +15 -0
- package/dist/lib/license-key-files.js +102 -0
- package/dist/lib/module-license-gate.d.ts +79 -0
- package/dist/lib/module-license-gate.js +160 -12
- package/dist/lib/module-tier.d.ts +45 -16
- package/dist/lib/module-tier.js +76 -0
- package/dist/lib/pro-callout.d.ts +16 -10
- package/dist/lib/pro-callout.js +34 -3
- package/dist/license-set.d.ts +34 -0
- package/dist/license-set.js +194 -0
- package/dist/module-architecture.d.ts +20 -0
- package/dist/module-architecture.js +77 -19
- package/dist/modules/commerce/0.6.1.tar.gz +0 -0
- package/dist/modules/docs-blog/0.6.1.tar.gz +0 -0
- package/dist/modules/marketing-site/0.6.1.tar.gz +0 -0
- package/dist/modules/minimalism-site/0.6.1.tar.gz +0 -0
- package/dist/modules/multi-tenant/0.6.1.tar.gz +0 -0
- package/dist/modules/saas-product/0.6.1.tar.gz +0 -0
- package/dist/modules/scheduling/0.6.1.tar.gz +0 -0
- package/dist/modules/signal-collectors/0.6.1.tar.gz +0 -0
- package/dist/modules/simple-service/0.6.1.tar.gz +0 -0
- package/dist/modules/workspace/0.6.1.tar.gz +0 -0
- package/dist/parse-args.d.ts +85 -10
- package/dist/parse-args.js +227 -15
- package/dist/programmatic.d.ts +81 -5
- package/dist/programmatic.js +291 -32
- package/dist/site-chrome.d.ts +18 -7
- package/dist/site-chrome.js +32 -14
- package/dist/site-roster.d.ts +127 -0
- package/dist/site-roster.js +499 -0
- package/dist/starter-grid.d.ts +216 -0
- package/dist/starter-grid.js +449 -0
- package/dist/surfaces.d.ts +20 -7
- package/dist/surfaces.js +204 -24
- package/dist/sync-routes.d.ts +9 -0
- package/dist/sync-routes.js +2 -2
- package/dist/template-manifest.d.ts +94 -19
- package/dist/template-manifest.js +170 -289
- package/dist/template-names.d.ts +14 -12
- package/dist/template-names.js +13 -17
- package/package.json +6 -4
- package/site-templates/_base/content/en/BookCall/book-call.mdx +52 -0
- package/site-templates/_base/content/en/Feature/feature-1.mdx +69 -0
- package/site-templates/_base/content/en/Feature/feature-2.mdx +69 -0
- package/site-templates/_base/content/en/Feature/feature-3.mdx +69 -0
- package/site-templates/_base/content/en/FeaturesList/features.mdx +91 -0
- package/site-templates/_base/content/en/GlossaryList/glossary.mdx +65 -0
- package/site-templates/_base/content/en/NewsletterSignup/newsletter.mdx +67 -0
- package/site-templates/_base/content/en/PortfolioItem/sample-four.mdx +105 -0
- package/site-templates/_base/content/en/PortfolioItem/sample-one.mdx +34 -0
- package/site-templates/_base/content/en/PortfolioItem/sample-three.mdx +94 -0
- package/site-templates/_base/content/en/PortfolioItem/sample-two.mdx +45 -0
- package/site-templates/_base/content/en/PortfolioList/portfolio.mdx +169 -0
- package/site-templates/_base/content/en/Pricing/pricing.mdx +71 -62
- package/site-templates/_base/content/en/PrivacyPage/privacy.mdx +17 -3
- package/site-templates/_base/content/en/TermsPage/terms.mdx +2 -3
- package/site-templates/_base/content/en/UseCase/persona-1.mdx +69 -0
- package/site-templates/_base/content/en/UseCase/persona-2.mdx +69 -0
- package/site-templates/_base/content/en/UseCase/persona-3.mdx +69 -0
- package/site-templates/designs/minimalism/DESIGN.md +162 -0
- package/site-templates/designs/standard/DESIGN.md +167 -0
- package/site-templates/marketing-site/DESIGN.md +59 -40
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +376 -57
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +230 -90
- package/site-templates/marketing-site/ailk-template.json +1 -0
- package/site-templates/marketing-site-free/DESIGN.md +59 -40
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +323 -63
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +323 -113
- package/site-templates/marketing-site-free/ailk-template.json +3 -2
- package/site-templates/marketing-site-free/content/en/NewsletterSignup/newsletter.mdx +59 -0
- package/site-templates/marketing-site-free/content/en/Pricing/pricing.mdx +199 -0
- package/site-templates/minimalism-site/DESIGN.md +54 -40
- package/site-templates/minimalism-site/TEMPLATE-MANIFEST.json +246 -19
- package/site-templates/minimalism-site/TEMPLATE-MANIFEST.md +173 -51
- package/site-templates/minimalism-site/ailk-template.json +3 -2
- package/site-templates/minimalism-site/content/en/BookCall/book-call.mdx +52 -0
- package/site-templates/minimalism-site/content/en/ContactPage/contact.mdx +3 -37
- package/site-templates/minimalism-site/content/en/PurchasePolicyPage/purchase-policy.mdx +123 -0
- package/site-templates/saas-product/DESIGN.md +59 -40
- package/site-templates/saas-product/TEMPLATE-MANIFEST.md +9 -1
- package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.json +369 -0
- package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.md +302 -0
- package/site-templates/shapes/local-business-service/ailk-template.json +10 -0
- package/site-templates/{simple-service → shapes/local-business-service}/content/en/AboutPage/about.mdx +4 -4
- package/site-templates/shapes/local-business-service/content/en/HomePage/home.mdx +70 -0
- package/site-templates/shapes/local-business-service/content/en/NewsletterSignup/newsletter.mdx +59 -0
- package/site-templates/shapes/local-business-service/content/en/PortfolioList/portfolio.mdx +171 -0
- package/site-templates/shapes/local-business-service/content/en/PurchasePolicyPage/purchase-policy.mdx +123 -0
- package/site-templates/shapes/local-business-service/content/en/Service/first-job.mdx +39 -0
- package/site-templates/shapes/local-business-service/content/en/Service/second-job.mdx +39 -0
- package/site-templates/shapes/local-business-service/content/en/Service/third-job.mdx +39 -0
- package/site-templates/shapes/local-business-service/content/en/ServicesList/services.mdx +61 -0
- package/site-templates/shapes/local-business-service/provenance.json +4 -0
- package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.json +362 -0
- package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.md +300 -0
- package/site-templates/shapes/organization-service/ailk-template.json +10 -0
- package/site-templates/shapes/organization-service/content/en/AboutPage/about.mdx +38 -0
- package/site-templates/shapes/organization-service/content/en/HomePage/home.mdx +85 -0
- package/site-templates/shapes/organization-service/content/en/NewsletterSignup/newsletter.mdx +59 -0
- package/site-templates/shapes/organization-service/content/en/PortfolioList/portfolio.mdx +171 -0
- package/site-templates/shapes/organization-service/content/en/PurchasePolicyPage/purchase-policy.mdx +123 -0
- package/site-templates/shapes/organization-service/content/en/Service/first-engagement.mdx +38 -0
- package/site-templates/shapes/organization-service/content/en/Service/second-engagement.mdx +38 -0
- package/site-templates/shapes/organization-service/content/en/Service/third-engagement.mdx +38 -0
- package/site-templates/shapes/organization-service/content/en/ServicesList/services.mdx +72 -0
- package/site-templates/shapes/organization-service/provenance.json +4 -0
- package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.json +369 -0
- package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.md +299 -0
- package/site-templates/shapes/person-service/ailk-template.json +10 -0
- package/site-templates/shapes/person-service/content/_site.mdx +85 -0
- package/site-templates/shapes/person-service/content/en/AboutPage/about.mdx +39 -0
- package/site-templates/shapes/person-service/content/en/HomePage/home.mdx +97 -0
- package/site-templates/shapes/person-service/content/en/NewsletterSignup/newsletter.mdx +59 -0
- package/site-templates/shapes/person-service/content/en/PortfolioList/portfolio.mdx +169 -0
- package/site-templates/shapes/person-service/content/en/PurchasePolicyPage/purchase-policy.mdx +123 -0
- package/site-templates/shapes/person-service/content/en/Service/first-engagement.mdx +38 -0
- package/site-templates/shapes/person-service/content/en/Service/second-engagement.mdx +38 -0
- package/site-templates/shapes/person-service/content/en/Service/third-engagement.mdx +38 -0
- package/site-templates/{simple-service → shapes/person-service}/content/en/ServicesList/services.mdx +16 -16
- package/site-templates/shapes/person-service/provenance.json +4 -0
- package/templates/.claude/settings.json +15 -0
- package/templates/.claude/skills/README.md +10 -9
- package/templates/.claude/skills/{scaffold-auth → add-auth}/SKILL.md +7 -7
- package/templates/.claude/skills/{scaffold-auth → add-auth}/references/env-vars.md +2 -2
- package/templates/.claude/skills/{scaffold-auth → add-auth}/references/oauth-app-setup.md +3 -3
- package/templates/.claude/skills/{scaffold-page → add-page}/SKILL.md +76 -62
- package/templates/.claude/skills/add-page/references/page-types.md +115 -0
- package/templates/.claude/skills/{scaffold-page → add-page}/templates/content.mdx.tmpl +4 -4
- package/templates/.claude/skills/{scaffold-page → add-page}/templates/route.tsx.tmpl +1 -1
- package/templates/.claude/skills/{audit-site → check-site}/SKILL.md +16 -17
- package/templates/.claude/skills/{audit-site → check-site}/references/common-errors.md +3 -3
- package/templates/.claude/skills/{audit-site → check-site}/references/findings-glossary.md +16 -14
- package/templates/.claude/skills/{audit-site → check-site}/references/severity-levels.md +1 -1
- package/templates/.claude/skills/fill-page/SKILL.md +180 -0
- package/templates/.claude/skills/fill-page/profiles/plumbing.yaml +80 -0
- package/templates/.claude/skills/fill-page/references/section-sources.md +41 -0
- package/templates/.claude/skills/setup-database/SKILL.md +156 -0
- package/templates/.claude/skills/setup-database/scripts/verify-inbox.ts +154 -0
- package/templates/.claude/skills/{provision-config → setup-deploy}/SKILL.md +73 -41
- package/templates/.claude/skills/{provision-config → setup-deploy}/references/env-vars-template.md +21 -30
- package/templates/.claude/skills/setup-deploy/references/runbook-template.md +204 -0
- package/templates/.claude/skills/{provision-config → setup-deploy}/references/vercel-json-template.md +1 -1
- package/templates/.claude/skills/upgrade-plan/SKILL.md +117 -0
- package/templates/.claude/skills/upgrade-plan/references/options.md +24 -0
- package/templates/.claude/skills/upgrade-plan/scripts/upgrade-plan.ts +527 -0
- package/templates/.github/workflows/ci.yml +5 -0
- package/templates/CLAUDE.md +8 -2
- package/templates/DESIGN.md +56 -44
- package/templates/README.md +116 -46
- package/templates/apps/api/.env.example +2 -1
- package/templates/apps/api/CLAUDE.md +3 -2
- package/templates/apps/api/package.json +1 -1
- package/templates/apps/api/src/config/__tests__/index.test.ts +146 -10
- package/templates/apps/api/src/config/index.ts +46 -14
- package/templates/apps/api/src/lib/__tests__/lead-capture-store.test.ts +30 -0
- package/templates/apps/api/src/lib/__tests__/lead-delivery.test.ts +96 -0
- package/templates/apps/api/src/lib/lead-capture-store.ts +12 -0
- package/templates/apps/api/src/lib/lead-delivery.ts +52 -0
- package/templates/apps/api/src/lib/license-key-store.ts +45 -0
- package/templates/apps/api/src/lib/site.ts +5 -3
- package/templates/apps/api/src/middleware/__tests__/auth.database-not-configured.test.ts +97 -0
- package/templates/apps/api/src/middleware/auth.ts +9 -0
- package/templates/apps/api/src/openapi/spec.ts +63 -12
- package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +127 -5
- package/templates/apps/api/src/routes/content/get-page.ts +86 -2
- package/templates/apps/api/src/routes/leads/__tests__/capture.test.ts +88 -0
- package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +100 -0
- package/templates/apps/api/src/routes/leads/index.ts +46 -1
- package/templates/apps/api/src/server.ts +13 -0
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +194 -0
- package/templates/apps/api/src/services/__tests__/leads-inbox.test.ts +124 -0
- package/templates/apps/api/src/services/lead-routing.ts +140 -30
- package/templates/apps/api/src/services/leads-inbox.ts +48 -11
- package/templates/apps/mcp/__tests__/audit-single-page.test.ts +75 -1
- package/templates/apps/mcp/__tests__/lib/audit-single-page-readiness-scan.test.ts +57 -9
- package/templates/apps/mcp/__tests__/parity.test.ts +5 -4
- package/templates/apps/mcp/package.json +2 -1
- package/templates/apps/mcp/src/index.ts +7 -0
- package/templates/apps/mcp/src/lib/audit-single-page.ts +36 -10
- package/templates/apps/mcp/src/lib/license-key-store.ts +45 -0
- package/templates/apps/mcp/src/tools/create_project.ts +21 -6
- package/templates/apps/web/__tests__/setup.ts +18 -0
- package/templates/apps/web/app/[locale]/(auth)/forgot-password/forgot-password-card.tsx +10 -2
- package/templates/apps/web/app/[locale]/(auth)/layout.tsx +2 -2
- package/templates/apps/web/app/[locale]/(auth)/reset-password/reset-password-card.tsx +10 -4
- package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-card.tsx +22 -14
- package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up-card.tsx +26 -18
- package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +12 -4
- package/templates/apps/web/app/[locale]/[slug]/__tests__/page.test.tsx +35 -4
- package/templates/apps/web/app/[locale]/[slug]/page.tsx +49 -9
- package/templates/apps/web/app/[locale]/book-a-call/page.tsx +50 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/case-studies/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/clients/__tests__/page-renderer-migration.test.tsx +3 -4
- package/templates/apps/web/app/[locale]/contact/ConversationalIntakeWrapper.tsx +67 -29
- package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +94 -11
- package/templates/apps/web/app/[locale]/contact/__tests__/contact-form-text.test.ts +32 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/contact.actions.test.ts +129 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +228 -86
- package/templates/apps/web/app/[locale]/contact/contact-form-text.ts +50 -0
- package/templates/apps/web/app/[locale]/contact/contact.actions.ts +73 -20
- package/templates/apps/web/app/[locale]/contact/page.tsx +115 -7
- package/templates/apps/web/app/[locale]/contact/site-contact-email.ts +54 -0
- package/templates/apps/web/app/[locale]/courses/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/downloads/__tests__/page-renderer-migration.test.tsx +3 -4
- package/templates/apps/web/app/[locale]/events/__tests__/page-renderer-migration.test.tsx +4 -5
- package/templates/apps/web/app/[locale]/features/[slug]/page.tsx +64 -0
- package/templates/apps/web/app/[locale]/features/page.tsx +52 -0
- package/templates/apps/web/app/[locale]/glossary/page.tsx +50 -0
- package/templates/apps/web/app/[locale]/how-to/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/how-to/page.tsx +25 -15
- package/templates/apps/web/app/[locale]/integrations/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/layout.tsx +146 -52
- package/templates/apps/web/app/[locale]/newsletter/__tests__/newsletter.actions.test.ts +75 -0
- package/templates/apps/web/app/[locale]/newsletter/__tests__/page.test.tsx +163 -0
- package/templates/apps/web/app/[locale]/newsletter/newsletter.actions.ts +66 -0
- package/templates/apps/web/app/[locale]/newsletter/page.tsx +142 -0
- package/templates/apps/web/app/[locale]/partners/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/portfolio/[slug]/page.tsx +59 -0
- package/templates/apps/web/app/[locale]/portfolio/page.tsx +49 -0
- package/templates/apps/web/app/[locale]/products/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/projects/__tests__/page-renderer-migration.test.tsx +4 -5
- package/templates/apps/web/app/[locale]/{cancellation → purchase-policy}/page.tsx +15 -10
- package/templates/apps/web/app/[locale]/resources/__tests__/page-renderer-migration.test.tsx +4 -5
- package/templates/apps/web/app/[locale]/services/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/software/[slug]/page.tsx +14 -1
- package/templates/apps/web/app/[locale]/software/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/software/page.tsx +25 -15
- package/templates/apps/web/app/[locale]/tech-articles/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/tech-articles/page.tsx +25 -15
- package/templates/apps/web/app/[locale]/testimonials/__tests__/page-renderer-migration.test.tsx +3 -4
- package/templates/apps/web/app/[locale]/use-cases/[slug]/page.tsx +63 -0
- package/templates/apps/web/app/[locale]/use-cases/__tests__/page-renderer-migration.test.tsx +3 -4
- package/templates/apps/web/app/[locale]/use-cases/page.tsx +15 -2
- package/templates/apps/web/app/[locale]/whitepapers/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/whitepapers/page.tsx +25 -15
- package/templates/apps/web/app/error-fallback.tsx +17 -45
- package/templates/apps/web/app/not-found.tsx +14 -38
- package/templates/apps/web/app/og/route.tsx +6 -0
- package/templates/apps/web/eslint.config.mjs +3 -0
- package/templates/apps/web/instrumentation.ts +14 -0
- package/templates/apps/web/lib/__tests__/structured-data.test.tsx +38 -0
- package/templates/apps/web/lib/license-key-store.ts +52 -0
- package/templates/apps/web/lib/nav-config.ts +82 -18
- package/templates/apps/web/lib/prisma-for-auth.ts +9 -6
- package/templates/apps/web/lib/site-brand.tsx +1 -1
- package/templates/apps/web/lib/site-chrome.ts +3 -3
- package/templates/apps/web/lib/site-identity.ts +148 -0
- package/templates/apps/web/lib/site-theme.ts +11 -0
- package/templates/apps/web/messages/en.json +78 -37
- 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 +3 -3
- package/templates/apps/web/public/logomark.svg +6 -3
- package/templates/apps/web/public/logos/ai-launch-kit-light.svg +4 -0
- package/templates/apps/web/public/logos/ai-launch-kit.svg +4 -0
- package/templates/apps/web/public/logos/atomic-agent-light.svg +4 -0
- package/templates/apps/web/public/logos/atomic-agent.svg +4 -0
- package/templates/apps/web/public/logos/salessmyth-light.svg +4 -0
- package/templates/apps/web/public/logos/salessmyth.svg +4 -0
- package/templates/apps/web/public/site.webmanifest +4 -4
- package/templates/apps/web/public/wordmark-light.svg +4 -0
- package/templates/apps/web/public/wordmark.svg +3 -3
- package/templates/apps/web/security-headers.config.ts +5 -1
- package/templates/content/_site.mdx +25 -0
- package/templates/content/en/AboutPage/about.mdx +150 -144
- package/templates/content/en/BlogList/blog.mdx +126 -0
- package/templates/content/en/ContactPage/contact.mdx +21 -92
- package/templates/content/en/FAQPage/faq.mdx +86 -57
- package/templates/content/en/HomePage/home.mdx +268 -217
- package/templates/content/en/PurchasePolicyPage/purchase-policy.mdx +181 -0
- package/templates/content/en/UseCasesList/use-cases.mdx +170 -0
- package/templates/database/CHANGELOG.md +14 -0
- package/templates/database/package.json +1 -1
- package/templates/package.json +3 -1
- package/templates/tools/lint-architecture.ts +315 -0
- package/templates/tools/tsconfig.json +14 -0
- package/versions.json +33 -33
- package/site-templates/simple-service/DESIGN.md +0 -148
- package/site-templates/simple-service/TEMPLATE-MANIFEST.json +0 -240
- package/site-templates/simple-service/TEMPLATE-MANIFEST.md +0 -278
- package/site-templates/simple-service/ailk-template.json +0 -10
- package/site-templates/simple-service/content/en/ContactPage/contact.mdx +0 -67
- package/site-templates/simple-service/content/en/FAQPage/faq.mdx +0 -56
- package/site-templates/simple-service/content/en/HomePage/home.mdx +0 -85
- package/site-templates/simple-service/content/en/Service/landscape-design.mdx +0 -40
- package/site-templates/simple-service/content/en/Service/lawn-maintenance.mdx +0 -40
- package/site-templates/simple-service/content/en/Service/seasonal-cleanup.mdx +0 -40
- package/site-templates/simple-service/provenance.json +0 -4
- package/templates/.claude/skills/add-schema/SKILL.md +0 -277
- package/templates/.claude/skills/add-schema/references/conflict-handling.md +0 -200
- package/templates/.claude/skills/add-schema/references/file-finding.md +0 -123
- package/templates/.claude/skills/add-schema/references/schema-types.md +0 -234
- package/templates/.claude/skills/provision-config/references/runbook-template.md +0 -248
- package/templates/.claude/skills/scaffold-page/references/page-types.md +0 -169
- package/templates/.claude/skills/suggest-site-pages/SKILL.md +0 -242
- package/templates/.claude/skills/suggest-site-pages/references/categories.md +0 -287
- package/templates/apps/web/app/[locale]/contact/ContactForm.tsx +0 -150
- package/templates/apps/web/app/[locale]/returns/page.tsx +0 -44
- package/templates/apps/web/app/[locale]/shipping/page.tsx +0 -44
- package/templates/content/en/CancellationPolicyPage/cancellation.mdx +0 -108
- package/templates/content/en/LandingPage/blog.mdx +0 -83
- package/templates/content/en/LandingPage/features.mdx +0 -103
- package/templates/content/en/LandingPage/how-it-works.mdx +0 -143
- package/templates/content/en/ReturnsPolicyPage/returns.mdx +0 -108
- package/templates/content/en/ShippingPolicyPage/shipping.mdx +0 -108
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The category profile (#6640; starter-template contract § D) — one YAML
|
|
3
|
+
* document per leaf category slug at `category-profiles/<slug>.yaml`, the data
|
|
4
|
+
* the `fill-page` skill reads to make one starter speak for one business type.
|
|
5
|
+
*
|
|
6
|
+
* Dependency-free by design: `create-ailk` ships `tar` as its only runtime
|
|
7
|
+
* dependency, so this is a hand-written validator in the style of
|
|
8
|
+
* `resolveTemplate`/`TemplateError`, not a Zod schema. The caller parses the
|
|
9
|
+
* YAML and injects the registries (page types, leaf categories); nothing here
|
|
10
|
+
* imports a workspace package at run time.
|
|
11
|
+
*/
|
|
12
|
+
import type { StarterShape } from "./template-manifest.js";
|
|
13
|
+
/**
|
|
14
|
+
* The contract's shape table, as data: each shape's primary node, the
|
|
15
|
+
* secondaries it allows, and whether it is reserved. Keyed by the manifest's
|
|
16
|
+
* own `StarterShape`, so a shape added there without a row here fails
|
|
17
|
+
* type-check.
|
|
18
|
+
*/
|
|
19
|
+
export declare const CATEGORY_PROFILE_SHAPES: {
|
|
20
|
+
readonly "organization-product": {
|
|
21
|
+
readonly primary: "Organization";
|
|
22
|
+
readonly secondaries: readonly ["SoftwareApplication"];
|
|
23
|
+
readonly reserved: false;
|
|
24
|
+
};
|
|
25
|
+
readonly "organization-service": {
|
|
26
|
+
readonly primary: "Organization";
|
|
27
|
+
readonly secondaries: readonly [];
|
|
28
|
+
readonly reserved: false;
|
|
29
|
+
};
|
|
30
|
+
readonly "local-business-service": {
|
|
31
|
+
readonly primary: "LocalBusiness";
|
|
32
|
+
readonly secondaries: readonly ["Person"];
|
|
33
|
+
readonly reserved: false;
|
|
34
|
+
};
|
|
35
|
+
readonly "local-business-product": {
|
|
36
|
+
readonly primary: "LocalBusiness";
|
|
37
|
+
readonly secondaries: readonly [];
|
|
38
|
+
readonly reserved: true;
|
|
39
|
+
};
|
|
40
|
+
readonly "person-service": {
|
|
41
|
+
readonly primary: "Person";
|
|
42
|
+
readonly secondaries: readonly ["Organization"];
|
|
43
|
+
readonly reserved: false;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* schema.org's `LocalBusiness` subtree, `LocalBusiness` itself included — the
|
|
48
|
+
* closure of the `LocalBusiness` union in `schema-dts`. A literal so `src/`
|
|
49
|
+
* needs no type package at run time; `category-profile.test.ts` proves it
|
|
50
|
+
* set-equal to the `schema-dts` closure, so it cannot drift silently.
|
|
51
|
+
*/
|
|
52
|
+
export declare const LOCAL_BUSINESS_TYPES: ReadonlySet<string>;
|
|
53
|
+
export declare const OFFERING_PRICING: readonly ["quote", "fixed", "hourly", "subscription"];
|
|
54
|
+
export declare const PROOF_KINDS: readonly ["credential", "reviews", "before-after", "response-time", "guarantee", "experience"];
|
|
55
|
+
export declare const CONTENT_POSTURES: readonly ["instructions", "sample"];
|
|
56
|
+
export interface CategoryOffering {
|
|
57
|
+
name: string;
|
|
58
|
+
job: string;
|
|
59
|
+
pricing: (typeof OFFERING_PRICING)[number];
|
|
60
|
+
}
|
|
61
|
+
export interface CategoryProof {
|
|
62
|
+
kind: (typeof PROOF_KINDS)[number];
|
|
63
|
+
label: string;
|
|
64
|
+
}
|
|
65
|
+
export type CategoryRosterDelta = {
|
|
66
|
+
op: "add";
|
|
67
|
+
route: string;
|
|
68
|
+
pageType: string;
|
|
69
|
+
reason: string;
|
|
70
|
+
} | {
|
|
71
|
+
op: "drop";
|
|
72
|
+
route: string;
|
|
73
|
+
reason: string;
|
|
74
|
+
};
|
|
75
|
+
export interface CategoryIntakeItem {
|
|
76
|
+
id: string;
|
|
77
|
+
ask: string;
|
|
78
|
+
/** The section types an answer to this question can fill. */
|
|
79
|
+
fills: string[];
|
|
80
|
+
}
|
|
81
|
+
export interface CategoryProfile {
|
|
82
|
+
slug: string;
|
|
83
|
+
shape: StarterShape;
|
|
84
|
+
/** The schema.org subtype of the primary node; only on `local-business-*`. */
|
|
85
|
+
subtype?: string;
|
|
86
|
+
secondary?: string;
|
|
87
|
+
offerings: CategoryOffering[];
|
|
88
|
+
proof: CategoryProof[];
|
|
89
|
+
vocabulary: {
|
|
90
|
+
problem: string[];
|
|
91
|
+
service: string[];
|
|
92
|
+
outcome: string[];
|
|
93
|
+
};
|
|
94
|
+
roster: CategoryRosterDelta[];
|
|
95
|
+
/** Nav label → replacement label. Declared, not yet applied to a scaffold. */
|
|
96
|
+
nav: Record<string, string>;
|
|
97
|
+
content_posture: (typeof CONTENT_POSTURES)[number];
|
|
98
|
+
intake: CategoryIntakeItem[];
|
|
99
|
+
}
|
|
100
|
+
/** A recorded tier-2 intake — `content/_intake.yaml`. */
|
|
101
|
+
export interface CategoryIntake {
|
|
102
|
+
answers: Record<string, string | string[] | null>;
|
|
103
|
+
}
|
|
104
|
+
export interface ParseCategoryProfileOptions {
|
|
105
|
+
/** The file's basename without `.yaml`; the profile's `slug` must equal it. */
|
|
106
|
+
fileSlug: string;
|
|
107
|
+
/** Registered page-type keys (the schema registry). */
|
|
108
|
+
pageTypes: ReadonlySet<string>;
|
|
109
|
+
/** Leaf category slugs (the category registry). */
|
|
110
|
+
categories: ReadonlySet<string>;
|
|
111
|
+
}
|
|
112
|
+
/** Thrown on an invalid profile or intake; the message names the failing field path. */
|
|
113
|
+
export declare class CategoryProfileError extends Error {
|
|
114
|
+
constructor(message: string);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Validate a parsed category profile.
|
|
118
|
+
* @throws CategoryProfileError naming the failing field path.
|
|
119
|
+
*/
|
|
120
|
+
export declare function parseCategoryProfile(raw: unknown, options: ParseCategoryProfileOptions): CategoryProfile;
|
|
121
|
+
/**
|
|
122
|
+
* Validate a recorded intake (`content/_intake.yaml`) against its profile.
|
|
123
|
+
* Unknown ids fail; an id the file omits is returned as `null` (unanswered).
|
|
124
|
+
* @throws CategoryProfileError naming the failing field path.
|
|
125
|
+
*/
|
|
126
|
+
export declare function parseIntake(raw: unknown, profile: CategoryProfile): CategoryIntake;
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The contract's shape table, as data: each shape's primary node, the
|
|
3
|
+
* secondaries it allows, and whether it is reserved. Keyed by the manifest's
|
|
4
|
+
* own `StarterShape`, so a shape added there without a row here fails
|
|
5
|
+
* type-check.
|
|
6
|
+
*/
|
|
7
|
+
export const CATEGORY_PROFILE_SHAPES = {
|
|
8
|
+
"organization-product": {
|
|
9
|
+
primary: "Organization",
|
|
10
|
+
secondaries: ["SoftwareApplication"],
|
|
11
|
+
reserved: false,
|
|
12
|
+
},
|
|
13
|
+
"organization-service": {
|
|
14
|
+
primary: "Organization",
|
|
15
|
+
secondaries: [],
|
|
16
|
+
reserved: false,
|
|
17
|
+
},
|
|
18
|
+
"local-business-service": {
|
|
19
|
+
primary: "LocalBusiness",
|
|
20
|
+
secondaries: ["Person"],
|
|
21
|
+
reserved: false,
|
|
22
|
+
},
|
|
23
|
+
"local-business-product": {
|
|
24
|
+
primary: "LocalBusiness",
|
|
25
|
+
secondaries: [],
|
|
26
|
+
reserved: true,
|
|
27
|
+
},
|
|
28
|
+
"person-service": {
|
|
29
|
+
primary: "Person",
|
|
30
|
+
secondaries: ["Organization"],
|
|
31
|
+
reserved: false,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* schema.org's `LocalBusiness` subtree, `LocalBusiness` itself included — the
|
|
36
|
+
* closure of the `LocalBusiness` union in `schema-dts`. A literal so `src/`
|
|
37
|
+
* needs no type package at run time; `category-profile.test.ts` proves it
|
|
38
|
+
* set-equal to the `schema-dts` closure, so it cannot drift silently.
|
|
39
|
+
*/
|
|
40
|
+
export const LOCAL_BUSINESS_TYPES = new Set([
|
|
41
|
+
"AccountingService", "AdultEntertainment", "AmusementPark", "AnimalShelter",
|
|
42
|
+
"ArchiveOrganization", "ArtGallery", "Attorney", "AutoBodyShop", "AutoDealer",
|
|
43
|
+
"AutoPartsStore", "AutoRental", "AutoRepair", "AutoWash", "AutomatedTeller",
|
|
44
|
+
"AutomotiveBusiness", "Bakery", "BankOrCreditUnion", "BarOrPub", "BeautySalon",
|
|
45
|
+
"BedAndBreakfast", "BikeStore", "BookStore", "BowlingAlley", "Brewery",
|
|
46
|
+
"CafeOrCoffeeShop", "Campground", "Casino", "ChildCare", "ClothingStore",
|
|
47
|
+
"ComedyClub", "CommunityHealth", "ComputerStore", "ConvenienceStore",
|
|
48
|
+
"CovidTestingFacility", "DaySpa", "Dentist", "DepartmentStore", "Dermatology",
|
|
49
|
+
"DietNutrition", "Distillery", "DryCleaningOrLaundry", "Electrician",
|
|
50
|
+
"ElectronicsStore", "Emergency", "EmergencyService", "EmploymentAgency",
|
|
51
|
+
"EntertainmentBusiness", "ExerciseGym", "FastFoodRestaurant", "FinancialService",
|
|
52
|
+
"FireStation", "Florist", "FoodEstablishment", "FurnitureStore", "GardenStore",
|
|
53
|
+
"GasStation", "GeneralContractor", "Geriatric", "GolfCourse", "GovernmentOffice",
|
|
54
|
+
"GroceryStore", "Gynecologic", "HVACBusiness", "HairSalon", "HardwareStore",
|
|
55
|
+
"HealthAndBeautyBusiness", "HealthClub", "HobbyShop", "HomeAndConstructionBusiness",
|
|
56
|
+
"HomeGoodsStore", "Hospital", "Hostel", "Hotel", "HousePainter", "IceCreamShop",
|
|
57
|
+
"IndividualPhysician", "InsuranceAgency", "InternetCafe", "JewelryStore",
|
|
58
|
+
"LegalService", "Library", "LiquorStore", "LocalBusiness", "Locksmith",
|
|
59
|
+
"LodgingBusiness", "MedicalBusiness", "MedicalClinic", "MensClothingStore",
|
|
60
|
+
"Midwifery", "MobilePhoneStore", "Motel", "MotorcycleDealer", "MotorcycleRepair",
|
|
61
|
+
"MovieRentalStore", "MovieTheater", "MovingCompany", "MusicStore", "NailSalon",
|
|
62
|
+
"NightClub", "Notary", "Nursing", "Obstetric", "OfficeEquipmentStore", "Oncologic",
|
|
63
|
+
"Optician", "Optometric", "Otolaryngologic", "OutletStore", "PawnShop", "Pediatric",
|
|
64
|
+
"PetStore", "Pharmacy", "Physician", "PhysiciansOffice", "Physiotherapy",
|
|
65
|
+
"PlasticSurgery", "Plumber", "Podiatric", "PoliceStation", "PostOffice",
|
|
66
|
+
"PrimaryCare", "ProfessionalService", "Psychiatric", "PublicHealth",
|
|
67
|
+
"PublicSwimmingPool", "RadioStation", "RealEstateAgent", "RecyclingCenter",
|
|
68
|
+
"Resort", "Restaurant", "RoofingContractor", "SelfStorage", "ShoeStore",
|
|
69
|
+
"ShoppingCenter", "SkiResort", "SportingGoodsStore", "SportsActivityLocation",
|
|
70
|
+
"SportsClub", "StadiumOrArena", "Store", "TattooParlor", "TelevisionStation",
|
|
71
|
+
"TennisComplex", "TireShop", "TouristInformationCenter", "ToyStore",
|
|
72
|
+
"TravelAgency", "VacationRental", "WholesaleStore", "Winery",
|
|
73
|
+
]);
|
|
74
|
+
export const OFFERING_PRICING = ["quote", "fixed", "hourly", "subscription"];
|
|
75
|
+
export const PROOF_KINDS = [
|
|
76
|
+
"credential",
|
|
77
|
+
"reviews",
|
|
78
|
+
"before-after",
|
|
79
|
+
"response-time",
|
|
80
|
+
"guarantee",
|
|
81
|
+
"experience",
|
|
82
|
+
];
|
|
83
|
+
export const CONTENT_POSTURES = ["instructions", "sample"];
|
|
84
|
+
/** Thrown on an invalid profile or intake; the message names the failing field path. */
|
|
85
|
+
export class CategoryProfileError extends Error {
|
|
86
|
+
constructor(message) {
|
|
87
|
+
super(message);
|
|
88
|
+
this.name = "CategoryProfileError";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function fail(path, message) {
|
|
92
|
+
throw new CategoryProfileError(`${path}: ${message}`);
|
|
93
|
+
}
|
|
94
|
+
function object(value, path) {
|
|
95
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
96
|
+
fail(path, "must be a mapping");
|
|
97
|
+
}
|
|
98
|
+
return value;
|
|
99
|
+
}
|
|
100
|
+
function keys(value, path, required, optional = []) {
|
|
101
|
+
const allowed = new Set([...required, ...optional]);
|
|
102
|
+
for (const key of Object.keys(value)) {
|
|
103
|
+
if (!allowed.has(key))
|
|
104
|
+
fail(path === "" ? key : `${path}.${key}`, "unknown key");
|
|
105
|
+
}
|
|
106
|
+
for (const key of required) {
|
|
107
|
+
if (!(key in value))
|
|
108
|
+
fail(path === "" ? key : `${path}.${key}`, "is required");
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function string(value, path) {
|
|
112
|
+
if (typeof value !== "string" || value.trim() === "")
|
|
113
|
+
fail(path, "must be a non-empty string");
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
116
|
+
function oneOf(value, allowed, path) {
|
|
117
|
+
if (!allowed.includes(value)) {
|
|
118
|
+
fail(path, `must be one of ${allowed.join(", ")} (got ${JSON.stringify(value)})`);
|
|
119
|
+
}
|
|
120
|
+
return value;
|
|
121
|
+
}
|
|
122
|
+
function list(value, path, nonEmpty) {
|
|
123
|
+
if (!Array.isArray(value))
|
|
124
|
+
fail(path, "must be a list");
|
|
125
|
+
if (nonEmpty && value.length === 0)
|
|
126
|
+
fail(path, "must not be empty");
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
function strings(value, path) {
|
|
130
|
+
return list(value, path, true).map((item, i) => string(item, `${path}[${i}]`));
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Validate a parsed category profile.
|
|
134
|
+
* @throws CategoryProfileError naming the failing field path.
|
|
135
|
+
*/
|
|
136
|
+
export function parseCategoryProfile(raw, options) {
|
|
137
|
+
const doc = object(raw, "(profile)");
|
|
138
|
+
keys(doc, "", ["slug", "shape", "offerings", "proof", "vocabulary", "roster", "nav", "content_posture", "intake"], ["subtype", "secondary"]);
|
|
139
|
+
const slug = string(doc.slug, "slug");
|
|
140
|
+
if (!options.categories.has(slug))
|
|
141
|
+
fail("slug", `"${slug}" is not a leaf category slug`);
|
|
142
|
+
if (slug !== options.fileSlug) {
|
|
143
|
+
fail("slug", `"${slug}" does not match its file name "${options.fileSlug}.yaml"`);
|
|
144
|
+
}
|
|
145
|
+
const shapes = Object.keys(CATEGORY_PROFILE_SHAPES);
|
|
146
|
+
const shape = oneOf(doc.shape, shapes, "shape");
|
|
147
|
+
const shapeRow = CATEGORY_PROFILE_SHAPES[shape];
|
|
148
|
+
if (shapeRow.reserved)
|
|
149
|
+
fail("shape", `"${shape}" is reserved and not yet built`);
|
|
150
|
+
let subtype;
|
|
151
|
+
if (shapeRow.primary === "LocalBusiness") {
|
|
152
|
+
subtype = string(doc.subtype, "subtype");
|
|
153
|
+
if (!LOCAL_BUSINESS_TYPES.has(subtype)) {
|
|
154
|
+
fail("subtype", `"${subtype}" is not a schema.org LocalBusiness subtype`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else if (doc.subtype !== undefined) {
|
|
158
|
+
fail("subtype", `is only valid on a local-business-* shape, not "${shape}"`);
|
|
159
|
+
}
|
|
160
|
+
let secondary;
|
|
161
|
+
if (doc.secondary !== undefined) {
|
|
162
|
+
if (shapeRow.secondaries.length === 0)
|
|
163
|
+
fail("secondary", `is not allowed on "${shape}"`);
|
|
164
|
+
secondary = oneOf(doc.secondary, shapeRow.secondaries, "secondary");
|
|
165
|
+
}
|
|
166
|
+
const offerings = list(doc.offerings, "offerings", true).map((item, i) => {
|
|
167
|
+
const path = `offerings[${i}]`;
|
|
168
|
+
const o = object(item, path);
|
|
169
|
+
keys(o, path, ["name", "job", "pricing"]);
|
|
170
|
+
return {
|
|
171
|
+
name: string(o.name, `${path}.name`),
|
|
172
|
+
job: string(o.job, `${path}.job`),
|
|
173
|
+
pricing: oneOf(o.pricing, OFFERING_PRICING, `${path}.pricing`),
|
|
174
|
+
};
|
|
175
|
+
});
|
|
176
|
+
const proof = list(doc.proof, "proof", true).map((item, i) => {
|
|
177
|
+
const path = `proof[${i}]`;
|
|
178
|
+
const p = object(item, path);
|
|
179
|
+
keys(p, path, ["kind", "label"]);
|
|
180
|
+
return { kind: oneOf(p.kind, PROOF_KINDS, `${path}.kind`), label: string(p.label, `${path}.label`) };
|
|
181
|
+
});
|
|
182
|
+
const vocab = object(doc.vocabulary, "vocabulary");
|
|
183
|
+
keys(vocab, "vocabulary", ["problem", "service", "outcome"]);
|
|
184
|
+
const vocabulary = {
|
|
185
|
+
problem: strings(vocab.problem, "vocabulary.problem"),
|
|
186
|
+
service: strings(vocab.service, "vocabulary.service"),
|
|
187
|
+
outcome: strings(vocab.outcome, "vocabulary.outcome"),
|
|
188
|
+
};
|
|
189
|
+
const roster = list(doc.roster ?? [], "roster", false).map((item, i) => {
|
|
190
|
+
const path = `roster[${i}]`;
|
|
191
|
+
const r = object(item, path);
|
|
192
|
+
const op = oneOf(r.op, ["add", "drop"], `${path}.op`);
|
|
193
|
+
const route = string(r.route, `${path}.route`);
|
|
194
|
+
if (!route.startsWith("/"))
|
|
195
|
+
fail(`${path}.route`, `must start with "/"`);
|
|
196
|
+
if (op === "drop") {
|
|
197
|
+
keys(r, path, ["op", "route", "reason"]);
|
|
198
|
+
return { op, route, reason: string(r.reason, `${path}.reason`) };
|
|
199
|
+
}
|
|
200
|
+
keys(r, path, ["op", "route", "pageType", "reason"]);
|
|
201
|
+
const pageType = string(r.pageType, `${path}.pageType`);
|
|
202
|
+
if (!options.pageTypes.has(pageType)) {
|
|
203
|
+
fail(`${path}.pageType`, `"${pageType}" is not a registered page type`);
|
|
204
|
+
}
|
|
205
|
+
return { op, route, pageType, reason: string(r.reason, `${path}.reason`) };
|
|
206
|
+
});
|
|
207
|
+
const navDoc = doc.nav === null ? {} : object(doc.nav, "nav");
|
|
208
|
+
const nav = Object.fromEntries(Object.entries(navDoc).map(([from, to]) => [from, string(to, `nav.${from}`)]));
|
|
209
|
+
const ids = new Set();
|
|
210
|
+
const intake = list(doc.intake, "intake", true).map((item, i) => {
|
|
211
|
+
const path = `intake[${i}]`;
|
|
212
|
+
const q = object(item, path);
|
|
213
|
+
keys(q, path, ["id", "ask", "fills"]);
|
|
214
|
+
const id = string(q.id, `${path}.id`);
|
|
215
|
+
if (ids.has(id))
|
|
216
|
+
fail(`${path}.id`, `"${id}" is not unique`);
|
|
217
|
+
ids.add(id);
|
|
218
|
+
return { id, ask: string(q.ask, `${path}.ask`), fills: strings(q.fills, `${path}.fills`) };
|
|
219
|
+
});
|
|
220
|
+
return {
|
|
221
|
+
slug,
|
|
222
|
+
shape,
|
|
223
|
+
...(subtype !== undefined ? { subtype } : {}),
|
|
224
|
+
...(secondary !== undefined ? { secondary } : {}),
|
|
225
|
+
offerings,
|
|
226
|
+
proof,
|
|
227
|
+
vocabulary,
|
|
228
|
+
roster,
|
|
229
|
+
nav,
|
|
230
|
+
content_posture: oneOf(doc.content_posture, CONTENT_POSTURES, "content_posture"),
|
|
231
|
+
intake,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Validate a recorded intake (`content/_intake.yaml`) against its profile.
|
|
236
|
+
* Unknown ids fail; an id the file omits is returned as `null` (unanswered).
|
|
237
|
+
* @throws CategoryProfileError naming the failing field path.
|
|
238
|
+
*/
|
|
239
|
+
export function parseIntake(raw, profile) {
|
|
240
|
+
const doc = object(raw, "(intake)");
|
|
241
|
+
keys(doc, "", ["answers"]);
|
|
242
|
+
const given = doc.answers === null ? {} : object(doc.answers, "answers");
|
|
243
|
+
const known = new Set(profile.intake.map((q) => q.id));
|
|
244
|
+
for (const id of Object.keys(given)) {
|
|
245
|
+
if (!known.has(id))
|
|
246
|
+
fail(`answers.${id}`, "is not an intake id of this profile");
|
|
247
|
+
}
|
|
248
|
+
const answers = {};
|
|
249
|
+
for (const { id } of profile.intake) {
|
|
250
|
+
const value = given[id];
|
|
251
|
+
const path = `answers.${id}`;
|
|
252
|
+
if (value === undefined || value === null)
|
|
253
|
+
answers[id] = null;
|
|
254
|
+
else if (typeof value === "string")
|
|
255
|
+
answers[id] = value.trim() === "" ? null : value;
|
|
256
|
+
else if (Array.isArray(value))
|
|
257
|
+
answers[id] = value.map((v, i) => string(v, `${path}[${i}]`));
|
|
258
|
+
else
|
|
259
|
+
fail(path, "must be a string, a list of strings, or null");
|
|
260
|
+
}
|
|
261
|
+
return { answers };
|
|
262
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -3,8 +3,12 @@
|
|
|
3
3
|
* `create-ailk` — entry point.
|
|
4
4
|
*
|
|
5
5
|
* Orchestration:
|
|
6
|
-
* 0. on
|
|
6
|
+
* 0. on an `add-module` / `upgrade` subcommand: parse its own argv, run
|
|
7
|
+
* addModule(), print the report, exit (2 on a refusal it names)
|
|
8
|
+
* on a `sync-routes` subcommand: parse its own argv, run syncRoutesSync(),
|
|
7
9
|
* print the report, exit (1 iff routes are missing in check mode)
|
|
10
|
+
* on a `license set` subcommand: write the key into the scaffold, push it
|
|
11
|
+
* to a linked Vercel project and redeploy, exit (#6683 D9)
|
|
8
12
|
* 1. parse argv → CliOptions
|
|
9
13
|
* 2. on --help: print helpText, exit 0
|
|
10
14
|
* 3. on --version: print version, exit 0
|
|
@@ -30,9 +34,11 @@ import { spawnSync } from "node:child_process";
|
|
|
30
34
|
import { readFileSync } from "node:fs";
|
|
31
35
|
import { dirname, resolve } from "node:path";
|
|
32
36
|
import { fileURLToPath } from "node:url";
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
37
|
+
import { ADD_MODULE_HELP, AddModuleRefusedError, formatAddModuleReport, parseAddModuleArgs, } from "./add-module.js";
|
|
38
|
+
import { FREE_PREREQUISITES_NOTE, LICENSED_PREREQUISITES_NOTE, isLicensedInstall, proCallout, } from "./lib/pro-callout.js";
|
|
39
|
+
import { LICENSE_SET_HELP, runLicenseSet } from "./license-set.js";
|
|
40
|
+
import { helpText, parseCliArgs, parseModules, resolveCliShape, toScaffoldOptions, } from "./parse-args.js";
|
|
41
|
+
import { addModule, formatSurfaceDrift, scaffold, syncArchitectureSync, syncCatalogSync, syncRoutesSync, } from "./programmatic.js";
|
|
36
42
|
import { CORE_SURFACE_PATHS, SURFACE_ALIASES } from "./surfaces.js";
|
|
37
43
|
import { SYNC_ROUTES_HELP, formatSyncRoutesReport, parseSyncRoutesArgs, } from "./sync-routes.js";
|
|
38
44
|
const PKG_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
@@ -83,6 +89,32 @@ function readScaffoldPrerequisites(targetAbs) {
|
|
|
83
89
|
}
|
|
84
90
|
async function main() {
|
|
85
91
|
const argv = process.argv.slice(2);
|
|
92
|
+
// `add-module` (alias `upgrade`, #6653): apply a purchased module to an
|
|
93
|
+
// existing site. Its own argv shape, like `sync-routes` below. A refusal it
|
|
94
|
+
// names (a free module, a patch outside the config allowlist, an unreadable
|
|
95
|
+
// architecture.yaml) exits 2; anything else — the licence gate included —
|
|
96
|
+
// falls through to the top-level catch, exit 1, as a scaffold would.
|
|
97
|
+
if (argv[0] === "add-module" || argv[0] === "upgrade") {
|
|
98
|
+
const args = parseAddModuleArgs(argv.slice(1));
|
|
99
|
+
if (args.help || args.module === undefined) {
|
|
100
|
+
process.stdout.write(ADD_MODULE_HELP);
|
|
101
|
+
process.exit(0);
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
const result = await addModule({
|
|
105
|
+
targetDir: args.target ?? process.cwd(),
|
|
106
|
+
moduleName: args.module,
|
|
107
|
+
});
|
|
108
|
+
process.stdout.write(formatAddModuleReport(result, args.module));
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
if (!(err instanceof AddModuleRefusedError))
|
|
112
|
+
throw err;
|
|
113
|
+
process.stderr.write(`[create-ailk] ${err.message}\n`);
|
|
114
|
+
process.exit(err.exitCode);
|
|
115
|
+
}
|
|
116
|
+
process.exit(0);
|
|
117
|
+
}
|
|
86
118
|
// Subcommand dispatch, ahead of the flag parser: `sync-routes` has its own
|
|
87
119
|
// argv shape (#4986). Check mode exits 1 iff a template route is missing;
|
|
88
120
|
// locally modified routes are reported but do not fail; --apply exits 0.
|
|
@@ -99,7 +131,19 @@ async function main() {
|
|
|
99
131
|
process.stdout.write(formatSyncRoutesReport(result, args.apply));
|
|
100
132
|
process.exit(!args.apply && result.missing.length > 0 ? 1 : 0);
|
|
101
133
|
}
|
|
102
|
-
|
|
134
|
+
// `license set <key|-> [dir]` (#6683 D9): its own argv shape, ahead of the
|
|
135
|
+
// flag parser like `sync-routes`. The key reaches `vercel` on stdin only.
|
|
136
|
+
if (argv[0] === "license" && argv[1] === "set") {
|
|
137
|
+
process.exit(runLicenseSet(argv.slice(2), {
|
|
138
|
+
cwd: process.cwd(),
|
|
139
|
+
stdout: (text) => process.stdout.write(text),
|
|
140
|
+
stderr: (text) => process.stderr.write(text),
|
|
141
|
+
readStdin: () => readFileSync(0, "utf8"),
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
// No silent default (#6754 D2/D3): a scaffold run naming neither --shape
|
|
145
|
+
// nor --template asks for a shape on a terminal and is refused without one.
|
|
146
|
+
const opts = await resolveCliShape(parseCliArgs(argv, { interactive: process.stdin.isTTY === true }), { input: process.stdin, output: process.stdout });
|
|
103
147
|
// Deprecation notice for a retired `--surfaces` id (#5563 D2). `surfaces.ts`
|
|
104
148
|
// stays pure — `canonicalizeSurface` only resolves the alias — so this is
|
|
105
149
|
// the one place that prints: compare the raw `--surfaces` argv token
|
|
@@ -118,7 +162,13 @@ async function main() {
|
|
|
118
162
|
}
|
|
119
163
|
}
|
|
120
164
|
if (opts.help) {
|
|
121
|
-
process.stdout.write(helpText +
|
|
165
|
+
process.stdout.write(helpText +
|
|
166
|
+
"\n" +
|
|
167
|
+
SYNC_ROUTES_HELP +
|
|
168
|
+
"\n" +
|
|
169
|
+
ADD_MODULE_HELP +
|
|
170
|
+
"\n" +
|
|
171
|
+
LICENSE_SET_HELP);
|
|
122
172
|
process.exit(0);
|
|
123
173
|
}
|
|
124
174
|
if (opts.version) {
|
|
@@ -161,22 +211,16 @@ async function main() {
|
|
|
161
211
|
// scaffold() gates + fetches any requested paid module (K1, #5089) and any
|
|
162
212
|
// named site template's own bundle (#5481), then validates and copies the
|
|
163
213
|
// local template — throws ValidationError or LicenseRequiredError on
|
|
164
|
-
// rejection (caught at top level below).
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
//
|
|
214
|
+
// rejection (caught at top level below). The network invariant (#6759 D6):
|
|
215
|
+
// no key → no request at all; a key with no --modules → the D4 level probe
|
|
216
|
+
// (3-4 requests); an explicit --modules list → requests only for the paid
|
|
217
|
+
// modules it names, exactly as before this card. `--template
|
|
218
|
+
// marketing-site-free` still rides the no-key case (record D11): its
|
|
168
219
|
// `requires` is all-free, so it installs from the shipped
|
|
169
220
|
// `site-templates/` tree with no key and no request.
|
|
170
|
-
const { targetDir: targetAbs, surfaces } = await scaffold(
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
surfaces: opts.surfaces,
|
|
174
|
-
withCatalog: opts.withCatalog,
|
|
175
|
-
// Spread rather than passed as `undefined` so a run naming no template
|
|
176
|
-
// hands `scaffold()` exactly the option object it got before the flag
|
|
177
|
-
// existed (spec 5481 § 3 DD3's byte-identity floor).
|
|
178
|
-
...(opts.template !== undefined ? { template: opts.template } : {}),
|
|
179
|
-
});
|
|
221
|
+
const { targetDir: targetAbs, surfaces, starter, modules: effectiveModules, } = await scaffold(toScaffoldOptions(opts));
|
|
222
|
+
// The starter scaffold() resolved — from --template, or from --shape (#6754 D4).
|
|
223
|
+
const templateName = starter?.template ?? opts.template;
|
|
180
224
|
if (opts.install) {
|
|
181
225
|
// Hard-coded argv. User input only flows through `cwd`, never the command.
|
|
182
226
|
const result = spawnSync("pnpm", ["install"], {
|
|
@@ -188,19 +232,28 @@ async function main() {
|
|
|
188
232
|
throw new Error(`pnpm install failed in ${targetAbs} (exit ${result.status}). Run it manually after fixing the issue.`);
|
|
189
233
|
}
|
|
190
234
|
}
|
|
191
|
-
// Next-steps message.
|
|
235
|
+
// Next-steps message. A licensed install (#6480) is not told it needs no
|
|
236
|
+
// licence key, and is not pitched the Pro tier it already owns. Reads the
|
|
237
|
+
// EFFECTIVE module set (#6759 D6) — result.modules already carries any
|
|
238
|
+
// template's requires and the key's derived level, so it stays correct for
|
|
239
|
+
// a keyed default scaffold, which opts.modules alone would not have shown.
|
|
240
|
+
const licensed = isLicensedInstall({ modules: effectiveModules });
|
|
192
241
|
const lines = [
|
|
193
242
|
"",
|
|
194
|
-
|
|
195
|
-
? `Scaffolded AI Launch Kit starter from the "${
|
|
243
|
+
templateName !== undefined
|
|
244
|
+
? `Scaffolded AI Launch Kit starter from the "${templateName}" template.`
|
|
196
245
|
: "Scaffolded AI Launch Kit starter.",
|
|
246
|
+
...(starter !== undefined
|
|
247
|
+
? [`Starter: shape ${starter.shape}, design ${starter.design}.`]
|
|
248
|
+
: []),
|
|
197
249
|
"",
|
|
198
250
|
];
|
|
199
251
|
// Prerequisites (#6440 AC4) — every one the scaffold enforces, and nothing
|
|
200
252
|
// it does not. A starter reaches `pnpm dev` AND `pnpm build` on a toolchain
|
|
201
253
|
// alone: the Prisma client is generated by `database`'s own `postinstall`,
|
|
202
|
-
// so there is no generator step to remember, and no database
|
|
203
|
-
//
|
|
254
|
+
// so there is no generator step to remember, and no database or vendor API
|
|
255
|
+
// key gates either command (a free install also says no licence key does;
|
|
256
|
+
// a licensed one, #6480, does not). Saying so is the point — the previous
|
|
204
257
|
// output named no prerequisites at all, which left a customer to discover
|
|
205
258
|
// the toolchain floor from a failure.
|
|
206
259
|
const prereqs = readScaffoldPrerequisites(targetAbs);
|
|
@@ -210,7 +263,12 @@ async function main() {
|
|
|
210
263
|
].filter((part) => part !== undefined);
|
|
211
264
|
if (toolchain.length > 0) {
|
|
212
265
|
lines.push(`Prerequisites: ${toolchain.join(" and ")} — that is all.`);
|
|
213
|
-
lines.push(
|
|
266
|
+
lines.push(licensed ? LICENSED_PREREQUISITES_NOTE : FREE_PREREQUISITES_NOTE);
|
|
267
|
+
lines.push("");
|
|
268
|
+
}
|
|
269
|
+
// Only when asked for (#6683 D9) — a run without --license is unchanged.
|
|
270
|
+
if (opts.license !== undefined) {
|
|
271
|
+
lines.push("Licence key written to apps/api/.env and ailk.key (owner-only, gitignored).");
|
|
214
272
|
lines.push("");
|
|
215
273
|
}
|
|
216
274
|
lines.push("Next steps:");
|
|
@@ -226,15 +284,17 @@ async function main() {
|
|
|
226
284
|
lines.push("");
|
|
227
285
|
lines.push(`Route surfaces: ${CORE_SURFACE_PATHS.join(", ")}` +
|
|
228
286
|
(surfaces.length > 0 ? ` + ${surfaces.join(", ")}` : ""));
|
|
229
|
-
lines.push("Add another with the `
|
|
287
|
+
lines.push("Add another with the `add-page` skill, or re-run with --surfaces <id>.");
|
|
230
288
|
// Only when asked for. The default scaffold's output is unchanged.
|
|
231
289
|
if (opts.withCatalog) {
|
|
232
290
|
lines.push("Component catalog: COMPONENT-CATALOG.md — search it by capability before building a component.");
|
|
233
291
|
}
|
|
234
292
|
lines.push("Reference: ADR 0007 — pnpm create ailk@latest is the canonical install path.");
|
|
235
293
|
lines.push("");
|
|
236
|
-
|
|
237
|
-
|
|
294
|
+
if (!licensed) {
|
|
295
|
+
lines.push(proCallout());
|
|
296
|
+
lines.push("");
|
|
297
|
+
}
|
|
238
298
|
process.stdout.write(lines.join("\n"));
|
|
239
299
|
}
|
|
240
300
|
main().catch((err) => {
|
package/dist/index.d.ts
CHANGED
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export { copyTemplate, type CopyTemplateOptions } from "./copy.js";
|
|
7
7
|
export { parseCliArgs, parseModules, ParseError, helpText, RECOGNIZED_MODULES, type ModuleName, type CliOptions, } from "./parse-args.js";
|
|
8
|
-
export { CATALOG_TARGET_PATH, FREE_MODULES, LicenseRequiredError, PAID_MODULES, classifyModules, formatSurfaceDrift, scaffold, scaffoldSync, syncArchitectureSync, syncCatalogSync, syncRoutesSync, type ResolvePaidModulesOptions, type ScaffoldOptions, type ScaffoldResult, type SyncArchitectureOptions, type SyncArchitectureResult, type SyncCatalogResult, type SyncRoutesSyncOptions, } from "./programmatic.js";
|
|
8
|
+
export { CATALOG_TARGET_PATH, FREE_MODULES, addModule, LicenseRequiredError, PAID_MODULES, classifyModules, formatSurfaceDrift, scaffold, scaffoldSync, syncArchitectureSync, syncCatalogSync, syncRoutesSync, type AddModuleOptions, type ResolvePaidModulesOptions, type ScaffoldOptions, type ScaffoldResult, type SyncArchitectureOptions, type SyncArchitectureResult, type SyncCatalogResult, type SyncRoutesSyncOptions, } from "./programmatic.js";
|
|
9
9
|
export { DEFAULT_AILK_MODULE_REGISTRY_URL, FetchModuleError, fetchModule, type FetchModuleOptions, } from "./lib/fetch-module.js";
|
|
10
|
-
export { extractModuleBundle, type ExtractModuleBundleOptions, } from "./lib/extract-module-bundle.js";
|
|
10
|
+
export { extractModuleBundle, type ExistingFilePolicy, type ExtractModuleBundleOptions, type InstallReportRow, } from "./lib/extract-module-bundle.js";
|
|
11
|
+
export { ADD_MODULE_HELP, AddModuleRefusedError, applyModuleToScaffold, formatAddModuleReport, parseAddModuleArgs, type AddModuleReportRow, type AddModuleResult, type ApplyModuleOptions, } from "./add-module.js";
|
|
12
|
+
export { SITE_ROSTER_PATH, readSiteRoster, siteRosterProblems, } from "./site-roster.js";
|
|
11
13
|
export { SYNC_ROUTES_HELP, formatSyncRoutesReport, listTemplateRouteDirs, parseSyncRoutesArgs, syncRoutes, type RouteReport, type SyncRoutesOptions, type SyncRoutesResult, } from "./sync-routes.js";
|
|
12
14
|
export { deriveArchitecture } from "./derive-architecture.js";
|
|
13
15
|
export { CORE_ROUTE_DIRS, CORE_SURFACE_PATHS, NAV_CONFIG_PATH, GATEABLE_SURFACES, MODULE_SURFACES, RECOGNIZED_SURFACES, ROUTE_SINCE, SurfaceError, excludedContentPaths, excludedRoutePaths, parseSurfaces, renderNavConfig, resolveSurfaces, surfaceForRouteDir, type Surface, } from "./surfaces.js";
|
package/dist/index.js
CHANGED
|
@@ -5,12 +5,17 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export { copyTemplate } from "./copy.js";
|
|
7
7
|
export { parseCliArgs, parseModules, ParseError, helpText, RECOGNIZED_MODULES, } from "./parse-args.js";
|
|
8
|
-
export { CATALOG_TARGET_PATH, FREE_MODULES, LicenseRequiredError, PAID_MODULES, classifyModules, formatSurfaceDrift, scaffold, scaffoldSync, syncArchitectureSync, syncCatalogSync, syncRoutesSync, } from "./programmatic.js";
|
|
8
|
+
export { CATALOG_TARGET_PATH, FREE_MODULES, addModule, LicenseRequiredError, PAID_MODULES, classifyModules, formatSurfaceDrift, scaffold, scaffoldSync, syncArchitectureSync, syncCatalogSync, syncRoutesSync, } from "./programmatic.js";
|
|
9
9
|
// The paid-module fetch/extract primitives (K1, #5089 — Flavor 3). Exported
|
|
10
10
|
// so tooling (and this repo's own evidence-run scripts) can drive the fetch
|
|
11
11
|
// path directly without re-implementing its shape.
|
|
12
12
|
export { DEFAULT_AILK_MODULE_REGISTRY_URL, FetchModuleError, fetchModule, } from "./lib/fetch-module.js";
|
|
13
13
|
export { extractModuleBundle, } from "./lib/extract-module-bundle.js";
|
|
14
|
+
// `create-ailk add-module` (#6653) — apply a purchased module to an existing
|
|
15
|
+
// site in place. The pure core, beside `sync-routes`'s.
|
|
16
|
+
export { ADD_MODULE_HELP, AddModuleRefusedError, applyModuleToScaffold, formatAddModuleReport, parseAddModuleArgs, } from "./add-module.js";
|
|
17
|
+
// The site's page roster of record and its install-diff check (#6682).
|
|
18
|
+
export { SITE_ROSTER_PATH, readSiteRoster, siteRosterProblems, } from "./site-roster.js";
|
|
14
19
|
// `create-ailk sync-routes` (#4986) — the template-route drift check + copy.
|
|
15
20
|
// The pure module is exported so tooling can run it against any template dir.
|
|
16
21
|
export { SYNC_ROUTES_HELP, formatSyncRoutesReport, listTemplateRouteDirs, parseSyncRoutesArgs, syncRoutes, } from "./sync-routes.js";
|