Skip to content

feat: TUI dashboard overhaul — codeburn-inspired, done better#24

Merged
AbhilashSri merged 19 commits intomainfrom
feat/tui-dashboard-overhaul
Apr 17, 2026
Merged

feat: TUI dashboard overhaul — codeburn-inspired, done better#24
AbhilashSri merged 19 commits intomainfrom
feat/tui-dashboard-overhaul

Conversation

@vicarious11
Copy link
Copy Markdown
Owner

@vicarious11 vicarious11 commented Apr 15, 2026

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 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 (was counting but discarding the name).

New dashboard panels (Rich text, no plotext):

  • Cost by Project — horizontal bars with dollar amounts
  • Cost by Model — per-model cost breakdown
  • Daily Cost — unicode sparkline with total/avg/peak
  • Activity Breakdown — 8 categories classified from actual tool calls
  • One-shot Rate — % of edits that pass without retry
  • Tools — colored list with status, sessions, tokens, cost

Activity classifier (analysis/classifier.py):

  • Uses real tool_breakdown data when available (Edit/Write = coding, Read/Grep = exploration, Bash+pytest = testing)
  • Falls back to prompt keywords for tools without tool call data
  • 8 categories: coding, debugging, testing, exploration, refactoring, git_ops, planning, other

README rewritten with full feature docs, data extraction details, architecture diagram.

Test plan

  • 248 tests passing
  • Ruff clean on all changed files
  • Manual: .venv/bin/agenttop --demo shows all panels
  • Manual: .venv/bin/agenttop shows real data with tool_breakdown

🤖 Generated with Claude Code

@vicarious11 vicarious11 force-pushed the feat/tui-dashboard-overhaul branch 3 times, most recently from 4f3a151 to f20e73a Compare April 15, 2026 09:47
…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>
@vicarious11 vicarious11 force-pushed the feat/tui-dashboard-overhaul branch from f20e73a to 86b72d3 Compare April 15, 2026 10:06
vicarious11 and others added 3 commits April 15, 2026 15:47
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>
@vicarious11 vicarious11 requested a review from AbhilashSri April 17, 2026 06:45
vicarious11 and others added 13 commits April 17, 2026 12:40
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).
@AbhilashSri
Copy link
Copy Markdown
Collaborator

🔴 Critical Issue: Breaking Schema Change Without Migration Path

Location:

The Session model has two new fields added:

Problem

This is a breaking schema change. Existing sessions in the database were created without these fields. While the prevents immediate crashes, this causes:

  1. Silent failures - Analysis code expecting these fields will get empty dicts and produce incomplete results
  2. Data loss - Historical sessions won't have tool breakdown data populated
  3. 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:

  1. Document the migration strategy - How should existing sessions be handled?
  2. Add a migration script - Populate and from historical Claude Code JSONL data where available
  3. 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.


Additional Note

The new (293 lines) and dashboard panels have no test coverage. Consider adding unit tests for these critical analytics functions before or after merge.

Copy link
Copy Markdown
Collaborator

@AbhilashSri AbhilashSri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 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:

  1. Silent failures - Analysis code expecting these fields will get empty dicts and produce incomplete results
  2. Data loss - Historical sessions won't have tool breakdown data populated
  3. 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:

  1. Document the migration strategy - How should existing sessions be handled?
  2. Add a migration script - Populate tool_breakdown and models_used from historical Claude Code JSONL data where available
  3. 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.
@vicarious11
Copy link
Copy Markdown
Owner Author

Thanks @AbhilashSri — addressed both points in 87a9316.

On the "breaking schema change"

Respectfully, I think there's a confusion between the Pydantic model and the SQLite schema. Checked this carefully:

The sessions table in db.py doesn't store tool_breakdown or models_used. Never has. Look at upsert_session — it only persists 10 columns (id, tool, project, start_time/end_time, message_count, tool_call_count, total_tokens, estimated_cost_usd, prompts). The new fields were added to the Pydantic model, not the DB schema.

Also — the DB isn't the source of truth for the dashboards. The web server and TUI both call collector.collect_sessions() on every refresh, which re-parses the raw JSONL / SQLite files under ~/.claude/, ~/.cursor/, etc. You can confirm this in src/agenttop/web/server.py:117 (_collect_all_sessions) — no DB reads in the hot path. The SQLite store is a future-use cache for the suggestions feature.

So the flow is:

~/.claude/**/*.jsonl  →  ClaudeCodeCollector  →  fresh Session objects (populated)  →  dashboard

Every run, every refresh. The default_factory=dict exists for collectors that don't expose tool-call data (Cursor, Kiro, Codex, Copilot) — not as a migration safety net.

Full write-up: docs/ARCHITECTURE.md — includes a data-availability matrix per collector.

On test coverage for the classifier

