Add implementation plans for morning report, Claude ops dashboard, and realtime monitoring features.
11 KiB
Claude Ops Dashboard Implementation Plan
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: Extend the existing Go web dashboard to monitor Claude Code agent activity, context usage, skills/commands usage signals, and cost-related token stats from your local ~/.claude/ directory.
Architecture: Keep the current lightweight Go HTTP server + static HTML/JS frontend. Add a new “Claude Ops” section with API endpoints that parse local Claude Code state files (primarily ~/.claude/stats-cache.json, ~/.claude/history.jsonl, ~/.claude/state/component-registry.json, and directory listings under ~/.claude/agents, ~/.claude/skills, ~/.claude/commands). Frontend gains new navigation tabs + tables/charts using vanilla JS.
Tech Stack: Go 1.21+, chi router, vanilla HTML/CSS/JS (no React), file-based data sources under ~/.claude/.
Scope (YAGNI)
In this first iteration, focus on read-only analytics:
- Daily activity + token usage (from
~/.claude/stats-cache.json) - Recent sessions list (from
~/.claude/history.jsonlwithout parsing full message bodies) - Installed agents/skills/commands inventory (from
~/.claude/agents/,~/.claude/skills/,~/.claude/commands/) - “Debug” view: show which data files are missing/unreadable and last-modified timestamps
Explicitly out of scope:
- Real-time websocket streaming
- Multi-user auth
- Editing/triggering slash commands
- Deep semantic parsing of every history event (we’ll add iteratively)
Task 1: Add Claude directory config to server
Files:
- Modify:
~/.claude/dashboard/cmd/server/main.go - Modify:
~/.claude/dashboard/README.md
Step 1: Write the failing test
Create a minimal unit test that ensures server config defaults to ~/.claude when not specified.
- Create:
~/.claude/dashboard/cmd/server/config_test.go
package main
import (
"os"
"path/filepath"
"testing"
)
func TestDefaultClaudeDir(t *testing.T) {
home, err := os.UserHomeDir()
if err != nil {
t.Fatalf("UserHomeDir: %v", err)
}
want := filepath.Join(home, ".claude")
got := defaultClaudeDir()
if got != want {
t.Fatalf("defaultClaudeDir() = %q, want %q", got, want)
}
}
Step 2: Run test to verify it fails
Run: go test ./...
Expected: FAIL with undefined: defaultClaudeDir
Step 3: Write minimal implementation
- Modify:
~/.claude/dashboard/cmd/server/main.go
Add helper:
func defaultClaudeDir() string {
home, err := os.UserHomeDir()
if err != nil {
return "/home/will/.claude" // fallback; best-effort
}
return filepath.Join(home, ".claude")
}
Add CLI flag:
--claude(default~/.claude)
Step 4: Run test to verify it passes
Run: go test ./...
Expected: PASS
Step 5: Commit
git add cmd/server/main.go cmd/server/config_test.go README.md
git commit -m "feat: add default claude dir config"
Task 2: Add Claude models for API responses
Files:
- Create:
~/.claude/dashboard/internal/claude/models.go
Step 1: Write the failing test
- Create:
~/.claude/dashboard/internal/claude/models_test.go
package claude
import "testing"
func TestModelTypesCompile(t *testing.T) {
_ = StatsCache{}
_ = DailyActivity{}
_ = ModelUsage{}
}
Step 2: Run test to verify it fails
Run: go test ./...
Expected: FAIL with undefined: StatsCache
Step 3: Write minimal implementation
- Create:
~/.claude/dashboard/internal/claude/models.go
Implement structs matching the subset we use:
package claude
type DailyActivity struct {
Date string `json:"date"`
MessageCount int `json:"messageCount"`
SessionCount int `json:"sessionCount"`
ToolCallCount int `json:"toolCallCount"`
}
type DailyModelTokens struct {
Date string `json:"date"`
TokensByModel map[string]int `json:"tokensByModel"`
}
type ModelUsage struct {
InputTokens int `json:"inputTokens"`
OutputTokens int `json:"outputTokens"`
CacheReadInputTokens int `json:"cacheReadInputTokens"`
CacheCreationInputTokens int `json:"cacheCreationInputTokens"`
WebSearchRequests int `json:"webSearchRequests"`
CostUSD float64 `json:"costUSD"`
ContextWindow int `json:"contextWindow"`
}
type StatsCache struct {
Version int `json:"version"`
LastComputedDate string `json:"lastComputedDate"`
DailyActivity []DailyActivity `json:"dailyActivity"`
DailyModelTokens []DailyModelTokens `json:"dailyModelTokens"`
ModelUsage map[string]ModelUsage `json:"modelUsage"`
TotalSessions int `json:"totalSessions"`
TotalMessages int `json:"totalMessages"`
}
Step 4: Run test to verify it passes
Run: go test ./...
Expected: PASS
Step 5: Commit
git add internal/claude/models.go internal/claude/models_test.go
git commit -m "feat: add claude stats response models"
Task 3: Implement Claude file loader
Files:
- Create:
~/.claude/dashboard/internal/claude/loader.go - Test:
~/.claude/dashboard/internal/claude/loader_test.go
Step 1: Write the failing test
package claude
import (
"os"
"path/filepath"
"testing"
)
func TestLoadStatsCache(t *testing.T) {
dir := t.TempDir()
p := filepath.Join(dir, "stats-cache.json")
err := os.WriteFile(p, []byte(`{"version":1,"lastComputedDate":"2025-12-31","totalSessions":1,"totalMessages":2}`), 0644)
if err != nil {
t.Fatalf("WriteFile: %v", err)
}
loader := NewLoader(dir)
stats, err := loader.LoadStatsCache()
if err != nil {
t.Fatalf("LoadStatsCache: %v", err)
}
if stats.TotalSessions != 1 {
t.Fatalf("TotalSessions=%d", stats.TotalSessions)
}
}
Step 2: Run test to verify it fails
Run: go test ./...
Expected: FAIL with undefined: NewLoader
Step 3: Write minimal implementation
Implement Loader with:
NewLoader(claudeDir string)LoadStatsCache() (*StatsCache, error)reads<claudeDir>/stats-cache.jsonListDir(name string) ([]DirEntry, error)foragents/,skills/,commands/FileInfo(path string) (FileMeta, error)for debug view
Step 4: Run test to verify it passes
Run: go test ./...
Expected: PASS
Step 5: Commit
git add internal/claude/loader.go internal/claude/loader_test.go
git commit -m "feat: load claude stats-cache.json"
Task 4: Add new Claude Ops API routes
Files:
- Modify:
~/.claude/dashboard/cmd/server/main.go - Create:
~/.claude/dashboard/internal/api/claude_handlers.go - Modify:
~/.claude/dashboard/internal/api/handlers.go(only if you want shared helpers)
Step 1: Write the failing test
- Create:
~/.claude/dashboard/internal/api/claude_handlers_test.go
package api
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/go-chi/chi/v5"
"github.com/will/k8s-agent-dashboard/internal/claude"
)
type fakeLoader struct{}
func (f fakeLoader) LoadStatsCache() (*claude.StatsCache, error) {
return &claude.StatsCache{TotalSessions: 3}, nil
}
func TestGetClaudeStats(t *testing.T) {
r := chi.NewRouter()
r.Get("/api/claude/stats", GetClaudeStats(fakeLoader{}))
req := httptest.NewRequest(http.MethodGet, "/api/claude/stats", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != 200 {
t.Fatalf("status=%d body=%s", w.Code, w.Body.String())
}
}
Step 2: Run test to verify it fails
Run: go test ./...
Expected: FAIL with undefined: GetClaudeStats
Step 3: Write minimal implementation
Create endpoints:
GET /api/claude/health→ returns{status:"ok", claudeDir:"..."}and file presence checksGET /api/claude/stats→ returns parsedStatsCacheGET /api/claude/inventory→ lists agents/skills/commands entriesGET /api/claude/debug/files→ returns file metas for key files and last-modified
Wire in cmd/server/main.go:
- Build loader from
--claudeflag - Register routes under
/api/claude
Step 4: Run test to verify it passes
Run: go test ./...
Expected: PASS
Step 5: Commit
git add cmd/server/main.go internal/api/claude_handlers.go internal/api/claude_handlers_test.go
git commit -m "feat: add claude ops api endpoints"
Task 5: Add new UI navigation tabs
Files:
- Modify:
~/.claude/dashboard/cmd/server/web/index.html - Modify:
~/.claude/dashboard/cmd/server/web/static/css/style.css
Step 1: Make a minimal UI change (no tests)
Add nav buttons:
- Overview
- Usage
- Inventory
- Debug
Add new <section> elements mirroring existing “views” pattern.
Step 2: Manual verification
Run: go run ./cmd/server --port 8080 --data /tmp/k8s --claude ~/.claude
Expected: New tabs switch views (even if empty).
Step 3: Commit
git add cmd/server/web/index.html cmd/server/web/static/css/style.css
git commit -m "feat: add claude ops dashboard views"
Task 6: Implement frontend data fetching + rendering
Files:
- Modify:
~/.claude/dashboard/cmd/server/web/static/js/app.js
Step 1: Add API calls
Add functions:
loadClaudeStats()→GET /api/claude/statsloadClaudeInventory()→GET /api/claude/inventoryloadClaudeDebugFiles()→GET /api/claude/debug/files
Integrate into loadAllData().
Step 2: Add render functions
- Overview: show totals + lastComputedDate
- Usage: simple table for
dailyActivity(date, messages, sessions, tool calls) - Inventory: 3 columns lists: agents, skills, commands
- Debug: table of key files with status/missing + mtime
Step 3: Manual verification
Run: go run ./cmd/server --port 8080 --data /tmp/k8s --claude ~/.claude
Expected: Data populates from your local ~/.claude/stats-cache.json.
Step 4: Commit
git add cmd/server/web/static/js/app.js
git commit -m "feat: render claude usage and inventory data"
Task 7: Add “cost optimization” signals (derived)
Files:
- Modify:
~/.claude/dashboard/internal/api/claude_handlers.go - Modify:
~/.claude/dashboard/internal/claude/models.go
Step 1: Write the failing test
Add a test that expects derived fields:
- cache hit ratio estimate:
cacheReadInputTokens / (inputTokens + cacheReadInputTokens + cacheCreationInputTokens)(best-effort) - top model by output tokens
Step 2: Run test to verify it fails
Run: go test ./...
Expected: FAIL because fields aren’t present
Step 3: Implement derived summary endpoint
GET /api/claude/summaryreturns:- totals
- per-model tokens
- derived cost signals
Step 4: Run tests
Run: go test ./...
Expected: PASS
Step 5: Commit
git add internal/api/claude_handlers.go internal/claude/models.go internal/api/claude_handlers_test.go
git commit -m "feat: add claude summary and cache efficiency signals"
Task 8: End-to-end check
Files:
- None
Step 1: Run tests
Run: go test ./...
Expected: PASS
Step 2: Run server locally
Run: go run ./cmd/server --port 8080 --data /tmp/k8s --claude ~/.claude
Expected: Browser shows Claude Ops tabs with live data.
Notes / Follow-ups (later iterations)
- Parse
~/.claude/history.jsonlinto “sessions” and show recent slash commands usage (requires schema discovery) - Add “top tools called” chart (requires richer history parsing)
- Add alert thresholds (e.g., token spikes day-over-day)