feat: embed docs assistant#153
Conversation
📝 WalkthroughWalkthroughThis PR adds a script that generates JSONL manifests of documentation content (with frontmatter, routes, titles, and code-reference flags) for AI assistant consumption, wires it into the docs deploy workflow, gitignores its output, and adds a Root theme component injecting an assistant widget script/stylesheet based on document theme/locale. ChangesAI Docs Manifest Generation
Assistant Widget Injection
Estimated code review effort: 2 (Simple) | ~15 minutes Sequence Diagram(s)sequenceDiagram
participant DeployWorkflow as Deploy Workflow
participant ManifestScript as build-ai-docs-manifests.js
participant DocsDir as docs/ directory
participant StaticAI as static/ai/*.jsonl
DeployWorkflow->>ManifestScript: run node script
ManifestScript->>DocsDir: scan .md/.mdx files
ManifestScript->>ManifestScript: parse frontmatter, derive route/title/code_reference
ManifestScript->>StaticAI: write ums-docs-manifest.jsonl
ManifestScript->>StaticAI: write ums-code-reference-manifest.jsonl
ManifestScript-->>DeployWorkflow: log counts and paths
sequenceDiagram
participant Browser
participant RootComponent as Root Component
participant DOM as Document DOM
participant AssistantScript as Assistant Script (external)
Browser->>RootComponent: mount
RootComponent->>DOM: check for existing stylesheet link
RootComponent->>DOM: append stylesheet link if missing
RootComponent->>DOM: read theme/locale from document
RootComponent->>DOM: append assistant script with data-* attributes
DOM->>AssistantScript: load and initialize widget
Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (6)
src/theme/Root.js (1)
32-33: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueTheme captured once won't track color-mode toggles.
document.documentElement.dataset.themeis read a single time on mount, but Docusaurus updatesdata-themeon<html>client-side (no reload) when the user toggles light/dark. The injected widget'sdata-themewill therefore go stale after a toggle. Locale is fine since switching locales triggers a full navigation. If the widget can pick up theme changes at runtime, consider observing the attribute and updatingscript.dataset.theme(or re-syncing the widget) accordingly.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/theme/Root.js` around lines 32 - 33, The widget theme is only initialized once in Root.js from document.documentElement.dataset.theme, so it can drift after client-side light/dark toggles. Update the Root component’s theme wiring so script.dataset.theme is kept in sync with html data-theme changes at runtime, for example by observing that attribute and reapplying the current theme to the injected widget; keep the locale handling as-is since it only changes on navigation.scripts/build-ai-docs-manifests.js (5)
99-120: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winRaw
contentstill includes the frontmatter block.
content(Line 101, embedded at Line 118) is the raw file content, so every record'scontentfield includes the leading---\n...\n---\nfrontmatter block rather than clean document body text. For an AI knowledge base this pollutes the ingested text with metadata noise (title/sidebar_position/etc. lines) that duplicates the already-extractedtitlefield.♻️ Suggested fix to strip frontmatter from content
function recordFor(filePath) { const relativePath = path.relative(docsDir, filePath).replace(/\\/g, '/'); const content = fs.readFileSync(filePath, 'utf8'); const frontmatter = extractFrontmatter(content); const route = routeFor(relativePath); + const body = content.replace(/^---\n[\s\S]*?\n---\n/, ''); return { id: `docs:${route || 'index'}`, path: relativePath, title: titleFor(content, frontmatter, relativePath), route: publicUrlFor(route), url: publicUrlFor(route), locale: 'en', metadata: { path: relativePath, route, source: 'docusaurus', code_reference: isCodeReference(relativePath), }, - content, + content: body, }; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/build-ai-docs-manifests.js` around lines 99 - 120, The recordFor() helper is storing the raw file text in content, so the extracted frontmatter block is still being ingested along with the page body. Update recordFor() to remove the frontmatter from the content field before returning the record, using the existing extractFrontmatter() flow in scripts/build-ai-docs-manifests.js, so content contains only the cleaned document body while title and metadata keep using the parsed frontmatter.
34-57: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winHand-rolled frontmatter parser is fragile.
extractFrontmatteronly handles simplekey: valuelines and will silently mis-parse multi-line values, arrays (tags: [a, b]), or nested YAML commonly used in Docusaurus frontmatter — producing incorrect/empty metadata without any error. A dedicated YAML/frontmatter parser (e.g.gray-matterorjs-yaml) handles these cases correctly and is battle-tested for this exact use case.♻️ Suggested refactor using gray-matter
-function extractFrontmatter(content) { - if (!content.startsWith('---\n')) { - return {}; - } - - const end = content.indexOf('\n---\n', 4); - if (end === -1) { - return {}; - } - - const metadata = {}; - const lines = content.slice(4, end).split('\n'); - for (const line of lines) { - const match = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/); - if (!match) { - continue; - } - - const value = match[2].trim().replace(/^['"]|['"]$/g, ''); - metadata[match[1]] = value; - } - - return metadata; -} +const matter = require('gray-matter'); + +function extractFrontmatter(content) { + return matter(content).data; +}Requires adding
gray-matteras a dependency.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/build-ai-docs-manifests.js` around lines 34 - 57, The extractFrontmatter helper is a fragile hand-rolled YAML parser that only supports flat key/value lines and can silently misread Docusaurus frontmatter; replace the parsing logic in extractFrontmatter with a dedicated frontmatter/YAML parser such as gray-matter or js-yaml so it correctly handles arrays, nested objects, and multiline values. Update the manifest-building flow that consumes extractFrontmatter to use the parsed metadata structure from the new library, and add the needed dependency so the behavior is reliable across real docs files.
99-120: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
routefield is misleading and duplicatesurl.
record.routeis set to a full public URL viapublicUrlFor(route)(Line 109), butrecord.metadata.routeholds the raw slug (Line 114) — the same field name means two different things at different levels, which is confusing for downstream consumers of the JSONL. Additionally,record.routeandrecord.url(Line 110) are identical, making one field redundant.Consider renaming the top-level
routefield (e.g. tourl, dropping the duplicate) or renamingmetadata.routetometadata.slugfor clarity.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/build-ai-docs-manifests.js` around lines 99 - 120, The `recordFor` payload currently uses `route` to mean a full public URL while `metadata.route` stores the raw slug, and `route` is also duplicated by `url`, which is confusing for downstream JSONL consumers. Update `recordFor` in `scripts/build-ai-docs-manifests.js` so the top-level field names are unambiguous: either remove the redundant top-level `route` and keep `url`, or rename `metadata.route` to `metadata.slug` (or similar) so each name has one meaning. Keep the `id`, `path`, `title`, and `metadata.code_reference` behavior intact while aligning the `publicUrlFor`, `routeFor`, and `metadata` fields consistently.
99-120: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low valueNo error handling around file reads/writes.
recordFor(Line 101) andwriteJsonl(Lines 122-128) callfs.readFileSync/fs.writeFileSyncwith no try/catch. A single unreadable/binary-misnamed.mdfile will throw an unhandled exception and abort the whole manifest build without a clear message pointing at the offending file.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/build-ai-docs-manifests.js` around lines 99 - 120, The manifest build currently has no protection around filesystem reads/writes, so a single bad docs file can abort the whole run. Add try/catch handling in recordFor and writeJsonl, using the existing helpers and symbols like recordFor, writeJsonl, fs.readFileSync, and fs.writeFileSync to catch and surface the specific file path and operation that failed. Make the error message identify the offending file clearly and continue or fail gracefully with actionable context instead of an unhandled exception.
9-9: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDerive
publicBaseUrlfrom the Docusaurus configThis matches the current
url+baseUrl, but hardcoding it creates a second source of truth and will drift if either value changes. Pull it fromdocusaurus.config.jsinstead.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/build-ai-docs-manifests.js` at line 9, The hardcoded publicBaseUrl constant in build-ai-docs-manifests.js should be derived from the Docusaurus config instead of duplicating the site URL. Update the script to read the url and baseUrl values from docusaurus.config.js (or the shared config export used by the build) and construct publicBaseUrl from those values so it stays in sync. Keep the change localized to the publicBaseUrl definition and any nearby config-loading logic in this script.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@scripts/build-ai-docs-manifests.js`:
- Around line 99-120: The recordFor() helper is storing the raw file text in
content, so the extracted frontmatter block is still being ingested along with
the page body. Update recordFor() to remove the frontmatter from the content
field before returning the record, using the existing extractFrontmatter() flow
in scripts/build-ai-docs-manifests.js, so content contains only the cleaned
document body while title and metadata keep using the parsed frontmatter.
- Around line 34-57: The extractFrontmatter helper is a fragile hand-rolled YAML
parser that only supports flat key/value lines and can silently misread
Docusaurus frontmatter; replace the parsing logic in extractFrontmatter with a
dedicated frontmatter/YAML parser such as gray-matter or js-yaml so it correctly
handles arrays, nested objects, and multiline values. Update the
manifest-building flow that consumes extractFrontmatter to use the parsed
metadata structure from the new library, and add the needed dependency so the
behavior is reliable across real docs files.
- Around line 99-120: The `recordFor` payload currently uses `route` to mean a
full public URL while `metadata.route` stores the raw slug, and `route` is also
duplicated by `url`, which is confusing for downstream JSONL consumers. Update
`recordFor` in `scripts/build-ai-docs-manifests.js` so the top-level field names
are unambiguous: either remove the redundant top-level `route` and keep `url`,
or rename `metadata.route` to `metadata.slug` (or similar) so each name has one
meaning. Keep the `id`, `path`, `title`, and `metadata.code_reference` behavior
intact while aligning the `publicUrlFor`, `routeFor`, and `metadata` fields
consistently.
- Around line 99-120: The manifest build currently has no protection around
filesystem reads/writes, so a single bad docs file can abort the whole run. Add
try/catch handling in recordFor and writeJsonl, using the existing helpers and
symbols like recordFor, writeJsonl, fs.readFileSync, and fs.writeFileSync to
catch and surface the specific file path and operation that failed. Make the
error message identify the offending file clearly and continue or fail
gracefully with actionable context instead of an unhandled exception.
- Line 9: The hardcoded publicBaseUrl constant in build-ai-docs-manifests.js
should be derived from the Docusaurus config instead of duplicating the site
URL. Update the script to read the url and baseUrl values from
docusaurus.config.js (or the shared config export used by the build) and
construct publicBaseUrl from those values so it stays in sync. Keep the change
localized to the publicBaseUrl definition and any nearby config-loading logic in
this script.
In `@src/theme/Root.js`:
- Around line 32-33: The widget theme is only initialized once in Root.js from
document.documentElement.dataset.theme, so it can drift after client-side
light/dark toggles. Update the Root component’s theme wiring so
script.dataset.theme is kept in sync with html data-theme changes at runtime,
for example by observing that attribute and reapplying the current theme to the
injected widget; keep the locale handling as-is since it only changes on
navigation.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 586f58d2-66d3-47e1-8cc1-b9f44e168c2a
📒 Files selected for processing (4)
.github/workflows/deploy-docs.yml.gitignorescripts/build-ai-docs-manifests.jssrc/theme/Root.js
Summary
/ai/Verification
node scripts/build-ai-docs-manifests.jsnpm run build -- --locale enFor superdav42/tgc.church#197.
aidevops.sh v3.31.73 plugin for OpenCode v1.17.13
Summary by CodeRabbit
New Features
Chores