AvalonAvalon
GitHub

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 when llms.full is true
  • Markdown negotiation — HTML converted to markdown when the client sends Accept: text/markdown

Installation

>_ Terminal
bun add @useavalon/agent-optimization

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

OptionTypeDefaultDescription
sitemapboolean | SitemapConfig—Enable sitemap generation
sitemap.siteUrlstringrequired when objectOrigin for <loc> values
sitemap.changefreqstring?'weekly'Default change frequency
sitemap.prioritynumber?0.5Default priority (0–1)
sitemap.dynamicPathsRecord<string, string[]>?—Expand dynamic route params
sitemap.excludestring[]?—Glob patterns or path prefixes to skip
markdownboolean?—Serve markdown when Accept: text/markdown
llmsboolean | LlmsConfig—Enable llms.txt generation
llms.siteUrlstringrequired when objectOrigin for links in llms.txt
llms.siteNamestringrequired when objectH1 in llms.txt
llms.siteDescriptionstring?—Blockquote under the H1
llms.sectionsRecord<string, string[]>?—H2 heading → path prefixes
llms.excludestring[]?—Patterns to skip
llms.fullboolean?falseAlso 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 — metadata export used for titles and descriptions