create-ailk 0.7.0 → 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 +44 -10
- package/component-catalog.json +520 -5
- package/component-catalog.md +9 -6
- package/dist/cli.js +22 -24
- package/dist/lib/extract-module-bundle.js +5 -1
- 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.js +10 -2
- package/dist/parse-args.d.ts +78 -10
- package/dist/parse-args.js +204 -14
- package/dist/programmatic.d.ts +39 -9
- package/dist/programmatic.js +163 -25
- package/dist/site-roster.js +11 -6
- package/dist/starter-grid.d.ts +216 -0
- package/dist/starter-grid.js +449 -0
- package/dist/surfaces.d.ts +4 -4
- package/dist/surfaces.js +102 -43
- package/dist/template-manifest.d.ts +36 -17
- package/dist/template-manifest.js +121 -367
- package/dist/template-names.d.ts +14 -12
- package/dist/template-names.js +13 -17
- package/package.json +3 -3
- package/site-templates/_base/content/en/BookCall/book-call.mdx +0 -1
- 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 +0 -1
- package/site-templates/_base/content/en/NewsletterSignup/newsletter.mdx +0 -1
- 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/PrivacyPage/privacy.mdx +15 -0
- 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/standard/DESIGN.md +5 -0
- package/site-templates/marketing-site/DESIGN.md +5 -0
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +89 -25
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +35 -14
- package/site-templates/marketing-site-free/DESIGN.md +5 -0
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +95 -29
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +74 -44
- 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/TEMPLATE-MANIFEST.md +17 -29
- package/site-templates/minimalism-site/content/en/BookCall/book-call.mdx +0 -1
- package/site-templates/minimalism-site/content/en/PurchasePolicyPage/purchase-policy.mdx +0 -1
- package/site-templates/saas-product/DESIGN.md +5 -0
- package/site-templates/saas-product/TEMPLATE-MANIFEST.md +9 -1
- package/site-templates/{simple-service → shapes/local-business-service}/TEMPLATE-MANIFEST.json +96 -76
- 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/{simple-service → shapes/local-business-service}/content/en/PurchasePolicyPage/purchase-policy.mdx +7 -8
- 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/skills/README.md +3 -2
- package/templates/.claude/skills/add-page/references/page-types.md +4 -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/setup-deploy/SKILL.md +64 -32
- package/templates/.claude/skills/setup-deploy/references/env-vars-template.md +19 -28
- package/templates/.claude/skills/setup-deploy/references/runbook-template.md +58 -102
- package/templates/DESIGN.md +5 -0
- package/templates/README.md +46 -38
- package/templates/apps/api/CLAUDE.md +3 -2
- package/templates/apps/api/src/config/__tests__/index.test.ts +115 -14
- package/templates/apps/api/src/config/index.ts +35 -16
- 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/openapi/spec.ts +63 -12
- 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 +7 -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__/parity.test.ts +5 -4
- package/templates/apps/mcp/src/tools/create_project.ts +21 -6
- package/templates/apps/web/app/[locale]/contact/ConversationalIntakeWrapper.tsx +14 -6
- package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +10 -9
- 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 +227 -84
- 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 +105 -15
- package/templates/apps/web/app/[locale]/contact/site-contact-email.ts +54 -0
- 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]/layout.tsx +37 -5
- 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 +106 -14
- 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]/use-cases/[slug]/page.tsx +18 -3
- package/templates/apps/web/lib/nav-config.ts +48 -36
- package/templates/apps/web/lib/site-identity.ts +14 -0
- package/templates/apps/web/messages/en.json +28 -2
- package/templates/content/_site.mdx +4 -2
- package/templates/content/en/BlogList/blog.mdx +37 -16
- package/templates/content/en/ContactPage/contact.mdx +14 -44
- package/templates/content/en/HomePage/home.mdx +8 -16
- package/templates/content/en/PurchasePolicyPage/purchase-policy.mdx +0 -1
- package/templates/content/en/UseCasesList/use-cases.mdx +6 -6
- package/templates/database/CHANGELOG.md +7 -0
- package/templates/database/package.json +1 -1
- package/versions.json +33 -33
- package/site-templates/_base/content/en/UseCase/first-use-case.mdx +0 -23
- package/site-templates/_base/content/en/UseCase/second-use-case.mdx +0 -23
- package/site-templates/_base/content/en/UseCase/third-use-case.mdx +0 -23
- package/site-templates/simple-service/DESIGN.md +0 -162
- package/site-templates/simple-service/TEMPLATE-MANIFEST.md +0 -333
- package/site-templates/simple-service/ailk-template.json +0 -11
- package/site-templates/simple-service/content/en/BookCall/book-call.mdx +0 -53
- package/site-templates/simple-service/content/en/ContactPage/contact.mdx +0 -32
- 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/apps/web/app/[locale]/contact/ContactForm.tsx +0 -211
- package/templates/apps/web/app/[locale]/contact/__tests__/ContactForm.test.tsx +0 -96
- package/templates/content/en/SoftwareProduct/product.mdx +0 -175
package/dist/programmatic.js
CHANGED
|
@@ -25,13 +25,14 @@ import { deriveArchitecture } from "./derive-architecture.js";
|
|
|
25
25
|
import { extractModuleBundle, installBundleTree, } from "./lib/extract-module-bundle.js";
|
|
26
26
|
import { fetchTemplate } from "./lib/fetch-template.js";
|
|
27
27
|
import { assertLicenseKeyShape, writeLicenseKeyFiles, } from "./lib/license-key-files.js";
|
|
28
|
-
import { resolvePaidModules } from "./lib/module-license-gate.js";
|
|
28
|
+
import { FREE_DESIGNS, assertDesignAllowed, deriveLevel, resolveModuleSet, resolvePaidModules, } from "./lib/module-license-gate.js";
|
|
29
29
|
import { classifyModules } from "./lib/module-tier.js";
|
|
30
30
|
import { normalizeModules } from "./module-architecture.js";
|
|
31
|
+
import { RECOGNIZED_SHAPES, RESERVED_SHAPES, } from "./parse-args.js";
|
|
31
32
|
import { SITE_CHROME_PATH, renderSiteChrome, resolveSiteChrome, } from "./site-chrome.js";
|
|
32
33
|
import { GATEABLE_SURFACES, MODULE_SURFACES, NAV_CONFIG_PATH, excludedRoutePaths, renderNavConfig, resolveSurfaces, } from "./surfaces.js";
|
|
33
34
|
import { syncRoutes } from "./sync-routes.js";
|
|
34
|
-
import { BASE_LAYER_DIR, STARTER_DESIGNS, TemplateError, mergeRoster, resolveTemplate, resolveTemplateLineage, rosterDeclares, templateDelivery, } from "./template-manifest.js";
|
|
35
|
+
import { BASE_LAYER_DIR, SHAPE_STARTERS, STARTER_DESIGNS, TEMPLATE_MANIFESTS, TemplateError, mergeRoster, resolveTemplate, resolveTemplateLineage, rosterDeclares, templateDelivery, templateTreeDir, } from "./template-manifest.js";
|
|
35
36
|
import { validateTarget } from "./validate.js";
|
|
36
37
|
const PKG_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
37
38
|
const TEMPLATES_DIR = resolve(PKG_ROOT, "templates");
|
|
@@ -75,6 +76,37 @@ function countFiles(dir) {
|
|
|
75
76
|
}
|
|
76
77
|
return count;
|
|
77
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* The template a `shape` scaffolds (#6754 D5, extended #6757 D6 row 1): the
|
|
81
|
+
* one FREE-delivery manifest of that shape — a declared `--template` alias
|
|
82
|
+
* (`TEMPLATE_MANIFESTS`) or a bare-shape starter with no alias of its own
|
|
83
|
+
* (`SHAPE_STARTERS`), the union the candidate set draws from. Reads manifest
|
|
84
|
+
* fields only — no roster.
|
|
85
|
+
*
|
|
86
|
+
* @throws TemplateError on a reserved or unknown shape, a shape with no Free
|
|
87
|
+
* starter yet (D6), or more than one (never picked silently).
|
|
88
|
+
*/
|
|
89
|
+
function resolveShapeTemplate(shape) {
|
|
90
|
+
if (!RECOGNIZED_SHAPES.includes(shape)) {
|
|
91
|
+
const known = RECOGNIZED_SHAPES.join(", ");
|
|
92
|
+
throw new TemplateError(RESERVED_SHAPES.includes(shape)
|
|
93
|
+
? `shape "${shape}" is not available yet. Choose one of: ${known}.`
|
|
94
|
+
: `unknown shape "${shape}". Recognized: ${known}`);
|
|
95
|
+
}
|
|
96
|
+
const ofShape = [
|
|
97
|
+
...Object.values(TEMPLATE_MANIFESTS),
|
|
98
|
+
...Object.values(SHAPE_STARTERS),
|
|
99
|
+
].filter((manifest) => manifest.shape === shape);
|
|
100
|
+
const free = ofShape.filter((manifest) => templateDelivery(manifest) === "free");
|
|
101
|
+
if (free.length === 0) {
|
|
102
|
+
const names = ofShape.map((manifest) => manifest.name).join(", ");
|
|
103
|
+
throw new TemplateError(`shape "${shape}" has no Free starter yet — its page list lands with #6743. Templates built on this shape: ${names || "none"}.`);
|
|
104
|
+
}
|
|
105
|
+
if (free.length > 1) {
|
|
106
|
+
throw new TemplateError(`shape "${shape}" has more than one Free starter: ${free.map((manifest) => manifest.name).join(", ")}`);
|
|
107
|
+
}
|
|
108
|
+
return free[0];
|
|
109
|
+
}
|
|
78
110
|
/**
|
|
79
111
|
* Scaffold an AI Launch Kit starter project into targetDir (synchronous).
|
|
80
112
|
*
|
|
@@ -118,7 +150,12 @@ export function scaffoldSync(opts) {
|
|
|
118
150
|
if (opts.withCatalog === true)
|
|
119
151
|
writeComponentCatalog(targetAbs);
|
|
120
152
|
const filesCreated = countFiles(targetAbs);
|
|
121
|
-
return {
|
|
153
|
+
return {
|
|
154
|
+
targetDir: targetAbs,
|
|
155
|
+
filesCreated,
|
|
156
|
+
surfaces: enabledSurfaces,
|
|
157
|
+
modules: normalizeModules(opts.modules ?? ["marketing"]),
|
|
158
|
+
};
|
|
122
159
|
}
|
|
123
160
|
/**
|
|
124
161
|
* Scaffold an AI Launch Kit starter project into targetDir (async wrapper).
|
|
@@ -138,8 +175,14 @@ export function scaffoldSync(opts) {
|
|
|
138
175
|
* its fetched bundle or — when it requires only free modules — from the
|
|
139
176
|
* `site-templates/` tree this package ships.
|
|
140
177
|
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
178
|
+
* Between 2 and 3, `assertDesignAllowed` (#6760) refuses a design the caller
|
|
179
|
+
* CHOSE (`opts.design`) for a level below Pro, HERE, also before anything is
|
|
180
|
+
* written to disk. A template's own declared design is never gated (#6778
|
|
181
|
+
* option (c)): the template's `requires` already decide its level.
|
|
182
|
+
*
|
|
183
|
+
* And, ahead of all five when `opts.template` (or `opts.shape`, #6754 D5) is
|
|
184
|
+
* set (0): resolve the template and union its `requires` with `opts.modules`,
|
|
185
|
+
* so every step above
|
|
143
186
|
* runs over the EFFECTIVE module set rather than the requested one.
|
|
144
187
|
*
|
|
145
188
|
* `scaffoldSync` itself stays licensing-oblivious on purpose: every existing
|
|
@@ -155,8 +198,17 @@ export async function scaffold(opts) {
|
|
|
155
198
|
const licenseKey = opts.license ?? opts.licenseKey;
|
|
156
199
|
// Resolved first, and by the registry rather than re-validated here: the
|
|
157
200
|
// name already passed `parse-args.ts`, and `resolveTemplate` throws a
|
|
158
|
-
// `TemplateError` for a programmatic caller that skipped the parser.
|
|
159
|
-
|
|
201
|
+
// `TemplateError` for a programmatic caller that skipped the parser. A
|
|
202
|
+
// `shape` resolves to its one Free starter and from there is the template
|
|
203
|
+
// path verbatim (#6754 D5); neither leaves the bare overlay untouched.
|
|
204
|
+
if (opts.template !== undefined && opts.shape !== undefined) {
|
|
205
|
+
throw new TemplateError("pass a template or a shape, not both");
|
|
206
|
+
}
|
|
207
|
+
const template = opts.template !== undefined
|
|
208
|
+
? resolveTemplate(opts.template)
|
|
209
|
+
: opts.shape !== undefined
|
|
210
|
+
? resolveShapeTemplate(opts.shape)
|
|
211
|
+
: undefined;
|
|
160
212
|
// #6638 — the design this scaffold renders: the caller's override, else the
|
|
161
213
|
// template's own. Unset for a scaffold naming neither.
|
|
162
214
|
const design = opts.design ?? template?.design;
|
|
@@ -165,29 +217,108 @@ export async function scaffold(opts) {
|
|
|
165
217
|
}
|
|
166
218
|
// #5618 D2 — the `extends` chain is resolved in the client registry before
|
|
167
219
|
// any fetch: an unknown parent or a cycle is refused here, with the target
|
|
168
|
-
// still non-existent, like the four refusals below.
|
|
169
|
-
|
|
170
|
-
|
|
220
|
+
// still non-existent, like the four refusals below. The registry is the
|
|
221
|
+
// union of TEMPLATE_MANIFESTS and SHAPE_STARTERS (#6757 D6 row 1): a
|
|
222
|
+
// `--shape` run's resolved template may be a bare-shape starter, whose name
|
|
223
|
+
// (the shape itself) `TEMPLATE_MANIFESTS` alone does not carry.
|
|
224
|
+
if (template !== undefined) {
|
|
225
|
+
resolveTemplateLineage(template.name, {
|
|
226
|
+
...TEMPLATE_MANIFESTS,
|
|
227
|
+
...SHAPE_STARTERS,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
171
230
|
// The effective module set: union(template.requires, parsed --modules),
|
|
172
231
|
// normalized through `normalizeModules` — the single ordering authority
|
|
173
232
|
// (parent record D7). Order-independence falls out rather than being
|
|
174
233
|
// engineered: it returns `RECOGNIZED_MODULES.filter(...)`, declaration-
|
|
175
234
|
// ordered and deduped, so the same SET in any argument order is the same
|
|
176
235
|
// list.
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
236
|
+
let modules;
|
|
237
|
+
let bundles;
|
|
238
|
+
// The run's derived level (#6759), for `assertDesignAllowed` below.
|
|
239
|
+
// Undefined on the alias branch and on a `--modules`-keep run (D2/D4 in
|
|
240
|
+
// module-license-gate.ts — the level plays no part in composing either),
|
|
241
|
+
// so a non-`standard` design on either path derives it separately, below.
|
|
242
|
+
let level;
|
|
243
|
+
if (opts.template !== undefined) {
|
|
244
|
+
// Alias run — untouched (NFR-4): the union line and its
|
|
245
|
+
// resolvePaidModules({ …, templateName }) call stay exactly as they were.
|
|
246
|
+
modules = normalizeModules([
|
|
247
|
+
...(template?.requires ?? []),
|
|
248
|
+
...(opts.modules ?? ["marketing"]),
|
|
249
|
+
]);
|
|
250
|
+
const { paid } = classifyModules(modules);
|
|
251
|
+
bundles = await resolvePaidModules({
|
|
252
|
+
paidModules: paid,
|
|
253
|
+
licenseKey,
|
|
254
|
+
...(template !== undefined ? { templateName: template.name } : {}),
|
|
255
|
+
...(opts.moduleRegistryUrl !== undefined
|
|
256
|
+
? { registryUrl: opts.moduleRegistryUrl }
|
|
257
|
+
: {}),
|
|
258
|
+
...(opts.fetchImpl !== undefined ? { fetchImpl: opts.fetchImpl } : {}),
|
|
259
|
+
});
|
|
260
|
+
// #6760 — `resolveModuleSet` never runs on this branch (NFR-4), so no
|
|
261
|
+
// level was derived above. Derive one — from the registry's per-module
|
|
262
|
+
// answers, never a key claim (D2) — only when the caller CHOSE a
|
|
263
|
+
// non-`standard` design alongside the template (`opts.design`). An
|
|
264
|
+
// alias's own declared design is never gated (#6778 option (c)), so a
|
|
265
|
+
// plain `--template minimalism-site` run makes no extra network call and
|
|
266
|
+
// is never refused on its design.
|
|
267
|
+
if (opts.design !== undefined && !FREE_DESIGNS.includes(opts.design)) {
|
|
268
|
+
({ level } = await deriveLevel({
|
|
269
|
+
licenseKey,
|
|
270
|
+
...(opts.moduleRegistryUrl !== undefined
|
|
271
|
+
? { registryUrl: opts.moduleRegistryUrl }
|
|
272
|
+
: {}),
|
|
273
|
+
...(opts.fetchImpl !== undefined ? { fetchImpl: opts.fetchImpl } : {}),
|
|
274
|
+
}));
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
// Shape and bare runs — the key's level decides (#6759 D6), never a flag,
|
|
279
|
+
// a manifest or a key claim.
|
|
280
|
+
const chosen = await resolveModuleSet({
|
|
281
|
+
named: opts.modules,
|
|
282
|
+
licenseKey,
|
|
283
|
+
...(opts.moduleRegistryUrl !== undefined
|
|
284
|
+
? { registryUrl: opts.moduleRegistryUrl }
|
|
285
|
+
: {}),
|
|
286
|
+
...(opts.fetchImpl !== undefined ? { fetchImpl: opts.fetchImpl } : {}),
|
|
287
|
+
});
|
|
288
|
+
bundles = chosen.bundles;
|
|
289
|
+
level = chosen.level;
|
|
290
|
+
modules =
|
|
291
|
+
template !== undefined
|
|
292
|
+
? normalizeModules([...template.requires, ...chosen.modules])
|
|
293
|
+
: chosen.modules;
|
|
294
|
+
// A paid member of the shape's own starter `requires` not already
|
|
295
|
+
// fetched during the level probe (D6) — every buildable shape's starter
|
|
296
|
+
// requires only `marketing` today, so this is presently a no-op branch.
|
|
297
|
+
if (template !== undefined) {
|
|
298
|
+
const templatePaid = classifyModules(template.requires).paid;
|
|
299
|
+
const stillMissing = templatePaid.filter((m) => !bundles.has(m));
|
|
300
|
+
if (stillMissing.length > 0) {
|
|
301
|
+
const extra = await resolvePaidModules({
|
|
302
|
+
paidModules: stillMissing,
|
|
303
|
+
licenseKey,
|
|
304
|
+
...(opts.moduleRegistryUrl !== undefined
|
|
305
|
+
? { registryUrl: opts.moduleRegistryUrl }
|
|
306
|
+
: {}),
|
|
307
|
+
...(opts.fetchImpl !== undefined
|
|
308
|
+
? { fetchImpl: opts.fetchImpl }
|
|
309
|
+
: {}),
|
|
310
|
+
});
|
|
311
|
+
for (const [k, v] of extra)
|
|
312
|
+
bundles.set(k, v);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
// #6760 D4 — after the level is resolved, before any fetch or write below.
|
|
317
|
+
// Only the caller's chosen design is gated (#6778 option (c)); a template's
|
|
318
|
+
// own declared design is not. `level ?? "free"` fails closed: a
|
|
319
|
+
// `--modules`-keep run under-reports its level by design (NFR-2), so a
|
|
320
|
+
// chosen non-`standard` design on that path is refused rather than trusted.
|
|
321
|
+
assertDesignAllowed(opts.design, level ?? "free");
|
|
191
322
|
// The template's OWN bundle, fetched here — before `scaffoldSync` — so the
|
|
192
323
|
// fourth refusal (the template's bundle unpublished, parent record D17 /
|
|
193
324
|
// spec 5481 § 3 DD5 row 4) throws with the target still non-existent, like
|
|
@@ -326,7 +457,7 @@ export async function scaffold(opts) {
|
|
|
326
457
|
ownership,
|
|
327
458
|
})
|
|
328
459
|
: (() => {
|
|
329
|
-
const sourceRoot = join(SITE_TEMPLATES_DIR, template.name);
|
|
460
|
+
const sourceRoot = join(SITE_TEMPLATES_DIR, templateTreeDir(template.name));
|
|
330
461
|
if (!existsSync(sourceRoot)) {
|
|
331
462
|
throw new Error(`template "${template.name}": ${sourceRoot} is missing — the published package must carry site-templates/ in its "files" list`);
|
|
332
463
|
}
|
|
@@ -351,6 +482,13 @@ export async function scaffold(opts) {
|
|
|
351
482
|
if (opts.license !== undefined) {
|
|
352
483
|
writeLicenseKeyFiles(result.targetDir, opts.license);
|
|
353
484
|
}
|
|
485
|
+
// #6754 D4/D5 — the resolved starter, so the CLI names what it built.
|
|
486
|
+
if (template !== undefined && design !== undefined) {
|
|
487
|
+
return {
|
|
488
|
+
...result,
|
|
489
|
+
starter: { template: template.name, shape: template.shape, design },
|
|
490
|
+
};
|
|
491
|
+
}
|
|
354
492
|
return result;
|
|
355
493
|
}
|
|
356
494
|
/**
|
package/dist/site-roster.js
CHANGED
|
@@ -30,7 +30,7 @@ import { AddModuleRefusedError, readArchitectureHeader, } from "./add-module-cor
|
|
|
30
30
|
import { diffTwoStrings } from "./lib/extract-module-bundle.js";
|
|
31
31
|
import { MODULE_SLICES, normalizeModules, } from "./module-architecture.js";
|
|
32
32
|
import { substituteMarkers } from "./substitute.js";
|
|
33
|
-
import { BASE_LAYER_DIR, TEMPLATE_MANIFESTS, resolveTemplate, rosterDeclares, } from "./template-manifest.js";
|
|
33
|
+
import { BASE_LAYER_DIR, SHAPE_STARTERS, TEMPLATE_MANIFESTS, resolveTemplate, rosterDeclares, templateTreeDir, } from "./template-manifest.js";
|
|
34
34
|
/** The site-root file that is the roster of record (#6658 D7). */
|
|
35
35
|
export const SITE_ROSTER_PATH = ".ailk-site-manifest.json";
|
|
36
36
|
/**
|
|
@@ -168,10 +168,15 @@ export function readSiteRoster(targetDir) {
|
|
|
168
168
|
if (path === undefined)
|
|
169
169
|
return undefined;
|
|
170
170
|
const file = parseRosterFile(readFileSync(join(targetDir, path), "utf8"), path);
|
|
171
|
-
const name = file.doc["template"];
|
|
171
|
+
const name = String(file.doc["template"]);
|
|
172
172
|
let template;
|
|
173
173
|
try {
|
|
174
|
-
|
|
174
|
+
// #6757 D6 — a bare-shape starter's name (the shape itself) resolves
|
|
175
|
+
// through SHAPE_STARTERS first; `resolveTemplate` alone never sees one
|
|
176
|
+
// (NFR-3).
|
|
177
|
+
template = Object.prototype.hasOwnProperty.call(SHAPE_STARTERS, name)
|
|
178
|
+
? SHAPE_STARTERS[name]
|
|
179
|
+
: resolveTemplate(name);
|
|
175
180
|
}
|
|
176
181
|
catch (err) {
|
|
177
182
|
throw new AddModuleRefusedError(`${path} names no declared template (${err.message}), so the page roster is unknown`);
|
|
@@ -279,7 +284,7 @@ function expectedRow(ref, targetDir) {
|
|
|
279
284
|
/** The bytes a scaffold installed at `path` from the template, base or overlay layer. */
|
|
280
285
|
function installedSeed(opts, path) {
|
|
281
286
|
const layers = [
|
|
282
|
-
join(opts.siteTemplatesDir, opts.roster.template.name),
|
|
287
|
+
join(opts.siteTemplatesDir, templateTreeDir(opts.roster.template.name)),
|
|
283
288
|
join(opts.siteTemplatesDir, BASE_LAYER_DIR),
|
|
284
289
|
opts.templatesDir,
|
|
285
290
|
];
|
|
@@ -308,7 +313,7 @@ export function planRosterAppend(opts) {
|
|
|
308
313
|
const inSet = new Set(opts.modules);
|
|
309
314
|
const refs = opts.references.filter((ref) => inSet.has(ref.module));
|
|
310
315
|
const contributed = new Set(refs.map((ref) => ref.contribution.route));
|
|
311
|
-
const shipped = readShippedManifest(opts.siteTemplatesDir, roster.template.name);
|
|
316
|
+
const shipped = readShippedManifest(opts.siteTemplatesDir, templateTreeDir(roster.template.name));
|
|
312
317
|
const templateRows = [];
|
|
313
318
|
const moduleRows = new Map();
|
|
314
319
|
const superseded = [];
|
|
@@ -393,7 +398,7 @@ function layerInstalledSeeds(template, modules, opts) {
|
|
|
393
398
|
}
|
|
394
399
|
// The template's own tree, as the generator's `ownedContent` reads it: an
|
|
395
400
|
// `installMap` source lands at its mapped destination instead.
|
|
396
|
-
const ownRoot = join(opts.siteTemplatesDir, template.name);
|
|
401
|
+
const ownRoot = join(opts.siteTemplatesDir, templateTreeDir(template.name));
|
|
397
402
|
const ownManifest = join(ownRoot, "ailk-template.json");
|
|
398
403
|
const ownMap = existsSync(ownManifest)
|
|
399
404
|
? (JSON.parse(readFileSync(ownManifest, "utf8")).installMap ?? {})
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The starter grid's data (#6753): the shape rosters and the alias table
|
|
3
|
+
* every `--template` name resolves through. A starter is one shape rendered
|
|
4
|
+
* in one design with a module list (starter-template contract § The three
|
|
5
|
+
* axes); an alias names those coordinates plus how its own rows differ from
|
|
6
|
+
* the shape's standard roster.
|
|
7
|
+
*
|
|
8
|
+
* DATA ONLY, AND IMPORT-FREE AT RUNTIME. Every import below is `import type`,
|
|
9
|
+
* erased at emit. `template-names.ts` derives `RECOGNIZED_TEMPLATES` from
|
|
10
|
+
* `ALIASES` and `parse-args.ts` imports that at runtime, so a runtime import
|
|
11
|
+
* here could reach `lib/module-tier.ts` and re-open the `RECOGNIZED_MODULES`
|
|
12
|
+
* temporal-dead-zone cycle `template-names.ts`'s header records (spec 6753
|
|
13
|
+
* D1). The composition itself — shape roster, minus `drop`, plus `add` —
|
|
14
|
+
* lives in `template-manifest.ts`, whose `resolveTemplate` is the one reader
|
|
15
|
+
* that yields a template's roster. Nothing but those two files imports this one.
|
|
16
|
+
*/
|
|
17
|
+
import type { CategorySlug } from "@working-theory/schema/categories";
|
|
18
|
+
import type { ModuleName } from "./parse-args.js";
|
|
19
|
+
import type { TemplateLayout } from "./site-chrome.js";
|
|
20
|
+
import type { StarterDesign, StarterShape, TemplateRosterRow } from "./template-manifest.js";
|
|
21
|
+
/** The shapes a starter can be built from — every StarterShape but the reserved one. */
|
|
22
|
+
export type BuildableShape = Exclude<StarterShape, "local-business-product">;
|
|
23
|
+
/** How an alias's own rows differ from its shape's standard roster. */
|
|
24
|
+
export interface AliasDifferences {
|
|
25
|
+
/** Shape-roster routes this alias does not carry. */
|
|
26
|
+
readonly drop?: readonly string[];
|
|
27
|
+
/** Rows this alias carries that its shape roster does not. */
|
|
28
|
+
readonly add?: readonly TemplateRosterRow[];
|
|
29
|
+
/** The retained shape route `add` is inserted before; absent → appended. */
|
|
30
|
+
readonly before?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Further positioned groups (#6757 D7): each group's rows go before its
|
|
33
|
+
* own `before` route, applied group by group in array order, AFTER `add`
|
|
34
|
+
* is placed. The one alias that needs more than one anchor
|
|
35
|
+
* (`minimalism-site`, which interleaves rows at two separate points in the
|
|
36
|
+
* shape's row order) uses this; every other alias leaves it unset.
|
|
37
|
+
*/
|
|
38
|
+
readonly insert?: readonly {
|
|
39
|
+
readonly before: string;
|
|
40
|
+
readonly rows: readonly TemplateRosterRow[];
|
|
41
|
+
}[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* One `--template` name: the grid coordinates it stands for.
|
|
45
|
+
*
|
|
46
|
+
* No `tier`, `level`, `price` or optional-module member, for the reason
|
|
47
|
+
* `TemplateManifest` carries none (parent § D4, D12): the level is derived
|
|
48
|
+
* from `modules`, never declared, and the type is what makes declaring one a
|
|
49
|
+
* compile error.
|
|
50
|
+
*/
|
|
51
|
+
export interface TemplateAlias {
|
|
52
|
+
readonly shape: BuildableShape;
|
|
53
|
+
readonly design: StarterDesign;
|
|
54
|
+
/** Becomes the manifest's `requires`. */
|
|
55
|
+
readonly modules: readonly ModuleName[];
|
|
56
|
+
readonly categories: readonly CategorySlug[];
|
|
57
|
+
readonly layout?: TemplateLayout;
|
|
58
|
+
readonly differences?: AliasDifferences;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Each buildable shape's standard roster (#6753 D3) — the rows an alias starts
|
|
62
|
+
* from before its `differences` apply.
|
|
63
|
+
*
|
|
64
|
+
* `organization-product` is today's base roster, the `/blog` teaser and the
|
|
65
|
+
* portfolio pair. `organization-service`, `local-business-service` (#6757)
|
|
66
|
+
* and `person-service` (#6758, roadmap § 6 Phase 2) are each their own rows,
|
|
67
|
+
* the `/blog` teaser and the portfolio pair.
|
|
68
|
+
*/
|
|
69
|
+
export declare const SHAPE_ROSTERS: Readonly<Record<BuildableShape, readonly TemplateRosterRow[]>>;
|
|
70
|
+
/**
|
|
71
|
+
* The `--template` names and the grid coordinates each stands for (#6753) —
|
|
72
|
+
* the one table `TEMPLATE_MANIFESTS` and `RECOGNIZED_TEMPLATES` derive from,
|
|
73
|
+
* in the declaration order both keep. The plain name is the Pro one: it is
|
|
74
|
+
* the flagship and the demo (Sam, 2026-09-14, parent § D12).
|
|
75
|
+
*
|
|
76
|
+
* `workspace` appears in no module list. It is a Pro module, but an authed app
|
|
77
|
+
* surface contributing no PUBLIC page to a marketing site, and `modules` names
|
|
78
|
+
* what the roster uses (parent § D15). A Pro key unlocks it regardless of any
|
|
79
|
+
* template.
|
|
80
|
+
*
|
|
81
|
+
* `commerce` is a per-site add-on available at any level, so no alias exists
|
|
82
|
+
* FOR it (parent § D13); it still contributes rows to whichever alias takes
|
|
83
|
+
* it, through `mergeRoster`.
|
|
84
|
+
*
|
|
85
|
+
* Frozen (#5507): the registry composed from this table is also the issuer's
|
|
86
|
+
* authorization allowlist (spec 5480 § C7). A test needing a different table
|
|
87
|
+
* passes its own to `resolveTemplate(name, aliases)` rather than mutating
|
|
88
|
+
* this one.
|
|
89
|
+
*/
|
|
90
|
+
export declare const ALIASES: Readonly<{
|
|
91
|
+
/**
|
|
92
|
+
* The Free template, and the mechanism's own proof (parent § D11): it
|
|
93
|
+
* requires only `marketing`, so the keyless merge path is exercised by a
|
|
94
|
+
* template we ship anyway rather than by a throwaway fixture. The bare
|
|
95
|
+
* `organization-product` shape — no differences at all.
|
|
96
|
+
*/
|
|
97
|
+
readonly "marketing-site-free": {
|
|
98
|
+
readonly shape: "organization-product";
|
|
99
|
+
readonly design: "standard";
|
|
100
|
+
readonly modules: readonly ["marketing"];
|
|
101
|
+
readonly categories: readonly ["accounting", "legal", "technology"];
|
|
102
|
+
readonly layout: {
|
|
103
|
+
readonly announcementBanner: true;
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* The Pro flagship. Its module list is OFFERINGS § 4 offering 3's tiers line
|
|
108
|
+
* — "docs, scheduling and flows modules" — where "flows" is
|
|
109
|
+
* `signal-collectors` (`MODULE_SURFACES` maps it to `["flows"]`). It drops
|
|
110
|
+
* the shape's `/blog` teaser because docs-blog contributes the real `/blog`,
|
|
111
|
+
* and adds the signal-collectors sample rows ahead of the portfolio pair.
|
|
112
|
+
*/
|
|
113
|
+
readonly "marketing-site": {
|
|
114
|
+
readonly shape: "organization-product";
|
|
115
|
+
readonly design: "standard";
|
|
116
|
+
readonly modules: readonly ["marketing", "docs-blog", "scheduling", "signal-collectors"];
|
|
117
|
+
readonly categories: readonly ["accounting", "legal", "consulting", "hvac", "roofing", "plumbing", "electrical", "landscaping", "auto-repair", "real-estate", "technology", "medical-specialty"];
|
|
118
|
+
readonly differences: {
|
|
119
|
+
readonly drop: readonly ["/blog"];
|
|
120
|
+
readonly add: readonly import("./module-architecture.js").PageRosterRow[];
|
|
121
|
+
readonly before: "/portfolio";
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* Pro Plus — the Pro list plus `multi-tenant`, which is correctly in the
|
|
126
|
+
* list because it DOES contribute pages (the org and workspace surfaces).
|
|
127
|
+
* Its `pageContributions` is nonetheless unset today, deferred to its own
|
|
128
|
+
* instance, so this template's merged roster carries the same contributed
|
|
129
|
+
* rows as `marketing-site`'s and no more. That is a module which has
|
|
130
|
+
* declared no rows yet — visible in the diff — not a merge that dropped
|
|
131
|
+
* declared rows.
|
|
132
|
+
*/
|
|
133
|
+
readonly "saas-product": {
|
|
134
|
+
readonly shape: "organization-product";
|
|
135
|
+
readonly design: "standard";
|
|
136
|
+
readonly modules: readonly ["marketing", "docs-blog", "scheduling", "signal-collectors", "multi-tenant"];
|
|
137
|
+
readonly categories: readonly ["technology", "medical-specialty"];
|
|
138
|
+
readonly differences: {
|
|
139
|
+
readonly drop: readonly ["/blog"];
|
|
140
|
+
readonly add: readonly import("./module-architecture.js").PageRosterRow[];
|
|
141
|
+
readonly before: "/portfolio";
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
/**
|
|
145
|
+
* The second Pro template (#5589) — the reports-first, scroll-snap deck
|
|
146
|
+
* site. NOT `marketing-site` plus a module: its `modules` omit `docs-blog`,
|
|
147
|
+
* so the two Pro templates are siblings under `_base` (#5618 D2), never a
|
|
148
|
+
* lineage. Level still derives from `modules`: `scheduling` and
|
|
149
|
+
* `signal-collectors` are paid, so `templateDelivery` returns `"paid"`
|
|
150
|
+
* exactly as it does for `marketing-site`.
|
|
151
|
+
*
|
|
152
|
+
* It composes the `organization-service` shape roster (#6757) rather than
|
|
153
|
+
* the organization-product one: the deck home replaces the document-scroll
|
|
154
|
+
* home, and `/how-it-works`, the product page, `/pricing` and `/sign-in`
|
|
155
|
+
* are not pages this site has. The roster is what the
|
|
156
|
+
* template IS, so an unrostered page has neither a route directory
|
|
157
|
+
* (`rosterSurfaces`) nor its `_base`/overlay seed (#6646 D1).
|
|
158
|
+
*
|
|
159
|
+
* It declares NO `layout` block (#6638): the deck home, mark-only nav and
|
|
160
|
+
* centered footer are the `minimalism` design's, supplied by
|
|
161
|
+
* `DESIGN_LAYOUTS` from `design:` — the look any shape can wear. The
|
|
162
|
+
* roster rows the `organization-service` shape does not derive
|
|
163
|
+
* (`/reports`, `/referral`) are recorded with their reasons in this
|
|
164
|
+
* template's TEMPLATE-MANIFEST.md § 3a.
|
|
165
|
+
*
|
|
166
|
+
* `/reports` and `/referral` carry no `GATEABLE_SURFACES` entry and need
|
|
167
|
+
* none: both are single-segment `LandingPage` slugs served by the generic
|
|
168
|
+
* `[slug]` route (#5469 D2), the same mechanism Free's `/how-it-works`
|
|
169
|
+
* uses. `/reports/[slug]` (the reader) is the signal-collectors module's
|
|
170
|
+
* own contributed row.
|
|
171
|
+
*
|
|
172
|
+
* `/services/[slug]` declares its seed DIRECTORY as `contentDir`
|
|
173
|
+
* (`content/en/Service`, as `routes-map.ts` records it). The three
|
|
174
|
+
* samples ride the template tree unmapped, landing at that directory.
|
|
175
|
+
*
|
|
176
|
+
* Since #6757, `organization-service` carries a real standard roster
|
|
177
|
+
* (D1), so this alias's `differences` is additions and drops only: it
|
|
178
|
+
* drops the `/blog` teaser (a Pro alias does not upsell its own level,
|
|
179
|
+
* D8) and the portfolio pair (the reports are the proof, not a
|
|
180
|
+
* portfolio), adds the signal-collectors samples, and INSERTS its two own
|
|
181
|
+
* rows — `/reports` before `/about`, `/referral` before `/faq` (D7) — to
|
|
182
|
+
* reproduce its pre-#6757 row order (§ 1.2) exactly.
|
|
183
|
+
*/
|
|
184
|
+
readonly "minimalism-site": {
|
|
185
|
+
readonly shape: "organization-service";
|
|
186
|
+
readonly design: "minimalism";
|
|
187
|
+
readonly modules: readonly ["marketing", "scheduling", "signal-collectors"];
|
|
188
|
+
readonly categories: readonly ["real-estate", "technology", "legal"];
|
|
189
|
+
readonly differences: {
|
|
190
|
+
readonly drop: readonly ["/blog", "/portfolio", "/portfolio/[slug]"];
|
|
191
|
+
readonly add: readonly import("./module-architecture.js").PageRosterRow[];
|
|
192
|
+
readonly insert: readonly [{
|
|
193
|
+
readonly before: "/about";
|
|
194
|
+
readonly rows: readonly [import("./module-architecture.js").PageRosterRow];
|
|
195
|
+
}, {
|
|
196
|
+
readonly before: "/faq";
|
|
197
|
+
readonly rows: readonly [import("./module-architecture.js").PageRosterRow];
|
|
198
|
+
}];
|
|
199
|
+
};
|
|
200
|
+
};
|
|
201
|
+
}>;
|
|
202
|
+
/**
|
|
203
|
+
* The coordinates a keyless `--shape S` builds (#6757 D5) — the composed
|
|
204
|
+
* FREE manifest per shape, for a shape with no `--template` alias of its
|
|
205
|
+
* own. Frozen and `Partial`; `organization-product` needs none — its
|
|
206
|
+
* keyless tree is `marketing-site-free`'s (#6754 AC-2).
|
|
207
|
+
*
|
|
208
|
+
* No `differences` (a bare shape is its standard roster, D1) and no
|
|
209
|
+
* `layout`. Every category is a leaf of `packages/schema/src/categories.ts`,
|
|
210
|
+
* as `TemplateManifest.categories` requires. `person-service`'s single
|
|
211
|
+
* `consulting` category is the operator's disposition (2026-09-26,
|
|
212
|
+
* ai-launch-kit-internal#6758): the leaf did not exist when #6758's spec was
|
|
213
|
+
* written, and adding it replaced that spec's `["accounting", "legal",
|
|
214
|
+
* "technology"]` author-pick stand-in.
|
|
215
|
+
*/
|
|
216
|
+
export declare const SHAPE_STARTER_ALIASES: Readonly<Partial<Record<BuildableShape, TemplateAlias>>>;
|