From e265799c267af258e23d02cc7e39489a72df6ca4 Mon Sep 17 00:00:00 2001 From: "S.Gromov" Date: Mon, 27 Apr 2026 09:37:23 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20=D1=83=D1=81=D1=82=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D0=BF=D1=80=D0=BE=D0=B1=D0=BB=D0=B5=D0=BC?= =?UTF-8?q?=D1=8B=20=D0=B4=D0=BE=D1=81=D1=82=D1=83=D0=BF=D0=BD=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D0=B8=20llms.txt=20=D0=B4=D0=BB=D1=8F=20AI-=D0=B0=D0=B3?= =?UTF-8?q?=D0=B5=D0=BD=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Caddyfile: text/plain применяется только к существующим .txt/.md - Caddyfile: несуществующие .txt/.md отдают 404 вместо HTML-фолбэка - Caddyfile: добавлены редиректы /docs/llms*.txt → /llms*.txt - Caddyfile: добавлен HTTP-заголовок Link с rel="llms" (RFC 8288) - лендинг: относительные ссылки на llms.txt заменены на абсолютные - лендинг: добавлено явное упоминание путей в тексте карточки - config: добавлены на llms.txt в - generate-llms: добавлена генерация robots.txt и sitemap.xml --- .vitepress/config.ts | 7 +++++ Caddyfile | 32 ++++++++++++++++++++--- docs/index.md | 9 ++++--- generate-llms.ts | 62 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 103 insertions(+), 7 deletions(-) diff --git a/.vitepress/config.ts b/.vitepress/config.ts index 61b899f..522078c 100644 --- a/.vitepress/config.ts +++ b/.vitepress/config.ts @@ -127,6 +127,13 @@ export default defineConfig({ title: 'NextJS Style Guide', description: 'Стандарты разработки на Next.js + TypeScript с архитектурой SLM', + // Дублируем указатель на llms.txt в — для агентов, + // которые читают HTML, но не парсят полный DOM/href. + head: [ + ['link', { rel: 'alternate', type: 'text/plain', href: '/llms.txt', title: 'llms.txt' }], + ['link', { rel: 'alternate', type: 'text/plain', href: '/llms-full.txt', title: 'llms-full.txt' }], + ], + vite: { plugins: [utf8TextPlugin], define: { diff --git a/Caddyfile b/Caddyfile index 8bbac9f..e9412f9 100644 --- a/Caddyfile +++ b/Caddyfile @@ -1,10 +1,34 @@ :8080 { root * /srv - # Кириллица в .txt и .md ломается без явного charset - @text path *.txt *.md - header @text Content-Type "text/plain; charset=utf-8" + # Устаревшие пути llms.txt в подпапках → корень. + # Без этого опечатка `/docs/llms.txt` уходит в SPA-фолбэк и + # отдаёт HTML под видом text/plain — агент верит, что получил llms.txt. + redir /docs/llms.txt /llms.txt 301 + redir /docs/llms-full.txt /llms-full.txt 301 + + # Подсказка агентам, где лежит карта документации (RFC 8288). + # Позволяет найти llms.txt без парсинга DOM — по HTTP-заголовку. + header Link "; rel=\"llms\"" + + # Кириллица в .txt/.md ломается без явного charset. + # Применяем заголовок только к РЕАЛЬНО существующим файлам, + # иначе SPA-фолбэк (HTML) уезжает с Content-Type: text/plain. + @existingText { + path *.txt *.md + file + } + header @existingText Content-Type "text/plain; charset=utf-8" + + # Несуществующие .txt/.md → 404, не HTML-фолбэк. + # Это критично для llms.txt: агент должен получить честный 404, + # а не валидный «как бы текст» с лендингом внутри. + @missingText { + path *.txt *.md + not file + } + respond @missingText 404 file_server - try_files {path} /index.html + try_files {path} {path}/ /index.html } diff --git a/docs/index.md b/docs/index.md index c1046c8..1e54dfe 100644 --- a/docs/index.md +++ b/docs/index.md @@ -90,10 +90,13 @@ function toggleTheme(value) {

Ассистенту

-

Карта документации в формате llms.txt для AI-агентов.

+

+ Карта документации для AI-агентов: + /llms.txt, /llms-full.txt. +

diff --git a/generate-llms.ts b/generate-llms.ts index e197db7..9afcf9f 100644 --- a/generate-llms.ts +++ b/generate-llms.ts @@ -14,6 +14,9 @@ const PUBLIC_DIR = 'docs/public'; /** Префикс URL документации. Соответствует структуре `docs/docs/...`. */ const DOC_PREFIX = '/docs/'; +/** Канонический хост сайта (для sitemap/robots). Можно переопределить через ENV. */ +const SITE_URL = (process.env.SITE_URL || 'https://nextjs-style-guide.gromlab.ru').replace(/\/$/, ''); + interface SidebarItem { text: string; link?: string; @@ -488,6 +491,63 @@ const writeManifest = (): void => { console.log(`${PUBLIC_DIR}/manifest.json создан`); }; +/** + * Сгенерировать `robots.txt` с указанием sitemap и явными ссылками + * на llms.txt/llms-full.txt — стандартные файлы, которые читают агенты. + */ +const buildRobots = (): void => { + const lines = [ + 'User-agent: *', + 'Allow: /', + '', + `Sitemap: ${SITE_URL}/sitemap.xml`, + '', + '# Карта документации для AI-агентов:', + `# ${SITE_URL}/llms.txt`, + `# ${SITE_URL}/llms-full.txt`, + '', + ]; + fs.mkdirSync(PUBLIC_DIR, { recursive: true }); + fs.writeFileSync(path.join(PUBLIC_DIR, 'robots.txt'), lines.join('\n'), 'utf8'); + console.log(`${PUBLIC_DIR}/robots.txt создан`); +}; + +/** + * Сгенерировать `sitemap.xml` из sidebar + корневые ресурсы для LLM + * (llms.txt, llms-full.txt) — чтобы агенты, читающие sitemap, видели их. + */ +const buildSitemap = (): void => { + const sidebar = cfg.themeConfig.sidebar; + const entries = flattenSidebar(sidebar); + + const urls = new Set(); + urls.add(`${SITE_URL}/`); + urls.add(`${SITE_URL}/llms.txt`); + urls.add(`${SITE_URL}/llms-full.txt`); + + for (const entry of entries) { + const link = entry.link; + // VitePress отдаёт страницы как HTML; для index — каталог со слешем. + const url = link.endsWith('/') ? `${SITE_URL}${link}` : `${SITE_URL}${link}.html`; + urls.add(url); + } + + const today = BUILD_DATE.slice(0, 10); + const xml = [ + '', + '', + ...[...urls].map( + (loc) => ` ${loc}${today}`, + ), + '', + '', + ].join('\n'); + + fs.mkdirSync(PUBLIC_DIR, { recursive: true }); + fs.writeFileSync(path.join(PUBLIC_DIR, 'sitemap.xml'), xml, 'utf8'); + console.log(`${PUBLIC_DIR}/sitemap.xml создан`); +}; + /** Скопировать `index.md` документации в корневой README без frontmatter. */ const buildReadme = (): void => { const indexPath = 'docs/docs/index.md'; @@ -506,4 +566,6 @@ buildLlmsFull(); copyMdFiles(); buildZip(); writeManifest(); +buildRobots(); +buildSitemap(); buildReadme();