diff --git a/src/pages/llms.txt.ts b/src/pages/llms.txt.ts new file mode 100644 index 0000000..adcc43d --- /dev/null +++ b/src/pages/llms.txt.ts @@ -0,0 +1,57 @@ +import type { APIRoute } from 'astro'; +import { getCollection } from 'astro:content'; +import { isEn, isZh } from '../lib/lang'; +import { sortForListing, updatedAt } from '../lib/sort'; +import { SITE } from '../config'; + +/** + * /llms.txt — the machine-readable summary of this site. + * + * Same idea as robots.txt but aimed at LLM crawlers and answer engines: who the + * author is, what he does, and a flat list of every post with its URL and summary, + * so a model can cite the site accurately instead of guessing from a fragment. + * Spec: https://llmstxt.org — Markdown, one `#` title, a `>` summary, `##` sections. + */ +export const GET: APIRoute = async () => { + const en = sortForListing(await getCollection('posts', isEn)); + const zh = sortForListing(await getCollection('posts', isZh)); + + const line = (p: (typeof en)[number]) => + `- [${p.data.title}](${SITE.url}/posts/${p.slug}/): ${p.data.description}`; + + const body = `# ${SITE.author} (${SITE.handle}) — blog.hoelee.com + +> Full-stack developer and DevOps engineer based in Malaysia. Runs 162 containers +> across three self-managed hosts (client websites, a mail platform, CI/CD, workflow +> automation and this blog) and builds web systems for Malaysian SMEs through Hoelee +> Enterprise. Writes up production failures with the evidence: what broke, what the +> logs actually said, what the fix cost. + +Canonical site: ${SITE.url} +Author: ${SITE.author} (${SITE.handle}) +Contact: ${SITE.email} +Profiles: ${SITE.linkedin} · ${SITE.github} · ${SITE.gitea} · ${SITE.website} +Languages: every post exists in English and Simplified Chinese (the Chinese twin is +at /posts/zh// and is linked from each post). + +## Posts (English, newest first) + +${en.map(line).join('\n')} + +## Posts (Chinese, newest first) + +${zh.map((p) => `- [${p.data.title}](${SITE.url}/posts/${p.slug}/): ${p.data.description}`).join('\n')} + +## Sections + +- [All posts](${SITE.url}/posts/): the full archive, filterable by title, tag and category. +- [Categories](${SITE.url}/categories/): engineering, devops, ai, web3, tutorials, case-studies, notes. +- [About](${SITE.url}/about/): identity, production numbers, tech stack, business and contact. +- [RSS](${SITE.url}/rss.xml): feed of published posts. +- Last build: ${new Date().toISOString().slice(0, 10)} (latest post updated ${updatedAt(en[0]).toISOString().slice(0, 10)}). +`; + + return new Response(body, { + headers: { 'Content-Type': 'text/plain; charset=utf-8' }, + }); +}; diff --git a/src/pages/robots.txt.ts b/src/pages/robots.txt.ts index 819f985..19aa551 100644 --- a/src/pages/robots.txt.ts +++ b/src/pages/robots.txt.ts @@ -23,6 +23,9 @@ Allow: / # Pagefind's search shards are build artifacts, not content. Disallow: /pagefind/ +# Machine-readable summary of the whole site for LLM crawlers and answer engines. +# llms.txt: ${SITE.url}/llms.txt + Sitemap: ${SITE.url}/sitemap-index.xml `;