vitepress-plugin-llms
Publishes LLM-friendly artifacts for a VitePress site: an llmstxt.org index (/llms.txt), a single-file full-text dump of all pages (/llms-full.txt) and a per-page mirror of the source Markdown (<url>.md). A tool that downloads /foo/index.md receives the source, not the VitePress HTML wrapper.
Useful when documentation needs to be consumed by search-index robots (GEO), assistants such as ChatGPT/Claude and internal RAG pipelines: the VitePress HTML render is redundant and noisy for them.
Installation
pnpm add @ampernic/vitepress-plugin-llmsIf the package is installed from Forgejo instead of npmjs, also add the scope to .npmrc:
@ampernic:registry=https://altlinux.space/api/packages/ampernic/npm/Usage
The plugin returns three VitePress config hooks, wire them into defineConfig at the root:
// .vitepress/config.ts
import { defineConfig } from 'vitepress'
import { LlmsPlugin } from '@ampernic/vitepress-plugin-llms'
const llms = LlmsPlugin({
hostname: 'https://docs.altlinux.space',
title: 'ALT Linux Documentation',
description: 'Reference documentation for ALT distributions.',
section: (p) => p.url.split('/').filter(Boolean)[0] ?? 'root',
filter: (p) => !p.url.endsWith('/404.html'),
})
export default defineConfig({
transformPageData: llms.transformPageData,
buildEnd: llms.buildEnd,
vite: llms.vite,
})Through createSharedConfig in @ampernic/vitepress-theme-alt-docs these hooks are wired automatically; options go under the llms field.
In dev mode vite.plugins adds middleware that serves .md with Content-Type: text/markdown; charset=utf-8, so browsers and external clients do not miss the MIME type.
Options
| Option | Type | Default | Description |
|---|---|---|---|
hostname | string | -- | Absolute origin for absoluteUrl fields in llms.txt / llms-full.txt. Without it URLs stay root-relative |
title | string | 'Documentation' | Title of llms.txt and llms-full.txt. Defaults to the root page frontmatter (altHome.hero.title or title) |
description | string | '' | One-line description right under the title. Fallback also comes from the root frontmatter |
fallbackSummary | string | '' | Summary of an llms.txt entry when the page has no first paragraph |
emitIndex | boolean | true | Write llms.txt |
emitFullText | boolean | true | Write llms-full.txt |
emitPerPageMarkdown | boolean | true | Place a <url>.md mirror next to every HTML page |
filter | (page: PageMeta) => boolean | -- | Removes pages (auto-generated indexes, redirects) from every artifact |
section | (page: PageMeta) => string | first URL segment | Grouping key in the llms.txt tree |
pageTitle | (page: PageMeta) => string | frontmatter title | pageData.title | Renames an index entry |
summary | (page: PageMeta) => string | first paragraph of the source | One-line summary of the page in llms.txt |
renderSource | (page: PageMeta) => string | undefined | -- | Rewrites what goes into artifacts and per-page .md. Needed to expand frontmatter-driven landings (<DocSections />) into real Markdown for LLM |
fullTextMaxBytes | number | 8 * 1024 * 1024 | Hard limit on llms-full.txt size. Older LLM clients choke on multi-megabyte files. 0 means no limit |
preamble | string | -- | Extra Markdown block in the header of llms.txt: contacts, license, project description |
extraPages | () => SyntheticPage[] | -- | Synthetic pages appended to the set before emit. Useful for aggregator sites that list pages they do not host themselves |
What goes into the artifacts
llms.txt: title + description + optional preamble + a flat list of sectionless entries + entries grouped bysection(page)with links and a one-line summary of each page.llms-full.txt: title + description + one block per page,---separator,url:/title:header. Order is lexicographic by URL so the diff between builds is deterministic.- Per-page
.md: raw Markdown from the repo. For<url>/VitePress returns<url>index.md, for<url>it returns<url>.md.
Notes on the output
renderSourceruns beforefilter. When the root landing is rendered from frontmatter, returning full Markdown from it is the only way to give the LLM something meaningful.- Title and description of
llms.txtresolve as: plugin options >altHome.hero.title/altHome.hero.textof the root page > itstitle/description> defaults. On a per-distribution site the "root" is${base}(for example,/alt-workstation/), not/. - Without
hostnameentry URLs are root-relative. GEO indexers often require absolute links, so it is better to always set it. - The per-page
.mdis written tooutDirwithout thesite.baseprefix: on deploy the distribution already sits under/<distro>/, and doubling the prefix would give a404.