create-ailk 0.9.0 → 0.11.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.
Files changed (104) hide show
  1. package/README.md +10 -5
  2. package/dist/cli.js +17 -2
  3. package/dist/content-payloads.d.ts +37 -0
  4. package/dist/content-payloads.js +64 -0
  5. package/dist/lib/fetch-module.d.ts +3 -3
  6. package/dist/lib/fetch-module.js +11 -5
  7. package/dist/lib/fetch-template.d.ts +1 -1
  8. package/dist/lib/fetch-template.js +7 -4
  9. package/dist/lib/module-license-gate.d.ts +29 -23
  10. package/dist/lib/module-license-gate.js +98 -0
  11. package/dist/module-architecture.d.ts +9 -0
  12. package/dist/module-architecture.js +46 -14
  13. package/dist/parse-args.d.ts +2 -0
  14. package/dist/parse-args.js +7 -0
  15. package/dist/programmatic.js +12 -4
  16. package/dist/starter-grid.d.ts +4 -67
  17. package/dist/starter-grid.js +6 -84
  18. package/dist/surfaces.js +2 -3
  19. package/dist/template-manifest.d.ts +1 -1
  20. package/dist/template-manifest.js +5 -5
  21. package/package.json +6 -4
  22. package/release-scope.json +25 -0
  23. package/site-templates/designs/minimalism/sample/README.md +2 -2
  24. package/site-templates/designs/minimalism/sample/content/en/HomePage/home.mdx +1 -1
  25. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +4 -3
  26. package/templates/.claude/agents/web.md +10 -8
  27. package/templates/.claude/rules/architecture.md +10 -7
  28. package/templates/.claude/settings.json +12 -0
  29. package/templates/.env.example +24 -3
  30. package/templates/CLAUDE.md +38 -22
  31. package/templates/README.md +12 -12
  32. package/templates/apps/api/jest.config.cjs +1 -0
  33. package/templates/apps/api/package.json +1 -0
  34. package/templates/apps/mcp/CLAUDE.md +23 -10
  35. package/templates/apps/mcp/__tests__/catalog-drift.test.ts +160 -17
  36. package/templates/apps/mcp/__tests__/index.test.ts +118 -1
  37. package/templates/apps/mcp/__tests__/lib/commerce-claim.test.ts +425 -5
  38. package/templates/apps/mcp/__tests__/module-exclusion.test.ts +67 -3
  39. package/templates/apps/mcp/__tests__/server.test.ts +162 -1
  40. package/templates/apps/mcp/src/index.ts +35 -3
  41. package/templates/apps/mcp/src/lib/commerce-claim.ts +193 -24
  42. package/templates/apps/mcp/src/server.ts +77 -5
  43. package/templates/apps/mcp/src/tools/purchase.ts +1 -1
  44. package/templates/apps/web/app/.well-known/mcp.json/build-manifest.ts +15 -0
  45. package/templates/apps/web/app/.well-known/mcp.json/route.ts +10 -3
  46. package/templates/apps/web/src/lib/commerce-claim.ts +15 -6
  47. package/templates/content/_site.mdx +23 -4
  48. package/templates/database/CHANGELOG.md +7 -0
  49. package/templates/database/package.json +1 -1
  50. package/templates/package.json +2 -0
  51. package/versions.json +34 -33
  52. package/dist/modules/commerce/0.6.0.tar.gz +0 -0
  53. package/dist/modules/commerce/0.6.1.tar.gz +0 -0
  54. package/dist/modules/docs-blog/0.6.0.tar.gz +0 -0
  55. package/dist/modules/docs-blog/0.6.1.tar.gz +0 -0
  56. package/dist/modules/marketing-site/0.6.1.tar.gz +0 -0
  57. package/dist/modules/minimalism-site/0.6.1.tar.gz +0 -0
  58. package/dist/modules/multi-tenant/0.6.0.tar.gz +0 -0
  59. package/dist/modules/multi-tenant/0.6.1.tar.gz +0 -0
  60. package/dist/modules/saas-product/0.6.1.tar.gz +0 -0
  61. package/dist/modules/scheduling/0.6.0.tar.gz +0 -0
  62. package/dist/modules/scheduling/0.6.1.tar.gz +0 -0
  63. package/dist/modules/signal-collectors/0.6.0.tar.gz +0 -0
  64. package/dist/modules/signal-collectors/0.6.1.tar.gz +0 -0
  65. package/dist/modules/simple-service/0.6.1.tar.gz +0 -0
  66. package/dist/modules/workspace/0.6.0.tar.gz +0 -0
  67. package/dist/modules/workspace/0.6.1.tar.gz +0 -0
  68. package/site-templates/aliases/minimalism-site.TEMPLATE-MANIFEST.json +0 -538
  69. package/templates/.claude/skills/README.md +0 -37
  70. package/templates/.claude/skills/add-auth/SKILL.md +0 -261
  71. package/templates/.claude/skills/add-auth/references/env-vars.md +0 -98
  72. package/templates/.claude/skills/add-auth/references/oauth-app-setup.md +0 -172
  73. package/templates/.claude/skills/add-page/SKILL.md +0 -260
  74. package/templates/.claude/skills/add-page/references/page-types.md +0 -124
  75. package/templates/.claude/skills/add-page/scripts/site-pages.mjs +0 -87
  76. package/templates/.claude/skills/add-page/templates/comparison-page.mdx.tmpl +0 -99
  77. package/templates/.claude/skills/add-page/templates/content.mdx.tmpl +0 -18
  78. package/templates/.claude/skills/add-page/templates/flow-landing-page.mdx.tmpl +0 -43
  79. package/templates/.claude/skills/add-page/templates/report-page.mdx.tmpl +0 -52
  80. package/templates/.claude/skills/add-page/templates/reports-list.mdx.tmpl +0 -38
  81. package/templates/.claude/skills/check-site/SKILL.md +0 -235
  82. package/templates/.claude/skills/check-site/references/common-errors.md +0 -113
  83. package/templates/.claude/skills/check-site/references/findings-glossary.md +0 -140
  84. package/templates/.claude/skills/check-site/references/severity-levels.md +0 -114
  85. package/templates/.claude/skills/fill-page/SKILL.md +0 -199
  86. package/templates/.claude/skills/fill-page/profiles/plumbing.yaml +0 -80
  87. package/templates/.claude/skills/fill-page/references/section-sources.md +0 -41
  88. package/templates/.claude/skills/setup-database/SKILL.md +0 -156
  89. package/templates/.claude/skills/setup-database/scripts/verify-inbox.ts +0 -154
  90. package/templates/.claude/skills/setup-deploy/SKILL.md +0 -275
  91. package/templates/.claude/skills/setup-deploy/references/env-vars-template.md +0 -192
  92. package/templates/.claude/skills/setup-deploy/references/runbook-template.md +0 -204
  93. package/templates/.claude/skills/setup-deploy/references/vercel-json-template.md +0 -98
  94. package/templates/.claude/skills/update-page/SKILL.md +0 -117
  95. package/templates/.claude/skills/update-page/scripts/update-page.ts +0 -640
  96. package/templates/.claude/skills/update-page/templates/redirects.mjs.tmpl +0 -31
  97. package/templates/.claude/skills/upgrade-plan/SKILL.md +0 -121
  98. package/templates/.claude/skills/upgrade-plan/references/options.md +0 -24
  99. package/templates/.claude/skills/upgrade-plan/scripts/upgrade-plan.ts +0 -527
  100. package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-31.552Z-fallback-1789916251590-037e9005.json +0 -8
  101. package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-33.840Z-fallback-1789916253842-b52659e9.json +0 -12
  102. package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-33.844Z-fallback-1789916253845-6b266f8c.json +0 -12
  103. package/templates/apps/mcp/__tests__/lib/require-paid-tier.test.ts +0 -75
  104. package/templates/apps/mcp/src/lib/require-paid-tier.ts +0 -44
