A lightweight, read-only viewer for OpenSpec content — browse specs, changes, and tasks with structure.
spek turns your local OpenSpec directory into a navigable, searchable interface. Instead of reading raw Markdown files in a text editor, spek provides structured browsing with BDD syntax highlighting, task progress tracking, and full-text search.
Live Demo — Try it instantly in your browser, no install needed.
Available in three forms:
- Web — Local Express + React app, accessible in any browser
- VS Code Extension — Integrated Webview Panel within your editor
- IntelliJ Plugin — JCEF-based Tool Window for IntelliJ IDEA and other JetBrains IDEs
All are read-only and local-only. No server deployment, no authentication, no data leaves your machine.
- Dashboard — Overview of specs count, changes count, task completion rates, plus lifecycle stats (avg archived lifecycle, stale active changes)
- Specs Browser — Alphabetical listing with detail view and revision history
- Changes Browser — Active and archived changes with tabbed views (Proposal / Design / Tasks / Specs); each row surfaces creation and archive dates plus lifecycle duration
- Git Worktree Aggregation — Discovers every git worktree of a repo and merges their in-flight changes into one view — built for the AI-agent era of parallel worktrees
- Timeline — Horizontal Gantt-style chart of every change's lifecycle, with optional spec-topic grouping, status filters, and an auto-scaling time axis
- BDD Syntax Highlighting — Visual distinction for WHEN/GIVEN, THEN, AND, MUST/SHALL keywords
- Task Progress — Checkbox parsing with section-grouped progress bars
- Full-text Search —
Cmd+K/Ctrl+Kto search across all specs and changes - Dark / Light Theme — Toggle between themes; dark by default
- Spec History — Git-based timestamp tracking for spec revisions
- Responsive Layout — Works on various screen sizes
- VS Code Sidebar — Activity Bar icon with TreeView for browsing specs and changes directly from the sidebar
In the AI-agent era, a single repository often has several git worktrees in flight at once — each agent, or each parallel task, working on its own branch in its own worktree. The OpenSpec changes for that work scatter across those worktrees, and pointing a viewer at any one directory shows only a fraction of what's happening.
spek discovers every worktree of a repository (via git worktree list) and aggregates their in-flight changes into one view. Point spek at any worktree — or the main repo — and you see the whole picture:
- Active changes from every worktree, each tagged with its source branch
- Archived changes merged and deduplicated across worktrees
- An aggregation toggle (auto-on when multiple worktrees exist); main-worktree changes stay unlabelled so feature-worktree work stands out
- Works in the Web app and the VS Code extension (panel + sidebar), with live refresh when any worktree's
openspec/changes
git clone https://github.com/spekhq/spek.git
cd spek
npm install
npm run devOpen http://localhost:5173, enter a path to a repo containing an openspec/ directory, and start browsing.
npm installcompiles the shared@spekjs/corepackage, andnpm run devrebuilds it before launching — so a fresh clone starts with no extra build step.
Install from the Visual Studio Marketplace. The extension activates automatically when your workspace contains openspec/config.yaml.
Once activated, click the spek icon in the Activity Bar to browse specs and changes from the sidebar. Click any item to open the full viewer panel.
Commands:
spek: Open spek— Open the viewer panelspek: Search OpenSpec— Open search dialogspek: Open Dashboard— Open the dashboard from sidebar
Install from the JetBrains Marketplace — search for "spek" in Settings > Plugins > Marketplace.
The plugin activates automatically when your project contains an openspec/ directory. Click the spek icon in the right sidebar to open the viewer.
Action:
- Tools > Open spek — Open the viewer panel
Overview of specs count, changes count, and task completion rates.
Alphabetical listing of all spec topics with filter support.
Visual distinction for BDD keywords — WHEN/GIVEN (blue), THEN (green), AND (gray), MUST/SHALL (red).
Active and archived changes listed chronologically, with lifecycle duration on every row.
Horizontal Gantt-style view of every change's lifecycle — active bars extend to today, archived ones render as fixed segments.
Tabbed view with one tab per artifact the change contains. For the default spec-driven schema that's Proposal, Design, Specs, and Tasks; the schema a change was authored under is shown as a badge.
spek renders whatever artifacts a change actually contains, so changes authored under a custom OpenSpec schema (e.g. superpowers-bridge with brainstorm / plan / verify / retrospective) show every artifact as its own tab. Tabs default to last-modified order so the artifact you're actively editing surfaces first, and a sort control lets you switch to Schema order or A–Z. Schema order comes from the OpenSpec authority (the openspec CLI) rather than spek parsing the schema format itself, so it's correct for any schema; when the CLI isn't available (or the change is archived) that mode falls back to the default order and says why.
Cmd+K / Ctrl+K to search across all specs and changes.
Use spek as a GitHub Action to automatically build a static OpenSpec site in your CI pipeline.
- uses: actions/checkout@v7
with:
fetch-depth: 0 # Recommended for accurate change timestamps
- uses: spekhq/spek@v1
with:
title: "My Project - OpenSpec"name: Build OpenSpec Site
on:
push:
branches: [main]
paths: ["openspec/**"]
permissions:
pages: write
id-token: write
jobs:
build-and-deploy:
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deploy.outputs.page_url }}
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: spekhq/spek@v1
with:
title: "My Project - OpenSpec"
- uses: actions/upload-pages-artifact@v5
with:
path: spek-output
- name: Deploy to GitHub Pages
id: deploy
uses: actions/deploy-pages@v5| Input | Description | Default |
|---|---|---|
repo-path |
Path to the repo containing openspec/ |
. |
output-path |
Output HTML file path | spek-output/spek.html |
title |
Page title | OpenSpec Viewer |
spek-version |
spek version (tag, branch, or SHA) | master |
generate-badges |
Generate SVG badge files | false |
| Output | Description |
|---|---|
html-path |
Absolute path to the generated HTML file |
badges-path |
Absolute path to the generated badges directory |
Enable generate-badges to generate SVG status badges (specs count, open changes, tasks progress) alongside your static site. Deploy them to GitHub Pages and reference in your README:
- uses: spekhq/spek@v1
with:
title: "My Project - OpenSpec"
generate-badges: trueThen in your README:


