DEV Community

Cover image for I built a local-first dashboard for everything Claude Code does on my machine
Creative Brain Inc
Creative Brain Inc

Posted on

I built a local-first dashboard for everything Claude Code does on my machine

Claude Observatory turns ~/.claude into a searchable, visual command center — 16 modules, live SSE updates, zero outbound network. Here's what's inside and why I built it.

Every Claude session, every token, every command — visible

If you use Claude Code daily, your ~/.claude/ directory already contains the answer to most of the questions you keep asking yourself:

  • "How much did I spend on Claude last week, and on which projects?"
  • "Where did I write that prompt three weeks ago?"
  • "Which tool calls are slowest or failing most?"
  • "Which skills, MCPs, and CLAUDE.md files are actually loaded for this project?"
  • "What did I tell Claude to do that I forgot to follow up on?"

The data is right there in JSONL files, SQLite databases, and config blobs scattered across folders. It's just not browsable.

So I built Claude Observatory — a local-first Next.js dashboard that reads ~/.claude/, indexes it into SQLite, and exposes 16 modules + 7 "killer" features. No outbound network, bound to 127.0.0.1, no telemetry. Your data never leaves the machine.

This is the technical tour: what's in the box, how it's wired, and what I learned shipping it.


The dashboard at a glance

Command Center

The Command Center is the home page — today's spend, recent sessions, live status dot, and quick links into every module. There's also a dark theme if you live in the terminal:

Dashboard, dark

The left sidebar is the navigation spine. Every module is a separate route, every route is bookmarkable, and the live status dot at the top tells you whether the file watcher is attached.


The three-layer architecture

~/.claude/                    →  Index Layer            →  UI (Next.js)
├── projects/*.jsonl              ├── parser workers          ├── 16 module routes
├── sessions/                     ├── SQLite + FTS5           ├── live SSE updates
├── todos/                        ├── chokidar watcher        ├── recharts/d3 viz
├── skills/                       ├── API routes              └── monaco editor
├── bash-commands.log             └── SSE stream
├── cost-tracker.log
└── 17+ more sources
Enter fullscreen mode Exit fullscreen mode

Stack: Next.js 16 (App Router) · React 19 · TypeScript strict · Tailwind 4 · shadcn/ui · better-sqlite3 with FTS5 · zod · chokidar · TanStack Query · zustand · recharts · d3 · Monaco Editor.

Why local SQLite over a hosted Postgres? Because the data is local. Round-tripping your conversation history through a cloud DB just to render a chart of your own spend is the wrong tradeoff. better-sqlite3 is synchronous, lives next to the app, and runs the entire 16-module dashboard with sub-50ms p95 on most pages.


The 16 modules

Here's the full list — Command Center · Sessions · Search · Cost & Tokens · Tool Heatmap · Bash Explorer · File History · Skills · MCP Hub · Memory · Todos · Activity · Telemetry · Prompts · Settings · Headless & Agent Mode.

Let me show the ones that surprised me most.

Sessions Explorer

Sessions

Every session, filterable by project / model / status / text content. Click in and you get the full conversation. This is the page I open when I half-remember an exchange and want the verbatim transcript.

Cost & Token Analytics

Cost & Tokens

Daily / weekly / monthly USD + token breakdown, per project and per model. The first time I opened this page on my own data I made three immediate workflow changes. That's what the dashboard is for.

Tool Usage Heatmap

Tool Heatmap

Calendar heatmap + 24h distribution + per-tool table + outliers. p95 = 14.9ms. The "when does Claude actually run" question turns out to be answerable to the hour.

Bash Explorer

Bash Explorer

Every shell command Claude has run on your behalf, with a dangerous-pattern panel that flags rm -rf, force-pushes, sudo, etc. Built over the tool_calls table via json_extract — no schema migration needed.

File History & Diff

File History

Every file Claude has written or edited, with a lazy-loaded Monaco DiffEditor on the detail page. 676 files indexed on my machine. Useful when you want to roll back a specific edit but don't want to read commits.

Skills Library

Skills

Inventory of every skill under ~/.claude/skills/ and ~/.claude/plugins/*/skills/, with FTS5-powered trigger counts. 862 skills on my machine. That's not a typo.

MCP Hub

MCP Hub

Declared (mcp.json) plus observed MCP servers, merged into one view, with env values scrubbed before display. Tells you what's actually wired vs. what you think is wired.

Memory Explorer

Memory

