Agent Optimization Plugin
Serve llms.txt, markdown to AI clients, and sitemap.xml with @useavalon/agent-optimization.
@useavalon/agent-optimization makes the site readable to AI agents and sitemap crawlers. It does not inject Open Graph, Twitter cards, or JSON-LD. Those live in @useavalon/seo.
/sitemap.xml— Sitemap Protocol 0.9 from discovered page routes/llms.txt— index of the site for LLMs (llmstxt.org)/llms-full.txt— full page markdown whenllms.fullistrue- Markdown negotiation — HTML converted to markdown when the client sends
Accept: text/markdown
Installation
create-avalon can add this plugin when you select agent-optimization.
Usage
import { defineConfig, type UserConfig } from 'vite';
import { avalon } from '@useavalon/avalon';
import { agentOptimization } from '@useavalon/agent-optimization';
export default defineConfig(async (): Promise<UserConfig> => {
const avalonPlugins = await avalon({
core: 'preact',
modules: 'app/modules',
});
return {
plugins: [
agentOptimization({
sitemap: {
siteUrl: 'https://example.com',
changefreq: 'weekly',
exclude: ['/admin/**', '/login'],
},
markdown: true,
llms: {
siteUrl: 'https://example.com',
siteName: 'My Site',
siteDescription: 'A site built with Avalon',
sections: {
Pages: ['/'],
Docs: ['/docs'],
Blog: ['/blog'],
},
full: true,
},
}),
...avalonPlugins,
],
};
});
Put agentOptimization() before the Avalon plugin list so middleware can patch res.end before SSR writes HTML.
Configuration
| Option | Type | Default | Description |
|---|---|---|---|
sitemap | boolean | SitemapConfig | — | Enable sitemap generation |
sitemap.siteUrl | string | required when object | Origin for <loc> values |
sitemap.changefreq | string? | 'weekly' | Default change frequency |
sitemap.priority | number? | 0.5 | Default priority (0–1) |
sitemap.dynamicPaths | Record<string, string[]>? | — | Expand dynamic route params |
sitemap.exclude | string[]? | — | Glob patterns or path prefixes to skip |
markdown | boolean? | — | Serve markdown when Accept: text/markdown |
llms | boolean | LlmsConfig | — | Enable llms.txt generation |
llms.siteUrl | string | required when object | Origin for links in llms.txt |
llms.siteName | string | required when object | H1 in llms.txt |
llms.siteDescription | string? | — | Blockquote under the H1 |
llms.sections | Record<string, string[]>? | — | H2 heading → path prefixes |
llms.exclude | string[]? | — | Patterns to skip |
llms.full | boolean? | false | Also write /llms-full.txt |
sitemap: true or llms: true without a config object falls back to http://localhost and logs a warning. Pass an object with siteUrl (and siteName for llms) in production.
Sitemap
The plugin discovers routes from your Avalon pages directories and writes /sitemap.xml. It skips dynamic segments (:param), private _-prefixed routes, and paths matching exclude.
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://example.com/</loc>
<changefreq>weekly</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://example.com/docs</loc>
<changefreq>weekly</changefreq>
<priority>0.5</priority>
</url>
</urlset>
Expand dynamic routes with dynamicPaths:
agentOptimization({
sitemap: {
siteUrl: 'https://example.com',
dynamicPaths: {
'/blog/:slug': ['/blog/hello-world', '/blog/release-notes'],
},
},
});
llms.txt
/llms.txt is a markdown index: site name, optional description, then categorized links.
# My Site
> A site built with Avalon
## Pages
- [Home](https://example.com/): Welcome
## Docs
- [Introduction](https://example.com/docs): Get started
llms.sections keys become H2 headings. Each value is a list of path prefixes. Routes that do not match a section are omitted from those groups.
Set full: true to also generate /llms-full.txt with concatenated page markdown. That pass renders pages at build time.
Markdown content negotiation
curl -H "Accept: text/markdown" https://example.com/docs/introduction
---
title: "Introduction"
description: "Get started with Avalon"
---
# Introduction
Get started with Avalon...
The plugin renders HTML as usual, converts the #app, <main>, or <body> tree to markdown, and adds YAML frontmatter from the page title and description.
API
export { agentOptimization } from '@useavalon/agent-optimization';
export { buildLlmsTxt, buildLlmsFullTxt, routesToLlmsEntries } from '@useavalon/agent-optimization';
export { htmlToMarkdown, buildFrontMatter, shouldServeMarkdown } from '@useavalon/agent-optimization';
export { buildSitemapXml, routesToSitemapEntries } from '@useavalon/agent-optimization';
export type {
AgentOptimizationConfig,
LlmsConfig,
SitemapConfig,
PageMetadata,
} from '@useavalon/agent-optimization';
JSON-LD helpers (buildWebPageJsonLd, injectJsonLd) are on @useavalon/seo, not this package.
With SEO
plugins: [
seo({ siteUrl: 'https://example.com', siteName: 'My Site' }),
agentOptimization({
sitemap: { siteUrl: 'https://example.com' },
markdown: true,
llms: { siteUrl: 'https://example.com', siteName: 'My Site' },
}),
...avalonPlugins,
],
Related
- SEO — Open Graph, Twitter cards, canonical URLs, JSON-LD
- Page Metadata —
metadataexport used for titles and descriptions