Skip to main content

docs

Generate a documentation site from your ADRs and specs. Use when the user says "generate docs", "create a docs site", or wants to publish their architecture decisions.

Governing Artifacts

ADR-0016 · SPEC-0014

Usage

/sdd:docs [project name or options] [--module <name>]
Required Tools
  • Bash
  • Read
  • Write
  • Edit
  • Glob
  • Grep
  • Task
  • AskUserQuestion

Overview

Harness portability. This skill runs on any agent harness that loads Agent Skills — Claude Code, Codex CLI, OpenCode, Crush. Tool names used below (AskUserQuestion, Task, TeamCreate, SendMessage, TaskCreate, ToolSearch, mcp__*, $\{CLAUDE_PLUGIN_ROOT\}) denote capabilities, not hard requirements: map each to your harness's equivalent or use the documented fallback per $\{CLAUDE_PLUGIN_ROOT\}/references/harness-compat.md. References to CLAUDE.md mean the project memory file (CLAUDE.md, AGENTS.md, or CRUSH.md) per harness-compat § "Project Memory File".

Transform ADRs and OpenSpec specs (located via the Artifact Path Resolution pattern from $\{CLAUDE_PLUGIN_ROOT\}/references/shared-patterns.md) into a polished documentation website with:

  • RFC 2119 keyword highlighting (MUST, SHALL, MAY, etc.)
  • ADR cross-reference linking (📝 ADR-0001 becomes a clickable link)
  • SPEC cross-reference linking (PREFIX-NNN links to spec requirement anchors)
  • Status/Date/Domain badge components
  • Requirement box components for spec tables
  • Consequence keyword highlighting (Good/Bad/Neutral) in ADRs
  • Dark mode support
  • Auto-generated sidebars

Supports two modes:

  • Scaffold mode: Creates a standalone docs-site/ with its own Docusaurus installation
  • Integration mode: Generates a build-time plugin into an existing Docusaurus site

Process

Step 0: Resolve Artifact Paths

<!-- Governing: ADR-0016 (Workspace Mode), SPEC-0014 REQ "Artifact Path Resolution" -->

Follow the Artifact Path Resolution pattern from $\{CLAUDE_PLUGIN_ROOT\}/references/shared-patterns.md to determine the ADR and spec directories. If $ARGUMENTS contains --module &lt;name>, resolve paths relative to that module; otherwise, in a workspace, aggregate across all modules. The resolved ADR directory is \{adr-dir\} and spec directory is \{spec-dir\}.

<!-- Governing: ADR-0016 (Workspace Mode), SPEC-0014 REQ "Cross-Module Aggregation" -->

Cross-module aggregation: When in aggregate mode (no --module, workspace detected), include all modules' artifacts in the docs site. Organize the sidebar navigation by module:

Architecture/
├── api/
│ ├── ADRs/
│ │ ├── ADR-0001: Choose REST over GraphQL
│ │ └── ADR-0002: Choose PostgreSQL
│ └── Specs/
│ └── SPEC-0001: Web Dashboard
├── worker/
│ ├── ADRs/
│ │ └── ADR-0001: Choose Redis for queues
│ └── Specs/
│ └── SPEC-0001: Job Processing
└── Overview (cross-module index page)

Each module's artifacts are transformed independently and placed under a module-named directory in the docs output. The index page lists all modules with artifact counts. When --module is provided, generate docs for that single module only (flat structure, no module subdirectory). When in single-module mode (no workspace), operate normally with the existing flat structure.

Step 1: Pre-flight Checks

  • Check if Node.js is installed. If not, tell the user: "Node.js is required to run the docs site. Please install it from https://nodejs.org/ and re-run this command." and stop.
  • Check if \{adr-dir\} has any ADR .md files
  • Check if \{spec-dir\} has any spec directories (containing spec.md). Validate spec pairing per $\{CLAUDE_PLUGIN_ROOT\}/references/shared-patterns.md § "Spec Pairing Validation".
  • If NEITHER has content, tell the user: "No ADRs or specs found. Create some first with /sdd:adr or /sdd:spec, then re-run /sdd:docs." and stop.
  • If only one has content, proceed but note which is empty (e.g., "No specs found yet -- the docs site will only include ADRs for now.")

Step 2: Detect Existing Docusaurus Site and Upgrade State

2.1: Check for upgrade manifest

Check if .sdd-docs.json exists at the project root.

