Skip to content

feat: embed docs assistant#153

Merged
superdav42 merged 1 commit into
mainfrom
issue-197-docs-assistant
Jul 6, 2026
Merged

feat: embed docs assistant#153
superdav42 merged 1 commit into
mainfrom
issue-197-docs-assistant

Conversation

@superdav42

@superdav42 superdav42 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Summary

  • embeds the Superdav AI Agent public chat widget on the Docusaurus docs site
  • generates JSONL manifests for the WordPress knowledge importer during docs deploy
  • deploys the generated manifests with the static docs build under /ai/

Verification

  • node scripts/build-ai-docs-manifests.js
  • npm run build -- --locale en

For superdav42/tgc.church#197.


aidevops.sh v3.31.73 plugin for OpenCode v1.17.13

Summary by CodeRabbit

  • New Features

    • Added an embedded assistant experience to the site, including automatic theme and language matching.
    • Expanded documentation publishing to include AI-ready content manifests.
  • Chores

    • Updated deployment setup to generate the new documentation artifacts during the docs build.
    • Ignored generated AI manifest files in version control.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This 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.

Changes

AI Docs Manifest Generation

Layer / File(s) Summary
Manifest script core logic
scripts/build-ai-docs-manifests.js
New script scans docs/, parses frontmatter, derives routes/titles, flags code references, and writes two JSONL manifest files.
Workflow and ignore-file wiring
.github/workflows/deploy-docs.yml, .gitignore
Deploy workflow runs the manifest script during doc preparation; generated static/ai/*.jsonl files are gitignored.

Assistant Widget Injection

Layer / File(s) Summary
Root component and injection helpers
src/theme/Root.js
New Root component injects an assistant script and stylesheet into the page on mount, configured via theme/locale data attributes, with idempotency checks.

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
Loading
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
Loading

Possibly related PRs

  • Ultimate-Multisite/docs#137: Extends the same prepare-docs-source workflow job/artifact structure that this PR's manifest-build step is added to.

Suggested labels: review-feedback-scanned, origin:interactive, status:in-review

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: embedding a docs assistant widget.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-197-docs-assistant

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (6)
src/theme/Root.js (1)

32-33: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Theme captured once won't track color-mode toggles.

document.documentElement.dataset.theme is read a single time on mount, but Docusaurus updates data-theme on <html> client-side (no reload) when the user toggles light/dark. The injected widget's data-theme will 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 updating script.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 win

Raw content still includes the frontmatter block.

content (Line 101, embedded at Line 118) is the raw file content, so every record's content field includes the leading ---\n...\n---\n frontmatter 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-extracted title field.

♻️ 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 win

Hand-rolled frontmatter parser is fragile.

extractFrontmatter only handles simple key: value lines 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-matter or js-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-matter as 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

route field is misleading and duplicates url.

record.route is set to a full public URL via publicUrlFor(route) (Line 109), but record.metadata.route holds 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.route and record.url (Line 110) are identical, making one field redundant.

Consider renaming the top-level route field (e.g. to url, dropping the duplicate) or renaming metadata.route to metadata.slug for 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 value

No error handling around file reads/writes.

recordFor (Line 101) and writeJsonl (Lines 122-128) call fs.readFileSync/fs.writeFileSync with no try/catch. A single unreadable/binary-misnamed .md file 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 win

Derive publicBaseUrl from the Docusaurus config

This matches the current url + baseUrl, but hardcoding it creates a second source of truth and will drift if either value changes. Pull it from docusaurus.config.js instead.

🤖 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

📥 Commits

Reviewing files that changed from the base of the PR and between 37d392c and 17da45f.

📒 Files selected for processing (4)
  • .github/workflows/deploy-docs.yml
  • .gitignore
  • scripts/build-ai-docs-manifests.js
  • src/theme/Root.js

@superdav42 superdav42 merged commit 0726a72 into main Jul 6, 2026
5 checks passed
@superdav42 superdav42 deleted the issue-197-docs-assistant branch July 6, 2026 18:28
@superdav42 superdav42 added the review-feedback-scanned Merged PR already scanned for quality feedback label Jul 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review-feedback-scanned Merged PR already scanned for quality feedback

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant