import { createFileRoute } from "@tanstack/react-router"; import { useEffect, useState } from "react"; import { api, type AnalyticsData } from "@/lib/api"; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Separator } from "@/components/ui/separator"; import { Brain, TrendingUp, AlertTriangle, CheckCircle, Zap, FileCode, GitBranch, Clock, Shield, Activity, Cpu, Lightbulb, BookOpen, Wrench, Users, MessageSquare, Search, FolderOpen, Code, } from "lucide-react"; export const Route = createFileRoute("/")({ component: Dashboard }); const COLORS = ["#3b82f6", "#8b5cf6", "#10b981", "#f59e0b", "#ef4444", "#06b6d4", "#ec4899", "#84cc16"]; // ── Insight types ── interface Insight { icon: React.ReactNode; severity: "positive" | "warning" | "critical" | "neutral"; metric: string; evidence: string; action: string; roi: string; } const SEV_STYLES = { positive: { border: "border-emerald-500/30", bg: "bg-emerald-500/5", badge: "bg-emerald-500/15 text-emerald-400", label: "Nice" }, warning: { border: "border-amber-500/30", bg: "bg-amber-500/5", badge: "bg-amber-500/15 text-amber-400", label: "Heads up" }, critical: { border: "border-red-500/30", bg: "bg-red-500/5", badge: "bg-red-500/15 text-red-400", label: "Fix this" }, neutral: { border: "border-blue-500/30", bg: "bg-blue-500/5", badge: "bg-blue-500/15 text-blue-400", label: "FYI" }, }; function InsightCard({ icon, severity, metric, evidence, action, roi }: Insight) { const s = SEV_STYLES[severity]; return ( {/* Header: badge + title */}
{s.label}
{/* Main metric - the headline */}
{icon}

{metric}

{/* Evidence - short and readable */}

{evidence}

{/* Action + ROI as distinct blocks */}

What to do

{action}

Why it matters

{roi}

); } // ── Big number stat ── function Stat({ label, value, sub, icon: Icon, color }: { label: string; value: string | number; sub: string; icon: typeof Zap; color: string; }) { return ( {label}
{value}

{sub}

); } // ── Ratio bar ── function RatioBar({ items }: { items: { label: string; value: number; color: string }[] }) { const total = items.reduce((a, b) => a + b.value, 0); if (total === 0) return null; return (
{items.map((item, i) => (
))}
{items.map((item, i) => ( {item.label}: {item.value} ({Math.round(100 * item.value / total)}%) ))}
); } // ── Mini number ── function Mini({ label, value, color }: { label: string; value: string | number; color?: string }) { return (
{value}