If .sdd-docs.json exists:

  • Read and parse the manifest
  • Check if the siteDir referenced in the manifest still exists on disk
    • If siteDir exists → enter Upgrade Mode (Step 3C). Skip Steps 2.2 and 2.3.
    • If siteDir is missing → warn the user: "Found .sdd-docs.json but the site directory \{siteDir\} no longer exists." Use AskUserQuestion to offer:
      • "Re-scaffold a new docs site" → proceed with Scaffold Mode (Step 3A)
      • "Cancel" → stop

If .sdd-docs.json does NOT exist, continue to Step 2.2.

2.2: Scan for existing Docusaurus sites

Scan the project root for directories containing docusaurus.config.ts or docusaurus.config.js:

find . -maxdepth 2 -name 'docusaurus.config.*' -not -path './docs-site/*' -not -path './node_modules/*' 2>/dev/null
2.3: Choose mode

If an existing docs site directory is detected (docs-site/ exists or an integration site was found in 2.2) but no .sdd-docs.json:

  • Warn: "Upgrade tracking unavailable — .sdd-docs.json not found."
  • Use AskUserQuestion to offer:
    • "Create manifest from current state" → compute SHA-256 checksums of all managed files in the existing site, write .sdd-docs.json using the current state as baseline, then enter Upgrade Mode (Step 3C)
    • "Continue without upgrade tracking" → proceed to mode selection below
    • "Cancel" → stop

If an existing non-scaffold Docusaurus site is found (from Step 2.2), use AskUserQuestion to let the user choose:

  • Option A: "Integrate into {directory}" -- proceed with Integration Mode (Step 3B)
  • Option B: "Scaffold a new docs site" -- proceed with Scaffold Mode (Step 3A)

If no existing site is found, proceed directly with Scaffold Mode (Step 3A).


Step 3A: Scaffold Mode

Read and follow the plugin's skills/docs/references/scaffold-mode.md for the full scaffold workflow. After completion, proceed to Step 4 below.


Step 3B: Integration Mode

Read and follow the plugin's skills/docs/references/integration-mode.md for the full integration workflow. After completion, proceed to Step 4 below.


Step 3C: Upgrade Mode

Read and follow the plugin's skills/docs/references/upgrade-mode.md for the full upgrade workflow. This handles manifest-based file management, conflict resolution, and new template detection.


Step 4: Create Manifest

Runs after Step 3A or 3B to establish upgrade tracking.

Determine managed files based on mode:

  • Scaffold: files in docs-site/plugins/sdd-content/, docs-site/src/components/, docs-site/src/css/, docs-site/src/theme/
  • Integration: files in \{site\}/plugins/sync-spec-docs/, \{site\}/src/components/design-docs/, \{site\}/src/css/design-docs.css, \{site\}/src/theme/MDXComponents.tsx (if created/modified)

Compute SHA-256 checksum for each file (shasum -a 256 \{file-path\}), then write .sdd-docs.json:

{
"version": "<plugin version from .claude-plugin/plugin.json>",
"mode": "scaffold" | "integration",
"siteDir": "<relative path to site dir>",
"createdAt": "<ISO 8601>",
"updatedAt": "<ISO 8601>",
"files": {
"<relative-path>": { "checksum": "sha256:<hex-digest>", "managed": true }
}
}

Tell the user: "Created .sdd-docs.json with {N} tracked files. Future runs of /sdd:docs will detect changes and offer upgrades."


Rules

  • Always read templates from the plugin directory, don't recreate from memory
  • Configure the Docusaurus site for the current project (title, URLs, etc.)
  • The transform scripts must work with the project's actual directory structure
  • Don't include OpenAPI plugin config unless the project has an OpenAPI spec
  • Keep spec-emojis.json and spec-mapping.json as generated files (populated by build-spec-mapping.js)
  • In integration mode, NEVER overwrite the existing site's docusaurus.config.ts wholesale -- only add the plugin entry and CSS import
  • In integration mode, ALWAYS namespace components under design-docs/ to avoid collisions with existing components
  • In integration mode, generated files go to \{site\}/docs/architecture/ -- this directory is gitignored and regenerated on every build
  • Always create .sdd-docs.json after a fresh scaffold or integration install (Step 4)
  • Never delete or skip manifest creation -- it is required for upgrade tracking
  • During upgrades (Step 3C), always ask before overwriting user-modified files
  • The manifest files object uses project-root-relative paths as keys
  • Checksum format is always sha256:&lt;hex-digest> (lowercase hex)
  • When creating a manifest from an existing site (Step 2.3 "Create manifest from current state"), set all files to managed: true and use their current checksums as the baseline
  • In workspace aggregate mode, MUST organize docs navigation by module with per-module subdirectories (Governing: 📝 ADR-0016, SPEC-0014 REQ "Cross-Module Aggregation")
  • In workspace aggregate mode, MUST generate a cross-module index page listing all modules with artifact counts
  • In workspace aggregate mode, transform scripts run per-module with output directed to module-named subdirectories
  • When --module is provided, generate docs for that single module only using flat structure

