From f594d4fad50fee61cf13b97129a25d941a84029f Mon Sep 17 00:00:00 2001 From: soumyadebroy3 <43921833+soumyadebroy3@users.noreply.github.com> Date: Tue, 2 Jun 2026 10:15:18 +0530 Subject: [PATCH 1/2] fix(models): show claude-opus-4-8 as "Opus 4.8"; emit friendly model names in menubar payload claude-opus-4-8 had no entry in the model display-name maps, so it prefix-matched claude-opus-4 and rendered as "Opus 4" in every model breakdown (menubar / GNOME / Windows / HTML / TUI). Add the 4.8 mapping to both getShortModelName (models.ts) and the Claude provider. Also restore friendly-name shortening at the payload display layer: buildTopModels and the trend-tooltip topModels were passing raw model ids through (day-level aggregation keys by raw id for accuracy), which would have shown bare slugs like "claude-opus-4-8" once a user updated the CLI. They now map raw ids to display names and collapse dated/pinned variants into one row. Adds a getShortModelName('claude-opus-4-8') === 'Opus 4.8' test. --- src/main.ts | 20 +++++++++++++++----- src/menubar-json.ts | 20 +++++++++++++++++--- src/models.ts | 1 + src/providers/claude.ts | 1 + tests/models.test.ts | 5 +++++ 5 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/main.ts b/src/main.ts index e5f70c6..562b78f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,7 +3,7 @@ import { safeRunGit } from './git-safe.js' import { installMenubarApp } from './menubar-installer.js' import { installTrayApp } from './tray-installer.js' import { exportCsv, exportJson, type PeriodExport } from './export.js' -import { loadPricing, setModelAliases } from './models.js' +import { getShortModelName, loadPricing, setModelAliases } from './models.js' import { parseAllSessions, filterProjectsByName, clearSessionCache } from './parser.js' import { convertCost } from './currency.js' import { renderStatusBar } from './format.js' @@ -287,10 +287,20 @@ function buildDailyHistory(days: ReturnType) { outputTokens: d.outputTokens, cacheReadTokens: d.cacheReadTokens, cacheWriteTokens: d.cacheWriteTokens, - topModels: Object.entries(d.models).slice(0, 3).map(([n, m]) => ({ - name: n, cost: m.cost, calls: m.calls, - inputTokens: m.inputTokens, outputTokens: m.outputTokens, - })), + // Merge raw model ids into friendly display names (claude-opus-4-8 -> + // Opus 4.8) and collapse dated/pinned variants before taking the top 3, + // so the trend tooltip shows clean names, not bare slugs. + topModels: (() => { + const merged = new Map() + for (const [n, m] of Object.entries(d.models)) { + const name = getShortModelName(n) + const acc = merged.get(name) ?? { name, cost: 0, calls: 0, inputTokens: 0, outputTokens: 0 } + acc.cost += m.cost; acc.calls += m.calls + acc.inputTokens += m.inputTokens; acc.outputTokens += m.outputTokens + merged.set(name, acc) + } + return [...merged.values()].sort((a, b) => b.cost - a.cost).slice(0, 3) + })(), })) } diff --git a/src/menubar-json.ts b/src/menubar-json.ts index 0a73c3a..1809e19 100644 --- a/src/menubar-json.ts +++ b/src/menubar-json.ts @@ -19,6 +19,7 @@ export type ProviderCost = { cost: number } import type { OptimizeResult } from './optimize.js' +import { getShortModelName } from './models.js' const TOP_ACTIVITIES_LIMIT = 20 const TOP_MODELS_LIMIT = 20 @@ -203,10 +204,23 @@ function buildTopActivities(categories: PeriodData['categories']): MenubarPayloa } function buildTopModels(models: PeriodData['models']): MenubarPayload['current']['topModels'] { - return models - .filter(m => m.name !== SYNTHETIC_MODEL_NAME) + // Aggregate by display name so raw model ids (e.g. claude-opus-4-8) render as + // friendly names (Opus 4.8) and version-pinned/dated variants of the same + // model collapse into one row. Day-level aggregation keys by raw id for + // accuracy; the friendly mapping happens here, at the display layer, so the + // menubar/GNOME/Windows UIs don't show bare model slugs. + const merged = new Map() + for (const m of models) { + if (m.name === SYNTHETIC_MODEL_NAME) continue + const name = getShortModelName(m.name) + const acc = merged.get(name) ?? { name, cost: 0, calls: 0 } + acc.cost += m.cost + acc.calls += m.calls + merged.set(name, acc) + } + return [...merged.values()] + .sort((a, b) => b.cost - a.cost) .slice(0, TOP_MODELS_LIMIT) - .map(m => ({ name: m.name, cost: m.cost, calls: m.calls })) } function buildOptimize(optimize: OptimizeResult | null): MenubarPayload['optimize'] { diff --git a/src/models.ts b/src/models.ts index 1d9781d..61b5af3 100644 --- a/src/models.ts +++ b/src/models.ts @@ -438,6 +438,7 @@ const autoModelNames: Record = { } const SHORT_NAMES: Record = { + 'claude-opus-4-8': 'Opus 4.8', 'claude-opus-4-7': 'Opus 4.7', 'claude-opus-4-6': 'Opus 4.6', 'claude-opus-4-5': 'Opus 4.5', diff --git a/src/providers/claude.ts b/src/providers/claude.ts index afbdd09..89f8fa0 100644 --- a/src/providers/claude.ts +++ b/src/providers/claude.ts @@ -5,6 +5,7 @@ import { homedir } from 'node:os' import type { Provider, SessionSource, SessionParser } from './types.js' const shortNames: Record = { + 'claude-opus-4-8': 'Opus 4.8', 'claude-opus-4-7': 'Opus 4.7', 'claude-opus-4-6': 'Opus 4.6', 'claude-opus-4-5': 'Opus 4.5', diff --git a/tests/models.test.ts b/tests/models.test.ts index 538b01a..ec23f00 100644 --- a/tests/models.test.ts +++ b/tests/models.test.ts @@ -54,6 +54,11 @@ describe('getShortModelName', () => { it('maps claude-opus-4-6 with date suffix', () => { expect(getShortModelName('claude-opus-4-6-20260205')).toBe('Opus 4.6') }) + + it('maps claude-opus-4-8 to Opus 4.8 (not the bare Opus 4 prefix)', () => { + expect(getShortModelName('claude-opus-4-8')).toBe('Opus 4.8') + expect(getShortModelName('claude-opus-4-8-20260601')).toBe('Opus 4.8') + }) }) describe('builtin aliases - getModelCosts', () => { From ac83659d1337bebbd2e62d3a7eccc134c9219d89 Mon Sep 17 00:00:00 2001 From: soumyadebroy3 <43921833+soumyadebroy3@users.noreply.github.com> Date: Tue, 2 Jun 2026 10:15:19 +0530 Subject: [PATCH 2/2] =?UTF-8?q?release:=20v2.4.5=20=E2=80=94=20Opus=204.8?= =?UTF-8?q?=20display=20name=20+=20friendly=20menubar=20model=20names?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 11 +++++++++++ package-lock.json | 4 ++-- package.json | 2 +- windows/package.json | 2 +- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b1c6ce..49aa0e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 2.4.5 - 2026-06-02 + +### Fixed +- **`claude-opus-4-8` now displays as "Opus 4.8"** instead of falling back to the + bare "Opus 4" prefix — in the menubar, GNOME, Windows, the HTML report, and + the TUI dashboard's model breakdowns. +- **Menubar/GNOME/Windows model rows show friendly names, not raw model slugs.** + The menubar payload's `topModels` and the trend tooltip's per-model names now + map raw ids to display names and collapse dated/pinned variants of the same + model into one row, instead of emitting ids like `claude-opus-4-8`. + ## 2.4.4 - 2026-06-01 Token-usage display now reflects real throughput, plus a sweep of diff --git a/package-lock.json b/package-lock.json index 57669f7..c697b70 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@soumyadebroy3/codeburn", - "version": "2.4.4", + "version": "2.4.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@soumyadebroy3/codeburn", - "version": "2.4.4", + "version": "2.4.5", "license": "MIT", "dependencies": { "chalk": "^5.6.2", diff --git a/package.json b/package.json index e2290be..c1b177e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@soumyadebroy3/codeburn", - "version": "2.4.4", + "version": "2.4.5", "description": "See where your AI coding tokens go - by task, tool, model, and project", "type": "module", "main": "./dist/cli.js", diff --git a/windows/package.json b/windows/package.json index ba1764b..21308a3 100644 --- a/windows/package.json +++ b/windows/package.json @@ -1,6 +1,6 @@ { "name": "codeburn-tray", - "version": "2.4.4", + "version": "2.4.5", "description": "CodeBurn Windows system-tray app (Tauri). Mirrors the macOS Swift menubar.", "type": "module", "private": true,