Skip to content

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

VisualPlanner

A tiny self-hosted server for storing and browsing self-contained HTML "plans". AI agents push plans as HTML via an authenticated REST API; humans browse them in a React UI. Designed to run on a headless box reachable over a Tailnet.

Each plan is a single .html file stored on disk, plus an index.json catalog. The index is rebuilt from the .html files on startup, so deleting index.json is non-destructive.

Prerequisites

  • Bun (runtime + test runner)

Quick start

cp .env.example .env          # then edit .env and set VISUALPLANNER_TOKEN
bun install
bun run dev                   # API server on :7070 (watches for changes)
bun run dev:ui                # Vite UI on :5173 (proxies /api -> :7070)

Open http://localhost:5173 in development.

When agents publish plans during development, set VISUALPLANNER_VIEWER_URL to the browser-facing Vite URL (for example, http://localhost:5173) while keeping VISUALPLANNER_URL pointed at the API (http://localhost:7070). The publishing skill uses the viewer URL for returned plan links and falls back to the API URL in production.

For production (builds the UI, then serves it from the API origin on :7070):

bun run start

Scripts

During normal agent work in a worktree, run targeted tests covering the changed area. The unscoped test suite is for CI or an explicitly requested cross-workspace check; do not start it independently from multiple worktrees.

Script Description
bun run dev Run the API server with --watch (port 7070).
bun run dev:ui Run the Vite dev server for the UI (port 5173, proxies /api).
bun run build Build the UI into dist/client/.
bun run start Build, then run the production server (serves UI + API on :7070).
bun run test -- path/to/test.test.ts Run targeted tests (bun:test).
bun run typecheck Type-check with tsc --noEmit.
bun run lint Lint with Biome.
bun run format Format the codebase in place with Biome.
bun run check Apply Biome's safe lint + format fixes in place.

Configuration

All settings are optional environment variables (read from .env in the working directory, with real process.env taking precedence):

Variable Default Meaning
VISUALPLANNER_TOKEN change-me (disabled) Bearer token agents must present to write. Unset/change-me = no auth.
VISUALPLANNER_PORT 7070 Port to listen on.
VISUALPLANNER_DATA_DIR ~/.local/share/visualplanner Where plan .html files and index.json are stored. ~ is expanded.
VISUALPLANNER_BIND auto (Tailscale IP, else 127.0.0.1) Hostname to bind. Empty/unset auto-detects a Tailscale 100.x address.
VISUALPLANNER_PROJECT_ALIASES (none) Comma-separated old:new project name aliases, e.g. HI0890-Zephyr:Zephyr,org/old:New. Normalizes project names on intake and index reconcile.

Auth model

  • Content writes (POST/PUT/DELETE on plans) require the token, sent as either Authorization: Bearer <token> or X-VisualPlanner-Token: <token>.
  • Reads (GET) and reader actions (PUT /api/plans/:id/read) are always unauthenticated, even when a token is set.
  • The SPA asks for the token once (a "Token required" dialog the first time you delete a plan), stores it in localStorage, and sends it as X-VisualPlanner-Token on content writes — the same token agents use.
  • If VISUALPLANNER_TOKEN is unset or literally change-me, auth is disabled entirely. The server then refuses to bind beyond localhost — if no token is set and the resolved bind address is not loopback, startup aborts with an error. Set a real token to listen on a Tailscale interface.
  • The API is same-origin only (no CORS headers). The SPA is served from the same origin, so it works unchanged; other websites cannot read plans cross-origin. Reads are still unauthenticated, so anyone who can reach the host directly can read plans — keep sensitive content out of them.

Generate a token with:

openssl rand -hex 32

REST API

Method Path Auth Description
GET /api/plans none List plan metadata, newest-first.
GET /api/plans/:id none Get one plan's metadata.
GET /api/plans/:id/html none Get a plan's raw HTML (text/html).
POST /api/plans token Create a plan. Returns 201 { plan }.
PUT /api/plans/:id token Update a plan in place (id stable).
DELETE /api/plans/:id token Delete a plan. Returns { ok: true }.
PUT /api/plans/:id/read none Mark a plan read/unread. Body: { read: boolean }.
GET /api/events none SSE stream: created/updated/deleted events.
GET /api/search?q= none Full-text search inside plan HTML. Returns { results: [{ id, snippet }] }.

Create / update body

{
  "project": "my-project",        // bare repo name; default "untitled"
  "title": "Refactor the foo",    // default "Untitled plan"
  "sessionId": "abc123",          // optional
  "type": "refactor",             // refactor|feature|bugfix|architecture|research; default "architecture"
  "scope": "frontend",            // backend|frontend|infra|tooling|docs; default "backend"
  "template": "refactor-migration", // ui-change|architecture-decision|backend-contract|bug-diagnosis|refactor-migration|research-brief
  "html": "<!doctype html>..."    // required, non-empty (create only)
}

PUT accepts any subset of the above.

template controls the plan's document contract while type and scope remain independent metadata facets. If omitted, the server derives a compatible template from the existing type and scope values, so older agents and catalogs continue to work.

The plan id is derived as slugify(project)-slugify(title), with a -N suffix appended on collision (e.g. my-project-refactor-the-foo-2).

Example

curl -X POST http://localhost:7070/api/plans \
  -H "Authorization: Bearer $VISUALPLANNER_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"project\":\"demo\",\"title\":\"hello\",\"html\":\"<h1>Hi</h1>\"}"

Project layout

src/
  client/        Vite + React SPA (editorial "desk at night" feed UI)
  components/    Vendored shadcn ui/ primitives (used by the token prompt)
  server/        Hono + Bun API, disk-backed Store, SSE events, config, auth
  shared/        Types shared between client and server
skill/           Agent authoring skill + Editorial house style and templates

The skill/ directory holds the publishing skill that agents use to generate plans in the VisualPlanner Editorial house style, plus plan.css, gallery.html, and six content contracts in skill/templates/.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages