feat: TUI dashboard overhaul — codeburn-inspired, done better#24
feat: TUI dashboard overhaul — codeburn-inspired, done better#24AbhilashSri merged 19 commits intomainfrom
Conversation
4f3a151 to
f20e73a
Compare
…er, one-shot rate
Session model gains two new fields:
- tool_breakdown: {Edit: 5, Bash: 3, Read: 12} — actual tool names
extracted from Claude Code JSONL tool_use content blocks
- models_used: {opus-4-6: 8, sonnet-4-6: 12} — per-message model ID
Claude collector now extracts tool_use block names from JSONL
(was counting but discarding the name field).
Activity classifier uses real tool call data when available:
- Edit/Write -> coding, Read/Grep -> exploration
- Bash with test commands -> testing, git commands -> git_ops
- Prompt keywords for debugging/refactoring
- Falls back to keyword-only for tools without tool_breakdown
Dashboard rewritten with Rich text panels:
- Cost by project, cost by model (horizontal bars)
- Daily cost sparkline (unicode block chars)
- Activity breakdown (8 categories, from real data)
- One-shot rate (% of edits passing without retry)
- Tool list with status, sessions, tokens, cost
- No plotext dependency on dashboard tab
README rewritten with full feature documentation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
f20e73a to
86b72d3
Compare
New API: GET /api/activity — returns activity classification, one-shot rate, and cost by project (all deterministic, no LLM). Web Overview tab gains two new panels in the right sidebar: - Activity: bar chart with 8 categories + one-shot rate badge - Cost by Project: horizontal bars with dollar amounts Now consistent with TUI dashboard — same data, same panels. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix: days=0 now returns ALL sessions (was defaulting to 7) - Sort: Recent, Top Cost, Least Cost, Most Tokens, Longest - Tool breakdown chips on each session row (Edit 5, Bash 3, Read 12) - Detail pane shows tool_breakdown + models_used as chips - Model chips show short names (opus-4-6, sonnet-4-6) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Workflow panel showed fake "efficiency per dollar" metrics that can't be computed from token data alone. Removed panel and script. Activity + Cost by Project panels already cover this better. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Major bugs fixed:
- Analyze 3 sessions no longer shows $11K (global model cost).
Now computes cost from exact per-model tokens in selected sessions.
- Cache hit rate 9990% → fixed. Backend sends percentage (99.9),
frontend was multiplying by 100 again.
- models_used now stores full token breakdown per model per session:
{inputTokens, outputTokens, cacheReadInputTokens, count}
No more 30/70 estimation — exact data from JSONL.
- Stats (tokens, cost, sessions) scoped to selected sessions only.
Also: README updated, workflow panel removed, test fixed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Top bar was showing billed tokens (17.7M) while model panel showed total including cache (5.0B). Now uses model usage data when available — shows the real total across all models. Label changes to "Total Tok" when model data is used. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Model Usage panel was showing 5.0B for Opus because it included cacheReadInputTokens. Now shows billed tokens (input+output) which matches the top bar total. Cache reads are still used for cost calculation but not displayed as the primary token count. Top bar: 17.8M (billed) — correct, unchanged. Model panel: now also shows billed per model — numbers match. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Each model row now shows a breakdown line: in 1.2M out 8.4M cache 4.9B Hover tooltip also shows full breakdown. Numbers now match top bar — billed tokens as primary, cache as detail. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Web gains a Daily Cost histogram (was only in TUI); TUI gains an Hourly Activity sparkline (was only on web). Both dashboards now surface the same core insights. - web: new Daily Cost panel rendered from sessions, with local- timezone bucketing and total/avg/peak footer - tui: new HourlyActivityPanel; dashboard grid reworked to 4 rows to fit 7 panels cleanly
install.sh now clones the repo, runs setup, installs a PATH shim in ~/.local/bin, and asks the user which mode to launch (web/TUI × real/ demo). Works end-to-end from a single `curl | bash` for non-devs. README "Install" section replaced with the one-liner plus the four post-install commands.
AnalysisView inherits from VerticalScroll so the page scrolls past the charts into the Insights block. Previously sat inside a plain Static, so long LLM-generated insights were stuck off-screen with no way to reach them.
Previously each panel rendered only the sparkline plus a dim border-subtitle, so the bars had no visible X-axis context. Both panels now render three lines: a bright inline summary at the top (total / avg / peak), the sparkline filling the middle, and a dim-cyan X-axis strip at the bottom — dates for daily cost, hours for hourly activity — spread evenly across the panel width.
DashboardView now inherits from VerticalScroll, so the whole panel stack scrolls when the terminal height can't fit all 7 panels. The StatsBar stays docked at the top (doesn't scroll away). The TOOLS row uses height: auto with min-height so it grows to fit all collectors instead of being clipped by a fixed 1fr allocation.
Rewrites the README around launch-ready marketing surface: a 5-MB hero GIF at the top, a short personal-narrative "Why this exists" block, a comparison table against ccusage / cursor-stats / the Anthropic Console, and a Roadmap section so people can star-to- follow. All factual content (install, data sources, activity classification, Map-Reduce-Generate optimizer, privacy, config) is preserved verbatim — this is marketing polish, not feature drift.
Replaces the implicit plain-bullet description of the TUI with an actual 21-second screen recording (GIF) showing the 7 panels, the scrolling dashboard, axis labels, and tab navigation. Paired with the web GIF at the top so the README proves what it describes. Also added repo topics via gh (claude-code, cursor, kiro, copilot, codex, llm-observability, developer-tools, terminal-ui, ai-coding-tools, cost-tracking) for search discoverability.
Previously the chart defaulted to the last 30 days when the user picked "All time", so users with 6 months of usage only saw a month. Now when days=0 the panel derives the range from the earliest session's timestamp and renders that many bars. Fixed windows (1/7/30d) are unchanged. Empty state still falls back to 7 days so the panel never collapses to nothing. Same logic in the TUI (dashboard.py) and the web panel (panels.js).
Landing view now shows the last 30 days (CLI, TUI, web selector and App.days all default to 30). When the user explicitly picks "All time", the Daily Cost chart auto-buckets: ≤ 60 days → daily bars ≤ 365 days → weekly bars > 365 → monthly bars Summary and X-axis labels adapt their units (/d, /wk, /mo). So a user with six months of data sees ~26 weekly bars instead of 180 0.4-char-wide slivers. Same logic in both TUI (dashboard.py) and web (panels.js).
🔴 Critical Issue: Breaking Schema Change Without Migration PathLocation: The Session model has two new fields added: ProblemThis is a breaking schema change. Existing sessions in the database were created without these fields. While the prevents immediate crashes, this causes:
Required ActionBefore merging, please:
The activity classifier and dashboard work is excellent, but this schema change needs proper handling for existing data. Additional NoteThe new (293 lines) and dashboard panels have no test coverage. Consider adding unit tests for these critical analytics functions before or after merge. |
AbhilashSri
left a comment
There was a problem hiding this comment.
🔴 Critical Issue: Breaking Schema Change Without Migration Path
Location: src/agenttop/models.py
The Session model has two new fields added:
- tool_breakdown: dict[str, int] - Field(default_factory=dict)
- models_used: dict[str, Any] - Field(default_factory=dict)
Problem
This is a BREAKING schema change. Existing sessions in the database were created without these fields. While the default_factory=dict prevents immediate crashes, this causes:
- Silent failures - Analysis code expecting these fields will get empty dicts and produce incomplete results
- Data loss - Historical sessions won't have tool breakdown data populated
- Dashboard issues - New panels showing activity breakdown, one-shot rate, and tool frequency will show incomplete/incorrect data for existing sessions
Required Action
Before merging, please:
- Document the migration strategy - How should existing sessions be handled?
- Add a migration script - Populate tool_breakdown and models_used from historical Claude Code JSONL data where available
- OR clearly document that these fields are only available for new sessions going forward
The activity classifier and dashboard work is excellent, but this schema change needs proper handling for existing data.
Adds tests/test_classifier.py (26 tests, all paths): - classify_session tool-breakdown path (coding / exploration / planning / debug-bump override / heavy-edit dominance / unknown-tool fallback) - classify_session prompt-keyword fallback (debugging / refactoring / exploration / default-to-coding / empty-session) - classify_sessions zero-fills all 8 activity categories - compute_oneshot_rate (all-clean / retry signals / no-edits / Hinglish retry keywords) - compute_tool_frequency aggregation across multiple sessions - compute_cost_by_project sorting, rounding, unknown-project handling - compute_cost_by_model (opus / haiku / unknown-family / zero-token exclusion / sort-by-cost) Adds docs/ARCHITECTURE.md clarifying that sessions are re-parsed from source files on every run (no DB-migration concern for new Session fields — the SQLite `sessions` table has never persisted tool_breakdown or models_used, and is not the source of truth for the dashboards). Also documents data availability per collector. Full suite: 274 passed.
|
Thanks @AbhilashSri — addressed both points in On the "breaking schema change"Respectfully, I think there's a confusion between the Pydantic model and the SQLite schema. Checked this carefully: The Also — the DB isn't the source of truth for the dashboards. The web server and TUI both call So the flow is: Every run, every refresh. The Full write-up: On test coverage for the classifierValid point, no pushback. Added
Full suite: 274 passed. |
The legacy drawer UI was replaced by the Analyze tab (session- explorer.js), but optimizer.js still ran init() on DOMContentLoaded and hit a null reference on the old #optimizer-drawer node, throwing a TypeError in the browser console on every page load. Added a guard so init bails cleanly when the drawer nodes aren't in the layout — the rest of the module stays intact in case the drawer is ever reinstated.
✅ Code Review: PR #24 - APPROVED FOR MERGEAll critical issues from the previous review have been successfully addressed! ✅ Session Schema Migration - RESOLVEDPrevious Concern: Breaking schema change without migration path Resolution:
The architecture documentation clearly explains the data flow and why this is not a breaking change. Well done! ✅ Test Coverage Added - RESOLVEDPrevious Concern: No tests for classifier (293 new lines) and dashboard calculations Resolution:
📊 Final Code Quality Assessment
🎯 What's Excellent
🚀 Verdict: APPROVEDOverall Grade: A+ This PR is ready to merge. The architecture documentation and comprehensive test coverage address all previous concerns. Excellent work addressing the review feedback! Reviewed by: @abhilashbivalkar |
✅ Code Review: PR #24 - APPROVED FOR MERGEAll critical issues from the previous review have been successfully addressed! ✅ Session Schema Migration - RESOLVEDPrevious Concern: Breaking schema change without migration path Resolution:
The architecture documentation clearly explains the data flow and why this is not a breaking change. Well done! ✅ Test Coverage Added - RESOLVEDPrevious Concern: No tests for classifier (293 new lines) and dashboard calculations Resolution:
📊 Final Code Quality Assessment
🎯 What's Excellent
🚀 Verdict: APPROVEDOverall Grade: A+ This PR is ready to merge. The architecture documentation and comprehensive test coverage address all previous concerns. Excellent work addressing the review feedback! Reviewed by: @AbhilashSri |
AbhilashSri
left a comment
There was a problem hiding this comment.
Excellent work! All concerns from the previous review have been addressed:
✅ Architecture documentation added (docs/ARCHITECTURE.md)
✅ Comprehensive test coverage added (26 new tests)
✅ All tests passing (274/274)
✅ Schema migration concern clearly explained
This PR is ready to merge. Great job!
Summary
Complete rewrite of the TUI Dashboard tab + Session model upgrade.
Session model gains two new fields:
tool_breakdown: {Edit: 5, Bash: 3, Read: 12}— actual tool call names extracted from Claude Code JSONLtool_usecontent blocksmodels_used: {opus-4-6: 8, sonnet-4-6: 12}— per-message model IDClaude collector now extracts tool_use block names (was counting but discarding the name).
New dashboard panels (Rich text, no plotext):
Activity classifier (
analysis/classifier.py):README rewritten with full feature docs, data extraction details, architecture diagram.
Test plan
.venv/bin/agenttop --demoshows all panels.venv/bin/agenttopshows real data with tool_breakdown🤖 Generated with Claude Code