From 676509912aad3178d94077dcc4a714ffd71263f5 Mon Sep 17 00:00:00 2001 From: Laith Al-Saadoon <9553966+theagenticguy@users.noreply.github.com> Date: Mon, 27 Apr 2026 11:17:21 -0700 Subject: [PATCH] docs(site): inject LLM-nav banner + 'See also' footer into every .md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors the code.claude.com/docs pattern so any .md fetched by an LLM points back to the site index and surfaces 3-5 related pages. Every per-page .md emitted by starlight-page-actions now starts with a blockquote banner linking /llms.txt and the three scoped bundles (user-guide, mcp, contributing), and ends with a hand-curated "See also" section. The curation lives in packages/docs/scripts/inject-llm-nav.mjs as a RELATED map keyed by page slug — one entry per page. The script runs as an astro-build postbuild step (build = "astro build && node scripts/inject-llm-nav.mjs") and is idempotent. --- packages/docs/package.json | 2 +- packages/docs/scripts/inject-llm-nav.mjs | 275 +++++++++++++++++++++++ 2 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 packages/docs/scripts/inject-llm-nav.mjs diff --git a/packages/docs/package.json b/packages/docs/package.json index 2624e760..80fd48f6 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -11,7 +11,7 @@ "scripts": { "dev": "astro dev", "start": "astro dev", - "build": "astro build", + "build": "astro build && node scripts/inject-llm-nav.mjs", "preview": "astro preview", "check": "astro check", "clean": "rm -rf dist .astro" diff --git a/packages/docs/scripts/inject-llm-nav.mjs b/packages/docs/scripts/inject-llm-nav.mjs new file mode 100644 index 00000000..a22f07c9 --- /dev/null +++ b/packages/docs/scripts/inject-llm-nav.mjs @@ -0,0 +1,275 @@ +#!/usr/bin/env node +// Postbuild: inject LLM-navigation helpers into every per-page .md emitted +// by starlight-page-actions, mirroring the pattern from +// https://code.claude.com/docs/en/agent-sdk/python.md: +// +// 1. Index banner at the top of every page pointing at /llms.txt +// 2. "See also" footer with 3-5 curated related-page links +// +// Runs after `astro build` against packages/docs/dist/**/*.md. + +import { promises as fs } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const DIST = path.resolve(__dirname, "..", "dist"); +const BASE = "/opencodehub"; +const SITE = "https://theagenticguy.github.io"; + +const INDEX_BANNER = `> ## Documentation Index +> Fetch the complete documentation index at: ${SITE}${BASE}/llms.txt +> Use this file to discover all available pages before exploring further. +> Scoped bundles: [user-guide](${SITE}${BASE}/_llms-txt/user-guide.txt) · [mcp](${SITE}${BASE}/_llms-txt/mcp.txt) · [contributing](${SITE}${BASE}/_llms-txt/contributing.txt) + +`; + +// Per-page "See also" — curated by section. +// Keys are the doc slug (path from dist/ without .md extension, leading slash). +const RELATED = { + "/index": [ + ["Quick start", `${BASE}/start-here/quick-start/`], + ["What is OpenCodeHub?", `${BASE}/start-here/what-is-opencodehub/`], + ["MCP tools", `${BASE}/mcp/tools/`], + ["CLI reference", `${BASE}/reference/cli/`], + ], + + // Start here + "/start-here/what-is-opencodehub": [ + ["Install", `${BASE}/start-here/install/`], + ["Quick start", `${BASE}/start-here/quick-start/`], + ["MCP overview", `${BASE}/mcp/overview/`], + ], + "/start-here/install": [ + ["Quick start", `${BASE}/start-here/quick-start/`], + ["First query", `${BASE}/start-here/first-query/`], + ["Troubleshooting", `${BASE}/guides/troubleshooting/`], + ], + "/start-here/quick-start": [ + ["First query", `${BASE}/start-here/first-query/`], + ["Indexing a repo", `${BASE}/guides/indexing-a-repo/`], + ["Using with Claude Code", `${BASE}/guides/using-with-claude-code/`], + ["MCP tools", `${BASE}/mcp/tools/`], + ], + "/start-here/first-query": [ + ["CLI reference", `${BASE}/reference/cli/`], + ["MCP tools", `${BASE}/mcp/tools/`], + ["Indexing a repo", `${BASE}/guides/indexing-a-repo/`], + ], + + // Guides + "/guides/indexing-a-repo": [ + ["CLI reference — analyze", `${BASE}/reference/cli/`], + ["Troubleshooting", `${BASE}/guides/troubleshooting/`], + ["Language matrix", `${BASE}/reference/languages/`], + ], + "/guides/using-with-claude-code": [ + ["Using with Cursor", `${BASE}/guides/using-with-cursor/`], + ["Using with Codex", `${BASE}/guides/using-with-codex/`], + ["MCP overview", `${BASE}/mcp/overview/`], + ["MCP tools", `${BASE}/mcp/tools/`], + ], + "/guides/using-with-cursor": [ + ["Using with Claude Code", `${BASE}/guides/using-with-claude-code/`], + ["Using with Codex", `${BASE}/guides/using-with-codex/`], + ["MCP overview", `${BASE}/mcp/overview/`], + ], + "/guides/using-with-codex": [ + ["Using with Claude Code", `${BASE}/guides/using-with-claude-code/`], + ["Using with Cursor", `${BASE}/guides/using-with-cursor/`], + ["MCP overview", `${BASE}/mcp/overview/`], + ], + "/guides/using-with-windsurf": [ + ["Using with Claude Code", `${BASE}/guides/using-with-claude-code/`], + ["Using with Cursor", `${BASE}/guides/using-with-cursor/`], + ["MCP overview", `${BASE}/mcp/overview/`], + ], + "/guides/using-with-opencode": [ + ["Using with Claude Code", `${BASE}/guides/using-with-claude-code/`], + ["Using with Cursor", `${BASE}/guides/using-with-cursor/`], + ["MCP overview", `${BASE}/mcp/overview/`], + ], + "/guides/cross-repo-groups": [ + ["MCP tools — group_*", `${BASE}/mcp/tools/`], + ["CLI reference — group", `${BASE}/reference/cli/`], + ["Indexing a repo", `${BASE}/guides/indexing-a-repo/`], + ], + "/guides/ci-integration": [ + ["CLI reference — verdict / detect-changes", `${BASE}/reference/cli/`], + ["MCP tools — verdict", `${BASE}/mcp/tools/`], + ["Error codes", `${BASE}/reference/error-codes/`], + ], + "/guides/troubleshooting": [ + ["CLI reference — doctor", `${BASE}/reference/cli/`], + ["Error codes", `${BASE}/reference/error-codes/`], + ["Install", `${BASE}/start-here/install/`], + ], + + // Reference + "/reference/cli": [ + ["MCP tools", `${BASE}/mcp/tools/`], + ["Configuration", `${BASE}/reference/configuration/`], + ["Error codes", `${BASE}/reference/error-codes/`], + ], + "/reference/configuration": [ + ["CLI reference", `${BASE}/reference/cli/`], + ["Using with Claude Code", `${BASE}/guides/using-with-claude-code/`], + ["Troubleshooting", `${BASE}/guides/troubleshooting/`], + ], + "/reference/error-codes": [ + ["CLI reference", `${BASE}/reference/cli/`], + ["MCP overview", `${BASE}/mcp/overview/`], + ["Troubleshooting", `${BASE}/guides/troubleshooting/`], + ], + "/reference/languages": [ + ["Adding a language provider", `${BASE}/contributing/adding-a-language-provider/`], + ["Indexing a repo", `${BASE}/guides/indexing-a-repo/`], + ["Architecture overview", `${BASE}/architecture/overview/`], + ], + + // MCP + "/mcp/overview": [ + ["MCP tools", `${BASE}/mcp/tools/`], + ["Resources", `${BASE}/mcp/resources/`], + ["Prompts", `${BASE}/mcp/prompts/`], + ["Using with Claude Code", `${BASE}/guides/using-with-claude-code/`], + ], + "/mcp/tools": [ + ["MCP overview", `${BASE}/mcp/overview/`], + ["Resources", `${BASE}/mcp/resources/`], + ["Prompts", `${BASE}/mcp/prompts/`], + ["CLI reference", `${BASE}/reference/cli/`], + ], + "/mcp/resources": [ + ["MCP overview", `${BASE}/mcp/overview/`], + ["MCP tools", `${BASE}/mcp/tools/`], + ["Prompts", `${BASE}/mcp/prompts/`], + ], + "/mcp/prompts": [ + ["MCP overview", `${BASE}/mcp/overview/`], + ["MCP tools", `${BASE}/mcp/tools/`], + ["Resources", `${BASE}/mcp/resources/`], + ], + + // Contributing + "/contributing/overview": [ + ["Dev loop", `${BASE}/contributing/dev-loop/`], + ["Commit conventions", `${BASE}/contributing/commit-conventions/`], + ["IP hygiene", `${BASE}/contributing/ip-hygiene/`], + ["Adding a language provider", `${BASE}/contributing/adding-a-language-provider/`], + ], + "/contributing/dev-loop": [ + ["Commit conventions", `${BASE}/contributing/commit-conventions/`], + ["Testing", `${BASE}/contributing/testing/`], + ["Release process", `${BASE}/contributing/release-process/`], + ], + "/contributing/commit-conventions": [ + ["Release process", `${BASE}/contributing/release-process/`], + ["Dev loop", `${BASE}/contributing/dev-loop/`], + ["Contributing overview", `${BASE}/contributing/overview/`], + ], + "/contributing/release-process": [ + ["Commit conventions", `${BASE}/contributing/commit-conventions/`], + ["Contributing overview", `${BASE}/contributing/overview/`], + ["Supply chain", `${BASE}/architecture/supply-chain/`], + ], + "/contributing/ip-hygiene": [ + ["Supply chain", `${BASE}/architecture/supply-chain/`], + ["Contributing overview", `${BASE}/contributing/overview/`], + ["Dev loop", `${BASE}/contributing/dev-loop/`], + ], + "/contributing/adding-a-language-provider": [ + ["Language matrix", `${BASE}/reference/languages/`], + ["Architecture overview", `${BASE}/architecture/overview/`], + ["Testing", `${BASE}/contributing/testing/`], + ], + "/contributing/testing": [ + ["Dev loop", `${BASE}/contributing/dev-loop/`], + ["Architecture overview", `${BASE}/architecture/overview/`], + ["Determinism", `${BASE}/architecture/determinism/`], + ], + + // Architecture + "/architecture/overview": [ + ["Monorepo map", `${BASE}/architecture/monorepo-map/`], + ["ADRs", `${BASE}/architecture/adrs/`], + ["Determinism", `${BASE}/architecture/determinism/`], + ["Supply chain", `${BASE}/architecture/supply-chain/`], + ], + "/architecture/monorepo-map": [ + ["Architecture overview", `${BASE}/architecture/overview/`], + ["Adding a language provider", `${BASE}/contributing/adding-a-language-provider/`], + ["Dev loop", `${BASE}/contributing/dev-loop/`], + ], + "/architecture/adrs": [ + ["Architecture overview", `${BASE}/architecture/overview/`], + ["Determinism", `${BASE}/architecture/determinism/`], + ["Supply chain", `${BASE}/architecture/supply-chain/`], + ], + "/architecture/determinism": [ + ["Architecture overview", `${BASE}/architecture/overview/`], + ["Testing", `${BASE}/contributing/testing/`], + ["ADRs", `${BASE}/architecture/adrs/`], + ], + "/architecture/supply-chain": [ + ["IP hygiene", `${BASE}/contributing/ip-hygiene/`], + ["Architecture overview", `${BASE}/architecture/overview/`], + ["Release process", `${BASE}/contributing/release-process/`], + ], +}; + +function slugForFile(mdPath) { + const rel = path.relative(DIST, mdPath).replace(/\\/g, "/"); + return "/" + rel.replace(/\.md$/, ""); +} + +function seeAlso(slug) { + const links = RELATED[slug]; + if (!links) return ""; + const lines = links.map(([label, href]) => `* [${label}](${href})`).join("\n"); + return `\n\n## See also\n\n${lines}\n`; +} + +async function walk(dir) { + const ents = await fs.readdir(dir, { withFileTypes: true }); + const out = []; + for (const e of ents) { + const full = path.join(dir, e.name); + if (e.isDirectory()) out.push(...(await walk(full))); + else if (e.isFile() && e.name.endsWith(".md")) out.push(full); + } + return out; +} + +async function main() { + let patched = 0; + let skipped = 0; + const files = await walk(DIST); + for (const file of files) { + // Skip llms.txt-family (they're already the index). + if (file.endsWith("/llms.txt")) continue; + if (file.includes("/_llms-txt/")) continue; + + const original = await fs.readFile(file, "utf8"); + + // Idempotency guard — don't double-inject. + if (original.startsWith("> ## Documentation Index")) { + skipped += 1; + continue; + } + + const slug = slugForFile(file); + const body = INDEX_BANNER + original + seeAlso(slug); + await fs.writeFile(file, body, "utf8"); + patched += 1; + } + + console.warn( + `[inject-llm-nav] patched ${patched} .md files, skipped ${skipped} already-patched`, + ); +} + +main().catch((err) => { + console.error("[inject-llm-nav] failed:", err); + process.exitCode = 1; +});