create-ailk 0.26.0 → 0.28.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 +86 -9
- package/dist/add-module-core.js +2 -1
- package/dist/add-module.d.ts +2 -1
- package/dist/add-module.js +9 -4
- package/dist/add-route.js +46 -19
- package/dist/body-heading.d.ts +56 -0
- package/dist/body-heading.js +394 -0
- package/dist/cli.js +28 -1
- package/dist/content-payloads.js +2 -2
- package/dist/derive-architecture.js +8 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -1
- package/dist/lib/auth-secret-file.d.ts +6 -2
- package/dist/lib/auth-secret-file.js +46 -20
- package/dist/lib/env-catalog.js +20 -0
- package/dist/lib/site-modules.d.ts +8 -0
- package/dist/lib/site-modules.js +34 -0
- package/dist/module-architecture.js +25 -14
- package/dist/paid-sections.d.ts +17 -0
- package/dist/paid-sections.js +39 -0
- package/dist/programmatic.d.ts +16 -0
- package/dist/programmatic.js +73 -4
- package/dist/remove-module.d.ts +98 -0
- package/dist/remove-module.js +620 -0
- package/dist/seed-flows.d.ts +2 -2
- package/dist/seed-flows.js +16 -9
- package/dist/site-roster.js +4 -1
- package/dist/starter-grid.d.ts +26 -0
- package/dist/starter-grid.js +34 -0
- package/dist/surfaces.d.ts +26 -0
- package/dist/surfaces.js +112 -11
- package/dist/sync-routes.d.ts +7 -0
- package/dist/sync-routes.js +53 -6
- package/dist/template-pages.d.ts +36 -0
- package/dist/template-pages.js +129 -0
- package/dist/topology.d.ts +2 -1
- package/dist/topology.js +4 -2
- package/dist/update.d.ts +3 -1
- package/dist/update.js +90 -6
- package/package.json +3 -3
- package/release-scope.json +2 -1
- package/site-templates/aliases/hub.TEMPLATE-MANIFEST.json +115 -0
- package/templates/.env.example +12 -2
- package/templates/CLAUDE.md +20 -20
- package/templates/README.md +19 -19
- package/templates/apps/api/.env.example +16 -5
- package/templates/apps/api/CLAUDE.md +32 -11
- package/templates/apps/api/src/__tests__/vercel-handler.test.ts +37 -0
- package/templates/apps/api/src/vercel-handler.ts +8 -1
- package/templates/apps/api/vercel.json +7 -1
- package/templates/apps/mcp/CLAUDE.md +18 -10
- package/templates/apps/web/.env.example +27 -0
- package/templates/apps/web/CLAUDE.md +56 -8
- package/templates/apps/web/__tests__/setup.ts +7 -0
- package/templates/apps/web/app/.well-known/mcp.json/route.ts +7 -7
- package/templates/apps/web/app/[locale]/(auth)/sign-in/__tests__/next-path.test.ts +24 -1
- package/templates/apps/web/app/[locale]/(auth)/sign-in/__tests__/page.test.tsx +51 -0
- package/templates/apps/web/app/[locale]/(auth)/sign-in/next-path.ts +13 -0
- package/templates/apps/web/app/[locale]/(auth)/sign-in/page.tsx +10 -4
- package/templates/apps/web/app/[locale]/(auth)/sign-up/__tests__/page.test.tsx +197 -4
- package/templates/apps/web/app/[locale]/(auth)/sign-up/page.tsx +23 -7
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/[pieceId]/versions/[version]/file/route.ts +61 -2
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/__tests__/file-route.test.ts +81 -0
- package/templates/apps/web/app/[locale]/(authed)/account/connect/__tests__/page.test.tsx +197 -0
- package/templates/apps/web/app/[locale]/(authed)/account/connect/page.tsx +152 -0
- package/templates/apps/web/app/[locale]/about/__tests__/about-routes.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/careers/[slug]/[action]/page.tsx +76 -21
- package/templates/apps/web/app/[locale]/careers/[slug]/apply.actions.ts +46 -16
- package/templates/apps/web/app/[locale]/careers/[slug]/page.tsx +20 -3
- package/templates/apps/web/app/[locale]/careers/__tests__/apply.actions.test.ts +180 -2
- package/templates/apps/web/app/[locale]/careers/__tests__/careers-set.test.tsx +6 -2
- package/templates/apps/web/app/[locale]/careers/__tests__/job-apply-page.test.tsx +122 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/case-studies/[slug]/page.tsx +20 -3
- package/templates/apps/web/app/[locale]/case-studies/__tests__/hub-children.test.tsx +65 -0
- package/templates/apps/web/app/[locale]/case-studies/__tests__/page-renderer-migration.test.tsx +6 -2
- package/templates/apps/web/app/[locale]/case-studies/page.tsx +23 -11
- package/templates/apps/web/app/[locale]/checkout/__tests__/thank-you-account.test.tsx +11 -1
- package/templates/apps/web/app/[locale]/clients/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/consent.actions.ts +44 -6
- package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +4 -4
- package/templates/apps/web/app/[locale]/contact/__tests__/contact.actions.test.ts +269 -42
- package/templates/apps/web/app/[locale]/contact/contact.actions.ts +88 -30
- package/templates/apps/web/app/[locale]/courses/[slug]/page.tsx +20 -3
- package/templates/apps/web/app/[locale]/courses/__tests__/hub-children.test.tsx +32 -0
- package/templates/apps/web/app/[locale]/courses/__tests__/page-renderer-migration.test.tsx +6 -2
- package/templates/apps/web/app/[locale]/courses/page.tsx +22 -6
- package/templates/apps/web/app/[locale]/events/[slug]/page.tsx +15 -2
- package/templates/apps/web/app/[locale]/events/__tests__/hub-children.test.tsx +32 -0
- package/templates/apps/web/app/[locale]/events/__tests__/page-renderer-migration.test.tsx +8 -2
- package/templates/apps/web/app/[locale]/events/page.tsx +23 -7
- package/templates/apps/web/app/[locale]/features/__tests__/hub-children.test.tsx +32 -0
- package/templates/apps/web/app/[locale]/features/page.tsx +23 -25
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/flow.actions.test.ts +95 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/save.actions.test.ts +193 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/email-moment.ts +92 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +10 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsClient.tsx +513 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsEmailStep.tsx +122 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/ResultsClient.no-commerce.test.tsx +195 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/landing.test.tsx +293 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/page.test.tsx +397 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/checkout-seam.ts +74 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/page.tsx +264 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/save.actions.ts +75 -0
- package/templates/apps/web/app/[locale]/for/__tests__/hub-children.test.tsx +32 -0
- package/templates/apps/web/app/[locale]/for/__tests__/page-renderer-migration.test.tsx +5 -1
- package/templates/apps/web/app/[locale]/for/page.tsx +23 -25
- package/templates/apps/web/app/[locale]/how-to/[slug]/page.tsx +20 -3
- package/templates/apps/web/app/[locale]/how-to/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/integrations/[slug]/page.tsx +25 -4
- package/templates/apps/web/app/[locale]/integrations/__tests__/hub-children.test.tsx +38 -0
- package/templates/apps/web/app/[locale]/integrations/__tests__/page-renderer-migration.test.tsx +8 -2
- package/templates/apps/web/app/[locale]/integrations/page.tsx +23 -11
- package/templates/apps/web/app/[locale]/layout.tsx +161 -128
- package/templates/apps/web/app/[locale]/page.tsx +51 -3
- package/templates/apps/web/app/[locale]/partners/[slug]/page.tsx +16 -3
- package/templates/apps/web/app/[locale]/partners/__tests__/hub-children.test.tsx +32 -0
- package/templates/apps/web/app/[locale]/partners/__tests__/page-renderer-migration.test.tsx +6 -2
- package/templates/apps/web/app/[locale]/partners/page.tsx +22 -6
- package/templates/apps/web/app/[locale]/portfolio/__tests__/hub-children.test.tsx +38 -0
- package/templates/apps/web/app/[locale]/portfolio/page.tsx +25 -24
- package/templates/apps/web/app/[locale]/pricing/[slug]/__tests__/page.test.tsx +321 -0
- package/templates/apps/web/app/[locale]/pricing/[slug]/page.tsx +87 -0
- package/templates/apps/web/app/[locale]/pricing/__tests__/offerings.test.tsx +3 -0
- package/templates/apps/web/app/[locale]/pricing/__tests__/pricing-pages.test.tsx +215 -0
- package/templates/apps/web/app/[locale]/pricing/page.tsx +56 -341
- package/templates/apps/web/app/[locale]/products/[slug]/page.tsx +21 -4
- package/templates/apps/web/app/[locale]/products/__tests__/hub-children.test.tsx +32 -0
- package/templates/apps/web/app/[locale]/products/__tests__/page-renderer-migration.test.tsx +8 -2
- package/templates/apps/web/app/[locale]/products/page.tsx +22 -6
- package/templates/apps/web/app/[locale]/projects/__tests__/hub-children.test.tsx +32 -0
- package/templates/apps/web/app/[locale]/projects/__tests__/page-renderer-migration.test.tsx +4 -0
- package/templates/apps/web/app/[locale]/projects/page.tsx +18 -11
- package/templates/apps/web/app/[locale]/resources/[slug]/page.tsx +25 -4
- package/templates/apps/web/app/[locale]/resources/__tests__/hub-children.test.tsx +38 -0
- package/templates/apps/web/app/[locale]/resources/__tests__/page-renderer-migration.test.tsx +6 -2
- package/templates/apps/web/app/[locale]/resources/page.tsx +20 -10
- package/templates/apps/web/app/[locale]/services/[slug]/[child]/page.tsx +68 -0
- package/templates/apps/web/app/[locale]/services/[slug]/page.tsx +64 -9
- package/templates/apps/web/app/[locale]/services/__tests__/hub-children.test.tsx +158 -0
- package/templates/apps/web/app/[locale]/services/__tests__/page-renderer-migration.test.tsx +12 -2
- package/templates/apps/web/app/[locale]/services/__tests__/service-detail-sections.test.tsx +223 -0
- package/templates/apps/web/app/[locale]/services/__tests__/service-families.test.tsx +368 -0
- package/templates/apps/web/app/[locale]/services/page.tsx +45 -19
- package/templates/apps/web/app/[locale]/software/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/team/__tests__/hub-children.test.tsx +32 -0
- package/templates/apps/web/app/[locale]/team/page.tsx +25 -24
- package/templates/apps/web/app/[locale]/tech-articles/[slug]/page.tsx +24 -3
- package/templates/apps/web/app/[locale]/tech-articles/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/templates/[slug]/__tests__/page.test.tsx +203 -0
- package/templates/apps/web/app/[locale]/templates/[slug]/page.tsx +81 -0
- package/templates/apps/web/app/[locale]/templates/__tests__/page.test.tsx +236 -0
- package/templates/apps/web/app/[locale]/templates/page.tsx +108 -0
- package/templates/apps/web/app/[locale]/testimonials/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/whitepapers/[slug]/page.tsx +24 -3
- package/templates/apps/web/app/[locale]/whitepapers/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/api/auth/hub/authorize/__tests__/route.test.ts +322 -2
- package/templates/apps/web/app/api/auth/hub/authorize/route.ts +161 -29
- package/templates/apps/web/app/api/hub-session/__tests__/routes.test.ts +51 -0
- package/templates/apps/web/app/api/hub-session/sign-in/route.ts +13 -0
- package/templates/apps/web/app/api/offerings/checkout/[lookupKey]/__tests__/route.test.ts +233 -7
- package/templates/apps/web/app/api/offerings/checkout/[lookupKey]/route.ts +92 -41
- package/templates/apps/web/app/api/offerings/checkout/__tests__/route.test.ts +77 -39
- package/templates/apps/web/app/llms.txt/route.ts +41 -3
- package/templates/apps/web/app/robots.txt/route.ts +5 -1
- package/templates/apps/web/app/sitemap.xml/route.ts +45 -3
- package/templates/apps/web/auth.ts +102 -50
- package/templates/apps/web/components/signed-in-nav.tsx +8 -1
- package/templates/apps/web/lib/__tests__/bot-check.test.ts +254 -0
- package/templates/apps/web/lib/__tests__/consent-visitor.test.ts +212 -13
- package/templates/apps/web/lib/__tests__/deck-page.test.ts +13 -1
- package/templates/apps/web/lib/__tests__/docs-index.test.ts +93 -0
- package/templates/apps/web/lib/__tests__/docs-path.test.ts +168 -0
- package/templates/apps/web/lib/__tests__/hub-children.fixtures.ts +278 -0
- package/templates/apps/web/lib/__tests__/nav-label-keys.test.ts +138 -0
- package/templates/apps/web/lib/__tests__/newsletter.actions.test.ts +178 -2
- package/templates/apps/web/lib/__tests__/offerings-pricing.test.ts +183 -0
- package/templates/apps/web/lib/__tests__/page-chrome.test.ts +412 -0
- package/templates/apps/web/lib/__tests__/pricing-page.test.ts +142 -0
- package/templates/apps/web/lib/__tests__/relay.test.ts +484 -4
- package/templates/apps/web/lib/__tests__/route-family.test.ts +57 -0
- package/templates/apps/web/lib/__tests__/service-listing.test.ts +111 -0
- package/templates/apps/web/lib/__tests__/services-path.test.ts +96 -0
- package/templates/apps/web/lib/__tests__/site-header.test.ts +151 -0
- package/templates/apps/web/lib/__tests__/structured-data.test.tsx +148 -6
- package/templates/apps/web/lib/__tests__/telemetry-scrub.test.ts +84 -0
- package/templates/apps/web/lib/__tests__/template-listing.test.ts +158 -0
- package/templates/apps/web/lib/auth-secret.ts +57 -0
- package/templates/apps/web/lib/bot-check.ts +138 -0
- package/templates/apps/web/lib/consent-visitor.ts +81 -7
- package/templates/apps/web/lib/deck-page.ts +6 -3
- package/templates/apps/web/lib/docs-index.ts +100 -0
- package/templates/apps/web/lib/docs-path.ts +94 -0
- package/templates/apps/web/lib/hub-children.tsx +318 -0
- package/templates/apps/web/lib/hub-session/__tests__/sign-in-gate.test.tsx +22 -2
- package/templates/apps/web/lib/hub-session/sign-in-gate.tsx +11 -8
- package/templates/apps/web/lib/modules.generated.ts +9 -0
- package/templates/apps/web/lib/nav-label-check.ts +61 -0
- package/templates/apps/web/lib/nav-label-keys.ts +78 -0
- package/templates/apps/web/lib/newsletter.actions.ts +31 -6
- package/templates/apps/web/lib/offerings-pricing.ts +135 -6
- package/templates/apps/web/lib/page-chrome.ts +367 -0
- package/templates/apps/web/lib/posting-title.ts +26 -4
- package/templates/apps/web/lib/pricing-page.ts +430 -0
- package/templates/apps/web/lib/relay.ts +44 -4
- package/templates/apps/web/lib/route-family.ts +69 -0
- package/templates/apps/web/lib/service-listing.ts +89 -0
- package/templates/apps/web/lib/services-path.ts +97 -0
- package/templates/apps/web/lib/sign-up-agreements.ts +17 -6
- package/templates/apps/web/lib/site-header.ts +94 -0
- package/templates/apps/web/lib/telemetry-scrub.ts +68 -3
- package/templates/apps/web/lib/template-listing.ts +169 -0
- package/templates/apps/web/messages/en.json +144 -3
- package/templates/apps/web/middleware.ts +179 -12
- package/templates/apps/web/next.config.mjs +220 -3
- package/templates/apps/web/package.json +3 -2
- package/templates/apps/web/scripts/assert-content-bundled.ts +28 -6
- package/templates/apps/web/scripts/check-nav-labels.ts +39 -0
- package/templates/apps/web/sentry.server.config.ts +30 -2
- package/templates/apps/web/src/lib/__tests__/rate-limit.test.ts +67 -9
- package/templates/apps/web/src/lib/rate-limit.ts +68 -27
- package/templates/apps/web/src/lib/routes.ts +15 -1
- package/versions.json +48 -48
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The templates route family's generated seeds (#10051 D10) — the /templates
|
|
3
|
+
* hub seed plus one placeholder TemplatePage per AILK template, derived from
|
|
4
|
+
* `TEMPLATE_MANIFESTS` so a new template never needs a hand-written seed.
|
|
5
|
+
* `add-route templates` writes them (`GENERATED_SEEDS` in `add-route.ts`);
|
|
6
|
+
* the site then edits or deletes them like any content.
|
|
7
|
+
*
|
|
8
|
+
* A seed never invents a fact: its copy is placeholder (`placeholder: true`,
|
|
9
|
+
* renderPage's replace-before-launch label), it has no thumbnail, and its Get
|
|
10
|
+
* it action is `/contact`. Only a template whose `templateDelivery` is
|
|
11
|
+
* `"free"` gets `price: 0` — a paid template's price is the site's to set.
|
|
12
|
+
*
|
|
13
|
+
* Pure (no fs). create-ailk has no YAML dependency by design, so every
|
|
14
|
+
* frontmatter value is written as `JSON.stringify(value)` — a JSON scalar,
|
|
15
|
+
* array or object is valid YAML flow syntax (the `derive-architecture.ts`
|
|
16
|
+
* precedent).
|
|
17
|
+
*/
|
|
18
|
+
import { RECOGNIZED_MODULES } from "./parse-args.js";
|
|
19
|
+
import { TEMPLATE_MANIFESTS, templateDelivery, } from "./template-manifest.js";
|
|
20
|
+
/** What every template carries whatever its modules: the base site. */
|
|
21
|
+
export const BASE_INCLUDE = {
|
|
22
|
+
title: "The base site",
|
|
23
|
+
description: "Your pages with structured data on every one, plus an API and an MCP server agents can read.",
|
|
24
|
+
};
|
|
25
|
+
/** One row per module, typed exhaustively so a new module fails type-check. */
|
|
26
|
+
export const MODULE_INCLUDES = {
|
|
27
|
+
marketing: {
|
|
28
|
+
title: "Marketing pages",
|
|
29
|
+
description: "The pages a marketing site needs, with lead capture.",
|
|
30
|
+
},
|
|
31
|
+
"docs-blog": {
|
|
32
|
+
title: "Docs and blog",
|
|
33
|
+
description: "A documentation section and a blog, written as content files.",
|
|
34
|
+
},
|
|
35
|
+
"multi-tenant": {
|
|
36
|
+
title: "Organizations and workspaces",
|
|
37
|
+
description: "Many customers on one deployment, each with their own members.",
|
|
38
|
+
},
|
|
39
|
+
workspace: {
|
|
40
|
+
title: "Admin",
|
|
41
|
+
description: "Your own signed-in area to edit pages, manage assets and review submissions.",
|
|
42
|
+
},
|
|
43
|
+
scheduling: {
|
|
44
|
+
title: "Scheduling",
|
|
45
|
+
description: "Booking pages visitors use to schedule time with you.",
|
|
46
|
+
},
|
|
47
|
+
"signal-collectors": {
|
|
48
|
+
title: "Quizzes and reports",
|
|
49
|
+
description: "Assessments and downloadable reports that capture interested visitors.",
|
|
50
|
+
},
|
|
51
|
+
commerce: {
|
|
52
|
+
title: "Checkout",
|
|
53
|
+
description: "Take payment through your own Stripe account.",
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
const HUB_SEED_PATH = "content/en/TemplatesList/templates.mdx";
|
|
57
|
+
/** Words a naive capitalisation would mis-case in a template name. */
|
|
58
|
+
const WORD_CASING = { saas: "SaaS" };
|
|
59
|
+
/** `marketing-site-free` → `Marketing Site Free`; `saas-product` → `SaaS Product`. */
|
|
60
|
+
function titleCase(name) {
|
|
61
|
+
return name
|
|
62
|
+
.split("-")
|
|
63
|
+
.map((word) => WORD_CASING[word] ?? word.charAt(0).toUpperCase() + word.slice(1))
|
|
64
|
+
.join(" ");
|
|
65
|
+
}
|
|
66
|
+
/** A seed file: a JSON-valued YAML frontmatter block and a placeholder body. */
|
|
67
|
+
function mdx(frontmatter, body) {
|
|
68
|
+
const lines = Object.entries(frontmatter).map(([key, value]) => `${key}: ${JSON.stringify(value)}`);
|
|
69
|
+
return `---\n${lines.join("\n")}\n---\n\n${body}\n`;
|
|
70
|
+
}
|
|
71
|
+
function templateSeed(name, manifest) {
|
|
72
|
+
const title = titleCase(name);
|
|
73
|
+
const includes = [
|
|
74
|
+
BASE_INCLUDE,
|
|
75
|
+
...RECOGNIZED_MODULES.filter((module) => manifest.requires.includes(module)).map((module) => MODULE_INCLUDES[module]),
|
|
76
|
+
];
|
|
77
|
+
const free = templateDelivery(manifest) === "free";
|
|
78
|
+
return {
|
|
79
|
+
path: `content/en/TemplatePage/${name}.mdx`,
|
|
80
|
+
text: mdx({
|
|
81
|
+
type: "TemplatePage",
|
|
82
|
+
slug: name,
|
|
83
|
+
title,
|
|
84
|
+
description: `Replace with a one-sentence description of the ${title} template.`,
|
|
85
|
+
summary: `Replace with what a site built on ${title} gives a customer.`,
|
|
86
|
+
placeholder: true,
|
|
87
|
+
template: name,
|
|
88
|
+
categories: [...manifest.categories],
|
|
89
|
+
includes,
|
|
90
|
+
demoUrl: `https://${name}.salessmyth.com`,
|
|
91
|
+
getItUrl: "/contact",
|
|
92
|
+
...(free && { price: 0, priceCurrency: "USD" }),
|
|
93
|
+
}, `{/* Placeholder: /templates/${name} renders its hero and "What's included" from the frontmatter above. Replace the copy, add a thumbnail and set getItUrl to your buy link before launch. */}`),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The hub seed, then one TemplatePage seed per manifest, sorted by name.
|
|
98
|
+
* `manifests` defaults to the frozen registry; a test may pass its own.
|
|
99
|
+
*/
|
|
100
|
+
export function templatePageSeeds(manifests = TEMPLATE_MANIFESTS) {
|
|
101
|
+
const description = "Replace with a one-sentence description of the templates you sell.";
|
|
102
|
+
const hub = {
|
|
103
|
+
path: HUB_SEED_PATH,
|
|
104
|
+
text: mdx({
|
|
105
|
+
type: "TemplatesList",
|
|
106
|
+
slug: "templates",
|
|
107
|
+
title: "Templates",
|
|
108
|
+
description,
|
|
109
|
+
placeholder: true,
|
|
110
|
+
// A tight hero keeps the first cards — the hub's actions — inside
|
|
111
|
+
// the first screen (design review round 1, B1).
|
|
112
|
+
sections: [
|
|
113
|
+
{
|
|
114
|
+
id: "templates-hero",
|
|
115
|
+
type: "hero",
|
|
116
|
+
data: {
|
|
117
|
+
headline: "Templates",
|
|
118
|
+
subheadline: description,
|
|
119
|
+
gapBelow: "tight",
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
}, "{/* Placeholder: /templates lists every TemplatePage automatically. Replace the description before launch. */}"),
|
|
124
|
+
};
|
|
125
|
+
const pages = Object.keys(manifests)
|
|
126
|
+
.sort()
|
|
127
|
+
.map((name) => templateSeed(name, manifests[name]));
|
|
128
|
+
return [hub, ...pages];
|
|
129
|
+
}
|
package/dist/topology.d.ts
CHANGED
|
@@ -32,7 +32,8 @@ export declare const SPOKE_EXCLUDED_MODULE_PATHS: readonly string[];
|
|
|
32
32
|
* The `(authed)` seeds of the sign-in-through-the-hub mode (#7894 D9): the
|
|
33
33
|
* whole `(authed)` group goes in the default mode; in the hub mode its account
|
|
34
34
|
* area, layout and gate seam stay, and only these go — the operator surfaces,
|
|
35
|
-
* the Better Auth gate
|
|
35
|
+
* the Better Auth gate, the hub's own consent page (#9966 D2, a 404 on a
|
|
36
|
+
* spoke) and the suites that mock `auth.ts` by path.
|
|
36
37
|
*/
|
|
37
38
|
export declare const HUB_MODE_AUTHED_SEEDS: readonly string[];
|
|
38
39
|
/** The generated seam files (D5; #7874 D12). A seam is rewritten, never removed. */
|
package/dist/topology.js
CHANGED
|
@@ -86,11 +86,13 @@ export const SPOKE_EXCLUDED_MODULE_PATHS = [
|
|
|
86
86
|
* The `(authed)` seeds of the sign-in-through-the-hub mode (#7894 D9): the
|
|
87
87
|
* whole `(authed)` group goes in the default mode; in the hub mode its account
|
|
88
88
|
* area, layout and gate seam stay, and only these go — the operator surfaces,
|
|
89
|
-
* the Better Auth gate
|
|
89
|
+
* the Better Auth gate, the hub's own consent page (#9966 D2, a 404 on a
|
|
90
|
+
* spoke) and the suites that mock `auth.ts` by path.
|
|
90
91
|
*/
|
|
91
92
|
export const HUB_MODE_AUTHED_SEEDS = [
|
|
92
93
|
"apps/web/app/[locale]/(authed)/__tests__",
|
|
93
94
|
"apps/web/app/[locale]/(authed)/account/__tests__",
|
|
95
|
+
"apps/web/app/[locale]/(authed)/account/connect",
|
|
94
96
|
"apps/web/app/[locale]/(authed)/admin",
|
|
95
97
|
"apps/web/app/[locale]/(authed)/appointments",
|
|
96
98
|
"apps/web/app/[locale]/(authed)/edit",
|
|
@@ -154,7 +156,7 @@ export function spokeRemovedRoots(opts) {
|
|
|
154
156
|
}
|
|
155
157
|
/**
|
|
156
158
|
* The seeds that apply — an mcp seed is moot when `apps/mcp` itself goes; in
|
|
157
|
-
* the hub sign-in mode the `(authed)` seed is replaced by
|
|
159
|
+
* the hub sign-in mode the `(authed)` seed is replaced by nine (#7894 D9).
|
|
158
160
|
*/
|
|
159
161
|
function spokeSeeds(opts) {
|
|
160
162
|
return modeSeeds(opts.signIn).filter((seed) => opts.mcp || !underPrefix(seed, SPOKE_MCP_ROOT));
|
package/dist/update.d.ts
CHANGED
|
@@ -45,6 +45,8 @@ export interface UpdateResult {
|
|
|
45
45
|
file: string;
|
|
46
46
|
tests: string[];
|
|
47
47
|
}>;
|
|
48
|
+
/** The mdx-body pages under `content/` given a `# ` heading this run (#7495). */
|
|
49
|
+
headingsAdded: string[];
|
|
48
50
|
backfilled?: {
|
|
49
51
|
version: string;
|
|
50
52
|
files: number;
|
|
@@ -57,6 +59,6 @@ export interface UpdateArgs {
|
|
|
57
59
|
target: string | undefined;
|
|
58
60
|
help: boolean;
|
|
59
61
|
}
|
|
60
|
-
export declare const UPDATE_HELP = "create-ailk update \u2014 bring template fixes into a site's copied app code.\n\nUsage:\n create-ailk update [<target-dir>]\n\nArguments:\n <target-dir> The existing site. Defaults to the current dir.\n\nOptions:\n --help, -h Print this message.\n\nReads .ailk-template-files.json (written beside .ailk-site-manifest.json by\nevery template scaffold) to tell an untouched copy from an edit. An\nuntouched apps/web, apps/api or apps/mcp file, or a root tools/jest file the\napp jest configs require, whose template copy changed is overwritten. An\nedited file whose template copy also changed is three-way merged with\n`git` (git must be on PATH); a conflict leaves the file alone and is\nlisted under \"Needs review\". Every template test that imports a file\nleft under \"Needs review\" or \"Skipped\" (by a relative import) is held at the\nsite's version with it, its record entry is not advanced, and it is listed\nunder \"Held back with <file>\" \u2014 reconcile the file, then run update again to\nbring its tests forward. Every other file in the run still updates.\nA site with no record gets one on its first run, from the version its\n.ailk-site-manifest.json already records. A site scaffolded before\n@working-theory/mcp-server has its copied apps/mcp moved to the boot set:\nthe server and each module's paid MCP pack join apps/mcp/package.json, the\ncopied server files go, and apps/mcp/src/lib/paid-tools.generated.ts is\nrewritten on every run. A site scaffolded before @working-theory/api has its\ncopied apps/api moved to the boot file: the app becomes @working-theory/app-api,\nthe package and the six @working-theory/api-<module> packages join\napps/api/package.json, its bins and build:vercel delegate to the package, and\nthe copied server files go. Run `pnpm install` after either move, since the\napp's package.json gained dependencies. Commit before running, and review the\ndiff after.\n";
|
|
62
|
+
export declare const UPDATE_HELP = "create-ailk update \u2014 bring template fixes into a site's copied app code.\n\nUsage:\n create-ailk update [<target-dir>]\n\nArguments:\n <target-dir> The existing site. Defaults to the current dir.\n\nOptions:\n --help, -h Print this message.\n\nReads .ailk-template-files.json (written beside .ailk-site-manifest.json by\nevery template scaffold) to tell an untouched copy from an edit. An\nuntouched apps/web, apps/api or apps/mcp file, or a root tools/jest file the\napp jest configs require, whose template copy changed is overwritten. An\nedited file whose template copy also changed is three-way merged with\n`git` (git must be on PATH); a conflict leaves the file alone and is\nlisted under \"Needs review\". Every template test that imports a file\nleft under \"Needs review\" or \"Skipped\" (by a relative import) is held at the\nsite's version with it, its record entry is not advanced, and it is listed\nunder \"Held back with <file>\" \u2014 reconcile the file, then run update again to\nbring its tests forward. Every other file in the run still updates.\nA site with no record gets one on its first run, from the version its\n.ailk-site-manifest.json already records. A site scaffolded before\n@working-theory/mcp-server has its copied apps/mcp moved to the boot set:\nthe server and each module's paid MCP pack join apps/mcp/package.json, the\ncopied server files go, and apps/mcp/src/lib/paid-tools.generated.ts is\nrewritten on every run. A site scaffolded before @working-theory/api has its\ncopied apps/api moved to the boot file: the app becomes @working-theory/app-api,\nthe package and the six @working-theory/api-<module> packages join\napps/api/package.json, its bins and build:vercel delegate to the package, and\nthe copied server files go. Run `pnpm install` after either move, since the\napp's package.json gained dependencies. Each mdx-body page in content/ with no\n'# ' heading gets its title and description written into its body as its\nheading and intro, listed under \"Page heading added\"; a page with one heading\nis left as it is, and one with several, or one whose frontmatter it cannot\nread, is listed under \"Needs review\". Commit before running, and review the\ndiff after.\n";
|
|
61
63
|
/** Parse the argv AFTER the `update` word. Strict `node:util` parseArgs, like `parseAdoptArgs`. */
|
|
62
64
|
export declare function parseUpdateArgs(argv: string[]): UpdateArgs;
|
package/dist/update.js
CHANGED
|
@@ -27,24 +27,29 @@
|
|
|
27
27
|
* (#9566): the record retires the copied server files and lands the boot
|
|
28
28
|
* files; the app's rename, its dependencies and delegating scripts, and the
|
|
29
29
|
* bundle-delivered module files are planned here. Every refusal throws
|
|
30
|
-
* before the first write.
|
|
30
|
+
* before the first write. Last, every mdx-body page in the site's `content/`
|
|
31
|
+
* with no `# ` heading gets its title and description written into its body
|
|
32
|
+
* (#7495, `body-heading.ts`); a page with one or more headings is never
|
|
33
|
+
* touched, and `content/` is never recorded.
|
|
31
34
|
*
|
|
32
35
|
* Pure module: no `import.meta.url` — the CLI supplies `templatesDir`, the
|
|
33
36
|
* `adopt.ts` / `add-route.ts` split.
|
|
34
37
|
*/
|
|
35
38
|
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, rmdirSync, writeFileSync, } from "node:fs";
|
|
36
|
-
import { dirname, join, resolve, sep } from "node:path";
|
|
39
|
+
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
37
40
|
import { parseArgs } from "node:util";
|
|
38
41
|
import { AddModuleRefusedError, readArchitectureHeader } from "./add-module-core.js";
|
|
42
|
+
import { headingViolations, mdxBodyRule, migrateBodyHeadings } from "./body-heading.js";
|
|
39
43
|
import { buildScaffoldMarkers, isExcluded, isTextFile, renderTemplateFile, templateDestination } from "./copy.js";
|
|
40
44
|
import { fetchTemplateSnapshot } from "./lib/fetch-template-snapshot.js";
|
|
41
45
|
import { mergeThreeWay } from "./lib/merge-three-way.js";
|
|
46
|
+
import { publicTreeRemoved } from "./lib/site-modules.js";
|
|
42
47
|
import { MODULE_SLICES } from "./module-architecture.js";
|
|
43
48
|
import { MODULE_FLAGS_PATH, PAID_MCP_TOOLS_PATH, writeModuleFlags, writePaidMcpTools, } from "./paid-sections.js";
|
|
44
49
|
import { ParseError } from "./parse-args.js";
|
|
45
50
|
import { SITE_ROSTER_PATH } from "./site-roster.js";
|
|
46
51
|
import { SPOKE_CHECKOUT_TEMPLATE_PATHS } from "./spoke-checkout.js";
|
|
47
|
-
import { excludedRoutePaths } from "./surfaces.js";
|
|
52
|
+
import { excludedRoutePaths, isRemovedPublicRoutePath } from "./surfaces.js";
|
|
48
53
|
import { DATABASE_BOOT_FILES, ROOT_TOOL_FILES, TEMPLATE_RECORD_PATH, isRecordable, parseTemplateRecord, serializeTemplateRecord, sha256Hex, } from "./template-record.js";
|
|
49
54
|
import { MCP_SEAM_PATH, MCP_SERVER_HUB, TopologyError, isSpokeExcluded, readSpokeShape, spokeExclusions, } from "./topology.js";
|
|
50
55
|
/** A refusal the CLI exits 2 on, thrown before anything is written. */
|
|
@@ -736,6 +741,47 @@ function planApiMove(targetDir, templatesDir, bootSet, recorded, versions) {
|
|
|
736
741
|
move.review = unrecorded.review;
|
|
737
742
|
return move;
|
|
738
743
|
}
|
|
744
|
+
// ── The web app's carried dependencies (#9889 D9) ─────────────────────────
|
|
745
|
+
const WEB_PACKAGE_JSON = "apps/web/package.json";
|
|
746
|
+
/**
|
|
747
|
+
* The third-party packages `update` adds to a site's `apps/web/package.json`
|
|
748
|
+
* because template-owned web code imports them: `package.json` is never
|
|
749
|
+
* recorded, so without this a site updated onto that code would not build.
|
|
750
|
+
* Frozen; a name joins only with a spec.
|
|
751
|
+
*/
|
|
752
|
+
const WEB_CARRIED_DEPENDENCIES = Object.freeze(["botid"]);
|
|
753
|
+
/**
|
|
754
|
+
* The site's `apps/web/package.json` with each carried dependency the running
|
|
755
|
+
* template declares and the site does not (in `dependencies` or
|
|
756
|
+
* `devDependencies`, at any range) added at the template's range, or
|
|
757
|
+
* `undefined` when there is nothing to add or no web app. Throws before any
|
|
758
|
+
* write when the site's file is not valid JSON.
|
|
759
|
+
*/
|
|
760
|
+
function planWebDependencies(targetDir, templatesDir) {
|
|
761
|
+
const sitePath = join(targetDir, ...WEB_PACKAGE_JSON.split("/"));
|
|
762
|
+
const templatePath = join(templatesDir, ...WEB_PACKAGE_JSON.split("/"));
|
|
763
|
+
if (!existsSync(sitePath) || !existsSync(templatePath))
|
|
764
|
+
return undefined;
|
|
765
|
+
let pkg;
|
|
766
|
+
try {
|
|
767
|
+
pkg = JSON.parse(readFileSync(sitePath, "utf8"));
|
|
768
|
+
}
|
|
769
|
+
catch (err) {
|
|
770
|
+
throw new UpdateRefusedError(`${WEB_PACKAGE_JSON} is not valid JSON: ${err.message}`);
|
|
771
|
+
}
|
|
772
|
+
const templateDeps = JSON.parse(readFileSync(templatePath, "utf8")).dependencies ?? {};
|
|
773
|
+
const entries = {};
|
|
774
|
+
for (const name of WEB_CARRIED_DEPENDENCIES) {
|
|
775
|
+
const range = templateDeps[name];
|
|
776
|
+
if (range === undefined)
|
|
777
|
+
continue;
|
|
778
|
+
if (Object.hasOwn(pkg.dependencies ?? {}, name) || Object.hasOwn(pkg.devDependencies ?? {}, name)) {
|
|
779
|
+
continue;
|
|
780
|
+
}
|
|
781
|
+
entries[name] = range;
|
|
782
|
+
}
|
|
783
|
+
return Object.keys(entries).length > 0 ? withDependencies(pkg, entries) : undefined;
|
|
784
|
+
}
|
|
739
785
|
export async function updateSite(opts) {
|
|
740
786
|
const targetDir = resolve(opts.targetDir);
|
|
741
787
|
const version = opts.version;
|
|
@@ -905,6 +951,9 @@ export async function updateSite(opts) {
|
|
|
905
951
|
const apiMove = existsSync(apiPkgPath)
|
|
906
952
|
? planApiMove(targetDir, opts.templatesDir, new Set([...newMap.keys()].filter((path) => path.startsWith(`${API_SRC}/`))), new Set(Object.keys(record.files)), opts.versions ?? {})
|
|
907
953
|
: undefined;
|
|
954
|
+
// ── The web app's carried dependencies (#9889 D9), planned before the
|
|
955
|
+
// first write so an unparseable apps/web/package.json refuses the run.
|
|
956
|
+
const webPackageJson = planWebDependencies(targetDir, opts.templatesDir);
|
|
908
957
|
// ── Every recorded row (D4) ────────────────────────────────────────────
|
|
909
958
|
const updated = [];
|
|
910
959
|
const merged = [];
|
|
@@ -1047,10 +1096,17 @@ export async function updateSite(opts) {
|
|
|
1047
1096
|
// module object at a boot-set path goes before the writes below, and the
|
|
1048
1097
|
// template's re-export lands there as an added file.
|
|
1049
1098
|
const retired = new Set(apiMove?.deletes ?? []);
|
|
1099
|
+
// #10040 D10 — a site that dropped `marketing` never gains a public route
|
|
1100
|
+
// file from a newer template, tombstone or not.
|
|
1101
|
+
const marketingRemoved = publicTreeRemoved(targetDir);
|
|
1050
1102
|
const knownPaths = new Set([...Object.keys(record.files), ...(knownSnapshotPaths ?? [])]);
|
|
1051
1103
|
for (const path of [...newMap.keys()].sort()) {
|
|
1052
1104
|
if (Object.prototype.hasOwnProperty.call(record.files, path))
|
|
1053
1105
|
continue;
|
|
1106
|
+
if (marketingRemoved && isRemovedPublicRoutePath(path)) {
|
|
1107
|
+
skipped.push({ path, reason: "marketing removed" });
|
|
1108
|
+
continue;
|
|
1109
|
+
}
|
|
1054
1110
|
// The guard runs before the disk check, so a full-copy site's boot path
|
|
1055
1111
|
// reads as guarded, never as "exists, not in the record" (#9569 DD8).
|
|
1056
1112
|
if (bootFileGuarded(path)) {
|
|
@@ -1201,6 +1257,26 @@ export async function updateSite(opts) {
|
|
|
1201
1257
|
moved.updated.push(API_PACKAGE_JSON);
|
|
1202
1258
|
pruneEmptyDirs(join(targetDir, ...API_SRC.split("/")));
|
|
1203
1259
|
}
|
|
1260
|
+
// #9889 D9 — the web app's carried dependencies, after the record's writes.
|
|
1261
|
+
if (webPackageJson !== undefined) {
|
|
1262
|
+
writeFileSync(join(targetDir, ...WEB_PACKAGE_JSON.split("/")), webPackageJson);
|
|
1263
|
+
moved.updated.push(WEB_PACKAGE_JSON);
|
|
1264
|
+
}
|
|
1265
|
+
// #7495 D6 — the page body owns its H1: give every heading-less mdx-body
|
|
1266
|
+
// page its title and description, read the rule after the writes so a
|
|
1267
|
+
// route this run added counts. Its rows join `review` after `heldFiles`
|
|
1268
|
+
// was computed, so a content path never holds a template test back.
|
|
1269
|
+
const contentDir = join(targetDir, "content");
|
|
1270
|
+
const rule = mdxBodyRule(targetDir);
|
|
1271
|
+
const sitePath = (abs) => relative(targetDir, abs).split(sep).join("/");
|
|
1272
|
+
const headings = migrateBodyHeadings(contentDir, rule);
|
|
1273
|
+
for (const { path, count } of headingViolations(contentDir, rule)) {
|
|
1274
|
+
if (count > 1)
|
|
1275
|
+
review.push({ path: sitePath(path), reason: `${count} '# ' headings; keep one` });
|
|
1276
|
+
}
|
|
1277
|
+
for (const { path, reason } of headings.refused) {
|
|
1278
|
+
review.push({ path: sitePath(path), reason: `no '# ' heading added: ${reason}` });
|
|
1279
|
+
}
|
|
1204
1280
|
return {
|
|
1205
1281
|
targetDir,
|
|
1206
1282
|
version,
|
|
@@ -1208,6 +1284,7 @@ export async function updateSite(opts) {
|
|
|
1208
1284
|
review,
|
|
1209
1285
|
skipped,
|
|
1210
1286
|
heldBack,
|
|
1287
|
+
headingsAdded: headings.written.map(sitePath),
|
|
1211
1288
|
...(backfilled !== undefined ? { backfilled } : {}),
|
|
1212
1289
|
};
|
|
1213
1290
|
}
|
|
@@ -1223,6 +1300,7 @@ export function formatUpdateReport(result) {
|
|
|
1223
1300
|
["Added", result.added],
|
|
1224
1301
|
["Removed", result.removed],
|
|
1225
1302
|
["Already current", result.current],
|
|
1303
|
+
["Page heading added", result.headingsAdded],
|
|
1226
1304
|
];
|
|
1227
1305
|
for (const [label, paths] of sections) {
|
|
1228
1306
|
if (paths.length === 0)
|
|
@@ -1248,14 +1326,16 @@ export function formatUpdateReport(result) {
|
|
|
1248
1326
|
result.added.length === 0 &&
|
|
1249
1327
|
result.removed.length === 0 &&
|
|
1250
1328
|
result.current.length === 0 &&
|
|
1329
|
+
result.headingsAdded.length === 0 &&
|
|
1251
1330
|
result.review.length === 0 &&
|
|
1252
1331
|
result.skipped.length === 0;
|
|
1253
1332
|
if (nothingHappened) {
|
|
1254
1333
|
return `[create-ailk] Already up to date (create-ailk ${result.version}).\n`;
|
|
1255
1334
|
}
|
|
1256
1335
|
// #9567 / #9566 — the MCP and API moves add dependencies (the mcp README
|
|
1257
|
-
// § Move, step 7, names the install; the API half says the same)
|
|
1258
|
-
|
|
1336
|
+
// § Move, step 7, names the install; the API half says the same), and so
|
|
1337
|
+
// does the web app's carried-dependency step (#9889 D9).
|
|
1338
|
+
for (const pkgJson of [MCP_PACKAGE_JSON, API_PACKAGE_JSON, WEB_PACKAGE_JSON]) {
|
|
1259
1339
|
if (result.updated.includes(pkgJson)) {
|
|
1260
1340
|
lines.push(`${pkgJson} gained dependencies: run \`pnpm install\`.`);
|
|
1261
1341
|
}
|
|
@@ -1294,7 +1374,11 @@ copied apps/api moved to the boot file: the app becomes @working-theory/app-api,
|
|
|
1294
1374
|
the package and the six @working-theory/api-<module> packages join
|
|
1295
1375
|
apps/api/package.json, its bins and build:vercel delegate to the package, and
|
|
1296
1376
|
the copied server files go. Run \`pnpm install\` after either move, since the
|
|
1297
|
-
app's package.json gained dependencies.
|
|
1377
|
+
app's package.json gained dependencies. Each mdx-body page in content/ with no
|
|
1378
|
+
'# ' heading gets its title and description written into its body as its
|
|
1379
|
+
heading and intro, listed under "Page heading added"; a page with one heading
|
|
1380
|
+
is left as it is, and one with several, or one whose frontmatter it cannot
|
|
1381
|
+
read, is listed under "Needs review". Commit before running, and review the
|
|
1298
1382
|
diff after.
|
|
1299
1383
|
`;
|
|
1300
1384
|
/** Parse the argv AFTER the `update` word. Strict `node:util` parseArgs, like `parseAdoptArgs`. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-ailk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"description": "Scaffolder for AI Launch Kit — `pnpm create ailk@latest <dir>` produces a starter repo consuming @working-theory/* from npm (per ADR 0007).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"tar": "^7.5.21"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@working-theory/schema": "0.
|
|
32
|
-
"@working-theory/templates": "0.
|
|
31
|
+
"@working-theory/schema": "0.50.0",
|
|
32
|
+
"@working-theory/templates": "0.50.0"
|
|
33
33
|
},
|
|
34
34
|
"repository": {
|
|
35
35
|
"type": "git",
|
package/release-scope.json
CHANGED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
{
|
|
2
|
+
"template": "hub",
|
|
3
|
+
"requires": [],
|
|
4
|
+
"categories": ["accounting", "legal", "real-estate"],
|
|
5
|
+
"shape": "organization-service",
|
|
6
|
+
"design": "standard",
|
|
7
|
+
"generatedHash": "7386e7f6ebcd8d291b75c265a428dec50612d25ae522cae1be611c4089a217a1",
|
|
8
|
+
"layout": {
|
|
9
|
+
"nav": "full",
|
|
10
|
+
"footer": "columns",
|
|
11
|
+
"announcementBanner": false,
|
|
12
|
+
"home": "default"
|
|
13
|
+
},
|
|
14
|
+
"theme": {
|
|
15
|
+
"corners": "square",
|
|
16
|
+
"eyebrowCase": "sentence",
|
|
17
|
+
"placeholders": "fill",
|
|
18
|
+
"checkmarks": "plain",
|
|
19
|
+
"colors": {
|
|
20
|
+
"light": {
|
|
21
|
+
"bg": "#ffffff",
|
|
22
|
+
"bgMuted": "#ffffff",
|
|
23
|
+
"surface": "#ffffff",
|
|
24
|
+
"border": "#000000",
|
|
25
|
+
"text": "#000000",
|
|
26
|
+
"textMuted": "#333333",
|
|
27
|
+
"primary": "#000000",
|
|
28
|
+
"primaryFg": "#ffffff",
|
|
29
|
+
"accent": "#000000"
|
|
30
|
+
},
|
|
31
|
+
"dark": {
|
|
32
|
+
"bg": "#000000",
|
|
33
|
+
"bgMuted": "#000000",
|
|
34
|
+
"surface": "#000000",
|
|
35
|
+
"border": "#ffffff",
|
|
36
|
+
"text": "#ffffff",
|
|
37
|
+
"textMuted": "#cccccc",
|
|
38
|
+
"primary": "#ffffff",
|
|
39
|
+
"primaryFg": "#000000",
|
|
40
|
+
"accent": "#ffffff"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"rows": [
|
|
45
|
+
{
|
|
46
|
+
"route": "/sign-in",
|
|
47
|
+
"kind": "route",
|
|
48
|
+
"pageType": null,
|
|
49
|
+
"schema": null,
|
|
50
|
+
"shell": "MarketingShell",
|
|
51
|
+
"chrome": "on",
|
|
52
|
+
"sections": []
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"route": "/sign-up",
|
|
56
|
+
"kind": "route",
|
|
57
|
+
"pageType": null,
|
|
58
|
+
"schema": null,
|
|
59
|
+
"shell": "MarketingShell",
|
|
60
|
+
"chrome": "on",
|
|
61
|
+
"sections": []
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"route": "/privacy",
|
|
65
|
+
"kind": "page",
|
|
66
|
+
"pageType": "PrivacyPage",
|
|
67
|
+
"schema": "PrivacyPage",
|
|
68
|
+
"shell": "MarketingShell",
|
|
69
|
+
"chrome": "on",
|
|
70
|
+
"sections": [
|
|
71
|
+
{
|
|
72
|
+
"type": "document-chassis",
|
|
73
|
+
"component": "DocumentChassis",
|
|
74
|
+
"options": {
|
|
75
|
+
"layout": "article"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"route": "/terms",
|
|
82
|
+
"kind": "page",
|
|
83
|
+
"pageType": "TermsPage",
|
|
84
|
+
"schema": "TermsPage",
|
|
85
|
+
"shell": "MarketingShell",
|
|
86
|
+
"chrome": "on",
|
|
87
|
+
"sections": [
|
|
88
|
+
{
|
|
89
|
+
"type": "document-chassis",
|
|
90
|
+
"component": "DocumentChassis",
|
|
91
|
+
"options": {
|
|
92
|
+
"layout": "article"
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"route": "/working-agreement",
|
|
99
|
+
"kind": "page",
|
|
100
|
+
"pageType": "WorkingAgreementPage",
|
|
101
|
+
"schema": "WorkingAgreementPage",
|
|
102
|
+
"shell": "MarketingShell",
|
|
103
|
+
"chrome": "on",
|
|
104
|
+
"sections": [
|
|
105
|
+
{
|
|
106
|
+
"type": "document-chassis",
|
|
107
|
+
"component": "DocumentChassis",
|
|
108
|
+
"options": {
|
|
109
|
+
"layout": "article"
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
}
|
package/templates/.env.example
CHANGED
|
@@ -462,13 +462,23 @@ CONTENT_ADAPTER=mdx
|
|
|
462
462
|
# SLACK_CONNECTION_KEY=
|
|
463
463
|
# SLACK_CONNECTION_KEY_PREVIOUS=
|
|
464
464
|
# The Organization that owns STRIPE_SECRET_KEY (#9285): with the multi-tenant
|
|
465
|
-
# module only the Organization whose id is
|
|
466
|
-
# means nobody.
|
|
465
|
+
# module selected in AILK_ENABLED_MODULES, only the Organization whose id is
|
|
466
|
+
# named here sells through it; unset means nobody. That holds whether or not
|
|
467
|
+
# the licence claims the module (#9968), so a downgraded deployment that still
|
|
468
|
+
# selects it must set this too. Without the module the single Organization
|
|
469
|
+
# owns it.
|
|
467
470
|
# STRIPE_ENV_ORGANIZATION_ID=
|
|
468
471
|
# HMAC-SHA256 key POST /v1/webhooks/calcom verifies the x-cal-signature-256
|
|
469
472
|
# header against (#6085). Unset makes the receiver answer 503 and accept
|
|
470
473
|
# nothing — the same fail-closed posture an unset STRIPE_WEBHOOK_SECRET has.
|
|
471
474
|
# CAL_WEBHOOK_SECRET=
|
|
475
|
+
# The prospect-channel archive cron's secret (#9855). Vercel Cron sends it as
|
|
476
|
+
# `Authorization: Bearer <CRON_SECRET>` to GET /v1/cron/prospect-channel-archive
|
|
477
|
+
# (the scheduling module; the scaffold's apps/api/vercel.json schedules it
|
|
478
|
+
# daily). Generate it with `openssl rand -hex 32`. Unset, or shorter than 32
|
|
479
|
+
# characters, makes the route answer 503 and archive nothing. The bin
|
|
480
|
+
# (`prospect-channel-archive:sweep`) needs no secret.
|
|
481
|
+
# CRON_SECRET=
|
|
472
482
|
# SCHEDULING_PUBLIC_BASE_URL=https://cal.com
|
|
473
483
|
# SCHEDULING_PROVIDER=cal
|
|
474
484
|
|
package/templates/CLAUDE.md
CHANGED
|
@@ -73,7 +73,7 @@ your module set; `pnpm architecture` enforces it, and a hook in
|
|
|
73
73
|
| Diff hygiene when editing existing files | `CONVENTIONS.md` § Commit + diff hygiene |
|
|
74
74
|
| Code style, testing, security | `CONVENTIONS.md` |
|
|
75
75
|
| Environment variables and what they unlock | `.env.example` |
|
|
76
|
-
| Deploy configuration | `apps/api/vercel.json`;
|
|
76
|
+
| Deploy configuration | `apps/api/vercel.json`; `/ailk:growth-ops deploy` |
|
|
77
77
|
| Verification before push | `pnpm preflight` (see § Checks) |
|
|
78
78
|
|
|
79
79
|
<!-- /ailk:template id="resource-map" -->
|
|
@@ -82,30 +82,30 @@ your module set; `pnpm architecture` enforces it, and a hook in
|
|
|
82
82
|
|
|
83
83
|
## Skills
|
|
84
84
|
|
|
85
|
-
The AI Launch Kit
|
|
86
|
-
this repo. `.claude/settings.json` names its marketplace and
|
|
87
|
-
Claude Code loads it once you trust this folder. To install it
|
|
88
|
-
these two lines in a Claude Code session:
|
|
85
|
+
The AI Launch Kit skill, `growth-ops`, comes from the `ailk` Claude Code
|
|
86
|
+
plugin, not from this repo. `.claude/settings.json` names its marketplace and
|
|
87
|
+
enables it, so Claude Code loads it once you trust this folder. To install it
|
|
88
|
+
by hand, run these two lines in a Claude Code session:
|
|
89
89
|
|
|
90
90
|
```
|
|
91
91
|
/plugin marketplace add working-theory-labs/claude-plugins
|
|
92
92
|
/plugin install ailk@working-theory
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
|
|
|
96
|
-
|
|
|
97
|
-
| `/ailk:
|
|
98
|
-
| `/ailk:
|
|
99
|
-
| `/ailk:
|
|
100
|
-
| `/ailk:
|
|
101
|
-
| `/ailk:
|
|
102
|
-
| `/ailk:
|
|
103
|
-
| `/ailk:
|
|
104
|
-
| `/ailk:
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
copies of these skills
|
|
108
|
-
keep scaffold-commerce, which the commerce module delivers.
|
|
95
|
+
| Command | Use when |
|
|
96
|
+
| ------------------------------ | ----------------------------------------------------------------- |
|
|
97
|
+
| `/ailk:growth-ops page add` | You need a new page, or extra JSON-LD on an existing one |
|
|
98
|
+
| `/ailk:growth-ops page rename` | A page should live at a different URL, or needs a new title |
|
|
99
|
+
| `/ailk:growth-ops page fill` | A page still carries `{placeholder}` copy |
|
|
100
|
+
| `/ailk:growth-ops audit` | You want the site's citation-readiness score and findings |
|
|
101
|
+
| `/ailk:growth-ops database` | Leads should land in a database inbox at `/leads` |
|
|
102
|
+
| `/ailk:growth-ops deploy` | You are ready to deploy to Vercel |
|
|
103
|
+
| `/ailk:growth-ops auth` | Visitors need to sign in (email, Google, GitHub) |
|
|
104
|
+
| `/ailk:growth-ops plan` | You are weighing Free against a paid level or the commerce add-on |
|
|
105
|
+
|
|
106
|
+
Ask in plain words and `growth-ops` picks the verb. An older scaffold also
|
|
107
|
+
carries its own copies of these jobs as skills: once the plugin is installed,
|
|
108
|
+
delete them; keep scaffold-commerce, which the commerce module delivers.
|
|
109
109
|
|
|
110
110
|
For design work, the third-party impeccable plugin is optional:
|
|
111
111
|
`/plugin marketplace add pbakaus/impeccable`, then
|
|
@@ -125,7 +125,7 @@ For design work, the third-party impeccable plugin is optional:
|
|
|
125
125
|
| `pnpm type-check` | Type errors in every workspace |
|
|
126
126
|
| `pnpm lint` | Lint errors in every workspace |
|
|
127
127
|
| `pnpm test` | Failing unit tests |
|
|
128
|
-
| `/ailk:
|
|
128
|
+
| `/ailk:growth-ops audit` | Missing or invalid JSON-LD, weak citation-readiness |
|
|
129
129
|
|
|
130
130
|
`.github/workflows/ci.yml` runs the preflight checks and `pnpm test` on every
|
|
131
131
|
pull request as the `preflight` status check. One step is yours: make
|