Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,18 @@ jobs:
- name: Build with VitePress
run: npm run docs:build

- name: Validate llms.txt
run: |
FILE="docs/.vitepress/dist/llms.txt"
test -f "$FILE" || { echo "FAIL: $FILE not found"; exit 1; }
head -1 "$FILE" | grep -q "^# Active Agent" || { echo "FAIL: missing H1"; exit 1; }
ENTRIES=$(grep -c "^- \[" "$FILE")
test "$ENTRIES" -ge 30 || { echo "FAIL: only $ENTRIES entries (expected >=30)"; exit 1; }
for section in "Getting Started" "Framework" "Agents" "Actions" "Providers" "Examples" "Contributing"; do
grep -q "^## $section" "$FILE" || { echo "FAIL: missing section '$section'"; exit 1; }
done
echo "llms.txt valid: $ENTRIES entries, all sections present"
Comment on lines +92 to +97
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI validation only enforces ENTRIES >= 30, so a partial/incorrect llms.txt could still pass (especially since generation currently can skip missing pages). Since the generator’s page list is fixed, consider asserting the exact expected entry count (or otherwise validating every expected URL/title) to reliably catch regressions.

Suggested change
ENTRIES=$(grep -c "^- \[" "$FILE")
test "$ENTRIES" -ge 30 || { echo "FAIL: only $ENTRIES entries (expected >=30)"; exit 1; }
for section in "Getting Started" "Framework" "Agents" "Actions" "Providers" "Examples" "Contributing"; do
grep -q "^## $section" "$FILE" || { echo "FAIL: missing section '$section'"; exit 1; }
done
echo "llms.txt valid: $ENTRIES entries, all sections present"
EXPECTED_ENTRIES=30
ENTRIES=$(grep -c "^- \[" "$FILE")
test "$ENTRIES" -eq "$EXPECTED_ENTRIES" || { echo "FAIL: $ENTRIES entries found (expected exactly $EXPECTED_ENTRIES)"; exit 1; }
for section in "Getting Started" "Framework" "Agents" "Actions" "Providers" "Examples" "Contributing"; do
grep -q "^## $section" "$FILE" || { echo "FAIL: missing section '$section'"; exit 1; }
done
echo "llms.txt valid: $ENTRIES entries (expected $EXPECTED_ENTRIES), all sections present"

Copilot uses AI. Check for mistakes.

- name: Upload current docs artifact
uses: actions/upload-artifact@v5
with:
Expand Down
9 changes: 7 additions & 2 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from "vitepress-plugin-group-icons"

import versions from './versions.json'
import { generateLlmsTxt } from './llms-txt'

