create-ailk 0.28.1 → 0.29.1

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.
@@ -124,16 +124,18 @@ Every tool maps 1:1 to an `apps/api` endpoint. When you change a tool, change th
124
124
  **Maintenance (2):** `audit`, `create_project` (stdio only, #9965 D13)
125
125
  **Flow (2):** `get_flow`, `submit_flow_step` — module-gated (`module: "signal-collectors"`, D-48, #5366) and licence-feature-tagged (`feature: "flows-deliverables"`, #7136): identity-blind like every tool in this list, registered only when the signal-collectors module is present, and gated on the deployment's `flows-deliverables` licence feature under `MCP_FEATURE_GATE_MODE`, the same shape Booking below has. `submit_flow_step` takes the HTTP write branch (its own daily cap, `MCP_ABUSE_SUBMIT_FLOW_STEP_DAILY_CAP`, default 50 — #8071)
126
126
  **Booking (4):** `list_availability`, `get_event_meta`, `list_meetings`, `create_booking` — module-gated (`module: "scheduling"`) and licence-feature-tagged (`feature: "scheduling"`, #7136), gated on the deployment's `scheduling` licence feature under `MCP_FEATURE_GATE_MODE`. `create_booking` takes the HTTP write branch (its own daily cap, `MCP_ABUSE_CREATE_BOOKING_DAILY_CAP`, default 20, and an `attendee.email` format check — #8071). `list_meetings` (#8843 D5) twins `GET /v1/schedule/meetings`: it takes no argument and returns the site's Live meetings — slug, visibility, position and whether each can be booked, plus a bookable one's calLink, title, length, description and price; `private` means unlisted, not secret
127
+ **Redirect (1):** `get_redirect` (#10202 D8) — module-gated (`module: "content-write"`) and licence-feature-tagged (`feature: "workspace"`, the content-write claim), gated on the deployment's `workspace` licence feature under `MCP_FEATURE_GATE_MODE`. It twins `GET /v1/redirects`: given an old site path (and an optional locale) it returns where the site permanently redirects it — `{ from, to, permanent: true }` — or the route's not-found body. Its handler rides `@working-theory/mcp-workspace`'s anonymous `tools` (`redirect.ts`), the pack the `workspace` and `multi-tenant` modules carry, and calls the module-level `apiClient` with no agent key
127
128
 
128
- ### Why the parity test counts 21 and a keyless server registers 19 (20 over stdio once Free is released)
129
+ ### Why the parity test counts 22 and a keyless server registers 20 (21 over stdio once Free is released)
129
130
 
130
- - **21 in the `TOOLS` array / parity test**: the `TOOLS` array in
131
- `packages/metadata/src/mcp-tool-catalog.ts` defines 21 anonymous tools; the
132
- parity test counts that full array. It also holds the thirty-one signed-in
133
- workspace tools (#3591), which no anonymous view returns — see § The
131
+ - **22 in the `TOOLS` array / parity test**: the `TOOLS` array in
132
+ `packages/metadata/src/mcp-tool-catalog.ts` defines 22 anonymous tools; the
133
+ parity test counts that full array. It also holds the thirty-three signed-in
134
+ workspace tools (#3591; the content-suggestion pair `draft_page_suggestions`
135
+ and `decide_page_suggestion` since #10218), which no anonymous view returns — see § The
134
136
  signed-in workspace surface below.
135
- - **19 registered on a KEYLESS deployment, 20 on a commerce-claimed one,
136
- while the Free level is held or over HTTP — 20 and 21 over stdio once it is
137
+ - **20 registered on a KEYLESS deployment, 21 on a commerce-claimed one,
138
+ while the Free level is held or over HTTP — 21 and 22 over stdio once it is
137
139
  released** (#5971, #9534, #9965 D13): `createMcpServer` (`apps/mcp/src/server.ts`) registers
138
140
  `getMcpToolRegistrations()` minus the topology seam's `UNSERVED_MCP_TOOLS`,
139
141
  minus `create_project` while create-ailk's `release-scope.json` holds
@@ -143,16 +145,16 @@ Every tool maps 1:1 to an `apps/api` endpoint. When you change a tool, change th
143
145
  `addon`-tagged tool whose claim this deployment does not hold.
144
146
  `start_purchase` carries `addon: "commerce"`, so it is the one entry whose
145
147
  presence depends on the licence — one fewer without the claim.
146
- - **19 free anonymous tools while Free is held or over HTTP (20 over stdio once released)**: since #5853 retired the three content-write
148
+ - **20 anonymous tools while Free is held or over HTTP (21 over stdio once released)**: since #5853 retired the three content-write
147
149
  tools, every registered tool remains anonymous — call-time gating checks a
148
- licence claim, never an identity. The six carrying `tier: "pro"`
150
+ licence claim, never an identity. The seven carrying `tier: "pro"`
149
151
  (`get_flow`, `submit_flow_step`, `list_availability`, `get_event_meta`,
150
- `list_meetings`, `create_booking`) are gated at REGISTRATION **and at call time** (#7315):
152
+ `list_meetings`, `create_booking`, `get_redirect`) are gated at REGISTRATION **and at call time** (#7315):
151
153
  each needs its module, and each catalog row names the licence `feature:`
152
154
  that entitles it. `start_purchase` is gated at call time by both this
153
155
  server-wide gate and its own Gate 2 in `tools/purchase.ts`.
154
156
  `MCP_FEATURE_GATE_MODE` (`packages/metadata`) decides what an unheld
155
- feature does. In `"warn"` mode (this release) the six register on module
157
+ feature does. In `"warn"` mode (this release) the seven register on module
156
158
  presence alone, and the boot (`bootMcp()`) logs one warning (stderr) per tool
157
159
  served without its feature. In `"refuse"` mode (the next release) they
158
160
  also need the deployment's licence to hold the feature, and
@@ -283,7 +285,9 @@ Example (lead routing):
283
285
  - Don't add a tool without the matching `apps/api` endpoint.
284
286
  - Don't put business logic in tool handlers — they're adapters.
285
287
 
286
- Last updated: 2026-10-26 — #9965 (D12, D13): `create_project` is served over stdio only — the HTTP transport, the default, never registers it, and the web manifest always drops it — and `trustOneHop` trusts its hop only on Vercel — verified against `packages/mcp-server/src/{server,run,serverless-app}.ts` and `apps/web/app/.well-known/mcp.json/route.ts`.
288
+ Last updated: 2026-10-29 — #10202 (D8): `get_redirect` (`mcp-workspace`'s `redirect.ts`, the pack's first anonymous handler) twins `GET /v1/redirects` under `module: "content-write"` and `feature: "workspace"` — 22 in `TOOLS`, 20 registered keyless and 21 commerce-claimed while Free is held, seven `tier: "pro"` — verified against `packages/metadata/src/mcp-tool-catalog.ts`, `packages-paid/mcp-workspace/src/index.ts` and `tests/parity/api-parity/kinds/site.ts`.
289
+
290
+ Prior: 2026-10-26 — #9965 (D12, D13): `create_project` is served over stdio only — the HTTP transport, the default, never registers it, and the web manifest always drops it — and `trustOneHop` trusts its hop only on Vercel — verified against `packages/mcp-server/src/{server,run,serverless-app}.ts` and `apps/web/app/.well-known/mcp.json/route.ts`.
287
291
 
288
292
  Prior: 2026-10-24 — #9749: `get_slack_connection_status` (`mcp-workspace`'s `slack_connection.ts`) and `get_prospect_channel_settings` / `set_prospect_channel_settings` (`mcp-scheduling`'s `prospect_channel_settings.ts`, the first signed-in handlers outside `mcp-workspace`) twin the Slack status read and the prospect-channel settings pair — the signed-in workspace surface is thirty-one tools — verified against `packages/metadata/src/mcp-tool-catalog.ts`, `packages-paid/mcp-workspace/src/index.ts`, `packages-paid/mcp-scheduling/src/index.ts` and `tests/parity/api-parity/kinds/site.ts`.
289
293
 
@@ -594,6 +594,14 @@ A site serves its services at `/services` unless `content/_site.mdx` sets `servi
594
594
  - **Surfaces.** The canonical, hreflang and `BreadcrumbList` (the base's title-cased last segment, then the category's title), `/sitemap.xml` and `/llms.txt` (`pagePathFor`), every nav and footer entry at `/services` (mapped in the locale layout and `components/signed-in-nav.tsx`), the hub's own section links, and route-family chrome (`lib/page-chrome.ts`) all follow it. A site that sets neither `services.path` nor any `parent` serves every URL exactly as before.
595
595
  - **Moving it.** `/ailk:growth-ops page rename` moves the base (`--from <current base> --to <new base>`): it writes `services.path` and keeps a previous moved base redirecting.
596
596
 
597
+ ## Nav-only lists (`navOnly`)
598
+
599
+ A list page whose frontmatter sets `navOnly: true` (valid on a registry list type only, #10200) has no page of its own: `honourNavOnly` (`lib/hub-children.tsx`) answers `permanentRedirect` (308) to the first child in the route's own child order (a hub's is slug order after `includeChild`, not the order of links its authored sections carry), at the card's public path with the locale prefix kept, and `notFound()` when it lists none. Only an exact `true` acts; anything else renders as before.
600
+
601
+ - **Where.** The thirteen hubs inside `hubPageInput` (slug order after `includeChild`; `/services` at `servicePublicPath`), `/alternatives` (`/vs/<slug>`, slug-read order), `/templates`, `/careers` (newest first) and `/reports` (newest first) with one call each, and the childless `/faq`, `/glossary`, `/clients`, `/testimonials` and the `[slug]` `BlogList` teaser, which 404.
602
+ - **Not honoured.** The docs-blog module's Fumadocs `/blog` and `/docs`; `DownloadsList` (`/downloads` already redirects to `/reports`); `LocationsList` (no route). `/sitemap.xml` and `/llms.txt` still list a Nav-only list's URL.
603
+ - **The nav is untouched.** `lib/nav-config.ts` is generated from route surfaces, not from list pages, so the heading and its children stay in the menu.
604
+
597
605
  ## AEO & performance
598
606
 
599
607
  - Static rendering is the default. Don't introduce client rendering without a reason.
@@ -610,7 +618,9 @@ A site serves its services at `/services` unless `content/_site.mdx` sets `servi
610
618
  - Don't reach into `apps/api` or `apps/mcp` from `apps/web`. They're peer apps — talk via HTTP/MCP.
611
619
  - Don't commit images to `screenshots/` — only the `.gitkeep` is tracked.
612
620
 
613
- Last updated: 2026-10-27 — § Where the services live (`services.path`): the services base, one category level, the 308 from `/services` and the `update-page` base move (#10043) — verified against `lib/services-path.ts`, `lib/service-listing.ts`, `middleware.ts`, `next.config.mjs` and `app/[locale]/services/`.
621
+ Last updated: 2026-10-28 — § Nav-only lists (`navOnly`): a Nav-only list's route answers 308 to its first child and 404 with none (#10200) — verified against `lib/hub-children.tsx` and the nine list routes it is called from beside the hubs.
622
+
623
+ Prior: 2026-10-27 — § Where the services live (`services.path`): the services base, one category level, the 308 from `/services` and the `update-page` base move (#10043) — verified against `lib/services-path.ts`, `lib/service-listing.ts`, `middleware.ts`, `next.config.mjs` and `app/[locale]/services/`.
614
624
 
615
625
  Prior: 2026-10-26 — § The same-origin relay: the bot check moves to `lib/bot-check.ts` and covers the relay's three lead routes and the seven public lead, flow and checkout Server Actions, with `BotIdClient` protecting every same-origin POST and the `AILK_BOT_CHECK` switch (#9255) — verified against `lib/bot-check.ts`, `lib/relay.ts`, `app/[locale]/layout.tsx` and the seven actions' files.
616
626
 
@@ -42,6 +42,8 @@ import {
42
42
  import type { Metadata } from "next";
43
43
  import type { ReactElement } from "react";
44
44
 
45
+ import { honourNavOnly } from "../../../lib/hub-children";
46
+
45
47
  // The content type is written as a literal in both renderPage calls below
46
48
  // (not a shared constant): `lib/__tests__/structured-data.test.tsx` and
47
49
  // tools/lint-check-content-renders.ts read `renderPage({ type: "…" })` off
@@ -87,6 +89,8 @@ export default async function Page({
87
89
  const load = getMdxSource ?? loadMdxSource;
88
90
  const type = await resolveType(slug, locale, load);
89
91
  if (type === "BlogList") {
92
+ const source = await load("BlogList", slug, locale);
93
+ honourNavOnly(source?.frontmatter, locale, []);
90
94
  return renderPage({
91
95
  type: "BlogList",
92
96
  slug,
@@ -27,6 +27,8 @@ import type { Metadata } from "next";
27
27
  import { notFound } from "next/navigation";
28
28
  import type { ReactElement } from "react";
29
29
 
30
+ import { honourNavOnly } from "../../../lib/hub-children";
31
+
30
32
  interface Props {
31
33
  params: Promise<{ locale: string }>;
32
34
  getMdxSource?: typeof defaultGetMdxSource;
@@ -60,6 +62,13 @@ export default async function Page({
60
62
  notFound();
61
63
  }
62
64
 
65
+ const slugs = await getMdxSlugs("ComparisonPage", locale);
66
+ honourNavOnly(
67
+ source.frontmatter,
68
+ locale,
69
+ slugs.map((slug) => `/vs/${slug}`),
70
+ );
71
+
63
72
  /**
64
73
  * Append one generated `items` section listing every ComparisonPage spoke
65
74
  * — D9's "the visible list and the CollectionPage's ItemList always
@@ -69,7 +78,6 @@ export default async function Page({
69
78
  */
70
79
  async function withSpokes(frontmatter: unknown): Promise<Section[]> {
71
80
  const declared = await loadSections(frontmatter);
72
- const slugs = await getMdxSlugs("ComparisonPage", locale);
73
81
  if (slugs.length === 0) return declared;
74
82
 
75
83
  const items = await Promise.all(
@@ -25,6 +25,8 @@ import type { Metadata } from "next";
25
25
  import { getTranslations } from "next-intl/server";
26
26
  import type { ReactElement } from "react";
27
27
 
28
+ import { honourNavOnly } from "../../../lib/hub-children";
29
+
28
30
  interface Props {
29
31
  params: Promise<{ locale: string }>;
30
32
  getMdxSource?: typeof defaultGetMdxSource;
@@ -75,6 +77,26 @@ export default async function Page({
75
77
  const loadSections = getPageSections ?? loadPageSections;
76
78
  const t = await getTranslations({ locale, namespace: "careers" });
77
79
 
80
+ const slugs = await listSlugs("JobPosting", locale);
81
+ const postings = await Promise.all(
82
+ slugs.map(async (slug) => {
83
+ const source = await load("JobPosting", slug, locale);
84
+ return { slug, fm: (source?.frontmatter ?? {}) as PostingFields };
85
+ }),
86
+ );
87
+ // Newest `publishedAt` first, then by slug; no date sorts last.
88
+ postings.sort(
89
+ (a, b) =>
90
+ (b.fm.publishedAt ?? "").localeCompare(a.fm.publishedAt ?? "") ||
91
+ a.slug.localeCompare(b.slug),
92
+ );
93
+ const source = await load("CareersList", "careers", locale);
94
+ honourNavOnly(
95
+ source?.frontmatter,
96
+ locale,
97
+ postings.map(({ slug }) => `/careers/${slug}`),
98
+ );
99
+
78
100
  /**
79
101
  * Put one generated `items` section first — the postings are the page's
80
102
  * main content — followed by the declared sections. Absent when there are
@@ -82,21 +104,7 @@ export default async function Page({
82
104
  */
83
105
  async function withPostings(frontmatter: unknown): Promise<Section[]> {
84
106
  const declared = await loadSections(frontmatter);
85
- const slugs = await listSlugs("JobPosting", locale);
86
- if (slugs.length === 0) return declared;
87
-
88
- const postings = await Promise.all(
89
- slugs.map(async (slug) => {
90
- const source = await load("JobPosting", slug, locale);
91
- return { slug, fm: (source?.frontmatter ?? {}) as PostingFields };
92
- }),
93
- );
94
- // Newest `publishedAt` first, then by slug; no date sorts last.
95
- postings.sort(
96
- (a, b) =>
97
- (b.fm.publishedAt ?? "").localeCompare(a.fm.publishedAt ?? "") ||
98
- a.slug.localeCompare(b.slug),
99
- );
107
+ if (postings.length === 0) return declared;
100
108
 
101
109
  const items = postings.map(({ slug, fm }) => {
102
110
  const type =
@@ -3,10 +3,16 @@
3
3
  import type {
4
4
  getMdxSource as defaultGetMdxSource,
5
5
  } from "@working-theory/page-renderer";
6
- import { renderPage, renderPageMetadata } from "@working-theory/page-renderer";
6
+ import {
7
+ getMdxSource as loadMdxSource,
8
+ renderPage,
9
+ renderPageMetadata,
10
+ } from "@working-theory/page-renderer";
7
11
  import type { Metadata } from "next";
8
12
  import type { ReactElement } from "react";
9
13
 
14
+ import { honourNavOnly } from "../../../lib/hub-children";
15
+
10
16
  interface Props {
11
17
  params: Promise<{ locale: string }>;
12
18
  getMdxSource?: typeof defaultGetMdxSource;
@@ -24,6 +30,12 @@ export default async function Page({
24
30
  getMdxSource,
25
31
  }: Props): Promise<ReactElement> {
26
32
  const { locale } = await params;
33
+ const source = await (getMdxSource ?? loadMdxSource)(
34
+ "ClientsList",
35
+ "clients",
36
+ locale,
37
+ );
38
+ honourNavOnly(source?.frontmatter, locale, []);
27
39
  return renderPage({
28
40
  type: "ClientsList",
29
41
  slug: "clients",
@@ -5,12 +5,15 @@ import type {
5
5
  getMdxSource as defaultGetMdxSource,
6
6
  getPageSections as defaultGetPageSections} from "@working-theory/page-renderer";
7
7
  import {
8
+ getMdxSource as loadMdxSource,
8
9
  renderPage,
9
10
  renderPageMetadata
10
11
  } from "@working-theory/page-renderer";
11
12
  import type { Metadata } from "next";
12
13
  import type { ReactElement } from "react";
13
14
 
15
+ import { honourNavOnly } from "../../../lib/hub-children";
16
+
14
17
  interface Props {
15
18
  params: Promise<{ locale: string }>;
16
19
  getMdxSource?: typeof defaultGetMdxSource;
@@ -30,6 +33,12 @@ export default async function Page({
30
33
  getPageSections,
31
34
  }: Props): Promise<ReactElement> {
32
35
  const { locale } = await params;
36
+ const source = await (getMdxSource ?? loadMdxSource)(
37
+ "FAQPage",
38
+ "faq",
39
+ locale,
40
+ );
41
+ honourNavOnly(source?.frontmatter, locale, []);
33
42
  return renderPage({
34
43
  type: "FAQPage",
35
44
  slug: "faq",
@@ -13,6 +13,8 @@ import {
13
13
  import type { Metadata } from "next";
14
14
  import type { ReactElement } from "react";
15
15
 
16
+ import { honourNavOnly } from "../../../lib/hub-children";
17
+
16
18
  interface Props {
17
19
  params: Promise<{ locale: string }>;
18
20
  getMdxSource?: typeof defaultGetMdxSource;
@@ -35,6 +37,7 @@ export default async function Page({
35
37
  }: Props): Promise<ReactElement> {
36
38
  const { locale } = await params;
37
39
  const source = await (getMdxSource ?? loadMdxSource)("GlossaryList", "glossary", locale);
40
+ honourNavOnly(source?.frontmatter, locale, []);
38
41
  const composed =
39
42
  source !== null &&
40
43
  Array.isArray((source.frontmatter as { sections?: unknown[] }).sections) &&
@@ -27,6 +27,7 @@ import { notFound } from "next/navigation";
27
27
  import { getTranslations } from "next-intl/server";
28
28
  import type { ReactElement } from "react";
29
29
 
30
+ import { honourNavOnly } from "../../../lib/hub-children";
30
31
  import {
31
32
  insertBeforeClosingCtas,
32
33
  listDetailPages,
@@ -67,6 +68,11 @@ export default async function Page({
67
68
  }
68
69
 
69
70
  const pages = await listDetailPages("TemplatePage", locale, load);
71
+ honourNavOnly(
72
+ source.frontmatter,
73
+ locale,
74
+ pages.map((page) => `/templates/${page.slug}`),
75
+ );
70
76
  const t = await getTranslations({ namespace: "templates", locale });
71
77
  const labels = templateLabels(t);
72
78
 
@@ -3,10 +3,16 @@
3
3
  import type {
4
4
  getMdxSource as defaultGetMdxSource,
5
5
  } from "@working-theory/page-renderer";
6
- import { renderPage, renderPageMetadata } from "@working-theory/page-renderer";
6
+ import {
7
+ getMdxSource as loadMdxSource,
8
+ renderPage,
9
+ renderPageMetadata,
10
+ } from "@working-theory/page-renderer";
7
11
  import type { Metadata } from "next";
8
12
  import type { ReactElement } from "react";
9
13
 
14
+ import { honourNavOnly } from "../../../lib/hub-children";
15
+
10
16
  interface Props {
11
17
  params: Promise<{ locale: string }>;
12
18
  getMdxSource?: typeof defaultGetMdxSource;
@@ -28,6 +34,12 @@ export default async function Page({
28
34
  getMdxSource,
29
35
  }: Props): Promise<ReactElement> {
30
36
  const { locale } = await params;
37
+ const source = await (getMdxSource ?? loadMdxSource)(
38
+ "TestimonialsList",
39
+ "testimonials",
40
+ locale,
41
+ );
42
+ honourNavOnly(source?.frontmatter, locale, []);
31
43
  return renderPage({
32
44
  type: "TestimonialsList",
33
45
  slug: "testimonials",
@@ -30,7 +30,9 @@ import {
30
30
  getMdxSource as loadMdxSource,
31
31
  getPageSections as loadPageSections,
32
32
  } from "@working-theory/page-renderer";
33
+ import { localeHref } from "@working-theory/ui";
33
34
  import { SectionRenderer } from "@working-theory/ui/sections";
35
+ import { notFound, permanentRedirect } from "next/navigation";
34
36
 
35
37
  import {
36
38
  type DetailPage,
@@ -242,6 +244,26 @@ function hasSections(frontmatter: unknown): boolean {
242
244
  return Array.isArray(sections) && sections.length > 0;
243
245
  }
244
246
 
247
+ /**
248
+ * Nav only (#10200): a list that is a heading in the site navigation with no
249
+ * page of its own answers a permanent redirect to its first child — in the
250
+ * route's own child order — and 404 when it lists none. Any other page
251
+ * returns, and renders as before.
252
+ */
253
+ export function honourNavOnly(
254
+ frontmatter: unknown,
255
+ locale: string,
256
+ childHrefs: readonly string[],
257
+ ): void {
258
+ if ((frontmatter as { navOnly?: unknown } | undefined)?.navOnly !== true)
259
+ return;
260
+ const first = childHrefs[0];
261
+ if (first === undefined) notFound();
262
+ permanentRedirect(
263
+ localeHref(locale, first, process.env.NEXT_PUBLIC_DEFAULT_LOCALE ?? "en"),
264
+ );
265
+ }
266
+
245
267
  type RenderPageInput = Parameters<typeof renderPage>[0];
246
268
 
247
269
  /**
@@ -279,25 +301,32 @@ export async function hubPageInput(
279
301
  };
280
302
  }
281
303
 
282
- const declared = await (loaders.getPageSections ?? loadPageSections)(
283
- source.frontmatter,
284
- );
285
- const pages = await listDetailPages(
304
+ const listed = await listDetailPages(
286
305
  config.childType,
287
306
  locale,
288
307
  load,
289
308
  loaders.getMdxSlugs ?? loadMdxSlugs,
290
309
  );
310
+ const pages = config.includeChild
311
+ ? listed.filter(config.includeChild)
312
+ : listed;
313
+ honourNavOnly(
314
+ source.frontmatter,
315
+ locale,
316
+ pages.flatMap((page) => {
317
+ const card = config.card?.(page) ?? defaultCard(page, config);
318
+ return card.link === undefined ? [] : [card.link.href];
319
+ }),
320
+ );
321
+ const declared = await (loaders.getPageSections ?? loadPageSections)(
322
+ source.frontmatter,
323
+ );
291
324
  // A prose hub renders its MDX body, never its declared sections, so its
292
325
  // existing links are read from the body and its cards stand alone after it.
293
326
  const linked = mdxBody
294
327
  ? bodyLinkPaths(source.body)
295
328
  : linkedPaths(declared);
296
- const generated = childCardsSection(
297
- linked,
298
- config.includeChild ? pages.filter(config.includeChild) : pages,
299
- config,
300
- );
329
+ const generated = childCardsSection(linked, pages, config);
301
330
  const placed =
302
331
  generated === null
303
332
  ? declared
@@ -1396,7 +1396,6 @@
1396
1396
  "notFoundHeadline": "Page not found",
1397
1397
  "notFoundDescription": "There is no page at this address. It may have been renamed or removed.",
1398
1398
  "editor": {
1399
- "tabPreview": "Preview",
1400
1399
  "toolChanges": "Show unsaved changes",
1401
1400
  "backLabel": "Pages",
1402
1401
  "statusDraft": "Draft",
@@ -1427,6 +1426,26 @@
1427
1426
  "toolNumberedList": "Numbered list",
1428
1427
  "toolLink": "Link",
1429
1428
  "toolImage": "Insert image",
1429
+ "toolView": "View",
1430
+ "toolRendered": "Rendered — how readers see it",
1431
+ "toolCode": "Markdown — the words with their marks",
1432
+ "toolInsert": "Insert from your library",
1433
+ "insertMeeting": "Add meeting",
1434
+ "insertMagnet": "Add magnet",
1435
+ "insertOffer": "Add offer",
1436
+ "insertAsset": "Add asset",
1437
+ "tips": {
1438
+ "h1": "Your page title is the H1. Use H2 for sections.",
1439
+ "h2": "Types ## before the line → a section heading.",
1440
+ "h3": "Types ### before the line → a smaller heading.",
1441
+ "bold": "Types **word** → bold.",
1442
+ "italic": "Types *word* → italic.",
1443
+ "bulletList": "Types - before each line → a bulleted list.",
1444
+ "numberedList": "Types 1. before each line → a numbered list.",
1445
+ "link": "Types [words](address) → a link.",
1446
+ "insert": "A meeting, magnet, offer or image from your library. AI uses these when it lays out the page.",
1447
+ "changes": "Compare your unsaved edits with the last save."
1448
+ },
1430
1449
  "issueRequired": "{field} is required.",
1431
1450
  "issueInvalid": "{field} isn't a valid choice.",
1432
1451
  "forYou": {
@@ -1455,7 +1474,8 @@
1455
1474
  "imageInsertError": "Could not upload that image. Try again.",
1456
1475
  "diffLegendAdded": "Added",
1457
1476
  "diffLegendRemoved": "Removed",
1458
- "diffLegendUnchanged": "Unchanged"
1477
+ "diffLegendUnchanged": "Unchanged",
1478
+ "tagPlaceholder": "{kind}: {slug}"
1459
1479
  },
1460
1480
  "sections": {
1461
1481
  "heading": "Sections",
package/versions.json CHANGED
@@ -1,66 +1,66 @@
1
1
  {
2
- "@working-theory/admin-ui": "0.50.0",
3
- "@working-theory/aeo": "0.50.0",
4
- "@working-theory/agent-context": "0.50.0",
2
+ "@working-theory/admin-ui": "0.52.0",
3
+ "@working-theory/aeo": "0.52.0",
4
+ "@working-theory/agent-context": "0.52.0",
5
5
  "@working-theory/agent-contract": "0.1.0",
6
6
  "@working-theory/agent-memory": "0.0.0",
7
7
  "@working-theory/ai": "0.1.0",
8
- "@working-theory/analytics": "0.50.0",
9
- "@working-theory/api": "0.50.0",
10
- "@working-theory/api-client": "0.50.0",
8
+ "@working-theory/analytics": "0.52.0",
9
+ "@working-theory/api": "0.52.0",
10
+ "@working-theory/api-client": "0.52.0",
11
11
  "@working-theory/cda-filesystem": "0.0.0",
12
- "@working-theory/cli": "0.50.0",
13
- "@working-theory/common": "0.50.0",
12
+ "@working-theory/cli": "0.52.0",
13
+ "@working-theory/common": "0.52.0",
14
14
  "@working-theory/consent": "0.1.0",
15
- "@working-theory/content-adapters": "0.50.0",
16
- "@working-theory/content-projection": "0.50.0",
17
- "@working-theory/email": "0.50.0",
15
+ "@working-theory/content-adapters": "0.52.0",
16
+ "@working-theory/content-projection": "0.52.0",
17
+ "@working-theory/email": "0.52.0",
18
18
  "@working-theory/env": "0.0.0",
19
- "@working-theory/eslint-config": "0.50.0",
20
- "@working-theory/fumadocs-schema": "0.50.0",
21
- "@working-theory/ids": "0.16.1",
22
- "@working-theory/jsonld": "0.50.0",
23
- "@working-theory/license": "0.50.0",
24
- "@working-theory/mcp-server": "0.50.0",
25
- "@working-theory/metadata": "0.50.0",
26
- "@working-theory/observability": "0.50.0",
27
- "@working-theory/page-renderer": "0.50.0",
19
+ "@working-theory/eslint-config": "0.52.0",
20
+ "@working-theory/fumadocs-schema": "0.52.0",
21
+ "@working-theory/ids": "0.17.0",
22
+ "@working-theory/jsonld": "0.52.0",
23
+ "@working-theory/license": "0.52.0",
24
+ "@working-theory/mcp-server": "0.52.0",
25
+ "@working-theory/metadata": "0.52.0",
26
+ "@working-theory/observability": "0.52.0",
27
+ "@working-theory/page-renderer": "0.52.0",
28
28
  "@working-theory/plugin-mcp": "0.1.3",
29
- "@working-theory/schema": "0.50.0",
30
- "@working-theory/schema-pack-registry": "0.50.0",
31
- "@working-theory/schema-validator": "0.50.0",
32
- "@working-theory/sections-registry": "0.50.0",
33
- "@working-theory/site-config": "0.50.0",
34
- "@working-theory/spoke-checkout": "0.7.6",
35
- "@working-theory/stripe-client": "0.50.0",
36
- "@working-theory/templates": "0.50.0",
37
- "@working-theory/theme": "0.50.0",
38
- "@working-theory/ui": "0.50.0",
39
- "@working-theory/validation": "0.50.0",
40
- "@working-theory/web-i18n": "0.50.0",
41
- "@working-theory/admin": "0.7.1",
42
- "@working-theory/api-commerce": "0.1.4",
43
- "@working-theory/api-content-write": "0.1.4",
44
- "@working-theory/api-hub": "0.3.1",
45
- "@working-theory/api-multi-tenant": "0.1.4",
46
- "@working-theory/api-scheduling": "0.4.1",
47
- "@working-theory/api-signal-collectors": "0.2.3",
48
- "@working-theory/api-workspace": "0.3.0",
29
+ "@working-theory/schema": "0.52.0",
30
+ "@working-theory/schema-pack-registry": "0.52.0",
31
+ "@working-theory/schema-validator": "0.52.0",
32
+ "@working-theory/sections-registry": "0.52.0",
33
+ "@working-theory/site-config": "0.52.0",
34
+ "@working-theory/spoke-checkout": "0.7.8",
35
+ "@working-theory/stripe-client": "0.52.0",
36
+ "@working-theory/templates": "0.52.0",
37
+ "@working-theory/theme": "0.52.0",
38
+ "@working-theory/ui": "0.52.0",
39
+ "@working-theory/validation": "0.52.0",
40
+ "@working-theory/web-i18n": "0.52.0",
41
+ "@working-theory/admin": "0.8.1",
42
+ "@working-theory/api-commerce": "0.1.6",
43
+ "@working-theory/api-content-write": "0.3.0",
44
+ "@working-theory/api-hub": "0.3.3",
45
+ "@working-theory/api-multi-tenant": "0.2.1",
46
+ "@working-theory/api-scheduling": "0.4.3",
47
+ "@working-theory/api-signal-collectors": "0.2.5",
48
+ "@working-theory/api-workspace": "0.3.2",
49
49
  "@working-theory/billing-client": "0.0.0",
50
50
  "@working-theory/branding": "0.0.0",
51
51
  "@working-theory/license-client": "0.0.0",
52
52
  "@working-theory/license-manifest": "0.0.0",
53
- "@working-theory/mcp-scheduling": "0.3.3",
54
- "@working-theory/mcp-signal-collectors": "0.2.4",
55
- "@working-theory/mcp-workspace": "0.3.3",
53
+ "@working-theory/mcp-scheduling": "0.3.5",
54
+ "@working-theory/mcp-signal-collectors": "0.2.6",
55
+ "@working-theory/mcp-workspace": "0.4.1",
56
56
  "@working-theory/partner-session": "0.0.0",
57
57
  "@working-theory/preview-protocol": "0.0.0",
58
- "@working-theory/ui-commerce": "0.23.28",
59
- "@working-theory/ui-docs-blog": "0.23.28",
60
- "@working-theory/ui-scheduling": "0.25.2",
61
- "@working-theory/ui-signal-collectors": "0.27.6",
58
+ "@working-theory/ui-commerce": "0.23.30",
59
+ "@working-theory/ui-docs-blog": "0.23.30",
60
+ "@working-theory/ui-scheduling": "0.25.4",
61
+ "@working-theory/ui-signal-collectors": "0.27.8",
62
62
  "@working-theory/web": "0.0.0",
63
63
  "@working-theory/app-api": "1.0.0",
64
64
  "@working-theory/mcp": "0.0.0",
65
- "@working-theory/database": "0.50.0"
65
+ "@working-theory/database": "0.52.0"
66
66
  }