{label}

); } // ── Insights engine ── function generateInsights(d: AnalyticsData): Insight[] { const ins: Insight[] = []; const t = d.totals; // ── Reading more than writing if (t.reads + t.writes >= 5) { const ratio = t.readWriteRatio; if (ratio > 5) { ins.push({ icon: , severity: "neutral", metric: `You read ${ratio}x more than you write`, evidence: `${t.reads} files read vs ${t.writes} files written. You're spending most of your AI time understanding code, not producing it.`, action: "Write a short plan before starting. Clarify what you want to change before reading everything.", roi: "Planning before coding reduces rework by 40%.", }); } else if (ratio < 2 && ratio > 0) { ins.push({ icon: , severity: "positive", metric: `Healthy balance: ${ratio}:1 read-to-write`, evidence: `You're reading just enough to write confidently. ${t.writes} files written with only ${t.reads} reads.`, action: "Keep this up — you're not over-analyzing or guessing blind.", roi: "This balance leads to fewer bugs on first attempt.", }); } } // ── Context filling up if (t.totalSessions >= 3) { const pct = t.compactRate; if (pct > 60) { ins.push({ icon: , severity: "warning", metric: `${pct}% of your sessions run out of context`, evidence: `${t.totalCompacts} times the agent had to compress your conversation. Each time, it forgets details from earlier work.`, action: "Start fresh sessions for new tasks instead of continuing long ones.", roi: `You'd recover ~${Math.round(t.totalCompacts * 3)} minutes of re-explaining lost context.`, }); } else if (pct < 20 && t.totalSessions >= 5) { ins.push({ icon: , severity: "positive", metric: `Only ${pct}% context overflow — you're efficient`, evidence: `${t.totalCompacts} compactions in ${t.totalSessions} sessions. Your tasks are well-scoped.`, action: "Share this pattern with your team — most developers hit 60%+.", roi: "Less context loss = less time re-explaining = faster completion.", }); } } // ── Errors if (t.totalEvents >= 20) { const rate = t.errorRate; if (rate > 10) { ins.push({ icon: , severity: "critical", metric: `${rate}% of your tool calls fail`, evidence: `${t.totalErrors} errors in ${t.totalEvents} calls. That's a lot of wasted back-and-forth.`, action: "Check if the same error keeps repeating. Add a CLAUDE.md rule to prevent it.", roi: `Fixing this saves ~${Math.round(t.totalErrors * 2)} minutes of retry loops.`, }); } else if (rate < 3) { ins.push({ icon: , severity: "positive", metric: `Only ${rate}% error rate — you're a power user`, evidence: `${t.totalErrors} errors in ${t.totalEvents} calls. Almost everything works on the first try.`, action: "Document your prompting style — others can learn from it.", roi: "Clean usage means more time building, less time debugging.", }); } } // ── Parallel work if (t.totalSessions >= 2) { if (t.totalSubagents > 0 && d.subagents.bursts > 0) { ins.push({ icon: , severity: "positive", metric: `You saved ~${d.subagents.timeSavedMin} min with parallel agents`, evidence: `${d.subagents.parallelCount} tasks ran simultaneously in ${d.subagents.bursts} bursts (up to ${d.subagents.maxConcurrent} at once).`, action: "Keep delegating research and exploration to subagents.", roi: "Parallel work is the single biggest time multiplier available to you.", }); } else if (t.totalSubagents === 0 && t.totalEvents > 50) { ins.push({ icon: , severity: "neutral", metric: "Everything ran sequentially", evidence: `${t.totalEvents} events, zero parallel agents. You're doing one thing at a time.`, action: "Try subagents for research — fire 3-5 at once instead of doing them one by one.", roi: "One parallel burst can turn 10 minutes of research into 2 minutes.", }); } } // ── Prompt efficiency if (t.totalPrompts >= 5 && t.totalSessions >= 2) { const pps = t.promptsPerSession; if (pps < 3) { ins.push({ icon: , severity: "positive", metric: `${pps} prompts per session — clear instructions`, evidence: `You give the agent clear, complete instructions. It works autonomously without needing constant guidance.`, action: "This is the ideal. Keep writing comprehensive upfront instructions.", roi: "Every avoided back-and-forth saves context for actual work.", }); } } // ── Mastery trend if (d.masteryTrend && d.masteryTrend.length >= 3) { const first = d.masteryTrend[0]; const last = d.masteryTrend[d.masteryTrend.length - 1]; if (last.error_rate < first.error_rate) { ins.push({ icon: , severity: "positive", metric: "Your error rate is dropping — you're getting better", evidence: `Week 1: ${first.error_rate}%, Latest: ${last.error_rate}%. Consistent improvement over ${d.masteryTrend.length} weeks.`, action: "Keep refining your prompting patterns.", roi: "Lower errors = less retry time = faster completion.", }); } } // ── Commit rate if (t.commitsPerSession > 1) { ins.push({ icon: , severity: "positive", metric: `${t.commitsPerSession} commits per session — high output`, evidence: `${t.totalCommits} total commits across ${t.totalSessions} sessions.`, action: "Maintain this shipping cadence.", roi: "Frequent commits reduce merge conflicts and context loss.", }); } // ── Edit-test cycles if (t.totalEditTestCycles > t.totalSessions * 2) { ins.push({ icon: , severity: "warning", metric: `${t.totalEditTestCycles} edit-error cycles — lots of retry loops`, evidence: `${(t.totalEditTestCycles / t.totalSessions).toFixed(1)} cycles per session on average.`, action: "Write tests first, or add patterns to CLAUDE.md to prevent common errors.", roi: "Fewer retry loops means faster task completion.", }); } // ── Task structure if (t.totalTasks > 0 && t.totalSessions >= 2) { ins.push({ icon: , severity: "positive", metric: `${(t.totalTasks / t.totalSessions).toFixed(1)} tasks per session — you plan ahead`, evidence: `${t.totalTasks} structured tasks across ${t.totalSessions} sessions. You break work into steps.`, action: "Keep using tasks — they make sessions resumable after breaks.", roi: "Structured sessions finish 30% faster than open-ended ones.", }); } return ins; } // ── Dashboard ── function Dashboard() { const [data, setData] = useState(null); useEffect(() => { api.analytics().then(setData); }, []); if (!data) return

Loading analytics...