// Build version dropdown items with absolute URLs for cross-version navigation
// This forces full page reload instead of client-side routing
Expand Down Expand Up @@ -61,7 +62,8 @@ export default defineConfig({
['meta', { property: 'og:description', content: 'The AI framework for Rails with less code & more fun.' }],
['meta', { property: 'og:url', content: 'https://activeagents.ai' }],
['meta', { property: 'og:type', content: 'website' }],
['script', { async: '', defer: '', src: 'https://buttons.github.io/buttons.js' }]
['script', { async: '', defer: '', src: 'https://buttons.github.io/buttons.js' }],
['link', { rel: 'help', type: 'text/markdown', href: '/llms.txt', title: 'LLM Documentation' }]
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new <link rel="help"> uses an absolute href: '/llms.txt', which bypasses VitePress base when building versioned docs (or any non-root deployment). This can produce a broken link in the rendered HTML head for non-root bases; consider prefixing with the configured base (or using VitePress's withBase helper) so the link resolves correctly in all builds.

Copilot uses AI. Check for mistakes.
],
cleanUrls: true,
themeConfig: {
Expand Down Expand Up @@ -145,6 +147,7 @@ export default defineConfig({
{ text: 'Contributing',
items: [
{ text: 'Documentation', link: '/contributing/documentation' },
{ text: 'LLMs.txt', link: '/llms_txt' },
]
},
],
Expand All @@ -161,5 +164,7 @@ export default defineConfig({
{ icon: 'github', link: 'https://github.com/activeagents/activeagent' }
],
},
lastUpdated: true
lastUpdated: true,

buildEnd: generateLlmsTxt
})
126 changes: 126 additions & 0 deletions docs/.vitepress/llms-txt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { readFileSync, writeFileSync } from 'fs'
import { join } from 'path'
import type { SiteConfig } from 'vitepress'

const BASE_URL = 'https://docs.activeagents.ai'

const sections = [
{
title: 'Getting Started',
pages: [{ path: 'getting_started' }],
},
{
Comment on lines +7 to +12
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generator hard-codes the list of pages in sections, but the repo contains other top-level pages (e.g. docs/index.md) that are not included. If the goal is “index of all documentation pages”, consider generating the list from the VitePress page data / filesystem (and explicitly excluding only pages you don’t want, like llms_txt.md).

Copilot uses AI. Check for mistakes.
title: 'Framework',
pages: [
{ path: 'framework' },
{ path: 'agents' },
{ path: 'providers' },
{ path: 'framework/configuration' },
{ path: 'framework/instrumentation' },
{ path: 'framework/retries' },
{ path: 'framework/rails' },
{ path: 'framework/testing' },
],
},
{
title: 'Agents',
pages: [
{ path: 'actions' },
{ path: 'agents/generation' },
{ path: 'agents/instructions' },
{ path: 'agents/streaming' },
{ path: 'agents/callbacks' },
{ path: 'agents/error_handling' },
],
},
{
title: 'Actions',
pages: [
{ path: 'actions/messages' },
{ path: 'actions/embeddings' },
{ path: 'actions/tools' },
{ path: 'actions/mcps' },
{ path: 'actions/structured_output' },
{ path: 'actions/usage' },
],
},
{
title: 'Providers',
pages: [
{ path: 'providers/anthropic' },
{ path: 'providers/ollama' },
{ path: 'providers/open_ai' },
{ path: 'providers/open_router' },
{ path: 'providers/mock' },
],
},
{
title: 'Examples',
pages: [
{ path: 'examples/browser-use-agent' },
{ path: 'examples/data_extraction_agent' },
{ path: 'examples/mcp-integration-agent' },
{ path: 'examples/research-agent' },
{ path: 'examples/support-agent' },
{ path: 'examples/translation-agent' },
{ path: 'examples/web-search-agent' },
],
},
{
title: 'Contributing',
pages: [
{ path: 'contributing/documentation' },
],
},
]

function parseFrontmatter(filePath: string): Record<string, string> {
const content = readFileSync(filePath, 'utf-8')
const match = content.match(/^---\n([\s\S]*?)\n---/)
if (!match) return {}

const fm: Record<string, string> = {}
for (const line of match[1].split('\n')) {
const m = line.match(/^(\w+):\s*(.+)$/)
if (m) fm[m[1]] = m[2].replace(/^["']|["']$/g, '')
}
return fm
}

export async function generateLlmsTxt(siteConfig: SiteConfig) {
let count = 0
const lines: string[] = []

lines.push('# Active Agent')
lines.push('')
lines.push('> ActiveAgent extends Rails MVC to AI interactions. Build intelligent agents using familiar patterns — controllers, actions, callbacks, and views. The AI framework for Rails with less code & more fun.')
lines.push('')

for (const section of sections) {
lines.push(`## ${section.title}`)
lines.push('')

for (const page of section.pages) {
const filePath = join(siteConfig.srcDir, `${page.path}.md`)
let fm: Record<string, string>
try {
fm = parseFrontmatter(filePath)
} catch (error) {
throw new Error(`Failed to read ${page.path}.md at ${filePath}: ${(error as Error).message}`)
}

const title = fm.title || page.path
const desc = fm.description || ''
const url = `${BASE_URL}/${page.path}`

lines.push(`- [${title}](${url}): ${desc}`)
Comment on lines +112 to +116
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

llms.txt URLs are generated as ${BASE_URL}/${page.path} without considering the VitePress base (used for versioned builds). This means versioned builds will emit an llms.txt whose links point at the unversioned pages instead of the built site’s actual paths. Consider incorporating siteConfig.site.base into the generated URLs, or skipping generation when base is not / if only the root site should expose llms.txt.

Copilot uses AI. Check for mistakes.
count++
}

lines.push('')
}

const outPath = join(siteConfig.outDir, 'llms.txt')
writeFileSync(outPath, lines.join('\n') + '\n')
console.log(`Generated ${outPath} with ${count} entries`)
}
31 changes: 31 additions & 0 deletions docs/llms_txt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
title: LLMs.txt
description: Machine-readable documentation index for AI tools and large language models following the llms.txt specification.
---
# {{ $frontmatter.title }}

Active Agent publishes an [`llms.txt`](/llms.txt) file — a curated, machine-readable index of documentation pages, following the [llms.txt specification](https://llmstxt.org).

## What is llms.txt?

The llms.txt spec provides a standard way for websites to offer documentation in a format optimized for large language models. Instead of crawling HTML pages, AI tools can fetch a single markdown file with structured links and descriptions for every page.

## Using llms.txt

Point your AI tool at the file:

```
https://docs.activeagents.ai/llms.txt
```

Most AI-powered coding assistants and chat interfaces can ingest this URL directly to get full context on Active Agent.

## Regenerating

The file is regenerated on every docs deploy as part of the VitePress build. To regenerate locally:

```bash
npm run docs:build
```

This parses frontmatter from the curated list of documentation markdown files and writes `llms.txt` to the build output directory.