SpecScribe 0.1.1-preview.2
See the version list below for details.
dotnet tool install --global SpecScribe --version 0.1.1-preview.2
dotnet new tool-manifest
dotnet tool install --local SpecScribe --version 0.1.1-preview.2
#tool dotnet:?package=SpecScribe&version=0.1.1-preview.2&prerelease
nuke :add-package SpecScribe --version 0.1.1-preview.2
SpecScribe
SpecScribe turns spec-driven-development artifacts into a human-readable website.
Frameworks like BMad (including its GDS game-development submodule) produce a wealth of markdown artifacts — PRDs, GDDs, epics, stories, requirements inventories, and architecture decision records. Those files are written for AI agents and power users, not for humans skimming project status. SpecScribe watches those artifacts and renders them into a styled, navigable, cross-linked static HTML site: epic and story dashboards with progress gauges, requirements traceability pages, rendered mermaid diagrams, and ADR indexes — regenerated live on every save.
Supported frameworks
SpecScribe renders artifacts from the spec-driven-development frameworks below. Support for additional frameworks is planned — see the Roadmap for feature-level plans.
| Framework | Version | Status |
|---|---|---|
| BMad Method | 6.10.0 | ✅ Supported |
| BMad GDS (Game Dev Studio) | 0.6.0 | ✅ Supported |
| GitHub Spec Kit | — | 🧭 Planned |
| GSD | — | ✅ Supported |
| GSD-Pi | — | 🧭 Planned |
| Superpowers | — | 🧭 Planned |
GSD Core support is per artifact family, not blanket: phases and plans become epics and stories, milestones
render as banded groups on the epics index, and .planning/STATE.md supplies the sprint view. Requirements,
planning docs and commands are deliberately not projected — each for its own stated reason, spelled out on the
portal's own About SDD › GSD page rather than left as unexplained blanks.
GSD and GSD-Pi are distinct products, not two versions of one thing. GSD Core is a slash-command framework layered on your AI coding runtime; it keeps every artifact as plain Markdown and JSON under
.planning/, with no database, and decomposes work as Milestone → Phase → Task. GSD Pi is an autonomous agent CLI whose single source of truth is a SQLite database at.gsd/gsd.db; the Markdown beside it is rendered from that database, and work decomposes as Milestone → Slice → Task. The oldergsd-build/gsd-2repository is retired and continues as GSD Pi. SpecScribe reads Markdown only — never the database.
BMad modules
BMad is not one thing — it installs as a set of modules under _bmad/{code}/, and BMad Builder can mint
new ones with arbitrary codes. SpecScribe identifies the installed module(s) from that directory name and
tells you honestly how deeply it understands each one. Installing a second module never degrades the one
you already had; a repo running BMM alongside Test Architect keeps its full BMM surface.
| Module | Code | What SpecScribe does |
|---|---|---|
| BMad Method | bmm |
Full projection — epics, stories, requirements, sprint, retros, planning docs, next-step commands, and the BMM glossary |
| Game Dev Studio | gds |
Full projection — the same families, with GDD / narrative planning docs and GDS commands |
| Test Architect | tea |
Named, with its test artifacts interpreted — they get their own page and a dashboard Module Coverage panel carrying the quality-gate verdict and coverage figures (see below) |
| Creative Intelligence Suite | cis |
Named — its real label and its parsed command catalog; its markdown renders through the generic document pass |
| BMad Builder | bmb |
Named — as above |
| Anything BMad Builder mints | any | Named — module identity is open-world, so a custom module is named rather than misidentified |
"Named" is a deliberate, stated boundary rather than a gap. For every module below the top two — Test Architect included — SpecScribe publishes no glossary and no planning-doc set, and the dashboard's artifact-coverage panel is omitted rather than reporting eight BMad Method artifact families the module never produces. The run records that omission as a non-fatal note on the Diagnostics page, which also names the module actually detected.
Two BMad core skills — present in every install regardless of module — get first-class surfaces:
bmad-forge-idea→ an Ideas page listing every forged idea workspace grouped by outcome (hardened / in progress / killed), each with a detail page, the originalforge-report.htmlcarried through verbatim, and forward links to the brief, PRD, or epic an idea produced where that link is evidenced on disk.bmad-testarch-*(Test Architect) → a Test Artifacts page, plus the first non-markdown sources SpecScribe reads:gate-decision.jsonande2e-trace-summary.jsonare read by exact filename so thePASS/CONCERNS/FAIL/WAIVEDgate verdict is visible instead of silently invisible to the*.mdscan.
Every discovered module artifact carries one of three coverage tiers, so the interpretation boundary is stated rather than guessed at:
| Tier | Meaning |
|---|---|
| Rendered | The document has its own page; SpecScribe reads its prose but interprets none of its structure |
| Summarized | SpecScribe extracts a structured headline — verdict, coverage figures — and surfaces it alongside the artifact, while the file itself is not fully modelled |
| Unsupported | Discovered and named, nothing interpreted. Not an error — an honest statement of the boundary |
Optional surfaces are omitted entirely when their artifacts don't exist: no forge workspaces means no Ideas page and no nav entry; no Test Architect artifacts means no Test Artifacts page and no Module Coverage panel.
Install
SpecScribe is a .NET global tool targeting .NET 10.
Not published to a package feed yet. Every Epic 16 release story is still backlog, so there is no
nuget.orgpackage and no tagged release. Install from a clone, and pin by commit SHA.
Prerequisites
- .NET 10 SDK.
- Node
^22.19.0 || ^24.11.0 || >=26.0.0— needed at generate time, not just to build. SpecScribe renders every page through a prebuilt JavaScript renderer (ADR 0022), sogenerateboots Node and fails with an actionable message when it is missing or out of range.
Build the renderer, then install the CLI
Both halves are required. Since
ADR 0034 no C# code path writes
content HTML: generate emits the JSON IR, then boots the renderer artefact and requests one route per
manifest entry. Without the artefact it produces an IR with no pages and exits non-zero.
# 1. Build the renderer artefact. Needs Node; the ~200 MB toolchain is build-time only
# and is never shipped — what it produces is a ~2.2 MB pure-JS artefact.
cd web
SPECSCRIBE_PACKAGE_BUILD=1 npm ci # PowerShell: $env:SPECSCRIBE_PACKAGE_BUILD='1'; npm ci
npm run sync:assets
npm run build:package # MUST be build:package, never build — see below
cd ..
# 2. Pack and install the CLI.
dotnet pack src/SpecScribe -c Release -o artifacts
dotnet tool install --global SpecScribe --add-source ./artifacts
SPECSCRIBE_PACKAGE_BUILD=1 is what lets step 1 run on a fresh clone: postinstall: nuxt prepare loads
web/nuxt.config.ts, which hard-fails when no IR is on disk, and on a fresh checkout there is none. The flag
stubs the route manifest empty, which is exactly what it exists for.
Use build:package, never build. A plain nuxt build bakes this project's pages into .output/public,
and Nitro serves public/ ahead of the SSR route — so an artefact built that way returns SpecScribe's own
pages for your project, with HTTP 200. A wrong answer with a success status.
That puts specscribe on your PATH (%USERPROFILE%\.dotnet\tools), so you can run it from any
project directory. To pick up a newer build later just re-pack and
dotnet tool update --global SpecScribe --add-source ./artifacts — there is no version to bump by hand. The
version comes from the nearest reachable git tag via MinVer, so an
untagged working build is 0.1.0-preview.0.<commits-since> and a build on a v0.1.0-preview.1 tag is exactly
0.1.0-preview.1 (ADR 0040 § 5). Read the version
off the produced .nupkg filename rather than typing one from memory.
Running against a project other than this one? That works with no extra configuration: the package carries its own renderer at
tools/<tfm>/any/renderer/, which lands beside the executable in the tool store and is whatgenerateresolves.SPECSCRIBE_RENDERER_DIRremains available as an explicit override — point it at a directory containingserver/index.mjsand SpecScribe uses that one or stops, never a different one.
See docs/Packaging.md for how a package is produced and verified, and for the two ways a packaging change can produce a green build that ships a package which cannot render.
Usage
specscribe # interactive menu (generate / watch / configure paths)
specscribe generate # generate the site once and exit
specscribe watch # generate, then regenerate on every file save (Ctrl+C to stop)
specscribe --help # full CLI help
Run with no arguments (or with unrecognized arguments) and SpecScribe drops into an interactive menu where you can generate, watch, or adjust paths before running.
Options
Both generate and watch accept:
| Option | Default |
|---|---|
--source <DIR> |
Walks up from the current directory to find _bmad-output/ |
--adrs <DIR> |
<repo root>/docs/adrs |
--output <DIR> |
<repo root>/SpecScribeOutput |
--project-name <NAME> |
project_name from _bmad/config.toml, else "BMad Live Docs" |
--deep-git |
Off — opt-in deeper git analytics (file hotspots + change coupling) as a distinct dashboard panel; leaving it off keeps baseline generation unaffected |
With no options, SpecScribe auto-discovers a BMad project from wherever you run it — so inside a
BMad repo, plain specscribe generate just works.
Publishing to GitHub Pages
You can publish SpecScribe output for any repository, not just this one.
Option A: Build and deploy with GitHub Actions (recommended)
What this costs today, stated up front. There is no published SpecScribe package, and the CLI cannot render without its Node renderer, so an external project must check SpecScribe out and build both halves. That is six steps whose order is load-bearing. Making this a single versioned step is Story 16.9 (backlog); until it ships, the workflow below is the supported path. It was verified end-to-end on 2026-08-06 against a separate repository — 940 pages,
errors=0.
Create .github/workflows/publish-specscribe-pages.yml:
name: Publish SpecScribe Docs
on:
push:
branches: ["main"]
workflow_dispatch:
permissions:
contents: read
concurrency:
group: pages
cancel-in-progress: false
env:
# Pin SpecScribe by commit SHA: there is no tagged release yet, and the CLI and its
# renderer MUST come from the same revision. Nothing validates a mismatched pair, and
# the failure mode is wrong output rather than an error.
SPECSCRIBE_REF: "<commit-sha>"
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Check out your project
uses: actions/checkout@v4
with:
# Full history so git metrics (commit count, activity heatmap) and the
# opt-in deep analytics (--deep-git) reflect real history, not just the tip.
fetch-depth: 0
- name: Check out SpecScribe
uses: actions/checkout@v4
with:
repository: IntegerMan/SpecScribe
ref: ${{ env.SPECSCRIBE_REF }}
path: .specscribe-src
# REQUIRED. MinVer derives the version from tag reachability, and a shallow
# clone has no tags — which yields a WRONG VERSION rather than an error.
fetch-depth: 0
- uses: actions/setup-dotnet@v4
with:
dotnet-version: "10.0.x"
- name: Setup Node
# Version comes from SpecScribe's own pin, never a hand-typed number.
uses: actions/setup-node@v4
with:
node-version-file: .specscribe-src/web/.nvmrc
cache: npm
cache-dependency-path: .specscribe-src/web/package-lock.json
# ── The renderer must exist BEFORE the generate. ──────────────────────────────────
# No C# path writes content HTML (ADR 0034). `generate` emits the IR, then boots this
# artefact and requests one route per manifest entry. With no artefact every route
# fails, errors=1, and the job exits non-zero having rendered nothing.
- name: Build the SpecScribe renderer
working-directory: .specscribe-src/web
env:
# `postinstall: nuxt prepare` loads nuxt.config.ts, which hard-fails when no IR is
# on disk — and none is, this early. This flag stubs the manifest empty, which is
# precisely what it exists for, and breaks the install-needs-IR cycle.
SPECSCRIBE_PACKAGE_BUILD: "1"
# `build:package`, NEVER `build`. A plain `nuxt build` bakes the building project's
# pages into .output/public, and Nitro serves public/ ahead of the SSR route — so
# the artefact would return SpecScribe's own pages for your project, at HTTP 200.
run: npm ci && npm run sync:assets && npm run build:package
- name: Install the SpecScribe CLI
# --tool-path (not --global) keeps the install explicit and off PATH-guessing.
# The version is derived from the git tag by MinVer, so there is no literal to
# keep in step with the csproj — read it off the .nupkg the pack just produced.
# (A hard-coded --version would break on the next commit, since an untagged
# build carries a commit-height suffix.)
run: |
dotnet pack .specscribe-src/src/SpecScribe/SpecScribe.csproj -c Release -o sspkg
VERSION=$(ls sspkg/SpecScribe.*.nupkg | sed 's|.*/SpecScribe\.||; s|\.nupkg$||')
dotnet tool install SpecScribe --version "$VERSION" \
--tool-path sstools --add-source sspkg
- name: Generate the portal
# No SPECSCRIBE_RENDERER_DIR: the package carries its own renderer beside the
# executable, so an external project needs no pointer back into this checkout.
# Runs from your repo root, so git analysis targets your history. Exits non-zero
# if any page errors, so a broken portal fails the job instead of publishing.
run: |
./sstools/specscribe generate \
--source _bmad-output \
--adrs docs/adrs \
--output SpecScribeOutput \
--project-name "My Project" \
--deep-git
- name: Upload pages artifact
uses: actions/upload-pages-artifact@v4
with:
path: SpecScribeOutput
deploy:
needs: build
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
# Declared per job, not workflow-wide, so the build job above never carries
# write scopes it does not need.
contents: read
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url || steps.deployment_retry.outputs.page_url }}
steps:
# GitHub's Pages backend intermittently returns "Deployment failed, try
# again later." The first attempt may fail without failing the job; the
# guarded retry re-invokes the deploy only when that happens.
- id: deployment
continue-on-error: true
uses: actions/deploy-pages@v5
- if: steps.deployment.outcome == 'failure'
run: sleep 30
- id: deployment_retry
if: steps.deployment.outcome == 'failure'
uses: actions/deploy-pages@v5
Notes:
- Replace
SPECSCRIBE_REF, the paths, and the project name for your layout. - Your project needs artifacts SpecScribe can read — today that means BMad (
bmm/gds). Spec Kit, GSD and GSD-Pi are planned; see Supported frameworks. - Do not reorder the steps. The renderer build must precede the generate. This repository's own publish workflow regressed exactly that way once, and the reason it was missed is instructive: it had no Node steps at all, so nothing in it looked like it depended on the renderer.
generateexits non-zero when any page errors, so the job fails rather than publishing a broken portal. A missing or out-of-range Node fails with a message naming the supported range.- The deploy step is retried once because GitHub's Pages backend occasionally reports a transient
Deployment failed, try again later.error; the retry avoids a full rebuild. - Full repository example workflow: https://github.com/IntegerMan/SpecScribe/blob/main/.github/workflows/publish-docs-live-pages.yml
Option B: Commit generated output and publish from that folder
If you commit generated site files, you can keep output in a single top-level folder like SpecScribeOutput
and configure GitHub Pages to serve that published content from version control.
For this mode:
- Run SpecScribe with
--output SpecScribeOutput. - Commit and push the generated
SpecScribeOutputfiles. - Configure GitHub Pages in repository settings to publish from the branch/folder setup that serves that directory.
This is useful if you prefer static output tracked in git instead of artifact-based deployment.
What it renders
- Dashboard — project-wide progress, epic/story completion gauges, git activity stats
- Epics & stories — parsed from BMad
epics.mdstructure, grouped and cross-linked, with status pills - Requirements traceability — FR/NFR inventory with epic coverage maps; requirement IDs in any document become anchor links
- ADRs — hand-authored architecture decision records rendered with rewritten cross-links
- Ideas — forged idea workspaces grouped by outcome, with the original forge report and forward links to whatever the idea became (omitted when no ideas exist)
- Test artifacts — Test Architect output with its quality-gate verdict, coverage figures, and a per-artifact coverage tier, plus a Module Coverage panel on the dashboard (omitted when absent)
- Mermaid diagrams — fenced
```mermaidblocks render client-side - Task lists — GitHub-style checkboxes render as progress
Source files are always read with shared access; the watcher never holds a write lock on anything it observes.
Roadmap
Planned framework support (Spec Kit, GSD-Pi, Superpowers) is tracked in the Supported frameworks table above. Feature-level plans:
- Git insights — richer history-derived views (velocity, file heatmaps) beyond the current commit stats
- Directory-structure insights — project-layout overviews generated from the tree itself
Development
dotnet build # build everything
dotnet test # run the unit tests
dotnet run --project src/SpecScribe -- generate # run without installing
pwsh tools/coverage/Get-Coverage.ps1 # browsable coverage report (add -Open to launch it)
The solution is SpecScribe.slnx; the tool lives in src/SpecScribe, tests in tests/SpecScribe.Tests.
The coverage report is written to the gitignored artifacts/coverage/html/. It renders the OpenCover file
dotnet test already produces — it is not a second coverage mechanism — and it covers C# only, so its
percentage is deliberately not the same number as the SonarCloud figure, which uses a different formula and
also counts extension/ and web/. tools/coverage/README.md explains the
difference and shows which figures to compare.
Continuous integration
Every push to main and every pull request builds the solution and runs the full test suite via
.github/workflows/build-test-analyze.yml, which also submits the
build to SonarQube Cloud for code-quality analysis.
Build and test need no configuration. The analysis half requires a one-time SonarCloud setup — generating a
token and storing it as the SONAR_TOKEN repository secret — described in
SonarCloud Setup. Until that is done CI still runs green; the scanner steps
simply skip, which is also what happens on pull requests from forks, since GitHub does not share secrets with
them.
Project health
| Measure | Value | What it means |
|---|---|---|
| Build | Passing | The latest main run of build-test-analyze.yml built the solution and the full test suite went green. |
| Coverage | 89.9% | SonarQube Cloud's blended figure — covered lines and covered conditions over all lines and conditions to cover. This is the number the badge above shows. |
| Line coverage | 91.9% | Executable lines hit by the tests. Higher than the blended figure. |
| Branch coverage | 85.7% | Conditional branches taken by the tests. Lower than the blended figure. |
| Maintainability | A — best rating | No technical-debt band is dragging the rating down. |
| Reliability | D — third-worst of five | 12 open bugs sit inside the analysis's new-code window, the worst of them rated critical. |
| Security | C — middle of five | 164 open vulnerabilities sit inside the new-code window; 160 of them are one rule, csharpsquid:S6444 (regular expressions declared without a timeout). |
| Quality gate | Failing — no badge shown | The Sonar way gate reports ERROR, because it demands the best rating on both reliability and security for new code. See the paragraph below. |
Measured 2026-07-29 against SonarQube Cloud's analysis of 240afae. These figures are a hand-written
snapshot and go stale as soon as CI runs again — the two badges under the title are always live, this table
is not. Refresh it with:
curl -s "https://sonarcloud.io/api/measures/component?component=IntegerMan_SpecScribe&metricKeys=alert_status,coverage,line_coverage,branch_coverage,sqale_rating,reliability_rating,security_rating"
Why there is no quality-gate badge. The gate is SonarQube Cloud's built-in Sonar way, and every one of
its conditions is advisory here: the workflow deliberately leaves sonar.qualitygate.wait unset, so a failing
gate never fails the build. The gate currently reports failing, and it will keep reporting failing until two
sizeable cleanups land, because Sonar's A rating means zero open issues of that class rather than no
severe ones. A permanently-red badge on the front page is worse than no badge, so the build and coverage
badges ship on their own. SonarCloud Setup records what the gate asserts, what is
holding it red, and who owns each cleanup.
License
MIT — Copyright (c) 2026 Matt Eland
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.11-preview.12 | 145 | 8/11/2026 |
| 0.1.10-preview.11 | 58 | 8/11/2026 |
| 0.1.9-preview.10 | 65 | 8/10/2026 |
| 0.1.6-preview.7 | 70 | 8/9/2026 |
| 0.1.5-preview.6 | 65 | 8/9/2026 |
| 0.1.3-preview.4 | 74 | 8/9/2026 |
| 0.1.1-preview.2 | 66 | 8/8/2026 |