Every CLAUDE.md plus the auto-memory store under ~/.claude/projects/<id>/memory/*.md. Six scopes (global / project / .claude / nested / auto-index / auto). Detail page shows siblings in the sidebar so you can navigate a project's whole memory tree without leaving the page.

Todos

ToDos

TodoWrite items across all sessions, with a 4-status filter and an abandoned-todo panel for things in_progress > 7 days. 30 files, 37 items, 13 abandoned on my machine when I first turned it on. (I had a productive afternoon after that.)

Activity Heatmap

Activity

Calendar heatmap of sessions per day, current streak, busiest hour. Reuses the heatmap component from /tools. Streak tracking turned out to be motivating in a way I didn't expect.

Telemetry

Telemetry

Error rate, slowest tools, retry counts. 30-day window: 4,986 calls, 9.7% error rate, Write at 17.9% is the worst-performing tool. I pivoted away from latency histograms because tool_calls.duration_ms is unpopulated in current JSONL — the honest banner on the page explains it.

Prompt Library

Prompts

Reads ~/.claude/history.jsonl on demand. Top-reused prompts, project facets, slash-command frequency. p95 = 3ms.

Settings (first write-capable surface)

Settings

This is where Mutations live — see "Editing without footguns" below.

Headless & Agent Mode

Headless

Inspects claude -p runs and the agent-harness session feed. Pair this with the API Server module if you want to use your own machine as a Claude server:

API Server


Live updates without polling

The Observatory watches ~/.claude/ in real time with chokidar (400ms trailing-edge debounce). When Claude Code writes a new JSONL line, the SSE stream at /api/stream invalidates the relevant TanStack Query keys and the UI updates without a refresh.

The LiveStatusDot in the sidebar shows one of three states:

  • ● Live (green) — watcher attached, last event was recent
  • ● Starting (amber) — watcher initialising
  • ● Offline (red) — watcher detached

Three colors, one component, zero confusion. I tried react-use-websocket first and ripped it out — SSE is simpler, one-way, survives proxies, and the entire watcher → broadcaster → SSE → query invalidation pipeline fits in about 150 lines.


Editing without footguns

Writes to ~/.claude/ are off by default. To enable them:

  1. Go to /settings
  2. Flip the Mutations toggle ON (persisted in zustand+localStorage)
  3. Every subsequent write request includes an X-Mutations-Confirmed: 1 header
  4. Server-side handlers reject any write without it
  5. Every overwrite creates a timestamped backup (e.g. settings.json.bak.2026-05-15T12-30-00)
  6. An in-memory audit log shows every write attempt — even rejected ones

This is enforced in code, not in UI alone. There is no way to mutate ~/.claude/ from the app without going through the gate. I'm bullish on this pattern for any tool that touches a user's config directory — toggle in client state, header in transit, gate at the server, audit log in memory. Four layers, all cheap.


The 7 killer features

Modules tell you what happened. Killer features change what you can do with it:

  1. Time-Travel Replay & Fork — Git rebase, but for AI conversations.
  2. Ask Your History — Natural-language Q&A over your entire Claude history.
  3. The Twin — A specialized agent fine-tuned on you.
  4. Self-Evolving CLAUDE.md — Your AI gets smarter every week, autonomously.
  5. AI ROI Dashboard — The single number that ends "is Claude worth it?"
  6. Budget Autopilot — Spend governance that runs itself.
  7. Personal Headless API — Your machine becomes a Claude server.

Some are alpha-shipping today:

Ask Your History uses a local prompt-to-SQL pipeline. Type a question, the system generates a SELECT against the index and answers in plain English. "What did I spend on the Reddit SaaS project last month?" becomes a chart and a sentence.

Ask Your History

AI ROI Dashboard answers the one question every team eventually asks. It's not magic — it's just every number you already have, presented as a ratio.

ROI Dashboard

Budget Autopilot sets per-project and per-month caps with anomaly alerts. Burn rate visible at a glance.

Budget Autopilot

Self-Evolving CLAUDE.md watches your sessions and suggests CLAUDE.md improvements weekly. Append, accept, reject — your call, never automatic.

Self-Evolving CLAUDE.md


Privacy: the part most observability tools skip

  • The dev server binds to 127.0.0.1. Never 0.0.0.0.
  • Zero outbound network traffic in the free tier. Cloud sync, AI summaries, and the Headless API are explicit opt-ins.
  • .credentials.json is never read or displayed — the UI only shows a Connected · expires in Nd string.
  • The SQLite index lives next to the app under data/observatory.db and can be wiped at any time with pnpm db:reset.
  • The watcher reads only; it never writes to ~/.claude/ unless Mutations is ON.

If you're going to build a tool that introspects another tool's home directory, the absolute minimum is to be loud about what it does and does not touch. The threat model lives in SECURITY.md and is enforced in tests.


First-run setup

First run, step 1

First run, step 2

pnpm install
pnpm scan:initial    # one-time full index of ~/.claude/
pnpm dev             # serves at http://127.0.0.1:7777
Enter fullscreen mode Exit fullscreen mode

The initial scan is the slow part — everything after that is incremental via the watcher. On a populated ~/.claude/ the first scan takes about a minute. After that the dashboard opens cold in under a second.


What I'd build differently if I started over

Three things, briefly:

  1. Start with the watcher, not the scanner. I built the full-scan pipeline first and bolted live updates on later. Building incremental from day one would have been cleaner.
  2. Treat tool_calls.duration_ms as aspirational. It's not populated reliably in current JSONL. Don't build histograms on top of fields that may not exist.
  3. One gate, four layers. The Mutations pattern (toggle → header → server check → audit log) is the design I'm most proud of. Worth doing from the start, not retrofitted.

If you are interested in trying it out

DM me and I will send you the repository. It's going to be an open source, MIT for the Solo tier. Bind to 127.0.0.1, run pnpm scan:initial, and open http://127.0.0.1:7777. If you use Claude Code daily, give it ten minutes — and let me know what you find on /cost and /tools.

If you build a module on top of it, I want to see it. There are 16 today; the architecture is a parser layer + a query layer + a route, and adding a 17th is genuinely a single afternoon.


Andy / Creative Brain Inc. — Toronto, Ontario. Find me at andy@creativebrain.ca.

Top comments (0)