Key Template Files Reference

Scaffold Mode Templates (templates/docusaurus/)

The templates directory contains production-ready versions of all files. The cp -r approach copies everything; you only need to customize docusaurus.config.ts and package.json.

Docusaurus Plugin (plugins/sdd-content/)
  • index.js -- Consolidated Docusaurus plugin that:
    • Uses lib-artifact-transforms for YAML frontmatter parsing (replaces ~120 lines of custom YAML)
    • Builds the artifact graph per SPEC-0018 edge schema with 5 ADR edge types + 4 spec edge types
    • Generates MDX files from ADRs and specs with badges, RFC 2119 keyword highlighting, cross-references (Governing: 📝 ADR-0006, SPEC-0004)
    • Generates index pages (landing page, ADR section, spec section) with sidebar hierarchy diagrams
    • Generates the artifact graph page with stats, full Mermaid flowchart, and orphan lists
    • Implements getPathsToWatch() for native Docusaurus hot reload (replaces chokidar-cli + concurrently)

Integration Mode Templates (templates/integration/sync-spec-docs/)

A self-contained Docusaurus plugin with adapted transform scripts.

Plugin Entry
  • index.js -- Docusaurus plugin that runs transforms during loadContent() and watches source files via getPathsToWatch()
Transform Scripts (lib/)
  • transform-adrs.js -- ADR transforms with parameterized paths
  • transform-openspecs.js -- OpenSpec transforms with parameterized paths
  • transform-utils.js -- Shared utilities (RFC 2119 keywords, cross-references, link fixing)
  • mdx-escape.js -- MDX v3 safety escaping
  • build-spec-mapping.js -- Spec ID mapping (returns data instead of writing files)
  • generate-index.js -- Index page generation with parameterized paths

Shared: React Components (templates/docusaurus/src/components/)

Used by both modes. In scaffold mode, they live at docs-site/src/components/. In integration mode, they're copied to \{site\}/src/components/design-docs/.

  • StatusBadge.tsx -- Status with emoji (accepted, proposed, draft, etc.)
  • DateBadge.tsx -- Date display with calendar emoji
  • DomainBadge.tsx -- Domain/category badge
  • PriorityBadge.tsx -- P0-P4 priority levels
  • SeverityBadge.tsx -- Critical/High/Medium/Low/Info
  • RFCLevelBadge.tsx -- Maps RFC 2119 keywords to severity colors
  • RequirementBox.tsx -- Bordered container for spec requirements with ID anchors
  • Field.tsx / FieldGroup.tsx -- Metadata label-value pairs

Shared: Theme and CSS (templates/docusaurus/src/)

  • src/theme/MDXComponents.tsx -- Registers all custom components for use in MDX
  • src/css/custom.css -- All badge, keyword, component, and dark mode styles

Reference

integration-mode.md

Generates a build-time Docusaurus plugin into an existing site. The plugin runs the same transforms as scaffold mode but writes output into the existing site's docs tree.

Let \{site\} be the path to the existing Docusaurus site directory (e.g., website/).

3B.1: Install dependencies and copy the plugin

First, install lib-artifact-transforms in the site's package.json:

cd {site} && npm install lib-artifact-transforms@github:joestump/lib-artifact-transforms#main

Then copy the integration plugin template from the plugin's templates/integration/sync-spec-docs/ directory to \{site\}/plugins/sync-spec-docs/:

cp -r {plugin-path}/templates/integration/sync-spec-docs {site}/plugins/sync-spec-docs

If \{site\}/plugins/sync-spec-docs/ already exists, ask the user before overwriting.

3B.2: Copy React components

Copy all component files from \{plugin-path\}/templates/docusaurus/src/components/ to \{site\}/src/components/design-docs/:

