create-ailk 0.6.0 → 0.7.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 +44 -5
- package/component-catalog.json +146 -21
- package/component-catalog.md +9 -6
- 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 +124 -13
- package/dist/copy.d.ts +14 -0
- package/dist/copy.js +38 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +6 -1
- package/dist/lib/extract-module-bundle.d.ts +76 -2
- package/dist/lib/extract-module-bundle.js +219 -8
- 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 +6 -1
- package/dist/lib/module-license-gate.js +6 -1
- package/dist/lib/pro-callout.d.ts +16 -10
- package/dist/lib/pro-callout.js +27 -4
- 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.0.tar.gz +0 -0
- package/dist/modules/commerce/0.6.1.tar.gz +0 -0
- package/dist/modules/docs-blog/0.6.0.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.0.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.0.tar.gz +0 -0
- package/dist/modules/scheduling/0.6.1.tar.gz +0 -0
- package/dist/modules/signal-collectors/0.6.0.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.0.tar.gz +0 -0
- package/dist/modules/workspace/0.6.1.tar.gz +0 -0
- package/dist/parse-args.d.ts +7 -0
- package/dist/parse-args.js +23 -1
- package/dist/programmatic.d.ts +46 -0
- package/dist/programmatic.js +144 -19
- 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 +494 -0
- package/dist/surfaces.d.ts +20 -7
- package/dist/surfaces.js +149 -28
- package/dist/sync-routes.d.ts +9 -0
- package/dist/sync-routes.js +2 -2
- package/dist/template-manifest.d.ts +58 -2
- package/dist/template-manifest.js +196 -69
- package/package.json +6 -4
- package/site-templates/_base/content/en/BookCall/book-call.mdx +53 -0
- package/site-templates/_base/content/en/GlossaryList/glossary.mdx +66 -0
- package/site-templates/_base/content/en/NewsletterSignup/newsletter.mdx +68 -0
- package/site-templates/_base/content/en/Pricing/pricing.mdx +71 -62
- package/site-templates/_base/content/en/PrivacyPage/privacy.mdx +2 -3
- package/site-templates/_base/content/en/TermsPage/terms.mdx +2 -3
- package/site-templates/_base/content/en/UseCase/first-use-case.mdx +23 -0
- package/site-templates/_base/content/en/UseCase/second-use-case.mdx +23 -0
- package/site-templates/_base/content/en/UseCase/third-use-case.mdx +23 -0
- package/site-templates/designs/minimalism/DESIGN.md +162 -0
- package/site-templates/designs/standard/DESIGN.md +162 -0
- package/site-templates/marketing-site/DESIGN.md +162 -0
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +312 -57
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +209 -90
- package/site-templates/marketing-site/ailk-template.json +1 -0
- package/site-templates/marketing-site-free/DESIGN.md +162 -0
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +260 -66
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +294 -114
- package/site-templates/marketing-site-free/ailk-template.json +3 -2
- package/site-templates/minimalism-site/DESIGN.md +162 -0
- package/site-templates/minimalism-site/TEMPLATE-MANIFEST.json +246 -19
- package/site-templates/minimalism-site/TEMPLATE-MANIFEST.md +185 -51
- package/site-templates/minimalism-site/ailk-template.json +3 -2
- package/site-templates/minimalism-site/content/en/BookCall/book-call.mdx +53 -0
- package/site-templates/minimalism-site/content/en/ContactPage/contact.mdx +3 -37
- package/site-templates/minimalism-site/content/en/PurchasePolicyPage/purchase-policy.mdx +124 -0
- package/site-templates/saas-product/DESIGN.md +162 -0
- package/site-templates/simple-service/DESIGN.md +162 -0
- package/site-templates/simple-service/TEMPLATE-MANIFEST.json +135 -26
- package/site-templates/simple-service/TEMPLATE-MANIFEST.md +114 -59
- package/site-templates/simple-service/ailk-template.json +4 -3
- package/site-templates/simple-service/content/en/BookCall/book-call.mdx +53 -0
- package/site-templates/simple-service/content/en/ContactPage/contact.mdx +1 -36
- package/site-templates/simple-service/content/en/PurchasePolicyPage/purchase-policy.mdx +124 -0
- package/templates/.claude/settings.json +15 -0
- package/templates/.claude/skills/README.md +8 -8
- 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 +111 -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/{provision-config → setup-deploy}/SKILL.md +8 -8
- package/templates/.claude/skills/{provision-config → setup-deploy}/references/env-vars-template.md +2 -2
- package/templates/.claude/skills/{provision-config → setup-deploy}/references/runbook-template.md +8 -8
- 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 +10 -5
- package/templates/DESIGN.md +162 -0
- package/templates/README.md +134 -38
- package/templates/apps/api/.env.example +2 -1
- package/templates/apps/api/package.json +1 -1
- package/templates/apps/api/src/config/__tests__/index.test.ts +37 -2
- package/templates/apps/api/src/config/index.ts +19 -6
- 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/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/server.ts +6 -0
- 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/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/web/PRODUCT.md +86 -0
- 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/ContactForm.tsx +94 -33
- package/templates/apps/web/app/[locale]/contact/ConversationalIntakeWrapper.tsx +53 -23
- package/templates/apps/web/app/[locale]/contact/__tests__/ContactForm.test.tsx +96 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +84 -2
- package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +5 -6
- package/templates/apps/web/app/[locale]/contact/page.tsx +25 -7
- 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]/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 +113 -51
- package/templates/apps/web/app/[locale]/newsletter/page.tsx +50 -0
- package/templates/apps/web/app/[locale]/partners/__tests__/page-renderer-migration.test.tsx +3 -3
- 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]/{shipping → 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 +48 -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__/site-theme.test.ts +135 -20
- 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 +64 -12
- package/templates/apps/web/lib/prisma-for-auth.ts +9 -6
- package/templates/apps/web/lib/site-brand.tsx +4 -8
- package/templates/apps/web/lib/site-chrome.ts +3 -3
- package/templates/apps/web/lib/site-identity.ts +134 -0
- package/templates/apps/web/lib/site-theme.ts +11 -0
- package/templates/apps/web/messages/en.json +52 -37
- package/templates/apps/web/middleware.ts +1 -1
- 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/apps/web/src/lib/commerce-sections.ts +35 -3
- package/templates/content/_site.mdx +41 -3
- package/templates/content/en/AboutPage/about.mdx +150 -144
- package/templates/content/en/BlogList/blog.mdx +105 -0
- package/templates/content/en/ContactPage/contact.mdx +44 -85
- package/templates/content/en/FAQPage/faq.mdx +86 -57
- package/templates/content/en/HomePage/home.mdx +276 -217
- package/templates/content/en/PurchasePolicyPage/purchase-policy.mdx +182 -0
- package/templates/content/en/SoftwareProduct/product.mdx +175 -0
- package/templates/content/en/UseCasesList/use-cases.mdx +170 -0
- package/templates/database/CHANGELOG.md +14 -0
- package/templates/database/package.json +3 -2
- 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/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/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]/cancellation/page.tsx +0 -44
- package/templates/apps/web/app/[locale]/returns/page.tsx +0 -44
- package/templates/apps/web/public/logo-dark.svg +0 -21
- package/templates/apps/web/public/logo-light.svg +0 -21
- 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
package/dist/surfaces.js
CHANGED
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
*
|
|
21
21
|
* The default (`--modules marketing`) enables NO gateable surface: every one of
|
|
22
22
|
* them is a claim about the business that the scaffolder cannot make. The path
|
|
23
|
-
* from there is the `
|
|
24
|
-
*
|
|
23
|
+
* from there is the `add-page` skill that already ships in
|
|
24
|
+
* `templates/.claude/skills/`.
|
|
25
25
|
*
|
|
26
26
|
* Zero runtime dependencies — create-ailk is a published, standalone CLI.
|
|
27
27
|
*
|
|
@@ -75,10 +75,19 @@ export const CORE_ROUTE_DIRS = [
|
|
|
75
75
|
// registering the group keeps `sign-in`'s existing `core` identity exactly.
|
|
76
76
|
`${ROUTE_ROOT}/(auth)`,
|
|
77
77
|
// #5469 D2 — always copied: it is what serves the Free template's
|
|
78
|
-
// /how-it-works
|
|
79
|
-
//
|
|
80
|
-
//
|
|
78
|
+
// /blog teaser page (its former /how-it-works and /features siblings
|
|
79
|
+
// moved to the /use-cases list and the product's own page), and it is
|
|
80
|
+
// inert for every slug that has no LandingPage MDX (notFound), so a
|
|
81
|
+
// scaffold carrying it and no content is byte-for-byte a scaffold with one
|
|
82
|
+
// more 404, nothing else.
|
|
81
83
|
`${ROUTE_ROOT}/[slug]`,
|
|
84
|
+
// #6658 D3 — the product's own detail page (`/software/product`, a
|
|
85
|
+
// SoftwareProduct → SoftwareApplication), which replaced the /features
|
|
86
|
+
// stand-in. Core for the same reason [slug] is: its content
|
|
87
|
+
// (`content/en/SoftwareProduct/product.mdx`) is in the always-copied overlay
|
|
88
|
+
// and the generated nav always links it, so every scaffold — template or
|
|
89
|
+
// not — must carry the route.
|
|
90
|
+
`${ROUTE_ROOT}/software`,
|
|
82
91
|
// #6084 D12 (the operator's shape-2 disposition) — the checkout return
|
|
83
92
|
// pair. The launchers pointing at them (`apps/mcp/src/tools/purchase.ts`,
|
|
84
93
|
// `apps/web/.../dev/purchase/purchase.actions.ts`) are ALREADY
|
|
@@ -118,6 +127,13 @@ export const GATEABLE_SURFACES = [
|
|
|
118
127
|
content: ["apps/web/content/<locale>/blog"],
|
|
119
128
|
navLabelKey: "blog",
|
|
120
129
|
},
|
|
130
|
+
{
|
|
131
|
+
// Module-less (#6658 D5), like `pricing`: every starter's roster names
|
|
132
|
+
// it. No `navLabelKey` — the footer links it, gated on this surface.
|
|
133
|
+
id: "book-a-call",
|
|
134
|
+
routes: [`${ROUTE_ROOT}/book-a-call`],
|
|
135
|
+
content: ["content/<locale>/BookCall"],
|
|
136
|
+
},
|
|
121
137
|
{
|
|
122
138
|
id: "careers",
|
|
123
139
|
routes: [`${ROUTE_ROOT}/careers`],
|
|
@@ -170,6 +186,12 @@ export const GATEABLE_SURFACES = [
|
|
|
170
186
|
routes: [`${ROUTE_ROOT}/flows`],
|
|
171
187
|
content: [],
|
|
172
188
|
},
|
|
189
|
+
{
|
|
190
|
+
// Module-less (#6658 D5) — see `book-a-call`.
|
|
191
|
+
id: "glossary",
|
|
192
|
+
routes: [`${ROUTE_ROOT}/glossary`],
|
|
193
|
+
content: ["content/<locale>/GlossaryList"],
|
|
194
|
+
},
|
|
173
195
|
{
|
|
174
196
|
id: "how-to",
|
|
175
197
|
routes: [`${ROUTE_ROOT}/how-to`],
|
|
@@ -206,6 +228,13 @@ export const GATEABLE_SURFACES = [
|
|
|
206
228
|
// functional gain, since no route derives from a message key.
|
|
207
229
|
navLabelKey: "schedule",
|
|
208
230
|
},
|
|
231
|
+
{
|
|
232
|
+
// Module-less (#6658 D5) — see `book-a-call`. The page's form posts to
|
|
233
|
+
// lead capture and sends no verification email.
|
|
234
|
+
id: "newsletter",
|
|
235
|
+
routes: [`${ROUTE_ROOT}/newsletter`],
|
|
236
|
+
content: ["content/<locale>/NewsletterSignup"],
|
|
237
|
+
},
|
|
209
238
|
{
|
|
210
239
|
id: "partners",
|
|
211
240
|
routes: [`${ROUTE_ROOT}/partners`],
|
|
@@ -247,11 +276,6 @@ export const GATEABLE_SURFACES = [
|
|
|
247
276
|
routes: [`${ROUTE_ROOT}/services`],
|
|
248
277
|
content: ["content/<locale>/ServicesList", "content/<locale>/Service"],
|
|
249
278
|
},
|
|
250
|
-
{
|
|
251
|
-
id: "software",
|
|
252
|
-
routes: [`${ROUTE_ROOT}/software`],
|
|
253
|
-
content: ["content/<locale>/SoftwareProduct"],
|
|
254
|
-
},
|
|
255
279
|
{
|
|
256
280
|
id: "tech-articles",
|
|
257
281
|
routes: [`${ROUTE_ROOT}/tech-articles`],
|
|
@@ -270,7 +294,7 @@ export const GATEABLE_SURFACES = [
|
|
|
270
294
|
{
|
|
271
295
|
id: "use-cases",
|
|
272
296
|
routes: [`${ROUTE_ROOT}/use-cases`],
|
|
273
|
-
content: ["content/<locale>/UseCasesList"],
|
|
297
|
+
content: ["content/<locale>/UseCasesList", "content/<locale>/UseCase"],
|
|
274
298
|
},
|
|
275
299
|
{
|
|
276
300
|
id: "whitepapers",
|
|
@@ -288,6 +312,14 @@ export const RECOGNIZED_SURFACES = GATEABLE_SURFACES.map((s) => s.id);
|
|
|
288
312
|
export const SURFACE_ALIASES = {
|
|
289
313
|
schedule: "meet",
|
|
290
314
|
};
|
|
315
|
+
/**
|
|
316
|
+
* Surface ids that became core routes (#6658 D3: `software` — the product's
|
|
317
|
+
* own page is in every scaffold). Accepted and dropped wherever a surface list
|
|
318
|
+
* is read — a `--surfaces` value or an existing site's `# Surfaces:` header —
|
|
319
|
+
* so a site scaffolded with `--surfaces software` keeps working: its route
|
|
320
|
+
* is copied regardless.
|
|
321
|
+
*/
|
|
322
|
+
export const CORE_PROMOTED_SURFACES = new Set(["software"]);
|
|
291
323
|
/** Canonical id for a `--surfaces` value, resolving a retired alias. */
|
|
292
324
|
function canonicalizeSurface(s) {
|
|
293
325
|
return SURFACE_ALIASES[s] ?? s;
|
|
@@ -346,7 +378,7 @@ export function parseSurfaces(raw) {
|
|
|
346
378
|
const requested = raw
|
|
347
379
|
.split(",")
|
|
348
380
|
.map((s) => canonicalizeSurface(s.trim().toLowerCase()))
|
|
349
|
-
.filter(
|
|
381
|
+
.filter((s) => s !== "" && !CORE_PROMOTED_SURFACES.has(s));
|
|
350
382
|
for (const s of requested) {
|
|
351
383
|
if (!RECOGNIZED_SURFACES.includes(s)) {
|
|
352
384
|
throw new SurfaceError(`unknown surface "${s}". Recognized: ${RECOGNIZED_SURFACES.join(", ")}`);
|
|
@@ -376,6 +408,8 @@ export function resolveSurfaces(modules = ["marketing"], explicit = []) {
|
|
|
376
408
|
const selected = new Set(modules.map((m) => m.trim().toLowerCase()));
|
|
377
409
|
for (const raw of explicit) {
|
|
378
410
|
const s = canonicalizeSurface(raw);
|
|
411
|
+
if (CORE_PROMOTED_SURFACES.has(s))
|
|
412
|
+
continue;
|
|
379
413
|
if (!RECOGNIZED_SURFACES.includes(s)) {
|
|
380
414
|
throw new SurfaceError(`unknown surface "${s}". Recognized: ${RECOGNIZED_SURFACES.join(", ")}`);
|
|
381
415
|
}
|
|
@@ -458,6 +492,10 @@ export function excludedContentPaths(enabled) {
|
|
|
458
492
|
export const ROUTE_SINCE = {
|
|
459
493
|
about: "0.0.0",
|
|
460
494
|
"blog/[[...slug]]": "0.0.0",
|
|
495
|
+
// #6658 — four genuinely new route directories (D4, D5), attaching at
|
|
496
|
+
// today's fixed-group version, the same "attach at today's version" rule
|
|
497
|
+
// #6084, #5977, #5678, #5479, #5371 and #5048 applied.
|
|
498
|
+
"book-a-call": "0.26.0",
|
|
461
499
|
careers: "0.0.0",
|
|
462
500
|
"careers/[slug]": "0.0.0",
|
|
463
501
|
"case-studies": "0.0.0",
|
|
@@ -475,6 +513,7 @@ export const ROUTE_SINCE = {
|
|
|
475
513
|
"flows/[slug]": "0.0.0",
|
|
476
514
|
"flows/[slug]/results/[payload]": "0.14.0",
|
|
477
515
|
"flows/[slug]/start": "0.18.0",
|
|
516
|
+
glossary: "0.26.0",
|
|
478
517
|
"how-to": "0.0.0",
|
|
479
518
|
"how-to/[slug]": "0.0.0",
|
|
480
519
|
integrations: "0.0.0",
|
|
@@ -486,6 +525,7 @@ export const ROUTE_SINCE = {
|
|
|
486
525
|
// in rather than the "predates the manifest" 0.0.0 above.
|
|
487
526
|
"meet/[slug]": "0.5.0",
|
|
488
527
|
"meet/confirmed": "0.0.0",
|
|
528
|
+
newsletter: "0.26.0",
|
|
489
529
|
partners: "0.0.0",
|
|
490
530
|
"partners/[slug]": "0.0.0",
|
|
491
531
|
// #5479 — three genuinely new route directories, so they attach at today's
|
|
@@ -519,6 +559,7 @@ export const ROUTE_SINCE = {
|
|
|
519
559
|
testimonials: "0.0.0",
|
|
520
560
|
"thanks/[slug]": "0.14.0",
|
|
521
561
|
"use-cases": "0.0.0",
|
|
562
|
+
"use-cases/[slug]": "0.26.0",
|
|
522
563
|
whitepapers: "0.0.0",
|
|
523
564
|
"whitepapers/[slug]": "0.0.0",
|
|
524
565
|
// #5014 — the (authed) route GROUP: layout.tsx + sign-in-gate.tsx sit
|
|
@@ -708,44 +749,113 @@ export const NAV_CONFIG_PATH = "apps/web/lib/nav-config.ts";
|
|
|
708
749
|
* surface set for the same reason the route tree does.
|
|
709
750
|
*
|
|
710
751
|
* The base items are the Free marketing-site chrome (#5469 D5, the approved
|
|
711
|
-
* chrome diff recorded on that card):
|
|
712
|
-
* a Product flyout (
|
|
713
|
-
* and About.
|
|
714
|
-
*
|
|
715
|
-
*
|
|
716
|
-
*
|
|
717
|
-
* the nav still never links a page the
|
|
752
|
+
* chrome diff recorded on that card, re-pointed by #6658 D8):
|
|
753
|
+
* a Product flyout (Use cases · Features · FAQ), then Use cases, Blog, Pricing
|
|
754
|
+
* and About. Features links the product's own page (`/software/product`), a
|
|
755
|
+
* core route like `/faq` and `/about`; `/blog` is always linked (the teaser or
|
|
756
|
+
* the docs-blog module serves it). Use cases and Pricing are gateable
|
|
757
|
+
* surfaces: each item is emitted only when its surface is enabled (a template
|
|
758
|
+
* roster enables it, #5469 D10), so the nav still never links a page the
|
|
759
|
+
* scaffold does not carry.
|
|
718
760
|
*
|
|
719
761
|
* Only gateable surfaces carrying a `navLabelKey` are linked after the base
|
|
720
762
|
* items, because a nav entry needs a translated label. Emitting an entry whose
|
|
721
763
|
* `labelKey` has no translation would trade a dead link for a missing-message
|
|
722
764
|
* error.
|
|
723
765
|
*
|
|
766
|
+
* The same file carries `footerColumns`, the footer's column groups (#6468):
|
|
767
|
+
* label keys and paths only, under the same gates, so the footer links every
|
|
768
|
+
* seed page the header does not.
|
|
769
|
+
*
|
|
724
770
|
* Deterministic: gateable entries follow GATEABLE_SURFACES order (alphabetical
|
|
725
771
|
* by id), after the fixed base items.
|
|
726
772
|
*/
|
|
727
773
|
export function renderNavConfig(enabled) {
|
|
728
774
|
const on = new Set(enabled);
|
|
729
|
-
|
|
775
|
+
// `blog` is linked unconditionally below (operator ruling 2026-09-23), so
|
|
776
|
+
// it is excluded here; `use-cases` is the Free roster's own page (a
|
|
777
|
+
// module-less surface the roster enables, like `pricing`), linked in its
|
|
778
|
+
// fixed positions below rather than appended.
|
|
779
|
+
const linked = GATEABLE_SURFACES.filter((s) => s.navLabelKey !== undefined &&
|
|
780
|
+
on.has(s.id) &&
|
|
781
|
+
s.id !== "blog" &&
|
|
782
|
+
s.id !== "use-cases");
|
|
783
|
+
const useCases = on.has("use-cases");
|
|
730
784
|
const entries = [
|
|
731
785
|
" {",
|
|
732
786
|
' labelKey: "product",',
|
|
733
787
|
" flyout: {",
|
|
734
|
-
|
|
735
|
-
|
|
788
|
+
// SOFT_LOCK_OK: #6508 — width "panel" (D4/D5): a fixed 640px, two-column
|
|
789
|
+
// grid, replacing the "list"/"trigger" one-column menu. `presentation` is
|
|
790
|
+
// omitted — it defaults to "grid" (D5). The `docs` action is dropped (it
|
|
791
|
+
// duplicated the first feature's /how-it-works destination, § 1.4).
|
|
792
|
+
' width: "panel",',
|
|
736
793
|
" features: [",
|
|
737
|
-
|
|
738
|
-
|
|
794
|
+
...(useCases
|
|
795
|
+
? [' { labelKey: "useCases", descKey: "useCasesDesc", icon: Layers, path: "/use-cases" },']
|
|
796
|
+
: []),
|
|
797
|
+
' { labelKey: "features", descKey: "featuresDesc", icon: Sparkles, path: "/software/product" },',
|
|
739
798
|
' { labelKey: "faq", descKey: "faqDesc", icon: HelpCircle, path: "/faq" },',
|
|
740
799
|
" ],",
|
|
741
|
-
|
|
800
|
+
" actions: [],",
|
|
742
801
|
" },",
|
|
743
802
|
" },",
|
|
744
|
-
|
|
803
|
+
// Operator ruling 2026-09-23: Product · Use cases · Blog · Pricing · About.
|
|
804
|
+
// `/blog` is always linked — the `blog` surface serves the real route, and
|
|
805
|
+
// with it off the `[slug]` route serves the overlay's module page.
|
|
806
|
+
...(useCases ? [' { labelKey: "useCases", path: "/use-cases" },'] : []),
|
|
807
|
+
' { labelKey: "blog", path: "/blog" },',
|
|
745
808
|
...(on.has("pricing") ? [' { labelKey: "pricing", path: "/pricing" },'] : []),
|
|
746
809
|
' { labelKey: "about", path: "/about" },',
|
|
747
810
|
...linked.map((s) => ` { labelKey: "${s.navLabelKey}", path: "/${s.id}" },`),
|
|
748
811
|
];
|
|
812
|
+
// The footer's column groups (operator ruling 2026-09-23, replacing #6468's
|
|
813
|
+
// Product/Company pair): Site pages, Features, Resources. Pricing rides the
|
|
814
|
+
// same `pricing` gate as the header item; `/blog` is always linked (above).
|
|
815
|
+
// The product links point at the product's own page, `/software/product`
|
|
816
|
+
// (#6658 D3): `software` is core, so they stay unconditional. Book a call,
|
|
817
|
+
// Glossary and Newsletter (#6658 D8) each ride their own surface's gate.
|
|
818
|
+
const footerEntries = [
|
|
819
|
+
" {",
|
|
820
|
+
' headingKey: "footerSitePagesHeading",',
|
|
821
|
+
" items: [",
|
|
822
|
+
' { labelKey: "features", path: "/software/product" },',
|
|
823
|
+
...(on.has("pricing")
|
|
824
|
+
? [' { labelKey: "pricing", path: "/pricing" },']
|
|
825
|
+
: []),
|
|
826
|
+
...(useCases ? [' { labelKey: "useCases", path: "/use-cases" },'] : []),
|
|
827
|
+
' { labelKey: "about", path: "/about" },',
|
|
828
|
+
' { labelKey: "contact", path: "/contact" },',
|
|
829
|
+
...(on.has("book-a-call")
|
|
830
|
+
? [' { labelKey: "bookACall", path: "/book-a-call" },']
|
|
831
|
+
: []),
|
|
832
|
+
" ],",
|
|
833
|
+
" },",
|
|
834
|
+
" {",
|
|
835
|
+
' headingKey: "footerFeaturesHeading",',
|
|
836
|
+
" items: [",
|
|
837
|
+
' { labelKey: "feature1", path: "/software/product" },',
|
|
838
|
+
' { labelKey: "feature2", path: "/software/product" },',
|
|
839
|
+
' { labelKey: "feature3", path: "/software/product" },',
|
|
840
|
+
' { labelKey: "feature4", path: "/software/product" },',
|
|
841
|
+
' { labelKey: "feature5", path: "/software/product" },',
|
|
842
|
+
' { labelKey: "allFeatures", path: "/software/product" },',
|
|
843
|
+
" ],",
|
|
844
|
+
" },",
|
|
845
|
+
" {",
|
|
846
|
+
' headingKey: "footerResourcesHeading",',
|
|
847
|
+
" items: [",
|
|
848
|
+
' { labelKey: "blog", path: "/blog" },',
|
|
849
|
+
...(on.has("glossary")
|
|
850
|
+
? [' { labelKey: "glossary", path: "/glossary" },']
|
|
851
|
+
: []),
|
|
852
|
+
...(on.has("newsletter")
|
|
853
|
+
? [' { labelKey: "newsletter", path: "/newsletter" },']
|
|
854
|
+
: []),
|
|
855
|
+
' { labelKey: "faq", path: "/faq" },',
|
|
856
|
+
" ],",
|
|
857
|
+
" },",
|
|
858
|
+
];
|
|
749
859
|
// The emitted file imports the consumer's `NavItem` type from the UI package.
|
|
750
860
|
// Assembled from fragments so THIS source file never contains the literal
|
|
751
861
|
// import string: `tools/lint-architecture.ts` matches on source text, so a
|
|
@@ -763,15 +873,26 @@ export function renderNavConfig(enabled) {
|
|
|
763
873
|
// points at a route this site actually carries — the nav is regenerated with
|
|
764
874
|
// the surface set so it can never link a page that was not scaffolded.
|
|
765
875
|
//
|
|
766
|
-
// Adding a link: scaffold the page (the \`
|
|
876
|
+
// Adding a link: scaffold the page (the \`add-page\` skill), then add an
|
|
767
877
|
// entry below with a \`labelKey\` that exists in the \`nav\` namespace of
|
|
768
878
|
// \`apps/web/messages/<locale>.json\`. Paths are locale-agnostic — Nav prefixes
|
|
769
879
|
// them with the active locale at render.
|
|
770
880
|
${navItemImport}
|
|
771
|
-
import {
|
|
881
|
+
import { HelpCircle, Layers, Sparkles } from "lucide-react";
|
|
772
882
|
|
|
773
883
|
export const navItems: NavItem[] = [
|
|
774
884
|
${entries.join("\n")}
|
|
775
885
|
];
|
|
886
|
+
|
|
887
|
+
// Footer column groups. Keys only — the layout resolves each \`headingKey\`
|
|
888
|
+
// and \`labelKey\` through the same \`nav\` labels as the header.
|
|
889
|
+
export interface FooterColumnConfig {
|
|
890
|
+
headingKey: string;
|
|
891
|
+
items: { labelKey: string; path: string }[];
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
export const footerColumns: FooterColumnConfig[] = [
|
|
895
|
+
${footerEntries.join("\n")}
|
|
896
|
+
];
|
|
776
897
|
`;
|
|
777
898
|
}
|
package/dist/sync-routes.d.ts
CHANGED
|
@@ -15,6 +15,15 @@ export interface SyncRoutesOptions {
|
|
|
15
15
|
versions: WorkspaceVersionMap;
|
|
16
16
|
/** Copy the missing routes into the target. Default false = check only. */
|
|
17
17
|
apply?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Surface ids to treat as carried whether or not any of their route dirs
|
|
20
|
+
* exists yet (#6653 D8) — `add-module` passes the added module's
|
|
21
|
+
* `MODULE_SURFACES`, since a surface whose routes ship from `templates/`
|
|
22
|
+
* would otherwise read as not-carried on first add and never be copied.
|
|
23
|
+
* Undefined for every other caller: the carried set is then exactly the
|
|
24
|
+
* presence-derived one.
|
|
25
|
+
*/
|
|
26
|
+
forceSurfaces?: readonly string[];
|
|
18
27
|
}
|
|
19
28
|
/** One route directory the report names. */
|
|
20
29
|
export interface RouteReport {
|
package/dist/sync-routes.js
CHANGED
|
@@ -141,10 +141,10 @@ export function syncRoutes(opts) {
|
|
|
141
141
|
// unrelated "projects" CONTENT surface is separately requested. Without
|
|
142
142
|
// this exclusion, a signal-collectors-only scaffold would falsely read as
|
|
143
143
|
// "carries projects" the moment its bundle lands that one file.
|
|
144
|
-
const carried = new Set();
|
|
144
|
+
const carried = new Set(opts.forceSurfaces ?? []);
|
|
145
145
|
const skippedSurfaces = [];
|
|
146
146
|
for (const surface of GATEABLE_SURFACES) {
|
|
147
|
-
const present = surface.routes.some((r) => {
|
|
147
|
+
const present = carried.has(surface.id) || surface.routes.some((r) => {
|
|
148
148
|
const p = join(targetAbs, r);
|
|
149
149
|
if (!existsSync(p) || !statSync(p).isDirectory())
|
|
150
150
|
return false;
|
|
@@ -18,6 +18,33 @@ import type { CategorySlug } from "@working-theory/schema/categories";
|
|
|
18
18
|
import { type PageRosterRow } from "./module-architecture.js";
|
|
19
19
|
import type { ModuleName } from "./parse-args.js";
|
|
20
20
|
import type { TemplateLayout } from "./site-chrome.js";
|
|
21
|
+
/**
|
|
22
|
+
* The closed **shape** enum (starter-template contract § The three axes,
|
|
23
|
+
* operator-disposed 2026-09-24) — `<primary node>-<offering node>` in
|
|
24
|
+
* schema.org terms, kebab-cased. Declared beside `TemplateManifest`, the same
|
|
25
|
+
* pattern `ModuleName` uses in `parse-args.ts`: a `readonly [...] as const`
|
|
26
|
+
* array plus its derived literal-union type, so an unrecognized value fails
|
|
27
|
+
* type-check rather than a runtime validation.
|
|
28
|
+
*/
|
|
29
|
+
export declare const STARTER_SHAPES: readonly ["organization-product", "organization-service", "local-business-service", "local-business-product", "person-service"];
|
|
30
|
+
export type StarterShape = (typeof STARTER_SHAPES)[number];
|
|
31
|
+
/**
|
|
32
|
+
* The closed **design** enum (same contract section) — the look applied to
|
|
33
|
+
* any shape; orthogonal to it by construction.
|
|
34
|
+
*/
|
|
35
|
+
export declare const STARTER_DESIGNS: readonly ["standard", "minimalism"];
|
|
36
|
+
export type StarterDesign = (typeof STARTER_DESIGNS)[number];
|
|
37
|
+
/**
|
|
38
|
+
* Each design's layout knobs (#6638) — the part of a design that is chrome,
|
|
39
|
+
* so a template names `design:` instead of re-typing a `layout` block, and any
|
|
40
|
+
* shape can wear any design. `resolveSiteChrome(layout, design)` reads it:
|
|
41
|
+
* an explicit `TemplateManifest.layout` wins field by field, then this, then
|
|
42
|
+
* `DEFAULT_SITE_CHROME`. `standard` declares nothing — it IS the defaults, so
|
|
43
|
+
* spelling them out here would be a second copy of them to drift. A plain
|
|
44
|
+
* object, not a file: this module touches no disk. The design's documented
|
|
45
|
+
* look lives in `site-templates/designs/<design>/DESIGN.md`.
|
|
46
|
+
*/
|
|
47
|
+
export declare const DESIGN_LAYOUTS: Readonly<Record<StarterDesign, TemplateLayout>>;
|
|
21
48
|
/**
|
|
22
49
|
* One site template: its fixed module list and the page rows it brings itself.
|
|
23
50
|
*
|
|
@@ -57,6 +84,20 @@ export interface TemplateManifest {
|
|
|
57
84
|
* registry — never a group here.
|
|
58
85
|
*/
|
|
59
86
|
categories: readonly CategorySlug[];
|
|
87
|
+
/**
|
|
88
|
+
* The shape this template's roster renders (starter-template contract § The
|
|
89
|
+
* three axes) — `<primary node>-<offering node>`, from the closed
|
|
90
|
+
* {@link STARTER_SHAPES} enum. Owns the entity preset, the roster and each
|
|
91
|
+
* page's section stack; the machine truth the showcase, the build-starter
|
|
92
|
+
* skill and the render gate read — never the directory name.
|
|
93
|
+
*/
|
|
94
|
+
shape: StarterShape;
|
|
95
|
+
/**
|
|
96
|
+
* The design this template renders (same contract section) — the look,
|
|
97
|
+
* orthogonal to `shape` by construction: any design applies to any shape.
|
|
98
|
+
* From the closed {@link STARTER_DESIGNS} enum.
|
|
99
|
+
*/
|
|
100
|
+
design: StarterDesign;
|
|
60
101
|
/**
|
|
61
102
|
* The template this one EXTENDS — its lineage, never its level (#5618 D2).
|
|
62
103
|
* A single parent, not a list: "later layers win file by file" needs a
|
|
@@ -70,7 +111,7 @@ export interface TemplateManifest {
|
|
|
70
111
|
*/
|
|
71
112
|
extends: string;
|
|
72
113
|
/** Base rows, before any module's contribution is merged. */
|
|
73
|
-
roster: readonly
|
|
114
|
+
roster: readonly TemplateRosterRow[];
|
|
74
115
|
/** Bundle-relative source → scaffold-relative destination. */
|
|
75
116
|
installMap?: Record<string, string>;
|
|
76
117
|
/**
|
|
@@ -94,6 +135,21 @@ export interface TemplateManifest {
|
|
|
94
135
|
* the directory-scanning tests key their skip on.
|
|
95
136
|
*/
|
|
96
137
|
export declare const BASE_LAYER_DIR = "_base";
|
|
138
|
+
/**
|
|
139
|
+
* One row of a template's own roster — the same shape as a module's
|
|
140
|
+
* `PageRosterRow`, `contentDir` included (#6646 disposition (a)): a module's
|
|
141
|
+
* contributed detail row (`/reports/[slug]`) serves a seed directory exactly
|
|
142
|
+
* as a template's own does. Where `routes-map.ts` records the route's
|
|
143
|
+
* `contentPath`, the generator refuses a `contentDir` that disagrees.
|
|
144
|
+
*/
|
|
145
|
+
export type TemplateRosterRow = PageRosterRow;
|
|
146
|
+
/**
|
|
147
|
+
* Whether a merged roster declares one scaffold-relative content file
|
|
148
|
+
* (#6646 D1): named by a row's `content`, or under a row's `contentDir`.
|
|
149
|
+
* The one predicate the scaffold's per-row install and the manifest
|
|
150
|
+
* generator's install-diff both read, so the two cannot disagree.
|
|
151
|
+
*/
|
|
152
|
+
export declare function rosterDeclares(roster: readonly TemplateRosterRow[], path: string): boolean;
|
|
97
153
|
/** Thrown on an undeclared template name or a route claimed twice. Mirrors `SurfaceError`'s shape. */
|
|
98
154
|
export declare class TemplateError extends Error {
|
|
99
155
|
constructor(message: string);
|
|
@@ -161,7 +217,7 @@ export declare function resolveTemplateLineage(name: string, registry?: Readonly
|
|
|
161
217
|
* runtime by a base-wins or module-wins precedence rule, either of which
|
|
162
218
|
* silently produces a site missing a page someone declared.
|
|
163
219
|
*/
|
|
164
|
-
export declare function mergeRoster(manifest: TemplateManifest, modules: readonly string[]):
|
|
220
|
+
export declare function mergeRoster(manifest: TemplateManifest, modules: readonly string[]): TemplateRosterRow[];
|
|
165
221
|
/**
|
|
166
222
|
* Whether a template installs on the keyless path or needs a verified
|
|
167
223
|
* `AILK_LICENSE_KEY` — derived from `requires`, never declared (parent § D3).
|