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>
Replace hardcoded /usr/local/go/bin/go with dynamic PATH lookup.
The previous hardcoded path failed on any system where Go is
installed elsewhere (e.g., /usr/bin/go, ~/go/bin/go).
Uses conditional assignment (GO ?=) so users can still override
with GO=/custom/path make build when needed.
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>
Document two hard-won patterns in CLAUDE.md's new Architectural
Insights section:
- ANSI Width Calculation: lipgloss.Width() must be used instead of
len() for styled strings, since ANSI escape codes add ~20 bytes per
color code. fmt.Sprintf padding is similarly affected.
- JSON Top-Level Type Detection: bytes.Contains matches nested strings
in JSONL with embedded JSON. Correct approach tracks brace depth and
skips quoted strings to find actual top-level fields.
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 complete Cobra command tree (11 subcommands):
- cmd/root.go: Root command with persistent flags (--days, --project,
--model, --no-cache, --data-dir, --quiet, --no-subagents). Shared
loadData() orchestrates the full pipeline: tries cache-assisted
loading first, falls back to uncached parse on cache failure,
reports progress to stderr. applyFilters() applies project/model
substring filters and computes the time window.
- cmd/summary.go: Default command (also "cburn summary"). Renders a
bordered metrics table with token breakdown (5 types), cost with
cache savings, and per-day rates with period-over-period deltas.
- cmd/costs.go: Detailed cost analysis — breaks down costs by token
type (output, cache_write_1h, input, cache_write_5m, cache_read)
with share percentages, period comparison bar chart, and per-model
cost breakdown (input/output/cache/total columns).
- cmd/daily.go: Daily usage table (date, weekday, sessions, prompts,
tokens, cost) sorted most-recent-first.
- cmd/hourly.go: Activity heatmap showing prompt distribution across
24 hours with Unicode block bars, reports peak hour.
- cmd/models.go: Model usage ranking with API call counts, token
volumes, cost, and usage share percentage.
- cmd/projects.go: Project ranking by cost with session/prompt/token
counts.
- cmd/sessions.go: Session list sorted by recency with --limit flag
(default 20). Shows start time, project, duration, tokens, cost.
Marks subagent sessions with "(sub)" suffix.
- cmd/config_cmd.go: Displays current configuration across all
sections (general, admin API, appearance, budget) with auto-
detected plan ceiling.
- cmd/setup.go: Interactive first-run wizard — configures Admin API
key, default time range (7/30/90 days), and color theme (Flexoki
Dark, Catppuccin Mocha, Tokyo Night, Terminal). Saves to
~/.config/cburn/config.toml.
- cmd/tui.go: Launches the interactive Bubble Tea TUI dashboard,
passing through all filter flags and applying the configured theme.
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>
Set up the cburn project foundation:
- go.mod: Define module "cburn" targeting Go 1.24.2 with core
dependencies — Cobra (CLI framework), Bubble Tea + Lipgloss
(TUI), BurntSushi/toml (config), and modernc.org/sqlite (pure-Go
SQLite for the metrics cache, no CGO required).
- go.sum: Lock all direct and transitive dependency versions for
reproducible builds.
- main.go: Minimal entry point that delegates to cmd.Execute(),
following the standard Cobra application pattern.
- .gitignore: Exclude the compiled binary, IDE directories (.idea/,
.vscode/), and macOS .DS_Store files.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>