FS
← Back
● PRIVATE — Daily Driver

AgentOps Dashboard

Local read-only web dashboard that makes AI agent internals visible. Built for personal daily use — 14 data domains including a Network session inspector for conversation replay and tool-call auditing, multi-agent support (Claude Code, Gemini CLI and Google Antigravity CLI), a live quota view for Claude and AGY, an in-app terminal, a native macOS desktop app, and a custom workflow integration with claude-mem for cross-session context.

Next.jsReact 19TypeScriptFastAPIPythonTailwind CSS 4shadcn/uiSQLiteCaddyTailscale
14Data Domains
3Agents (Claude, Gemini, AGY)
macOSNative Desktop App (Tauri)
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 :9999→FastAPI :8000→Filesystem readers+SQLite (claude-mem)
Web: PM2 runs both processes via start.sh / stop.sh, Caddy + Tailscale for remote access. Desktop: Tauri bundles the backend and serves a static export.

// 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.
▪
Multi-agent architecture: Every agent-aware endpoint accepts ?agent=claude|gemini|antigravity. Readers branch on this param to select the correct directories (~/.claude/, ~/.gemini/, ~/.gemini/antigravity-cli/). The frontend AgentContext drives all page re-fetches on toggle. Agent-agnostic endpoints (e.g. tokens) accept but ignore the param.
▪
Antigravity (AGY) Integration: Extended 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.
▪
Native macOS desktop app: Packaged as a Tauri shell that supervises a PyInstaller-bundled FastAPI sidecar (restart policy, log file, parent watchdog) and serves a static Next.js export. Desktop mode swaps the web login for a per-launch token and bootstrap cookie, enforces a trusted host and CSP, recovers the login-shell PATH for GUI launches, and ships as an arm64 dmg via a single build script.
▪
Live Claude + AGY quota: Neither CLI exposes quota through an API, so the backend drives each TUI through a pty, sends /usage, strips the ANSI output and parses session / weekly / 5-hour limits into a cached payload. A parse that finds no limits is never cached, so a format change in the CLI shows an explicit error on the dashboard instead of a stale empty card.
▪
One-click terminals: The UI is global; endpoints still accept an optional, validated per-request project param. Project cards open the project in a new iTerm window, cd the current iTerm window into it (Shift for a new tab), or launch an in-app PTY terminal — paths are shell- and AppleScript-escaped before they reach osascript.
▪
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 by default: Config readers never mutate agent config or memory. Writes are limited to explicit user actions (file editor, terminal, open in editor) and every HTTP route sits behind a server-side API key. CORS locked to localhost:9999 plus an optional Tailscale origin.
▪
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.

// Assistant IN PROGRESS

A chat page that turns the dashboard into a personal assistant: a tool-calling agent loop that streams through a local LLM gateway, with a model selector and a status line for usage. Tools are added one at a time, each with its own guardrails and tests.

▪
Agent loop + model selector: The backend runs the tool-calling loop and streams deltas to the chat UI. The model is picked per conversation, and the stream survives page navigation through a module-level store.
▪
Read-only file and git tools: The agent can list, read, grep and find files and run git_log / git_diff, but only inside folders the user picks in a Folders picker. Roots must sit inside $HOME, credential dirs and secret-like files are never readable, symlinks cannot escape a root, and git runs with hooks and fsmonitor disabled and bounded output.
▪
Apple Notes and Apple Calendar: Notes are read through AppleScript; Calendar goes through a small signed Swift CLI on EventKit (list calendars and events, create and delete events), with calendar entitlements declared for the desktop app's Hardened Runtime.
▪
Next: More personal-data tools behind the same pattern, and a debug panel to exercise each tool live before the agent uses it.

// 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
Memory— CLAUDE.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/
Tokens— codeburn CLI — 5-min /tmp cache, mock fallback
MCP— Per-provider MCP servers — connection strings, headers and command secrets redacted
Terminal— In-app PTY terminal, project-scoped
Network— JSONL transcript logs — session replay + tool call inspection
Tooling— Homebrew + npm + pip + Cargo + Go + RubyGems
AgentOps Dashboard — main overview

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