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 */}
{/* Evidence - short and readable */}
{evidence}
{/* Action + ROI as distinct blocks */}
);
}
// ── 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 (
);
}
// ── 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 ── */}
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 && (
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 */}
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
))}
)}
)}
);
}