diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index f529d3fb..e3741231 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -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" + - name: Upload current docs artifact uses: actions/upload-artifact@v5 with: diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 02262704..536b0067 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -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 @@ -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' }] ], cleanUrls: true, themeConfig: { @@ -145,6 +147,7 @@ export default defineConfig({ { text: 'Contributing', items: [ { text: 'Documentation', link: '/contributing/documentation' }, + { text: 'LLMs.txt', link: '/llms_txt' }, ] }, ], @@ -161,5 +164,7 @@ export default defineConfig({ { icon: 'github', link: 'https://github.com/activeagents/activeagent' } ], }, - lastUpdated: true + lastUpdated: true, + + buildEnd: generateLlmsTxt }) diff --git a/docs/.vitepress/llms-txt.ts b/docs/.vitepress/llms-txt.ts new file mode 100644 index 00000000..8f78e87e --- /dev/null +++ b/docs/.vitepress/llms-txt.ts @@ -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' }], + }, + { + 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 { + const content = readFileSync(filePath, 'utf-8') + const match = content.match(/^---\n([\s\S]*?)\n---/) + if (!match) return {} + + const fm: Record = {} + 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 + 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}`) + count++ + } + + lines.push('') + } + + const outPath = join(siteConfig.outDir, 'llms.txt') + writeFileSync(outPath, lines.join('\n') + '\n') + console.log(`Generated ${outPath} with ${count} entries`) +} diff --git a/docs/llms_txt.md b/docs/llms_txt.md new file mode 100644 index 00000000..f28ae103 --- /dev/null +++ b/docs/llms_txt.md @@ -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.