From 136120dbd2d3c9cbdee123cacb6e1cd791320602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Az=C3=B3car?= Date: Tue, 5 May 2026 14:31:43 -0400 Subject: [PATCH 1/3] feat: add useLlmsAlternate composable for per-page markdown discovery Emits two RFC 8288 hints so AI agents can discover the markdown counterpart of an HTML page without parsing the body: - in - Link: <...>; rel="alternate"; type="text/markdown" HTTP header Composable accepts a string, ref, or getter; falsy values are ignored so it is safe to call before async data resolves. Pairs naturally with @nuxt/content's /raw/.md endpoint but works with any markdown URL the consumer chooses to advertise. --- README.md | 27 ++++++++++++++ playground/pages/[...slug].vue | 5 +++ src/module.ts | 7 +++- src/runtime/composables/useLlmsAlternate.ts | 41 +++++++++++++++++++++ test/alternate.test.ts | 23 ++++++++++++ test/fixtures/alternate/app.vue | 7 ++++ test/fixtures/alternate/nuxt.config.ts | 8 ++++ test/fixtures/alternate/package.json | 5 +++ 8 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 src/runtime/composables/useLlmsAlternate.ts create mode 100644 test/alternate.test.ts create mode 100644 test/fixtures/alternate/app.vue create mode 100644 test/fixtures/alternate/nuxt.config.ts create mode 100644 test/fixtures/alternate/package.json diff --git a/README.md b/README.md index a0579a5..73b9301 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ Nuxt LLMs automatically generates [`llms.txt` markdown documentation](https://ll - Generates & prerenders `/llms.txt` automatically - Generate & prerenders `/llms-full.txt` when enabled - Customizable sections directly from your `nuxt.config.ts` +- `useLlmsAlternate` composable to advertise per-page markdown alternates (RFC 8288) - Integrates with Nuxt modules and your application via the runtime hooks system ## Quick Setup @@ -120,6 +121,32 @@ export default defineNuxtConfig({ }) ``` +## Per-page markdown discovery + +`/llms.txt` advertises your site at the global level, but agents crawling individual pages have no standard way to know that a markdown counterpart exists. The `useLlmsAlternate` composable solves this by emitting two discovery hints per [RFC 8288](https://www.rfc-editor.org/rfc/rfc8288): + +- `` in the document `` +- `Link: <...>; rel="alternate"; type="text/markdown"` HTTP response header + +Agents can read the `Link` header from a `HEAD` request — without parsing HTML — and switch to the markdown URL for ingestion. + +```vue + + +``` + +The composable accepts a string, a `ref`, or a getter. Falsy values are ignored, so it is safe to call before async data resolves: + +```ts +const { data: post } = await useAsyncData(/* ... */) +useLlmsAlternate(() => post.value?.markdownUrl) +``` + +This pairs naturally with `@nuxt/content`'s `/raw/.md` endpoint, but works with any markdown URL — including endpoints you serve yourself. + ## Extending the documentation using hooks The module provides a hooks system that allows you to dynamically extend both documentation formats. There are two main hooks: diff --git a/playground/pages/[...slug].vue b/playground/pages/[...slug].vue index bdfde44..2fcc54b 100644 --- a/playground/pages/[...slug].vue +++ b/playground/pages/[...slug].vue @@ -4,6 +4,11 @@ const route = useRoute() const { data } = await useAsyncData(() => 'posts' + route.path, async () => { return await queryCollection('content').path(route.path).first() }) + +useLlmsAlternate(() => { + const path = data.value?.path + return path && path !== '/' ? `/raw${path}.md` : null +})