+
+
+
+
+
diff --git a/apps/astro-example/src/pages/docs/[...slug].md.ts b/apps/astro-example/src/pages/docs/[...slug].md.ts
new file mode 100644
index 0000000..9d59e70
--- /dev/null
+++ b/apps/astro-example/src/pages/docs/[...slug].md.ts
@@ -0,0 +1,14 @@
+import { createDocsEndpoint, createMarkdownStaticPaths } from "leadtype/astro";
+import { normalizeAgentReadabilityManifest } from "leadtype/llm/readability";
+import manifestJson from "../../../public/docs/agent-readability.json";
+import { source } from "../../lib/source";
+
+const manifest = normalizeAgentReadabilityManifest(manifestJson);
+
+export const getStaticPaths = createMarkdownStaticPaths({ source });
+
+export const GET = createDocsEndpoint({
+ manifest,
+});
+
+export const HEAD = GET;
diff --git a/apps/astro-example/src/pages/index.astro b/apps/astro-example/src/pages/index.astro
new file mode 100644
index 0000000..5aac480
--- /dev/null
+++ b/apps/astro-example/src/pages/index.astro
@@ -0,0 +1,17 @@
+
+
+ Leadtype Astro Example
+
+
+
+
+
Astro dogfood
+
Leadtype framework example
+
+ Astro renders the shared docs, serves markdown endpoints, and searches
+ static JSON with the plain browser helper.
+
+ Open docs
+
+
+
diff --git a/apps/astro-example/tsconfig.json b/apps/astro-example/tsconfig.json
new file mode 100644
index 0000000..e137bc6
--- /dev/null
+++ b/apps/astro-example/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "extends": "astro/tsconfigs/strict",
+ "compilerOptions": {
+ "types": ["astro/client"]
+ },
+ "include": ["src", "astro.config.mjs"]
+}
diff --git a/apps/example/server/utils/agent-readability.ts b/apps/example/server/utils/agent-readability.ts
index b32ab90..a9f81d3 100644
--- a/apps/example/server/utils/agent-readability.ts
+++ b/apps/example/server/utils/agent-readability.ts
@@ -1,9 +1,7 @@
import { readFile } from "node:fs/promises";
import { join } from "node:path";
-import type {
- AgentReadabilityManifest,
- MarkdownMirrorTarget,
-} from "leadtype/llm/readability";
+import type { MarkdownMirrorTarget } from "leadtype/llm/readability";
+import { normalizeAgentReadabilityManifest } from "leadtype/llm/readability";
import {
getHeader,
getRequestProtocol,
@@ -14,10 +12,8 @@ import manifestJson from "../../src/generated/agent-readability.json" with {
type: "json",
};
-export const agentReadabilityManifest = {
- ...manifestJson,
- version: 1,
-} as unknown as AgentReadabilityManifest;
+export const agentReadabilityManifest =
+ normalizeAgentReadabilityManifest(manifestJson);
export function getRequestOrigin(event: H3Event): string | undefined {
const forwardedHost = getHeader(event, "x-forwarded-host")
diff --git a/apps/example/src/generated/agent-readability.json b/apps/example/src/generated/agent-readability.json
index 135a041..b6f3294 100644
--- a/apps/example/src/generated/agent-readability.json
+++ b/apps/example/src/generated/agent-readability.json
@@ -1,6 +1,6 @@
{
"version": 1,
- "generatedAt": "2026-05-13T16:53:52.978Z",
+ "generatedAt": "2026-05-15T23:17:18.885Z",
"baseUrl": "https://leadtype.dev",
"product": {
"name": "Leadtype",
@@ -18,7 +18,22 @@
"groups": [
"get-started"
],
- "lastModified": "2026-05-13T07:19:12.000Z"
+ "lastModified": "2026-05-13T17:23:53.000Z",
+ "logicalPath": "index"
+ },
+ {
+ "title": "Collections",
+ "description": "Declare multi-source docs sets with defineCollection — local folders, git repos, and per-collection schemas.",
+ "urlPath": "/docs/authoring/collections",
+ "absoluteUrl": "https://leadtype.dev/docs/authoring/collections",
+ "markdownUrlPath": "/docs/authoring/collections.md",
+ "markdownAbsoluteUrl": "https://leadtype.dev/docs/authoring/collections.md",
+ "relativePath": "authoring/collections",
+ "groups": [
+ "authoring"
+ ],
+ "lastModified": "2026-05-15T16:46:08.000Z",
+ "logicalPath": "authoring/collections"
},
{
"title": "Components",
@@ -31,7 +46,8 @@
"groups": [
"authoring"
],
- "lastModified": "2026-05-13T07:19:12.000Z"
+ "lastModified": "2026-05-13T17:23:53.000Z",
+ "logicalPath": "authoring/components"
},
{
"title": "Frontmatter",
@@ -44,7 +60,8 @@
"groups": [
"authoring"
],
- "lastModified": "2026-05-13T07:19:12.000Z"
+ "lastModified": "2026-05-13T17:23:53.000Z",
+ "logicalPath": "authoring/frontmatter"
},
{
"title": "Add search",
@@ -57,7 +74,8 @@
"groups": [
"docs-site"
],
- "lastModified": "2026-05-13T07:19:12.000Z"
+ "lastModified": "2026-05-13T17:23:53.000Z",
+ "logicalPath": "build/add-search"
},
{
"title": "Build a docs site",
@@ -70,7 +88,22 @@
"groups": [
"docs-site"
],
- "lastModified": "2026-05-13T07:19:12.000Z"
+ "lastModified": "2026-05-13T20:57:41.000Z",
+ "logicalPath": "build/build-a-docs-site"
+ },
+ {
+ "title": "Framework integration matrix",
+ "description": "Use Leadtype with native-feeling recipes for Next, Nuxt, Astro, SvelteKit, TanStack Start, and Fumadocs.",
+ "urlPath": "/docs/build/framework-matrix",
+ "absoluteUrl": "https://leadtype.dev/docs/build/framework-matrix",
+ "markdownUrlPath": "/docs/build/framework-matrix.md",
+ "markdownAbsoluteUrl": "https://leadtype.dev/docs/build/framework-matrix.md",
+ "relativePath": "build/framework-matrix",
+ "groups": [
+ "docs-site"
+ ],
+ "lastModified": "2026-05-15T22:56:16.000Z",
+ "logicalPath": "build/framework-matrix"
},
{
"title": "Generate static artifacts",
@@ -83,7 +116,8 @@
"groups": [
"docs-site"
],
- "lastModified": "2026-05-13T07:19:12.000Z"
+ "lastModified": "2026-05-13T17:23:53.000Z",
+ "logicalPath": "build/generate-static-artifacts"
},
{
"title": "Integrate with Fumadocs",
@@ -96,7 +130,8 @@
"groups": [
"docs-site"
],
- "lastModified": "2026-05-13T16:32:01.000Z"
+ "lastModified": "2026-05-13T20:57:41.000Z",
+ "logicalPath": "build/integrate-with-fumadocs"
},
{
"title": "Optimize docs for agents",
@@ -109,7 +144,8 @@
"groups": [
"docs-site"
],
- "lastModified": "2026-05-13T07:19:12.000Z"
+ "lastModified": "2026-05-13T17:23:53.000Z",
+ "logicalPath": "build/optimize-docs-for-agents"
},
{
"title": "Use the source primitive",
@@ -122,7 +158,8 @@
"groups": [
"docs-site"
],
- "lastModified": "2026-05-13T16:48:26.000Z"
+ "lastModified": "2026-05-15T01:13:35.000Z",
+ "logicalPath": "build/use-the-source-primitive"
},
{
"title": "Validate in CI",
@@ -135,7 +172,8 @@
"groups": [
"docs-site"
],
- "lastModified": "2026-05-13T07:19:12.000Z"
+ "lastModified": "2026-05-13T17:23:53.000Z",
+ "logicalPath": "build/validate-in-ci"
},
{
"title": "How it works",
@@ -148,7 +186,8 @@
"groups": [
"get-started"
],
- "lastModified": "2026-05-11T17:53:02.000Z"
+ "lastModified": "2026-05-11T17:53:02.000Z",
+ "logicalPath": "how-it-works"
},
{
"title": "Methodology",
@@ -161,7 +200,8 @@
"groups": [
"get-started"
],
- "lastModified": "2026-05-13T07:19:12.000Z"
+ "lastModified": "2026-05-15T01:13:35.000Z",
+ "logicalPath": "methodology"
},
{
"title": "Bundle docs into a package",
@@ -174,7 +214,8 @@
"groups": [
"package-docs"
],
- "lastModified": "2026-05-13T07:19:12.000Z"
+ "lastModified": "2026-05-13T17:23:53.000Z",
+ "logicalPath": "package-docs/bundle"
},
{
"title": "Quickstart",
@@ -187,11 +228,26 @@
"groups": [
"get-started"
],
- "lastModified": "2026-05-13T16:48:26.000Z"
+ "lastModified": "2026-05-15T22:56:16.000Z",
+ "logicalPath": "quickstart"
+ },
+ {
+ "title": "Architecture",
+ "description": "The core / adapter boundary — what ships where, and the rules adapters must follow.",
+ "urlPath": "/docs/reference/architecture",
+ "absoluteUrl": "https://leadtype.dev/docs/reference/architecture",
+ "markdownUrlPath": "/docs/reference/architecture.md",
+ "markdownAbsoluteUrl": "https://leadtype.dev/docs/reference/architecture.md",
+ "relativePath": "reference/architecture",
+ "groups": [
+ "reference"
+ ],
+ "lastModified": "2026-05-15T01:13:35.000Z",
+ "logicalPath": "reference/architecture"
},
{
"title": "CLI",
- "description": "leadtype generate and leadtype lint — flags, exit codes, and JSON output.",
+ "description": "leadtype generate, leadtype sync, and leadtype lint — flags, exit codes, and JSON output.",
"urlPath": "/docs/reference/cli",
"absoluteUrl": "https://leadtype.dev/docs/reference/cli",
"markdownUrlPath": "/docs/reference/cli.md",
@@ -200,7 +256,8 @@
"groups": [
"reference"
],
- "lastModified": "2026-05-12T02:59:25.000Z"
+ "lastModified": "2026-05-15T16:46:08.000Z",
+ "logicalPath": "reference/cli"
},
{
"title": "Convert",
@@ -213,7 +270,8 @@
"groups": [
"reference"
],
- "lastModified": "2026-05-12T02:59:25.000Z"
+ "lastModified": "2026-05-12T02:59:25.000Z",
+ "logicalPath": "reference/convert"
},
{
"title": "Evals",
@@ -226,7 +284,8 @@
"groups": [
"reference"
],
- "lastModified": "2026-05-13T07:19:12.000Z"
+ "lastModified": "2026-05-13T17:23:53.000Z",
+ "logicalPath": "reference/evals"
},
{
"title": "Lint rules",
@@ -239,7 +298,8 @@
"groups": [
"reference"
],
- "lastModified": "2026-05-09T22:38:26.000Z"
+ "lastModified": "2026-05-09T22:38:26.000Z",
+ "logicalPath": "reference/lint"
},
{
"title": "LLM files",
@@ -252,7 +312,8 @@
"groups": [
"reference"
],
- "lastModified": "2026-05-13T07:19:12.000Z"
+ "lastModified": "2026-05-13T17:23:53.000Z",
+ "logicalPath": "reference/llm"
},
{
"title": "leadtype/mdx",
@@ -265,7 +326,8 @@
"groups": [
"reference"
],
- "lastModified": "2026-05-13T07:19:12.000Z"
+ "lastModified": "2026-05-13T20:57:41.000Z",
+ "logicalPath": "reference/mdx"
},
{
"title": "Remark plugins",
@@ -278,7 +340,8 @@
"groups": [
"reference"
],
- "lastModified": "2026-05-12T02:59:25.000Z"
+ "lastModified": "2026-05-13T20:57:41.000Z",
+ "logicalPath": "reference/remark"
},
{
"title": "Search",
@@ -291,7 +354,8 @@
"groups": [
"reference"
],
- "lastModified": "2026-05-12T02:59:25.000Z"
+ "lastModified": "2026-05-12T02:59:25.000Z",
+ "logicalPath": "reference/search"
},
{
"title": "createDocsSource",
@@ -304,7 +368,8 @@
"groups": [
"reference"
],
- "lastModified": "2026-05-13T16:48:26.000Z"
+ "lastModified": "2026-05-13T20:57:41.000Z",
+ "logicalPath": "reference/source"
}
],
"navigation": {
@@ -352,7 +417,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs#next",
"children": []
}
- ]
+ ],
+ "logicalPath": "index"
},
{
"urlPath": "/docs/how-it-works",
@@ -425,7 +491,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/how-it-works#where-to-next",
"children": []
}
- ]
+ ],
+ "logicalPath": "how-it-works"
},
{
"urlPath": "/docs/methodology",
@@ -471,7 +538,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/methodology#when-the-combination-shines",
"children": []
}
- ]
+ ],
+ "logicalPath": "methodology"
},
{
"urlPath": "/docs/quickstart",
@@ -535,7 +603,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/quickstart#next-steps",
"children": []
}
- ]
+ ],
+ "logicalPath": "quickstart"
}
],
"children": []
@@ -548,6 +617,117 @@
"title": "Authoring",
"description": "The content contract: frontmatter, groups, and the MDX components the pipeline can flatten.",
"pages": [
+ {
+ "urlPath": "/docs/authoring/collections",
+ "title": "Collections",
+ "description": "Declare multi-source docs sets with defineCollection — local folders, git repos, and per-collection schemas.",
+ "groups": [
+ "authoring"
+ ],
+ "toc": [
+ {
+ "id": "when-to-reach-for-this",
+ "title": "When to reach for this",
+ "level": 2,
+ "urlPath": "/docs/authoring/collections",
+ "urlWithHash": "/docs/authoring/collections#when-to-reach-for-this",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/collections#when-to-reach-for-this",
+ "children": []
+ },
+ {
+ "id": "definecollection",
+ "title": "defineCollection",
+ "level": 2,
+ "urlPath": "/docs/authoring/collections",
+ "urlWithHash": "/docs/authoring/collections#definecollection",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/collections#definecollection",
+ "children": [
+ {
+ "id": "fields",
+ "title": "Fields",
+ "level": 3,
+ "urlPath": "/docs/authoring/collections",
+ "urlWithHash": "/docs/authoring/collections#fields",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/collections#fields",
+ "children": []
+ }
+ ]
+ },
+ {
+ "id": "local-only-collections",
+ "title": "Local-only collections",
+ "level": 2,
+ "urlPath": "/docs/authoring/collections",
+ "urlWithHash": "/docs/authoring/collections#local-only-collections",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/collections#local-only-collections",
+ "children": []
+ },
+ {
+ "id": "remote-collections",
+ "title": "Remote collections",
+ "level": 2,
+ "urlPath": "/docs/authoring/collections",
+ "urlWithHash": "/docs/authoring/collections#remote-collections",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/collections#remote-collections",
+ "children": []
+ },
+ {
+ "id": "multi-repo-framework-matrix",
+ "title": "Multi-repo: framework matrix",
+ "level": 2,
+ "urlPath": "/docs/authoring/collections",
+ "urlWithHash": "/docs/authoring/collections#multi-repo-framework-matrix",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/collections#multi-repo-framework-matrix",
+ "children": []
+ },
+ {
+ "id": "per-collection-schemas",
+ "title": "Per-collection schemas",
+ "level": 2,
+ "urlPath": "/docs/authoring/collections",
+ "urlWithHash": "/docs/authoring/collections#per-collection-schemas",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/collections#per-collection-schemas",
+ "children": []
+ },
+ {
+ "id": "per-collection-groups",
+ "title": "Per-collection groups",
+ "level": 2,
+ "urlPath": "/docs/authoring/collections",
+ "urlWithHash": "/docs/authoring/collections#per-collection-groups",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/collections#per-collection-groups",
+ "children": []
+ },
+ {
+ "id": "filtering",
+ "title": "Filtering",
+ "level": 2,
+ "urlPath": "/docs/authoring/collections",
+ "urlWithHash": "/docs/authoring/collections#filtering",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/collections#filtering",
+ "children": []
+ },
+ {
+ "id": "running-it",
+ "title": "Running it",
+ "level": 2,
+ "urlPath": "/docs/authoring/collections",
+ "urlWithHash": "/docs/authoring/collections#running-it",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/collections#running-it",
+ "children": []
+ },
+ {
+ "id": "what-you-do-not-migrate",
+ "title": "What you do not migrate",
+ "level": 2,
+ "urlPath": "/docs/authoring/collections",
+ "urlWithHash": "/docs/authoring/collections#what-you-do-not-migrate",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/collections#what-you-do-not-migrate",
+ "children": []
+ }
+ ],
+ "logicalPath": "authoring/collections"
+ },
{
"urlPath": "/docs/authoring/components",
"title": "Components",
@@ -719,7 +899,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/components#guidelines",
"children": []
}
- ]
+ ],
+ "logicalPath": "authoring/components"
},
{
"urlPath": "/docs/authoring/frontmatter",
@@ -784,7 +965,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/frontmatter#what-this-gives-you",
"children": []
}
- ]
+ ],
+ "logicalPath": "authoring/frontmatter"
}
],
"children": []
@@ -859,7 +1041,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/build/add-search#verify",
"children": []
}
- ]
+ ],
+ "logicalPath": "build/add-search"
},
{
"urlPath": "/docs/build/build-a-docs-site",
@@ -914,7 +1097,82 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/build/build-a-docs-site#configure-product-and-groups",
"children": []
}
- ]
+ ],
+ "logicalPath": "build/build-a-docs-site"
+ },
+ {
+ "urlPath": "/docs/build/framework-matrix",
+ "title": "Framework integration matrix",
+ "description": "Use Leadtype with native-feeling recipes for Next, Nuxt, Astro, SvelteKit, TanStack Start, and Fumadocs.",
+ "groups": [
+ "docs-site"
+ ],
+ "toc": [
+ {
+ "id": "shared-build-step",
+ "title": "Shared build step",
+ "level": 2,
+ "urlPath": "/docs/build/framework-matrix",
+ "urlWithHash": "/docs/build/framework-matrix#shared-build-step",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/build/framework-matrix#shared-build-step",
+ "children": []
+ },
+ {
+ "id": "next-js-app-router",
+ "title": "Next.js App Router",
+ "level": 2,
+ "urlPath": "/docs/build/framework-matrix",
+ "urlWithHash": "/docs/build/framework-matrix#next-js-app-router",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/build/framework-matrix#next-js-app-router",
+ "children": []
+ },
+ {
+ "id": "astro",
+ "title": "Astro",
+ "level": 2,
+ "urlPath": "/docs/build/framework-matrix",
+ "urlWithHash": "/docs/build/framework-matrix#astro",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/build/framework-matrix#astro",
+ "children": []
+ },
+ {
+ "id": "sveltekit",
+ "title": "SvelteKit",
+ "level": 2,
+ "urlPath": "/docs/build/framework-matrix",
+ "urlWithHash": "/docs/build/framework-matrix#sveltekit",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/build/framework-matrix#sveltekit",
+ "children": []
+ },
+ {
+ "id": "nuxt-nitro",
+ "title": "Nuxt/Nitro",
+ "level": 2,
+ "urlPath": "/docs/build/framework-matrix",
+ "urlWithHash": "/docs/build/framework-matrix#nuxt-nitro",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/build/framework-matrix#nuxt-nitro",
+ "children": []
+ },
+ {
+ "id": "tanstack-start-and-fumadocs",
+ "title": "TanStack Start and Fumadocs",
+ "level": 2,
+ "urlPath": "/docs/build/framework-matrix",
+ "urlWithHash": "/docs/build/framework-matrix#tanstack-start-and-fumadocs",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/build/framework-matrix#tanstack-start-and-fumadocs",
+ "children": []
+ },
+ {
+ "id": "verification",
+ "title": "Verification",
+ "level": 2,
+ "urlPath": "/docs/build/framework-matrix",
+ "urlWithHash": "/docs/build/framework-matrix#verification",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/build/framework-matrix#verification",
+ "children": []
+ }
+ ],
+ "logicalPath": "build/framework-matrix"
},
{
"urlPath": "/docs/build/generate-static-artifacts",
@@ -996,7 +1254,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/build/generate-static-artifacts#verify",
"children": []
}
- ]
+ ],
+ "logicalPath": "build/generate-static-artifacts"
},
{
"urlPath": "/docs/build/integrate-with-fumadocs",
@@ -1069,7 +1328,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/build/integrate-with-fumadocs#add-search",
"children": []
}
- ]
+ ],
+ "logicalPath": "build/integrate-with-fumadocs"
},
{
"urlPath": "/docs/build/optimize-docs-for-agents",
@@ -1161,7 +1421,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/build/optimize-docs-for-agents#minimal-checklist",
"children": []
}
- ]
+ ],
+ "logicalPath": "build/optimize-docs-for-agents"
},
{
"urlPath": "/docs/build/use-the-source-primitive",
@@ -1225,12 +1486,21 @@
"children": []
},
{
- "id": "vite-mdx-js-rollup-works-for-vue-solid-svelte-starters",
- "title": "Vite + @mdx-js/rollup works for Vue, Solid, Svelte starters",
+ "id": "vue-vite",
+ "title": "Vue + Vite",
+ "level": 3,
+ "urlPath": "/docs/build/use-the-source-primitive",
+ "urlWithHash": "/docs/build/use-the-source-primitive#vue-vite",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/build/use-the-source-primitive#vue-vite",
+ "children": []
+ },
+ {
+ "id": "vite-mdx-js-rollup-react-preact-solid-or-custom-jsx-runtimes",
+ "title": "Vite + @mdx-js/rollup React, Preact, Solid, or custom JSX runtimes",
"level": 3,
"urlPath": "/docs/build/use-the-source-primitive",
- "urlWithHash": "/docs/build/use-the-source-primitive#vite-mdx-js-rollup-works-for-vue-solid-svelte-starters",
- "absoluteUrlWithHash": "https://leadtype.dev/docs/build/use-the-source-primitive#vite-mdx-js-rollup-works-for-vue-solid-svelte-starters",
+ "urlWithHash": "/docs/build/use-the-source-primitive#vite-mdx-js-rollup-react-preact-solid-or-custom-jsx-runtimes",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/build/use-the-source-primitive#vite-mdx-js-rollup-react-preact-solid-or-custom-jsx-runtimes",
"children": []
},
{
@@ -1316,7 +1586,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/build/use-the-source-primitive#reference",
"children": []
}
- ]
+ ],
+ "logicalPath": "build/use-the-source-primitive"
},
{
"urlPath": "/docs/build/validate-in-ci",
@@ -1380,7 +1651,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/build/validate-in-ci#what-to-fix-first",
"children": []
}
- ]
+ ],
+ "logicalPath": "build/validate-in-ci"
}
],
"children": []
@@ -1482,7 +1754,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/package-docs/bundle#what-s-next",
"children": []
}
- ]
+ ],
+ "logicalPath": "package-docs/bundle"
}
],
"children": []
@@ -1495,10 +1768,122 @@
"title": "Reference",
"description": "CLI flags, conversion APIs, remark plugins, LLM files, search, and lint rules.",
"pages": [
+ {
+ "urlPath": "/docs/reference/architecture",
+ "title": "Architecture",
+ "description": "The core / adapter boundary — what ships where, and the rules adapters must follow.",
+ "groups": [
+ "reference"
+ ],
+ "toc": [
+ {
+ "id": "the-three-layers",
+ "title": "The three layers",
+ "level": 2,
+ "urlPath": "/docs/reference/architecture",
+ "urlWithHash": "/docs/reference/architecture#the-three-layers",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/architecture#the-three-layers",
+ "children": [
+ {
+ "id": "core",
+ "title": "Core",
+ "level": 3,
+ "urlPath": "/docs/reference/architecture",
+ "urlWithHash": "/docs/reference/architecture#core",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/architecture#core",
+ "children": []
+ },
+ {
+ "id": "host-runtime-adapters-leadtype-search",
+ "title": "Host-runtime adapters leadtype/search/",
+ "level": 3,
+ "urlPath": "/docs/reference/architecture",
+ "urlWithHash": "/docs/reference/architecture#host-runtime-adapters-leadtype-search",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/architecture#host-runtime-adapters-leadtype-search",
+ "children": []
+ },
+ {
+ "id": "framework-adapters",
+ "title": "Framework adapters",
+ "level": 3,
+ "urlPath": "/docs/reference/architecture",
+ "urlWithHash": "/docs/reference/architecture#framework-adapters",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/architecture#framework-adapters",
+ "children": []
+ }
+ ]
+ },
+ {
+ "id": "planned-adapter-shapes",
+ "title": "Planned adapter shapes",
+ "level": 2,
+ "urlPath": "/docs/reference/architecture",
+ "urlWithHash": "/docs/reference/architecture#planned-adapter-shapes",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/architecture#planned-adapter-shapes",
+ "children": []
+ },
+ {
+ "id": "rules",
+ "title": "Rules",
+ "level": 2,
+ "urlPath": "/docs/reference/architecture",
+ "urlWithHash": "/docs/reference/architecture#rules",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/architecture#rules",
+ "children": [
+ {
+ "id": "flat-naming",
+ "title": "Flat naming",
+ "level": 3,
+ "urlPath": "/docs/reference/architecture",
+ "urlWithHash": "/docs/reference/architecture#flat-naming",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/architecture#flat-naming",
+ "children": []
+ },
+ {
+ "id": "no-rendered-dom",
+ "title": "No rendered DOM",
+ "level": 3,
+ "urlPath": "/docs/reference/architecture",
+ "urlWithHash": "/docs/reference/architecture#no-rendered-dom",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/architecture#no-rendered-dom",
+ "children": []
+ },
+ {
+ "id": "dependency-rule",
+ "title": "Dependency rule",
+ "level": 3,
+ "urlPath": "/docs/reference/architecture",
+ "urlWithHash": "/docs/reference/architecture#dependency-rule",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/architecture#dependency-rule",
+ "children": []
+ },
+ {
+ "id": "enforcement",
+ "title": "Enforcement",
+ "level": 3,
+ "urlPath": "/docs/reference/architecture",
+ "urlWithHash": "/docs/reference/architecture#enforcement",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/architecture#enforcement",
+ "children": []
+ }
+ ]
+ },
+ {
+ "id": "adding-a-new-framework-adapter",
+ "title": "Adding a new framework adapter",
+ "level": 2,
+ "urlPath": "/docs/reference/architecture",
+ "urlWithHash": "/docs/reference/architecture#adding-a-new-framework-adapter",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/architecture#adding-a-new-framework-adapter",
+ "children": []
+ }
+ ],
+ "logicalPath": "reference/architecture"
+ },
{
"urlPath": "/docs/reference/cli",
"title": "CLI",
- "description": "leadtype generate and leadtype lint — flags, exit codes, and JSON output.",
+ "description": "leadtype generate, leadtype sync, and leadtype lint — flags, exit codes, and JSON output.",
"groups": [
"reference"
],
@@ -1558,6 +1943,43 @@
}
]
},
+ {
+ "id": "sync",
+ "title": "sync",
+ "level": 2,
+ "urlPath": "/docs/reference/cli",
+ "urlWithHash": "/docs/reference/cli#sync",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/cli#sync",
+ "children": [
+ {
+ "id": "sync-modes-at-a-glance",
+ "title": "Sync modes at a glance",
+ "level": 3,
+ "urlPath": "/docs/reference/cli",
+ "urlWithHash": "/docs/reference/cli#sync-modes-at-a-glance",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/cli#sync-modes-at-a-glance",
+ "children": []
+ },
+ {
+ "id": "cache-layout",
+ "title": "Cache layout",
+ "level": 3,
+ "urlPath": "/docs/reference/cli",
+ "urlWithHash": "/docs/reference/cli#cache-layout",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/cli#cache-layout",
+ "children": []
+ },
+ {
+ "id": "sharing-one-clone-across-collections",
+ "title": "Sharing one clone across collections",
+ "level": 3,
+ "urlPath": "/docs/reference/cli",
+ "urlWithHash": "/docs/reference/cli#sharing-one-clone-across-collections",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/cli#sharing-one-clone-across-collections",
+ "children": []
+ }
+ ]
+ },
{
"id": "lint",
"title": "lint",
@@ -1585,7 +2007,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/reference/cli#library-entry-points",
"children": []
}
- ]
+ ],
+ "logicalPath": "reference/cli"
},
{
"urlPath": "/docs/reference/convert",
@@ -1640,7 +2063,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/reference/convert#pairing-with-remark-plugins",
"children": []
}
- ]
+ ],
+ "logicalPath": "reference/convert"
},
{
"urlPath": "/docs/reference/evals",
@@ -1695,7 +2119,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/reference/evals#run-the-evals",
"children": []
}
- ]
+ ],
+ "logicalPath": "reference/evals"
},
{
"urlPath": "/docs/reference/lint",
@@ -1807,7 +2232,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/reference/lint#practical-guidance",
"children": []
}
- ]
+ ],
+ "logicalPath": "reference/lint"
},
{
"urlPath": "/docs/reference/llm",
@@ -1990,7 +2416,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/reference/llm#base-url-precedence",
"children": []
}
- ]
+ ],
+ "logicalPath": "reference/llm"
},
{
"urlPath": "/docs/reference/mdx",
@@ -2100,7 +2527,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/reference/mdx#re-exported-path-helpers",
"children": []
}
- ]
+ ],
+ "logicalPath": "reference/mdx"
},
{
"urlPath": "/docs/reference/remark",
@@ -2165,7 +2593,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/reference/remark#plugin-selection-rules",
"children": []
}
- ]
+ ],
+ "logicalPath": "reference/remark"
},
{
"urlPath": "/docs/reference/search",
@@ -2256,7 +2685,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/reference/search#when-to-add-embeddings",
"children": []
}
- ]
+ ],
+ "logicalPath": "reference/search"
},
{
"urlPath": "/docs/reference/source",
@@ -2421,7 +2851,8 @@
}
]
}
- ]
+ ],
+ "logicalPath": "reference/source"
}
],
"children": []
diff --git a/apps/example/src/generated/docs-nav.json b/apps/example/src/generated/docs-nav.json
index 4039693..5b4ee9a 100644
--- a/apps/example/src/generated/docs-nav.json
+++ b/apps/example/src/generated/docs-nav.json
@@ -43,7 +43,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs#next",
"children": []
}
- ]
+ ],
+ "logicalPath": "index"
},
{
"urlPath": "/docs/how-it-works",
@@ -116,7 +117,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/how-it-works#where-to-next",
"children": []
}
- ]
+ ],
+ "logicalPath": "how-it-works"
},
{
"urlPath": "/docs/methodology",
@@ -162,7 +164,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/methodology#when-the-combination-shines",
"children": []
}
- ]
+ ],
+ "logicalPath": "methodology"
},
{
"urlPath": "/docs/quickstart",
@@ -226,7 +229,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/quickstart#next-steps",
"children": []
}
- ]
+ ],
+ "logicalPath": "quickstart"
}
],
"children": []
@@ -239,6 +243,117 @@
"title": "Authoring",
"description": "The content contract: frontmatter, groups, and the MDX components the pipeline can flatten.",
"pages": [
+ {
+ "urlPath": "/docs/authoring/collections",
+ "title": "Collections",
+ "description": "Declare multi-source docs sets with defineCollection — local folders, git repos, and per-collection schemas.",
+ "groups": [
+ "authoring"
+ ],
+ "toc": [
+ {
+ "id": "when-to-reach-for-this",
+ "title": "When to reach for this",
+ "level": 2,
+ "urlPath": "/docs/authoring/collections",
+ "urlWithHash": "/docs/authoring/collections#when-to-reach-for-this",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/collections#when-to-reach-for-this",
+ "children": []
+ },
+ {
+ "id": "definecollection",
+ "title": "defineCollection",
+ "level": 2,
+ "urlPath": "/docs/authoring/collections",
+ "urlWithHash": "/docs/authoring/collections#definecollection",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/collections#definecollection",
+ "children": [
+ {
+ "id": "fields",
+ "title": "Fields",
+ "level": 3,
+ "urlPath": "/docs/authoring/collections",
+ "urlWithHash": "/docs/authoring/collections#fields",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/collections#fields",
+ "children": []
+ }
+ ]
+ },
+ {
+ "id": "local-only-collections",
+ "title": "Local-only collections",
+ "level": 2,
+ "urlPath": "/docs/authoring/collections",
+ "urlWithHash": "/docs/authoring/collections#local-only-collections",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/collections#local-only-collections",
+ "children": []
+ },
+ {
+ "id": "remote-collections",
+ "title": "Remote collections",
+ "level": 2,
+ "urlPath": "/docs/authoring/collections",
+ "urlWithHash": "/docs/authoring/collections#remote-collections",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/collections#remote-collections",
+ "children": []
+ },
+ {
+ "id": "multi-repo-framework-matrix",
+ "title": "Multi-repo: framework matrix",
+ "level": 2,
+ "urlPath": "/docs/authoring/collections",
+ "urlWithHash": "/docs/authoring/collections#multi-repo-framework-matrix",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/collections#multi-repo-framework-matrix",
+ "children": []
+ },
+ {
+ "id": "per-collection-schemas",
+ "title": "Per-collection schemas",
+ "level": 2,
+ "urlPath": "/docs/authoring/collections",
+ "urlWithHash": "/docs/authoring/collections#per-collection-schemas",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/collections#per-collection-schemas",
+ "children": []
+ },
+ {
+ "id": "per-collection-groups",
+ "title": "Per-collection groups",
+ "level": 2,
+ "urlPath": "/docs/authoring/collections",
+ "urlWithHash": "/docs/authoring/collections#per-collection-groups",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/collections#per-collection-groups",
+ "children": []
+ },
+ {
+ "id": "filtering",
+ "title": "Filtering",
+ "level": 2,
+ "urlPath": "/docs/authoring/collections",
+ "urlWithHash": "/docs/authoring/collections#filtering",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/collections#filtering",
+ "children": []
+ },
+ {
+ "id": "running-it",
+ "title": "Running it",
+ "level": 2,
+ "urlPath": "/docs/authoring/collections",
+ "urlWithHash": "/docs/authoring/collections#running-it",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/collections#running-it",
+ "children": []
+ },
+ {
+ "id": "what-you-do-not-migrate",
+ "title": "What you do not migrate",
+ "level": 2,
+ "urlPath": "/docs/authoring/collections",
+ "urlWithHash": "/docs/authoring/collections#what-you-do-not-migrate",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/collections#what-you-do-not-migrate",
+ "children": []
+ }
+ ],
+ "logicalPath": "authoring/collections"
+ },
{
"urlPath": "/docs/authoring/components",
"title": "Components",
@@ -410,7 +525,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/components#guidelines",
"children": []
}
- ]
+ ],
+ "logicalPath": "authoring/components"
},
{
"urlPath": "/docs/authoring/frontmatter",
@@ -475,7 +591,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/authoring/frontmatter#what-this-gives-you",
"children": []
}
- ]
+ ],
+ "logicalPath": "authoring/frontmatter"
}
],
"children": []
@@ -541,7 +658,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/build/build-a-docs-site#configure-product-and-groups",
"children": []
}
- ]
+ ],
+ "logicalPath": "build/build-a-docs-site"
},
{
"urlPath": "/docs/build/use-the-source-primitive",
@@ -605,12 +723,21 @@
"children": []
},
{
- "id": "vite-mdx-js-rollup-works-for-vue-solid-svelte-starters",
- "title": "Vite + @mdx-js/rollup works for Vue, Solid, Svelte starters",
+ "id": "vue-vite",
+ "title": "Vue + Vite",
"level": 3,
"urlPath": "/docs/build/use-the-source-primitive",
- "urlWithHash": "/docs/build/use-the-source-primitive#vite-mdx-js-rollup-works-for-vue-solid-svelte-starters",
- "absoluteUrlWithHash": "https://leadtype.dev/docs/build/use-the-source-primitive#vite-mdx-js-rollup-works-for-vue-solid-svelte-starters",
+ "urlWithHash": "/docs/build/use-the-source-primitive#vue-vite",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/build/use-the-source-primitive#vue-vite",
+ "children": []
+ },
+ {
+ "id": "vite-mdx-js-rollup-react-preact-solid-or-custom-jsx-runtimes",
+ "title": "Vite + @mdx-js/rollup React, Preact, Solid, or custom JSX runtimes",
+ "level": 3,
+ "urlPath": "/docs/build/use-the-source-primitive",
+ "urlWithHash": "/docs/build/use-the-source-primitive#vite-mdx-js-rollup-react-preact-solid-or-custom-jsx-runtimes",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/build/use-the-source-primitive#vite-mdx-js-rollup-react-preact-solid-or-custom-jsx-runtimes",
"children": []
},
{
@@ -696,7 +823,82 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/build/use-the-source-primitive#reference",
"children": []
}
- ]
+ ],
+ "logicalPath": "build/use-the-source-primitive"
+ },
+ {
+ "urlPath": "/docs/build/framework-matrix",
+ "title": "Framework integration matrix",
+ "description": "Use Leadtype with native-feeling recipes for Next, Nuxt, Astro, SvelteKit, TanStack Start, and Fumadocs.",
+ "groups": [
+ "docs-site"
+ ],
+ "toc": [
+ {
+ "id": "shared-build-step",
+ "title": "Shared build step",
+ "level": 2,
+ "urlPath": "/docs/build/framework-matrix",
+ "urlWithHash": "/docs/build/framework-matrix#shared-build-step",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/build/framework-matrix#shared-build-step",
+ "children": []
+ },
+ {
+ "id": "next-js-app-router",
+ "title": "Next.js App Router",
+ "level": 2,
+ "urlPath": "/docs/build/framework-matrix",
+ "urlWithHash": "/docs/build/framework-matrix#next-js-app-router",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/build/framework-matrix#next-js-app-router",
+ "children": []
+ },
+ {
+ "id": "astro",
+ "title": "Astro",
+ "level": 2,
+ "urlPath": "/docs/build/framework-matrix",
+ "urlWithHash": "/docs/build/framework-matrix#astro",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/build/framework-matrix#astro",
+ "children": []
+ },
+ {
+ "id": "sveltekit",
+ "title": "SvelteKit",
+ "level": 2,
+ "urlPath": "/docs/build/framework-matrix",
+ "urlWithHash": "/docs/build/framework-matrix#sveltekit",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/build/framework-matrix#sveltekit",
+ "children": []
+ },
+ {
+ "id": "nuxt-nitro",
+ "title": "Nuxt/Nitro",
+ "level": 2,
+ "urlPath": "/docs/build/framework-matrix",
+ "urlWithHash": "/docs/build/framework-matrix#nuxt-nitro",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/build/framework-matrix#nuxt-nitro",
+ "children": []
+ },
+ {
+ "id": "tanstack-start-and-fumadocs",
+ "title": "TanStack Start and Fumadocs",
+ "level": 2,
+ "urlPath": "/docs/build/framework-matrix",
+ "urlWithHash": "/docs/build/framework-matrix#tanstack-start-and-fumadocs",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/build/framework-matrix#tanstack-start-and-fumadocs",
+ "children": []
+ },
+ {
+ "id": "verification",
+ "title": "Verification",
+ "level": 2,
+ "urlPath": "/docs/build/framework-matrix",
+ "urlWithHash": "/docs/build/framework-matrix#verification",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/build/framework-matrix#verification",
+ "children": []
+ }
+ ],
+ "logicalPath": "build/framework-matrix"
},
{
"urlPath": "/docs/build/integrate-with-fumadocs",
@@ -769,7 +971,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/build/integrate-with-fumadocs#add-search",
"children": []
}
- ]
+ ],
+ "logicalPath": "build/integrate-with-fumadocs"
},
{
"urlPath": "/docs/build/generate-static-artifacts",
@@ -851,7 +1054,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/build/generate-static-artifacts#verify",
"children": []
}
- ]
+ ],
+ "logicalPath": "build/generate-static-artifacts"
},
{
"urlPath": "/docs/build/optimize-docs-for-agents",
@@ -943,7 +1147,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/build/optimize-docs-for-agents#minimal-checklist",
"children": []
}
- ]
+ ],
+ "logicalPath": "build/optimize-docs-for-agents"
},
{
"urlPath": "/docs/build/add-search",
@@ -1007,7 +1212,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/build/add-search#verify",
"children": []
}
- ]
+ ],
+ "logicalPath": "build/add-search"
},
{
"urlPath": "/docs/build/validate-in-ci",
@@ -1071,7 +1277,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/build/validate-in-ci#what-to-fix-first",
"children": []
}
- ]
+ ],
+ "logicalPath": "build/validate-in-ci"
}
],
"children": []
@@ -1173,7 +1380,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/package-docs/bundle#what-s-next",
"children": []
}
- ]
+ ],
+ "logicalPath": "package-docs/bundle"
}
],
"children": []
@@ -1186,10 +1394,122 @@
"title": "Reference",
"description": "CLI flags, conversion APIs, remark plugins, LLM files, search, and lint rules.",
"pages": [
+ {
+ "urlPath": "/docs/reference/architecture",
+ "title": "Architecture",
+ "description": "The core / adapter boundary — what ships where, and the rules adapters must follow.",
+ "groups": [
+ "reference"
+ ],
+ "toc": [
+ {
+ "id": "the-three-layers",
+ "title": "The three layers",
+ "level": 2,
+ "urlPath": "/docs/reference/architecture",
+ "urlWithHash": "/docs/reference/architecture#the-three-layers",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/architecture#the-three-layers",
+ "children": [
+ {
+ "id": "core",
+ "title": "Core",
+ "level": 3,
+ "urlPath": "/docs/reference/architecture",
+ "urlWithHash": "/docs/reference/architecture#core",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/architecture#core",
+ "children": []
+ },
+ {
+ "id": "host-runtime-adapters-leadtype-search",
+ "title": "Host-runtime adapters leadtype/search/",
+ "level": 3,
+ "urlPath": "/docs/reference/architecture",
+ "urlWithHash": "/docs/reference/architecture#host-runtime-adapters-leadtype-search",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/architecture#host-runtime-adapters-leadtype-search",
+ "children": []
+ },
+ {
+ "id": "framework-adapters",
+ "title": "Framework adapters",
+ "level": 3,
+ "urlPath": "/docs/reference/architecture",
+ "urlWithHash": "/docs/reference/architecture#framework-adapters",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/architecture#framework-adapters",
+ "children": []
+ }
+ ]
+ },
+ {
+ "id": "planned-adapter-shapes",
+ "title": "Planned adapter shapes",
+ "level": 2,
+ "urlPath": "/docs/reference/architecture",
+ "urlWithHash": "/docs/reference/architecture#planned-adapter-shapes",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/architecture#planned-adapter-shapes",
+ "children": []
+ },
+ {
+ "id": "rules",
+ "title": "Rules",
+ "level": 2,
+ "urlPath": "/docs/reference/architecture",
+ "urlWithHash": "/docs/reference/architecture#rules",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/architecture#rules",
+ "children": [
+ {
+ "id": "flat-naming",
+ "title": "Flat naming",
+ "level": 3,
+ "urlPath": "/docs/reference/architecture",
+ "urlWithHash": "/docs/reference/architecture#flat-naming",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/architecture#flat-naming",
+ "children": []
+ },
+ {
+ "id": "no-rendered-dom",
+ "title": "No rendered DOM",
+ "level": 3,
+ "urlPath": "/docs/reference/architecture",
+ "urlWithHash": "/docs/reference/architecture#no-rendered-dom",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/architecture#no-rendered-dom",
+ "children": []
+ },
+ {
+ "id": "dependency-rule",
+ "title": "Dependency rule",
+ "level": 3,
+ "urlPath": "/docs/reference/architecture",
+ "urlWithHash": "/docs/reference/architecture#dependency-rule",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/architecture#dependency-rule",
+ "children": []
+ },
+ {
+ "id": "enforcement",
+ "title": "Enforcement",
+ "level": 3,
+ "urlPath": "/docs/reference/architecture",
+ "urlWithHash": "/docs/reference/architecture#enforcement",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/architecture#enforcement",
+ "children": []
+ }
+ ]
+ },
+ {
+ "id": "adding-a-new-framework-adapter",
+ "title": "Adding a new framework adapter",
+ "level": 2,
+ "urlPath": "/docs/reference/architecture",
+ "urlWithHash": "/docs/reference/architecture#adding-a-new-framework-adapter",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/architecture#adding-a-new-framework-adapter",
+ "children": []
+ }
+ ],
+ "logicalPath": "reference/architecture"
+ },
{
"urlPath": "/docs/reference/cli",
"title": "CLI",
- "description": "leadtype generate and leadtype lint — flags, exit codes, and JSON output.",
+ "description": "leadtype generate, leadtype sync, and leadtype lint — flags, exit codes, and JSON output.",
"groups": [
"reference"
],
@@ -1249,6 +1569,43 @@
}
]
},
+ {
+ "id": "sync",
+ "title": "sync",
+ "level": 2,
+ "urlPath": "/docs/reference/cli",
+ "urlWithHash": "/docs/reference/cli#sync",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/cli#sync",
+ "children": [
+ {
+ "id": "sync-modes-at-a-glance",
+ "title": "Sync modes at a glance",
+ "level": 3,
+ "urlPath": "/docs/reference/cli",
+ "urlWithHash": "/docs/reference/cli#sync-modes-at-a-glance",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/cli#sync-modes-at-a-glance",
+ "children": []
+ },
+ {
+ "id": "cache-layout",
+ "title": "Cache layout",
+ "level": 3,
+ "urlPath": "/docs/reference/cli",
+ "urlWithHash": "/docs/reference/cli#cache-layout",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/cli#cache-layout",
+ "children": []
+ },
+ {
+ "id": "sharing-one-clone-across-collections",
+ "title": "Sharing one clone across collections",
+ "level": 3,
+ "urlPath": "/docs/reference/cli",
+ "urlWithHash": "/docs/reference/cli#sharing-one-clone-across-collections",
+ "absoluteUrlWithHash": "https://leadtype.dev/docs/reference/cli#sharing-one-clone-across-collections",
+ "children": []
+ }
+ ]
+ },
{
"id": "lint",
"title": "lint",
@@ -1276,7 +1633,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/reference/cli#library-entry-points",
"children": []
}
- ]
+ ],
+ "logicalPath": "reference/cli"
},
{
"urlPath": "/docs/reference/convert",
@@ -1331,7 +1689,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/reference/convert#pairing-with-remark-plugins",
"children": []
}
- ]
+ ],
+ "logicalPath": "reference/convert"
},
{
"urlPath": "/docs/reference/evals",
@@ -1386,7 +1745,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/reference/evals#run-the-evals",
"children": []
}
- ]
+ ],
+ "logicalPath": "reference/evals"
},
{
"urlPath": "/docs/reference/lint",
@@ -1498,7 +1858,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/reference/lint#practical-guidance",
"children": []
}
- ]
+ ],
+ "logicalPath": "reference/lint"
},
{
"urlPath": "/docs/reference/llm",
@@ -1681,7 +2042,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/reference/llm#base-url-precedence",
"children": []
}
- ]
+ ],
+ "logicalPath": "reference/llm"
},
{
"urlPath": "/docs/reference/mdx",
@@ -1791,7 +2153,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/reference/mdx#re-exported-path-helpers",
"children": []
}
- ]
+ ],
+ "logicalPath": "reference/mdx"
},
{
"urlPath": "/docs/reference/remark",
@@ -1856,7 +2219,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/reference/remark#plugin-selection-rules",
"children": []
}
- ]
+ ],
+ "logicalPath": "reference/remark"
},
{
"urlPath": "/docs/reference/search",
@@ -1947,7 +2311,8 @@
"absoluteUrlWithHash": "https://leadtype.dev/docs/reference/search#when-to-add-embeddings",
"children": []
}
- ]
+ ],
+ "logicalPath": "reference/search"
},
{
"urlPath": "/docs/reference/source",
@@ -2112,7 +2477,8 @@
}
]
}
- ]
+ ],
+ "logicalPath": "reference/source"
}
],
"children": []
diff --git a/apps/example/src/generated/docs-pages.json b/apps/example/src/generated/docs-pages.json
index 1373be4..e1e841b 100644
--- a/apps/example/src/generated/docs-pages.json
+++ b/apps/example/src/generated/docs-pages.json
@@ -1,4 +1,19 @@
[
+ {
+ "slug": [
+ "authoring",
+ "collections"
+ ],
+ "urlPath": "/docs/authoring/collections",
+ "title": "Collections",
+ "description": "Declare multi-source docs sets with defineCollection — local folders, git repos, and per-collection schemas.",
+ "relativePath": "authoring/collections",
+ "extension": ".mdx",
+ "groups": [
+ "authoring"
+ ],
+ "globKey": "../../../../../docs/authoring/collections.mdx"
+ },
{
"slug": [
"authoring",
@@ -59,6 +74,21 @@
],
"globKey": "../../../../../docs/build/build-a-docs-site.mdx"
},
+ {
+ "slug": [
+ "build",
+ "framework-matrix"
+ ],
+ "urlPath": "/docs/build/framework-matrix",
+ "title": "Framework integration matrix",
+ "description": "Use Leadtype with native-feeling recipes for Next, Nuxt, Astro, SvelteKit, TanStack Start, and Fumadocs.",
+ "relativePath": "build/framework-matrix",
+ "extension": ".mdx",
+ "groups": [
+ "docs-site"
+ ],
+ "globKey": "../../../../../docs/build/framework-matrix.mdx"
+ },
{
"slug": [
"build",
@@ -203,6 +233,21 @@
],
"globKey": "../../../../../docs/quickstart.mdx"
},
+ {
+ "slug": [
+ "reference",
+ "architecture"
+ ],
+ "urlPath": "/docs/reference/architecture",
+ "title": "Architecture",
+ "description": "The core / adapter boundary — what ships where, and the rules adapters must follow.",
+ "relativePath": "reference/architecture",
+ "extension": ".mdx",
+ "groups": [
+ "reference"
+ ],
+ "globKey": "../../../../../docs/reference/architecture.mdx"
+ },
{
"slug": [
"reference",
@@ -210,7 +255,7 @@
],
"urlPath": "/docs/reference/cli",
"title": "CLI",
- "description": "leadtype generate and leadtype lint — flags, exit codes, and JSON output.",
+ "description": "leadtype generate, leadtype sync, and leadtype lint — flags, exit codes, and JSON output.",
"relativePath": "reference/cli",
"extension": ".mdx",
"groups": [
diff --git a/apps/example/src/generated/docs-search-content.json b/apps/example/src/generated/docs-search-content.json
index d324cf6..77d8702 100644
--- a/apps/example/src/generated/docs-search-content.json
+++ b/apps/example/src/generated/docs-search-content.json
@@ -1 +1 @@
-{"version":2,"generatedAt":"2026-05-13T16:50:59.893Z","chunks":["Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nLeadtype does not ship UI components. Your docs app owns runtime rendering, styling, and accessibility — it only has to honor a small naming contract so the remark pipeline can flatten each component into markdown for agents, search, and llms-full.txt .","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nWhy flatten at all?\n\nInteractive MDX components like ./shared/session-flow.mdx ``` ```mdx ``` ```mdx ``` ```mdx This content can be reused in multiple pages. ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nEach section below shows the authored MDX and a live render.","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nCallout\n\nWraps supporting context, warnings, or tips. Variants change styling but flatten identically into a blockquote. ℹ️ Info Heads up Callouts wrap supporting context, warnings, or tips. The remark pipeline flattens them into blockquotes so agents still see the content. ⚠️ Warning Don't do this Variants like warning, success, error, and tip change the styling but flatten identically.\n\n```tsx Body content goes here. ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nCards\n\nA grid of short, linked entry points. Flattens to a bullet list of links. Convert Remark Search\n\n```tsx ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nSteps\n\nNumbered walkthroughs. Flattens to an ordered list with bold step titles. 1. Author docs in MDX Use the components in this list as authoring affordances. 2. Run the conversion leadtype generate writes flattened markdown to public/docs/ . 3. Serve both formats HTML for humans, .md for agents — same URL, content negotiated by the Accept header.\n\n```tsx Use the components in this list.`leadtype generate` writes flattened markdown. ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nTabs\n\nGroup equivalent content. Flattens to bold headings followed by content so agents do not need a JSX-aware renderer to read every variant. tanstack-start Use a Vite middleware dev/preview and a Nitro middleware prod to negotiate the Accept header. next-js Wire content negotiation in middleware.ts and serve .md from a route handler. vite A configureServer middleware is enough for static deployments where .md files live in public/ .\n\n```tsx ……… ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nCommandTabs\n\nPackage-manager-aware install or run commands. Flattens to a markdown table with one row per manager. Use mode=\"install\" when command is a package name, mode=\"run\" when command is a CLI name, and mode=\"create\" for starter commands. Use the commands prop for exact per-manager overrides. Package manager Command -- -- npm npm install leadtype pnpm pnpm add leadtype yarn yarn add leadtype bun bun add leadtype Package manager Command -- -- npm npx leadtype lint pnpm pnpm dlx leadtype lint yarn yarn dlx leadtype lint bun bunx leadtype lint\n\n```tsx ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nPrompt\n\nDisplays an agent-ready prompt with a copy action. Flattening preserves the prompt body as a fenced prompt block so copied instructions also survive in .md , root llms-full.txt , and bundled AGENTS.md output. Copy prompt for your coding agent Use this after generating artifacts.\n\n```tsx Inspect `public/docs/agent-readability.json`, then wire markdown responses before the HTML docs route. ``` ```prompt Inspect `public/docs/agent-readability.json`, then wire markdown responses before the HTML docs route. ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nAudience\n\nSplits browser-only and agent-only guidance without forking the page. Human content renders in the docs UI and is omitted from markdown output; agent content is hidden in the browser and included in generated markdown. This sentence appears only in generated markdown for agents.\n\n```tsx Click the robot icon in the header.Read generated markdown before editing runtime routes. ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nFileTree\n\nShows project structure in guides and release instructions. Flattening emits a fenced text tree so agents can read the same hierarchy without JSX.\n\n```tsx ``` ```text public/ ├── llms.txt └── docs/ └── index.md ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nAccordion\n\nCollapsible details for secondary content. Flattening ignores open/closed state and emits every item — accordions are not a place to hide content from agents. When should I use accordions? Use them for supporting details, troubleshooting notes, and optional reference material. Closed content is still flattened by the remark pipeline. Are accordions a good place to hide content from LLMs? No. Conversion ignores the open/closed state and emits everything inside.\n\n```tsx Use them for supporting details and optional reference material. ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nTopicSwitcher\n\nNavigation across equivalent docs topics — frameworks, SDKs, runtimes, deployment targets, product areas. Reader-facing only; it does not automatically read LLM topic config. Framework React — React integration Vue — Vue integration Svelte — Svelte integration\n\n```tsx ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nTypeTable and ExtractedTypeTable\n\nTypeTable is for explicit prop or type rows you already know. ExtractedTypeTable reads a TypeScript file at conversion time and extracts the table from a named type — keep its path stable. Property Type Description Default Required -- -- -- -- -- title string Heading rendered above the callout body. - ✅ Required variant CalloutVariant Visual treatment for the callout. info Optional deprecated \\ \\ boolean\\ \\ deprecated Marks the row as deprecated. false Optional\n\n```tsx ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nExample\n\nData-driven preview and source examples. The host component receives code as data; add file loaders or dynamic imports outside leadtype when an example needs app-specific behavior.\n\n```tsx The host app owns styling and runtime components while leadtype owns conversion. ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nMermaid\n\nDiagrams authored as plain text. Renders client-side as interactive SVG. Flattening preserves the source as a fenced mermaid block so other tools can render the diagram from the markdown copy.\n\n```tsx |remark| Markdown Markdown -->|llms.txt| Agents`} /> ``` ```mermaid graph LR MDX -->|remark| Markdown Markdown -->|search index| API Markdown -->|llms.txt| Agents ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nGuidelines\n\nKeep runtime components in your docs app. Leadtype stays out of UI. Keep component names stable. Renaming groups: [authoring, docs-site, reference]\"] page1[\"page A group: authoring\"] page2[\"page B group: docs-site\"] page3[\"page C group: docs-site\"] resolver[\"group resolver\"] nav[\"navigation tree\"] llms[\"llms.txt sections\"] agents[\"AGENTS.md sections\"] cfg --> resolver page1 --> resolver page2 --> resolver page3 --> resolver resolver --> nav resolver --> llms resolver --> agents ```","Frontmatter\n\nRequired fields, group semantics, and how authored MDX becomes a navigation tree.\n\nFrontmatter\n\nHow groups become a nav tree\n\nNested groups\n\nDeclare children in the config to build deeper trees A page sets group remote-source and lands in that nested slot. Only leaf groups no children directly contain pages; non-leaf groups are headings only.\n\n```ts { slug: \"docs-site\", title: \"Build a Docs Site\", children: [ { slug: \"remote-source\", title: \"Remote source\" }, { slug: \"build-a-docs-site\", title: \"Build a docs site\" }, ], } ```","Frontmatter\n\nRequired fields, group semantics, and how authored MDX becomes a navigation tree.\n\nFrontmatter\n\nOptional fields\n\nThe default lint schema also accepts Property Type Description Default Required -- -- -- -- -- icon string Icon name resolved by your sidebar component. - Optional deprecated boolean Marks the page as deprecated in nav and search. - Optional deprecatedReason string Short message paired with deprecated. - Optional experimental boolean Marks the page as experimental. - Optional canary boolean Hides from stable channels. - Optional new boolean Highlights the page as recently added. - Optional draft boolean Excludes from generation entirely. - Optional tags string\\ Free-form tags for search facets. - Optional availableIn Array\\<\\ framework, url?, title? Cross-framework availability map for TopicSwitcher pages. - Optional full boolean Layout hint for docs UIs that support full-width pages. - Optional lastModified and lastAuthor are filled in automatically when you pass --enrich-git to the CLI. Don't author them by hand.","Frontmatter\n\nRequired fields, group semantics, and how authored MDX becomes a navigation tree.\n\nFrontmatter\n\nLint rules\n\nleadtype lint enforces the schema, so violations surface in CI before they reach a build. The relevant rules schema — a required field is missing or has the wrong type. unknown-field — a top-level field isn't in the schema warn by default; --error-unknown to fail . parse-error — frontmatter or meta.json doesn't parse. invalid-link — a /docs/... link points to a route that doesn't exist. unresolved-placeholder — a doc URL still contains an unresolved framework placeholder. cross-framework-link — a framework-scoped page links to another framework's docs. See Lint rules for the full reference and how to extend the schema.","Frontmatter\n\nRequired fields, group semantics, and how authored MDX becomes a navigation tree.\n\nFrontmatter\n\nWhat this gives you\n\nOnce frontmatter is consistent, the rest of the pipeline works without per-page configuration. The same group value drives The sidebar position The llms.txt section Search metadata and AGENTS.md grouping The search filtering UI if you build one Cross-framework link checks in lint One field, one source of truth.","Add search\n\nGenerate a static docs search index, query it at runtime, and optionally stream source-grounded answers.\n\nAdd search\n\nLeadtype search is static by default. Build time produces two JSON files; runtime code imports or fetches those files and queries them without a database.","Add search\n\nGenerate a static docs search index, query it at runtime, and optionally stream source-grounded answers.\n\nAdd search\n\nGenerate the files\n\nleadtype generate writes search files in site mode If you run the pipeline from scripts, call the search generator after conversion This writes The index contains compact ranking data. The content store contains the text used for excerpts and answer context.\n\n```bash npx leadtype generate --src . --out public --base-url https://example.com ``` ```ts import { generateDocsSearchFiles } from \"leadtype/search/node\"; await generateDocsSearchFiles({ outDir: \"public\", baseUrl: \"https://example.com\", }); ``` ```txt public/docs/search-index.json public/docs/search-content.json ```","Add search\n\nGenerate a static docs search index, query it at runtime, and optionally stream source-grounded answers.\n\nAdd search\n\nQuery at runtime\n\nResults include page URLs, heading paths, hash URLs, and snippets. Render them in your own search UI.\n\n```ts import { searchDocs, type DocsSearchContentStore, type DocsSearchIndex, } from \"leadtype/search\"; import contentJson from \"../public/docs/search-content.json\"; import indexJson from \"../public/docs/search-index.json\"; const index = indexJson as DocsSearchIndex; const content = contentJson as DocsSearchContentStore; const results = searchDocs(index, \"run lint\", { content }); ```","Add search\n\nGenerate a static docs search index, query it at runtime, and optionally stream source-grounded answers.\n\nAdd search\n\nAdd vocabulary aliases\n\nSearch starts with lexical matching, stemming, prefix matches, typo-tolerant fallbacks, and a small built-in synonym map. Add product-specific synonyms only when users search with words your docs do not use\n\n```ts const results = searchDocs(index, \"starter\", { content, synonyms: { starter: [\"quickstart\", \"getting started\"], }, }); ```","Add search\n\nGenerate a static docs search index, query it at runtime, and optionally stream source-grounded answers.\n\nAdd search\n\nOptional AI answers\n\nUse source-grounded answers only after basic search works. Leadtype retrieves chunks from the static index, builds a constrained prompt, and leaves model choice to the provider entry point you import Display sources next to the streamed response. Do not ask the model to answer from memory; the answer context is built from retrieved docs chunks.\n\n```ts import { streamDocsAnswer } from \"leadtype/search/vercel\"; const { response, sources } = streamDocsAnswer({ index, content, query: \"How do I run docs lint in CI?\", model: \"openai/gpt-5.5\", productName: \"My Library\", }); ```","Add search\n\nGenerate a static docs search index, query it at runtime, and optionally stream source-grounded answers.\n\nAdd search\n\nGuard the endpoint\n\nFor API routes that accept user queries, use the request helpers from leadtype/search validateDocsQuery to trim and cap query text. readJsonWithLimit to reject oversized JSON bodies. getClientIdentifier to read common proxy IP headers. createMemoryRateLimiter for demos. Production apps should adapt the rate limiter interface to a shared store such as Redis, Vercel KV, Cloudflare KV, or Durable Objects.","Add search\n\nGenerate a static docs search index, query it at runtime, and optionally stream source-grounded answers.\n\nAdd search\n\nVerify\n\npublic/docs/search-index.json and public/docs/search-content.json exist and are non-empty. Searching for an exact API name returns the expected reference page. Searching for a guide phrase returns a result with a section hash. AI answers cite sources from the returned sources metadata.","Build a docs site\n\nPick the right leadtype integration shape for your docs app, and what each path gives you.\n\nBuild a docs site\n\nLeadtype offers two integration shapes for a docs site. Pick the one that fits how your app is built — both produce the same content, just at different layers of your stack.","Build a docs site\n\nPick the right leadtype integration shape for your docs app, and what each path gives you.\n\nBuild a docs site\n\nPick your path\n\nPath When to choose What you wire -- -- -- Source primitive Most cases. You're building a Next, Vite, Astro, or Fumadocs app and compile MDX with your bundler. createDocsSource or leadtype/fumadocs + mdxSourcePlugins Static artifacts Your runtime needs flat files on disk CDN-only deploy, static export, agent-only consumption, multi-app sharing . leadtype generate CLI in your build script The two paths are not mutually exclusive — you can use the source primitive for your UI and still run leadtype generate for the llms.txt and agent-readability artifacts.\n\n```mermaid flowchart LR src[\"source MDX docs/*.mdx + meta.json\"] primitive[\"createDocsSource() (leadtype/mdx + leadtype/fumadocs)\"] cli[\"leadtype generate (CLI)\"] app[\"docs app (Next, Astro, Vite, Fumadocs)\"] artifacts[\"public/ llms.txt · docs/*.md search-index · sitemap\"] src --> primitive --> app src --> cli --> artifacts --> app ```","Build a docs site\n\nPick the right leadtype integration shape for your docs app, and what each path gives you.\n\nBuild a docs site\n\nSource primitive the common path\n\nIf your docs app compiles MDX through a bundler, use the source primitive. It gives your runtime loadPage slug returning frontmatter + AST + serialized markdown + TOC getNavigation returning the resolved group tree buildSearchIndex returning a static search index resolveInclude for programmatic partial loading → Use the source primitive — generic recipe → Integrate with Fumadocs — first-party adapter","Build a docs site\n\nPick the right leadtype integration shape for your docs app, and what each path gives you.\n\nBuild a docs site\n\nStatic artifacts the CLI path\n\nIf your runtime needs files on disk — for a CDN-only deploy, a static export, a separate agent-only service, or to share artifacts across multiple sibling apps — run the CLI. It writes public/llms.txt and public/llms-full.txt public/docs/ .md markdown mirrors public/docs/search-index.json + search-content.json public/docs/sitemap.xml , sitemap.md , robots.txt , agent-readability.json → Generate static artifacts — CLI workflow","Build a docs site\n\nPick the right leadtype integration shape for your docs app, and what each path gives you.\n\nBuild a docs site\n\nAdd the cross-cutting features\n\nWhichever path you pick, the same follow-on guides apply Add search — local search index + optional source-grounded answers Optimize docs for agents — markdown responses, llms.txt, discovery files Validate in CI — lint frontmatter, meta.json, internal links","Build a docs site\n\nPick the right leadtype integration shape for your docs app, and what each path gives you.\n\nBuild a docs site\n\nConfigure product and groups\n\nBoth paths read the same docs/docs.config.ts . Source repos own this file so groups and product metadata version with the docs Pages declare group in frontmatter; the config declares titles, order, and descriptions. See Frontmatter for the page-level schema.\n\n```ts import { defineDocsConfig } from \"leadtype\"; export default defineDocsConfig({ product: { name: \"c15t\", summary: \"Consent infrastructure for modern web apps.\", bullets: [\"Framework integrations.\", \"Consent primitives.\", \"Audit-friendly docs.\"], bestStartingPoints: [{ urlPath: \"/docs\" }, { urlPath: \"/docs/quickstart\" }], }, groups: [ { slug: \"get-started\", title: \"Get Started\" }, { slug: \"guides\", title: \"Guides\" }, { slug: \"reference\", title: \"Reference\" }, ], }); ```","Generate static artifacts\n\nRun leadtype generate from your build pipeline to write llms.txt, markdown mirrors, search index, sitemap, and agent-readability files to disk.\n\nGenerate static artifacts\n\nUse this path when your runtime needs files on disk — a CDN-only deploy, a static export, a separate agent-only service, or multiple sibling apps sharing one corpus. The leadtype generate CLI walks your MDX source and writes a complete set of files into public/ . For app runtimes that compile MDX through a bundler Next, Astro, Vite, Fumadocs , prefer the source primitive — it skips the disk round-trip.","Generate static artifacts\n\nRun leadtype generate from your build pipeline to write llms.txt, markdown mirrors, search index, sitemap, and agent-readability files to disk.\n\nGenerate static artifacts\n\nThe flow\n\n```mermaid flowchart LR repo[\"source repo docs/*.mdx\"] clone[\"checkout or clone .docs-src/c15t\"] lint[\"leadtype lint\"] generate[\"leadtype generate\"] public[\"public/ llms.txt · llms-full.txt docs/*.md search · agent metadata\"] app[\"any consumer (docs app, agent, CDN)\"] repo --> clone --> lint --> generate --> public --> app ```","Generate static artifacts\n\nRun leadtype generate from your build pipeline to write llms.txt, markdown mirrors, search index, sitemap, and agent-readability files to disk.\n\nGenerate static artifacts\n\nFetch the source repo\n\nIn CI, check out the docs source before the build. For a public repo, a shallow clone is enough For private repos, use your CI platform's checkout action or a read-only deploy key. The important part is that leadtype receives a normal filesystem path whose root contains docs/ .\n\n```bash rm -rf .docs-src/c15t git clone --depth 1 https://github.com/c15t/c15t .docs-src/c15t ```","Generate static artifacts\n\nRun leadtype generate from your build pipeline to write llms.txt, markdown mirrors, search index, sitemap, and agent-readability files to disk.\n\nGenerate static artifacts\n\nLint before generate\n\nRun lint against the fetched docs before writing generated output. Lint fails with file and line context, which is easier to debug than a later app build using stale artifacts\n\n```bash npx leadtype lint .docs-src/c15t/docs \\ --format github \\ --error-unknown \\ --max-warnings 0 ```","Generate static artifacts\n\nRun leadtype generate from your build pipeline to write llms.txt, markdown mirrors, search index, sitemap, and agent-readability files to disk.\n\nGenerate static artifacts\n\nGenerate\n\nPoint --src at the fetched repo root and --out at the directory you'll serve That command writes public/llms.txt and public/llms-full.txt public/docs/ .md public/docs/search-index.json and public/docs/search-content.json public/docs/sitemap.xml , sitemap.md , robots.txt , and agent-readability.json Use --json in CI so automation can record resolved groups, output files, filters, and search index stats.\n\n```bash npx leadtype generate \\ --src .docs-src/c15t \\ --out public \\ --base-url https://docs.example.com \\ --json ```","Generate static artifacts\n\nRun leadtype generate from your build pipeline to write llms.txt, markdown mirrors, search index, sitemap, and agent-readability files to disk.\n\nGenerate static artifacts\n\nMulti-source mounting\n\nSome projects keep docs and release notes side by side instead of putting everything under docs/ Repeat --docs-dir to include those folders in the same generated corpus By default, extra sources are mounted under /docs/ {title ?
{title}
: null}
{children}
); } ```","Integrate with Fumadocs\n\nWire leadtype's content layer into a fumadocs app for nav, search, and includes.\n\nIntegrate with Fumadocs\n\nLoad a page from a server component\n\nIf you prefer fumadocs's built-in page resolution, call source.getPage slug and import the source .mdx directly through fumadocs-mdx as you normally would — the mdxSourcePlugins preset will resolve includes during MDX compilation.\n\n```tsx title=\"app/docs/[[...slug]]/page.tsx\" import { notFound } from \"next/navigation\"; import { MDXRemote } from \"next-mdx-remote-client/rsc\"; import { source } from \"@/content/source\"; import { mdxComponents } from \"@/lib/mdx-components\"; export default async function Page({ params, }: { params: Promise<{ slug?: string[] }>; }) { const { slug } = await params; const page = await source.leadtype.loadPage(slug ?? []); if (!page) { notFound(); } return (
{page.title}
); } ```","Integrate with Fumadocs\n\nWire leadtype's content layer into a fumadocs app for nav, search, and includes.\n\nIntegrate with Fumadocs\n\nAdd search\n\nBuild a search index from the same source For provider-specific search Vercel AI, TanStack, Cloudflare , wire the bundle into a leadtype/search/ adapter. See Search.\n\n```ts title=\"app/api/search/route.ts\" import { source } from \"@/content/source\"; const bundle = await source.leadtype.buildSearchIndex(); export async function GET() { return Response.json(bundle.index); } ```","Optimize docs for agents\n\nSet up llms.txt, markdown mirrors, JSON-LD, sitemaps, robots.txt, and audit checks for an agent-readable docs site.\n\nOptimize docs for agents\n\nUse this guide when you already have a docs site and want agents to find, fetch, attribute, and cite the same content humans read in the browser. Leadtype handles the generated files. Your app wires those files into routing and HTML. The default output shape is based on the repo's agent evals. See Evals for the benchmark summary and the open question around larger-corpus llms-full.txt scaling.","Optimize docs for agents\n\nSet up llms.txt, markdown mirrors, JSON-LD, sitemaps, robots.txt, and audit checks for an agent-readable docs site.\n\nOptimize docs for agents\n\nWhat good looks like\n\nAn agent-readable docs site has four layers 1. Discovery files /llms.txt , /sitemap.xml , /sitemap.md , and /robots.txt tell agents what exists and where to start. 2. Markdown retrieval Each docs page has a markdown mirror at /docs/page.md , and agent requests to /docs/page can receive markdown instead of HTML. 3. Structured HTML metadata Human HTML pages include JSON-LD, canonical links, and markdown alternate links so agents can extract page identity without guessing from the DOM. 4. Attribution metadata Markdown responses include canonical url and last updated frontmatter so copied content keeps its source and freshness.","Optimize docs for agents\n\nSet up llms.txt, markdown mirrors, JSON-LD, sitemaps, robots.txt, and audit checks for an agent-readable docs site.\n\nOptimize docs for agents\n\n1. Generate the artifacts\n\nRun the site-mode pipeline before your app build This writes the docs-scoped files under public/docs/ and the top-level public/llms.txt The generated agent-readability.json manifest is the bridge between build-time content and runtime requests. It contains page URLs, markdown mirror paths, titles, descriptions, group navigation, and freshness dates.\n\n```bash npx leadtype generate \\ --src . \\ --out public \\ --base-url https://leadtype.dev \\ --name \"My product\" \\ --summary \"One sentence about the product.\" ``` ```txt public/ ├── llms.txt ├── llms-full.txt └── docs/ ├── index.md ├── quickstart.md ├── llms.txt ├── sitemap.xml ├── sitemap.md ├── robots.txt └── agent-readability.json ```","Optimize docs for agents\n\nSet up llms.txt, markdown mirrors, JSON-LD, sitemaps, robots.txt, and audit checks for an agent-readable docs site.\n\nOptimize docs for agents\n\n2. Add one middleware\n\nPut the agent-readable routes before your HTML docs route. This Node/Bun example handles root discovery files, docs-scoped discovery files, direct .md URLs, and Accept text/markdown requests in one place If you also have marketing, blog, changelog, or product pages, pass them through the optional pages field — the regenerator merges them into the rebased output The other generated artifacts — /llms.txt , /docs/llms.txt , /llms-full.txt , /docs/agent-readability.json — use root-relative links and serve fine as static files straight from public/ . Keep the docs-scoped versions too /docs/sitemap.xml etc. . Audits and agents may request both /sitemap.xml and /docs/sitemap.xml , especially when the audited URL is /docs .\n\n```ts import { readFile } from \"node:fs/promises\"; import { join } from \"node:path\"; import manifestJson from \"../public/docs/agent-readability.json\" with { type: \"json\", }; import { createAgentMarkdownResponse, createRobotsTxtResponse, createSitemapMarkdownResponse, createSitemapXmlResponse, type AgentReadabilityManifest, type MarkdownMirrorTarget, } from \"leadtype/llm/readability\"; const manifest = { ...manifestJson, version: 1, } as AgentReadabilityManifest; async function readMarkdownFile( target: MarkdownMirrorTarget ): Promise { try { return await readFile(join(process.cwd(), \"public\", target.filePath), \"utf8\"); } catch (error) { if ( typeof error === \"object\" && error !== null && \"code\" in error && (error.code === \"ENOENT\" || error.code === \"ENOTDIR\") ) { return null; } throw error; } } export async function handleDocsRequest( request: Request ): Promise { if (request.method !== \"GET\" && request.method !== \"HEAD\") { return null; } const url = new URL(request.url); const requestOrigin = url.origin; switch (url.pathname) { case \"/sitemap.xml\": case \"/docs/sitemap.xml\": return createSitemapXmlResponse({ manifest, requestOrigin }); case","Optimize docs for agents\n\nSet up llms.txt, markdown mirrors, JSON-LD, sitemaps, robots.txt, and audit checks for an agent-readable docs site.\n\nOptimize docs for agents\n\n2. Add one middleware\n\nstOrigin = url.origin; switch (url.pathname) { case \"/sitemap.xml\": case \"/docs/sitemap.xml\": return createSitemapXmlResponse({ manifest, requestOrigin }); case \"/sitemap.md\": case \"/docs/sitemap.md\": return createSitemapMarkdownResponse({ manifest, requestOrigin }); case \"/robots.txt\": return createRobotsTxtResponse({ manifest, requestOrigin }); case \"/docs/robots.txt\": return createRobotsTxtResponse({ manifest, requestOrigin, sitemapUrlPath: \"/docs/sitemap.xml\", }); default: return createAgentMarkdownResponse({ urlPath: url.pathname, method: request.method, headers: Object.fromEntries(request.headers), manifest, readMarkdownFile, requestOrigin, }); } } ``` ```ts return createSitemapXmlResponse({ manifest, requestOrigin, pages: [...manifest.pages, ...marketingPages, ...blogPages], }); ```","Optimize docs for agents\n\nSet up llms.txt, markdown mirrors, JSON-LD, sitemaps, robots.txt, and audit checks for an agent-readable docs site.\n\nOptimize docs for agents\n\n3. Add JSON-LD to docs pages\n\nUse the manifest entry for the current page and render Schema.org JSON-LD into the HTML head Use renderJsonLd page, manifest if your framework has a typed metadata API. Use renderJsonLdScript page, manifest if your framework expects an HTML string. Also add canonical and markdown alternate links The JSON-LD gives agents the page title, description, canonical URL, last modified date, and breadcrumbs without scraping your rendered layout.\n\n```ts import agentManifest from \"../public/docs/agent-readability.json\"; import { renderJsonLd, renderJsonLdScript } from \"leadtype/llm/readability\"; const page = agentManifest.pages.find( (entry) => entry.urlPath === \"/docs/quickstart\" ); if (page) { const jsonLd = renderJsonLd(page, agentManifest); const script = renderJsonLdScript(page, agentManifest); } ``` ```html ```","Optimize docs for agents\n\nSet up llms.txt, markdown mirrors, JSON-LD, sitemaps, robots.txt, and audit checks for an agent-readable docs site.\n\nOptimize docs for agents\n\n4. Return markdown to agents\n\nThe middleware above uses createAgentMarkdownResponse . It returns a Web Response or null when the path is not an agent-oriented markdown request and handles Accept text/markdown and Accept text/plain content negotiation q-values respected . Known AI user-agent headers GPTBot, ClaudeBot, Bingbot, AmazonBot, MetaExternalAgent, PerplexityBot, MistralBot, AppleBot, ByteSpider, YouBot, … . Direct .md URLs such as /docs/quickstart.md . canonical url and last updated frontmatter aliases injected automatically. 200 markdown responses for missing docs pages, so agents do not discard the body. Content-Type text/markdown; charset=utf-8 , Vary Accept , User-Agent , Link <… ; rel=\"canonical\" , Cache-Control public, max-age=300, must-revalidate . readMarkdownFile may be sync or async. In Node/Bun, read from disk. In Cloudflare, fetch from KV/R2 or an asset binding. In Vercel Edge, fetch from the deployment's static asset URL. Put that logic wherever your framework can intercept docs requests before its HTML route Framework/runtime Where it usually goes -- -- TanStack Start / nitro server/middleware/agent-readability.ts h3 .","Optimize docs for agents\n\nSet up llms.txt, markdown mirrors, JSON-LD, sitemaps, robots.txt, and audit checks for an agent-readable docs site.\n\nOptimize docs for agents\n\n4. Return markdown to agents\n\nn intercept docs requests before its HTML route Framework/runtime Where it usually goes -- -- TanStack Start / nitro server/middleware/agent-readability.ts h3 . One middleware handles both the markdown response and the sitemap/robots regenerators — runs in dev, preview, and prod. Nuxt server/middleware/agent-readability.ts h3 — same shape as the TanStack Start example. Next.js middleware.ts Edge or a catch-all route handler before the docs page. Astro An endpoint at pages/docs/ ...slug .md.ts or astro middleware . Cloudflare Workers/Pages Worker fetch handler with KV/R2 asset binding for the markdown reader. Express/Hono/Fastify Middleware before the docs HTML route. Tip if you keep static sitemap.xml / sitemap.md / robots.txt files in your build output, your framework's static handler may serve them before your middleware can rebase URLs to the live origin. Either delete the static copies after the build so the middleware always runs or make sure your middleware is registered ahead of static-asset serving. Do not rewrite llms.txt , sitemap.xml , sitemap.md , robots.txt , llms-full.txt , or agent-readability.json to page markdown. The helper leaves those artifact paths alone.","Optimize docs for agents\n\nSet up llms.txt, markdown mirrors, JSON-LD, sitemaps, robots.txt, and audit checks for an agent-readable docs site.\n\nOptimize docs for agents\n\n4. Return markdown to agents\n\nWhy the sitemap and robots responses are regenerated, not static\n\nsitemap.xml 's }) { const { slug = [] } = await params; const page = await source.loadPage(slug); if (!page) notFound(); return (
{page.title}
{page.description ?
{page.description}
: null} ); } export async function generateStaticParams() { const pages = await source.listPages(); return pages.map((page) => ({ slug: page.slug })); } ```","Use the source primitive\n\nWire createDocsSource into Next, Astro, Vite, Nuxt, SvelteKit, or any MDX-aware bundler. Same primitive, multiple host shapes.\n\nUse the source primitive\n\nWire into your framework\n\nAstro Content Collections\n\nUse Astro's native content collection schema for typed frontmatter. Call source.loadPage from leadtype only when you need programmatic include resolution, search, or navigation. See Astro's content collections docs for the routing pattern.\n\n```ts title=\"astro.config.mjs\" import { defineConfig } from \"astro/config\"; import mdx from \"@astrojs/mdx\"; import { mdxSourcePlugins } from \"leadtype/mdx\"; export default defineConfig({ integrations: [mdx({ remarkPlugins: [...mdxSourcePlugins] })], }); ```","Use the source primitive\n\nWire createDocsSource into Next, Astro, Vite, Nuxt, SvelteKit, or any MDX-aware bundler. Same primitive, multiple host shapes.\n\nUse the source primitive\n\nWire into your framework\n\nTanStack Start\n\nGenerate docs-pages.json at build time by calling createDocsSource .listPages from a build script and writing each page's slug , urlPath , and globKey path relative to the catch-all route, POSIX separators .\n\n```ts title=\"vite.config.ts\" import mdx from \"@mdx-js/rollup\"; import { tanstackStart } from \"@tanstack/react-start/plugin/vite\"; import viteReact from \"@vitejs/plugin-react\"; import { mdxSourcePlugins } from \"leadtype/mdx\"; import remarkFrontmatter from \"remark-frontmatter\"; import { defineConfig } from \"vite\"; export default defineConfig({ plugins: [ { ...mdx({ providerImportSource: \"@mdx-js/react\", remarkPlugins: [remarkFrontmatter, ...mdxSourcePlugins], }), enforce: \"pre\", }, tanstackStart(), viteReact({ include: /\\.(mdx|[jt]sx?)$/ }), ], }); ``` ```tsx title=\"src/routes/docs/$.tsx\" import { createFileRoute, notFound } from \"@tanstack/react-router\"; import { type ComponentType, lazy, Suspense } from \"react\"; import docsPages from \"@/generated/docs-pages.json\"; const pagesBySlug = new Map( (docsPages as { slug: string[]; globKey: string }[]).map((p) => [ p.slug.join(\"/\"), p, ]) ); // import.meta.glob keys are POSIX paths; the build-time manifest writes // matching keys so each slug maps to its lazy-loaded MDX module.","Use the source primitive\n\nWire createDocsSource into Next, Astro, Vite, Nuxt, SvelteKit, or any MDX-aware bundler. Same primitive, multiple host shapes.\n\nUse the source primitive\n\nWire into your framework\n\nTanStack Start\n\ng.join(\"/\"), p, ]) ); // import.meta.glob keys are POSIX paths; the build-time manifest writes // matching keys so each slug maps to its lazy-loaded MDX module. const mdxModules = import.meta.glob<{ default: ComponentType }>( \"../../../content/docs/**/*.mdx\" ); export const Route = createFileRoute(\"/docs/$\")({ beforeLoad: ({ params }) => { if (!pagesBySlug.get((params._splat ?? \"\").replace(/\\/+$/, \"\"))) { throw notFound(); } }, component: DocsCatchAllRoute, }); function DocsCatchAllRoute() { const { _splat } = Route.useParams(); const page = pagesBySlug.get((_splat ?? \"\").replace(/\\/+$/, \"\")); if (!page) { return null; } const Mdx = lazy(mdxModules[page.globKey]); return ( ); } ```","Use the source primitive\n\nWire createDocsSource into Next, Astro, Vite, Nuxt, SvelteKit, or any MDX-aware bundler. Same primitive, multiple host shapes.\n\nUse the source primitive\n\nWire into your framework\n\nVite + @mdx-js/rollup works for Vue, Solid, Svelte starters\n\n```ts title=\"vite.config.ts\" import mdx from \"@mdx-js/rollup\"; import { mdxSourcePlugins } from \"leadtype/mdx\"; export default { plugins: [ mdx({ remarkPlugins: [...mdxSourcePlugins] }), // ...your framework plugin: viteReact / vue / solid / svelte ], }; ```","Use the source primitive\n\nWire createDocsSource into Next, Astro, Vite, Nuxt, SvelteKit, or any MDX-aware bundler. Same primitive, multiple host shapes.\n\nUse the source primitive\n\nWire into your framework\n\nNuxt\n\n```ts title=\"nuxt.config.ts\" import { mdxSourcePlugins } from \"leadtype/mdx\"; export default defineNuxtConfig({ modules: [\"@nuxtjs/mdc\"], mdc: { remarkPlugins: [...mdxSourcePlugins] }, }); ```","Use the source primitive\n\nWire createDocsSource into Next, Astro, Vite, Nuxt, SvelteKit, or any MDX-aware bundler. Same primitive, multiple host shapes.\n\nUse the source primitive\n\nWire into your framework\n\nSvelteKit + mdsvex\n\n```ts title=\"svelte.config.js\" import { mdsvex } from \"mdsvex\"; import { mdxSourcePlugins } from \"leadtype/mdx\"; export default { extensions: [\".svelte\", \".svx\", \".mdx\"], preprocess: mdsvex({ remarkPlugins: [...mdxSourcePlugins] }), }; ```","Use the source primitive\n\nWire createDocsSource into Next, Astro, Vite, Nuxt, SvelteKit, or any MDX-aware bundler. Same primitive, multiple host shapes.\n\nUse the source primitive\n\nWire into your framework\n\nPattern for any other host\n\nIf your framework's MDX integration accepts a remark plugin list, leadtype works. Three pieces every time 1. Add mdxSourcePlugins to the remark list so ( ), // ... Tabs, Tab, Steps, Step, Cards, Card, TypeTable, etc. }; ```","Use the source primitive\n\nWire createDocsSource into Next, Astro, Vite, Nuxt, SvelteKit, or any MDX-aware bundler. Same primitive, multiple host shapes.\n\nUse the source primitive\n\nBuild the sidebar from navigation\n\nEach page carries a toc field DocsTableOfContentsItem you can render as an \"On this page\" rail.\n\n```tsx title=\"components/sidebar.tsx\" import { source } from \"@/lib/source\"; export async function Sidebar({ currentUrlPath }: { currentUrlPath: string }) { const navigation = await source.getNavigation(); return ( ); } ```","Use the source primitive\n\nWire createDocsSource into Next, Astro, Vite, Nuxt, SvelteKit, or any MDX-aware bundler. Same primitive, multiple host shapes.\n\nUse the source primitive\n\nMatch heading slugs\n\nsource.loadPage .toc uses slugifyDocsHeading to derive anchor IDs. Your rendered heading components need matching id attributes Wire Heading2 and h3 , etc. into your mdxComponents map. Authors can still pin an explicit id on a heading.\n\n```tsx import { slugifyDocsHeading } from \"leadtype/llm/readability\"; function textFromChildren(children: unknown): string { if (typeof children === \"string\" || typeof children === \"number\") { return String(children); } if (Array.isArray(children)) return children.map(textFromChildren).join(\"\"); // (recurse into React elements as needed) return \"\"; } const Heading2 = ({ children, id, ...props }: React.HTMLAttributes) => { const headingId = id ?? slugifyDocsHeading(textFromChildren(children)); return
{children}
; }; ```","Use the source primitive\n\nWire createDocsSource into Next, Astro, Vite, Nuxt, SvelteKit, or any MDX-aware bundler. Same primitive, multiple host shapes.\n\nUse the source primitive\n\nBuild a search index\n\nFor provider-specific search Vercel AI, TanStack, Cloudflare , feed the bundle into a leadtype/search/ adapter — see Add search.\n\n```tsx title=\"app/api/search/route.ts\" import { source } from \"@/lib/source\"; const bundle = await source.buildSearchIndex(); export async function GET() { return Response.json(bundle.index); } ```","Use the source primitive\n\nWire createDocsSource into Next, Astro, Vite, Nuxt, SvelteKit, or any MDX-aware bundler. Same primitive, multiple host shapes.\n\nUse the source primitive\n\nTroubleshooting\n\n lint-report.json ```","Validate in CI\n\nRun leadtype lint in CI so frontmatter, navigation, and link issues fail PRs before publish.\n\nValidate in CI\n\nLocal pre-push hook\n\nCatch issues before they reach CI by running lint in a husky pre-push hook Keep it under a second by limiting the scan to changed files when you have many pages. The CLI accepts repeated --ignore globs to skip stale or generated paths.\n\n```bash #!/usr/bin/env sh npx leadtype lint docs --max-warnings 0 ```","Validate in CI\n\nRun leadtype lint in CI so frontmatter, navigation, and link issues fail PRs before publish.\n\nValidate in CI\n\nRun before generate\n\nWhen leadtype lint and leadtype generate both run in the same job, lint first Generate fails noisily on unknown groups or broken includes. Lint fails specifically on content schema problems with file/line context — much easier to debug.\n\n```bash npx leadtype lint docs --error-unknown npx leadtype generate --src . --out public --json ```","Validate in CI\n\nRun leadtype lint in CI so frontmatter, navigation, and link issues fail PRs before publish.\n\nValidate in CI\n\nWhat to fix first\n\nWhen CI fails on a lot of violations, fix them in this order 1. parse-error — frontmatter is broken; nothing else can validate. 2. schema — missing or wrong-typed required fields. 3. unresolved-placeholder — content bug, not a config bug. 4. invalid-link and cross-framework-link — usually a stale link after a docs move. 5. unknown-field — last; either delete the field or extend the schema.","How it works\n\nThe mental model: one MDX source, a remark pipeline, two output modes, three audiences.\n\nHow it works\n\nLeadtype takes one input — a folder of MDX — and produces every shape your docs need to take. This page names every piece so the rest of the docs make sense.","How it works\n\nThe mental model: one MDX source, a remark pipeline, two output modes, three audiences.\n\nHow it works\n\nThe pipeline\n\nThe remark stack is what turns interactive MDX components into agent-readable markdown. JSX gets flattened — a title · description · group · body\"] fm[\"frontmatter parser\"] remark[\"remark plugin stack (strip imports → flatten components)\"] groups[\"group resolver (nav tree from frontmatter)\"] md[\"docs/*.md\"] site_idx[\"llms.txt · llms-full.txt sitemap · robots · manifest (website mode only)\"] search[\"search-index.json · search-content.json (website mode only)\"] agents_md[\"AGENTS.md (--bundle mode only)\"] nav[\"navigation manifest (group → page)\"] src --> fm fm --> remark fm --> groups remark --> md md --> site_idx md --> search groups --> site_idx groups --> agents_md groups --> nav ```","How it works\n\nThe mental model: one MDX source, a remark pipeline, two output modes, three audiences.\n\nHow it works\n\nTwo output modes\n\nleadtype generate has two modes that read the same source and emit different shapes Property Type Description Default Required -- -- -- -- -- Site mode default leadtype generate --out public Writes llms.txt, llms-full.txt, docs/search-index.json, docs/sitemap.xml, docs/sitemap.md, docs/robots.txt, docs/agent-readability.json, and docs/\\ .md to a public/ directory your docs website serves. This is what you wire into a Vite, Next.js, Astro, or TanStack Start build. - Optional Bundle mode leadtype generate --bundle --out packages/foo Writes AGENTS.md at the package root and docs/\\ .md beneath it, both with relative paths. Skips llms.txt, llms-full.txt, search, sitemap, robots, and Agent Readability files — those are website-only. Designed for npm tarballs that ship docs alongside the published code. - Optional","How it works\n\nThe mental model: one MDX source, a remark pipeline, two output modes, three audiences.\n\nHow it works\n\nThe artifacts\n\nProperty Type Description Default Required -- -- -- -- -- Markdown .md docs/\\public/*\"] bundle_out[\"bundle mode node_modules/<pkg>/*\"] human[\"Humans (browser)\"] http_agent[\"HTTP agents (fetch /llms.txt or Accept: text/markdown)\"] search_ui[\"Search UI AI answers\"] offline_agent[\"Coding agents (Claude Code, Codex, Cursor, Copilot, …)\"] site_out -- \"HTML render of MDX\" --> human site_out -- \"absolute URLs\" --> http_agent site_out -- \"search-index.json\" --> search_ui bundle_out -- \"version-matched AGENTS.md\" --> offline_agent ```","How it works\n\nThe mental model: one MDX source, a remark pipeline, two output modes, three audiences.\n\nHow it works\n\nVocabulary\n\nA few terms you will see throughout the docs. Property Type Description Default Required -- -- -- -- -- flatten verb Convert an interactive MDX component into a portable markdown equivalent. A \\(your source)\"] site_run[\"leadtype generate\"] bundle_run[\"leadtype generate --bundle\"] site_out[\"public/ llms.txt · llms-full.txt docs/*.md · sitemap agent-readability.json\"] bundle_out[\"packages/<name>/ AGENTS.md · docs/*.md\"] humans[\"Humans (browser)\"] http_agents[\"HTTP agents (via /llms.txt or Accept: text/markdown)\"] search[\"Search UI AI answers\"] offline_agents[\"Coding agents can read version-matched node_modules/<pkg>/AGENTS.md\"] src --> site_run src --> bundle_run site_run --> site_out bundle_run --> bundle_out site_out --> humans site_out --> http_agents site_out --> search bundle_out --> offline_agents ```","Leadtype\n\nOne MDX source. Hosted docs artifacts, package-bundled AGENTS.md, and search output from the same pipeline.\n\nLeadtype\n\nStart here\n\nQuickstart → — three steps to a working source primitive, then plug it into your framework. Or skip ahead Build a docs site compares every integration path side-by-side.","Leadtype\n\nOne MDX source. Hosted docs artifacts, package-bundled AGENTS.md, and search output from the same pipeline.\n\nLeadtype\n\nWhat leadtype produces\n\nA source primitive for your app llms.txt, sitemaps, agent metadata AGENTS.md for npm packages","Leadtype\n\nOne MDX source. Hosted docs artifacts, package-bundled AGENTS.md, and search output from the same pipeline.\n\nLeadtype\n\nNext\n\nNew here? Read the Quickstart — three steps to a working source. Want the mental model first? Read How it works for the pipeline diagram and the names of every artifact it produces. Comparing tools? See Methodology for how leadtype differs from Fumadocs, Starlight, and Mintlify.","Methodology\n\nHow leadtype differs from Fumadocs, Starlight, and Mintlify.\n\nMethodology\n\nLeadtype is a docs pipeline, not a docs website framework . It produces the artifacts a docs site needs markdown, llms.txt, search index, navigation and stays out of routing, layout, and theming.","Methodology\n\nHow leadtype differs from Fumadocs, Starlight, and Mintlify.\n\nMethodology\n\nThe short version\n\nTool What it gives you -- -- Fumadocs A React docs framework Next.js, TanStack Start, React Router, Waku . Supports llms.txt and content negotiation via framework route handlers. Starlight An Astro docs site framework. llms.txt via the starlight-llms-txt community plugin not built-in ; static client-side search via Pagefind. Mintlify A hosted docs SaaS with an optional headless Astro mode that still calls Mintlify's search and assistant APIs . Leadtype The portable content layer behind any of the above. Choose a website framework when the main job is publishing a polished docs UI quickly. Consider a hosted platform if you want managed publishing, search, analytics, and AI features and accept that service dependency. Opt for leadtype when you need to own the generated docs artifacts framework-agnostic markdown, navigation, search, llms.txt , a root llms-full.txt fallback, and optional AGENTS.md package bundles that ship inside npm tarballs.","Methodology\n\nHow leadtype differs from Fumadocs, Starlight, and Mintlify.\n\nMethodology\n\nWhat leadtype owns\n\nThe MDX tag contract — typed prop shapes for (your repo)\"] cli[\"leadtype generate --bundle --out packages/<name>\"] bundle[\"packages/<name>/ AGENTS.md docs/*.md\"] publish[\"npm publish\"] install[\"npm install <name>\"] consume[\"node_modules/<name>/ AGENTS.md → docs/*.md version-matched offline docs\"] src --> cli cli --> bundle bundle --> publish publish --> install install --> consume ```","Bundle docs into a package\n\nShip agent-readable docs inside an npm tarball — AGENTS.md at the package root plus per-topic .md files.\n\nBundle docs into a package\n\nWhy AGENTS.md, not llms.txt?\n\nllms.txt is a website convention — a file at /llms.txt with absolute URLs that an agent fetches over HTTP. Inside an npm tarball it's the wrong shape every link points at a hosted URL the agent may not be able to reach, and no major coding agent looks for node modules/ 0) { for (const { urlPath, slug } of navigation.unknown) { process.stderr.write(`error: ${urlPath} declares unknown group \"${slug}\".\\n`); } process.exit(1); } await generateAgentsMd({ srcDir: REPO_ROOT, outDir: PACKAGE_ROOT, product: docsConfig.product, groups: docsConfig.groups, }); ```","Bundle docs into a package\n\nShip agent-readable docs inside an npm tarball — AGENTS.md at the package root plus per-topic .md files.\n\nBundle docs into a package\n\nVerify before publishing\n\nnpm pack --dry-run should list AGENTS.md docs/ / .md If AGENTS.md is missing, check files in package.json . If .md files are missing, check the --include / --exclude filters.\n\n```bash npx leadtype generate --bundle --src . --out packages/my-package cd packages/my-package && npm pack --dry-run ```","Bundle docs into a package\n\nShip agent-readable docs inside an npm tarball — AGENTS.md at the package root plus per-topic .md files.\n\nBundle docs into a package\n\nTell consuming projects to use the bundle\n\nAGENTS.md inside your tarball is available at node modules/ When working with the `` library, read the bundled docs in `node_modules//AGENTS.md` first — they're version-matched to the installed package and stay accurate as the library updates. ```","Bundle docs into a package\n\nShip agent-readable docs inside an npm tarball — AGENTS.md at the package root plus per-topic .md files.\n\nBundle docs into a package\n\nWhen to use this\n\nUse this when agents should understand the package from the installed dependency itself — coding agents that don't have web access, IDE assistants, CLI tools, or air-gapped environments. Don't use this if your only goal is a public docs website. For that, see Build a docs site. You can use both — they read the same source MDX, just emit different shapes.","Bundle docs into a package\n\nShip agent-readable docs inside an npm tarball — AGENTS.md at the package root plus per-topic .md files.\n\nBundle docs into a package\n\nWhat's next\n\nCLI reference LLM files Lint in CI","Quickstart\n\nInstall leadtype, author your first MDX page, and create a source. Then plug it into whatever framework you're using.\n\nQuickstart\n\nThree steps to a working source primitive. Then plug it into your framework — Next, Fumadocs, Astro, Vite + Vue/Solid/Svelte, Nuxt, SvelteKit — using one of the recipes below.","Quickstart\n\nInstall leadtype, author your first MDX page, and create a source. Then plug it into whatever framework you're using.\n\nQuickstart\n\nInstall\n\nPackage manager Command -- -- npm npm install leadtype pnpm pnpm add leadtype yarn yarn add leadtype bun bun add leadtype","Quickstart\n\nInstall leadtype, author your first MDX page, and create a source. Then plug it into whatever framework you're using.\n\nQuickstart\n\nAuthor one page\n\nEvery page needs a title . Add group when the page should appear in generated navigation and llms.txt sections. See Frontmatter for the full content contract.\n\n```mdx title=\"content/docs/index.mdx\" --- title: \"My library\" description: \"What it does in one sentence.\" --- # My library Welcome. ```","Quickstart\n\nInstall leadtype, author your first MDX page, and create a source. Then plug it into whatever framework you're using.\n\nQuickstart\n\nCreate the source\n\nThat's the framework-neutral primitive. You can now call source.loadPage slug — resolved markdown + AST + frontmatter + TOC source.listPages — every page's slug, urlPath, and metadata source.getNavigation — grouped sidebar tree source.buildSearchIndex — static search bundle source.resolveInclude specifier — standalone include resolver\n\n```ts title=\"lib/source.ts\" import { createDocsSource } from \"leadtype\"; export const source = await createDocsSource({ contentDir: \"./content/docs\", baseUrl: \"https://example.com\", }); ```","Quickstart\n\nInstall leadtype, author your first MDX page, and create a source. Then plug it into whatever framework you're using.\n\nQuickstart\n\nPlug it into your framework\n\nPick the recipe that matches your stack. Each one shows the wiring on top of the same createDocsSource you just set up. Next App Router Fumadocs TanStack Start Astro Content Collections Vite + Vue / Solid / Svelte Nuxt SvelteKit","Quickstart\n\nInstall leadtype, author your first MDX page, and create a source. Then plug it into whatever framework you're using.\n\nQuickstart\n\nOr write static artifacts to disk\n\nSome pipelines need flat files — CDN-only deploys, static exports, agent-only services, multiple sibling apps sharing one corpus. Use the CLI That writes llms.txt , llms-full.txt , docs/ .md , search index, sitemap, and agent-readability.json to disk. See Generate static artifacts for the full flow.\n\n```sh npx leadtype generate --src . --out public --base-url https://example.com ```","Quickstart\n\nInstall leadtype, author your first MDX page, and create a source. Then plug it into whatever framework you're using.\n\nQuickstart\n\nNext steps\n\nGoal Page -- -- Compare every integration shape side-by-side Build a docs site Implement custom MDX tags Callout, Tabs, Steps, … leadtype/mdx Add search Add search Validate frontmatter and links in CI Validate in CI Serve markdown, sitemaps, robots, and JSON-LD for agents Optimize docs for agents Publish AGENTS.md inside an npm package Bundle docs into a package","CLI\n\nleadtype generate and leadtype lint — flags, exit codes, and JSON output.\n\nCLI\n\nTwo commands generate runs the full pipeline, lint validates content. Run leadtype help [options] ```","CLI\n\nleadtype generate and leadtype lint — flags, exit codes, and JSON output.\n\nCLI\n\ngenerate\n\nConvert MDX, then either produce website artifacts default or a package bundle --bundle . Flag Default Description -- -- -- --src ; summary: { filesScanned: number; errors: number; warnings: number; }; }; ```","Lint rules\n\nSchema, link, and navigation checks. CLI and library API.\n\nLint rules\n\nDefault schemas\n\nDocs frontmatter\n\nField Required Type -- -- -- title Yes non-empty string description No string icon No string deprecated No boolean deprecatedReason No string experimental No boolean canary No boolean new No boolean draft No boolean tags No string array group No string or string array availableIn No array of framework, url?, title? full No boolean lastModified and lastAuthor are produced by the converter when --enrich-git is set. Don't author them.","Lint rules\n\nSchema, link, and navigation checks. CLI and library API.\n\nLint rules\n\nDefault schemas\n\nChangelog frontmatter\n\nField Required Type -- -- -- title Yes non-empty string version Yes SemVer string date Yes ISO-8601 or parseable date description No string icon No string type No release , improvement , retired , or deprecation tags No string array canary No boolean authors No string or string array draft No boolean","Lint rules\n\nSchema, link, and navigation checks. CLI and library API.\n\nLint rules\n\nDefault schemas\n\nmeta.json\n\nField Required Type -- -- -- pages Yes string array title No non-empty string root No boolean icon No string defaultOpen No boolean nav.sidebar No section or combined nav.label No string nav.mode No string","Lint rules\n\nSchema, link, and navigation checks. CLI and library API.\n\nLint rules\n\nCustom schemas\n\nPass a Valibot schema to extend or replace the defaults Once you provide a custom schema, unknown-field warnings apply to that schema. Add --error-unknown in CI to keep your contract strict.\n\n```ts import * as v from \"valibot\"; import { lintDocs } from \"leadtype/lint\"; const customFrontmatter = v.object({ title: v.pipe(v.string(), v.minLength(1)), audience: v.picklist([\"beginner\", \"advanced\"]), }); await lintDocs({ srcDir: \"docs\", schemas: { frontmatter: customFrontmatter }, }); ```","Lint rules\n\nSchema, link, and navigation checks. CLI and library API.\n\nLint rules\n\nPractical guidance\n\nRun lint before leadtype generate so content errors fail fast. Use --format github in GitHub Actions and --format json in any other CI. Treat unresolved-placeholder as a content bug first — usually a missing entry in availableIn or a stale URL template. After a docs move, lint and run meta.json updates together; they drift at the same time. For wiring lint into pipelines, see Validate in CI.","LLM files\n\nGenerate llms.txt for hosted websites and AGENTS.md for npm-bundled offline reading.\n\nLLM files\n\nThe leadtype/llm entry point produces four flavors of agent-facing output, all derived from the same docs source generateLlmsTxt — for hosted websites. Emits the /llms.txt convention with root-relative markdown mirror links. generateLLMFullContextFiles — root /llms-full.txt fallback containing all generated markdown docs. Pairs with generateLlmsTxt . generateAgentReadabilityArtifacts — docs-scoped sitemap.xml , sitemap.md , robots.txt , and JSON manifest data that a host app can merge into site-level files. generateAgentsMd — for npm-bundled docs. Emits an AGENTS.md index with relative ./docs/ A library that does one thing well. - Helper that handles the boring parts. - Type-safe by default. - Works in any runtime. ## Best Starting Points - [Documentation](/docs/index.md) - [Quickstart](/docs/quickstart.md) ## Get Started Five-minute happy path and the mental model. - [Quickstart](/docs/quickstart.md): Install and run the pipeline. - [How it works](/docs/how-it-works.md): The mental model. ## Reference CLI flags and conversion APIs. - [CLI](/docs/reference/cli.md): Every flag. ```","LLM files\n\nGenerate llms.txt for hosted websites and AGENTS.md for npm-bundled offline reading.\n\nLLM files\n\nTypical sequence\n\ngenerateLLMFullContextFiles and generateAgentReadabilityArtifacts read from A library that does one thing well. These docs ship inside the package so coding agents can read them offline. Open the topic file you need from the list below — paths are relative to this file. ## Get Started - [Quickstart](./docs/quickstart.md): Install and run the pipeline. - [How it works](./docs/how-it-works.md): The mental model. ## Reference - [CLI](./docs/reference/cli.md): Every flag. ```","LLM files\n\nGenerate llms.txt for hosted websites and AGENTS.md for npm-bundled offline reading.\n\nLLM files\n\nresolveDocsNavigation\n\nSame group-resolution logic the LLM files use, but returns the navigation manifest as a plain object — useful for driving a sidebar UI Write the result to src/generated/docs-nav.json and import it from your sidebar component Now your sidebar can import a static manifest with the same group tree the LLM files use.\n\n```ts const navigation = await resolveDocsNavigation({ srcDir: \".\", baseUrl: \"https://leadtype.dev\", groups: docsConfig.groups, }); if (navigation.unknown.length > 0) { for (const { urlPath, slug } of navigation.unknown) { process.stderr.write(`error: ${urlPath} declares unknown group \"${slug}\".\\n`); } process.exit(1); } ``` ```ts import { mkdir, writeFile } from \"node:fs/promises\"; await mkdir(\"src/generated\", { recursive: true }); await writeFile( \"src/generated/docs-nav.json\", `${JSON.stringify(navigation, null, 2)}\\n` ); ```","LLM files\n\nGenerate llms.txt for hosted websites and AGENTS.md for npm-bundled offline reading.\n\nLLM files\n\nTables of contents\n\nFor the heading slug contract and renderer wiring, see Use the source primitive. This section covers the build-time APIs only. resolveDocsNavigation includes a toc array on every page by default. The default range is h2 – h3 , which keeps page-level h1 titles out of sidebars. If you only need TOC data and not the full group tree, call resolveDocsTableOfContents For custom pipelines, extractDocsTableOfContents accepts a markdown or MDX string plus page URL metadata and returns plain JSON. It ignores frontmatter and fenced code blocks, and it uses the same slugifyDocsHeading helper that rendered headings must use to keep id attributes in sync.\n\n```ts const navigation = await resolveDocsNavigation({ srcDir: \".\", baseUrl: \"https://leadtype.dev\", groups: docsConfig.groups, toc: { minLevel: 2, maxLevel: 4 }, }); ``` ```ts const pages = await resolveDocsTableOfContents({ srcDir: \".\", baseUrl: \"https://leadtype.dev\", }); ```","LLM files\n\nGenerate llms.txt for hosted websites and AGENTS.md for npm-bundled offline reading.\n\nLLM files\n\nGroup design\n\nThe groups you pass to these APIs come from docs.config.ts . Two principles Use groups for routing, not sharding. Groups organize llms.txt , navigation, search metadata, and AGENTS.md . The root llms-full.txt remains the broad fallback. Write group descriptions for routing, not flavor text. Agents read those descriptions to decide which pages to load. \"How to install and run\" beats \"Welcome to our guides!\"","LLM files\n\nGenerate llms.txt for hosted websites and AGENTS.md for npm-bundled offline reading.\n\nLLM files\n\nBase URL precedence\n\nPass baseUrl explicitly, or use environment variables for layered fallback The package-specific LEADTYPE AGENT BASE URL lets each package override an org-wide default. BASE URL covers most CI/deployment platforms, and a final hardcoded fallback keeps local builds working without env setup.\n\n```ts const baseUrl = process.env.LEADTYPE_AGENT_BASE_URL || process.env.BASE_URL || process.env.PORTLESS_URL || \"https://leadtype.dev\"; ```","leadtype/mdx\n\nTag type contracts and the build-time source preset for consumers rendering MDX themselves.\n\nleadtype/mdx\n\nThe leadtype/mdx subpath is the consumer-facing MDX surface — everything you need to compile leadtype-authored MDX in your own renderer fumadocs, Next App Router, Vite + @mdx-js, Astro content collections . It exports three things 1. Tag type contracts — typed prop shapes for every custom MDX tag. 2. mdxSourcePlugins — a remark preset that performs build-time resolution only expand includes, resolve & HTMLAttributes & { children?: ReactNode }; export function Callout({ variant, title, children, ...rest }: ReactCalloutProps) { return ( ); } ```","leadtype/mdx\n\nTag type contracts and the build-time source preset for consumers rendering MDX themselves.\n\nleadtype/mdx\n\nFramework-neutral by design\n\nVue\n\n```vue ```","leadtype/mdx\n\nTag type contracts and the build-time source preset for consumers rendering MDX themselves.\n\nleadtype/mdx\n\nFramework-neutral by design\n\nSvelte\n\n```svelte ```","leadtype/mdx\n\nTag type contracts and the build-time source preset for consumers rendering MDX themselves.\n\nleadtype/mdx\n\nFramework-neutral by design\n\nSolid\n\n```tsx import type { CalloutProps } from \"leadtype/mdx\"; import type { JSX } from \"solid-js\"; type SolidCalloutProps = Omit & { children?: JSX.Element; }; export function Callout(props: SolidCalloutProps) { return ( ); } ```","leadtype/mdx\n\nTag type contracts and the build-time source preset for consumers rendering MDX themselves.\n\nleadtype/mdx\n\nFramework-neutral by design\n\nAstro\n\n```astro --- import type { CalloutProps } from \"leadtype/mdx\"; type Props = Omit; const { variant = \"info\", title } = Astro.props as Props; --- ```","leadtype/mdx\n\nTag type contracts and the build-time source preset for consumers rendering MDX themselves.\n\nleadtype/mdx\n\nFramework-neutral by design\n\nFull type inventory\n\nEvery prop name is part of the 1.0 contract — bumping a shape is a breaking change. New optional props are minor.\n\n```ts import type { // Layout AudienceProps, AudienceTarget, SectionProps, DetailsProps, // Callouts CalloutProps, CalloutVariant, CalloutTypeAlias, // Navigation / structure TabsProps, TabProps, StepsProps, StepProps, AccordionProps, AccordionItemProps, // Cards / topics CardsProps, CardProps, CardVariant, TopicSwitcherProps, TopicSwitcherItem, // File tree FileTreeProps, FolderProps, FileProps, // Code / commands CommandTabsProps, CommandMode, PackageManager, ExampleProps, ExampleSourceFile, PromptProps, // Type tables TypeTableProps, TypeTableProperty, ExtractedTypeTableProps, // Diagrams MermaidProps, } from \"leadtype/mdx\"; ```","leadtype/mdx\n\nTag type contracts and the build-time source preset for consumers rendering MDX themselves.\n\nleadtype/mdx\n\nFramework-neutral by design\n\nBuild-time only {title ?
); } ```","leadtype/mdx\n\nTag type contracts and the build-time source preset for consumers rendering MDX themselves.\n\nleadtype/mdx\n\nInclude resolution\n\nresolveInclude reads + classifies an include target without going through remark — useful when loading a partial outside of the bundler pipeline parseIncludeSpecifier specifier and extractMdxSection root, id are exposed for callers that want to compose their own pipeline.\n\n```ts import { resolveInclude } from \"leadtype/mdx\"; const result = await resolveInclude(\"./shared/install.mdx#bun\", { fromDir: process.cwd(), }); if (result.kind === \"markdown\") { console.log(result.content); // frontmatter-stripped body console.log(result.section); // \"bun\" } else { console.log(result.lang, result.content); // code-block form } ```","leadtype/mdx\n\nTag type contracts and the build-time source preset for consumers rendering MDX themselves.\n\nleadtype/mdx\n\nRe-exported path helpers\n\nRouting primitives that previously lived under leadtype/internal are re-exported for consumer use\n\n```ts import { type DocsPathMount, normalizeBaseUrl, normalizeDocsPath, normalizeUrlPrefix, stripDocsExtension, toDocsUrlPath, } from \"leadtype/mdx\"; ```","Remark plugins\n\nThe default plugin stack that flattens MDX components into markdown.\n\nRemark plugins\n\nThe remark stack is what turns interactive MDX into agent-readable markdown. Imports get stripped first, placeholders get resolved, then each named component is flattened into a markdown equivalent. Order matters.\n\n```ts import { defaultRemarkPlugins, remarkInclude, remarkTypeTableToMarkdown, } from \"leadtype/remark\"; ```","Remark plugins\n\nThe default plugin stack that flattens MDX components into markdown.\n\nRemark plugins\n\nThe default stack\n\ndefaultRemarkPlugins runs the stack in this order 1. remarkRemoveImports — strip MDX import and export statements. 2. remarkRemoveJsxComments — strip / ... / JSX comments. 3. remarkResolveDocPlaceholders — replace framework and similar placeholders in URLs. 4. remarkAudienceToMarkdown — include ri --> rj --> rd --> rau --> rs --> rc --> rcd --> rdt --> rm --> rct --> rst --> rt --> rtt --> ra --> rts --> rft --> rp --> re --> out ```","Remark plugins\n\nThe default plugin stack that flattens MDX components into markdown.\n\nRemark plugins\n\nThe default stack\n\n./shared/auth.mdx ``` ```mdx ``` ```mdx ``` ```mdx This content can be reused in multiple pages. ``` ```ts remarkPlugins: [ [remarkInclude, [\"docs\", \"content\"]], ...defaultRemarkPlugins, ]; ```","Remark plugins\n\nThe default plugin stack that flattens MDX components into markdown.\n\nRemark plugins\n\nOptional plugins\n\nremarkTypeTableToMarkdown with basePath\n\n p !== remarkTypeTableToMarkdown), [remarkTypeTableToMarkdown, { basePath: process.cwd() }], ]; ``` ```mdx ```","Remark plugins\n\nThe default plugin stack that flattens MDX components into markdown.\n\nRemark plugins\n\nPlugin selection rules\n\nUse defaultRemarkPlugins for any agent-facing or LLM output. Add remarkInclude when docs are composed from shared fragments. Use individual plugins only when you intentionally want to omit a flattener e.g. you don't use /docs/search-index.json /docs/search-content.json ``` ```ts await generateDocsSearchFiles({ outDir: \"public\", baseUrl: \"https://leadtype.dev\", mounts: [ { pathPrefix: \"\", urlPrefix: \"/docs\" }, { pathPrefix: \"changelog\", urlPrefix: \"/changelog\" }, ], }); ```","Search\n\nStatic search index, runtime helpers, and source-grounded answer streaming.\n\nSearch\n\nRuntime search\n\nThe runtime is edge-safe — no Node APIs, works on Vercel, Cloudflare, and anywhere else Results include heading paths, hash URLs, and snippets ready for a search UI. Search is still local and dependency-free, but it is not exact-token only. Query terms expand through lightweight stemming, prefix matches, typo-tolerant fallbacks, and a small built-in synonym map. Exact matches keep the highest weight so API names and config keys stay precise. Pass synonyms when your docs use product-specific vocabulary\n\n```ts import { searchDocs, type DocsSearchIndex, type DocsSearchContentStore, } from \"leadtype/search\"; import indexJson from \"../public/docs/search-index.json\"; import contentJson from \"../public/docs/search-content.json\"; const results = searchDocs( indexJson as DocsSearchIndex, \"tabs install\", { content: contentJson as DocsSearchContentStore } ); ``` ```ts const results = searchDocs(index, \"starter\", { content, synonyms: { starter: [\"quickstart\", \"getting started\"], }, }); ```","Search\n\nStatic search index, runtime helpers, and source-grounded answer streaming.\n\nSearch\n\nReading docs at runtime\n\nThe same index doubles as a virtual filesystem. Three readers, picked by what you have Use readDocsContentFile when you need the entire page for context links . Use readDocsContentChunk when a search result already named the right heading.\n\n```ts import { listDocsContentFiles, readDocsContentFile, readDocsContentChunk, } from \"leadtype/search\"; const allFiles = listDocsContentFiles(index); const wholePage = readDocsContentFile(index, \"guides/quickstart\", content); const oneChunk = readDocsContentChunk(index, \"chunk-0\", content); ```","Search\n\nStatic search index, runtime helpers, and source-grounded answer streaming.\n\nSearch\n\nSource-grounded answers\n\ncreateAnswerContext turns a query plus retrieved chunks into a system and prompt you pass to any model The system message instructs the model to answer only from the retrieved context, cite sources with 1 -style references, and say so when the context is insufficient.\n\n```ts import { createAnswerContext } from \"leadtype/search\"; const context = createAnswerContext(index, \"how do I run lint?\", { content, productName: \"My Library\", }); // → { system, prompt, sources } ```","Search\n\nStatic search index, runtime helpers, and source-grounded answer streaming.\n\nSearch\n\nStreaming via provider entry points\n\nThree thin wrappers around createAnswerContext that stream a Response and surface sources separately. Use one matching your runtime response is a plain text Response. sources is metadata for citation links — display it separately, don't embed it in the streamed answer. For TanStack, pass an explicit adapter . For Cloudflare, build one with createCloudflareDocsAdapter provider, model, options binding env.AI.gateway \"docs\" .\n\n```ts import { streamDocsAnswer } from \"leadtype/search/vercel\"; // Vercel AI SDK / AI Gateway import { streamDocsAnswer } from \"leadtype/search/tanstack\"; // TanStack AI import { streamDocsAnswer } from \"leadtype/search/cloudflare\"; // Cloudflare AI Gateway / Workers AI ``` ```ts const { response, sources } = streamDocsAnswer({ index, content, query, model: \"openai/gpt-5.5\", productName: \"My Library\", }); ```","Search\n\nStatic search index, runtime helpers, and source-grounded answer streaming.\n\nSearch\n\nBash tool adapters\n\nWhen you want an agent to explore docs with shell commands instead of receiving pre-selected chunks The adapter exposes a read-only virtual /docs filesystem with ls , cat , find , grep , and rg . Network commands, code execution, and writes are disabled. Use createDocsBashTool for Vercel AI SDK tool sets and createDocsBashTools for TanStack-compatible tools over the same filesystem.\n\n```ts import { createDocsBashTool, createDocsBashTools } from \"leadtype/search/bash\"; const { tools, instructions } = await createDocsBashTool(index, content); ```","Search\n\nStatic search index, runtime helpers, and source-grounded answer streaming.\n\nSearch\n\nAbuse guards\n\nReusable utilities for the request path Helper Purpose -- -- validateDocsQuery Trim and cap query text. readJsonWithLimit Reject oversized JSON bodies before parse. getClientIdentifier Read common proxy IP headers. createMemoryRateLimiter Implements RateLimiter for demos. The in-memory limiter is fine for demos. Production apps should adapt the RateLimiter interface to a shared store — Redis, Vercel KV, Cloudflare KV, or Durable Objects.","Search\n\nStatic search index, runtime helpers, and source-grounded answer streaming.\n\nSearch\n\nWhen to add embeddings\n\nStart with the local index. It is static, cheap, edge-safe, and fast for exact API names, config keys, error messages, and paths. Add embeddings only when Users search with vocabulary that doesn't match the docs e.g. \"make it faster\" matching a \"performance optimization\" page . Your docs grow past tens of thousands of chunks and the cold-start memory hit becomes noticeable. Even then, keep the lexical index for exact matches and layer embeddings on top — they're complementary, not replacements.","createDocsSource\n\nFramework-neutral docs source primitive — navigation, page loader, search index, and include resolver.\n\ncreateDocsSource\n\ncreateDocsSource is the framework-neutral entry point for any consumer that wants to render leadtype-authored MDX in their own renderer. It composes leadtype's primitives resolveDocsNavigation , convertMdxFile , createDocsSearchIndex , resolveInclude into a single source object. For fumadocs specifically, use the thin leadtype/fumadocs adapter that wraps this primitive.\n\n```ts import { createDocsSource } from \"leadtype\"; const source = await createDocsSource({ contentDir: \"./content/docs\", baseUrl: \"https://example.com\", groups: [ { slug: \"get-started\", title: \"Get Started\" }, { slug: \"guides\", title: \"Guides\" }, ], }); const page = await source.loadPage(\"quickstart\"); const navigation = await source.getNavigation(); const search = await source.buildSearchIndex(); ```","createDocsSource\n\nFramework-neutral docs source primitive — navigation, page loader, search index, and include resolver.\n\ncreateDocsSource\n\nConfiguration\n\nOption Type Notes -- -- -- contentDir string Required. Directory containing source .md / .mdx files. baseUrl string Used for absolute URLs in TOC and search index. groups DocsGroup Doc groups for navigation. Empty groups = all pages ungrouped. mounts DocsPathMount Multi-mount routing advanced . remarkPlugins PluggableList Defaults to mdxSourcePlugins . Pass to skip transforms. toc DocsTableOfContentsOptions \\ false TOC tuning; false skips TOC entirely. searchIndex CreateDocsSearchIndexOptions Search-index tuning. createDocsSource does no I/O on construction beyond a directory scan for listPages caching. Page bodies are loaded lazily.","createDocsSource\n\nFramework-neutral docs source primitive — navigation, page loader, search index, and include resolver.\n\ncreateDocsSource\n\nSource methods\n\ngetNavigation Promise; markdown: string; // resolved, plugin-transformed markdown ast: Root; // mdast Root after the source preset — render this for live MDX toc: DocsTableOfContentsItem[]; } ```","createDocsSource\n\nFramework-neutral docs source primitive — navigation, page loader, search index, and include resolver.\n\ncreateDocsSource\n\nSource methods\n\nbuildSearchIndex Promise./shared/session-flow.mdx ``` ```mdx ``` ```mdx ``` ```mdx This content can be reused in multiple pages. ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nEach section below shows the authored MDX and a live render.","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nCallout\n\nWraps supporting context, warnings, or tips. Variants change styling but flatten identically into a blockquote. ℹ️ Info Heads up Callouts wrap supporting context, warnings, or tips. The remark pipeline flattens them into blockquotes so agents still see the content. ⚠️ Warning Don't do this Variants like warning, success, error, and tip change the styling but flatten identically.\n\n```tsx Body content goes here. ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nCards\n\nA grid of short, linked entry points. Flattens to a bullet list of links. Convert Remark Search\n\n```tsx ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nSteps\n\nNumbered walkthroughs. Flattens to an ordered list with bold step titles. 1. Author docs in MDX Use the components in this list as authoring affordances. 2. Run the conversion leadtype generate writes flattened markdown to public/docs/ . 3. Serve both formats HTML for humans, .md for agents — same URL, content negotiated by the Accept header.\n\n```tsx Use the components in this list.`leadtype generate` writes flattened markdown. ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nTabs\n\nGroup equivalent content. Flattens to bold headings followed by content so agents do not need a JSX-aware renderer to read every variant. tanstack-start Use a Vite middleware dev/preview and a Nitro middleware prod to negotiate the Accept header. next-js Wire content negotiation in middleware.ts and serve .md from a route handler. vite A configureServer middleware is enough for static deployments where .md files live in public/ .\n\n```tsx ……… ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nCommandTabs\n\nPackage-manager-aware install or run commands. Flattens to a markdown table with one row per manager. Use mode=\"install\" when command is a package name, mode=\"run\" when command is a CLI name, and mode=\"create\" for starter commands. Use the commands prop for exact per-manager overrides. Package manager Command -- -- npm npm install leadtype pnpm pnpm add leadtype yarn yarn add leadtype bun bun add leadtype Package manager Command -- -- npm npx leadtype lint pnpm pnpm dlx leadtype lint yarn yarn dlx leadtype lint bun bunx leadtype lint\n\n```tsx ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nPrompt\n\nDisplays an agent-ready prompt with a copy action. Flattening preserves the prompt body as a fenced prompt block so copied instructions also survive in .md , root llms-full.txt , and bundled AGENTS.md output. Copy prompt for your coding agent Use this after generating artifacts.\n\n```tsx Inspect `public/docs/agent-readability.json`, then wire markdown responses before the HTML docs route. ``` ```prompt Inspect `public/docs/agent-readability.json`, then wire markdown responses before the HTML docs route. ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nAudience\n\nSplits browser-only and agent-only guidance without forking the page. Human content renders in the docs UI and is omitted from markdown output; agent content is hidden in the browser and included in generated markdown. This sentence appears only in generated markdown for agents.\n\n```tsx Click the robot icon in the header.Read generated markdown before editing runtime routes. ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nFileTree\n\nShows project structure in guides and release instructions. Flattening emits a fenced text tree so agents can read the same hierarchy without JSX.\n\n```tsx ``` ```text public/ ├── llms.txt └── docs/ └── index.md ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nAccordion\n\nCollapsible details for secondary content. Flattening ignores open/closed state and emits every item — accordions are not a place to hide content from agents. When should I use accordions? Use them for supporting details, troubleshooting notes, and optional reference material. Closed content is still flattened by the remark pipeline. Are accordions a good place to hide content from LLMs? No. Conversion ignores the open/closed state and emits everything inside.\n\n```tsx Use them for supporting details and optional reference material. ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nTopicSwitcher\n\nNavigation across equivalent docs topics — frameworks, SDKs, runtimes, deployment targets, product areas. Reader-facing only; it does not automatically read LLM topic config. Framework React — React integration Vue — Vue integration Svelte — Svelte integration\n\n```tsx ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nTypeTable and ExtractedTypeTable\n\nTypeTable is for explicit prop or type rows you already know. ExtractedTypeTable reads a TypeScript file at conversion time and extracts the table from a named type — keep its path stable. Property Type Description Default Required -- -- -- -- -- title string Heading rendered above the callout body. - ✅ Required variant CalloutVariant Visual treatment for the callout. info Optional deprecated \\ \\ boolean\\ \\ deprecated Marks the row as deprecated. false Optional\n\n```tsx ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nExample\n\nData-driven preview and source examples. The host component receives code as data; add file loaders or dynamic imports outside leadtype when an example needs app-specific behavior.\n\n```tsx The host app owns styling and runtime components while leadtype owns conversion. ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nComponent reference\n\nMermaid\n\nDiagrams authored as plain text. Renders client-side as interactive SVG. Flattening preserves the source as a fenced mermaid block so other tools can render the diagram from the markdown copy.\n\n```tsx |remark| Markdown Markdown -->|llms.txt| Agents`} /> ``` ```mermaid graph LR MDX -->|remark| Markdown Markdown -->|search index| API Markdown -->|llms.txt| Agents ```","Components\n\nMDX components the pipeline knows how to flatten into agent-readable markdown.\n\nComponents\n\nGuidelines\n\nKeep runtime components in your docs app. Leadtype stays out of UI. Keep component names stable. Renaming groups: [authoring, docs-site, reference]\"] page1[\"page A group: authoring\"] page2[\"page B group: docs-site\"] page3[\"page C group: docs-site\"] resolver[\"group resolver\"] nav[\"navigation tree\"] llms[\"llms.txt sections\"] agents[\"AGENTS.md sections\"] cfg --> resolver page1 --> resolver page2 --> resolver page3 --> resolver resolver --> nav resolver --> llms resolver --> agents ```","Frontmatter\n\nRequired fields, group semantics, and how authored MDX becomes a navigation tree.\n\nFrontmatter\n\nHow groups become a nav tree\n\nNested groups\n\nDeclare children in the config to build deeper trees A page sets group remote-source and lands in that nested slot. Only leaf groups no children directly contain pages; non-leaf groups are headings only.\n\n```ts { slug: \"docs-site\", title: \"Build a Docs Site\", children: [ { slug: \"remote-source\", title: \"Remote source\" }, { slug: \"build-a-docs-site\", title: \"Build a docs site\" }, ], } ```","Frontmatter\n\nRequired fields, group semantics, and how authored MDX becomes a navigation tree.\n\nFrontmatter\n\nOptional fields\n\nThe default lint schema also accepts Property Type Description Default Required -- -- -- -- -- icon string Icon name resolved by your sidebar component. - Optional deprecated boolean Marks the page as deprecated in nav and search. - Optional deprecatedReason string Short message paired with deprecated. - Optional experimental boolean Marks the page as experimental. - Optional canary boolean Hides from stable channels. - Optional new boolean Highlights the page as recently added. - Optional draft boolean Excludes from generation entirely. - Optional tags string\\ Free-form tags for search facets. - Optional availableIn Array\\<\\ framework, url?, title? Cross-framework availability map for TopicSwitcher pages. - Optional full boolean Layout hint for docs UIs that support full-width pages. - Optional lastModified and lastAuthor are filled in automatically when you pass --enrich-git to the CLI. Don't author them by hand.","Frontmatter\n\nRequired fields, group semantics, and how authored MDX becomes a navigation tree.\n\nFrontmatter\n\nLint rules\n\nleadtype lint enforces the schema, so violations surface in CI before they reach a build. The relevant rules schema — a required field is missing or has the wrong type. unknown-field — a top-level field isn't in the schema warn by default; --error-unknown to fail . parse-error — frontmatter or meta.json doesn't parse. invalid-link — a /docs/... link points to a route that doesn't exist. unresolved-placeholder — a doc URL still contains an unresolved framework placeholder. cross-framework-link — a framework-scoped page links to another framework's docs. See Lint rules for the full reference and how to extend the schema.","Frontmatter\n\nRequired fields, group semantics, and how authored MDX becomes a navigation tree.\n\nFrontmatter\n\nWhat this gives you\n\nOnce frontmatter is consistent, the rest of the pipeline works without per-page configuration. The same group value drives The sidebar position The llms.txt section Search metadata and AGENTS.md grouping The search filtering UI if you build one Cross-framework link checks in lint One field, one source of truth.","Add search\n\nGenerate a static docs search index, query it at runtime, and optionally stream source-grounded answers.\n\nAdd search\n\nLeadtype search is static by default. Build time produces two JSON files; runtime code imports or fetches those files and queries them without a database.","Add search\n\nGenerate a static docs search index, query it at runtime, and optionally stream source-grounded answers.\n\nAdd search\n\nGenerate the files\n\nleadtype generate writes search files in site mode If you run the pipeline from scripts, call the search generator after conversion This writes The index contains compact ranking data. The content store contains the text used for excerpts and answer context.\n\n```bash npx leadtype generate --src . --out public --base-url https://example.com ``` ```ts import { generateDocsSearchFiles } from \"leadtype/search/node\"; await generateDocsSearchFiles({ outDir: \"public\", baseUrl: \"https://example.com\", }); ``` ```txt public/docs/search-index.json public/docs/search-content.json ```","Add search\n\nGenerate a static docs search index, query it at runtime, and optionally stream source-grounded answers.\n\nAdd search\n\nQuery at runtime\n\nResults include page URLs, heading paths, hash URLs, and snippets. Render them in your own search UI.\n\n```ts import { searchDocs, type DocsSearchContentStore, type DocsSearchIndex, } from \"leadtype/search\"; import contentJson from \"../public/docs/search-content.json\"; import indexJson from \"../public/docs/search-index.json\"; const index = indexJson as DocsSearchIndex; const content = contentJson as DocsSearchContentStore; const results = searchDocs(index, \"run lint\", { content }); ```","Add search\n\nGenerate a static docs search index, query it at runtime, and optionally stream source-grounded answers.\n\nAdd search\n\nAdd vocabulary aliases\n\nSearch starts with lexical matching, stemming, prefix matches, typo-tolerant fallbacks, and a small built-in synonym map. Add product-specific synonyms only when users search with words your docs do not use\n\n```ts const results = searchDocs(index, \"starter\", { content, synonyms: { starter: [\"quickstart\", \"getting started\"], }, }); ```","Add search\n\nGenerate a static docs search index, query it at runtime, and optionally stream source-grounded answers.\n\nAdd search\n\nOptional AI answers\n\nUse source-grounded answers only after basic search works. Leadtype retrieves chunks from the static index, builds a constrained prompt, and leaves model choice to the provider entry point you import Display sources next to the streamed response. Do not ask the model to answer from memory; the answer context is built from retrieved docs chunks.\n\n```ts import { streamDocsAnswer } from \"leadtype/search/vercel\"; const { response, sources } = streamDocsAnswer({ index, content, query: \"How do I run docs lint in CI?\", model: \"openai/gpt-5.5\", productName: \"My Library\", }); ```","Add search\n\nGenerate a static docs search index, query it at runtime, and optionally stream source-grounded answers.\n\nAdd search\n\nGuard the endpoint\n\nFor API routes that accept user queries, use the request helpers from leadtype/search validateDocsQuery to trim and cap query text. readJsonWithLimit to reject oversized JSON bodies. getClientIdentifier to read common proxy IP headers. createMemoryRateLimiter for demos. Production apps should adapt the rate limiter interface to a shared store such as Redis, Vercel KV, Cloudflare KV, or Durable Objects.","Add search\n\nGenerate a static docs search index, query it at runtime, and optionally stream source-grounded answers.\n\nAdd search\n\nVerify\n\npublic/docs/search-index.json and public/docs/search-content.json exist and are non-empty. Searching for an exact API name returns the expected reference page. Searching for a guide phrase returns a result with a section hash. AI answers cite sources from the returned sources metadata.","Build a docs site\n\nPick the right leadtype integration shape for your docs app, and what each path gives you.\n\nBuild a docs site\n\nLeadtype offers two integration shapes for a docs site. Pick the one that fits how your app is built — both produce the same content, just at different layers of your stack.","Build a docs site\n\nPick the right leadtype integration shape for your docs app, and what each path gives you.\n\nBuild a docs site\n\nPick your path\n\nPath When to choose What you wire -- -- -- Source primitive Most cases. You're building a Next, Vite, Astro, or Fumadocs app and compile MDX with your bundler. createDocsSource or leadtype/fumadocs + createMdxSourcePlugins Static artifacts Your runtime needs flat files on disk CDN-only deploy, static export, agent-only consumption, multi-app sharing . leadtype generate CLI in your build script The two paths are not mutually exclusive — you can use the source primitive for your UI and still run leadtype generate for the llms.txt and agent-readability artifacts.\n\n```mermaid flowchart LR src[\"source MDX docs/*.mdx + meta.json\"] primitive[\"createDocsSource() (leadtype/mdx + leadtype/fumadocs)\"] cli[\"leadtype generate (CLI)\"] app[\"docs app (Next, Astro, Vite, Fumadocs)\"] artifacts[\"public/ llms.txt · docs/*.md search-index · sitemap\"] src --> primitive --> app src --> cli --> artifacts --> app ```","Build a docs site\n\nPick the right leadtype integration shape for your docs app, and what each path gives you.\n\nBuild a docs site\n\nSource primitive the common path\n\nIf your docs app compiles MDX through a bundler, use the source primitive. It gives your runtime loadPage slug returning frontmatter + AST + serialized markdown + TOC getNavigation returning the resolved group tree buildSearchIndex returning a static search index resolveInclude for programmatic partial loading → Use the source primitive — generic recipe → Integrate with Fumadocs — first-party adapter","Build a docs site\n\nPick the right leadtype integration shape for your docs app, and what each path gives you.\n\nBuild a docs site\n\nStatic artifacts the CLI path\n\nIf your runtime needs files on disk — for a CDN-only deploy, a static export, a separate agent-only service, or to share artifacts across multiple sibling apps — run the CLI. It writes public/llms.txt and public/llms-full.txt public/docs/ .md markdown mirrors public/docs/search-index.json + search-content.json public/docs/sitemap.xml , sitemap.md , robots.txt , agent-readability.json → Generate static artifacts — CLI workflow","Build a docs site\n\nPick the right leadtype integration shape for your docs app, and what each path gives you.\n\nBuild a docs site\n\nAdd the cross-cutting features\n\nWhichever path you pick, the same follow-on guides apply Add search — local search index + optional source-grounded answers Optimize docs for agents — markdown responses, llms.txt, discovery files Validate in CI — lint frontmatter, meta.json, internal links","Build a docs site\n\nPick the right leadtype integration shape for your docs app, and what each path gives you.\n\nBuild a docs site\n\nConfigure product and groups\n\nBoth paths read the same docs/docs.config.ts . Source repos own this file so groups and product metadata version with the docs Pages declare group in frontmatter; the config declares titles, order, and descriptions. See Frontmatter for the page-level schema.\n\n```ts import { defineDocsConfig } from \"leadtype\"; export default defineDocsConfig({ product: { name: \"c15t\", summary: \"Consent infrastructure for modern web apps.\", bullets: [\"Framework integrations.\", \"Consent primitives.\", \"Audit-friendly docs.\"], bestStartingPoints: [{ urlPath: \"/docs\" }, { urlPath: \"/docs/quickstart\" }], }, groups: [ { slug: \"get-started\", title: \"Get Started\" }, { slug: \"guides\", title: \"Guides\" }, { slug: \"reference\", title: \"Reference\" }, ], }); ```","Framework integration matrix\n\nUse Leadtype with native-feeling recipes for Next, Nuxt, Astro, SvelteKit, TanStack Start, and Fumadocs.\n\nFramework integration matrix\n\nLeadtype stays framework-neutral at the core, then exposes thin adapters where a framework has a native routing, prerender, or state convention. The adapters do not render UI components; your app owns markup, styling, and accessibility. Framework Human docs route Agent markdown route Search helper Dogfood app -- -- -- -- -- Next.js App Router generateStaticParams + server component middleware or leadtype/next route handler leadtype/search/react apps/next-example Astro getStaticPaths endpoint at pages/docs/ ...slug .md.ts leadtype/search/client apps/astro-example SvelteKit +page.server.ts + +page.svelte +server.ts under a .md route leadtype/search/svelte apps/sveltekit-example Nuxt/Nitro Vue page + Nitro API route Nitro route or middleware leadtype/search/vue apps/nuxt-example TanStack Start catch-all route + generated manifest server route / Nitro middleware leadtype/search/react Planned Fumadocs leadtype/fumadocs source adapter Next route/middleware pattern React search helper Planned","Framework integration matrix\n\nUse Leadtype with native-feeling recipes for Next, Nuxt, Astro, SvelteKit, TanStack Start, and Fumadocs.\n\nFramework integration matrix\n\nShared build step\n\nEvery dogfood app generates the same site artifacts before the framework build That writes /llms.txt , /llms-full.txt , /docs/ .md , search JSON, sitemap files, robots files, and /docs/agent-readability.json . SvelteKit uses the same command shape with --out static because SvelteKit serves static assets from that directory. If your docs are mounted somewhere other than /docs , pass the matching path to the adapter's basePath or artifactBasePath option.\n\n```bash leadtype generate \\ --src ../../examples/shared-docs \\ --out public \\ --base-url http://localhost:3000 ```","Framework integration matrix\n\nUse Leadtype with native-feeling recipes for Next, Nuxt, Astro, SvelteKit, TanStack Start, and Fumadocs.\n\nFramework integration matrix\n\nNext.js App Router\n\nUse leadtype/next for static params and page data Use leadtype/search/react in client search components. Use createDocsProxy when a Next Proxy owns markdown negotiation for the same route tree\n\n```tsx title=\"app/docs/[[...slug]]/page.tsx\" import { createGenerateStaticParams, createLoadPageData, } from \"leadtype/next\"; import { source } from \"@/lib/source\"; const loadPageData = createLoadPageData({ source }); export const generateStaticParams = createGenerateStaticParams({ source }); ``` ```ts title=\"proxy.ts\" import { createDocsProxy } from \"leadtype/next\"; export const proxy = createDocsProxy({ manifest }); ```","Framework integration matrix\n\nUse Leadtype with native-feeling recipes for Next, Nuxt, Astro, SvelteKit, TanStack Start, and Fumadocs.\n\nFramework integration matrix\n\nAstro\n\nUse leadtype/astro to match Astro's getStaticPaths shape and endpoint signature Astro does not need a fake hook. Use leadtype/search/client from a browser script or from any island framework.\n\n```ts title=\"src/pages/docs/[...slug].astro\" import { createGetStaticPaths, createLoadPageData } from \"leadtype/astro\"; import { source } from \"../../lib/source\"; export const getStaticPaths = createGetStaticPaths({ source }); const page = await createLoadPageData({ source })(Astro.params.slug); ``` ```ts title=\"src/pages/docs/[...slug].md.ts\" import { createDocsEndpoint, createMarkdownStaticPaths } from \"leadtype/astro\"; import { source } from \"../../lib/source\"; export const getStaticPaths = createMarkdownStaticPaths({ source }); export const GET = createDocsEndpoint({ manifest }); ```","Framework integration matrix\n\nUse Leadtype with native-feeling recipes for Next, Nuxt, Astro, SvelteKit, TanStack Start, and Fumadocs.\n\nFramework integration matrix\n\nSvelteKit\n\nUse mdsvex with the source preset, then keep page data in +page.server.ts and markdown responses in a .md endpoint Use leadtype/search/svelte for store-based client search.\n\n```ts title=\"src/routes/docs/[...slug]/+page.server.ts\" import { createEntries, createLoadPageData } from \"leadtype/sveltekit\"; import { source } from \"$lib/source\"; export const entries = createEntries({ source }); const loadPageData = createLoadPageData({ source }); ```","Framework integration matrix\n\nUse Leadtype with native-feeling recipes for Next, Nuxt, Astro, SvelteKit, TanStack Start, and Fumadocs.\n\nFramework integration matrix\n\nNuxt/Nitro\n\nNuxt Content is a Nuxt CMS. Leadtype is the portable artifact pipeline use it when the same source needs framework-agnostic markdown, search JSON, llms.txt , and package docs. Keep Leadtype source loading and adapter helpers in Nitro server files, not Vue client bundles Use leadtype/search/vue for the client search composable.\n\n```ts title=\"server/api/docs.get.ts\" import { createLoadPageData } from \"leadtype/nuxt\"; const source = await getSource(); const loadPageData = createLoadPageData({ source }); ```","Framework integration matrix\n\nUse Leadtype with native-feeling recipes for Next, Nuxt, Astro, SvelteKit, TanStack Start, and Fumadocs.\n\nFramework integration matrix\n\nTanStack Start and Fumadocs\n\nTanStack Start follows the same shape as the existing dogfood app generate a route manifest, lazy-load MDX modules by slug, and use leadtype/search/react for search. Fumadocs uses leadtype/fumadocs to map the source primitive into Fumadocs' Source contract. Pair it with the same generated artifact serving used by the Next example.","Framework integration matrix\n\nUse Leadtype with native-feeling recipes for Next, Nuxt, Astro, SvelteKit, TanStack Start, and Fumadocs.\n\nFramework integration matrix\n\nVerification\n\nThe implemented dogfood apps are expected to build in CI\n\n```bash bun --filter next-example build bun --filter astro-example build bun --filter sveltekit-example build bun --filter nuxt-example build ```","Generate static artifacts\n\nRun leadtype generate from your build pipeline to write llms.txt, markdown mirrors, search index, sitemap, and agent-readability files to disk.\n\nGenerate static artifacts\n\nUse this path when your runtime needs files on disk — a CDN-only deploy, a static export, a separate agent-only service, or multiple sibling apps sharing one corpus. The leadtype generate CLI walks your MDX source and writes a complete set of files into public/ . For app runtimes that compile MDX through a bundler Next, Astro, Vite, Fumadocs , prefer the source primitive — it skips the disk round-trip.","Generate static artifacts\n\nRun leadtype generate from your build pipeline to write llms.txt, markdown mirrors, search index, sitemap, and agent-readability files to disk.\n\nGenerate static artifacts\n\nThe flow\n\n```mermaid flowchart LR repo[\"source repo docs/*.mdx\"] clone[\"checkout or clone .docs-src/c15t\"] lint[\"leadtype lint\"] generate[\"leadtype generate\"] public[\"public/ llms.txt · llms-full.txt docs/*.md search · agent metadata\"] app[\"any consumer (docs app, agent, CDN)\"] repo --> clone --> lint --> generate --> public --> app ```","Generate static artifacts\n\nRun leadtype generate from your build pipeline to write llms.txt, markdown mirrors, search index, sitemap, and agent-readability files to disk.\n\nGenerate static artifacts\n\nFetch the source repo\n\nIn CI, check out the docs source before the build. For a public repo, a shallow clone is enough For private repos, use your CI platform's checkout action or a read-only deploy key. The important part is that leadtype receives a normal filesystem path whose root contains docs/ .\n\n```bash rm -rf .docs-src/c15t git clone --depth 1 https://github.com/c15t/c15t .docs-src/c15t ```","Generate static artifacts\n\nRun leadtype generate from your build pipeline to write llms.txt, markdown mirrors, search index, sitemap, and agent-readability files to disk.\n\nGenerate static artifacts\n\nLint before generate\n\nRun lint against the fetched docs before writing generated output. Lint fails with file and line context, which is easier to debug than a later app build using stale artifacts\n\n```bash npx leadtype lint .docs-src/c15t/docs \\ --format github \\ --error-unknown \\ --max-warnings 0 ```","Generate static artifacts\n\nRun leadtype generate from your build pipeline to write llms.txt, markdown mirrors, search index, sitemap, and agent-readability files to disk.\n\nGenerate static artifacts\n\nGenerate\n\nPoint --src at the fetched repo root and --out at the directory you'll serve That command writes public/llms.txt and public/llms-full.txt public/docs/ .md public/docs/search-index.json and public/docs/search-content.json public/docs/sitemap.xml , sitemap.md , robots.txt , and agent-readability.json Use --json in CI so automation can record resolved groups, output files, filters, and search index stats.\n\n```bash npx leadtype generate \\ --src .docs-src/c15t \\ --out public \\ --base-url https://docs.example.com \\ --json ```","Generate static artifacts\n\nRun leadtype generate from your build pipeline to write llms.txt, markdown mirrors, search index, sitemap, and agent-readability files to disk.\n\nGenerate static artifacts\n\nMulti-source mounting\n\nSome projects keep docs and release notes side by side instead of putting everything under docs/ Repeat --docs-dir to include those folders in the same generated corpus By default, extra sources are mounted under /docs/ {title ?
{title}
: null}
{children}
); } ```","Integrate with Fumadocs\n\nWire leadtype's content layer into a fumadocs app for nav, search, and includes.\n\nIntegrate with Fumadocs\n\nLoad a page from a server component\n\nIf you prefer fumadocs's built-in page resolution, call source.getPage slug and import the source .mdx directly through fumadocs-mdx as you normally would — the createMdxSourcePlugins preset will resolve includes during MDX compilation.\n\n```tsx title=\"app/docs/[[...slug]]/page.tsx\" import { notFound } from \"next/navigation\"; import { MDXRemote } from \"next-mdx-remote-client/rsc\"; import { source } from \"@/content/source\"; import { mdxComponents } from \"@/lib/mdx-components\"; export default async function Page({ params, }: { params: Promise<{ slug?: string[] }>; }) { const { slug } = await params; const page = await source.leadtype.loadPage(slug ?? []); if (!page) { notFound(); } return (
{page.title}
); } ```","Integrate with Fumadocs\n\nWire leadtype's content layer into a fumadocs app for nav, search, and includes.\n\nIntegrate with Fumadocs\n\nAdd search\n\nBuild a search index from the same source For provider-specific search Vercel AI, TanStack, Cloudflare , wire the bundle into a leadtype/search/ adapter. See Search.\n\n```ts title=\"app/api/search/route.ts\" import { source } from \"@/content/source\"; const bundle = await source.leadtype.buildSearchIndex(); export async function GET() { return Response.json(bundle.index); } ```","Optimize docs for agents\n\nSet up llms.txt, markdown mirrors, JSON-LD, sitemaps, robots.txt, and audit checks for an agent-readable docs site.\n\nOptimize docs for agents\n\nUse this guide when you already have a docs site and want agents to find, fetch, attribute, and cite the same content humans read in the browser. Leadtype handles the generated files. Your app wires those files into routing and HTML. The default output shape is based on the repo's agent evals. See Evals for the benchmark summary and the open question around larger-corpus llms-full.txt scaling.","Optimize docs for agents\n\nSet up llms.txt, markdown mirrors, JSON-LD, sitemaps, robots.txt, and audit checks for an agent-readable docs site.\n\nOptimize docs for agents\n\nWhat good looks like\n\nAn agent-readable docs site has four layers 1. Discovery files /llms.txt , /sitemap.xml , /sitemap.md , and /robots.txt tell agents what exists and where to start. 2. Markdown retrieval Each docs page has a markdown mirror at /docs/page.md , and agent requests to /docs/page can receive markdown instead of HTML. 3. Structured HTML metadata Human HTML pages include JSON-LD, canonical links, and markdown alternate links so agents can extract page identity without guessing from the DOM. 4. Attribution metadata Markdown responses include canonical url and last updated frontmatter so copied content keeps its source and freshness.","Optimize docs for agents\n\nSet up llms.txt, markdown mirrors, JSON-LD, sitemaps, robots.txt, and audit checks for an agent-readable docs site.\n\nOptimize docs for agents\n\n1. Generate the artifacts\n\nRun the site-mode pipeline before your app build This writes the docs-scoped files under public/docs/ and the top-level public/llms.txt The generated agent-readability.json manifest is the bridge between build-time content and runtime requests. It contains page URLs, markdown mirror paths, titles, descriptions, group navigation, and freshness dates.\n\n```bash npx leadtype generate \\ --src . \\ --out public \\ --base-url https://leadtype.dev \\ --name \"My product\" \\ --summary \"One sentence about the product.\" ``` ```txt public/ ├── llms.txt ├── llms-full.txt └── docs/ ├── index.md ├── quickstart.md ├── llms.txt ├── sitemap.xml ├── sitemap.md ├── robots.txt └── agent-readability.json ```","Optimize docs for agents\n\nSet up llms.txt, markdown mirrors, JSON-LD, sitemaps, robots.txt, and audit checks for an agent-readable docs site.\n\nOptimize docs for agents\n\n2. Add one middleware\n\nPut the agent-readable routes before your HTML docs route. This Node/Bun example handles root discovery files, docs-scoped discovery files, direct .md URLs, and Accept text/markdown requests in one place If you also have marketing, blog, changelog, or product pages, pass them through the optional pages field — the regenerator merges them into the rebased output The other generated artifacts — /llms.txt , /docs/llms.txt , /llms-full.txt , /docs/agent-readability.json — use root-relative links and serve fine as static files straight from public/ . Keep the docs-scoped versions too /docs/sitemap.xml etc. . Audits and agents may request both /sitemap.xml and /docs/sitemap.xml , especially when the audited URL is /docs .\n\n```ts import { readFile } from \"node:fs/promises\"; import { join } from \"node:path\"; import manifestJson from \"../public/docs/agent-readability.json\" with { type: \"json\", }; import { createAgentMarkdownResponse, createRobotsTxtResponse, createSitemapMarkdownResponse, createSitemapXmlResponse, type AgentReadabilityManifest, type MarkdownMirrorTarget, } from \"leadtype/llm/readability\"; const manifest = { ...manifestJson, version: 1, } as AgentReadabilityManifest; async function readMarkdownFile( target: MarkdownMirrorTarget ): Promise { try { return await readFile(join(process.cwd(), \"public\", target.filePath), \"utf8\"); } catch (error) { if ( typeof error === \"object\" && error !== null && \"code\" in error && (error.code === \"ENOENT\" || error.code === \"ENOTDIR\") ) { return null; } throw error; } } export async function handleDocsRequest( request: Request ): Promise { if (request.method !== \"GET\" && request.method !== \"HEAD\") { return null; } const url = new URL(request.url); const requestOrigin = url.origin; switch (url.pathname) { case \"/sitemap.xml\": case \"/docs/sitemap.xml\": return createSitemapXmlResponse({ manifest, requestOrigin }); case","Optimize docs for agents\n\nSet up llms.txt, markdown mirrors, JSON-LD, sitemaps, robots.txt, and audit checks for an agent-readable docs site.\n\nOptimize docs for agents\n\n2. Add one middleware\n\nstOrigin = url.origin; switch (url.pathname) { case \"/sitemap.xml\": case \"/docs/sitemap.xml\": return createSitemapXmlResponse({ manifest, requestOrigin }); case \"/sitemap.md\": case \"/docs/sitemap.md\": return createSitemapMarkdownResponse({ manifest, requestOrigin }); case \"/robots.txt\": return createRobotsTxtResponse({ manifest, requestOrigin }); case \"/docs/robots.txt\": return createRobotsTxtResponse({ manifest, requestOrigin, sitemapUrlPath: \"/docs/sitemap.xml\", }); default: return createAgentMarkdownResponse({ urlPath: url.pathname, method: request.method, headers: Object.fromEntries(request.headers), manifest, readMarkdownFile, requestOrigin, }); } } ``` ```ts return createSitemapXmlResponse({ manifest, requestOrigin, pages: [...manifest.pages, ...marketingPages, ...blogPages], }); ```","Optimize docs for agents\n\nSet up llms.txt, markdown mirrors, JSON-LD, sitemaps, robots.txt, and audit checks for an agent-readable docs site.\n\nOptimize docs for agents\n\n3. Add JSON-LD to docs pages\n\nUse the manifest entry for the current page and render Schema.org JSON-LD into the HTML head Use renderJsonLd page, manifest if your framework has a typed metadata API. Use renderJsonLdScript page, manifest if your framework expects an HTML string. Also add canonical and markdown alternate links The JSON-LD gives agents the page title, description, canonical URL, last modified date, and breadcrumbs without scraping your rendered layout.\n\n```ts import agentManifest from \"../public/docs/agent-readability.json\"; import { renderJsonLd, renderJsonLdScript } from \"leadtype/llm/readability\"; const page = agentManifest.pages.find( (entry) => entry.urlPath === \"/docs/quickstart\" ); if (page) { const jsonLd = renderJsonLd(page, agentManifest); const script = renderJsonLdScript(page, agentManifest); } ``` ```html ```","Optimize docs for agents\n\nSet up llms.txt, markdown mirrors, JSON-LD, sitemaps, robots.txt, and audit checks for an agent-readable docs site.\n\nOptimize docs for agents\n\n4. Return markdown to agents\n\nThe middleware above uses createAgentMarkdownResponse . It returns a Web Response or null when the path is not an agent-oriented markdown request and handles Accept text/markdown and Accept text/plain content negotiation q-values respected . Known AI user-agent headers GPTBot, ClaudeBot, Bingbot, AmazonBot, MetaExternalAgent, PerplexityBot, MistralBot, AppleBot, ByteSpider, YouBot, … . Direct .md URLs such as /docs/quickstart.md . canonical url and last updated frontmatter aliases injected automatically. 200 markdown responses for missing docs pages, so agents do not discard the body. Content-Type text/markdown; charset=utf-8 , Vary Accept , User-Agent , Link <… ; rel=\"canonical\" , Cache-Control public, max-age=300, must-revalidate . readMarkdownFile may be sync or async. In Node/Bun, read from disk. In Cloudflare, fetch from KV/R2 or an asset binding. In Vercel Edge, fetch from the deployment's static asset URL. Put that logic wherever your framework can intercept docs requests before its HTML route Framework/runtime Where it usually goes -- -- TanStack Start / nitro server/middleware/agent-readability.ts h3 .","Optimize docs for agents\n\nSet up llms.txt, markdown mirrors, JSON-LD, sitemaps, robots.txt, and audit checks for an agent-readable docs site.\n\nOptimize docs for agents\n\n4. Return markdown to agents\n\nn intercept docs requests before its HTML route Framework/runtime Where it usually goes -- -- TanStack Start / nitro server/middleware/agent-readability.ts h3 . One middleware handles both the markdown response and the sitemap/robots regenerators — runs in dev, preview, and prod. Nuxt server/middleware/agent-readability.ts h3 — same shape as the TanStack Start example. Next.js middleware.ts Edge or a catch-all route handler before the docs page. Astro An endpoint at pages/docs/ ...slug .md.ts or astro middleware . Cloudflare Workers/Pages Worker fetch handler with KV/R2 asset binding for the markdown reader. Express/Hono/Fastify Middleware before the docs HTML route. Tip if you keep static sitemap.xml / sitemap.md / robots.txt files in your build output, your framework's static handler may serve them before your middleware can rebase URLs to the live origin. Either delete the static copies after the build so the middleware always runs or make sure your middleware is registered ahead of static-asset serving. Do not rewrite llms.txt , sitemap.xml , sitemap.md , robots.txt , llms-full.txt , or agent-readability.json to page markdown. The helper leaves those artifact paths alone.","Optimize docs for agents\n\nSet up llms.txt, markdown mirrors, JSON-LD, sitemaps, robots.txt, and audit checks for an agent-readable docs site.\n\nOptimize docs for agents\n\n4. Return markdown to agents\n\nWhy the sitemap and robots responses are regenerated, not static\n\nsitemap.xml 's }) { const page = await loadPageData((await params).slug); if (!page) notFound(); return (
{page.title}
{page.description ?
{page.description}
: null} ); } ``` ```ts","Use the source primitive\n\nWire createDocsSource into Next, Astro, Vite, Nuxt, SvelteKit, or any MDX-aware bundler. Same primitive, multiple host shapes.\n\nUse the source primitive\n\nWire into your framework\n\nNext App Router\n\npage.title} {page.description ?
{page.description}
: null} ); } ``` ```ts title=\"app/docs/[[...slug]]/route.ts\" import { createDocsRouteHandler } from \"leadtype/next\"; import manifest from \"@/generated/agent-readability.json\" with { type: \"json\" }; export const GET = createDocsRouteHandler({ manifest: { ...manifest, version: 1 } as const, }); ```","Use the source primitive\n\nWire createDocsSource into Next, Astro, Vite, Nuxt, SvelteKit, or any MDX-aware bundler. Same primitive, multiple host shapes.\n\nUse the source primitive\n\nWire into your framework\n\nAstro Content Collections\n\nUse Astro's native content collection schema for typed frontmatter. Call source.loadPage from leadtype only when you need programmatic include resolution, search, or navigation. See Astro's content collections docs for the routing pattern.\n\n```ts title=\"astro.config.mjs\" import { defineConfig } from \"astro/config\"; import mdx from \"@astrojs/mdx\"; import { createMdxSourcePlugins } from \"leadtype/mdx\"; export default defineConfig({ integrations: [mdx({ remarkPlugins: [...createMdxSourcePlugins()] })], }); ```","Use the source primitive\n\nWire createDocsSource into Next, Astro, Vite, Nuxt, SvelteKit, or any MDX-aware bundler. Same primitive, multiple host shapes.\n\nUse the source primitive\n\nWire into your framework\n\nTanStack Start\n\nGenerate docs-pages.json at build time by calling createDocsSource .listPages from a build script and writing each page's slug , urlPath , and globKey path relative to the catch-all route, POSIX separators . There is no leadtype/tanstack-start export yet. The future adapter should wrap this same shape with TanStack Router route helpers, server functions, and React state helpers. TanStack AI answer streaming already lives separately under leadtype/search/tanstack .\n\n```ts title=\"vite.config.ts\" import mdx from \"@mdx-js/rollup\"; import { tanstackStart } from \"@tanstack/react-start/plugin/vite\"; import viteReact from \"@vitejs/plugin-react\"; import { createMdxSourcePlugins } from \"leadtype/mdx\"; import remarkFrontmatter from \"remark-frontmatter\"; import { defineConfig } from \"vite\"; export default defineConfig({ plugins: [ { ...mdx({ providerImportSource: \"@mdx-js/react\", remarkPlugins: [remarkFrontmatter, ...createMdxSourcePlugins()], }), enforce: \"pre\", }, tanstackStart(), viteReact({ include: /\\.(mdx|[jt]sx?)$/ }), ], }); ``` ```tsx title=\"src/routes/docs/$.tsx\" import { createFileRoute, notFound } from \"@tanstack/react-router\"; import { type ComponentType, lazy, Suspense, useMemo } from \"react\"; import docsPages from \"@/generated/docs-pages.json\"; type DocsPage = { slug: string[]; globKey: string; urlPath: string }; const pagesBySlug = new Map( (docsPages as DocsPage[]).map((p) => [p.slug.join(\"/\"), p]) ); // import.meta.glob keys are POSIX paths; the build-time manifest writes // matching keys so each slug maps to its lazy-loaded MDX module.","Use the source primitive\n\nWire createDocsSource into Next, Astro, Vite, Nuxt, SvelteKit, or any MDX-aware bundler. Same primitive, multiple host shapes.\n\nUse the source primitive\n\nWire into your framework\n\nTanStack Start\n\nlug.join(\"/\"), p]) ); // import.meta.glob keys are POSIX paths; the build-time manifest writes // matching keys so each slug maps to its lazy-loaded MDX module. const mdxModules = import.meta.glob<{ default: ComponentType }>( \"../../../content/docs/**/*.mdx\" ); const TRAILING_SLASH = /\\/+$/; function resolvePage(splat: string | undefined): DocsPage | null { if (!splat) return null; return pagesBySlug.get(splat.replace(TRAILING_SLASH, \"\")) ?? null; } function MissingMdxModule({ urlPath }: { urlPath: string }) { return (
MDX module not found for {urlPath}. Re-run your docs manifest script after adding new pages.
); } export const Route = createFileRoute(\"/docs/$\")({ beforeLoad: ({ params }) => { if (!resolvePage(params._splat)) throw notFound(); }, component: DocsCatchAllRoute, }); function DocsCatchAllRoute() { const { _splat } = Route.useParams(); // beforeLoad guarantees this is non-null when the component renders. const page = resolvePage(_splat) as DocsPage; // `lazy()` must run outside the render body — calling it inline returns // a fresh lazy component every render, defeats Suspense caching, and // remounts the MDX page on every parent re-render.","Use the source primitive\n\nWire createDocsSource into Next, Astro, Vite, Nuxt, SvelteKit, or any MDX-aware bundler. Same primitive, multiple host shapes.\n\nUse the source primitive\n\nWire into your framework\n\nTanStack Start\n\nender body — calling it inline returns // a fresh lazy component every render, defeats Suspense caching, and // remounts the MDX page on every parent re-render. Memoize on the // globKey so the cached lazy component is reused across renders. Guard // the lookup in case the manifest references a file the glob didn't // pick up (stale manifest, file deleted between builds, …). const MdxComponent = useMemo(() => { const loader = mdxModules[page.globKey]; if (!loader) { return () => ; } return lazy(loader); }, [page.globKey, page.urlPath]); return ( ); } ```","Use the source primitive\n\nWire createDocsSource into Next, Astro, Vite, Nuxt, SvelteKit, or any MDX-aware bundler. Same primitive, multiple host shapes.\n\nUse the source primitive\n\nWire into your framework\n\nVue + Vite\n\nUse a Vue content or MDX plugin that exposes a unified/remark plugin list, then pass Leadtype's source preset into that plugin. Leadtype supplies the transforms, route/search data, and tag prop contracts; your Vue app owns rendering. For client search today, fetch /docs/search-index.json and /docs/search-content.json , then call searchDocs from leadtype/search . A future leadtype/search/vue helper should expose the same behavior as a Vue composable.\n\n```ts title=\"vite.config.ts\" import vue from \"@vitejs/plugin-vue\"; import { createMdxSourcePlugins } from \"leadtype/mdx\"; import { defineConfig } from \"vite\"; const leadtypeRemarkPlugins = createMdxSourcePlugins(); export default defineConfig({ plugins: [ vue(), // Add `leadtypeRemarkPlugins` to your Vue MDX/content plugin's // remark/unified plugin list. ], }); ```","Use the source primitive\n\nWire createDocsSource into Next, Astro, Vite, Nuxt, SvelteKit, or any MDX-aware bundler. Same primitive, multiple host shapes.\n\nUse the source primitive\n\nWire into your framework\n\nVite + @mdx-js/rollup React, Preact, Solid, or custom JSX runtimes\n\nNuxt, SvelteKit, Astro, TanStack Start, Vue, and Svelte recipes wire createDocsSource manually today. Dedicated leadtype/nuxt , leadtype/sveltekit , leadtype/astro , leadtype/tanstack-start , leadtype/search/vue , and leadtype/search/svelte adapters with native handler factories and state primitives are tracked under issue 41 and the search-helper follow-up 45.\n\n```ts title=\"vite.config.ts\" import mdx from \"@mdx-js/rollup\"; import { createMdxSourcePlugins } from \"leadtype/mdx\"; export default { plugins: [ mdx({ remarkPlugins: [...createMdxSourcePlugins()] }), // ...your framework plugin: viteReact / vue / solid / svelte ], }; ```","Use the source primitive\n\nWire createDocsSource into Next, Astro, Vite, Nuxt, SvelteKit, or any MDX-aware bundler. Same primitive, multiple host shapes.\n\nUse the source primitive\n\nWire into your framework\n\nNuxt\n\nUse Nitro routes for generated markdown and Agent Readability artifacts. For search, load the generated JSON files from a Vue composable and pass them to searchDocs from leadtype/search ; a future leadtype/nuxt adapter should make that composable copy-paste small without owning UI.\n\n```ts title=\"nuxt.config.ts\" import { createMdxSourcePlugins } from \"leadtype/mdx\"; export default defineNuxtConfig({ modules: [\"@nuxtjs/mdc\"], mdc: { remarkPlugins: [...createMdxSourcePlugins()] }, }); ```","Use the source primitive\n\nWire createDocsSource into Next, Astro, Vite, Nuxt, SvelteKit, or any MDX-aware bundler. Same primitive, multiple host shapes.\n\nUse the source primitive\n\nWire into your framework\n\nSvelteKit + mdsvex\n\nUse +page.server.ts to call source.loadPage slug , entries to prerender known routes from source.listPages , and +server.ts for markdown or JSON artifact responses. For search, load generated JSON into a store or rune and call searchDocs ; a future leadtype/search/svelte helper should keep that state primitive UI-free.\n\n```ts title=\"svelte.config.js\" import { mdsvex } from \"mdsvex\"; import { createMdxSourcePlugins } from \"leadtype/mdx\"; export default { extensions: [\".svelte\", \".svx\", \".mdx\"], preprocess: mdsvex({ remarkPlugins: [...createMdxSourcePlugins()] }), }; ```","Use the source primitive\n\nWire createDocsSource into Next, Astro, Vite, Nuxt, SvelteKit, or any MDX-aware bundler. Same primitive, multiple host shapes.\n\nUse the source primitive\n\nWire into your framework\n\nPattern for any other host\n\nIf your framework's MDX integration accepts a remark plugin list, leadtype works. Three pieces every time 1. Add createMdxSourcePlugins to the remark list so ( ), // ... Tabs, Tab, Steps, Step, Cards, Card, TypeTable, etc. }; ```","Use the source primitive\n\nWire createDocsSource into Next, Astro, Vite, Nuxt, SvelteKit, or any MDX-aware bundler. Same primitive, multiple host shapes.\n\nUse the source primitive\n\nBuild the sidebar from navigation\n\nEach page carries a toc field DocsTableOfContentsItem you can render as an \"On this page\" rail.\n\n```tsx title=\"components/sidebar.tsx\" import { source } from \"@/lib/source\"; export async function Sidebar({ currentUrlPath }: { currentUrlPath: string }) { const navigation = await source.getNavigation(); return ( ); } ```","Use the source primitive\n\nWire createDocsSource into Next, Astro, Vite, Nuxt, SvelteKit, or any MDX-aware bundler. Same primitive, multiple host shapes.\n\nUse the source primitive\n\nMatch heading slugs\n\nsource.loadPage .toc uses slugifyDocsHeading to derive anchor IDs. Your rendered heading components need matching id attributes Wire Heading2 and h3 , etc. into your mdxComponents map. Authors can still pin an explicit id on a heading.\n\n```tsx import { slugifyDocsHeading } from \"leadtype/llm/readability\"; function textFromChildren(children: unknown): string { if (typeof children === \"string\" || typeof children === \"number\") { return String(children); } if (Array.isArray(children)) return children.map(textFromChildren).join(\"\"); // (recurse into React elements as needed) return \"\"; } const Heading2 = ({ children, id, ...props }: React.HTMLAttributes) => { const headingId = id ?? slugifyDocsHeading(textFromChildren(children)); return
{children}
; }; ```","Use the source primitive\n\nWire createDocsSource into Next, Astro, Vite, Nuxt, SvelteKit, or any MDX-aware bundler. Same primitive, multiple host shapes.\n\nUse the source primitive\n\nBuild a search index\n\nFor provider-specific search Vercel AI, TanStack, Cloudflare , feed the bundle into a leadtype/search/ adapter — see Add search.\n\n```tsx title=\"app/api/search/route.ts\" import { source } from \"@/lib/source\"; const bundle = await source.buildSearchIndex(); export async function GET() { return Response.json(bundle.index); } ```","Use the source primitive\n\nWire createDocsSource into Next, Astro, Vite, Nuxt, SvelteKit, or any MDX-aware bundler. Same primitive, multiple host shapes.\n\nUse the source primitive\n\nTroubleshooting\n\n lint-report.json ```","Validate in CI\n\nRun leadtype lint in CI so frontmatter, navigation, and link issues fail PRs before publish.\n\nValidate in CI\n\nLocal pre-push hook\n\nCatch issues before they reach CI by running lint in a husky pre-push hook Keep it under a second by limiting the scan to changed files when you have many pages. The CLI accepts repeated --ignore globs to skip stale or generated paths.\n\n```bash #!/usr/bin/env sh npx leadtype lint docs --max-warnings 0 ```","Validate in CI\n\nRun leadtype lint in CI so frontmatter, navigation, and link issues fail PRs before publish.\n\nValidate in CI\n\nRun before generate\n\nWhen leadtype lint and leadtype generate both run in the same job, lint first Generate fails noisily on unknown groups or broken includes. Lint fails specifically on content schema problems with file/line context — much easier to debug.\n\n```bash npx leadtype lint docs --error-unknown npx leadtype generate --src . --out public --json ```","Validate in CI\n\nRun leadtype lint in CI so frontmatter, navigation, and link issues fail PRs before publish.\n\nValidate in CI\n\nWhat to fix first\n\nWhen CI fails on a lot of violations, fix them in this order 1. parse-error — frontmatter is broken; nothing else can validate. 2. schema — missing or wrong-typed required fields. 3. unresolved-placeholder — content bug, not a config bug. 4. invalid-link and cross-framework-link — usually a stale link after a docs move. 5. unknown-field — last; either delete the field or extend the schema.","How it works\n\nThe mental model: one MDX source, a remark pipeline, two output modes, three audiences.\n\nHow it works\n\nLeadtype takes one input — a folder of MDX — and produces every shape your docs need to take. This page names every piece so the rest of the docs make sense.","How it works\n\nThe mental model: one MDX source, a remark pipeline, two output modes, three audiences.\n\nHow it works\n\nThe pipeline\n\nThe remark stack is what turns interactive MDX components into agent-readable markdown. JSX gets flattened — a title · description · group · body\"] fm[\"frontmatter parser\"] remark[\"remark plugin stack (strip imports → flatten components)\"] groups[\"group resolver (nav tree from frontmatter)\"] md[\"docs/*.md\"] site_idx[\"llms.txt · llms-full.txt sitemap · robots · manifest (website mode only)\"] search[\"search-index.json · search-content.json (website mode only)\"] agents_md[\"AGENTS.md (--bundle mode only)\"] nav[\"navigation manifest (group → page)\"] src --> fm fm --> remark fm --> groups remark --> md md --> site_idx md --> search groups --> site_idx groups --> agents_md groups --> nav ```","How it works\n\nThe mental model: one MDX source, a remark pipeline, two output modes, three audiences.\n\nHow it works\n\nTwo output modes\n\nleadtype generate has two modes that read the same source and emit different shapes Property Type Description Default Required -- -- -- -- -- Site mode default leadtype generate --out public Writes llms.txt, llms-full.txt, docs/search-index.json, docs/sitemap.xml, docs/sitemap.md, docs/robots.txt, docs/agent-readability.json, and docs/\\ .md to a public/ directory your docs website serves. This is what you wire into a Vite, Next.js, Astro, or TanStack Start build. - Optional Bundle mode leadtype generate --bundle --out packages/foo Writes AGENTS.md at the package root and docs/\\ .md beneath it, both with relative paths. Skips llms.txt, llms-full.txt, search, sitemap, robots, and Agent Readability files — those are website-only. Designed for npm tarballs that ship docs alongside the published code. - Optional","How it works\n\nThe mental model: one MDX source, a remark pipeline, two output modes, three audiences.\n\nHow it works\n\nThe artifacts\n\nProperty Type Description Default Required -- -- -- -- -- Markdown .md docs/\\public/*\"] bundle_out[\"bundle mode node_modules/<pkg>/*\"] human[\"Humans (browser)\"] http_agent[\"HTTP agents (fetch /llms.txt or Accept: text/markdown)\"] search_ui[\"Search UI AI answers\"] offline_agent[\"Coding agents (Claude Code, Codex, Cursor, Copilot, …)\"] site_out -- \"HTML render of MDX\" --> human site_out -- \"absolute URLs\" --> http_agent site_out -- \"search-index.json\" --> search_ui bundle_out -- \"version-matched AGENTS.md\" --> offline_agent ```","How it works\n\nThe mental model: one MDX source, a remark pipeline, two output modes, three audiences.\n\nHow it works\n\nVocabulary\n\nA few terms you will see throughout the docs. Property Type Description Default Required -- -- -- -- -- flatten verb Convert an interactive MDX component into a portable markdown equivalent. A \\(your source)\"] site_run[\"leadtype generate\"] bundle_run[\"leadtype generate --bundle\"] site_out[\"public/ llms.txt · llms-full.txt docs/*.md · sitemap agent-readability.json\"] bundle_out[\"packages/<name>/ AGENTS.md · docs/*.md\"] humans[\"Humans (browser)\"] http_agents[\"HTTP agents (via /llms.txt or Accept: text/markdown)\"] search[\"Search UI AI answers\"] offline_agents[\"Coding agents can read version-matched node_modules/<pkg>/AGENTS.md\"] src --> site_run src --> bundle_run site_run --> site_out bundle_run --> bundle_out site_out --> humans site_out --> http_agents site_out --> search bundle_out --> offline_agents ```","Leadtype\n\nOne MDX source. Hosted docs artifacts, package-bundled AGENTS.md, and search output from the same pipeline.\n\nLeadtype\n\nStart here\n\nQuickstart → — three steps to a working source primitive, then plug it into your framework. Or skip ahead Build a docs site compares every integration path side-by-side.","Leadtype\n\nOne MDX source. Hosted docs artifacts, package-bundled AGENTS.md, and search output from the same pipeline.\n\nLeadtype\n\nWhat leadtype produces\n\nA source primitive for your app llms.txt, sitemaps, agent metadata AGENTS.md for npm packages","Leadtype\n\nOne MDX source. Hosted docs artifacts, package-bundled AGENTS.md, and search output from the same pipeline.\n\nLeadtype\n\nNext\n\nNew here? Read the Quickstart — three steps to a working source. Want the mental model first? Read How it works for the pipeline diagram and the names of every artifact it produces. Comparing tools? See Methodology for how leadtype differs from Fumadocs, Starlight, and Mintlify.","Methodology\n\nHow leadtype differs from Fumadocs, Starlight, and Mintlify.\n\nMethodology\n\nLeadtype is a docs pipeline, not a docs website framework . It produces the artifacts a docs site needs markdown, llms.txt, search index, navigation and stays out of routing, layout, and theming.","Methodology\n\nHow leadtype differs from Fumadocs, Starlight, and Mintlify.\n\nMethodology\n\nThe short version\n\nTool What it gives you -- -- Fumadocs A React docs framework Next.js, TanStack Start, React Router, Waku . Supports llms.txt and content negotiation via framework route handlers. Starlight An Astro docs site framework. llms.txt via the starlight-llms-txt community plugin not built-in ; static client-side search via Pagefind. Mintlify A hosted docs SaaS with an optional headless Astro mode that still calls Mintlify's search and assistant APIs . Leadtype The portable content layer behind any of the above. Choose a website framework when the main job is publishing a polished docs UI quickly. Consider a hosted platform if you want managed publishing, search, analytics, and AI features and accept that service dependency. Opt for leadtype when you need to own the generated docs artifacts framework-agnostic markdown, navigation, search, llms.txt , a root llms-full.txt fallback, and optional AGENTS.md package bundles that ship inside npm tarballs.","Methodology\n\nHow leadtype differs from Fumadocs, Starlight, and Mintlify.\n\nMethodology\n\nWhat leadtype owns\n\nThe MDX tag contract — typed prop shapes for (your repo)\"] cli[\"leadtype generate --bundle --out packages/<name>\"] bundle[\"packages/<name>/ AGENTS.md docs/*.md\"] publish[\"npm publish\"] install[\"npm install <name>\"] consume[\"node_modules/<name>/ AGENTS.md → docs/*.md version-matched offline docs\"] src --> cli cli --> bundle bundle --> publish publish --> install install --> consume ```","Bundle docs into a package\n\nShip agent-readable docs inside an npm tarball — AGENTS.md at the package root plus per-topic .md files.\n\nBundle docs into a package\n\nWhy AGENTS.md, not llms.txt?\n\nllms.txt is a website convention — a file at /llms.txt with absolute URLs that an agent fetches over HTTP. Inside an npm tarball it's the wrong shape every link points at a hosted URL the agent may not be able to reach, and no major coding agent looks for node modules/ 0) { for (const { urlPath, slug } of navigation.unknown) { process.stderr.write(`error: ${urlPath} declares unknown group \"${slug}\".\\n`); } process.exit(1); } await generateAgentsMd({ srcDir: REPO_ROOT, outDir: PACKAGE_ROOT, product: docsConfig.product, groups: docsConfig.groups, }); ```","Bundle docs into a package\n\nShip agent-readable docs inside an npm tarball — AGENTS.md at the package root plus per-topic .md files.\n\nBundle docs into a package\n\nVerify before publishing\n\nnpm pack --dry-run should list AGENTS.md docs/ / .md If AGENTS.md is missing, check files in package.json . If .md files are missing, check the --include / --exclude filters.\n\n```bash npx leadtype generate --bundle --src . --out packages/my-package cd packages/my-package && npm pack --dry-run ```","Bundle docs into a package\n\nShip agent-readable docs inside an npm tarball — AGENTS.md at the package root plus per-topic .md files.\n\nBundle docs into a package\n\nTell consuming projects to use the bundle\n\nAGENTS.md inside your tarball is available at node modules/ When working with the `` library, read the bundled docs in `node_modules//AGENTS.md` first — they're version-matched to the installed package and stay accurate as the library updates. ```","Bundle docs into a package\n\nShip agent-readable docs inside an npm tarball — AGENTS.md at the package root plus per-topic .md files.\n\nBundle docs into a package\n\nWhen to use this\n\nUse this when agents should understand the package from the installed dependency itself — coding agents that don't have web access, IDE assistants, CLI tools, or air-gapped environments. Don't use this if your only goal is a public docs website. For that, see Build a docs site. You can use both — they read the same source MDX, just emit different shapes.","Bundle docs into a package\n\nShip agent-readable docs inside an npm tarball — AGENTS.md at the package root plus per-topic .md files.\n\nBundle docs into a package\n\nWhat's next\n\nCLI reference LLM files Lint in CI","Quickstart\n\nInstall leadtype, author your first MDX page, and create a source. Then plug it into whatever framework you're using.\n\nQuickstart\n\nThree steps to a working source primitive. Then plug it into your framework — Next, Fumadocs, Astro, Vite + Vue/Solid/Svelte, Nuxt, SvelteKit — using one of the recipes below.","Quickstart\n\nInstall leadtype, author your first MDX page, and create a source. Then plug it into whatever framework you're using.\n\nQuickstart\n\nInstall\n\nPackage manager Command -- -- npm npm install leadtype pnpm pnpm add leadtype yarn yarn add leadtype bun bun add leadtype","Quickstart\n\nInstall leadtype, author your first MDX page, and create a source. Then plug it into whatever framework you're using.\n\nQuickstart\n\nAuthor one page\n\nEvery page needs a title . Add group when the page should appear in generated navigation and llms.txt sections. See Frontmatter for the full content contract.\n\n```mdx title=\"content/docs/index.mdx\" --- title: \"My library\" description: \"What it does in one sentence.\" --- # My library Welcome. ```","Quickstart\n\nInstall leadtype, author your first MDX page, and create a source. Then plug it into whatever framework you're using.\n\nQuickstart\n\nCreate the source\n\nThat's the framework-neutral primitive. You can now call source.loadPage slug — resolved markdown + AST + frontmatter + TOC source.listPages — every page's slug, urlPath, and metadata source.getNavigation — grouped sidebar tree source.buildSearchIndex — static search bundle source.resolveInclude specifier — standalone include resolver\n\n```ts title=\"lib/source.ts\" import { createDocsSource } from \"leadtype\"; export const source = await createDocsSource({ contentDir: \"./content/docs\", baseUrl: \"https://example.com\", }); ```","Quickstart\n\nInstall leadtype, author your first MDX page, and create a source. Then plug it into whatever framework you're using.\n\nQuickstart\n\nPlug it into your framework\n\nPick the recipe that matches your stack. Each one shows the wiring on top of the same createDocsSource you just set up. Next App Router Fumadocs TanStack Start Astro Content Collections Vite + Vue / Solid / Svelte Nuxt SvelteKit","Quickstart\n\nInstall leadtype, author your first MDX page, and create a source. Then plug it into whatever framework you're using.\n\nQuickstart\n\nOr write static artifacts to disk\n\nSome pipelines need flat files — CDN-only deploys, static exports, agent-only services, multiple sibling apps sharing one corpus. Use the CLI That writes llms.txt , llms-full.txt , docs/ .md , search index, sitemap, and agent-readability.json to disk. See Generate static artifacts for the full flow.\n\n```sh npx leadtype generate --src . --out public --base-url https://example.com ```","Quickstart\n\nInstall leadtype, author your first MDX page, and create a source. Then plug it into whatever framework you're using.\n\nQuickstart\n\nNext steps\n\nGoal Page -- -- Compare every integration shape side-by-side Framework integration matrix Implement custom MDX tags Callout, Tabs, Steps, … leadtype/mdx Add search Add search Validate frontmatter and links in CI Validate in CI Serve markdown, sitemaps, robots, and JSON-LD for agents Optimize docs for agents Publish AGENTS.md inside an npm package Bundle docs into a package","Architecture\n\nThe core / adapter boundary — what ships where, and the rules adapters must follow.\n\nArchitecture\n\nLeadtype is split into a framework-neutral core and a small set of thin adapters that wire the core into specific host frameworks. The rules below are the contract — they tell you where new code lives, what it can import, and what it can never ship.","Architecture\n\nThe core / adapter boundary — what ships where, and the rules adapters must follow.\n\nArchitecture\n\nThe three layers\n\nCore\n\nSubpath Purpose -- -- leadtype createDocsSource , defineDocsConfig , agent-readability types. leadtype/mdx Tag types CalloutProps , TabsProps , … and createMdxSourcePlugins . leadtype/remark Remark plugins include, type-table, agent flattening . leadtype/convert MDX → markdown conversion. leadtype/llm llms.txt , llms-full.txt , sitemap, AGENTS.md, agent-readability manifest. leadtype/llm/readability Runtime helpers createAgentMarkdownResponse , content negotiation . leadtype/search Static BM25 index + edge-safe runtime. leadtype/search/ node,vercel,tanstack,cloudflare,ai,bash Host-runtime adapters for the search/answer pipeline. leadtype/lint Frontmatter, navigation, and link validation. Core has zero framework runtime dependencies . It imports no React, Vue, Svelte, Next, Nuxt, SvelteKit, Astro, or Solid code. Anything published under one of the subpaths above must stay framework-neutral.","Architecture\n\nThe core / adapter boundary — what ships where, and the rules adapters must follow.\n\nArchitecture\n\nThe three layers\n\nHost-runtime adapters leadtype/search/\n\nThe leadtype/search/ subpaths are scoped by where the code runs — Node, Vercel Edge, Cloudflare Workers, TanStack runtime, the bash tool — not by which UI framework wraps them. They cover the answer-streaming/search read path on their target runtime.","Architecture\n\nThe core / adapter boundary — what ships where, and the rules adapters must follow.\n\nArchitecture\n\nThe three layers\n\nFramework adapters\n\nSubpath Host What it exports -- -- -- leadtype/fumadocs fumadocs-core fumadocsSource — maps DocsSource to fumadocs's Source interface. leadtype/next Next.js App Router server createGenerateStaticParams , createLoadPageData , createDocsRouteHandler . No React. leadtype/next/client Next.js App Router client useLeadtypeSearch React hook, createSearchClient vanilla factory. Framework adapters are thin. They wire the core primitives into the host's native conventions and declare their host package as an optional peer dependency . A consumer that doesn't use fumadocs never installs fumadocs-core; a consumer that doesn't use Next never installs React.","Architecture\n\nThe core / adapter boundary — what ships where, and the rules adapters must follow.\n\nArchitecture\n\nPlanned adapter shapes\n\nThe boundary applies to every framework, including adapters that are not exported yet Future subpath Native host shape -- -- leadtype/nuxt Nitro route helpers, build-time source wiring, and Vue composables. leadtype/sveltekit +page.server.ts , +server.ts , entries , and Svelte stores/runes. leadtype/astro getStaticPaths , endpoint helpers, Content Collections interop, and island-friendly client helpers. leadtype/tanstack-start TanStack Router route helpers, static route manifests, server functions, and React state helpers. leadtype/search/vue Vue composables over generated search-index.json and search-content.json . leadtype/search/svelte Svelte stores/runes over generated search artifacts. These are future public surfaces, not hidden exports. Until they ship, use the recipes in Use the source primitive, which wire the same core primitives into each framework directly. leadtype/tanstack-start is intentionally separate from leadtype/search/tanstack the former would adapt docs routing and source data to TanStack Start, while the latter is the existing TanStack AI answer-streaming runtime.","Architecture\n\nThe core / adapter boundary — what ships where, and the rules adapters must follow.\n\nArchitecture\n\nRules\n\nFlat naming\n\nAdapters live under leadtype/ [options] ```","CLI\n\nleadtype generate, leadtype sync, and leadtype lint — flags, exit codes, and JSON output.\n\nCLI\n\ngenerate\n\nConvert MDX, then either produce website artifacts default or a package bundle --bundle . Flag Default Description -- -- -- --src ; summary: { filesScanned: number; errors: number; warnings: number; }; }; ```","Lint rules\n\nSchema, link, and navigation checks. CLI and library API.\n\nLint rules\n\nDefault schemas\n\nDocs frontmatter\n\nField Required Type -- -- -- title Yes non-empty string description No string icon No string deprecated No boolean deprecatedReason No string experimental No boolean canary No boolean new No boolean draft No boolean tags No string array group No string or string array availableIn No array of framework, url?, title? full No boolean lastModified and lastAuthor are produced by the converter when --enrich-git is set. Don't author them.","Lint rules\n\nSchema, link, and navigation checks. CLI and library API.\n\nLint rules\n\nDefault schemas\n\nChangelog frontmatter\n\nField Required Type -- -- -- title Yes non-empty string version Yes SemVer string date Yes ISO-8601 or parseable date description No string icon No string type No release , improvement , retired , or deprecation tags No string array canary No boolean authors No string or string array draft No boolean","Lint rules\n\nSchema, link, and navigation checks. CLI and library API.\n\nLint rules\n\nDefault schemas\n\nmeta.json\n\nField Required Type -- -- -- pages Yes string array title No non-empty string root No boolean icon No string defaultOpen No boolean nav.sidebar No section or combined nav.label No string nav.mode No string","Lint rules\n\nSchema, link, and navigation checks. CLI and library API.\n\nLint rules\n\nCustom schemas\n\nPass a Valibot schema to extend or replace the defaults Once you provide a custom schema, unknown-field warnings apply to that schema. Add --error-unknown in CI to keep your contract strict.\n\n```ts import * as v from \"valibot\"; import { lintDocs } from \"leadtype/lint\"; const customFrontmatter = v.object({ title: v.pipe(v.string(), v.minLength(1)), audience: v.picklist([\"beginner\", \"advanced\"]), }); await lintDocs({ srcDir: \"docs\", schemas: { frontmatter: customFrontmatter }, }); ```","Lint rules\n\nSchema, link, and navigation checks. CLI and library API.\n\nLint rules\n\nPractical guidance\n\nRun lint before leadtype generate so content errors fail fast. Use --format github in GitHub Actions and --format json in any other CI. Treat unresolved-placeholder as a content bug first — usually a missing entry in availableIn or a stale URL template. After a docs move, lint and run meta.json updates together; they drift at the same time. For wiring lint into pipelines, see Validate in CI.","LLM files\n\nGenerate llms.txt for hosted websites and AGENTS.md for npm-bundled offline reading.\n\nLLM files\n\nThe leadtype/llm entry point produces four flavors of agent-facing output, all derived from the same docs source generateLlmsTxt — for hosted websites. Emits the /llms.txt convention with root-relative markdown mirror links. generateLLMFullContextFiles — root /llms-full.txt fallback containing all generated markdown docs. Pairs with generateLlmsTxt . generateAgentReadabilityArtifacts — docs-scoped sitemap.xml , sitemap.md , robots.txt , and JSON manifest data that a host app can merge into site-level files. generateAgentsMd — for npm-bundled docs. Emits an AGENTS.md index with relative ./docs/ A library that does one thing well. - Helper that handles the boring parts. - Type-safe by default. - Works in any runtime. ## Best Starting Points - [Documentation](/docs/index.md) - [Quickstart](/docs/quickstart.md) ## Get Started Five-minute happy path and the mental model. - [Quickstart](/docs/quickstart.md): Install and run the pipeline. - [How it works](/docs/how-it-works.md): The mental model. ## Reference CLI flags and conversion APIs. - [CLI](/docs/reference/cli.md): Every flag. ```","LLM files\n\nGenerate llms.txt for hosted websites and AGENTS.md for npm-bundled offline reading.\n\nLLM files\n\nTypical sequence\n\ngenerateLLMFullContextFiles and generateAgentReadabilityArtifacts read from A library that does one thing well. These docs ship inside the package so coding agents can read them offline. Open the topic file you need from the list below — paths are relative to this file. ## Get Started - [Quickstart](./docs/quickstart.md): Install and run the pipeline. - [How it works](./docs/how-it-works.md): The mental model. ## Reference - [CLI](./docs/reference/cli.md): Every flag. ```","LLM files\n\nGenerate llms.txt for hosted websites and AGENTS.md for npm-bundled offline reading.\n\nLLM files\n\nresolveDocsNavigation\n\nSame group-resolution logic the LLM files use, but returns the navigation manifest as a plain object — useful for driving a sidebar UI Write the result to src/generated/docs-nav.json and import it from your sidebar component Now your sidebar can import a static manifest with the same group tree the LLM files use.\n\n```ts const navigation = await resolveDocsNavigation({ srcDir: \".\", baseUrl: \"https://leadtype.dev\", groups: docsConfig.groups, }); if (navigation.unknown.length > 0) { for (const { urlPath, slug } of navigation.unknown) { process.stderr.write(`error: ${urlPath} declares unknown group \"${slug}\".\\n`); } process.exit(1); } ``` ```ts import { mkdir, writeFile } from \"node:fs/promises\"; await mkdir(\"src/generated\", { recursive: true }); await writeFile( \"src/generated/docs-nav.json\", `${JSON.stringify(navigation, null, 2)}\\n` ); ```","LLM files\n\nGenerate llms.txt for hosted websites and AGENTS.md for npm-bundled offline reading.\n\nLLM files\n\nTables of contents\n\nFor the heading slug contract and renderer wiring, see Use the source primitive. This section covers the build-time APIs only. resolveDocsNavigation includes a toc array on every page by default. The default range is h2 – h3 , which keeps page-level h1 titles out of sidebars. If you only need TOC data and not the full group tree, call resolveDocsTableOfContents For custom pipelines, extractDocsTableOfContents accepts a markdown or MDX string plus page URL metadata and returns plain JSON. It ignores frontmatter and fenced code blocks, and it uses the same slugifyDocsHeading helper that rendered headings must use to keep id attributes in sync.\n\n```ts const navigation = await resolveDocsNavigation({ srcDir: \".\", baseUrl: \"https://leadtype.dev\", groups: docsConfig.groups, toc: { minLevel: 2, maxLevel: 4 }, }); ``` ```ts const pages = await resolveDocsTableOfContents({ srcDir: \".\", baseUrl: \"https://leadtype.dev\", }); ```","LLM files\n\nGenerate llms.txt for hosted websites and AGENTS.md for npm-bundled offline reading.\n\nLLM files\n\nGroup design\n\nThe groups you pass to these APIs come from docs.config.ts . Two principles Use groups for routing, not sharding. Groups organize llms.txt , navigation, search metadata, and AGENTS.md . The root llms-full.txt remains the broad fallback. Write group descriptions for routing, not flavor text. Agents read those descriptions to decide which pages to load. \"How to install and run\" beats \"Welcome to our guides!\"","LLM files\n\nGenerate llms.txt for hosted websites and AGENTS.md for npm-bundled offline reading.\n\nLLM files\n\nBase URL precedence\n\nPass baseUrl explicitly, or use environment variables for layered fallback The package-specific LEADTYPE AGENT BASE URL lets each package override an org-wide default. BASE URL covers most CI/deployment platforms, and a final hardcoded fallback keeps local builds working without env setup.\n\n```ts const baseUrl = process.env.LEADTYPE_AGENT_BASE_URL || process.env.BASE_URL || process.env.PORTLESS_URL || \"https://leadtype.dev\"; ```","leadtype/mdx\n\nTag type contracts and the build-time source preset for consumers rendering MDX themselves.\n\nleadtype/mdx\n\nThe leadtype/mdx subpath is the consumer-facing MDX surface — everything you need to compile leadtype-authored MDX in your own renderer fumadocs, Next App Router, Vite + @mdx-js, Astro content collections . It exports three things 1. Tag type contracts — typed prop shapes for every custom MDX tag. 2. createMdxSourcePlugins / mdxSourcePlugins — a remark preset that performs build-time resolution only expand includes, resolve & HTMLAttributes & { children?: ReactNode }; export function Callout({ variant, title, children, ...rest }: ReactCalloutProps) { return ( ); } ```","leadtype/mdx\n\nTag type contracts and the build-time source preset for consumers rendering MDX themselves.\n\nleadtype/mdx\n\nFramework-neutral by design\n\nVue\n\n```vue ```","leadtype/mdx\n\nTag type contracts and the build-time source preset for consumers rendering MDX themselves.\n\nleadtype/mdx\n\nFramework-neutral by design\n\nSvelte\n\n```svelte ```","leadtype/mdx\n\nTag type contracts and the build-time source preset for consumers rendering MDX themselves.\n\nleadtype/mdx\n\nFramework-neutral by design\n\nSolid\n\n```tsx import type { CalloutProps } from \"leadtype/mdx\"; import type { JSX } from \"solid-js\"; type SolidCalloutProps = Omit & { children?: JSX.Element; }; export function Callout(props: SolidCalloutProps) { return ( ); } ```","leadtype/mdx\n\nTag type contracts and the build-time source preset for consumers rendering MDX themselves.\n\nleadtype/mdx\n\nFramework-neutral by design\n\nAstro\n\n```astro --- import type { CalloutProps } from \"leadtype/mdx\"; type Props = Omit; const { variant = \"info\", title } = Astro.props as Props; --- ```","leadtype/mdx\n\nTag type contracts and the build-time source preset for consumers rendering MDX themselves.\n\nleadtype/mdx\n\nFramework-neutral by design\n\nFull type inventory\n\nEvery prop name is part of the 1.0 contract — bumping a shape is a breaking change. New optional props are minor.\n\n```ts import type { // Layout AudienceProps, AudienceTarget, SectionProps, DetailsProps, // Callouts CalloutProps, CalloutVariant, CalloutTypeAlias, // Navigation / structure TabsProps, TabProps, StepsProps, StepProps, AccordionProps, AccordionItemProps, // Cards / topics CardsProps, CardProps, CardVariant, TopicSwitcherProps, TopicSwitcherItem, // File tree FileTreeProps, FolderProps, FileProps, // Code / commands CommandTabsProps, CommandMode, PackageManager, ExampleProps, ExampleSourceFile, PromptProps, // Type tables TypeTableProps, TypeTableProperty, ExtractedTypeTableProps, // Diagrams MermaidProps, } from \"leadtype/mdx\"; ```","leadtype/mdx\n\nTag type contracts and the build-time source preset for consumers rendering MDX themselves.\n\nleadtype/mdx\n\nFramework-neutral by design\n\nBuild-time only {title ?
); } ```","leadtype/mdx\n\nTag type contracts and the build-time source preset for consumers rendering MDX themselves.\n\nleadtype/mdx\n\nInclude resolution\n\nresolveInclude reads + classifies an include target without going through remark — useful when loading a partial outside of the bundler pipeline parseIncludeSpecifier specifier and extractMdxSection root, id are exposed for callers that want to compose their own pipeline.\n\n```ts import { resolveInclude } from \"leadtype/mdx\"; const result = await resolveInclude(\"./shared/install.mdx#bun\", { fromDir: process.cwd(), }); if (result.kind === \"markdown\") { console.log(result.content); // frontmatter-stripped body console.log(result.section); // \"bun\" } else { console.log(result.lang, result.content); // code-block form } ```","leadtype/mdx\n\nTag type contracts and the build-time source preset for consumers rendering MDX themselves.\n\nleadtype/mdx\n\nRe-exported path helpers\n\nRouting primitives that previously lived under leadtype/internal are re-exported for consumer use\n\n```ts import { type DocsPathMount, normalizeBaseUrl, normalizeDocsPath, normalizeUrlPrefix, stripDocsExtension, toDocsUrlPath, } from \"leadtype/mdx\"; ```","Remark plugins\n\nThe default plugin stack that flattens MDX components into markdown.\n\nRemark plugins\n\nThe remark stack is what turns interactive MDX into agent-readable markdown. Imports get stripped first, placeholders get resolved, then each named component is flattened into a markdown equivalent. Order matters.\n\n```ts import { defaultRemarkPlugins, remarkInclude, remarkTypeTableToMarkdown, } from \"leadtype/remark\"; ```","Remark plugins\n\nThe default plugin stack that flattens MDX components into markdown.\n\nRemark plugins\n\nThe default stack\n\ndefaultRemarkPlugins runs the stack in this order 1. remarkRemoveImports — strip MDX import and export statements. 2. remarkRemoveJsxComments — strip / ... / JSX comments. 3. remarkResolveDocPlaceholders — replace framework and similar placeholders in URLs. 4. remarkAudienceToMarkdown — include ri --> rj --> rd --> rau --> rs --> rc --> rcd --> rdt --> rm --> rct --> rst --> rt --> rtt --> ra --> rts --> rft --> rp --> re --> out ```","Remark plugins\n\nThe default plugin stack that flattens MDX components into markdown.\n\nRemark plugins\n\nThe default stack\n\n./shared/auth.mdx ``` ```mdx ``` ```mdx ``` ```mdx This content can be reused in multiple pages. ``` ```ts remarkPlugins: [ [remarkInclude, [\"docs\", \"content\"]], ...defaultRemarkPlugins, ]; ```","Remark plugins\n\nThe default plugin stack that flattens MDX components into markdown.\n\nRemark plugins\n\nOptional plugins\n\nremarkTypeTableToMarkdown with basePath\n\n p !== remarkTypeTableToMarkdown), [ remarkTypeTableToMarkdown, { basePath: process.cwd(), strict: true }, ], ]; ``` ```mdx ```","Remark plugins\n\nThe default plugin stack that flattens MDX components into markdown.\n\nRemark plugins\n\nPlugin selection rules\n\nUse defaultRemarkPlugins for any agent-facing or LLM output. Add remarkInclude when docs are composed from shared fragments. Use individual plugins only when you intentionally want to omit a flattener e.g. you don't use /docs/search-index.json /docs/search-content.json ``` ```ts await generateDocsSearchFiles({ outDir: \"public\", baseUrl: \"https://leadtype.dev\", mounts: [ { pathPrefix: \"\", urlPrefix: \"/docs\" }, { pathPrefix: \"changelog\", urlPrefix: \"/changelog\" }, ], }); ```","Search\n\nStatic search index, runtime helpers, and source-grounded answer streaming.\n\nSearch\n\nRuntime search\n\nThe runtime is edge-safe — no Node APIs, works on Vercel, Cloudflare, and anywhere else Results include heading paths, hash URLs, and snippets ready for a search UI. Search is still local and dependency-free, but it is not exact-token only. Query terms expand through lightweight stemming, prefix matches, typo-tolerant fallbacks, and a small built-in synonym map. Exact matches keep the highest weight so API names and config keys stay precise. Pass synonyms when your docs use product-specific vocabulary\n\n```ts import { searchDocs, type DocsSearchIndex, type DocsSearchContentStore, } from \"leadtype/search\"; import indexJson from \"../public/docs/search-index.json\"; import contentJson from \"../public/docs/search-content.json\"; const results = searchDocs( indexJson as DocsSearchIndex, \"tabs install\", { content: contentJson as DocsSearchContentStore } ); ``` ```ts const results = searchDocs(index, \"starter\", { content, synonyms: { starter: [\"quickstart\", \"getting started\"], }, }); ```","Search\n\nStatic search index, runtime helpers, and source-grounded answer streaming.\n\nSearch\n\nReading docs at runtime\n\nThe same index doubles as a virtual filesystem. Three readers, picked by what you have Use readDocsContentFile when you need the entire page for context links . Use readDocsContentChunk when a search result already named the right heading.\n\n```ts import { listDocsContentFiles, readDocsContentFile, readDocsContentChunk, } from \"leadtype/search\"; const allFiles = listDocsContentFiles(index); const wholePage = readDocsContentFile(index, \"guides/quickstart\", content); const oneChunk = readDocsContentChunk(index, \"chunk-0\", content); ```","Search\n\nStatic search index, runtime helpers, and source-grounded answer streaming.\n\nSearch\n\nSource-grounded answers\n\ncreateAnswerContext turns a query plus retrieved chunks into a system and prompt you pass to any model The system message instructs the model to answer only from the retrieved context, cite sources with 1 -style references, and say so when the context is insufficient.\n\n```ts import { createAnswerContext } from \"leadtype/search\"; const context = createAnswerContext(index, \"how do I run lint?\", { content, productName: \"My Library\", }); // → { system, prompt, sources } ```","Search\n\nStatic search index, runtime helpers, and source-grounded answer streaming.\n\nSearch\n\nStreaming via provider entry points\n\nThree thin wrappers around createAnswerContext that stream a Response and surface sources separately. Use one matching your runtime response is a plain text Response. sources is metadata for citation links — display it separately, don't embed it in the streamed answer. For TanStack, pass an explicit adapter . For Cloudflare, build one with createCloudflareDocsAdapter provider, model, options binding env.AI.gateway \"docs\" .\n\n```ts import { streamDocsAnswer } from \"leadtype/search/vercel\"; // Vercel AI SDK / AI Gateway import { streamDocsAnswer } from \"leadtype/search/tanstack\"; // TanStack AI import { streamDocsAnswer } from \"leadtype/search/cloudflare\"; // Cloudflare AI Gateway / Workers AI ``` ```ts const { response, sources } = streamDocsAnswer({ index, content, query, model: \"openai/gpt-5.5\", productName: \"My Library\", }); ```","Search\n\nStatic search index, runtime helpers, and source-grounded answer streaming.\n\nSearch\n\nBash tool adapters\n\nWhen you want an agent to explore docs with shell commands instead of receiving pre-selected chunks The adapter exposes a read-only virtual /docs filesystem with ls , cat , find , grep , and rg . Network commands, code execution, and writes are disabled. Use createDocsBashTool for Vercel AI SDK tool sets and createDocsBashTools for TanStack-compatible tools over the same filesystem.\n\n```ts import { createDocsBashTool, createDocsBashTools } from \"leadtype/search/bash\"; const { tools, instructions } = await createDocsBashTool(index, content); ```","Search\n\nStatic search index, runtime helpers, and source-grounded answer streaming.\n\nSearch\n\nAbuse guards\n\nReusable utilities for the request path Helper Purpose -- -- validateDocsQuery Trim and cap query text. readJsonWithLimit Reject oversized JSON bodies before parse. getClientIdentifier Read common proxy IP headers. createMemoryRateLimiter Implements RateLimiter for demos. The in-memory limiter is fine for demos. Production apps should adapt the RateLimiter interface to a shared store — Redis, Vercel KV, Cloudflare KV, or Durable Objects.","Search\n\nStatic search index, runtime helpers, and source-grounded answer streaming.\n\nSearch\n\nWhen to add embeddings\n\nStart with the local index. It is static, cheap, edge-safe, and fast for exact API names, config keys, error messages, and paths. Add embeddings only when Users search with vocabulary that doesn't match the docs e.g. \"make it faster\" matching a \"performance optimization\" page . Your docs grow past tens of thousands of chunks and the cold-start memory hit becomes noticeable. Even then, keep the lexical index for exact matches and layer embeddings on top — they're complementary, not replacements.","createDocsSource\n\nFramework-neutral docs source primitive — navigation, page loader, search index, and include resolver.\n\ncreateDocsSource\n\ncreateDocsSource is the framework-neutral entry point for any consumer that wants to render leadtype-authored MDX in their own renderer. It composes leadtype's primitives resolveDocsNavigation , convertMdxFile , createDocsSearchIndex , resolveInclude into a single source object. For fumadocs specifically, use the thin leadtype/fumadocs adapter that wraps this primitive.\n\n```ts import { createDocsSource } from \"leadtype\"; const source = await createDocsSource({ contentDir: \"./content/docs\", baseUrl: \"https://example.com\", groups: [ { slug: \"get-started\", title: \"Get Started\" }, { slug: \"guides\", title: \"Guides\" }, ], }); const page = await source.loadPage(\"quickstart\"); const navigation = await source.getNavigation(); const search = await source.buildSearchIndex(); ```","createDocsSource\n\nFramework-neutral docs source primitive — navigation, page loader, search index, and include resolver.\n\ncreateDocsSource\n\nConfiguration\n\nOption Type Notes -- -- -- contentDir string Required. Directory containing source .md / .mdx files. baseUrl string Used for absolute URLs in TOC and search index. groups DocsGroup Doc groups for navigation. Empty groups = all pages ungrouped. mounts DocsPathMount Multi-mount routing advanced . remarkPlugins PluggableList Defaults to Leadtype's source preset. Pass to skip transforms. typeTableBasePath string Base directory for ; markdown: string; // resolved, plugin-transformed markdown ast: Root; // mdast Root after the source preset — render this for live MDX toc: DocsTableOfContentsItem[]; } ```","createDocsSource\n\nFramework-neutral docs source primitive — navigation, page loader, search index, and include resolver.\n\ncreateDocsSource\n\nSource methods\n\nbuildSearchIndex Promise;
+}) {
+ const page = await loadPageData((await params).slug);
+ if (!page) {
+ notFound();
+ }
+
+ return (
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/next-example/app/layout.tsx b/apps/next-example/app/layout.tsx
new file mode 100644
index 0000000..53057a3
--- /dev/null
+++ b/apps/next-example/app/layout.tsx
@@ -0,0 +1,10 @@
+import type { ReactNode } from "react";
+import "./styles.css";
+
+export default function RootLayout({ children }: { children: ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/apps/next-example/app/page.tsx b/apps/next-example/app/page.tsx
new file mode 100644
index 0000000..dc183f3
--- /dev/null
+++ b/apps/next-example/app/page.tsx
@@ -0,0 +1,17 @@
+import Link from "next/link";
+
+export default function HomePage() {
+ return (
+
+
Next.js App Router dogfood
+
Leadtype framework example
+
+ This app renders shared Leadtype docs, serves generated agent artifacts,
+ and searches static JSON through leadtype/search/react.
+