Valid point, no pushback. Added tests/test_classifier.py — 26 tests covering:

  • classify_session tool-breakdown path (coding / exploration / planning, debug-bump override behaviour, heavy-edit dominance, unknown-tool fallback)
  • classify_session keyword-fallback path (all 7 categories)
  • classify_sessions zero-fill aggregation
  • compute_oneshot_rate (clean / retry signals / Hinglish / no-edits)
  • compute_tool_frequency
  • compute_cost_by_project (sort, round, unknown-project)
  • compute_cost_by_model (opus / haiku / unknown-family / zero-token exclusion)

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.
@vicarious11 vicarious11 requested a review from AbhilashSri April 17, 2026 10:54
@AbhilashSri
Copy link
Copy Markdown
Collaborator

✅ Code Review: PR #24 - APPROVED FOR MERGE

All critical issues from the previous review have been successfully addressed!

✅ Session Schema Migration - RESOLVED

Previous Concern: Breaking schema change without migration path

Resolution:

  • Added comprehensive docs/ARCHITECTURE.md explaining:
    • Sessions are re-parsed from source files on every run (not from DB)
    • SQLite sessions table never stored tool_breakdown or models_used
    • No migration needed - fresh collector runs always populate these fields
    • Pydantic's Field(default_factory=dict) guarantees compatibility
    • Graceful fallback for tools without detailed data

The architecture documentation clearly explains the data flow and why this is not a breaking change. Well done!

✅ Test Coverage Added - RESOLVED

Previous Concern: No tests for classifier (293 new lines) and dashboard calculations

Resolution:

  • Added tests/test_classifier.py with 26 comprehensive tests:
    • ✅ Tool-breakdown classification (6 tests)
    • ✅ Prompt-keyword fallback (5 tests)
    • ✅ Activity category zero-fill (1 test)
    • ✅ One-shot rate computation (4 tests)
    • ✅ Tool frequency aggregation (2 tests)
    • ✅ Cost by project (3 tests)
    • ✅ Cost by model (5 tests)
  • All tests passing: 274/274 ✅

📊 Final Code Quality Assessment

Aspect Grade Notes
Functionality A+ Classifier excellent + full test coverage
Code Quality A+ Clean, well-documented
Testing A+ 26 new tests, all paths covered
Documentation A+ Architecture docs added

🎯 What's Excellent

  1. Activity Classifier - Brilliant deterministic approach using real tool call data with smart fallback
  2. Architecture Documentation - Clear explanation of data flow and persistence model
  3. Comprehensive Tests - All code paths covered including edge cases (Hinglish retry signals!)
  4. TUI Dashboard - Rich text panels with multiple new visualizations
  5. Additional Fix - optimizer.js init no-ops when drawer DOM is absent

🚀 Verdict: APPROVED

Overall 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
Date: 2026-04-17

@AbhilashSri
Copy link
Copy Markdown
Collaborator

✅ Code Review: PR #24 - APPROVED FOR MERGE

All critical issues from the previous review have been successfully addressed!

✅ Session Schema Migration - RESOLVED

Previous Concern: Breaking schema change without migration path

Resolution:

  • Added comprehensive docs/ARCHITECTURE.md explaining:
    • Sessions are re-parsed from source files on every run (not from DB)
    • SQLite sessions table never stored tool_breakdown or models_used
    • No migration needed - fresh collector runs always populate these fields
    • Pydantic's Field(default_factory=dict) guarantees compatibility
    • Graceful fallback for tools without detailed data

The architecture documentation clearly explains the data flow and why this is not a breaking change. Well done!

✅ Test Coverage Added - RESOLVED

Previous Concern: No tests for classifier (293 new lines) and dashboard calculations

Resolution:

  • Added tests/test_classifier.py with 26 comprehensive tests:
    • ✅ Tool-breakdown classification (6 tests)
    • ✅ Prompt-keyword fallback (5 tests)
    • ✅ Activity category zero-fill (1 test)
    • ✅ One-shot rate computation (4 tests)
    • ✅ Tool frequency aggregation (2 tests)
    • ✅ Cost by project (3 tests)
    • ✅ Cost by model (5 tests)
  • All tests passing: 274/274 ✅

📊 Final Code Quality Assessment

Aspect Grade Notes
Functionality A+ Classifier excellent + full test coverage
Code Quality A+ Clean, well-documented
Testing A+ 26 new tests, all paths covered
Documentation A+ Architecture docs added

🎯 What's Excellent

  1. Activity Classifier - Brilliant deterministic approach using real tool call data with smart fallback
  2. Architecture Documentation - Clear explanation of data flow and persistence model
  3. Comprehensive Tests - All code paths covered including edge cases (Hinglish retry signals!)
  4. TUI Dashboard - Rich text panels with multiple new visualizations
  5. Additional Fix - optimizer.js init no-ops when drawer DOM is absent

🚀 Verdict: APPROVED

Overall 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
Date: 2026-04-17

Copy link
Copy Markdown
Collaborator

@AbhilashSri AbhilashSri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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!

@AbhilashSri AbhilashSri merged commit 5baca77 into main Apr 17, 2026
1 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants