Skip to content

Commit 00420c8

Browse files
committed
Refactor LLMs generation and add static root index
Switch from auto-generating a monolithic llms.txt/llms-full.txt to a hand-curated static/llms.txt and per-section indexes. Wrap docusaurus-plugin-llms with an llms-html-injector wrapper so generation runs before post-processing (normalizing .md layout, rewriting llms*.txt URLs, and injecting <link rel="alternate">). Update docusaurus.config.js to disable the classic docs slot, disable root/full LLM generators, and adjust path transformations. Update scripts/verify-llms-output.js to mirror the new behavior (customLLMFiles list, disabled root generators, post-process step, and copy static/llms.txt into the verify outDir). Add static/llms.txt, add vercel redirects for the removed whats-new page, remove docs/whats-new.md, and tweak a navbar CSS comment. Export postProcessLlmsOutput for reuse by the verify script.
1 parent 4621a81 commit 00420c8

7 files changed

Lines changed: 294 additions & 82 deletions

File tree

docs/whats-new.md

Lines changed: 0 additions & 3 deletions
This file was deleted.

docusaurus.config.js

Lines changed: 33 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -162,16 +162,13 @@ const config = {
162162
[
163163
'classic',
164164
{
165-
docs: {
166-
id: 'docs',
167-
path: 'docs',
168-
routeBasePath: '/',
169-
editUrl: 'https://github.com/MetaMask/metamask-docs/edit/main/',
170-
sidebarPath: false,
171-
breadcrumbs: false,
172-
remarkPlugins,
173-
rehypePlugins,
174-
},
165+
// The classic preset's `docs` plugin is disabled. It previously
166+
// rendered a single orphan page (`docs/whats-new.md`) at `/whats-new/`
167+
// that pointed offsite for release notes. The page had no incoming
168+
// internal links and is redirected to `/` in `vercel.json`. Per-product
169+
// documentation is served by dedicated `plugin-content-docs` instances
170+
// configured below in `plugins`, not by this preset slot.
171+
docs: false,
175172
pages: {
176173
path: 'src/pages',
177174
routeBasePath: '/',
@@ -323,39 +320,32 @@ const config = {
323320
containerId: 'GTM-5FGPLC2Q',
324321
},
325322
],
323+
// The llms-html-injector plugin wraps docusaurus-plugin-llms so that
324+
// generation (per-section `llms-<product>.txt` files and per-page `.md`
325+
// files) runs sequentially before our post-processing (path normalization,
326+
// index URL rewrites, and `<link rel="alternate" type="text/markdown">`
327+
// injection). Wrapping is required because Docusaurus 3.x executes
328+
// `postBuild` hooks concurrently via `Promise.all`, so registering both
329+
// plugins separately would let the injector race against the generator.
326330
[
327-
'docusaurus-plugin-llms',
331+
'./src/plugins/llms-html-injector',
328332
{
329333
// Set docsDir to site root to collect from all directories
330334
docsDir: '.',
331-
// Generate a comprehensive root llms.txt + llms-full.txt covering every page
332-
// (Agent Score: llms-txt-coverage check). The per-section files below remain
333-
// useful as scoped indexes for AI tools.
334-
generateLLMsTxt: true,
335-
generateLLMsFullTxt: true,
336-
// Emit an individual .md file next to every doc page so .md URLs return 200
337-
// (Agent Score: markdown-url-support and llms-txt-directive-md checks). The
338-
// companion llms-html-injector plugin (registered below) normalizes these
335+
// The root llms.txt is hand-curated at static/llms.txt to stay under
336+
// the AFDocs 50,000-char `llms-txt-size` threshold. The aggregate
337+
// walker descends one level into the per-section indexes listed below,
338+
// so coverage is preserved without dumping every page into a flat
339+
// index. Likewise, a monolithic llms-full.txt would exceed every
340+
// agent's context window (and crashes browser tabs at multi-MB
341+
// sizes); the per-section *-full.txt files cover the bulk content.
342+
generateLLMsTxt: false,
343+
generateLLMsFullTxt: false,
344+
// Emit an individual .md file next to every doc page so .md URLs
345+
// return 200 (Agent Score: markdown-url-support and
346+
// llms-txt-directive-md checks). The wrapper below normalizes these
339347
// files to URL-aligned paths and injects <link rel="alternate"> tags.
340348
generateMarkdownFiles: true,
341-
// Custom framing for the root index, preserving the prose previously
342-
// hand-maintained in static/llms.txt.
343-
rootContent: `> MetaMask is the leading self-custodial cryptocurrency wallet and Web3 gateway, enabling developers to build dapps that connect to the MetaMask browser extension and mobile app across EVM and Solana ecosystems.
344-
345-
This file follows the [llmstxt.org](https://llmstxt.org/) specification. Section-scoped indexes are also available:
346-
347-
- [MetaMask Connect](https://docs.metamask.io/llms-metamask-connect.txt) ([full](https://docs.metamask.io/llms-metamask-connect-full.txt))
348-
- [Embedded Wallets](https://docs.metamask.io/llms-embedded-wallets.txt) ([full](https://docs.metamask.io/llms-embedded-wallets-full.txt))
349-
- [Smart Accounts Kit](https://docs.metamask.io/llms-smart-accounts-kit.txt) ([full](https://docs.metamask.io/llms-smart-accounts-kit-full.txt))
350-
- [Services](https://docs.metamask.io/llms-services.txt) ([full](https://docs.metamask.io/llms-services-full.txt))
351-
- [Developer dashboard](https://docs.metamask.io/llms-dashboard.txt) ([full](https://docs.metamask.io/llms-dashboard-full.txt))
352-
- [Snaps](https://docs.metamask.io/llms-snaps.txt) ([full](https://docs.metamask.io/llms-snaps-full.txt))
353-
- [Tutorials](https://docs.metamask.io/llms-tutorials.txt) ([full](https://docs.metamask.io/llms-tutorials-full.txt))
354-
`,
355-
fullRootContent: `> MetaMask is the leading self-custodial cryptocurrency wallet and Web3 gateway, enabling developers to build dapps that connect to the MetaMask browser extension and mobile app across EVM and Solana ecosystems.
356-
357-
This file contains the complete MetaMask developer documentation in a single markdown document, following the [llmstxt.org](https://llmstxt.org/) specification.
358-
`,
359349
// Ignore common non-doc directories
360350
// Note: src/pages/** is not ignored so tutorials can be collected
361351
// Each customLLMFiles entry filters by includePatterns, so only matching files are included
@@ -397,10 +387,13 @@ This file contains the complete MetaMask developer documentation in a single mar
397387
],
398388
excludeImports: true,
399389
removeDuplicateHeadings: true,
400-
// Path transformation to fix URL construction
401-
// Since docsDir is '.', we need to remove 'docs/' prefix and handle src/pages paths
390+
// Strip `src/pages` from generated URLs so a file at
391+
// `src/pages/quickstart/foo.md` resolves to `/quickstart/foo/` to
392+
// match the public route. `docsDir` is `.` (the site root) so any
393+
// future top-level prefixes that should be hidden from URLs would
394+
// be added here.
402395
pathTransformation: {
403-
ignorePaths: ['docs', 'src/pages'],
396+
ignorePaths: ['src/pages'],
404397
},
405398
// Generate separate files for each section
406399
customLLMFiles: [
@@ -519,11 +512,6 @@ This file contains the complete MetaMask developer documentation in a single mar
519512
],
520513
},
521514
],
522-
// Runs in postBuild after docusaurus-plugin-llms generates files. Normalizes
523-
// .md files to URL-aligned paths, rewrites llms*.txt URLs to match, and
524-
// injects <link rel="alternate" type="text/markdown"> into every doc page's
525-
// <head> (Agent Score: llms-txt-directive-html / llms-txt-directive-md).
526-
'./src/plugins/llms-html-injector',
527515
],
528516
clientModules: [require.resolve('./src/client/scroll-fix.js')],
529517
themeConfig:

scripts/verify-llms-output.js

Lines changed: 165 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,136 @@
33
* Dev-only sanity check that invokes docusaurus-plugin-llms against the current
44
* docs source tree, using the same options we configured in docusaurus.config.js.
55
*
6-
* This lets us verify the new generateMarkdownFiles output and llms.txt coverage
7-
* without doing a full `docusaurus build` (which is currently blocked by an
8-
* unrelated swizzled-Tabs incompatibility on this machine).
6+
* After the move to a hand-curated root `static/llms.txt`, the standard
7+
* llms.txt / llms-full.txt generators are disabled in production. This script
8+
* mirrors that: both flags are off, customLLMFiles drives every per-section
9+
* file, and the static root is copied into the verify outDir so the post-
10+
* injector summary reflects what end users actually see at /llms.txt.
911
*
1012
* Usage: node scripts/verify-llms-output.js [outDir]
1113
*/
1214

1315
const path = require('path')
1416
const fs = require('fs/promises')
1517

18+
// Keep this list in sync with the customLLMFiles array in docusaurus.config.js.
19+
// Section indexes (no fullContent) stay under the AFDocs 50KB recommendation
20+
// for individual indexes; -full.txt files contain the bulk content per product.
21+
const CUSTOM_LLM_FILES = [
22+
{
23+
filename: 'llms-embedded-wallets.txt',
24+
includePatterns: ['embedded-wallets/**/*.{md,mdx}'],
25+
fullContent: false,
26+
title: 'MetaMask Embedded Wallets documentation',
27+
description: 'Documentation links for MetaMask Embedded Wallets',
28+
},
29+
{
30+
filename: 'llms-embedded-wallets-full.txt',
31+
includePatterns: ['embedded-wallets/**/*.{md,mdx}'],
32+
fullContent: true,
33+
title: 'MetaMask Embedded Wallets documentation',
34+
description: 'Complete documentation for MetaMask Embedded Wallets',
35+
},
36+
{
37+
filename: 'llms-metamask-connect.txt',
38+
includePatterns: ['metamask-connect/**/*.{md,mdx}'],
39+
fullContent: false,
40+
title: 'MetaMask Connect documentation',
41+
description: 'Documentation links for MetaMask Connect',
42+
},
43+
{
44+
filename: 'llms-metamask-connect-full.txt',
45+
includePatterns: ['metamask-connect/**/*.{md,mdx}'],
46+
fullContent: true,
47+
title: 'MetaMask Connect documentation',
48+
description: 'Complete documentation for MetaMask Connect',
49+
},
50+
{
51+
filename: 'llms-smart-accounts-kit.txt',
52+
includePatterns: ['smart-accounts-kit/**/*.{md,mdx}'],
53+
fullContent: false,
54+
title: 'MetaMask Smart Accounts Kit documentation',
55+
description: 'Documentation links for MetaMask Smart Accounts Kit',
56+
},
57+
{
58+
filename: 'llms-smart-accounts-kit-full.txt',
59+
includePatterns: ['smart-accounts-kit/**/*.{md,mdx}'],
60+
fullContent: true,
61+
title: 'MetaMask Smart Accounts Kit documentation',
62+
description: 'Complete documentation for MetaMask Smart Accounts Kit',
63+
},
64+
{
65+
filename: 'llms-snaps.txt',
66+
includePatterns: ['snaps/**/*.{md,mdx}'],
67+
fullContent: false,
68+
title: 'Snaps documentation',
69+
description: 'Documentation links for Snaps',
70+
},
71+
{
72+
filename: 'llms-snaps-full.txt',
73+
includePatterns: ['snaps/**/*.{md,mdx}'],
74+
fullContent: true,
75+
title: 'Snaps documentation',
76+
description: 'Complete documentation for Snaps',
77+
},
78+
{
79+
filename: 'llms-wallet.txt',
80+
includePatterns: ['wallet/**/*.{md,mdx}'],
81+
fullContent: false,
82+
title: 'Wallet API documentation',
83+
description: 'Documentation links for Wallet API',
84+
},
85+
{
86+
filename: 'llms-wallet-full.txt',
87+
includePatterns: ['wallet/**/*.{md,mdx}'],
88+
fullContent: true,
89+
title: 'Wallet API documentation',
90+
description: 'Complete documentation for Wallet API',
91+
},
92+
{
93+
filename: 'llms-tutorials.txt',
94+
includePatterns: ['src/pages/tutorials/**/*.{md,mdx}'],
95+
fullContent: false,
96+
title: 'Tutorials',
97+
description: 'Documentation links for MetaMask tutorials',
98+
},
99+
{
100+
filename: 'llms-tutorials-full.txt',
101+
includePatterns: ['src/pages/tutorials/**/*.{md,mdx}'],
102+
fullContent: true,
103+
title: 'Tutorials',
104+
description: 'Complete documentation for MetaMask tutorials',
105+
},
106+
{
107+
filename: 'llms-dashboard.txt',
108+
includePatterns: ['developer-tools/dashboard/**/*.{md,mdx}'],
109+
fullContent: false,
110+
title: 'Developer dashboard documentation',
111+
description: 'Documentation links for MetaMask Developer dashboard',
112+
},
113+
{
114+
filename: 'llms-dashboard-full.txt',
115+
includePatterns: ['developer-tools/dashboard/**/*.{md,mdx}'],
116+
fullContent: true,
117+
title: 'Developer dashboard documentation',
118+
description: 'Complete documentation for MetaMask Developer dashboard',
119+
},
120+
{
121+
filename: 'llms-services.txt',
122+
includePatterns: ['services/**/*.md'],
123+
fullContent: false,
124+
title: 'Services documentation',
125+
description: 'Documentation links for MetaMask services',
126+
},
127+
{
128+
filename: 'llms-services-full.txt',
129+
includePatterns: ['services/**/*.md'],
130+
fullContent: true,
131+
title: 'Services documentation',
132+
description: 'Complete documentation for MetaMask services',
133+
},
134+
]
135+
16136
async function main() {
17137
const siteDir = path.resolve(__dirname, '..')
18138
const outDir = path.resolve(siteDir, process.argv[2] || '.llms-verify')
@@ -26,12 +146,12 @@ async function main() {
26146

27147
const options = {
28148
docsDir: '.',
29-
generateLLMsTxt: true,
30-
generateLLMsFullTxt: true,
149+
generateLLMsTxt: false,
150+
generateLLMsFullTxt: false,
31151
generateMarkdownFiles: true,
32152
excludeImports: true,
33153
removeDuplicateHeadings: true,
34-
pathTransformation: { ignorePaths: ['docs', 'src/pages'] },
154+
pathTransformation: { ignorePaths: ['src/pages'] },
35155
ignoreFiles: [
36156
'node_modules/**',
37157
'build/**',
@@ -66,7 +186,7 @@ async function main() {
66186
'LICENSE*',
67187
'gator_versioned_docs/**',
68188
],
69-
customLLMFiles: [],
189+
customLLMFiles: CUSTOM_LLM_FILES,
70190
}
71191

72192
const context = {
@@ -84,17 +204,38 @@ async function main() {
84204
console.log(`Collected ${allDocFiles.length} source markdown files`)
85205

86206
await generator.generateStandardLLMFiles(context, allDocFiles)
207+
await generator.generateCustomLLMFiles(context, allDocFiles)
87208

88209
console.log('\n--- Pre-injector summary ---')
89210
printSummary(await summarize(outDir))
90211

91-
const injector = require(path.join(siteDir, 'src/plugins/llms-html-injector'))()
92-
await injector.postBuild({ outDir, routesPaths: [] })
212+
// Invoke only the post-processing stage. The injector module also exports a
213+
// wrapper plugin (used in docusaurus.config.js) that internally instantiates
214+
// docusaurus-plugin-llms; we've already run the generator above, so we skip
215+
// straight to normalize/rewrite/inject.
216+
const { postProcessLlmsOutput } = require(path.join(siteDir, 'src/plugins/llms-html-injector'))
217+
await postProcessLlmsOutput(outDir)
218+
219+
// Mirror Docusaurus's static-asset copy step. In a real build, anything in
220+
// `static/` is copied verbatim to `outDir`, so static/llms.txt becomes
221+
// build/llms.txt. Replicating that here lets the post-injector summary show
222+
// the curated root file end users will receive.
223+
await copyStaticLlms(siteDir, outDir)
93224

94225
console.log('\n--- Post-injector summary ---')
95226
printSummary(await summarize(outDir))
96227
}
97228

229+
async function copyStaticLlms(siteDir, outDir) {
230+
const src = path.join(siteDir, 'static', 'llms.txt')
231+
const dest = path.join(outDir, 'llms.txt')
232+
try {
233+
await fs.copyFile(src, dest)
234+
} catch (err) {
235+
console.warn(`[verify-llms-output] Could not copy static/llms.txt: ${err.message}`)
236+
}
237+
}
238+
98239
function printSummary(summary) {
99240
for (const [key, value] of Object.entries(summary)) {
100241
console.log(`${key}: ${Array.isArray(value) ? value.join(', ') : value}`)
@@ -129,7 +270,21 @@ async function summarize(outDir) {
129270
const s = await fs.stat(llmsFull)
130271
summary.llmsFullTxtBytes = s.size
131272
} catch {
132-
summary.llmsFullTxt = 'MISSING'
273+
summary.llmsFullTxt = 'ABSENT (expected — generation disabled)'
274+
}
275+
// Per-section files
276+
const sectionEntries = await fs.readdir(outDir, { withFileTypes: true })
277+
const sectionFiles = sectionEntries
278+
.filter(e => e.isFile() && /^llms-[^.]+\.txt$/.test(e.name))
279+
.map(e => e.name)
280+
.sort()
281+
if (sectionFiles.length > 0) {
282+
const sizes = []
283+
for (const name of sectionFiles) {
284+
const s = await fs.stat(path.join(outDir, name))
285+
sizes.push(`${name}=${s.size}`)
286+
}
287+
summary.sectionFiles = sizes
133288
}
134289
summary.sampleMdFiles = mdFiles
135290
.filter(f => /\.md$/.test(f) && !/llms[^/]*\.(md|txt)$/.test(f))

0 commit comments

Comments
 (0)