From brainstorm-preview
Serve Markdown files under `docs/` (specs/plans from `superpowers:brainstorming` and `superpowers:writing-plans`) as styled HTML on an HTTP server bound to 0.0.0.0 so the user can review them from another device on their Tailscale tailnet. Use when the user asks to "preview the spec/plan in a browser", "view the brainstorming output as HTML", "serve docs over the network", "render plans for remote review", "open the plan on my phone via tailscale". Also auto-invoke with `--latest` right after `superpowers:brainstorming` writes a spec or `superpowers:writing-plans` writes a plan, so the freshly-written doc is highlighted and a direct URL is printed — unless the user opted out.
How this skill is triggered — by the user, by Claude, or both
Slash command
/brainstorm-preview:brainstorm-previewThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Spins up a single-file Python web server that walks the project's `docs/` tree, renders every `*.md` file to styled HTML (with client-side rendering for ```dot / ```mermaid blocks via CDN), and prints the URLs the user can open — including the Tailscale tailnet URL so peers on the same tailnet can reach it.
Spins up a single-file Python web server that walks the project's docs/ tree, renders every *.md file to styled HTML (with client-side rendering for dot / mermaid blocks via CDN), and prints the URLs the user can open — including the Tailscale tailnet URL so peers on the same tailnet can reach it.
superpowers:brainstorming or superpowers:writing-plans and wants to review the resulting spec/plan in a browser instead of in the terminal.docs/ content.Do NOT use this skill when the user wants to:
superpowers:brainstorming / superpowers:writing-plans).The skill description hints at auto-invocation after brainstorming/writing-plans
writes a spec or plan. In practice that hint loses to the brainstorming
workflow's "do not invoke any other skill" rule, so Claude rarely fires the
skill on its own. Make it deterministic with a PostToolUse hook instead.
~/.claude/hooks/brainstorm-preview-trigger.sh:
#!/usr/bin/env bash
set -e
INPUT=$(cat)
TOOL=$(printf '%s' "$INPUT" | jq -r '.tool_name // empty')
FP=$(printf '%s' "$INPUT" | jq -r '.tool_input.file_path // empty')
case "$TOOL" in Write|Edit|MultiEdit) ;; *) exit 0 ;; esac
case "$FP" in
*/docs/superpowers/specs/*.md|*/docs/superpowers/plans/*.md) ;;
*) exit 0 ;;
esac
ROOT="${FP%%/docs/superpowers/*}"
[ -d "$ROOT/docs" ] || exit 0
PORT=8765
lsof -ti :"$PORT" >/dev/null 2>&1 && exit 0
nohup uv run \
~/.claude/skills/brainstorm-preview/scripts/serve.py \
--docs-root "$ROOT" --port "$PORT" --latest \
>/tmp/brainstorm-preview.log 2>&1 &
disown
~/.claude/settings.json (add a second entry alongside the existing notifier):
"PostToolUse": [
{ "matcher": "Write|Edit|MultiEdit",
"hooks": [{ "type": "command",
"command": "/Users/<you>/.claude/hooks/brainstorm-preview-trigger.sh" }] }
]
After the hook spawns the server, retrieve the URLs from
/tmp/brainstorm-preview.log (use tail or BashOutput if Claude is still in
the session) and relay them as clickable markdown links. The hook deliberately
does nothing when the port is already bound — preserves URLs from the original
startup output and avoids restart churn.
Confirm the project root contains docs/ (preferably with docs/superpowers/specs/ and/or docs/superpowers/plans/). If not, tell the user and stop.
Decide which startup flag to pass:
--latest (or --open <relpath> if the exact file path is already known)./view/<relpath> URL for the new doc.Start the server in the background from the project root so the conversation stays interactive:
uv run /Users/dinhnguyen/.claude/skills/brainstorm-preview/scripts/serve.py \
--docs-root "$PWD" --port 8765 [--latest | --open <docs/relpath>]
Use Bash with run_in_background: true so the server doesn't block. The script prints local, LAN, Tailscale, and (when --latest / --open is set) latest: URLs to stdout on startup.
Read the first ~20 lines of the background process's output (via BashOutput on the started shell) and relay the printed URLs to the user as clickable markdown links ([url](url)). Do not wrap URLs in inline code (`url`) or fenced code blocks — those break the click target in most renderers. Include the Tailscale URL if it appears. Do not invent or rewrite URLs — only show what the script actually printed.
If the user later asks to stop the preview, KillShell the background process.
When this skill runs right after brainstorming or writing-plans, relay the URLs as clickable markdown links — never wrap a URL in inline code (`...`) or a fenced code block, because most renderers (Claude Code, IDE terminals) will not make a code-styled URL clickable.
URL format — the /view/ prefix is followed by the path relative to --docs-root (the project root), which includes the leading docs/ segment. Do NOT strip docs/. A spec at docs/superpowers/specs/foo.md is served at http://host:8765/view/docs/superpowers/specs/foo.md, never at /view/superpowers/specs/foo.md (that path returns 404).
The natural handoff is:
Spec/plan written to
docs/superpowers/specs/foo.md. Preview server up — click to review:
Pull the exact latest: URLs out of the script's startup output whenever possible — the script emits the correct path so you don't have to construct it. Do not invent the URL. Render each URL with markdown link syntax ([label-or-url](url)) so it stays clickable.
If the server is already running from a previous turn and the script's latest: output is not available, build the URL by prefixing /view/ directly to the relative path of the file from the project root (e.g., docs/...md). Quick verification: curl -s -o /dev/null -w '%{http_code}\n' <url> should return 200, not 404.
When the superpowers:brainstorming Visual Companion (scripts/start-server.sh, node server.cjs) starts during the same session, it emits a server-started JSON with a single url field (commonly http://localhost:<random-port>). Its --url-host flag has been known to ship a wrong/stale IP in prior runs, and the upstream plugin only prints one URL.
After the companion's start-server.sh succeeds:
port field out of the server-started JSON.tailscale ip -4 (via Bash). Take the first non-empty stdout line.Visual companion up:
- local: http://localhost:<port>/
- tailscale: http://<ts-ip>:<port>/
tailscale ip -4 is missing or empty, just relay the original url field — no fabricated IP.Do not modify scripts/start-server.sh or scripts/server.cjs in the brainstorming plugin to "fix" this upstream. The Tailscale URL is purely a presentation augmentation done by this conversation's agent. Skill files in the upstream plugin cache get overwritten on update.
scripts/serve.py (PEP 723 inline metadata; uses uv run to fetch its own deps the first time):
<docs-root>/docs for every *.md file. Groups them in the index as Specs (docs/superpowers/specs/...), Plans (docs/superpowers/plans/...), and Other docs (everything else), sorted newest first by mtime.markdown + pygments (fenced code, tables, TOC, codehilite, task-list checkboxes).```mermaid) render client-side via cdn.jsdelivr.net/npm/mermaid@10.```dot or ```graphviz) render client-side via cdn.jsdelivr.net/npm/d3-graphviz@5 + @hpcc-js/wasm.0.0.0.0 by default so Tailscale peers reach it.tailscale ip -4. If tailscale isn't installed or returns no addresses, the script prints (not detected — run \tailscale ip -4` to verify)` and continues.| Flag | Default | Notes |
|---|---|---|
--port | 8765 | Override via flag or PORT env var. |
--docs-root | . | Project root that contains docs/. |
--bind | 0.0.0.0 | Set to 127.0.0.1 to keep the server local-only. |
uv is missingIf uv is not on PATH, run with the system Python after installing the two deps once:
pip install markdown pygments
python3 /Users/dinhnguyen/.claude/skills/brainstorm-preview/scripts/serve.py --docs-root "$PWD"
--port <other>. Common collisions: 3000 (Next.js), 5173 (Vite), 4000 (Jekyll).tailscale status to confirm the tailnet is up; the script only reports the URL if tailscale ip -4 returns at least one address.dot / mermaid blocks fall back to plain code blocks./view/...: the requested path tried to escape --docs-root. Move the file under docs/ or rerun with a higher --docs-root..md is served. Images embedded in markdown that reference relative paths won't load — the server has no static asset route by design.docs/ trees (>10k files) will be slow. None of the brainstorming output dirs come close.Fetches up-to-date documentation from Context7 for libraries and frameworks like React, Next.js, Prisma. Use for setup questions, API references, and code examples.
Applies a firm's KYC/AML rules grid to parsed onboarding records: assigns risk rating, checks required documents, outputs rule outcomes with citations, and routes for escalation.
Generates daily or weekly digests of activity from connected sources (chat, email, docs, tasks, CRM), highlighting action items, decisions, mentions, and project updates.
npx claudepluginhub dinhnguyen/claude-plugins --plugin brainstorm-preview