A Claude plugin that walks you through a manifest-tracked 7-phase workflow (purpose → data discovery → collectors → UI → scaffold → smoke test → handoff) and drops a working Python-stdlib dashboard into your codebase. No pip install, no framework, no bundler. Decisions first, data second.
examples/minimal — every panel kind, fake data, runs on port 8765:
examples/engagements — realistic portfolio-tracking dashboard with severity cascades, stacked-bar revenue timeline, Kanban-by-phase active grid, and a live SQLite event log (storage.py demo). Runs on port 8766:
Both examples boot with python run.py. No install step. They ship with completed dashboard-manifest.md files so you can read the full 7-phase record of how they were built.
Option A — as a Claude Code plugin (recommended for ongoing use):
/plugin add https://github.com/walm00/local-dashboard-builder
Claude Code reads .claude-plugin/plugin.json, auto-discovers the skill, and makes it available in every repo you work in. Updates are managed by Claude Code's plugin manager.
Option B — install into a specific repo:
# One-liner (from your target repo):
curl -fsSL https://raw.githubusercontent.com/walm00/local-dashboard-builder/main/install.sh | bash
# Or clone and run:
git clone https://github.com/walm00/local-dashboard-builder /tmp/ldb
cd /path/to/your/repo
python /tmp/ldb/install.pyThe installer copies skills/local-dashboard-builder/, .claude-plugin/plugin.json, and .claude/scripts/local-dashboard-builder-update.py into your repo. The updater is prefix-named so it coexists cleanly with other Claude Code plugins that also ship an update.py. Safe to re-run — existing files are never overwritten. Python 3.8+ required; no pip install.
If you installed via Option B and want to pull a newer release:
python .claude/scripts/local-dashboard-builder-update.py # interactive, shows diff
python .claude/scripts/local-dashboard-builder-update.py --dry-run # preview only
python .claude/scripts/local-dashboard-builder-update.py --yes # apply without prompt
python .claude/scripts/local-dashboard-builder-update.py --tag v0.3.0 # pin to a specific releaseThe update script replaces only framework files (skills/local-dashboard-builder/, .claude-plugin/plugin.json, and itself at .claude/scripts/local-dashboard-builder-update.py). Your scaffolded dashboards and filled manifests are never touched. Another plugin's update.py sitting next to it in .claude/scripts/ is left alone — the prefix keeps them from colliding.
Plugin-install users (Option A) get updates from Claude Code automatically.
python skills/local-dashboard-builder/scripts/scaffold.py \
--name <subject> \
--target <path-to-repo> \
[--port 8765] \
[--data-source empty|csv|sqlite|git|subprocess|filesystem] \
[--chart-library none|echarts] \
[--owns-pipeline]Creates <target>/.claude/scripts/<subject>_dashboard/ with the template files copied over plus a blank dashboard-manifest.md. Fill the manifest phase by phase (WHY first), wire collectors + panels, and run.
Flags:
--owns-pipeline— dropsschema.sql,scanner.py, anddata/.gitignorefor a self-contained dashboard that owns its own SQLite pipeline.--chart-library echarts— vendorsdownload_echarts.pyalongside the dashboard and wires<script src="/static/vendor/echarts.min.js">into the HTML. Runpython download_echarts.pyonce after scaffolding to fetch a pinned ECharts build (no npm, no CDN at runtime). Unlocks thescatterandheatmappanel kinds. Default isnone(stdlib SVG renderers only).
Every dashboard's dashboard-manifest.md carries a schema version. Run the validator any time to confirm the manifest is well-formed for the current phase:
python skills/local-dashboard-builder/scripts/validate.py \
.claude/scripts/<subject>_dashboard/dashboard-manifest.mdExit 0 = OK; exit 1 = gate failure with a specific field name. Both example dashboards validate cleanly out of the box.
Every dashboard is tracked against a dashboard-manifest.md that the skill refuses to let you skip:
- Purpose / WHY — what question, who reads it, what decisions it triggers
- Repo Discovery — data inventory, source-to-decision mapping
- Collectors — one row per panel:
name | source | ttl | severity_rule - UI Mapping —
id | title | kind | collector | span | ttl | severity | number - Scaffold & Wire — paths, port, run command
- Test / Smoke — 8-check results (launch, endpoints, TTL persistence, panel isolation, offline)
- Handoff — sign-off checklist
Three gates fire during the workflow:
- Purpose Alignment (Phase 1) — refuses to discover data until you've written down the decisions the dashboard must trigger.
- Approach Alignment (Phase 4) — purpose ↔ panels sanity check before a line of code is scaffolded.
- Smoke Passed (Phase 6) — blocks handoff until 8 smoke checks pass.
Failing a gate bounces you back to the relevant phase with an iteration log entry.
See skills/local-dashboard-builder/references/planning-workflow.md for the full per-phase gate and done-criteria reference.
Nine kinds ship in the box — each with a documented JSON contract (references/data-contract.md) and a matching renderer in dashboard.js:
| Kind | Good for | Chart library? |
|---|---|---|
metric |
single-number KPIs, stat strips | stdlib |
list |
ranked items, outstanding money, per-row severity | stdlib |
table |
tabular rows with columns + alignment | stdlib |
grid |
Kanban-style cards, status-by-phase | stdlib |
progress |
goal tracking (cash, completion %) | stdlib |
feed |
time-ordered events, sweeps, activity log | stdlib |
chart |
line / bar / stacked-bar trends, TTL-cached | stdlib (SVG) |
scatter |
two-axis distributions, correlations | ECharts |
heatmap |
calendar / matrix intensities | ECharts |
scatter and heatmap require --chart-library echarts at scaffold time. Everything else renders with plain SVG — no pip, no runtime CDN.
- Progressive disclosure via a drawer. Any list/grid/table row that includes a
_drawer_keyin its payload becomes clickable — the template auto-wires a detail drawer. Register arenderBody(item, panel, allData)callback onwindow.DASHBOARD_DRAWERto customise the drawer per key; no per-dashboard boilerplate. - URL-driven filters. Panels can read
?range=Q2(or any query-string parameter) on each refresh. Pair with acontrols=block onserve()to render filter chips that update the URL and trigger re-fetches. Theengagementsexample ships period-slicing chips (YTD / Q2 / 90d / all) that filter the revenue timeline. - Typed error envelopes. When a collector fails,
_error_envelope()returns a structured payload with an optionalhintfield; the panel renders a muted error card instead of a raw stack trace. One broken panel never breaks the rest of the grid. - Empty-envelope pattern. Collectors can return
{"missing": true, "missing_message": "..."}to render a "no data yet" state cleanly — used automatically by ECharts-backed panels when the library isn't vendored. - Severity cascade. Per-row or per-collector severity bubbles up to a header badge (
ok / warn / critical). CVD-safe palette — tested against Deuteranopia / Protanopia / Tritanopia so the severities stay distinguishable for colour-blind readers. - TTL caching + per-panel error isolation.
server.pycaches each collector's result for its declared TTL and catches exceptions at the panel boundary.
Read references/design-principles.md for the full pattern catalog.
A dashboard is a decision tool, not a data dump. Phase 1 refuses to discover data until you've written down what decisions the dashboard must trigger. Every downstream phase derives from that.
Tech stack is Python stdlib only — no pip install, no framework, no bundler. Any machine that can run Python can run a dashboard built by this skill. The starter template loads HTML/CSS/JS from sibling files so the design is editable in a syntax-highlighted editor without touching Python.
local-dashboard-builder/
├── README.md
├── LICENSE # MIT
├── CHANGELOG.md
├── VERSION # semver, single source of truth
├── install.sh / install.py # install into an existing repo
├── .claude-plugin/plugin.json # Claude plugin manifest
├── .claude/scripts/local-dashboard-builder-update.py # end-user update script (prefix-named to coexist with other plugins)
├── skills/local-dashboard-builder/
│ ├── SKILL.md # 7-phase workflow, the contract
│ ├── templates/ # dashboard.html / .css / .js + server.py + storage.py + favicon.svg
│ │ └── download_echarts.py # optional ECharts vendoring (scatter / heatmap)
│ ├── references/ # planning-workflow, design-principles, data-contract, example-collectors
│ └── scripts/
│ ├── scaffold.py # drop a starter dashboard into any repo
│ ├── validate.py # check a dashboard-manifest.md against the schema gates
│ └── indexer.py # JSONL -> SQLite rebuilder
└── examples/
├── minimal/ # all nine panel kinds, fake data
└── engagements/ # realistic portfolio dashboard, SQLite event log, URL filters
Issues and PRs welcome at https://github.com/walm00/local-dashboard-builder. The plugin follows the 7-phase workflow itself — changes to templates or the skill should update CHANGELOG.md and, where relevant, the reference docs.
MIT — see LICENSE.

