mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-10 08:05:14 +02:00
411 lines
12 KiB
Markdown
411 lines
12 KiB
Markdown
# Agent-Friendly Docs Site Implementation Plan
|
|
|
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
|
|
**Goal:** Make `docs-site` discoverable and readable by coding agents through `llms.txt`, bundled markdown, per-page markdown routes, markdown negotiation, and stricter agent-friendly docs content.
|
|
|
|
**Architecture:** Keep the existing Next 15 + Fumadocs app. Add a small `lib/llm-docs.ts` module that reads Fumadocs pages and builds machine-readable markdown responses, then expose those responses through route handlers and a markdown negotiation proxy. Rewrite existing MDX pages in place so the rendered UI and machine-readable routes share one source of truth.
|
|
|
|
**Tech Stack:** Next.js 15 App Router, Fumadocs, MDX, TypeScript, pnpm, Node 22.
|
|
|
|
---
|
|
|
|
### Task 1: Machine-Readable Docs Routes
|
|
|
|
**Files:**
|
|
- Create: `docs-site/lib/llm-docs.ts`
|
|
- Create: `docs-site/app/llms.txt/route.ts`
|
|
- Create: `docs-site/app/llms-full.txt/route.ts`
|
|
- Create: `docs-site/app/llms.mdx/docs/[[...slug]]/route.ts`
|
|
- Modify: `docs-site/next.config.mjs`
|
|
|
|
- [ ] **Step 1: Add the LLM docs utility**
|
|
|
|
Create `docs-site/lib/llm-docs.ts` with functions that:
|
|
|
|
```ts
|
|
import { source } from "@/lib/source";
|
|
|
|
const SITE_ORIGIN = "https://ktx.dev";
|
|
|
|
export type LlmDocsPage = {
|
|
title: string;
|
|
description?: string;
|
|
url: string;
|
|
markdownUrl: string;
|
|
slug: string[];
|
|
getMarkdown: () => Promise<string>;
|
|
};
|
|
|
|
export function getLlmDocsPages(): LlmDocsPage[] {
|
|
return source.getPages().map((page) => ({
|
|
title: page.data.title,
|
|
description: page.data.description,
|
|
url: page.url,
|
|
markdownUrl: `${page.url}.md`,
|
|
slug: page.slugs,
|
|
getMarkdown: async () => normalizeMarkdown(await page.data.getText("raw")),
|
|
}));
|
|
}
|
|
|
|
export function getLlmDocsPage(slug: string[] | undefined) {
|
|
const page = source.getPage(slug);
|
|
if (!page) return null;
|
|
|
|
return {
|
|
title: page.data.title,
|
|
description: page.data.description,
|
|
url: page.url,
|
|
markdownUrl: `${page.url}.md`,
|
|
slug: page.slugs,
|
|
getMarkdown: async () => normalizeMarkdown(await page.data.getText("raw")),
|
|
} satisfies LlmDocsPage;
|
|
}
|
|
|
|
export async function getPageMarkdown(page: LlmDocsPage) {
|
|
const body = await page.getMarkdown();
|
|
const description = page.description ? `\n\n> ${page.description}` : "";
|
|
|
|
return `# ${page.title}${description}\n\nCanonical URL: ${page.url}\nMarkdown URL: ${page.markdownUrl}\n\n${body}`;
|
|
}
|
|
|
|
export function buildLlmsTxt() {
|
|
const pages = getLlmDocsPages();
|
|
const byUrl = new Map(pages.map((page) => [page.url, page]));
|
|
const link = (url: string, label: string, fallbackDescription: string) => {
|
|
const page = byUrl.get(url);
|
|
const description = page?.description ?? fallbackDescription;
|
|
return `- [${label}](${url}): ${description}`;
|
|
};
|
|
|
|
return `# KTX
|
|
|
|
> Agent-native context layer for analytics engineering and database agents.
|
|
|
|
KTX provides semantic-layer files, warehouse scans, knowledge pages, provenance, and agent-facing tools that help coding agents answer analytics questions without inventing metrics or joins.
|
|
|
|
## Start Here
|
|
|
|
${link("/docs/getting-started/introduction", "Introduction", "What KTX is and who it is for")}
|
|
${link("/docs/getting-started/quickstart", "Quickstart", "Set up KTX and build your first context")}
|
|
${link("/docs/guides/serving-agents", "Serving Agents", "Expose KTX context through MCP and CLI tools")}
|
|
${link("/docs/guides/writing-context", "Writing Context", "Write semantic sources and knowledge pages")}
|
|
|
|
## Machine-Readable Documentation
|
|
|
|
- [Full documentation](/llms-full.txt): All docs pages in one plain-text markdown response
|
|
- [Quickstart markdown](/docs/getting-started/quickstart.md): Raw markdown for the setup guide
|
|
- [Agent CLI markdown](/docs/cli-reference/ktx-agent.md): Raw markdown for machine-readable agent commands
|
|
- [Serving Agents markdown](/docs/guides/serving-agents.md): Raw markdown for MCP and CLI workflows
|
|
|
|
## CLI Reference
|
|
|
|
${link("/docs/cli-reference/ktx-setup", "ktx setup", "Interactive project setup")}
|
|
${link("/docs/cli-reference/ktx-agent", "ktx agent", "Machine-readable commands for coding agents")}
|
|
${link("/docs/cli-reference/ktx-sl", "ktx sl", "Semantic-layer commands")}
|
|
${link("/docs/cli-reference/ktx-wiki", "ktx wiki", "Knowledge page commands")}
|
|
${link("/docs/cli-reference/ktx-connection", "ktx connection", "Connection management commands")}
|
|
|
|
## Integrations
|
|
|
|
${link("/docs/integrations/agent-clients", "Agent Clients", "Configure Claude Code, Cursor, Codex, and OpenCode")}
|
|
${link("/docs/integrations/primary-sources", "Primary Sources", "Connect KTX to databases and warehouses")}
|
|
${link("/docs/integrations/context-sources", "Context Sources", "Ingest dbt, LookML, Metabase, Looker, MetricFlow, and Notion")}
|
|
`;
|
|
}
|
|
|
|
export async function buildLlmsFullTxt() {
|
|
const pages = getLlmDocsPages();
|
|
const rendered = await Promise.all(pages.map(getPageMarkdown));
|
|
return [`# KTX Full Documentation`, `Source: ${SITE_ORIGIN}`, ...rendered].join("\n\n---\n\n");
|
|
}
|
|
|
|
function normalizeMarkdown(markdown: string) {
|
|
return markdown.trim().replace(/\n{3,}/g, "\n\n");
|
|
}
|
|
```
|
|
|
|
- [ ] **Step 2: Add route handlers**
|
|
|
|
Create route files:
|
|
|
|
```ts
|
|
import { buildLlmsTxt } from "@/lib/llm-docs";
|
|
|
|
export const dynamic = "force-static";
|
|
|
|
export function GET() {
|
|
return new Response(buildLlmsTxt(), {
|
|
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
|
});
|
|
}
|
|
```
|
|
|
|
```ts
|
|
import { buildLlmsFullTxt } from "@/lib/llm-docs";
|
|
|
|
export const dynamic = "force-static";
|
|
|
|
export async function GET() {
|
|
return new Response(await buildLlmsFullTxt(), {
|
|
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
|
});
|
|
}
|
|
```
|
|
|
|
```ts
|
|
import { getLlmDocsPage, getPageMarkdown } from "@/lib/llm-docs";
|
|
import { notFound } from "next/navigation";
|
|
|
|
export const dynamic = "force-static";
|
|
|
|
export async function GET(
|
|
_request: Request,
|
|
props: { params: Promise<{ slug?: string[] }> },
|
|
) {
|
|
const params = await props.params;
|
|
const page = getLlmDocsPage(params.slug);
|
|
if (!page) notFound();
|
|
|
|
return new Response(await getPageMarkdown(page), {
|
|
headers: { "Content-Type": "text/markdown; charset=utf-8" },
|
|
});
|
|
}
|
|
|
|
export function generateStaticParams() {
|
|
return getLlmDocsPages().map((page) => ({ slug: page.slug }));
|
|
}
|
|
```
|
|
|
|
- [ ] **Step 3: Add `.md` rewrite**
|
|
|
|
Modify `docs-site/next.config.mjs`:
|
|
|
|
```js
|
|
import { createMDX } from "fumadocs-mdx/next";
|
|
|
|
const withMDX = createMDX();
|
|
|
|
/** @type {import('next').NextConfig} */
|
|
const config = {
|
|
async rewrites() {
|
|
return [
|
|
{
|
|
source: "/docs/:path*.md",
|
|
destination: "/llms.mdx/docs/:path*",
|
|
},
|
|
];
|
|
},
|
|
};
|
|
|
|
export default withMDX(config);
|
|
```
|
|
|
|
- [ ] **Step 4: Build check**
|
|
|
|
Run: `pnpm --filter ktx-docs build`
|
|
|
|
Expected: Next build completes and static routes include `llms.txt`, `llms-full.txt`, and the LLM markdown route.
|
|
|
|
### Task 2: Markdown Negotiation
|
|
|
|
**Files:**
|
|
- Create: `docs-site/proxy.ts`
|
|
|
|
- [ ] **Step 1: Add markdown negotiation proxy**
|
|
|
|
Create `docs-site/proxy.ts`:
|
|
|
|
```ts
|
|
import { isMarkdownPreferred, rewritePath } from "fumadocs-core/negotiation";
|
|
import { NextResponse, type NextRequest } from "next/server";
|
|
|
|
const { rewrite } = rewritePath("/docs/*path", "/llms.mdx/docs/*path");
|
|
|
|
export function proxy(request: NextRequest) {
|
|
if (!isMarkdownPreferred(request)) {
|
|
return NextResponse.next();
|
|
}
|
|
|
|
const rewrittenPath = rewrite(request.nextUrl.pathname);
|
|
if (!rewrittenPath) {
|
|
return NextResponse.next();
|
|
}
|
|
|
|
return NextResponse.rewrite(new URL(rewrittenPath, request.nextUrl));
|
|
}
|
|
|
|
export const config = {
|
|
matcher: ["/docs/:path*"],
|
|
};
|
|
```
|
|
|
|
- [ ] **Step 2: Verify build**
|
|
|
|
Run: `pnpm --filter ktx-docs build`
|
|
|
|
Expected: Build passes with the proxy included.
|
|
|
|
### Task 3: Agent-Friendly High-Priority Guides
|
|
|
|
**Files:**
|
|
- Modify: `docs-site/content/docs/getting-started/quickstart.mdx`
|
|
- Modify: `docs-site/content/docs/guides/serving-agents.mdx`
|
|
- Modify: `docs-site/content/docs/guides/writing-context.mdx`
|
|
|
|
- [ ] **Step 1: Rewrite quickstart structure**
|
|
|
|
Add sections for:
|
|
|
|
- Workflow summary
|
|
- Generated files
|
|
- Common errors and recovery
|
|
|
|
Keep existing setup detail, but make each command block copy-pasteable and each expected output complete enough for agents to recognize success.
|
|
|
|
- [ ] **Step 2: Rewrite Serving Agents as API reference**
|
|
|
|
Add tables for MCP tool inputs and CLI command inputs. Add workflows:
|
|
|
|
- Answer an analytics question through MCP
|
|
- Answer an analytics question through CLI
|
|
- Safely execute SQL with row limits
|
|
|
|
- [ ] **Step 3: Rewrite Writing Context with schemas and workflows**
|
|
|
|
Add semantic-source field tables, knowledge-page field tables, and workflows:
|
|
|
|
- Inspect a source
|
|
- Edit and validate a source
|
|
- Query through the semantic layer
|
|
- Write and search a knowledge page
|
|
|
|
- [ ] **Step 4: Build check**
|
|
|
|
Run: `pnpm --filter ktx-docs build`
|
|
|
|
Expected: MDX compiles without syntax errors.
|
|
|
|
### Task 4: CLI Reference Normalization
|
|
|
|
**Files:**
|
|
- Modify: `docs-site/content/docs/cli-reference/*.mdx`
|
|
|
|
- [ ] **Step 1: Normalize every CLI page**
|
|
|
|
For each CLI reference page, ensure this structure exists:
|
|
|
|
```md
|
|
## Command signature
|
|
|
|
```bash
|
|
ktx <command> [subcommand] [options]
|
|
```
|
|
|
|
## Subcommands
|
|
|
|
| Subcommand | Description |
|
|
|---|---|
|
|
|
|
## Options
|
|
|
|
| Flag | Type | Required | Description | Default |
|
|
|---|---|---|---|---|
|
|
|
|
## Examples
|
|
|
|
```bash
|
|
ktx <real-command> --real-flag realistic-value
|
|
```
|
|
|
|
## Output
|
|
|
|
```text
|
|
complete expected output shape
|
|
```
|
|
|
|
## Common errors
|
|
|
|
| Error | Cause | Recovery |
|
|
|---|---|---|
|
|
```
|
|
|
|
Only add sections that are relevant to the command; do not invent output for commands whose output is intentionally interactive.
|
|
|
|
- [ ] **Step 2: Build check**
|
|
|
|
Run: `pnpm --filter ktx-docs build`
|
|
|
|
Expected: MDX compiles without syntax errors.
|
|
|
|
### Task 5: Integration and Concept Page Polish
|
|
|
|
**Files:**
|
|
- Modify: `docs-site/content/docs/integrations/agent-clients.mdx`
|
|
- Modify: `docs-site/content/docs/integrations/primary-sources.mdx`
|
|
- Modify: `docs-site/content/docs/integrations/context-sources.mdx`
|
|
- Modify: `docs-site/content/docs/concepts/*.mdx`
|
|
- Modify: `docs-site/content/docs/benchmarks/link-detection.mdx`
|
|
|
|
- [ ] **Step 1: Normalize integrations**
|
|
|
|
Add structured sections for supported values, config snippets, authentication, generated files, and recovery notes. Keep existing examples aligned with current KTX commands.
|
|
|
|
- [ ] **Step 2: Add agent usage notes**
|
|
|
|
For concept and benchmark pages, add a compact `## Agent usage notes` section that tells agents when the page is relevant and which concrete page to read next.
|
|
|
|
- [ ] **Step 3: Build check**
|
|
|
|
Run: `pnpm --filter ktx-docs build`
|
|
|
|
Expected: MDX compiles without syntax errors.
|
|
|
|
### Task 6: Route Verification and Final Checks
|
|
|
|
**Files:**
|
|
- No required source changes unless verification finds a bug.
|
|
|
|
- [ ] **Step 1: Run production build**
|
|
|
|
Run: `pnpm --filter ktx-docs build`
|
|
|
|
Expected: Build succeeds.
|
|
|
|
- [ ] **Step 2: Run TypeScript check**
|
|
|
|
Run: `pnpm --filter ktx-docs exec tsc --noEmit`
|
|
|
|
Expected: TypeScript exits successfully.
|
|
|
|
- [ ] **Step 3: Start local server**
|
|
|
|
Run: `pnpm --filter ktx-docs start`
|
|
|
|
Expected: Server starts on an available port.
|
|
|
|
- [ ] **Step 4: Verify machine-readable routes**
|
|
|
|
Run:
|
|
|
|
```bash
|
|
curl -i http://localhost:3000/llms.txt
|
|
curl -i http://localhost:3000/llms-full.txt
|
|
curl -i http://localhost:3000/docs/getting-started/quickstart.md
|
|
curl -i -H "Accept: text/markdown" http://localhost:3000/docs/getting-started/quickstart
|
|
curl -i http://localhost:3000/docs/not-a-page.md
|
|
```
|
|
|
|
Expected:
|
|
|
|
- `/llms.txt`: `200`, `Content-Type: text/plain; charset=utf-8`
|
|
- `/llms-full.txt`: `200`, `Content-Type: text/plain; charset=utf-8`
|
|
- `/docs/getting-started/quickstart.md`: `200`, `Content-Type: text/markdown; charset=utf-8`
|
|
- `/docs/getting-started/quickstart` with `Accept: text/markdown`: `200`, `Content-Type: text/markdown; charset=utf-8`
|
|
- `/docs/not-a-page.md`: `404`
|
|
|
|
- [ ] **Step 5: Inspect final diff**
|
|
|
|
Run: `git diff --stat && git diff --check`
|
|
|
|
Expected: Diff contains only docs-site and plan changes, with no whitespace errors.
|