mkdir -p {site}/src/components/design-docs
cp {plugin-path}/templates/docusaurus/src/components/*.tsx {site}/src/components/design-docs/

3B.3: Add CSS styles

Read \{plugin-path\}/templates/docusaurus/src/css/custom.css and create \{site\}/src/css/design-docs.css containing ONLY the design-specific styles. Exclude the :root \{ ... \} and [data-theme='dark'] \{ ... \} CSS variable blocks at the top of the file (the ones that set --ifm-color-primary-* and --docusaurus-highlighted-code-line-bg). These are Docusaurus theme colors that would override the existing site's color scheme.

Include everything from the /* Badge Components */ comment onwards.

Then add the CSS import. Check how the site loads custom CSS:

  • If the site's docusaurus.config.ts has a customCss option, add './src/css/design-docs.css' as an additional entry (it can be an array)
  • Otherwise, add @import './design-docs.css'; at the top of the site's existing custom CSS file

3B.4: Register MDX components

Check if \{site\}/src/theme/MDXComponents.tsx exists:

If it does NOT exist, create it:

import React from 'react';
import MDXComponents from '@theme-original/MDXComponents';
import DateBadge from '@site/src/components/design-docs/DateBadge';
import DomainBadge from '@site/src/components/design-docs/DomainBadge';
import PriorityBadge from '@site/src/components/design-docs/PriorityBadge';
import SeverityBadge from '@site/src/components/design-docs/SeverityBadge';
import StatusBadge from '@site/src/components/design-docs/StatusBadge';
import RFCLevelBadge from '@site/src/components/design-docs/RFCLevelBadge';
import RequirementBox from '@site/src/components/design-docs/RequirementBox';
import Field from '@site/src/components/design-docs/Field';
import FieldGroup from '@site/src/components/design-docs/FieldGroup';

export default {
...MDXComponents,
DateBadge,
DomainBadge,
PriorityBadge,
SeverityBadge,
StatusBadge,
RFCLevelBadge,
RequirementBox,
Field,
FieldGroup,
};

If it DOES exist, read the existing file and merge the design-docs imports: add import lines for each component from @site/src/components/design-docs/, add component names to the default export, and preserve all existing imports and registrations.

3B.5: Register the plugin in docusaurus.config

Read \{site\}/docusaurus.config.ts (or .js) and add the sync plugin to the plugins array. Determine the correct projectRoot by computing the relative path from \{site\} to the project root.

Example -- if the site is at website/ and the project root is ..:

plugins: [
['./plugins/sync-spec-docs', {
projectRoot: '..',
docsPath: 'docs', // adjust to match the site's existing docs path
}],
],

To find the existing docs path, look for the path option in the classic preset's docs config. If not specified, Docusaurus defaults to 'docs'.

If the config already has a plugins array, append to it. If not, add a new plugins property.

3B.6: Add .gitignore entries

Check for .gitignore at the project root. Add entries for the generated output directory:

# Design docs (generated by sync-spec-docs plugin)
{site}/docs/architecture/

3B.7: Run initial build and verify

cd {site} && npx docusaurus build 2>&1 | tail -20

If the build fails, diagnose and fix. Common problems:

  • Missing dependencies: run npm install in the site directory
  • MDXComponents merge conflict: check the merge was done correctly
  • Plugin path error: verify the relative projectRoot is correct

3B.8: Update .claudeignore

Add entries to ignore the site's build artifacts:

{site}/node_modules/
{site}/build/
{site}/.docusaurus/

3B.9: Report results

Tell the user what was created:

  • Plugin installed at \{site\}/plugins/sync-spec-docs/
  • Components installed at \{site\}/src/components/design-docs/
  • CSS added at \{site\}/src/css/design-docs.css
  • Generated docs will appear at \{site\}/docs/architecture/
  • The plugin auto-syncs when ADRs or specs change during npm run start

Then ask: "Integration complete! Want me to start the dev server? (cd \{site\} && npm run start)"

After completion, proceed to Step 4: Create Manifest (back in SKILL.md).

scaffold-mode.md