@@ -1,260 +0,0 @@
1
- ---
2
- name: add-page
3
- description: Add a new page to this AI Launch Kit site — a new service, a new "who it's for" page, a new booking page, a comparison against a competitor, a stand-alone landing page, or a whole route the site doesn't carry yet like /pricing — with its content file, its structured data and its nav link, in one confirm-before-write pass. Use this whenever the user wants a page that doesn't exist yet, even if they don't say "page" — "add a service for kitchen remodels", "I need a /pricing page", "make a page for dentists", "add a comparison with Acme", "we need a booking page for on-site estimates", "add a landing page for the spring offer", or a structured triple like "pricing — Pricing — plans and FAQ". Not for changing a page that already exists (fill-page writes content; update-page renames).
4
- ---
5
-
6
- # add-page
7
-
8
- Add a page this site can actually serve, at the path its routes really use,
9
- with a nav link, and nothing else. One plan, one confirmation, then write.
10
-
11
- The single most important fact about this site: **almost every page type
12
- already has its route.** Routes live in `apps/web/app/[locale]/…` and render
13
- whatever content file sits in `content/en/<Type>/`. So adding a page is
14
- usually **adding one content file**, not writing route code. Route code is
15
- only ever added by the `create-ailk add-route` command, never by hand — a
16
- hand-written route drifts from the template the site is built from and
17
- breaks the next `create-ailk` upgrade.
18
-
19
- ## Step 1 — Read what this site can serve
20
-
21
- Run, from the site's root:
22
-
23
- ```bash
24
- node .claude/skills/add-page/scripts/site-pages.mjs
25
- ```
26
-
27
- It prints the site's shape, design and level (from `.ailk-site-manifest.json`),
28
- then one row per page type this site renders: its route, whether it takes new
29
- pages (a route with a `[slug]` does; a fixed route like `/about` is one page),
30
- and the pages already written. Add `--json` if you want to parse it.
31
-
32
- Treat this output as the truth about the site. `references/page-types.md`
33
- lists every page type AI Launch Kit knows — the vocabulary — but many of them
34
- are not on this site. Never offer a page type the script doesn't list unless
35
- you are also going to add its route (Step 3, case D).
36
-
37
- Then read `.ailk-site-manifest.json` at the site root yourself, the site's
38
- roster of record:
39
-
40
- - `modules` — the modules this site has. Add any module the `# Modules:`
41
- line of `architecture.yaml` names that `modules` does not: `create-ailk
42
- add-module` records a module it adds there. With no manifest, or none
43
- carrying `modules`, that line is the set.
44
- - `rows` — every route the site already has (`route`) and the page type
45
- each renders (`pageType`).
46
- - `level` — present only when the scaffold derived it from the licence key.
47
- It never decides what to offer; `modules` does.
48
-
49
- A page type renders on this site only when the **Module** column of
50
- `references/page-types.md` names a module in that set. Never offer,
51
- suggest or write any other page type: a type whose Module the site lacks is
52
- case E, never case D.
53
-
54
- ## Step 2 — Work out which page type the user wants
55
-
56
- The user may give a structured triple (`<slug> — <Type> — <purpose>`) or plain
57
- words. Resolve plain words to a type with `references/page-types.md`: an exact
58
- slug match first (`pricing` → `Pricing`), then a prefix (`services/…` →
59
- `Service`), then the purpose ("company story" → `AboutPage`). If two types
60
- fit, or none does, ask — a wrong type gives the page the wrong structured
61
- data, which is what search and answer engines read.
62
-
63
- Also settle the slug: lowercase, hyphenated, from the user's words
64
- (`kitchen-remodels`, not `kitchen_remodels` or `KitchenRemodels`).
65
-
66
- ## Step 3 — Decide which case this is
67
-
68
- Check case C first: if the page the user describes already exists (the type is on a fixed route and its one page is in the "existing" column, or the slug is taken), stop there — nothing else in this skill applies.
69
-
70
- | Case | When | What you'll write |
71
- |---|---|---|
72
- | **A. New page on an existing route** | The type is listed and takes new pages (`Service`, `UseCase`, `PortfolioItem`, `BookCall`, `ThankYouPage`, …) | One content file: `content/en/<Type>/<slug>.mdx`. Plus a nav link if the page belongs in the nav. |
73
- | **B. Stand-alone landing page** | A one-off page at `/<slug>` that isn't any specific type (an offer, a campaign) | One content file: `content/en/LandingPage/<slug>.mdx`. The site's catch-all route serves it. |
74
- | **C. The page already exists** | A fixed-route type already has its page (`/about`, `/contact`, `/faq`), or `<slug>` is taken | Nothing. Say so. To change its words, use `fill-page`; to rename it or move its URL, use `update-page`. |
75
- | **D. The route isn't on this site** | The type exists in `page-types.md` but not in the script's output (e.g. `/pricing` on a person-service site) | First the route: `pnpm create ailk@latest add-route <route-id>` (run in the site root). `<route-id>` is the route family's name as `pnpm create ailk@latest --help` lists it under `--surfaces` — usually the first path segment (`pricing`, `alternatives`, `careers`). Then continue as case A (or case C for a fixed route — `add-route` seeds its page). |
76
- | **E. Needs a module the site doesn't have** | The type's **Module** in `page-types.md` is not in the site's modules (Step 1), or `add-route` refuses, naming a module (e.g. docs, blog, scheduling-backed booking) | Nothing yet. Tell the user which module renders it and which level carries it (the **Level** column), and point at the `upgrade-plan` skill. Don't try to recreate the module's routes by hand. |
77
-
78
- Why case B uses `LandingPage` rather than a new route: a route directory is
79
- template code the site inherits and upgrades; a content file is the owner's.
80
- Keeping one-off pages as content keeps upgrades clean.
81
-
82
- Comparison pages (`/vs/<competitor>`, listed on `/alternatives`) are case A on
83
- the `alternatives` route family, or case D when the site doesn't carry it yet
84
- (`add-route alternatives`). The `/alternatives` hub lists every comparison
85
- automatically — never hand-list them on the hub's page.
86
-
87
- Reports and flow landing pages are content on routes the **signal-collectors
88
- module** owns — see "Reports" and "Flow landing pages" below.
89
-
90
- ## Step 4 — Draft the content file
91
-
92
- Copy the shape of a page the site already has **of the same type** — pick one
93
- from the "existing" column (e.g. `content/en/Service/first-engagement.mdx`).
94
- Its frontmatter is exactly what that type's route and structured-data builder
95
- expect, so matching it is how the new page renders and emits valid JSON-LD.
96
- Keep the same keys and section structure; replace the words.
97
-
98
- - Pick the page to copy with care: if the only page of this type is an odd one
99
- (a hub slug, a stub), start from `templates/content.mdx.tmpl` instead and
100
- borrow the section shapes from `content/en/HomePage/home.mdx`.
101
- - Set `slug` to the new slug and write a real `title` and `description` from
102
- what the user said. `description` becomes the meta description and feeds the
103
- structured data, so make it a true one-sentence summary.
104
- - Use the site's own name from `content/_site.mdx` wherever the page names
105
- the business (a comparison's "us" side, a CTA) — don't leave `{Your Name}`
106
- when the answer is on disk.
107
- - A time-limited offer carries its end date in structured data
108
- (`schema: { offers: { validThrough: "2027-05-31" } }`) and says it in the
109
- copy, so the page doesn't keep advertising an expired deal.
110
- - Where you don't have the owner's facts (prices, names, results), write
111
- clearly-marked placeholder copy — never invent facts about their business.
112
- Offer `fill-page` afterwards to write it properly.
113
- - If no page of this type exists yet, start from `templates/content.mdx.tmpl`
114
- (or `templates/comparison-page.mdx.tmpl` for a comparison) and fill in the
115
- `type` from `page-types.md`. Reports and flow landing pages have their own
116
- templates — see their sections below.
117
- - Extra structured data (an `Offer`, a `Review`, an `author`) goes in the
118
- frontmatter `schema:` block — see "Adding schema to a page" below. Don't
119
- hand-write JSON-LD anywhere.
120
-
121
- ## Step 5 — Link the page from its hub and the nav
122
-
123
- **The hub page.** Detail types sit under a list page: `Service` under
124
- `ServicesList` (`/services`), `UseCase` under `UseCasesList` (`/for`),
125
- `PortfolioItem` under `PortfolioList` (`/portfolio`). Open the hub's content
126
- file. If it lists its entries by hand (an `items` section with one entry per
127
- page, each with an `href`), add an entry for the new page in the same shape —
128
- otherwise visitors can't find it. The comparisons hub (`/alternatives`) and the
129
- reports hub (`/reports`) are the exceptions: they list their pages automatically.
130
-
131
- **The nav.**
132
-
133
- The site nav is `apps/web/lib/nav-config.ts`; its labels are keys in the
134
- `nav` section of `apps/web/messages/<locale>.json` (one file per language
135
- under `apps/web/messages/`).
136
-
137
- - A detail page under a list (a new service, a new persona) usually belongs in
138
- that list's flyout or group in `nav-config.ts`. A stand-alone landing page
139
- usually doesn't belong in the nav at all — ask if unsure. If it isn't in the
140
- nav or a hub, tell the user how visitors will reach it (a link from the home
141
- page, an email, an ad), because nothing on the site points to it yet.
142
- - Name a new `labelKey` after the page in camelCase (`kitchenRemodels`), not
143
- after its position (`service4`), so it still makes sense after reordering.
144
- - A flyout may list the same links twice — once in `features` (the compact
145
- view) and once in `groups` (the grouped view). Add the link to both.
146
- - To add a link: add an entry with the new `labelKey` and the locale-free path
147
- (`/services/kitchen-remodels`, not `/en/…`), and add that key with its label
148
- to **every** messages file (a site with one language has one file). A key
149
- missing from one language breaks that language's nav.
150
- - Know the trade-off and tell the user once: `create-ailk` regenerates the nav
151
- when a route or module is added, but only while `nav-config.ts` is exactly as
152
- generated. Once hand-edited, later `add-route`/`add-module` runs leave it
153
- alone and print the change they wanted instead.
154
-
155
- ## Step 6 — Show the plan, confirm once, write
156
-
157
- Show every file you will create or change — the content file in full, and the
158
- exact lines added to the hub page, `nav-config.ts` and each messages file — plus any
159
- `add-route` command you'll run first. Wait for one confirmation. Then run the
160
- command (if any) and write the files. Don't write anything before the user
161
- confirms, and don't touch any other file.
162
-
163
- ## Step 7 — Check it
164
-
165
- Run `pnpm build` from the site root. Then tell the user:
166
-
167
- - the new page's URL (with the locale, e.g. `/en/services/kitchen-remodels`)
168
- - that `fill-page` can write its real copy, and `check-site` can score it
169
- for search and answer-engine readiness.
170
-
171
- If the build fails, the usual cause is frontmatter that doesn't match the
172
- type — compare the new file with the page you copied in Step 4.
173
-
174
- ## Reports
175
-
176
- `ReportPage` (the `/reports/<slug>` report reader) is a hub-and-spoke family
177
- listed on the `/reports` hub (`ReportsList`). Its routes are
178
- **module-owned code** (the signal-collectors module's own
179
- `app/[locale]/reports/[slug]/page.tsx` and `app/[locale]/reports/page.tsx`),
180
- so this skill writes report **content only**, never report route code
181
- (spec 6892 D5).
182
-
183
- When the resolved type is `ReportPage` — a structured triple naming it, or
184
- free text like "add a report on Q3 results" — do this instead of Steps 3–4:
185
-
186
- 1. Refuse when `app/[locale]/reports/[slug]/page.tsx` is absent, naming the
187
- `signal-collectors` module: "This site has no report reader yet — run
188
- `create-ailk add-module signal-collectors` first."
189
- 2. Write `content/en/ReportPage/<slug>.mdx` from
190
- `templates/report-page.mdx.tmpl` (substituting `{{SLUG}}`, `{{TITLE}}`,
191
- `{{DESCRIPTION}}`, `{{DATE_PUBLISHED}}` — an ISO date — and
192
- `{{DATE_LABEL}}` — the display string, e.g. "March 2025"). Refuse only
193
- when that file already exists.
194
- 3. When `content/en/ReportsList/reports.mdx` is absent, write it from
195
- `templates/reports-list.mdx.tmpl` (substituting `{{TITLE}}`/`{{DESCRIPTION}}`
196
- for the hub's own copy).
197
-
198
- The hub's own card list is **generated at request time** from whichever
199
- `ReportPage` seeds exist (`app/[locale]/reports/page.tsx` injects them) —
200
- never hand-list reports on the hub's MDX. Steps 5–7 apply as usual.
201
-
202
- ## Flow landing pages
203
-
204
- A flow landing page is a single, standalone `LandingPage` whose CTA opens a
205
- named flow at `/flows/<slug>/start` — the referral-request shape (spec 6892
206
- D3). It belongs to no hub, but like the report family its route is
207
- **module-owned code** (the signal-collectors module's own
208
- `app/[locale]/flows/[slug]/start/page.tsx`), so this skill writes content
209
- only.
210
-
211
- When the consumer asks for a flow landing page — "add a referral page",
212
- "add a landing page for the intake flow" — given a flow slug:
213
-
214
- 1. Refuse when `app/[locale]/flows/[slug]/start/page.tsx` is absent, naming
215
- the `signal-collectors` module: "This site has no flow engine yet — run
216
- `create-ailk add-module signal-collectors` first."
217
- 2. Refuse when `content/en/LandingPage/<slug>.mdx` already exists — this
218
- is a single standalone page, not a family member with its own detail
219
- route.
220
- 3. Write `content/en/LandingPage/<slug>.mdx` from
221
- `templates/flow-landing-page.mdx.tmpl`, substituting `{{SLUG}}`,
222
- `{{TITLE}}`, `{{DESCRIPTION}}` as usual and `{{FLOW_SLUG}}` — the flow's
223
- own slug, which may differ from the page's `{{SLUG}}`.
224
-
225
- This skill writes no flow definition (`database/flows/<slug>.flow.json`) —
226
- that is a separate artifact, seeded through the flow engine, not through a
227
- page.
228
-
229
- ## Adding schema to a page
230
-
231
- Every page's frontmatter has an escape hatch, `schema:`, that the page's
232
- structured-data builder merges on top of what it derives (frontmatter wins).
233
- Use it to add sub-entities or enrich fields, on a new page or an existing one:
234
-
235
- ```yaml
236
- ---
237
- type: Service
238
- slug: kitchen-remodels
239
- title: Kitchen remodels
240
- description: Full kitchen remodels, from design to final inspection.
241
- schema:
242
- offers:
243
- "@type": Offer
244
- priceCurrency: USD
245
- price: "15000"
246
- ---
247
- ```
248
-
249
- On types that wrap a described entity in a page (`Service`, `Product`,
250
- `BlogPosting`, `JobPosting`), most keys land on the entity; on others they
251
- merge at the top level. This is also how a page gets a supplementary type
252
- with no page type of its own — `Offer`, `Review`, `AggregateRating`.
253
-
254
- ## What this skill does not do
255
-
256
- - Write or copy route code by hand — `create-ailk add-route` does that.
257
- - Change an existing page's words (`fill-page`), name or URL (`update-page`).
258
- - Add a module or change the site's level (`upgrade-plan` explains; the
259
- owner decides).
260
- - Edit `_site.mdx`, the design, or another page's content.
@@ -1,124 +0,0 @@
1
- <!-- GENERATED — do not hand-edit. Regenerate with:
2
- cd packages/create-ailk && pnpm page-types:build
3
- Source: packages/schema/src/registry/entries.ts
4
- Generator: packages/create-ailk/scripts/generate-page-types-reference.ts
5
- -->
6
-
7
- # Page-Type → schema.org Registry Reference
8
-
9
- The full page-type catalog the `add-page` skill reads at runtime to
10
- resolve a slug or page-purpose hint to a family, chassis, schema.org
11
- `@type`, and route convention. Generated from
12
- `packages/schema/src/registry/entries.ts` — every registered page type
13
- appears exactly once, list/detail pairs are shown together, and no row
14
- here can name a type the registry doesn't have.
15
-
16
- The skill resolves in this order:
17
-
18
- 1. **Exact slug match** — `pricing` → `Pricing`.
19
- 2. **Slug-prefix match** — `blog/<anything>` → `BlogPosting`.
20
- 3. **Free-text hint match** — "company story" / "team" → `AboutPage`.
21
- 4. **Ask** — if nothing matches, ask the consumer rather than guessing.
22
-
23
- **Level** and **Module** name what a page type needs: the module whose
24
- code renders it, and the level that module comes with. Read the site's
25
- `.ailk-site-manifest.json` `modules` first — a page type whose Module is
26
- not in that list is one this site cannot render, so it is never offered;
27
- name its module and level instead (the `upgrade-plan` skill explains the
28
- levels). The level is the module's, never a claim about this site's key.
29
-
30
- `<slug>` in a route below is the page's own slug (e.g. `hello-world`);
31
- `<Type>` families using two-layer builders route supplementary schema
32
- through the `schema:` frontmatter override — see this skill's
33
- [Adding schema to an existing page](../SKILL.md#adding-schema-to-an-existing-page) section, or `packages/schema/README.md` for the full
34
- override contract.
35
-
36
- ## Core
37
-
38
- | Key | `@type` | Family | Chassis | Route | Level | Module |
39
- | --- | --- | --- | --- | --- | --- | --- |
40
- | `HomePage` | `HomePage` | core | home | / | Free | `marketing` |
41
- | `AboutPage` | `AboutPage` | core | detail | /about | Free | `marketing` |
42
- | `ContactPage` | `ContactPage` | core | detail | /contact | Free | `marketing` |
43
- | `PrivacyPage` | `WebPage` | core | detail | /privacy | Free | `marketing` |
44
- | `TermsPage` | `WebPage` | core | detail | /terms | Free | `marketing` |
45
- | `PurchasePolicyPage` | `WebPage` | core | detail | /purchase-policy | Free | `marketing` |
46
- | `WorkingAgreementPage` | `WebPage` | core | detail | /working-agreement | Free | `marketing` |
47
- | `LandingPage` | `WebPage` | core | home | /<slug> | Free | `marketing` |
48
- | `ThankYouPage` | `WebPage` | core | detail | /thanks/<slug> | Free | `marketing` |
49
- | `BookCall` | `WebPage` | core | home | /meet/:slug | Pro | `scheduling` |
50
- | `NewsletterSignup` | `WebPage` | core | home | /newsletter | Free | `marketing` |
51
- | `DemoRequest` | `WebPage` | core | home | /demo-request | Free | `marketing` |
52
- | `TrialSignup` | `WebPage` | core | home | /trial-signup | Free | `marketing` |
53
- | `Waitlist` | `WebPage` | core | home | /waitlist | Free | `marketing` |
54
- | `Assessment` | `WebApplication` | core | home | /assessment | Pro | `signal-collectors` |
55
- | `Calculator` | `WebApplication` | core | home | /calculator | Pro | `signal-collectors` |
56
- | `CampaignLP` | `WebPage` | core | home | /<slug> | Free | `marketing` |
57
-
58
- ## Identity
59
-
60
- | Key | `@type` | Family | Chassis | Route | Level | Module |
61
- | --- | --- | --- | --- | --- | --- | --- |
62
- | `Organization` | `Organization` | identity | detail | — site-wide identity — emitted on the home page or in a layout | Free | `marketing` |
63
- | `Person` | `Person` | identity | detail | — site-wide identity — emitted on the home page or in a layout | Free | `marketing` |
64
- | `BreadcrumbList` | `BreadcrumbList` | identity | detail | — site-wide — attached alongside another page's own schema | Free | `marketing` |
65
- | `ContactPoint` | `ContactPoint` | identity | detail | — site-wide — embedded in `Organization` or `Person` | Free | `marketing` |
66
-
67
- ## Canonical
68
-
69
- | Key | `@type` | Family | Chassis | Route | Level | Module |
70
- | --- | --- | --- | --- | --- | --- | --- |
71
- | `ServicesList` | `CollectionPage` | canonical-list | list | /services | Free | `marketing` |
72
- | `Service` | `Service` | canonical-detail | detail | /services/<slug> | Free | `marketing` |
73
- | `ProductsList` | `CollectionPage` | canonical-list | list | /products | Free | `marketing` |
74
- | `Product` | `Product` | canonical-detail | detail | /products/<slug> | Free | `marketing` |
75
- | `ProjectsList` | `CollectionPage` | canonical-list | list | /projects | Free | `marketing` |
76
- | `Project` | `CreativeWork` | canonical-detail | detail | /projects/<slug> | Free | `marketing` |
77
- | `FAQPage` | `FAQPage` | canonical-list | list | /faq | Free | `marketing` |
78
- | `CareersList` | `CollectionPage` | canonical-list | list | /careers | Free | `marketing` |
79
- | `JobPosting` | `JobPosting` | canonical-detail | detail | /careers/<slug> | Free | `marketing` |
80
- | `UseCasesList` | `CollectionPage` | canonical-list | list | /for | Free | `marketing` |
81
- | `UseCase` | `WebPage` | canonical-detail | detail | /for/<slug> | Free | `marketing` |
82
- | `PortfolioList` | `CollectionPage` | canonical-list | list | /portfolio | Free | `marketing` |
83
- | `PortfolioItem` | `WebPage` | canonical-detail | detail | /portfolio/<slug> | Free | `marketing` |
84
- | `FeaturesList` | `CollectionPage` | canonical-list | list | /features | Free | `marketing` |
85
- | `Feature` | `WebPage` | canonical-detail | detail | /features/<slug> | Free | `marketing` |
86
- | `ComparisonsList` | `CollectionPage` | canonical-list | list | /alternatives | Free | `marketing` |
87
- | `ComparisonPage` | `WebPage` | canonical-detail | detail | /vs/<slug> | Free | `marketing` |
88
- | `ReportsList` | `CollectionPage` | canonical-list | list | /reports | Free | `marketing` |
89
- | `ReportPage` | `Report` | canonical-detail | detail | /reports/<slug> | Pro | `signal-collectors` |
90
- | `TestimonialsList` | `CollectionPage` | canonical-list | list | /testimonials | Free | `marketing` |
91
- | `ClientsList` | `CollectionPage` | canonical-list | list | /clients | Free | `marketing` |
92
- | `DownloadsList` | `CollectionPage` | canonical-list | list | /downloads | Free | `marketing` |
93
- | `EventsList` | `CollectionPage` | canonical-list | list | /events | Free | `marketing` |
94
- | `Event` | `Event` | canonical-detail | detail | /events/<slug> | Free | `marketing` |
95
- | `CoursesList` | `CollectionPage` | canonical-list | list | /courses | Free | `marketing` |
96
- | `Course` | `Course` | canonical-detail | detail | /courses/<slug> | Free | `marketing` |
97
- | `TeamList` | `CollectionPage` | canonical-list | list | /team | Free | `marketing` |
98
- | `TeamMember` | `ProfilePage` | canonical-detail | detail | /team/<slug> | Free | `marketing` |
99
- | `PartnersList` | `CollectionPage` | canonical-list | list | /partners | Free | `marketing` |
100
- | `Partner` | `AboutPage` | canonical-detail | detail | /partners/<slug> | Free | `marketing` |
101
- | `IntegrationsList` | `CollectionPage` | canonical-list | list | /integrations | Free | `marketing` |
102
- | `Integration` | `WebPage` | canonical-detail | detail | /integrations/<slug> | Free | `marketing` |
103
- | `GlossaryList` | `CollectionPage` | canonical-list | list | /glossary | Free | `marketing` |
104
- | `LocationsList` | `CollectionPage` | canonical-list | list | /locations | Free | `marketing` |
105
- | `Location` | `Place` | canonical-detail | detail | /locations/<slug> | Free | `marketing` |
106
- | `DocsList` | `CollectionPage` | canonical-list | list | /docs | Pro | `docs-blog` |
107
- | `SoftwareProduct` | `WebPage` | canonical-detail | detail | /software/<slug> | Free | `marketing` |
108
- | `ServiceAreaPage` | `WebPage` | canonical-detail | detail | /service-areas/<slug> | Free | `marketing` |
109
- | `Pricing` | `WebPage` | canonical-detail | detail | /pricing | Free | `marketing` |
110
-
111
- ## Editorial
112
-
113
- | Key | `@type` | Family | Chassis | Route | Level | Module |
114
- | --- | --- | --- | --- | --- | --- | --- |
115
- | `BlogList` | `Blog` | editorial-list | list | /blog | Free | `marketing` |
116
- | `BlogPosting` | `BlogPosting` | editorial-detail | detail | /blog/<slug> | Pro | `docs-blog` |
117
- | `ResourcesHub` | `CollectionPage` | editorial-list | list | /resources | Free | `marketing` |
118
- | `ResourceDetail` | `Article` | editorial-detail | detail | /resources/<slug> | Free | `marketing` |
119
- | `CaseStudiesList` | `CollectionPage` | editorial-list | list | /case-studies | Free | `marketing` |
120
- | `CaseStudy` | `Article` | editorial-detail | detail | /case-studies/<slug> | Free | `marketing` |
121
- | `HowTo` | `HowTo` | editorial-detail | detail | /how-to/<slug> | Free | `marketing` |
122
- | `Whitepaper` | `Article` | editorial-detail | detail | /whitepapers/<slug> | Free | `marketing` |
123
- | `TechArticle` | `TechArticle` | editorial-detail | detail | /tech-articles/<slug> | Free | `marketing` |
124
- | `DocsDetail` | `TechArticle` | editorial-detail | detail | /docs/<slug> | Pro | `docs-blog` |
@@ -1,87 +0,0 @@
1
- #!/usr/bin/env node
2
- // site-pages.mjs — report what THIS site can serve, read from the site itself.
3
- //
4
- // Usage: node .claude/skills/add-page/scripts/site-pages.mjs [--json] [<site-root>]
5
- //
6
- // It scans apps/web/app/[locale]/**/page.tsx for the page type each route
7
- // renders (the `type: "X"` literal in renderPage / renderPageMetadata, or a
8
- // getMdxSource("X", …) call), then pairs each type with its content folder and
9
- // the pages already written there. It also echoes .ailk-site-manifest.json's
10
- // shape, design and (when recorded) level. Read-only: it never writes.
11
- import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
12
- import { join, relative, sep } from "node:path";
13
-
14
- const args = process.argv.slice(2);
15
- const asJson = args.includes("--json");
16
- const root = args.find((a) => !a.startsWith("--")) ?? process.cwd();
17
- const appRoot = join(root, "apps/web/app/[locale]");
18
- if (!existsSync(appRoot)) {
19
- console.error(`site-pages: ${appRoot} not found — run from the site's root.`);
20
- process.exit(2);
21
- }
22
-
23
- // Types whose content lives outside content/<locale>/<Type>.
24
- const CONTENT_OVERRIDES = {
25
- BlogPosting: "apps/web/content/en/blog",
26
- BlogList: "apps/web/content/en/blog",
27
- DocsDetail: "apps/web/content/en/docs",
28
- DocsList: "apps/web/content/en/docs",
29
- };
30
-
31
- function walk(dir, out = []) {
32
- for (const name of readdirSync(dir)) {
33
- const abs = join(dir, name);
34
- if (name === "__tests__" || name === "node_modules") continue;
35
- if (statSync(abs).isDirectory()) walk(abs, out);
36
- else if (name === "page.tsx") out.push(abs);
37
- }
38
- return out;
39
- }
40
-
41
- const routes = [];
42
- for (const file of walk(appRoot)) {
43
- const src = readFileSync(file, "utf8");
44
- const types = new Set();
45
- for (const m of src.matchAll(/type:\s*"([A-Z][A-Za-z]+)"/g)) types.add(m[1]);
46
- for (const m of src.matchAll(/getMdxSource\(\s*"([A-Z][A-Za-z]+)"/g)) types.add(m[1]);
47
- const dir = relative(appRoot, file).split(sep).slice(0, -1);
48
- // Route groups like (auth) do not appear in the URL.
49
- const path = "/" + dir.filter((s) => !/^\(.*\)$/.test(s)).join("/");
50
- const dynamic = dir.some((s) => s.startsWith("["));
51
- routes.push({ route: path === "/" ? "/" : path, file: relative(root, file), types: [...types], dynamic });
52
- }
53
-
54
- const byType = {};
55
- for (const r of routes) {
56
- for (const t of r.types) {
57
- const entry = (byType[t] ??= { type: t, routes: [], contentDir: CONTENT_OVERRIDES[t] ?? `content/en/${t}`, pages: [] });
58
- entry.routes.push(r.route);
59
- }
60
- }
61
- for (const e of Object.values(byType)) {
62
- const abs = join(root, e.contentDir);
63
- if (existsSync(abs)) {
64
- e.pages = readdirSync(abs).filter((f) => /\.mdx?$/.test(f)).map((f) => f.replace(/\.mdx?$/, ""));
65
- }
66
- // A type served by a dynamic route takes new pages; a static-only type is one page.
67
- e.takesNewPages = routes.some((r) => r.dynamic && r.types.includes(e.type));
68
- }
69
-
70
- let manifest = null;
71
- const manifestPath = join(root, ".ailk-site-manifest.json");
72
- if (existsSync(manifestPath)) {
73
- const m = JSON.parse(readFileSync(manifestPath, "utf8"));
74
- manifest = { shape: m.shape ?? null, design: m.design ?? null, level: m.level ?? null, modules: m.modules ?? m.requires ?? null };
75
- }
76
-
77
- const result = { manifest, types: Object.values(byType).sort((a, b) => a.type.localeCompare(b.type)) };
78
- if (asJson) {
79
- console.log(JSON.stringify(result, null, 2));
80
- } else {
81
- if (manifest) console.log(`shape: ${manifest.shape} design: ${manifest.design} level: ${manifest.level ?? "(not recorded)"}`);
82
- console.log("");
83
- console.log("type".padEnd(22) + "route(s)".padEnd(34) + "new pages?".padEnd(12) + "existing");
84
- for (const e of result.types) {
85
- console.log(e.type.padEnd(22) + e.routes.join(" ").padEnd(34) + (e.takesNewPages ? "yes" : "no").padEnd(12) + (e.pages.join(", ") || "—"));
86
- }
87
- }
@@ -1,99 +0,0 @@
1
- ---
2
- type: ComparisonPage
3
- slug: {{SLUG}}
4
- placeholder: true
5
- title: {{TITLE}}
6
- description: {{DESCRIPTION}}
7
- sections:
8
- [
9
- {
10
- "id": "hero",
11
- "type": "hero",
12
- "data":
13
- {
14
- "eyebrow": "Comparisons",
15
- "headline": "{{TITLE}}",
16
- "subheadline": "{{DESCRIPTION}}",
17
- "primaryCta": { "label": "{Primary CTA}", "href": "/contact" },
18
- },
19
- },
20
- {
21
- "id": "decision-split",
22
- "type": "decision-split",
23
- "data":
24
- {
25
- "headline": "{Which one fits you}",
26
- "panels":
27
- [
28
- {
29
- "title": "{{TITLE}}",
30
- "checklist":
31
- [
32
- "{Reason 1 to choose us}",
33
- "{Reason 2 to choose us}",
34
- "{Reason 3 to choose us}",
35
- ],
36
- },
37
- {
38
- "title": "{The alternative}",
39
- "glyph": "cross",
40
- "checklist":
41
- [
42
- "{Reason 1 to choose them instead}",
43
- "{Reason 2 to choose them instead}",
44
- ],
45
- },
46
- ],
47
- },
48
- },
49
- {
50
- "id": "comparison-table",
51
- "type": "comparison-table",
52
- "data":
53
- {
54
- "headline": "{How they compare}",
55
- "columns":
56
- [
57
- { "key": "us", "name": "{{TITLE}}", "highlighted": true },
58
- { "key": "them", "name": "{The alternative}" },
59
- ],
60
- "groups":
61
- [
62
- {
63
- "groupName": "{Core features}",
64
- "rows":
65
- [
66
- {
67
- "featureName": "{Feature 1}",
68
- "values": { "us": true, "them": false },
69
- },
70
- {
71
- "featureName": "{Feature 2}",
72
- "values": { "us": true, "them": true },
73
- },
74
- ],
75
- },
76
- ],
77
- },
78
- },
79
- {
80
- "id": "cta",
81
- "type": "cta",
82
- "data":
83
- {
84
- "variant": "box",
85
- "headline": "{Ready to switch?}",
86
- "description": "{One sentence on what happens next}",
87
- "primaryCta": { "label": "{Primary CTA}", "href": "/contact" },
88
- "secondaryCta":
89
- {
90
- "label": "See all alternatives",
91
- "href": "/alternatives",
92
- "variant": "outline",
93
- },
94
- },
95
- },
96
- ]
97
- ---
98
-
99
- {/* Rendered from frontmatter `sections[]` by the /vs/<slug> route (D10 — the generic detail chassis: hero, decision-split, comparison-table, cta; no dedicated comparison section). Listed automatically by the /alternatives hub. Placeholder copy — replace every {brace} with your own words before you launch. */}
@@ -1,18 +0,0 @@
1
- ---
2
- type: {{TYPE}}
3
- slug: {{SLUG}}
4
- title: {{TITLE}}
5
- description: {{DESCRIPTION}}
6
- ---
7
-
8
- {/*
9
- Written by the add-page skill, used only when this site had no other page of
10
- this type to copy. The frontmatter drives the page's route and its JSON-LD:
11
- keep `type` as it is, and make `description` a true one-sentence summary.
12
- Run `fill-page` to write the real copy, then `check-site` to score it.
13
- Extra structured data goes in a `schema:` block — see the add-page skill.
14
- */}
15
-
16
- # {{TITLE}}
17
-
18
- {{DESCRIPTION}}
@@ -1,43 +0,0 @@
1
- ---
2
- type: LandingPage
3
- slug: {{SLUG}}
4
- placeholder: true
5
- title: {{TITLE}}
6
- description: {{DESCRIPTION}}
7
- sections:
8
- [
9
- {
10
- "id": "hero",
11
- "type": "hero",
12
- "data":
13
- {
14
- "variant": "centered",
15
- "eyebrow": "{{TITLE}}",
16
- "headline": "{{TITLE}}",
17
- "subheadline": "{{DESCRIPTION}}",
18
- "primaryCta":
19
- { "label": "{Get started →}", "href": "/flows/{{FLOW_SLUG}}/start" },
20
- "ctaSize": "xl",
21
- },
22
- },
23
- {
24
- "id": "request",
25
- "type": "cta",
26
- "data":
27
- {
28
- "variant": "minimal",
29
- "headline": "{Ready?}",
30
- "primaryCta":
31
- { "label": "{Get started →}", "href": "/flows/{{FLOW_SLUG}}/start" },
32
- },
33
- },
34
- ]
35
- ---
36
-
37
- {/* This page's CTA opens the `{{FLOW_SLUG}}` flow at
38
- /flows/{{FLOW_SLUG}}/start — a signal-collectors module route, not a page
39
- this seed owns. The flow definition itself
40
- (`database/flows/{{FLOW_SLUG}}.flow.json`) is a separate artifact this
41
- skill does not write; seed it via the flow engine before this page's CTA
42
- resolves. Placeholder copy — replace every {brace} with your own words
43
- before you launch. */}