feat(stats): surface "favorite tools" (most-run bash commands) on /stats
The surfacing half of the per-agent favorite-tools stat. Reads a `bash_commands(ts INTEGER NOT NULL, head TEXT NOT NULL)` table from the per-agent turn-stats.sqlite — one row per bash task, written by hive-bash-mcp (the capture half, landing separately) — and rolls up the top-10 command heads for a doughnut, mirroring the existing tool_breakdown. - stats.rs: new `Snapshot.bash_breakdown` + `read_bash_breakdown()`. The read is guarded: a missing `bash_commands` table (capture hasn't shipped / agent hasn't run a bash task) maps to an empty list, never an error — the snapshot degrades gracefully. Unit tests cover both the absent-table and populated cases (incl. window cutoff + ordering). - frontend: a "favorite tools (bash)" doughnut card on the agent /stats page, kept hidden until bash_breakdown has data so it never shows a permanently-empty doughnut while capture is pending. Part of #1433 (does not close it — pairs with the hive-bash-mcp capture half). Inert until the capture lands; merge order with it is irrelevant.
This commit is contained in:
parent
1569d55f78
commit
a5914274ba
3 changed files with 117 additions and 0 deletions
|
|
@ -33,6 +33,10 @@
|
|||
<div class="stats-card wide"><h3>token cost per bucket (sum across inferences)</h3><div class="chart-wrap"><canvas id="chart-cost"></canvas></div></div>
|
||||
<div class="stats-card wide"><h3>turns by model per bucket — model drives token cost</h3><div class="chart-wrap"><canvas id="chart-model"></canvas></div></div>
|
||||
<div class="stats-card"><h3>top tools</h3><div class="chart-wrap"><canvas id="chart-tools"></canvas></div></div>
|
||||
<!-- "favorite tools": most-run shell commands. Hidden until the
|
||||
bash_commands capture (hive-bash-mcp) has recorded data, so the
|
||||
card never shows a permanently-empty doughnut. -->
|
||||
<div class="stats-card" id="card-bash" hidden><h3>favorite tools (bash)</h3><div class="chart-wrap"><canvas id="chart-bash"></canvas></div></div>
|
||||
<div class="stats-card"><h3>wake source mix</h3><div class="chart-wrap"><canvas id="chart-wake"></canvas></div></div>
|
||||
<div class="stats-card"><h3>result mix</h3><div class="chart-wrap"><canvas id="chart-result"></canvas></div></div>
|
||||
<div class="stats-card wide"><h3>result trend per bucket — errors / rate-limits / compactions over time</h3><div class="chart-wrap"><canvas id="chart-result-trend"></canvas></div></div>
|
||||
|
|
|
|||
|
|
@ -340,8 +340,26 @@ window.Chart = Chart;
|
|||
});
|
||||
}
|
||||
|
||||
// "favorite tools" doughnut: most-run shell commands. The capture
|
||||
// (hive-bash-mcp -> bash_commands table) lands separately, so until
|
||||
// there's data we hide the whole card rather than show an empty
|
||||
// doughnut. Runs independently of turn_count (a bash task is tied to
|
||||
// a turn, but we don't want to couple the two reads).
|
||||
function renderBashCard(s) {
|
||||
const card = document.getElementById('card-bash');
|
||||
const items = s.bash_breakdown || [];
|
||||
if (!items.length) {
|
||||
if (card) card.hidden = true;
|
||||
destroy('chart-bash');
|
||||
return;
|
||||
}
|
||||
if (card) card.hidden = false;
|
||||
renderKeyCount('chart-bash', items, 'no bash commands');
|
||||
}
|
||||
|
||||
function render(s) {
|
||||
renderSummary(s);
|
||||
renderBashCard(s);
|
||||
if (s.turn_count === 0) {
|
||||
paintEmpty('chart-turns', 'no turns in window');
|
||||
paintEmpty('chart-duration', 'no turns in window');
|
||||
|
|
|
|||
Loading…
Reference in a new issue