FS
← Back
● PRIVATE — Daily Driver

AgentOps Dashboard

Local read-only web dashboard that makes AI agent internals visible. Built for personal daily use — 13 data domains including a Network session inspector for conversation replay and tool-call auditing, expanding agent support (Claude, Gemini, and Google Antigravity CLI in progress), and a custom workflow integration with claude-mem for cross-session context.

Next.jsReact 19TypeScriptFastAPIPythonTailwind CSS 4shadcn/uiSQLiteCaddyTailscale
13Data Domains
3Agents (Claude, Gemini, AGY)
DailyUsage
Read-onlyDesign Constraint

// Problem

Claude Code operates as a black box. Config, skills, rules, memory observations, agent definitions, token usage, installed commands — all scattered across the filesystem with no unified view. Debugging agent behavior or auditing what Claude actually has access to meant manual file reads across dozens of directories.

// My Role

Sole developer. Designed and built from scratch as a personal tool — no external users, no feature requests, no product requirements. Iterated daily based on real usage. The constraint of building for yourself is a useful forcing function: every feature has to justify its existence in the first 30 seconds of opening the app.

// Architecture

Next.js :3000FastAPI :8000Filesystem readers+SQLite (claude-mem)
PM2 manages both processes via start.sh / stop.sh

// Key Engineering Decisions

WAL proxy for SQLite: The claude-mem worker holds an exclusive write lock on its WAL file. A second connection returns 0 frames — the worker won't release. Solution: proxy through the worker's own HTTP API at :37777, paginating 100 rows at a time, with direct SQLite fallback if the worker is unreachable.
Dual-agent architecture: Every API endpoint accepts ?agent=claude|gemini. Readers branch on this param to select the correct directories (~/.claude/ vs ~/.gemini/). The frontend AgentContext drives all page re-fetches on toggle. Agent-agnostic endpoints (tokens, Obsidian) accept but ignore the param.
Antigravity (AGY) Integration (In Progress): Expanding beyond Claude/Gemini to support Google Antigravity. Built a dedicated AGY mode featuring a real-time projects grid, custom URI handlers (antigravity-ide://) to launch the native IDE directly from the web, and a unified hooks visualizer to parse complex lifecycle events.
claude-mem × AGY Integration: Wired the claude-mem MCP plugin into AGY's PreInvocation hook lifecycle so every new AGY session opens with the 50 most recent project observations injected as a system message. The hook reads the daemon port from ~/.claude-mem/worker.pid at runtime — bypassing the bun subprocess entirely — because worker-service.cjs computes port 37700+(uid%100)=37701 while the actual daemon runs on 37777. Calling the HTTP REST API directly (GET /api/observations?limit=50&project=<name>) returns 4k+ bytes of real cross-session context. A per-conversationId lockfile in /tmp/ ensures injection fires only on the first message, not every turn.
Resilient parallel fetch: The dashboard landing page uses Promise.allSettled for dual-agent data fetches. One agent misconfiguration doesn't break the page — partial data renders, errors surface as status pills.
Read-only constraint: No write endpoints. The dashboard never mutates config, memory, or any file. CORS locked to localhost:3000. This constraint removes an entire class of risk and lets me run it persistently without concern.
Security & Infrastructure: Hardened security using a Caddy reverse proxy and enforced Tailscale-only access for sensitive API routes. Both backend and frontend run as PM2 processes managed by start.sh / stop.sh.

// claude-mem × AGY: Cross-Session Memory Integration

The highest-value integration in the stack: wiring the claude-mem MCP plugin — a persistent SQLite observation store that records every Claude Code session — into the Antigravity CLI hook lifecycle, so AGY opens each new conversation with full cross-session context.

AGY hook tree
PreInvocation — claude-mem-context.py → injects 50 obs as system message (first turn only)
PreInvocation — dev-mode-reminder → injects dev hint (first turn only)
PostToolUse — agy-observation-hook.sh → writes new observation to SQLite
Stop — agy-observation-hook.sh → writes final session observation
Port discovery via worker.pid: worker-service.cjs computes its port as 37700+(uid%100) — for uid=501 that's 37701. The running daemon is on 37777 (set in settings.json). Calling the worker via bun from inside a hook subprocess produces the wrong port, spawns a fresh empty worker, and returns no memory. Fix: read ~/.claude-mem/worker.pid at runtime to get the actual port, then call GET /api/observations?limit=50&project=<name> directly over HTTP. Returns 4k+ bytes of real cross-session context instead of zero.
First-turn lockfile pattern: AGY's invocationNum is always 0 — it never increments between messages within a session. Cannot use it to detect first-turn. Instead: touch /tmp/agy-mem-seen-<conversationId> on the first hook fire; return {} immediately on all subsequent fires where the lockfile exists. conversationId is stable for the full session, so the lock correctly fires exactly once per conversation.
snake_case proto fields: AGY's hook return format uses protobuf under the hood. All field names must be snake_case. inject_steps → system_message → system_message is correct. systemMessage (camelCase) is silently ignored — no error, no effect. This was a multi-session debugging blind spot caught via /tmp/agy-claudemem.log (msg_len=0 despite successful HTTP call).
Two-layer memory injection (important distinction): When using AGY as a CCR provider (cli://agy), the MEMORY INJECTED block that appears in CCR's output comes from AGY's PreInvocation hook — it's AGY printing its own injected context to stdout. This is separate from Claude Code's own MCP system-reminder injection. CCR strips the AGY layer by splitting on ━━━ delimiters; the Claude Code layer is handled by the MCP plugin independently.

// Network — Session Inspector

The most operationally useful page in the dashboard. Network reads the local JSONL transcript logs that Claude Code, Gemini CLI, and Antigravity CLI write for every conversation, then surfaces them as a browsable session list with full conversation replay and tool-call inspection.

Session browsing: Two-pane layout: a filterable session list on the left with project name, timestamp, and duration — detail panel on the right. Click any session to load its full transcript. Sessions are discovered by scanning each agent's local log directories, no external API needed.
Conversation history replay: Every user prompt and model response rendered in sequence, preserving the original turn structure. System messages, injected context blocks, and thinking traces are visually distinguished so you can trace exactly what the agent saw at each step.
Tool usage and output inspection: Each tool call is rendered as a collapsible block showing the tool name, arguments, and full output. File edits display diffs, terminal commands show stdout/stderr, and search results are formatted inline. This makes it trivial to audit what the agent actually did versus what it claimed.
Cross-agent unified view: Same UI works across Claude Code, Gemini CLI, and Antigravity CLI sessions. Each agent writes transcripts in a different format (JSONL with varying schemas); the backend normalizes them into a common structure so the frontend renders all three identically.

// Data Domains

Config~/.claude/settings.json — global + project merged
Skills~/.claude/skills/ — frontmatter: name, description, tools
Plugins~/.claude/plugins/cache/ — manifests + GitHub READMEs
Rules~/.claude/rules/ — full Markdown content
MemoryCLAUDE.md + claude-mem worker HTTP API
Agents~/.claude/agents/*.md — model, tools, color
Commands~/.claude/commands/*.md — all 68 commands
Plans~/.claude/plans/*.md — content + slide-over preview
Scripts~/.claude/scripts/
Tokenscodeburn CLI — 5-min /tmp cache, mock fallback
ObsidianFixed vault — agent-agnostic
NetworkJSONL transcript logs — session replay + tool call inspection
ToolingHomebrew + npm + pip + Cargo + Go + RubyGems
AgentOps Dashboard — main overview

[01/06] — Dashboard overview — unified snapshot of the agent environment