Note: Use
fetch-depth: 0in your checkout step for accurate change timestamps. Without full git history, timestamps will be unavailable (the build still succeeds).
spek expects the following structure under your repository:
{repo}/openspec/
├── config.yaml
├── specs/
│ └── {topic}/
│ └── spec.md # BDD-formatted specification
└── changes/
├── {active-change}/ # In-progress changes
│ ├── .openspec.yaml
│ ├── proposal.md
│ ├── design.md
│ ├── tasks.md
│ └── specs/ # Delta specs for this change
└── archive/
└── {YYYY-MM-DD-desc}/ # Archived changes (same structure)
packages/
├── core/ # @spekjs/core — Pure logic (scanner, parser, types) [published to npm]
├── ui/ # @spekjs/ui — Reusable visualizations (graph, timeline) [published to npm]
├── web/ # @spekjs/web — Express API + React SPA
├── vscode/ # spek-vscode — VS Code Extension
└── intellij/ # spek-intellij — IntelliJ Platform Plugin (Kotlin)
Two packages are published so that hosts outside this monorepo can reuse them:
@spekjs/core— the engine: scanner, tasks parser, worktree aggregation, shared types. Pure Node.js, no framework.@spekjs/ui— the two visualizations:<SpecGraph>(force-directed spec ↔ change graph) and<ChangeTimeline>(Gantt-style change lifecycle). Presentational only — no router, no data layer, no CSS framework. Colours are an explicit contract of eight CSS custom properties, so a host re-themes them by overriding those.
Within this monorepo they resolve through npm workspaces, so development is not gated on their release cadence. Both are released manually and version independently of the root.
The frontend communicates through an ApiAdapter interface with two implementations:
- FetchAdapter — Web + IntelliJ version, calls REST API over HTTP (configurable base URL)
- MessageAdapter — VS Code version, uses
postMessageIPC with the extension host
This allows the same React UI to run in both environments without code changes.
Note that @spekjs/ui does not depend on ApiAdapter: its components take data through props.
Fetching is the host's concern — over HTTP here, over IPC in an Electron host.
| Layer | Technology |
|---|---|
| Core | TypeScript, Node.js |
| UI package | React 19 (peer), d3 (force, zoom, drag, selection, transition), hand-written CSS |
| Frontend | React 19, Vite 6, Tailwind CSS v4, React Router v7 |
| Backend | Express 4 |
| Markdown | react-markdown, remark-gfm |
| Search | Fuse.js |
| VS Code Extension | VS Code Webview API, esbuild |
| IntelliJ Plugin | Kotlin, JCEF, IntelliJ Platform SDK |
npm install # Install all workspace dependencies
npm run dev # Start Vite (5173) + Express (3001)
npm run build # Build core + ui + web
npm run build:core # Build @spekjs/core only
npm run build:ui # Build @spekjs/ui only
npm run build:webview # Build webview assets for VS Code extension
npm run build:vscode # Build VS Code extension
npm run build:intellij # Build IntelliJ webview assets
npm run type-check # TypeScript type checkIntelliJ Plugin build:
npm run build:intellij # Build frontend assets
cd packages/intellij && ./gradlew buildPlugin # Build plugin ZIPRequirements: Node.js 22+, Java 17+ (for IntelliJ plugin build)
spek watches openspec/ and live-reloads on changes. On filesystems that don't deliver native change events — 9p / drvfs / NFS / CIFS bind mounts, as used by devcontainers and WSL — spek automatically falls back to polling so newly created files are still detected. Detection is based on the watched path's filesystem type. To override:
SPEK_WATCH_POLLING=on(oroff) — force polling on/off for all surfacesCHOKIDAR_USEPOLLING=1/CHOKIDAR_INTERVAL=<ms>— Web and VS Code also honor chokidar's native env vars
This project was inspired by 龍哥(高見龍)'s articles on SDD (Spec Driven Development). Special thanks to him for his contributions to SDD and OpenSpec.
Thanks to everyone who has contributed to spek:
-
@nthansen (Norman Hansen)
- Custom OpenSpec schema support — a change's artifacts are discovered from disk, so any schema's artifacts render as their own tabs
- User-selectable artifact tab ordering (last-modified / schema order / A–Z), with schema order sourced from the OpenSpec CLI
- Schema badge across the Changes list, Dashboard, and Change Detail, hidden when it matches the repo default
- Polling-based live-reload fallback for filesystems without native change events (devcontainer / WSL / network mounts)
- Worktree-aware schema baseline — under aggregation each change's badge is judged against its own worktree's default schema, and scanning reads each worktree's config once instead of once per change
- TOC and
#hashnavigation that lands the target heading below the sticky header instead of behind it - Opening a change no longer consults the OpenSpec CLI once per change — the authoritative artifact order is cached per schema, since every change sharing a schema gets the same answer back
-
@david-lutz (David Lutz)
- Deduplication of active changes shared across git worktrees, in both the Changes list and the dependency graph — the surviving copy is elected from git divergence rather than file timestamps, which a fresh checkout rewrites
MIT








