app.go changes:
- Add loadConfigOrDefault() helper that returns sensible defaults when
config loading fails, ensuring TUI can always start even with
corrupted config files
- Extract scroll navigation constants (scrollOverhead, minHalfPageScroll,
minContentHeight) for clarity and consistency
- Apply accent border styling to loading card for visual polish
- Replace inline config.Load() calls with loadConfigOrDefault()
setup.go changes:
- Use loadConfigOrDefault() for consistent error handling during
setup wizard initialization and config persistence
The loadConfigOrDefault pattern improves user experience by gracefully
degrading rather than failing hard when config issues occur. Users can
still access the TUI and reconfigure via the Settings tab.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update all tab renderers to leverage the expanded theme palette and
polished components:
tab_overview.go:
- Use PanelCard (accent-bordered variant) for daily token chart
- Multi-color model bars: BlueBright, Cyan, Magenta, Yellow, Green
for visual distinction between models
- Pre-compute styles outside loops for better performance
- Use Cyan for today's hourly chart, Magenta for last-hour chart
tab_breakdown.go:
- Apply consistent background styling
- Use new accent variants for visual hierarchy
tab_costs.go:
- Proper background fill on cost tables
- Accent coloring for cost highlights
tab_sessions.go:
- Background continuity in session list
- Visual improvements to session detail view
tab_settings.go:
- Consistent styling with other tabs
The result is a dashboard where each tab feels visually cohesive,
with color providing semantic meaning (different colors for different
models, metrics, and states) rather than arbitrary decoration.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Comprehensive visual refresh of all TUI components:
card.go:
- Add semantic icons based on metric type (tokens=◈, sessions=◉,
cost=◆, cache=◇)
- Color-code metric values using new theme colors (Cyan, Magenta,
Green, Blue) for visual variety
- Add CardRow() helper that properly height-equalizes cards and
fills background on shorter cards to prevent "punched out" gaps
- Set explicit background on all style components
chart.go:
- Add background styling to sparkline renderer
- Ensure bar chart respects theme.Active.Surface background
progress.go:
- Add color gradient based on progress (Cyan→Accent→AccentBright)
- Style percentage text with bold and matching color
- Fix background fill on empty bar segments
statusbar.go:
- Complete redesign with SurfaceHover background
- Style keyboard hints: dim brackets, bright keys, muted labels
- Proper spacing and background continuity across sections
- Styled refresh indicator with AccentBright
tabbar.go:
- Add TabVisualWidth() for accurate mouse hit detection
- Modern underline-style active indicator using ━ characters
- AccentBright for active tab, proper dim styling for inactive
- Consistent Surface background across all tab elements
These changes create a cohesive visual language where every element
respects the dark background, icons add visual interest without
clutter, and color coding provides semantic meaning.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add new theme colors across all four color schemes (Flexoki Dark,
Catppuccin Mocha, Tokyo Night, Terminal) to enable more nuanced
visual states:
Surface variants:
- SurfaceHover: Highlighted surface for active tabs, selected rows
- SurfaceBright: Extra emphasis for focused elements
Border variants:
- BorderBright: Prominent borders for cards and focus states
- BorderAccent: Accent-colored borders for active/selected elements
Accent variants:
- AccentBright: Higher contrast accent for emphasis
- AccentDim: Muted accent for subtle backgrounds
New semantic colors:
- Magenta: Session-related metrics
- Cyan: Token-related metrics
These additions provide the foundation for visual polish work across
components and tabs, enabling icon coloring, gradient effects, and
proper background fill without relying solely on the base accent color.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement a long-running daemon service that continuously polls Claude
Code session logs and exposes usage data via local HTTP endpoints.
Architecture:
- internal/daemon/service.go: Core Service struct managing poll loop,
snapshot computation, event buffering, and HTTP handlers
- cmd/daemon.go: Cobra commands for start/status/stop with detach mode
HTTP Endpoints (default 127.0.0.1:8787):
- GET /healthz - Liveness probe for orchestration
- GET /v1/status - Current aggregate snapshot + daemon runtime info
- GET /v1/events - Recent event buffer as JSON array
- GET /v1/stream - Server-Sent Events for real-time updates
Snapshot model captures:
- Session/prompt/API call counts
- Token totals and estimated cost
- Cache hit rate
- Rolling daily averages (cost/day, tokens/day, sessions/day)
Delta detection emits events only when usage actually changes, keeping
the event stream lean for downstream consumers.
Detach mode (-d, --detach):
- Forks a child process with stdout/stderr redirected to log file
- Writes PID file for process management
- Parent exits after confirming child is running
This daemon serves as the foundation for planned capabilities like
incident replay, runaway detection, and session classification.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Change module path from 'cburn' to 'github.com/theirongolddev/cburn'
to enable standard Go remote installation:
go install github.com/theirongolddev/cburn@latest
This is a BREAKING CHANGE for any external code importing this module
(though as a CLI tool, this is unlikely to affect anyone).
All internal imports updated to use the new module path.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Mouse support:
- Wheel up/down scrolls session list in Sessions tab
- Left click on tab bar switches tabs
- Works alongside existing keyboard navigation
Session search:
- Press '/' to enter search mode with live preview
- Filters sessions by project name substring matching
- Shows match count as you type
- Enter to apply filter, Esc to cancel
- Search indicator shown in card title when active
- Esc clears active search filter
Cost integration:
- Use centralized AggregateCostBreakdown for model costs
- Consistent cost calculations between Overview and Costs tabs
Also fixes cursor clamping to prevent out-of-bounds access when
search results change the filtered session count.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extract token-type and per-model cost calculations from cmd/costs.go
into a dedicated pipeline.AggregateCostBreakdown() function. This
eliminates duplicate cost calculation logic between CLI and TUI.
New types:
- TokenTypeCosts: aggregate costs by input/output/cache types
- ModelCostBreakdown: per-model cost components
Benefits:
- Single source of truth for cost calculations
- Uses LookupPricingAt() for historical accuracy
- Both CLI and TUI now share the same cost logic
- Easier to maintain and extend
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Introduces LookupPricingAt() to resolve model pricing at a specific
timestamp instead of always using current prices. This is important
for accurate cost calculations when analyzing historical sessions
where pricing may have changed.
Changes:
- Add modelPricingVersion struct with EffectiveFrom timestamp
- Add defaultPricingHistory map for versioned pricing entries
- Update LookupPricing to delegate to LookupPricingAt(model, time.Now())
- Add comprehensive tests for time-windowed pricing lookup
The infrastructure supports future pricing changes by adding entries
to defaultPricingHistory with their effective dates.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Comprehensive project README covering installation, quick start,
full CLI command table with global flags and examples, TUI dashboard
keybindings and tab descriptions, theme options, configuration format
(TOML + env vars), session key setup instructions, caching behavior,
development commands (make targets), and package architecture diagram.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The top-spending-days card first sorts all days by cost descending to
find the N highest-cost days, then was iterating over the full sorted
slice instead of the limited subset. Additionally, the limited days
appeared in cost order rather than chronological order, making the
timeline hard to scan.
Fix: after slicing to the top N, re-sort that subset by date
(most recent first) before rendering, and iterate over the correct
limited slice.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a new "Row 2.5" to the overview tab between the trend chart and
model/activity panels, showing two side-by-side live activity charts:
- Today: 24-bar hourly token histogram with 12h-format labels
(12a, 1a, ... 11p). Header shows total tokens consumed today.
- Last Hour: 12-bar five-minute token histogram with relative-time
labels (-55, -50, ... -5, now). Header shows tokens in the last
60 minutes.
Both charts use BarChart with theme-colored bars (Blue for today,
Accent for last hour) and adapt height in compact layouts.
Helper functions hourLabels24() and minuteLabels() generate the
X-axis label arrays.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduce background data refresh so the dashboard stays current without
restarting. This touches four layers:
Config (config.go):
- New TUIConfig struct with AutoRefresh (bool) and RefreshIntervalSec
(int) fields, defaulting to enabled at 30-second intervals.
- Minimum interval floor of 10 seconds enforced at load time.
App core (app.go):
- RefreshDataMsg type for background refresh completion signaling.
- Auto-refresh state: interval timer, refreshing flag, lastRefresh
timestamp. Checked on every tick; fires refreshDataCmd when elapsed.
- refreshDataCmd: background goroutine that loads session data via cache
(with uncached fallback) and posts RefreshDataMsg on completion.
- Manual refresh keybind: 'r' triggers immediate refresh.
- Auto-refresh toggle keybind: 'R' toggles auto-refresh and persists
the preference to config.toml.
- Help text updated with r/R keybind documentation.
Status bar (statusbar.go):
- Shows spinning refresh indicator during active refresh.
- Shows auto-refresh icon when auto-refresh is enabled.
Settings tab (tab_settings.go):
- Two new editable fields: Auto Refresh (bool) and Refresh Interval
(seconds with 10s minimum).
- Settings display reads live App state to stay consistent with the
R toggle keybind (avoids stale config-file reads).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add data layer support for real-time usage visualization:
- MinuteStats type: holds token counts for 5-minute buckets, enabling
granular recent-activity views (12 buckets covering the last hour).
- AggregateTodayHourly(): computes 24 hourly token buckets for the
current local day by filtering sessions to today's date boundary and
slotting each into the correct hour index. Tracks prompts, sessions,
and total tokens per hour.
- AggregateLastHour(): computes 12 five-minute token buckets for the
last 60 minutes using reverse-offset bucketing (bucket 11 = most
recent 5 minutes, bucket 0 = 55-60 minutes ago). Bounds-clamped to
prevent off-by-one at the edges.
Both functions filter on StartTime locality and skip zero-time sessions,
consistent with existing aggregation patterns in the pipeline package.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Major rewrite of the Bubble Tea dashboard, adding live claude.ai
integration and splitting the monolithic app.go into focused tab modules.
App model (app.go):
- Integrate claudeai.Client for live subscription/rate-limit data
- Add SubDataMsg and async fetch with periodic refresh (every 5 min)
- Add spinner for loading states (charmbracelet/bubbles spinner)
- Integrate huh form library for in-TUI setup wizard
- Rework tab routing to dispatch to dedicated tab renderers
- Add compact layout detection for narrow terminals (<100 cols)
TUI setup wizard (setup.go):
- Full huh-based setup flow embedded in the TUI (not just CLI)
- Three-step form: credentials, preferences (time range + theme), confirm
- Pre-populates from existing config, validates session key prefix
- Returns to dashboard on completion with config auto-saved
New tab modules:
- tab_overview.go: summary cards (sessions, prompts, cost, time), daily
activity sparkline, rate-limit progress bars from live subscription data
- tab_breakdown.go: per-model usage table with calls, input/output tokens,
cost, and share percentage; compact mode for narrow terminals
- tab_costs.go: cost analysis with daily cost chart, model cost breakdown,
cache efficiency metrics, and budget tracking with progress bar
Rewritten tabs:
- tab_sessions.go: paginated session browser with sort-by-cost/tokens/time,
per-session detail view, model usage breakdown per session, improved
navigation (j/k, enter/esc, n/p for pages)
- tab_settings.go: updated to work with new theme struct and config fields
Theme simplification (theme/theme.go):
- Remove Purple and BorderHover fields from Theme struct — neither was
referenced in the new tab renderers, reducing per-theme boilerplate
from 14 to 12 color definitions
Component extraction (components/):
- Move Sparkline() and BarChart() from card.go to new chart.go, giving
visualization components their own file as complexity grows
- card.go retains MetricCard, ContentCard, LayoutRow, and CardInnerWidth
which are layout-focused
- New chart.go: Sparkline (unicode block chars) and BarChart (multi-row
with anchored Y-axis, optional X-axis labels, dynamic height/width)
- New progress.go: ProgressBar component with customizable width, color,
and percentage display — used by rate-limit and budget views
Status bar and tab bar updates:
- statusbar.go: adapt to simplified theme struct
- tabbar.go: adapt to simplified theme struct
Introduce a client for fetching subscription data from the claude.ai web
API, enabling rate-limit monitoring and overage tracking in the dashboard.
New package internal/claudeai:
- Client authenticates via session cookie (sk-ant-sid... prefix validated)
- FetchAll() retrieves orgs, usage windows, and overage in one call,
returning partial data when individual requests fail
- FetchOrganizations/FetchUsage/FetchOverageLimit for granular access
- Defensive utilization parsing handles polymorphic API responses: int
(75), float (0.75 or 75.0), and string ("75%" or "0.75"), normalizing
all to 0.0-1.0 range
- 10s request timeout, 1MB body limit, proper status code handling
(401/403 -> ErrUnauthorized, 429 -> ErrRateLimited)
Types (claudeai/types.go):
- Organization, UsageResponse, UsageWindow (raw), OverageLimit
- SubscriptionData (TUI-ready aggregate), ParsedUsage, ParsedWindow
Config changes (config/config.go):
- Add ClaudeAIConfig struct with session_key and org_id fields
- Add GetSessionKey() with CLAUDE_SESSION_KEY env var fallback
- Fix directory permissions 0o755 -> 0o750 (gosec G301)
- Fix Save() to propagate encoder errors before closing file
Address golangci-lint findings and improve error handling throughout:
Package doc comments:
- Add canonical "// Package X ..." comments to source, model, config,
pipeline, cli, store, and main packages for godoc compliance.
Security & correctness:
- Fix directory permissions 0o755 -> 0o750 in store/cache.go Open()
(gosec G301: restrict group write on cache directory)
- Fix config.Save() to check encoder error before closing file, preventing
silent data loss on encode failure
- Add //nolint:gosec annotations with justifications on intentional
patterns (constructed file paths, manual bounds checking, config fields)
- Add //nolint:nilerr on intentional error-swallowing in scanner WalkDir
- Add //nolint:revive on stuttering type names (ModelStats, ModelUsage)
that would break too many call sites to rename
Performance (perfsprint):
- Replace fmt.Sprintf("%d", n) with strconv.FormatInt(n, 10) in format.go
FormatTokens() and FormatNumber() hot paths
- Clean up redundant fmt.Sprintf patterns in FormatCost and FormatDelta
Code cleanup:
- Convert if-else chain to switch in parser.go skipJSONString() for clarity
- Remove unused indexedResult struct from pipeline/loader.go
- Add deferred cache.Close() in pipeline/bench_test.go to prevent leaks
- Add deferred cache.Close() in cmd/root.go data loading path
- Fix doc comment alignment in scanner.go decodeProjectName
- Remove trailing blank line in cmd/costs.go
- Fix duplicate "/day" suffix in cmd/summary.go cost-per-day formatting
- Rename shadowed variable 'max' -> 'maxVal' in cli/render.go Sparkline
Cover the core JSONL byte-level parser with targeted tests and a fuzz
harness to prevent regressions and catch panics on malformed input:
Unit tests:
- TestParseFile_UserMessages: verifies user message counting and
ProjectPath extraction from cwd field
- TestParseFile_AssistantDedup: confirms message-ID-based deduplication
where the last entry wins (handles edits/retries)
- TestParseFile_TimeRange: validates StartTime/EndTime tracking across
out-of-order timestamps
- TestParseFile_SystemDuration: tests turn_duration aggregation from
system entries (durationMs -> DurationSecs conversion)
- TestParseFile_EmptyFile: ensures zero stats without errors on empty input
- TestParseFile_MalformedLines: confirms graceful skip of unparseable
lines without aborting the entire file
- TestParseFile_CacheTokens: validates extraction of cache_read,
cache_creation_5m, and cache_creation_1h token fields
- TestExtractTopLevelType: table-driven tests for the byte-level type
extractor covering user, assistant, system, nested-type-ignored,
unknown, no-type, and empty cases
Fuzz test:
- FuzzExtractTopLevelType: seeds with realistic patterns plus edge cases
(unterminated strings, non-string type values, empty input). Asserts
the parser never panics and only returns known type strings or empty.
Uses a writeSession helper that creates temp JSONL files for each test,
keeping tests isolated and cleanup automatic via t.TempDir().
Add the core App Bubble Tea model that orchestrates the entire TUI
dashboard. The model manages:
- 10 navigable tabs: Dashboard, Costs, Sessions, Models, Projects,
Trends, Efficiency, Activity, Budget, and Settings. Each tab is
accessible via single-key shortcuts (d/c/s/m/p/t/e/a/b/x) or
left/right arrows for sequential navigation.
- Async data pipeline: launches the JSONL loader in a goroutine and
receives progress updates via a channel subscription, displaying
an animated loading screen with a spinner and file count. Data
loads once on startup and recomputes aggregates when filters change.
- Filter state: supports configurable time range (7/30/90 days),
project filter, and model filter. Changing any filter triggers
recomputation of all derived stats (summary, daily, model, project
breakdowns) including a comparison period for delta calculations.
- First-run detection: if no config file exists when data finishes
loading, automatically enters the setup wizard flow before showing
the dashboard.
- Tab-specific rendering: Dashboard shows metric cards with period-
over-period deltas, daily token/cost bar charts, model pie-style
breakdown, and top projects. Costs shows a per-model cost table.
Trends renders daily tokens, cost, and session bar charts.
Efficiency shows cache hit rate and savings metrics. Activity
renders an hourly heatmap. Budget tracks spend against plan limits
with a burn-rate projection chart.
- Help overlay: toggleable help panel listing all keyboard shortcuts,
rendered as a centered overlay above the active tab content.
Add three self-contained feature modules that plug into the root App
model via shared state structs and render methods.
setup.go -- First-run wizard with a 5-step flow: welcome screen, API
key entry (password-masked text input), default time range selector
(radio-style j/k navigation over 7/30/90 day options), theme picker
(radio-style over all registered themes), and a completion screen
that persists choices to ~/.config/cburn/config.toml. Gracefully
handles save failures by noting the settings apply for the current
session only.
tab_sessions.go -- Session browser with two view modes: split view
(1/3 condensed list + 2/3 detail pane, scrollable with offset
tracking) and full-screen detail. The detail body shows duration,
prompt/API-call ratio, per-type token breakdown with cache cost
attribution, per-model API call table, and subagent indicator.
tab_settings.go -- Runtime settings editor with 4 configurable
fields (API key, theme, default days, monthly budget). Supports
inline text input editing with Enter/Esc save/cancel flow, immediate
config persistence, flash "Saved!" confirmation, and error display
on save failure. Theme changes apply instantly without restart.
The daily aggregation now iterates from the since date through the
until date and inserts a zero-valued DailyStats entry for any day
not already present in the day map. This ensures sparklines and bar
charts render a continuous time axis with explicit zeros for idle
days, rather than connecting adjacent data points across gaps.
Also switch config file creation to os.OpenFile with explicit 0600
permissions and O_WRONLY|O_CREATE|O_TRUNC flags, matching the intent
of the original os.Create call while making the restricted permission
bits explicit for security.
Add BarChart component that renders multi-row Unicode bar charts with
anchored Y-axis labels, automatic tick-step computation, sub-sampling
for narrow terminals, and optional X-axis date labels. The chart
gracefully degrades to a sparkline when width/height is too small.
Add ContentCard, CardRow, and CardInnerWidth utilities for consistent
bordered card layout across all dashboard tabs. ContentCard renders
a lipgloss-bordered card with optional bold title; CardRow joins
pre-rendered cards horizontally; CardInnerWidth computes the usable
text width after accounting for border and padding.
Add LayoutRow helper that distributes a total width into n integer
widths that sum exactly, absorbing the integer-division remainder
into the first items -- eliminates off-by-one pixel drift in multi-
column layouts.
Refactor MetricCard to accept an outerWidth parameter and derive the
content width internally by subtracting border, replacing the old
raw-width parameter that required callers to do the subtraction.
MetricCardRow now uses LayoutRow for exact width distribution.
Refine TabBar to render all tabs on a single row when they fit within
the terminal width, falling back to the two-row layout only when
they overflow.
Simplify StatusBar by removing the unused filterInfo append that was
cluttering the left section.
Implement shared UI components used across the dashboard tabs:
- tui/components/card.go: Three components:
* MetricCard: bordered card with label (muted), value (bold), and
optional delta (dim) — used for the dashboard's top-level KPIs.
* MetricCardRow: renders N cards side-by-side using lipgloss
horizontal join, auto-calculating card width from available space.
* Sparkline: theme-colored Unicode block sparkline (8-level,
auto-scaled to series max). Used in Dashboard and Trends tabs.
* ProgressBar: filled/empty bar (accent + dim) with percentage
label. Used in the Budget tab for plan-relative spend.
- tui/components/statusbar.go: Bottom status bar with left-aligned
keybinding hints ([f]ilter [?]help [q]uit), current filter info,
and right-aligned data age indicator. Padding auto-fills to
terminal width.
- tui/components/tabbar.go: 10-tab navigation bar split across two
rows (Dashboard/Costs/Sessions/Models/Projects on row 1,
Trends/Efficiency/Activity/Budget/Settings on row 2). Each
inactive tab highlights its keyboard shortcut letter with [bracket]
notation. Active tab renders in accent color. Settings uses 'x'
as its shortcut (not present in the name, so appended). TabIdxByKey
maps key presses to tab indices.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement the theming layer for the interactive dashboard:
- tui/theme/theme.go: Theme struct with 14 semantic color roles
(Background, Surface, Border, BorderHover, TextDim, TextMuted,
TextPrimary, Accent, Green, Orange, Red, Blue, Purple, Yellow).
Four built-in themes:
* Flexoki Dark (default): warm, low-contrast palette designed for
extended reading. Teal accent (#3AA99F) on near-black background.
* Catppuccin Mocha: pastel warm tones with blue accent (#89B4FA)
on dark surface (#1E1E2E).
* Tokyo Night: cool blue/purple palette with blue accent (#7AA2F7)
on deep navy (#1A1B26).
* Terminal: ANSI 16-color only for maximum compatibility — maps
all roles to standard terminal colors (0-15), works correctly
in any terminal emulator regardless of true-color support.
Global Active variable holds the current theme. SetActive/ByName
enable runtime theme switching from the settings tab and setup
wizard.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement the presentation layer for terminal output:
- cli/format.go: Human-readable formatting functions — FormatTokens
(K/M/B suffixes), FormatCost (adaptive precision: $X.XX under $10,
$X.X under $100, $X over $100, comma-separated over $1000),
FormatDuration (Xh Ym / Xm / Xs), FormatNumber (comma-separated
integers), FormatPercent (0-1 -> "XX.X%"), FormatDelta (signed
cost delta with +/- prefix), FormatDayOfWeek (weekday number to
3-letter abbreviation).
- cli/render.go: lipgloss-styled output components using the Flexoki
Dark color palette:
* RenderTitle: centered title in a rounded border box.
* RenderTable: full-featured bordered table with auto-calculated
column widths, right-aligned numeric columns (all except first),
separator rows (triggered by "---" sentinel), and proper Unicode
box-drawing characters. Supports optional title header and
explicit column widths.
* RenderProgressBar: bracketed fill bar with current/total counts.
* RenderSparkline: Unicode block sparkline (8-level: ▁ through █)
from arbitrary float64 series, auto-scaled to max.
* RenderHorizontalBar: simple horizontal bar chart entry for
inline comparisons.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement the pipeline layer that orchestrates discovery, parsing,
caching, and aggregation:
- pipeline/loader.go: Load() discovers session files via ScanDir,
optionally filters out subagent files, then parses all files in
parallel using a bounded worker pool sized to GOMAXPROCS. Workers
read from a pre-filled channel (no contention on dispatch) and
report progress via an atomic counter and callback. LoadResult
tracks total files, parsed files, parse errors, and file errors.
- pipeline/aggregator.go: Five aggregation functions, all operating
on time-filtered session slices:
* Aggregate: computes SummaryStats across all sessions — total
tokens (5 types), estimated cost, cache savings (summed per-model
via config.CalculateCacheSavings), cache hit rate, and per-active-
day rates (cost, tokens, sessions, prompts, minutes).
* AggregateDays: groups sessions by local calendar date, sorted
most-recent-first.
* AggregateModels: groups by normalized model name with share
percentages, sorted by cost descending.
* AggregateProjects: groups by project name, sorted by cost.
* AggregateHourly: distributes prompt/session/token counts across
24 hour buckets (attributed to session start hour).
Also provides FilterByTime, FilterByProject, FilterByModel with
case-insensitive substring matching.
- pipeline/incremental.go: LoadWithCache() implements the incremental
loading strategy — compares discovered files against the cache's
file_tracker (mtime_ns + size), loads unchanged sessions from
SQLite, and only reparses files that changed. Reparsed results
are immediately saved back to cache. CacheDir/CachePath follow
XDG_CACHE_HOME convention (~/.cache/cburn/metrics.db).
- pipeline/bench_test.go: Benchmarks for ScanDir, ParseFile (worst-
case largest file), full Load, and LoadWithCache to measure the
incremental cache speedup.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement the caching layer that enables fast subsequent runs by
persisting parsed session data in SQLite:
- store/schema.go: DDL for three tables — sessions (primary metrics
with file_mtime_ns/file_size for change detection), session_models
(per-model breakdown, FK cascade on delete), and file_tracker
(path -> mtime+size mapping for cache invalidation). Indexes on
start_time and project for efficient time-range and filter queries.
- store/cache.go: Cache struct wrapping database/sql with WAL mode
and synchronous=normal for concurrent read safety and write
performance. Key operations:
* Open: creates the cache directory, opens/creates the database,
and ensures the schema is applied (idempotent via IF NOT EXISTS).
* GetTrackedFiles: returns the mtime/size map used by the pipeline
to determine which files need reparsing.
* SaveSession: transactional upsert of session stats + model
breakdown + file tracker entry. Uses INSERT OR REPLACE to handle
both new files and files that changed since last parse.
* LoadAllSessions: batch-loads all cached sessions with a two-pass
strategy — first loads session rows, then batch-loads model data
with an index map for O(1) join, avoiding N+1 queries.
Uses modernc.org/sqlite (pure-Go, no CGO) for zero-dependency
cross-platform builds.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement the bottom of the data pipeline — discovery and parsing of
Claude Code session files:
- source/types.go: Raw JSON deserialization types (RawEntry,
RawMessage, RawUsage, CacheCreation) matching the Claude Code
JSONL schema. DiscoveredFile carries file metadata including
decoded project name, session ID, and subagent relationship info.
- source/scanner.go: ScanDir walks ~/.claude/projects/ to discover
all .jsonl session files. Detects subagent files by the
<project>/<session>/subagents/agent-<id>.jsonl path pattern and
links them to parent sessions. decodeProjectName reverses Claude
Code's path-encoding convention (/-delimited path segments joined
with hyphens) by scanning for known parent markers (projects,
repos, src, code, workspace, dev) and extracting the project name
after the last marker.
- source/parser.go: ParseFile processes a single JSONL session file.
Uses a hybrid parsing strategy for performance:
* "user" and "system" entries: byte-level field extraction for
timestamps, cwd, and turn_duration (avoids JSON allocation).
extractTopLevelType tracks brace depth and string boundaries to
find only the top-level "type" field, early-exiting ~400 bytes
in for O(1) per line cost regardless of line length.
* "assistant" entries: full JSON unmarshal to extract token usage,
model name, and cost data.
Deduplicates API calls by message.id (keeping the last entry per
ID, which holds the final billed usage). Computes per-model cost
breakdown using config.CalculateCost and aggregates cache hit rate.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement the configuration layer that supports the entire cost
estimation pipeline:
- config/config.go: TOML-based config at ~/.config/cburn/config.toml
(XDG-compliant) with sections for general preferences, Admin API
credentials, budget tracking, appearance, and per-model pricing
overrides. Supports Load/Save with sensible defaults (30-day
window, subagents included, Flexoki Dark theme). Admin API key
resolution checks ANTHROPIC_ADMIN_KEY env var first, falling back
to the config file.
- config/pricing.go: Hardcoded pricing table for 8 Claude model
variants (Opus 4/4.1/4.5/4.6, Sonnet 4/4.5/4.6, Haiku 3.5/4.5)
with per-million-token rates across 5 billing dimensions: input,
output, cache_write_5m, cache_write_1h, cache_read, plus long-
context overrides (>200K tokens). NormalizeModelName strips date
suffixes (e.g., "claude-opus-4-5-20251101" -> "claude-opus-4-5").
CalculateCost and CalculateCacheSavings compute per-call USD costs
by multiplying token counts against the pricing table.
- config/plan.go: DetectPlan reads ~/.claude/.claude.json to
determine the billing plan type. Maps "stripe_subscription" to
the Max plan ($200/mo ceiling), everything else to Pro ($100/mo).
Used by the budget tab for plan-relative spend visualization.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Define the core data structures that flow through the entire pipeline:
- model/session.go: SessionStats (per-session aggregates including
token counts across 5 categories — input, output, cache_write_5m,
cache_write_1h, cache_read), APICall (deduplicated by message.id,
keyed to the final billed usage), and ModelUsage (per-model
breakdown within a session). Tracks subagent relationships via
IsSubagent/ParentSession fields.
- model/metrics.go: Higher-order aggregate types — SummaryStats
(top-level totals with per-active-day rates for cost, tokens,
sessions, and minutes), DailyStats/HourlyStats/WeeklyStats
(time-bucketed views), ModelStats (cross-session model comparison
with share percentages), ProjectStats (per-project ranking), and
PeriodComparison (current vs previous period for delta display).
- model/budget.go: BudgetStats with plan ceiling, custom budget,
burn rate, and projected monthly spend for the budget tab.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>