From 89cbae61457d5dbc1a462d1b9ad2b0a43db75475 Mon Sep 17 00:00:00 2001 From: Shannon Anahata Date: Wed, 13 May 2026 16:31:30 -0700 Subject: [PATCH 1/7] Added new script (generate-llms-txt.mjs) to generate an llms.txt file that will be available publicly on the docs site. Create an new SKILL.md file that references our sentry-for-ai repo and our skills.sentry.dev site, and points to our AGENTS.md and other skill pages within the repo. --- .gitignore | 1 + AGENTS.md | 11 ++ SKILL.md | 33 ++++++ package.json | 3 +- scripts/generate-llms-txt.mjs | 186 ++++++++++++++++++++++++++++++++++ 5 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 SKILL.md create mode 100644 scripts/generate-llms-txt.mjs diff --git a/.gitignore b/.gitignore index 8763df8b47941..b51a5640d2c94 100644 --- a/.gitignore +++ b/.gitignore @@ -100,6 +100,7 @@ public/og-images/* yalc.lock /public/doctree.json /public/doctree-dev.json +/public/llms.txt # Lychee cache .lycheecache diff --git a/AGENTS.md b/AGENTS.md index cdfde89f3df8c..7143aa0bb48cb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -85,6 +85,17 @@ When writing requirements in `develop-docs/`: - MDX override templates live in `md-overrides/` - Architecture spec: `specs/llm-friendly-docs.md` +## llms.txt + +- `docs.sentry.io/llms.txt` is the root LLM discovery index (follows [llmstxt.org](https://llmstxt.org) spec) +- Auto-generated from `public/doctree.json` by `scripts/generate-llms-txt.mjs` +- Run `pnpm generate-llms-txt` to regenerate, or it runs automatically as part of `pnpm build` +- Includes: product description, AI agent instructions, platform/section index, links to agent skills at `skills.sentry.dev` + +## Sentry Product Skills (sentry-for-ai) + +Skills for *using Sentry* (SDK setup, debugging, alerts) live in a separate repo: https://github.com/getsentry/sentry-for-ai. The skills in *this* repo (`.claude/skills/`, `.agents/skills/`) are for *contributing to the docs*. See `SKILL.md` at the repo root for a routing guide. + ## Plan Mode - Make the plan extremely concise. Sacrifice grammar for the sake of concision. diff --git a/SKILL.md b/SKILL.md new file mode 100644 index 0000000000000..6e54d90bb29e5 --- /dev/null +++ b/SKILL.md @@ -0,0 +1,33 @@ +--- +name: sentry-docs +description: Sentry documentation site (docs.sentry.io). Use for contributing to docs or understanding docs architecture. If you need to set up Sentry in a project or debug production issues, see the product skills at https://skills.sentry.dev. +--- + +# Sentry Documentation Repository + +This repo builds [docs.sentry.io](https://docs.sentry.io). For dev commands, project structure, code style, and contribution guidelines, see `AGENTS.md`. + +## Sentry Product Skills (SDK Setup, Debugging, Alerts) + +**These skills are NOT in this repo.** If you need to set up Sentry, debug production issues, or configure features, use the product skills: + +- **Full skill index**: https://skills.sentry.dev +- **SDK setup** (detect platform and install): https://skills.sentry.dev/sdks +- **Debugging workflows** (fix issues, review code): https://skills.sentry.dev/workflows +- **Feature setup** (AI monitoring, alerts, OTel): https://skills.sentry.dev/features + +Install as a plugin: +- Claude Code: `/install-plugin sentry` +- Cursor: Search "Sentry" in Cursor Settings > Plugins + +Source repo: https://github.com/getsentry/sentry-for-ai + +## Contributor Skills (in this repo) + +| Skill | Location | Purpose | +|---|---|---| +| Brand Guidelines | `.agents/skills/brand-guidelines/` | Audit content against Sentry brand voice | +| Docs Review | `.claude/skills/docs-review/SKILL.md` | Sentry documentation style guide | +| Technical Docs | `.claude/skills/technical-docs/SKILL.md` | Writing SDK and technical documentation | +| Commit | `.agents/skills/commit/SKILL.md` | Sentry conventional commit format | +| Create Branch | `.agents/skills/create-branch/SKILL.md` | Branch naming conventions | diff --git a/package.json b/package.json index 2d2c9b37618c6..2ea4ce8cf5652 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,9 @@ "dev": "pnpm enforce-redirects && concurrently \"pnpm sidecar\" \"node ./src/hotReloadWatcher.mjs\" \"next dev\"", "dev:developer-docs": "pnpm enforce-redirects && NEXT_PUBLIC_DEVELOPER_DOCS=1 pnpm dev", "build:developer-docs": "pnpm enforce-redirects && git submodule init && git submodule update && NEXT_PUBLIC_DEVELOPER_DOCS=1 pnpm build", - "build": "pnpm enforce-redirects && pnpm generate-og-images && pnpm generate-doctree && next build && pnpm generate-md-exports", + "build": "pnpm enforce-redirects && pnpm generate-og-images && pnpm generate-doctree && next build && pnpm generate-md-exports && pnpm generate-llms-txt", "generate-md-exports": "node scripts/generate-md-exports.mjs", + "generate-llms-txt": "node scripts/generate-llms-txt.mjs", "generate-og-images": "ts-node scripts/add-og-images.ts", "generate-doctree": "esbuild scripts/generate-doctree.ts --bundle --platform=node --packages=external --outfile=.next/generate-doctree.mjs --format=esm && node .next/generate-doctree.mjs", "vercel:build:developer-docs": "pnpm enforce-redirects && git submodule init && git submodule update && NEXT_PUBLIC_DEVELOPER_DOCS=1 pnpm build", diff --git a/scripts/generate-llms-txt.mjs b/scripts/generate-llms-txt.mjs new file mode 100644 index 0000000000000..186f30159cf32 --- /dev/null +++ b/scripts/generate-llms-txt.mjs @@ -0,0 +1,186 @@ +#!/usr/bin/env node +/* eslint-disable no-console */ +/** + * Generates /llms.txt from public/doctree.json. + * + * Run after `generate-doctree` (doctree.json must exist). + * Output: public/llms.txt + * + * Follows the llmstxt.org specification: + * - H1 with project name + * - Blockquote with summary + * - Prose sections (instructions for AI agents) + * - H2-delimited file lists with links to .md exports + */ + +import {readFile, writeFile} from 'node:fs/promises'; +import * as path from 'node:path'; +import {fileURLToPath} from 'node:url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ROOT = path.resolve(__dirname, '..'); +const DOCS_ORIGIN = 'https://docs.sentry.io'; + +// --- Doctree helpers (same logic as generate-md-exports.mjs) --- + +function getTitle(node) { + return node.frontmatter?.sidebar_title || node.frontmatter?.title || node.slug; +} + +function isVisible(node) { + return ( + !node.frontmatter?.sidebar_hidden && + !node.frontmatter?.draft && + !node.path?.includes('__v') && + (node.frontmatter?.title || node.frontmatter?.sidebar_title) + ); +} + +function getVisibleChildren(node) { + return (node.children || []) + .filter(isVisible) + .sort((a, b) => { + const orderDiff = + (a.frontmatter?.sidebar_order ?? 99) - (b.frontmatter?.sidebar_order ?? 99); + return orderDiff !== 0 ? orderDiff : getTitle(a).localeCompare(getTitle(b)); + }); +} + +function mdUrl(nodePath) { + // nodePath is like "platforms/javascript" (no leading slash) — produce absolute .md URL + const clean = nodePath.replace(/\/$/, ''); + if (!clean) { + return `${DOCS_ORIGIN}/index.md`; + } + return `${DOCS_ORIGIN}/${clean}.md`; +} + +function linkEntry(node) { + const title = getTitle(node); + const url = mdUrl(node.path); + const desc = node.frontmatter?.description; + if (desc) { + // Collapse to single line, trim + const oneLine = desc.replace(/\s+/g, ' ').trim(); + return `- [${title}](${url}): ${oneLine}`; + } + return `- [${title}](${url})`; +} + +// --- Build platform section with framework grouping --- + +function buildPlatformsSection(platformsNode) { + const lines = []; + const platforms = getVisibleChildren(platformsNode); + + for (const platform of platforms) { + const title = getTitle(platform); + const url = mdUrl(platform.path); + const guides = platform.children?.find(c => c.slug === 'guides'); + const visibleGuides = guides ? getVisibleChildren(guides) : []; + + if (visibleGuides.length > 0) { + const guideNames = visibleGuides.map(g => getTitle(g)).join(', '); + lines.push(`- [${title}](${url}): Frameworks: ${guideNames}`); + } else { + lines.push(`- [${title}](${url})`); + } + } + + return lines.join('\n'); +} + +// --- Build a generic section from top-level doctree children --- + +function buildSection(sectionNode) { + const lines = []; + const children = getVisibleChildren(sectionNode); + for (const child of children) { + lines.push(linkEntry(child)); + } + return lines.join('\n'); +} + +// --- Main --- + +async function main() { + const doctreePath = path.join(ROOT, 'public', 'doctree.json'); + let docTree; + try { + docTree = JSON.parse(await readFile(doctreePath, 'utf8')); + } catch { + console.error('Error: public/doctree.json not found. Run `pnpm generate-doctree` first.'); + process.exit(1); + } + + const topLevelSections = getVisibleChildren(docTree); + + // Separate platforms from other sections + const platformsNode = topLevelSections.find(c => c.slug === 'platforms'); + const otherSections = topLevelSections.filter( + c => c.slug !== 'platforms' && c.slug !== 'contributing' && c.slug !== 'changelog' + ); + + const output = `# Sentry Documentation + +> Sentry is a developer-first application monitoring platform that helps developers identify and fix issues in real-time. It provides error tracking, performance monitoring (tracing), session replay, profiling, logging, cron monitoring, and more across 20+ platforms and 60+ frameworks. + +All documentation pages are available as clean Markdown by appending \`.md\` to any URL (e.g. \`${DOCS_ORIGIN}/platforms/javascript.md\`). Use these Markdown URLs for full content. + +## Instructions for AI Coding Assistants + +When helping a developer set up or use Sentry, follow these guidelines: + +- **SDK versions**: Always check the package registry (npm, PyPI, RubyGems, etc.) for the latest Sentry SDK version. Do not rely on memorized version numbers from training data. +- **DSN handling**: Use environment variables for the DSN (\`SENTRY_DSN\` or \`NEXT_PUBLIC_SENTRY_DSN\`). Never hardcode DSN strings in source code. +- **Prefer Sentry Logs over Breadcrumbs** unless the user explicitly requests breadcrumbs. +- **Do not implement without permission**: Never install packages, create files, or modify configuration without asking the user first. +- **Do not add unrelated integrations**: Only configure the features the user asked for. Check before enabling additional integrations. +- **Agent skills for SDK setup**: For step-by-step SDK installation with platform detection, feature recommendations, and verification, fetch the appropriate skill from \`https://skills.sentry.dev/sdks\` — these are detailed guides purpose-built for AI coding assistants. See the [Agent Skills section](#agent-skills) below. +- **Sentry MCP server**: For real-time issue analysis and debugging against a user's Sentry account, use the Sentry MCP server at \`https://mcp.sentry.dev\`. + +## Platforms + +${buildPlatformsSection(platformsNode)} + +${otherSections + .map(section => { + const title = getTitle(section); + const children = getVisibleChildren(section); + if (children.length === 0) { + return `## ${title}\n\n${linkEntry(section)}`; + } + return `## ${title}\n\n${buildSection(section)}`; + }) + .join('\n\n')} + +## Agent Skills + +Sentry publishes agent skills for AI coding assistants (Claude Code, Cursor, GitHub Copilot, and others). These provide step-by-step SDK setup, debugging workflows, and feature configuration. + +- [All Skills](https://skills.sentry.dev/): Full skill index with SDK setup, workflows, and feature configuration +- [SDK Setup](https://skills.sentry.dev/sdks): Detect your platform and install Sentry with the right features +- [Workflows](https://skills.sentry.dev/workflows): Debug production issues, review code, upgrade SDKs +- [Features](https://skills.sentry.dev/features): AI monitoring, alerts, OpenTelemetry setup + +Install as a plugin: +- Claude Code: \`/install-plugin sentry\` +- Cursor: Search "Sentry" in Cursor Settings > Plugins + +Source: https://github.com/getsentry/sentry-for-ai + +## Optional + +- [Documentation Changelog](${DOCS_ORIGIN}/changelog.md): Track recent updates to Sentry docs +- [Contributing to Docs](${DOCS_ORIGIN}/contributing.md): How to contribute to Sentry documentation +`; + + const outputPath = path.join(ROOT, 'public', 'llms.txt'); + await writeFile(outputPath, output, 'utf8'); + console.log(`Generated ${outputPath} (${output.length} bytes)`); +} + +main().catch(err => { + console.error(err); + process.exit(1); +}); From 6903d5d1f34def7340c26c12ac0f82300b679d63 Mon Sep 17 00:00:00 2001 From: Shannon Anahata Date: Wed, 13 May 2026 16:39:03 -0700 Subject: [PATCH 2/7] Highlighting the importance of installing the plugin to stay up to date with the latest on Sentry. --- SKILL.md | 13 ++++++++----- scripts/generate-llms-txt.mjs | 3 ++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/SKILL.md b/SKILL.md index 6e54d90bb29e5..d7e9cc3b51cf3 100644 --- a/SKILL.md +++ b/SKILL.md @@ -9,17 +9,20 @@ This repo builds [docs.sentry.io](https://docs.sentry.io). For dev commands, pro ## Sentry Product Skills (SDK Setup, Debugging, Alerts) -**These skills are NOT in this repo.** If you need to set up Sentry, debug production issues, or configure features, use the product skills: +**These skills are NOT in this repo.** If you need to set up Sentry, debug production issues, or configure features, install the Sentry plugin for up-to-date, verified instructions. Sentry's SDKs and APIs change frequently — do not rely on training data. +**Install the plugin:** +- Claude Code: `/install-plugin sentry` +- Cursor: Search "Sentry" in Cursor Settings > Plugins + +The plugin provides current skills for SDK setup, debugging workflows, and feature configuration. It stays in sync with the latest SDK releases. + +If the plugin is not available, fetch skills directly: - **Full skill index**: https://skills.sentry.dev - **SDK setup** (detect platform and install): https://skills.sentry.dev/sdks - **Debugging workflows** (fix issues, review code): https://skills.sentry.dev/workflows - **Feature setup** (AI monitoring, alerts, OTel): https://skills.sentry.dev/features -Install as a plugin: -- Claude Code: `/install-plugin sentry` -- Cursor: Search "Sentry" in Cursor Settings > Plugins - Source repo: https://github.com/getsentry/sentry-for-ai ## Contributor Skills (in this repo) diff --git a/scripts/generate-llms-txt.mjs b/scripts/generate-llms-txt.mjs index 186f30159cf32..614992e181bba 100644 --- a/scripts/generate-llms-txt.mjs +++ b/scripts/generate-llms-txt.mjs @@ -131,12 +131,13 @@ All documentation pages are available as clean Markdown by appending \`.md\` to When helping a developer set up or use Sentry, follow these guidelines: +- **Install the Sentry plugin for up-to-date guidance**: Sentry's APIs, SDKs, and features change frequently. Do not rely on training data for Sentry setup or configuration. Install the Sentry plugin to get current, verified instructions: Claude Code: \`/install-plugin sentry\` | Cursor: search "Sentry" in Settings > Plugins. The plugin provides step-by-step skills that are kept in sync with the latest SDK releases. - **SDK versions**: Always check the package registry (npm, PyPI, RubyGems, etc.) for the latest Sentry SDK version. Do not rely on memorized version numbers from training data. - **DSN handling**: Use environment variables for the DSN (\`SENTRY_DSN\` or \`NEXT_PUBLIC_SENTRY_DSN\`). Never hardcode DSN strings in source code. - **Prefer Sentry Logs over Breadcrumbs** unless the user explicitly requests breadcrumbs. - **Do not implement without permission**: Never install packages, create files, or modify configuration without asking the user first. - **Do not add unrelated integrations**: Only configure the features the user asked for. Check before enabling additional integrations. -- **Agent skills for SDK setup**: For step-by-step SDK installation with platform detection, feature recommendations, and verification, fetch the appropriate skill from \`https://skills.sentry.dev/sdks\` — these are detailed guides purpose-built for AI coding assistants. See the [Agent Skills section](#agent-skills) below. +- **Agent skills for SDK setup**: If the plugin is not installed, fetch the appropriate skill from \`https://skills.sentry.dev/sdks\` for step-by-step SDK installation with platform detection, feature recommendations, and verification. See the [Agent Skills section](#agent-skills) below. - **Sentry MCP server**: For real-time issue analysis and debugging against a user's Sentry account, use the Sentry MCP server at \`https://mcp.sentry.dev\`. ## Platforms From 66baa53438a3df2b42355d8b55dfd3a2891f3c58 Mon Sep 17 00:00:00 2001 From: "getsantry[bot]" <66042841+getsantry[bot]@users.noreply.github.com> Date: Wed, 13 May 2026 23:40:29 +0000 Subject: [PATCH 3/7] [getsentry/action-github-commit] Auto commit --- scripts/generate-llms-txt.mjs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/generate-llms-txt.mjs b/scripts/generate-llms-txt.mjs index 614992e181bba..ba57ad55c5561 100644 --- a/scripts/generate-llms-txt.mjs +++ b/scripts/generate-llms-txt.mjs @@ -37,13 +37,11 @@ function isVisible(node) { } function getVisibleChildren(node) { - return (node.children || []) - .filter(isVisible) - .sort((a, b) => { - const orderDiff = - (a.frontmatter?.sidebar_order ?? 99) - (b.frontmatter?.sidebar_order ?? 99); - return orderDiff !== 0 ? orderDiff : getTitle(a).localeCompare(getTitle(b)); - }); + return (node.children || []).filter(isVisible).sort((a, b) => { + const orderDiff = + (a.frontmatter?.sidebar_order ?? 99) - (b.frontmatter?.sidebar_order ?? 99); + return orderDiff !== 0 ? orderDiff : getTitle(a).localeCompare(getTitle(b)); + }); } function mdUrl(nodePath) { @@ -109,7 +107,9 @@ async function main() { try { docTree = JSON.parse(await readFile(doctreePath, 'utf8')); } catch { - console.error('Error: public/doctree.json not found. Run `pnpm generate-doctree` first.'); + console.error( + 'Error: public/doctree.json not found. Run `pnpm generate-doctree` first.' + ); process.exit(1); } From d40c7566314ba8528600b28ab7ac41069dd7ea92 Mon Sep 17 00:00:00 2001 From: Shannon Anahata Date: Thu, 14 May 2026 11:44:26 -0700 Subject: [PATCH 4/7] feat(llms): unify llms.txt and index.md generation into single script Rewrote generate-llms-txt.mjs to produce both public/llms.txt and public/md-exports/index.md from the same content blocks and doctree walk. Removes md-overrides/index.mdx which had stale hardcoded content (Key Features list, product description) that drifted from the live site. Both files now share the same description, AI instructions, agent skills, and section listings. The only difference is platform/framework formatting: llms.txt uses compact inline format, index.md uses the expanded separate-sections layout. --- md-overrides/index.mdx | 31 ---- scripts/generate-llms-txt.mjs | 258 +++++++++++++++++++++++----------- 2 files changed, 179 insertions(+), 110 deletions(-) delete mode 100644 md-overrides/index.mdx diff --git a/md-overrides/index.mdx b/md-overrides/index.mdx deleted file mode 100644 index 0062471e2dd45..0000000000000 --- a/md-overrides/index.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: "Sentry Documentation" -append_sections: false ---- - -Sentry is a developer-first application monitoring platform that helps you identify and fix issues in real-time. It provides error tracking, performance monitoring, session replay, and more across all major platforms and frameworks. - -## Key Features - -- **Error Monitoring**: Capture and diagnose errors with full stack traces, breadcrumbs, and context -- **Tracing**: Track requests across services to identify performance bottlenecks -- **Session Replay**: Watch real user sessions to understand what led to errors -- **Profiling**: Identify slow functions and optimize application performance -- **Crons**: Monitor scheduled jobs and detect failures -- **Logs**: Collect and analyze application logs in context - -## Platforms - - - -## Frameworks - - - - - -## Quick Links - -- [Platform SDKs](/platforms) - Install Sentry for your language/framework -- [API Reference](/api) - Programmatic access to Sentry -- [CLI](/cli) - Command-line interface for Sentry operations diff --git a/scripts/generate-llms-txt.mjs b/scripts/generate-llms-txt.mjs index ba57ad55c5561..b84e059a6f4e1 100644 --- a/scripts/generate-llms-txt.mjs +++ b/scripts/generate-llms-txt.mjs @@ -1,19 +1,25 @@ #!/usr/bin/env node -/* eslint-disable no-console */ + /** - * Generates /llms.txt from public/doctree.json. + * Generates /llms.txt and /index.md from public/doctree.json. + * + * Both files share the same content (product description, AI instructions, + * section listings, agent skills). The only difference is how platforms and + * frameworks are formatted: + * - llms.txt: compact (frameworks listed inline per platform) + * - index.md: expanded (separate Platforms and Frameworks sections) * * Run after `generate-doctree` (doctree.json must exist). - * Output: public/llms.txt + * Output: + * - public/llms.txt + * - public/md-exports/index.md (overwrites md-exports pipeline output) * - * Follows the llmstxt.org specification: - * - H1 with project name - * - Blockquote with summary - * - Prose sections (instructions for AI agents) - * - H2-delimited file lists with links to .md exports + * llms.txt follows the llmstxt.org specification. + * index.md includes YAML frontmatter matching the md-exports convention. */ -import {readFile, writeFile} from 'node:fs/promises'; +import {existsSync} from 'node:fs'; +import {mkdir, readFile, writeFile} from 'node:fs/promises'; import * as path from 'node:path'; import {fileURLToPath} from 'node:url'; @@ -45,7 +51,6 @@ function getVisibleChildren(node) { } function mdUrl(nodePath) { - // nodePath is like "platforms/javascript" (no leading slash) — produce absolute .md URL const clean = nodePath.replace(/\/$/, ''); if (!clean) { return `${DOCS_ORIGIN}/index.md`; @@ -58,20 +63,18 @@ function linkEntry(node) { const url = mdUrl(node.path); const desc = node.frontmatter?.description; if (desc) { - // Collapse to single line, trim const oneLine = desc.replace(/\s+/g, ' ').trim(); return `- [${title}](${url}): ${oneLine}`; } return `- [${title}](${url})`; } -// --- Build platform section with framework grouping --- +// --- Platform/framework formatters --- -function buildPlatformsSection(platformsNode) { +/** llms.txt format: compact, frameworks inline per platform */ +function buildPlatformsCompact(platformsNode) { const lines = []; - const platforms = getVisibleChildren(platformsNode); - - for (const platform of platforms) { + for (const platform of getVisibleChildren(platformsNode)) { const title = getTitle(platform); const url = mdUrl(platform.path); const guides = platform.children?.find(c => c.slug === 'guides'); @@ -84,80 +87,93 @@ function buildPlatformsSection(platformsNode) { lines.push(`- [${title}](${url})`); } } - - return lines.join('\n'); + return `## Platforms\n\n${lines.join('\n')}`; } -// --- Build a generic section from top-level doctree children --- - -function buildSection(sectionNode) { +/** index.md format: separate Platforms list + Frameworks grouped by platform */ +function buildPlatformsExpanded(platformsNode) { + const platforms = getVisibleChildren(platformsNode); const lines = []; - const children = getVisibleChildren(sectionNode); - for (const child of children) { - lines.push(linkEntry(child)); - } - return lines.join('\n'); -} -// --- Main --- + // Flat platform list + lines.push('## Platforms\n'); + for (const platform of platforms) { + lines.push(`- [${getTitle(platform)}](${mdUrl(platform.path)})`); + } -async function main() { - const doctreePath = path.join(ROOT, 'public', 'doctree.json'); - let docTree; - try { - docTree = JSON.parse(await readFile(doctreePath, 'utf8')); - } catch { - console.error( - 'Error: public/doctree.json not found. Run `pnpm generate-doctree` first.' - ); - process.exit(1); + // Frameworks grouped by platform + lines.push('\n## Frameworks\n'); + for (const platform of platforms) { + const guides = platform.children?.find(c => c.slug === 'guides'); + const visibleGuides = guides ? getVisibleChildren(guides) : []; + if (visibleGuides.length === 0) { + continue; + } + lines.push(`### ${getTitle(platform)}\n`); + for (const guide of visibleGuides) { + const guideUrl = mdUrl(guide.path); + lines.push(`- [${getTitle(guide)}](${guideUrl})`); + } + lines.push(''); } - const topLevelSections = getVisibleChildren(docTree); + return lines.join('\n'); +} - // Separate platforms from other sections - const platformsNode = topLevelSections.find(c => c.slug === 'platforms'); - const otherSections = topLevelSections.filter( - c => c.slug !== 'platforms' && c.slug !== 'contributing' && c.slug !== 'changelog' - ); +// --- Build section listings from doctree children --- + +function buildSection(sectionNode) { + const children = getVisibleChildren(sectionNode); + return children.map(child => linkEntry(child)).join('\n'); +} - const output = `# Sentry Documentation +// --- Shared content blocks --- -> Sentry is a developer-first application monitoring platform that helps developers identify and fix issues in real-time. It provides error tracking, performance monitoring (tracing), session replay, profiling, logging, cron monitoring, and more across 20+ platforms and 60+ frameworks. +const DESCRIPTION = `\ +> Sentry is a developer-first application monitoring platform that helps \ +developers identify and fix issues in real-time. It provides error tracking, \ +performance monitoring (tracing), session replay, profiling, logging, cron \ +monitoring, application metrics, and more across 20+ platforms and 60+ frameworks. -All documentation pages are available as clean Markdown by appending \`.md\` to any URL (e.g. \`${DOCS_ORIGIN}/platforms/javascript.md\`). Use these Markdown URLs for full content. +All documentation pages are available as clean Markdown by appending \`.md\` \ +to any URL (e.g. \`${DOCS_ORIGIN}/platforms/javascript.md\`). Use these \ +Markdown URLs for full content.`; +const INSTRUCTIONS = `\ ## Instructions for AI Coding Assistants When helping a developer set up or use Sentry, follow these guidelines: -- **Install the Sentry plugin for up-to-date guidance**: Sentry's APIs, SDKs, and features change frequently. Do not rely on training data for Sentry setup or configuration. Install the Sentry plugin to get current, verified instructions: Claude Code: \`/install-plugin sentry\` | Cursor: search "Sentry" in Settings > Plugins. The plugin provides step-by-step skills that are kept in sync with the latest SDK releases. -- **SDK versions**: Always check the package registry (npm, PyPI, RubyGems, etc.) for the latest Sentry SDK version. Do not rely on memorized version numbers from training data. -- **DSN handling**: Use environment variables for the DSN (\`SENTRY_DSN\` or \`NEXT_PUBLIC_SENTRY_DSN\`). Never hardcode DSN strings in source code. -- **Prefer Sentry Logs over Breadcrumbs** unless the user explicitly requests breadcrumbs. -- **Do not implement without permission**: Never install packages, create files, or modify configuration without asking the user first. -- **Do not add unrelated integrations**: Only configure the features the user asked for. Check before enabling additional integrations. -- **Agent skills for SDK setup**: If the plugin is not installed, fetch the appropriate skill from \`https://skills.sentry.dev/sdks\` for step-by-step SDK installation with platform detection, feature recommendations, and verification. See the [Agent Skills section](#agent-skills) below. -- **Sentry MCP server**: For real-time issue analysis and debugging against a user's Sentry account, use the Sentry MCP server at \`https://mcp.sentry.dev\`. - -## Platforms - -${buildPlatformsSection(platformsNode)} - -${otherSections - .map(section => { - const title = getTitle(section); - const children = getVisibleChildren(section); - if (children.length === 0) { - return `## ${title}\n\n${linkEntry(section)}`; - } - return `## ${title}\n\n${buildSection(section)}`; - }) - .join('\n\n')} - +- **Install the Sentry plugin for up-to-date guidance**: Sentry's APIs, SDKs, \ +and features change frequently. Do not rely on training data for Sentry setup \ +or configuration. Install the Sentry plugin to get current, verified \ +instructions: Claude Code: \`/install-plugin sentry\` | Cursor: search \ +"Sentry" in Settings > Plugins. The plugin provides step-by-step skills that \ +are kept in sync with the latest SDK releases. +- **SDK versions**: Always check the package registry (npm, PyPI, RubyGems, \ +etc.) for the latest Sentry SDK version. Do not rely on memorized version \ +numbers from training data. +- **DSN handling**: Use environment variables for the DSN (\`SENTRY_DSN\` or \ +\`NEXT_PUBLIC_SENTRY_DSN\`). Never hardcode DSN strings in source code. +- **Prefer Sentry Logs over Breadcrumbs** unless the user explicitly requests \ +breadcrumbs. +- **Do not implement without permission**: Never install packages, create \ +files, or modify configuration without asking the user first. +- **Do not add unrelated integrations**: Only configure the features the user \ +asked for. Check before enabling additional integrations. +- **Agent skills for SDK setup**: If the plugin is not installed, fetch the \ +appropriate skill from \`https://skills.sentry.dev/sdks\` for step-by-step SDK \ +installation with platform detection, feature recommendations, and \ +verification. See the [Agent Skills section](#agent-skills) below. +- **Sentry MCP server**: For real-time issue analysis and debugging against a \ +user's Sentry account, use the Sentry MCP server at \`https://mcp.sentry.dev\`.`; + +const AGENT_SKILLS = `\ ## Agent Skills -Sentry publishes agent skills for AI coding assistants (Claude Code, Cursor, GitHub Copilot, and others). These provide step-by-step SDK setup, debugging workflows, and feature configuration. +Sentry publishes agent skills for AI coding assistants (Claude Code, Cursor, \ +GitHub Copilot, and others). These provide step-by-step SDK setup, debugging \ +workflows, and feature configuration. - [All Skills](https://skills.sentry.dev/): Full skill index with SDK setup, workflows, and feature configuration - [SDK Setup](https://skills.sentry.dev/sdks): Detect your platform and install Sentry with the right features @@ -168,17 +184,101 @@ Install as a plugin: - Claude Code: \`/install-plugin sentry\` - Cursor: Search "Sentry" in Cursor Settings > Plugins -Source: https://github.com/getsentry/sentry-for-ai +Source: https://github.com/getsentry/sentry-for-ai`; -## Optional +const ABOUT_SENTRY_DOCS = `\ +## About Sentry Docs - [Documentation Changelog](${DOCS_ORIGIN}/changelog.md): Track recent updates to Sentry docs -- [Contributing to Docs](${DOCS_ORIGIN}/contributing.md): How to contribute to Sentry documentation -`; +- [Contributing to Docs](${DOCS_ORIGIN}/contributing.md): How to contribute to Sentry documentation`; + +// --- Main --- + +async function main() { + const doctreePath = path.join(ROOT, 'public', 'doctree.json'); + let docTree; + try { + docTree = JSON.parse(await readFile(doctreePath, 'utf8')); + } catch { + console.error( + 'Error: public/doctree.json not found. Run `pnpm generate-doctree` first.' + ); + process.exit(1); + } + + const topLevelSections = getVisibleChildren(docTree); + const platformsNode = topLevelSections.find(c => c.slug === 'platforms'); + const otherSections = topLevelSections.filter( + c => c.slug !== 'platforms' && c.slug !== 'contributing' && c.slug !== 'changelog' + ); - const outputPath = path.join(ROOT, 'public', 'llms.txt'); - await writeFile(outputPath, output, 'utf8'); - console.log(`Generated ${outputPath} (${output.length} bytes)`); + // Build the shared section listings (everything except platforms) + const sectionListings = otherSections + .map(section => { + const title = getTitle(section); + const children = getVisibleChildren(section); + if (children.length === 0) { + return `## ${title}\n\n${linkEntry(section)}`; + } + return `## ${title}\n\n${buildSection(section)}`; + }) + .join('\n\n'); + + // --- llms.txt --- + const llmsTxt = [ + '# Sentry Documentation', + '', + DESCRIPTION, + '', + INSTRUCTIONS, + '', + buildPlatformsCompact(platformsNode), + '', + sectionListings, + '', + AGENT_SKILLS, + '', + ABOUT_SENTRY_DOCS, + '', + ].join('\n'); + + const llmsTxtPath = path.join(ROOT, 'public', 'llms.txt'); + await writeFile(llmsTxtPath, llmsTxt, 'utf8'); + console.log(`Generated ${llmsTxtPath} (${llmsTxt.length} bytes)`); + + // --- index.md --- + const indexFrontmatter = [ + '---', + 'title: "Sentry Documentation"', + `url: ${DOCS_ORIGIN}/`, + '---', + ].join('\n'); + + const indexMd = [ + indexFrontmatter, + '', + '# Sentry Documentation', + '', + DESCRIPTION, + '', + INSTRUCTIONS, + '', + buildPlatformsExpanded(platformsNode), + sectionListings, + '', + AGENT_SKILLS, + '', + ABOUT_SENTRY_DOCS, + '', + ].join('\n'); + + const mdExportsDir = path.join(ROOT, 'public', 'md-exports'); + if (!existsSync(mdExportsDir)) { + await mkdir(mdExportsDir, {recursive: true}); + } + const indexMdPath = path.join(mdExportsDir, 'index.md'); + await writeFile(indexMdPath, indexMd, 'utf8'); + console.log(`Generated ${indexMdPath} (${indexMd.length} bytes)`); } main().catch(err => { From 76ca3ebe45fab2d655603926912449fe389172f6 Mon Sep 17 00:00:00 2001 From: "getsantry[bot]" <66042841+getsantry[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 18:45:53 +0000 Subject: [PATCH 5/7] [getsentry/action-github-commit] Auto commit --- scripts/generate-llms-txt.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/generate-llms-txt.mjs b/scripts/generate-llms-txt.mjs index b84e059a6f4e1..2ad12f9f27c42 100644 --- a/scripts/generate-llms-txt.mjs +++ b/scripts/generate-llms-txt.mjs @@ -1,5 +1,5 @@ #!/usr/bin/env node - + /** * Generates /llms.txt and /index.md from public/doctree.json. * From b6c1d3bbf1f11a8fa1213f1bf1c81388b388a2ea Mon Sep 17 00:00:00 2001 From: Shannon Anahata Date: Thu, 14 May 2026 11:48:15 -0700 Subject: [PATCH 6/7] fix(llms): support developer-docs build by reading correct doctree file The developer-docs build (NEXT_PUBLIC_DEVELOPER_DOCS=1) produces doctree-dev.json instead of doctree.json. Match the pattern used by generate-md-exports.mjs to select the right filename and origin URL. --- scripts/generate-llms-txt.mjs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/scripts/generate-llms-txt.mjs b/scripts/generate-llms-txt.mjs index 2ad12f9f27c42..a26e98b0566a3 100644 --- a/scripts/generate-llms-txt.mjs +++ b/scripts/generate-llms-txt.mjs @@ -25,7 +25,9 @@ import {fileURLToPath} from 'node:url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const ROOT = path.resolve(__dirname, '..'); -const DOCS_ORIGIN = 'https://docs.sentry.io'; +const DOCS_ORIGIN = process.env.NEXT_PUBLIC_DEVELOPER_DOCS + ? 'https://develop.sentry.dev' + : 'https://docs.sentry.io'; // --- Doctree helpers (same logic as generate-md-exports.mjs) --- @@ -195,7 +197,10 @@ const ABOUT_SENTRY_DOCS = `\ // --- Main --- async function main() { - const doctreePath = path.join(ROOT, 'public', 'doctree.json'); + const doctreeFilename = process.env.NEXT_PUBLIC_DEVELOPER_DOCS + ? 'doctree-dev.json' + : 'doctree.json'; + const doctreePath = path.join(ROOT, 'public', doctreeFilename); let docTree; try { docTree = JSON.parse(await readFile(doctreePath, 'utf8')); From 6e35de49be58dfb7ff026d5c74860fee604e46ef Mon Sep 17 00:00:00 2001 From: Shannon Anahata Date: Thu, 14 May 2026 11:52:33 -0700 Subject: [PATCH 7/7] fix(llms): guard against undefined platformsNode Add null check to buildPlatformsCompact and buildPlatformsExpanded to prevent TypeError if the platforms node is missing or filtered out by isVisible. --- scripts/generate-llms-txt.mjs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/generate-llms-txt.mjs b/scripts/generate-llms-txt.mjs index a26e98b0566a3..001eee2b3aca1 100644 --- a/scripts/generate-llms-txt.mjs +++ b/scripts/generate-llms-txt.mjs @@ -75,6 +75,9 @@ function linkEntry(node) { /** llms.txt format: compact, frameworks inline per platform */ function buildPlatformsCompact(platformsNode) { + if (!platformsNode) { + return '## Platforms\n'; + } const lines = []; for (const platform of getVisibleChildren(platformsNode)) { const title = getTitle(platform); @@ -94,6 +97,9 @@ function buildPlatformsCompact(platformsNode) { /** index.md format: separate Platforms list + Frameworks grouped by platform */ function buildPlatformsExpanded(platformsNode) { + if (!platformsNode) { + return '## Platforms\n'; + } const platforms = getVisibleChildren(platformsNode); const lines = [];