diff --git a/apps/docs/app/docs/[slug]/llms.txt/route.ts b/apps/docs/app/docs/[slug]/llms.txt/route.ts
new file mode 100644
index 0000000..fc019e2
--- /dev/null
+++ b/apps/docs/app/docs/[slug]/llms.txt/route.ts
@@ -0,0 +1,31 @@
+import { hookToMarkdown } from "@/lib/hook-markdown";
+import { HOOKS, getHook } from "@/lib/hooks-registry";
+
+/* /docs//llms.txt — the machine-readable twin of every hook page: that
+ one hook rendered through the shared Markdown renderer. Prerendered for every
+ hook alongside its HTML page. */
+export function generateStaticParams() {
+ return HOOKS.map(({ slug }) => ({ slug }));
+}
+
+export async function GET(
+ _request: Request,
+ { params }: { params: Promise<{ slug: string }> },
+): Promise {
+ const { slug } = await params;
+ const hook = getHook(slug);
+
+ if (!hook) {
+ return new Response("Not found\n", {
+ status: 404,
+ headers: { "Content-Type": "text/plain; charset=utf-8" },
+ });
+ }
+
+ return new Response(`${hookToMarkdown(hook)}\n`, {
+ headers: {
+ "Content-Type": "text/plain; charset=utf-8",
+ "Cache-Control": "public, max-age=0, must-revalidate",
+ },
+ });
+}
diff --git a/apps/docs/app/docs/page.tsx b/apps/docs/app/docs/page.tsx
index 16b5458..3d2c79c 100644
--- a/apps/docs/app/docs/page.tsx
+++ b/apps/docs/app/docs/page.tsx
@@ -1,6 +1,7 @@
import type { Metadata } from "next";
import { CodeBlock } from "@/components/code-block";
import { HookCard } from "@/components/hook-card";
+import { ArrowRightIcon } from "@/components/icons";
import { InstallCommand } from "@/components/install-command";
import {
CATEGORY_LABELS,
@@ -46,6 +47,39 @@ export default function DocsIndexPage() {
can try it before you install it.
+
+
+
Building with an LLM?
+
+ Every hook — signatures, usage and source — as plain Markdown for AI
+ assistants and coding agents, generated from this site so it never
+ drifts.
+
@@ -66,6 +75,7 @@ export default function Home() {
+
diff --git a/apps/docs/app/robots.ts b/apps/docs/app/robots.ts
index 77a1b9b..31e4bd3 100644
--- a/apps/docs/app/robots.ts
+++ b/apps/docs/app/robots.ts
@@ -3,7 +3,9 @@ import { SITE_URL } from "@/lib/site";
export default function robots(): MetadataRoute.Robots {
return {
- rules: { userAgent: "*", allow: "/" },
+ // The llms.txt files are explicitly surfaced (already covered by "/", but
+ // listing them advertises the AI-friendly index and full dump to crawlers).
+ rules: { userAgent: "*", allow: ["/", "/llms.txt", "/llms-full.txt"] },
sitemap: `${SITE_URL}/sitemap.xml`,
};
}
diff --git a/apps/docs/app/sitemap.ts b/apps/docs/app/sitemap.ts
index c64f69f..3af12d8 100644
--- a/apps/docs/app/sitemap.ts
+++ b/apps/docs/app/sitemap.ts
@@ -24,5 +24,17 @@ export default function sitemap(): MetadataRoute.Sitemap {
changeFrequency: "yearly",
priority: 0.4,
},
+ {
+ url: `${SITE_URL}/llms.txt`,
+ lastModified,
+ changeFrequency: "monthly",
+ priority: 0.3,
+ },
+ {
+ url: `${SITE_URL}/llms-full.txt`,
+ lastModified,
+ changeFrequency: "monthly",
+ priority: 0.3,
+ },
];
}
diff --git a/apps/docs/components/ai-section.tsx b/apps/docs/components/ai-section.tsx
new file mode 100644
index 0000000..f92d534
--- /dev/null
+++ b/apps/docs/components/ai-section.tsx
@@ -0,0 +1,116 @@
+import { CopyButton } from "@/components/copy-button";
+import { SectionHeading } from "@/components/section-heading";
+import { BracesIcon, CopyIcon, ExternalLinkIcon } from "@/components/icons";
+import { buildLlmsIndex } from "@/lib/llms";
+import { HOOKS } from "@/lib/hooks-registry";
+import { SITE_URL } from "@/lib/site";
+
+/* Landing section that surfaces the AI-friendly surfaces (llms.txt, per-hook
+ Copy-for-AI, Open-in-ChatGPT/Claude) so visitors know the docs are built to
+ be read by a model, not just a human. Server component: the /llms.txt body
+ is built once from the registry and handed to the client CopyButton leaf. */
+
+const CARDS = [
+ {
+ Icon: BracesIcon,
+ title: "One file, the whole library",
+ body: (
+ <>
+ Point any model at{" "}
+ /llms.txt for the map, or{" "}
+ /llms-full.txt for every signature,
+ usage and source — generated from the same registry the docs use.
+ >
+ ),
+ },
+ {
+ Icon: CopyIcon,
+ title: "Copy any hook for AI",
+ body: (
+ <>
+ Every hook page has a one-click Copy for AI —
+ clean Markdown, ready to paste into your chat and start building.
+ >
+ ),
+ },
+ {
+ Icon: ExternalLinkIcon,
+ title: "Straight into your chat",
+ body: (
+ <>
+ Jump into ChatGPT or Claude prefilled with the hook's context and a
+ live docs link. Your AI pair is in the loop from line one.
+ >
+ ),
+ },
+];
+
+export function AiSection() {
+ const llmsIndex = buildLlmsIndex();
+ const prompt = `I'm using hookli, a zero-dependency, typed, SSR-safe React hooks library (${HOOKS.length} hooks). Full reference: ${SITE_URL}/llms-full.txt — help me pick and use the right hooks for my app.`;
+ const chatgptUrl = `https://chatgpt.com/?q=${encodeURIComponent(prompt)}`;
+ const claudeUrl = `https://claude.ai/new?q=${encodeURIComponent(prompt)}`;
+ const linkClass =
+ "inline-flex min-h-11 items-center gap-1.5 text-sm text-gray-body underline-offset-4 transition-colors duration-200 hover:text-fg hover:underline";
+
+ return (
+
+
+ Vibe-coding with an LLM? hookli ships a machine-readable map of all{" "}
+ {HOOKS.length} hooks, so your assistant recommends and wires them up
+ correctly — no stale copy-paste.
+ >
+ }
+ />
+
+