Creates a standalone Docusaurus site at docs-site/.

  1. Check for existing docs-site: Look for docs-site/ in the project root. If it exists, ask the user before overwriting.

  2. Copy the plugin's Docusaurus templates using cp -r from the plugin's templates/docusaurus/ directory to docs-site/ in the project root.

  3. Customize for the project by reading and modifying only these files:

    • docs-site/package.json -- update the project name from $ARGUMENTS or inferred from the repo
    • docs-site/docusaurus.config.ts -- update title, baseUrl, and GitHub URL for this project
    • Optionally customize the sdd-content plugin options if your ADRs/specs are in non-standard locations:
      • adrsDir (default: ../docs/adrs)
      • specsDir (default: ../docs/openspec/specs)
      • outputDir (default: ../docs-generated)
  4. Run npm install in the docs-site directory.

  5. Update .claudeignore: Check if .claudeignore exists at the project root. If not, create it. Add entries to ignore:

    docs-site/node_modules/
    docs-site/build/
    docs-site/.docusaurus/

    If .claudeignore already exists, append any missing entries.

  6. Report and offer to start: Tell the user what was created, then ask: "Docs site created! Want me to start the dev server? (cd docs-site && npm run start)"

After completion, proceed to Step 4: Create Manifest (back in SKILL.md).

upgrade-mode.md

Entered when .sdd-docs.json exists and the referenced siteDir is present on disk. Updates an existing docs installation to the latest plugin templates while preserving user customizations.

Read the manifest from .sdd-docs.json. Let \{mode\} be the manifest's mode field ("scaffold" or "integration"), and \{site\} be the resolved siteDir.

3C.1: Determine template source paths

Based on the manifest's mode:

  • Scaffold: template root is \{plugin-path\}/templates/docusaurus/
  • Integration: template root is \{plugin-path\}/templates/integration/sync-spec-docs/ for plugin files, and \{plugin-path\}/templates/docusaurus/src/components/ for shared components

3C.2: Process each managed file

For each entry in the manifest's files object where managed is true:

  1. Compute the current SHA-256 of the file on disk
  2. Compare against the manifest's stored checksum:
    • Checksums match (file unmodified by user) → replace silently with the new template version
    • Checksums differ (user has modified the file) → use AskUserQuestion:
      • "Accept new version" → overwrite with the template version
      • "Keep current" → leave as-is, update manifest checksum to current hash
      • "Opt out of management" → set managed: false in manifest (skip in future upgrades)
    • File missing from disk → re-create from the template

For entries where managed is false, skip entirely.

3C.2b: Pre-plugin migration detection (Scaffold mode only)

If scaffold mode AND \{site\}/scripts/ exists BUT \{site\}/plugins/ does NOT exist:

This is a pre-plugin installation (using the old 8-script approach). Offer the user:

  • "Migrate to new plugin" → delete \{site\}/scripts/, \{site\}/src/data/spec-mapping.json, \{site\}/src/data/spec-emojis.json (if present); install the new plugins/sdd-content/index.js; update package.json (remove chokidar-cli, concurrently, build-content/watch-content scripts); update docusaurus.config.ts to register the plugin
  • "Keep current setup" → do not migrate; set all managed files to managed: false

3C.3: Detect new template files

Check for files in the current plugin templates that are NOT listed in the manifest:

  • For scaffold: scan templates/docusaurus/plugins/sdd-content/, templates/docusaurus/src/components/, templates/docusaurus/src/css/, templates/docusaurus/src/theme/
  • For integration: scan templates/integration/sync-spec-docs/, templates/docusaurus/src/components/

For each new file: install it to the appropriate location and add to the manifest with managed: true and its SHA-256 checksum.

3C.4: Update the manifest

Write the updated .sdd-docs.json:

  • Set version to the current plugin version from .claude-plugin/plugin.json
  • Set updatedAt to the current ISO timestamp
  • Update all checksum values to reflect the current on-disk state
  • Preserve createdAt and mode from the original manifest

3C.5: Ensure .claudeignore exists

Check if .claudeignore includes ignore entries for \{site\}/node_modules/, \{site\}/build/, and \{site\}/.docusaurus/. If any are missing, append them.

3C.6: Run build and verify

  • For scaffold: run npm install in \{site\} if package.json changed, then offer to start the dev server
  • For integration: run a Docusaurus build to verify the plugin still works

3C.7: Report results

Tell the user:

  • Files updated silently (checksum matched)
  • Files with conflicts and what the user chose for each
  • New files added
  • Files skipped (managed: false)
  • New plugin version recorded in the manifest

Example Invocations

generate the docs site from our ADRs and specs
/sdd:docs
scaffold a Docusaurus site that mirrors our design artifacts
rebuild the documentation site with the latest ADRs
set up a docs site so we can publish our architecture decisions