Skip to content

Commit 5adc4fe

Browse files
BenGuanRanclaude
andauthored
feat(stats): add interactive /stats dashboard with cross-session tracking (#4779)
* docs(stats): add dashboard design spec and implementation plan Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(stats): add cross-session usage tracking service Add usageHistoryService to core with JSONL-based persistence, session replay from chat history with sessionId deduplication, time-range aggregation, and per-model/tool/file breakdown including latency fields. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(stats): add stats data service and ASCII chart utilities Add statsDataService for delta calculations, efficiency metrics, tool leaderboard, and heatmap/trend data. Add asciiCharts with braille line chart (Bresenham rendering) and GitHub-style contribution heatmap. Includes 38 unit tests covering both modules. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(stats): implement interactive /stats dashboard Add three-tab dialog: Session (live metrics), Activity (KPIs, heatmap, braille token trend chart, project ranking), and Efficiency (cache rate, tool success, latency cards, tool leaderboard, model comparison table). Supports tab/shift-tab navigation, r to cycle time ranges (all/month/ week/today), left/right to pan months in the trend chart, esc to close. Persist usage on /clear for accurate cross-session tracking. Update statsCommand tests for new dialog behavior and clearCommand tests for telemetry mock. Update /stats documentation in commands.md. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(i18n): add stats dashboard translations for all locales Add translations for stats dashboard UI strings in zh, zh-TW, ca, de, fr, ja, pt, ru. Add stats keys to en.js baseline and mustTranslateKeys. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(stats): use terminal default background for inactive heatmap cells Intensity 0 cells (no activity) now render without backgroundColor, inheriting the terminal's native background instead of a hardcoded color that renders incorrectly across different terminal themes. Also fix green gradient direction: brighter = more activity. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(stats): use dot markers for inactive heatmap cells Inactive cells render as '··' with no background color instead of colored blocks, matching common contribution graph designs. Active cells keep their green gradient backgrounds. Fix gradient direction so brighter green = more activity. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(stats): restore full session stats from original StatsDisplay Add back Session ID, Success Rate with color thresholds, User Agreement rate, Performance breakdown (Wall Time, Agent Active, API Time %, Tool Time %), and full token counts that were present in the original exit screen but missing from the new Session tab. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(stats): address PR review — timezone bugs, double-count, cleanup - Fix monthOffset overflow: setDate(1) before subtracting months to prevent day-count overflow (e.g. Mar 31 → Feb) - Fix UTC date-parse off-by-one: append 'T00:00:00' to date-only strings in calculateStreaks and HeatmapView fmtDate - Fix current session double-counted after rebuild: deduplicate by sessionId when injecting live session into loadStatsData - Remove unused bodyWidth prop from SessionTab - Remove 13 unused i18n keys (Overview, Favorite model, etc.) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(stats): add NaN guard, catch unhandled promise, fix useEffect race - Guard against malformed chat records with NaN timestamps in rebuild - Add .catch() to loadStatsData promise to prevent TUI crash - Add stale flag to useEffect to prevent race on rapid range cycling Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(i18n): sync locale files with en.js baseline for CI check Add 19 missing translations to zh-TW.js, remove extra keys from zh.js and zh-TW.js that were deleted from en.js in prior cleanup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(stats): use fake timers in getPreviousRangeBounds tests The test compared new Date() in the assertion against new Date() inside the function, which could differ by 1ms across a millisecond boundary. Pin system time to prevent flaky CI failures. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(i18n): restore Session and Success keys removed in error These keys are still referenced by t('Session') in stats-helpers.tsx and t('Success') in StatsEfficiencyTab.tsx. Add to en.js baseline and restore zh/zh-TW translations. * fix(stats): address R3 review — malformed record guard, arrow fix, error state - Skip malformed records in aggregateUsage (missing tools/files/models) - Use Object.create(null) to prevent prototype pollution on model names - Fix Avg Latency delta arrow direction (▼ for decrease, ▲ for increase) - Clamp fmtSuccessBar to prevent RangeError on corrupt data - Add error state UI when loadStatsData fails * fix(stats): address R4 review — DST fix, token consistency, tests, cleanup - Fix DST bug in getPreviousRangeBounds('today') using setDate - Unify token counting: project ranking uses totalTokens (same as KPI) - Add clearCommand tests for persistSessionUsage with/without activity - Remove dead code (unreachable sorted.length check) - Fix heatmap legend to use dot markers matching grid cells - Add 'Failed to load stats' i18n key to en/zh/zh-TW * fix(stats): include thoughtsTokens in totalTokens fallback calculation * Revert "feat(input): move physical cursor to visual cursor for IME input (#4652)" This reverts commit 77458ad. * fix(stats): prevent Yoga layout from compressing StatsDialog content Add flexShrink={0} to the outer Box of StatsDialog so that Yoga's default flex-shrink behavior does not compress KPI cards, charts, and tables when the dialog exceeds the available terminal height. The parent container in DefaultAppLayout already applies height + overflow="hidden" to clip overflow — this fix ensures content retains its natural size instead of being squeezed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(stats): add debug logging to catch blocks and strengthen test assertion - Add debugLogger to all catch blocks in usageHistoryService.ts for observability when file I/O or parsing fails - Strengthen test assertion from toContain('Session duration') to toContain('Session duration: 0s') to verify the zero-duration fallback --------- Co-authored-by: a.ran <benguanran.bgr@alibaba-inc.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 32fbeda commit 5adc4fe

48 files changed

Lines changed: 5813 additions & 434 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.qwen/plans/2025-06-03-stats-dashboard-redesign.md

Lines changed: 1337 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
# Stats Dashboard Redesign
2+
3+
## Overview
4+
5+
Redesign the `/stats` TUI dashboard to improve layout hierarchy, add efficiency metrics, tool usage details, and trend comparisons. The Session tab remains unchanged.
6+
7+
## Tab Structure
8+
9+
```
10+
Tab 1: Session (unchanged - live current-session metrics)
11+
Tab 2: Activity (time-based trends and usage patterns)
12+
Tab 3: Efficiency (performance metrics and tool analysis)
13+
```
14+
15+
## Time Range Selector
16+
17+
Cycle: `Today``Week``Month``All`
18+
19+
Triggered by pressing `r`. All data in Activity and Efficiency tabs is filtered by the selected range.
20+
21+
## Delta Calculation
22+
23+
Every KPI card shows a trend arrow comparing the current range against the previous equivalent range:
24+
25+
- Range = Today → compare today vs yesterday
26+
- Range = Week → compare last 7 days vs the 7 days before that
27+
- Range = Month → compare last 30 days vs the 30 days before that
28+
- Range = All → no delta shown
29+
30+
Display: positive = green `▲ +12%`, negative = red `▼ -3%`. For latency, lower is better so the colors invert.
31+
32+
Implementation: load two time slices from `usage_record.jsonl`, aggregate each, compute percentage change.
33+
34+
## Activity Tab
35+
36+
Layout from top to bottom:
37+
38+
### 1. KPI Row
39+
40+
Three metrics in a horizontal row, each with value + delta arrow:
41+
42+
| Metric | Source | Example |
43+
|--------|--------|---------|
44+
| Sessions | `report.sessionCount` | `42 ▲+8` |
45+
| Duration | `report.totalDurationMs` | `18h 32m ▲+2h` |
46+
| Tokens | sum of `report.models[*].totalTokens` | `2.4m ▲+12%` |
47+
48+
### 2. Heatmap
49+
50+
- Full width, GitHub-style grid
51+
- **Color intensity** = daily total token consumption (not session count)
52+
- **Today's cell** = distinct border or marker character (e.g., `[ ]` instead of ` `, or a brighter outline color)
53+
- Right-aligned metadata: `streak: 12d │ best: 23d`
54+
- Legend row: `Less ░░░░░ More`
55+
- Column labels: month abbreviations + day numbers
56+
- Row labels: Mon / Wed / Fri (compact 3-row mode)
57+
- Weeks shown: `min(26, max(8, floor((bodyWidth - 4) / 2)))`
58+
59+
### 3. Token Trend Chart
60+
61+
- Braille sub-pixel line chart (existing `buildLineChartData`)
62+
- Single series: total tokens per day
63+
- Height: 6 rows
64+
- Month navigation with `` `` when range = `all`
65+
- Month label: `← Jun 2025 →`
66+
67+
### 4. Project Ranking
68+
69+
Table showing top 5 projects:
70+
71+
```
72+
Project Sessions Tokens Duration
73+
qwen-code 28 1.8m 12h
74+
web-app 10 420k 4h
75+
infra 4 180k 2h
76+
```
77+
78+
Source: `report.projects` sorted by totalTokens descending.
79+
80+
## Efficiency Tab
81+
82+
Layout from top to bottom:
83+
84+
### 1. Performance Cards Row
85+
86+
Three boxed metric cards:
87+
88+
| Metric | Calculation | Source |
89+
|--------|-------------|--------|
90+
| Cache Hit Rate | `cachedTokens / inputTokens * 100` | `report.models[*].cachedTokens` / `inputTokens` |
91+
| Tool Success Rate | `totalSuccess / totalCalls * 100` | `report.tools.totalSuccess` / `totalCalls` |
92+
| Avg Latency | `totalLatencyMs / totalRequests` | Requires adding `totalLatencyMs` to persisted records OR computing from per-model data |
93+
94+
Each card shows: label, bold percentage/value, delta arrow.
95+
96+
Note on Avg Latency: The current `UsageSummaryRecord` does not persist latency data. Options:
97+
1. Compute from live `SessionMetrics` for current session only (show "—" for historical)
98+
2. Add `totalLatencyMs` field to the persisted record (migration: old records show "—")
99+
100+
**Decision: Option 2** — extend `UsageSummaryRecord` with optional `totalLatencyMs`. Old records without this field display "—" for latency delta.
101+
102+
### 2. Tool Leaderboard
103+
104+
Table showing top 8 tools by call count:
105+
106+
```
107+
Tool Calls Time Success
108+
edit 847 42.3s ██████████ 98%
109+
read 612 8.1s ██████████ 99%
110+
bash 431 67.8s █████████░ 89%
111+
glob 298 2.4s ██████████ 99%
112+
grep 256 3.1s █████████░ 97%
113+
write 189 12.5s ██████████ 96%
114+
agent 45 89.2s ████████░░ 82%
115+
```
116+
117+
- Success rate visualized as a 10-char bar: filled `` + empty ``
118+
- Color: green if ≥95%, orange if ≥80%, red if <80%
119+
- Source: `report.tools.topTools` (already computed, but needs duration added)
120+
121+
Note: Current `topTools` in aggregated report only has `count, success, fail`. Need to add `totalDurationMs` per tool to the aggregation.
122+
123+
### 3. Model Comparison Table
124+
125+
```
126+
Model Reqs In/Out Cache Latency
127+
● qwen-max 186 1.2m/340k 91% 2.1s
128+
● qwen-plus 124 890k/210k 84% 1.2s
129+
● qwen-turbo 67 310k/89k 72% 0.8s
130+
```
131+
132+
- Sorted by totalTokens descending
133+
- Color-coded dots (series colors)
134+
- Cache column: green ≥85%, orange ≥70%, red <70%
135+
- Source: `report.models`
136+
137+
### 4. Code Impact
138+
139+
Single-line summary:
140+
141+
```
142+
Code +2,847 lines / -1,203 lines net: +1,644
143+
```
144+
145+
Source: `report.files.linesAdded`, `report.files.linesRemoved`.
146+
147+
## Keyboard Controls
148+
149+
| Key | Action |
150+
|-----|--------|
151+
| `Tab` / `Shift+Tab` | Switch between tabs |
152+
| `r` | Cycle range: today → week → month → all |
153+
| `` / `h` | Previous month (chart navigation, range=all only) |
154+
| `` / `l` | Next month (chart navigation, range=all only) |
155+
| `Esc` | Close dialog |
156+
157+
## Data Layer Changes
158+
159+
### UsageSummaryRecord v1 Extensions (backward-compatible)
160+
161+
Add optional fields to existing schema:
162+
163+
```typescript
164+
interface UsageSummaryRecord {
165+
// ... existing fields ...
166+
totalLatencyMs?: number; // NEW: sum of all API response latencies
167+
tools: {
168+
// ... existing fields ...
169+
byName: Record<string, {
170+
count: number;
171+
success: number;
172+
fail: number;
173+
totalDurationMs?: number; // NEW: sum of tool execution time
174+
}>;
175+
};
176+
}
177+
```
178+
179+
### StatsData Extensions
180+
181+
```typescript
182+
interface StatsData {
183+
// ... existing fields ...
184+
delta?: {
185+
sessions: number | null; // percentage change
186+
duration: number | null;
187+
tokens: number | null;
188+
cacheRate: number | null;
189+
toolSuccess: number | null;
190+
avgLatency: number | null;
191+
};
192+
efficiency: {
193+
cacheHitRate: number;
194+
toolSuccessRate: number;
195+
avgLatencyMs: number | null;
196+
};
197+
toolLeaderboard: Array<{
198+
name: string;
199+
count: number;
200+
totalDurationMs: number;
201+
successRate: number;
202+
}>;
203+
}
204+
```
205+
206+
### Heatmap Data Change
207+
208+
Currently `buildHeatmapData` receives `Record<string, number>` where value = session count. Change to: value = total tokens for that day. The mapping to intensity levels (0-4) needs recalibration:
209+
210+
- 0: no usage
211+
- 1: < 10k tokens
212+
- 2: 10k - 50k tokens
213+
- 3: 50k - 200k tokens
214+
- 4: > 200k tokens
215+
216+
Thresholds should be computed dynamically based on the data distribution (percentile-based) rather than hardcoded, to adapt to different usage patterns.
217+
218+
### Today Highlight
219+
220+
In `buildHeatmapData`, mark today's cell with a special property. Render it with a distinct character or color attribute (e.g., bright white border characters `[▓]` instead of plain `▓▓`).
221+
222+
## Internationalization
223+
224+
All user-facing strings wrapped in `t()`. New i18n keys:
225+
226+
```
227+
stats.activity = "Activity"
228+
stats.efficiency = "Efficiency"
229+
stats.today = "Today"
230+
stats.sessions = "Sessions"
231+
stats.duration = "Duration"
232+
stats.tokens = "Tokens"
233+
stats.cacheHitRate = "Cache Hit Rate"
234+
stats.toolSuccessRate = "Tool Success"
235+
stats.avgLatency = "Avg Latency"
236+
stats.toolLeaderboard = "Tool Leaderboard"
237+
stats.calls = "Calls"
238+
stats.time = "Time"
239+
stats.success = "Success"
240+
stats.models = "Models"
241+
stats.reqs = "Reqs"
242+
stats.cache = "Cache"
243+
stats.latency = "Latency"
244+
stats.codeImpact = "Code Impact"
245+
stats.net = "net"
246+
stats.streak = "streak"
247+
stats.best = "best"
248+
stats.tokenTrend = "Token Trend"
249+
stats.projects = "Projects"
250+
stats.project = "Project"
251+
```
252+
253+
## Files to Modify
254+
255+
| File | Change |
256+
|------|--------|
257+
| `packages/cli/src/ui/components/StatsDialog.tsx` | Replace OverviewTab and ModelsTab with ActivityTab and EfficiencyTab |
258+
| `packages/core/src/services/usageHistoryService.ts` | Add delta calculation, extend aggregation for tool duration and latency |
259+
| `packages/cli/src/ui/utils/statsDataService.ts` | Extend StatsData with efficiency and delta fields |
260+
| `packages/cli/src/ui/utils/asciiCharts.ts` | Add today highlight to heatmap, adjust intensity mapping |
261+
| `packages/core/src/telemetry/uiTelemetry.ts` | Ensure latency is captured in persistence path |
262+
| `packages/cli/src/gemini.tsx` | Persist `totalLatencyMs` and per-tool duration in shutdown hook |
263+
| `packages/cli/src/i18n/*.ts` | Add new translation keys |
264+
265+
## Out of Scope
266+
267+
- Cost estimation (requires user-configured pricing, can be added later)
268+
- Per-file change tracking (not available in current data model)
269+
- Context window usage / compression metrics (not tracked)
270+
- Interactive drill-down into individual sessions

docs/users/features/commands.md

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -268,17 +268,19 @@ In headless (`--prompt`) or non-interactive contexts, `/diff` prints a plain-tex
268268

269269
Commands for obtaining information and performing system settings.
270270

271-
| Command | Description | Usage Examples |
272-
| --------------- | ------------------------------------------------------------- | -------------------------------- |
273-
| `/help` | Display help information for available commands | `/help` or `/?` |
274-
| `/status` | Display version information | `/status` or `/about` |
275-
| `/status paths` | Display current session file and log paths | `/status paths` |
276-
| `/stats` | Display detailed statistics for current session | `/stats` |
277-
| `/settings` | Open settings editor | `/settings` |
278-
| `/auth` | Change authentication method | `/auth` |
279-
| `/bug` | Submit issue about Qwen Code | `/bug Button click unresponsive` |
280-
| `/copy` | Copy AI output to clipboard (`/copy N` = Nth-last AI message) | `/copy` or `/copy 2` |
281-
| `/quit` | Exit Qwen Code immediately | `/quit` or `/exit` |
271+
| Command | Description | Usage Examples |
272+
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- |
273+
| `/help` | Display help information for available commands | `/help` or `/?` |
274+
| `/status` | Display version information | `/status` or `/about` |
275+
| `/status paths` | Display current session file and log paths | `/status paths` |
276+
| `/stats` | Open interactive usage statistics dashboard with three tabs: Session (live metrics), Activity (heatmap, token trend, project ranking), and Efficiency (cache rate, tool leaderboard, model comparison). Use `tab` to switch tabs, `r` to cycle time ranges, `←→` to pan months, `esc` to close. | `/stats` |
277+
| `/stats model` | Show per-model token breakdown and estimated cost | `/stats model` |
278+
| `/stats tools` | Show per-tool call counts | `/stats tools` |
279+
| `/settings` | Open settings editor | `/settings` |
280+
| `/auth` | Change authentication method | `/auth` |
281+
| `/bug` | Submit issue about Qwen Code | `/bug Button click unresponsive` |
282+
| `/copy` | Copy AI output to clipboard (`/copy N` = Nth-last AI message) | `/copy` or `/copy 2` |
283+
| `/quit` | Exit Qwen Code immediately | `/quit` or `/exit` |
282284

283285
### 1.10 Common Shortcuts
284286

0 commit comments

Comments
 (0)