; const t = data.totals; const insights = generateInsights(data); // Compute derived values const topTool = data.toolUsage[0]; const topMcp = data.mcpTools[0]; const peakHour = data.hourlyPattern.reduce((max, h) => h.count > (max?.count || 0) ? h : max, data.hourlyPattern[0]); const topProject = data.projectActivity[0]; const topFile = data.fileActivity[0]; return (

Dashboard

Personal insights · {t.totalSessions} sessions · {t.totalEvents} events

{/* KPI Strip */}
60 ? "text-amber-500" : "text-emerald-500"} /> 10 ? "text-red-500" : "text-emerald-500"} />
{/* Insights */} {insights.length > 0 && (

Insights & Actions

{insights.length}
{insights.map((ins, i) => )}
)} {/* ── Tool Usage ── */}
Tool Usage
What the agent does for you
{data.toolUsage.slice(0, 8).map((tool, i) => { const pct = Math.round(100 * tool.count / t.totalEvents); return (
{tool.tool} {tool.count} ({pct}%)
); })}
{/* ── MCP Tools ── */}
context-mode Tools
How you use the sandbox
{data.mcpTools.length > 0 ? ( <>
a + b.count, 0)} />
({ label: m.tool, value: m.count, color: COLORS[i % COLORS.length], }))} />
{data.mcpTools.slice(0, 6).map((m, i) => (
{m.tool} {m.count}
))}
) :

No MCP data yet

}
{/* ── Session Activity + When You Code ── */}
Session Activity
Your AI usage over time
{data.sessionsByDate.length > 0 && (
{data.sessionsByDate.slice(-7).map((d, i) => (
{d.date?.slice(5)}
{Array.from({ length: d.count }).map((_, j) => (
))} {d.compacts > 0 && Array.from({ length: d.compacts }).map((_, j) => (
))}
{d.count}s{d.compacts > 0 ? ` ${d.compacts}c` : ""}
))}
Sessions Compactions
)} {/* ── When You Code ── */}
When You Code
Schedule deep work at your peak hours
h.count > 0).length} />
{Array.from({ length: 24 }, (_, i) => { const h = data.hourlyPattern.find(p => p.hour === i); const count = h?.count || 0; const max = peakHour?.count || 1; const opacity = count > 0 ? 0.2 + 0.8 * (count / max) : 0.05; return (
0 ? `rgba(6, 182, 212, ${opacity})` : "hsl(var(--secondary))" }} title={`${String(i).padStart(2, "0")}:00 — ${count} events`} /> {i % 4 === 0 && {i}}
); })}
00:00 12:00 23:00
{/* ── Project Focus + Hot Files ── */}
Project Focus
Where your AI time goes
{data.projectActivity.slice(0, 6).map((p, i) => { const maxEv = data.projectActivity[0]?.events || 1; const pct = Math.round((p.events / maxEv) * 100); const name = p.project_dir?.split("/").filter(Boolean).slice(-2).join("/") || "Unknown"; return (
{name} {p.sessions}s · {p.events}e
); })}
Hot Files
Most interacted — candidates for better tooling
{data.fileActivity.slice(0, 8).map((f, i) => { const parts = f.file?.split("/") || []; const name = parts.pop() || f.file; const dir = parts.slice(-2).join("/"); return (
{f.count} {dir && {dir}/}{name}
); })}
{/* ── Explore/Execute + Work Modes ── */}
{data.exploreExecRatio.total > 0 && (
Explore vs Execute
Reading code vs writing code — your work balance
{(() => { const { explore, execute, total } = data.exploreExecRatio; const ratio = execute > 0 ? (explore / execute).toFixed(1) : explore; const explorePct = Math.round(100 * explore / Math.max(total, 1)); const executePct = 100 - explorePct; return ( <>
6 ? "text-amber-500" : "text-foreground"} />
); })()}
)} {data.workModes.length > 0 && (
Work Modes
How you approach tasks — investigate, implement, review, explore
{(() => { const total = data.workModes.reduce((a, b) => a + b.count, 0); return ( <>
({ label: m.mode, value: m.count, color: COLORS[i % COLORS.length], }))} /> ); })()}
)}
{/* ── Tool Mastery + Commit Rate ── */}
{/* Tool Mastery Curve */}
Tool Mastery
Are you getting better over time?
{data.masteryTrend && data.masteryTrend.length > 0 ? (() => { const last = data.masteryTrend[data.masteryTrend.length - 1]; const first = data.masteryTrend[0]; const improving = last.error_rate < first.error_rate; return ( <>
10 ? "text-amber-500" : ""} />
{data.masteryTrend.slice(-6).map((w, i) => { const maxRate = Math.max(...data.masteryTrend.map(m => m.error_rate), 1); const pct = Math.round((w.error_rate / maxRate) * 100); return (
{w.week?.slice(5)}
10 ? "#f59e0b" : "#3b82f6" }} />
{w.error_rate}%
); })}

{last.error_rate === 0 && first.error_rate <= 1 ? "Near-zero error rate across all weeks — you're writing precise, clean prompts." : improving ? `Error rate dropped from ${first.error_rate}% to ${last.error_rate}% — your skills are improving.` : `Error rate went from ${first.error_rate}% to ${last.error_rate}%. Check what changed — new tools, different project, or prompt drift?`}

); })() :

Not enough data yet

} {/* Commit Rate */}
Commit Rate
How productive are your sessions?
{(() => { const commits = t.totalCommits || 0; const perSession = t.commitsPerSession || 0; const sessionsWithCommit = data.commitRate ? data.commitRate.filter(c => c.commits > 0).length : 0; return ( <>
= 1 ? "text-emerald-500" : "text-muted-foreground"} />

{perSession >= 1 ? "Strong output — you're committing consistently every session." : perSession > 0 ? `${commits} commit in ${t.totalSessions} sessions. Most sessions are research/exploration — commits come in focused bursts.` : "No commits yet. That's fine if you're in exploration or debugging mode — commits will come when you ship."}

); })()}
{/* ── Rules Health + Edit-Test Cycles ── */}
{/* CLAUDE.md Health */}
Rules Health
Are your instruction files maintained?
{data.rulesFreshness && data.rulesFreshness.length > 0 ? (() => { const top = data.rulesFreshness[0]; const topName = top.rule_path?.split("/").pop() || top.rule_path; return ( <>
{data.rulesFreshness.slice(0, 6).map((r, i) => { const name = r.rule_path?.split("/").pop() || r.rule_path; const lastSeen = r.last_seen ? (() => { const diff = Date.now() - new Date(r.last_seen).getTime(); const days = Math.floor(diff / 86400000); return days === 0 ? "today" : days === 1 ? "1d ago" : `${days}d ago`; })() : "unknown"; return (
{name}
{lastSeen} {r.load_count}
); })}
); })() :

No rules data yet

}
{/* Edit-Test Cycles */}
Edit → Error Cycles
Write → fail → fix again loops
{(() => { const cycles = t.totalEditTestCycles || 0; const perSession = t.totalSessions > 0 ? (cycles / t.totalSessions).toFixed(1) : "0"; const sessionsHit = data.editTestCycles ? data.editTestCycles.length : 0; if (cycles === 0) { return (
Zero retry loops

No write→error→rewrite patterns detected. Your code works on the first try — clean prompting and clear instructions pay off.

); } return ( <>
3 ? "text-amber-500" : "text-emerald-500"} />

{Number(perSession) > 3 ? "High retry rate — consider writing tests first or adding patterns to CLAUDE.md to prevent common errors." : `${cycles} retry loops across ${sessionsHit} sessions. Manageable — keep an eye on which files trigger retries.`}

); })()}
{/* ── Git Flow + Parallel Work ── */}
{data.gitActivity.length > 0 && (
Git Flow
Your version control pattern per session
{(() => { const commits = data.gitActivity.filter(g => g.action === "commit").length; const pushes = data.gitActivity.filter(g => g.action === "push").length; // Group by session const sessions = new Map(); data.gitActivity.forEach(g => { if (!sessions.has(g.session_id)) { sessions.set(g.session_id, { project: g.project_dir?.split("/").filter(Boolean).slice(-2).join("/") || "-", actions: [], time: g.created_at, }); } sessions.get(g.session_id)!.actions.push(g.action); }); return ( <>
{[...sessions.entries()].slice(0, 5).map(([sid, s]) => (
{s.project} {s.time?.slice(5, 16)}
{s.actions.map((a, i) => ( {a} ))}
))}
); })()}
)} {data.subagents.total > 0 && (
Parallel Work
How effectively you delegate to subagents
{data.subagents.burstDetails.length > 0 && (

Parallel Bursts

{data.subagents.burstDetails.map((b: { size: number; time: string }, i: number) => (
{b.time?.slice(5, 16)}
{Array.from({ length: b.size }).map((_, j) => (
))}
{b.size} agents
))}
)} )}
); }