Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
20 changes: 15 additions & 5 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -287,10 +287,20 @@ function buildDailyHistory(days: ReturnType<typeof aggregateProjectsIntoDays>) {
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<string, { name: string; cost: number; calls: number; inputTokens: number; outputTokens: number }>()
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)
})(),
}))
}

Expand Down
20 changes: 17 additions & 3 deletions src/menubar-json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<string, { name: string; cost: number; calls: number }>()
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'] {
Expand Down
1 change: 1 addition & 0 deletions src/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ const autoModelNames: Record<string, string> = {
}

const SHORT_NAMES: Record<string, string> = {
'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',
Expand Down
1 change: 1 addition & 0 deletions src/providers/claude.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { homedir } from 'node:os'
import type { Provider, SessionSource, SessionParser } from './types.js'

const shortNames: Record<string, string> = {
'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',
Expand Down
5 changes: 5 additions & 0 deletions tests/models.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down
2 changes: 1 addition & 1 deletion windows/package.json
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
Loading