All notable changes to ccdash will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Codex CLI token accounting: ccdash now scans Codex rollout logs under
~/.codex/sessions/YYYY/MM/DD, combines their input, output, cached-input, and cache-write tokens with Claude Code usage, and shows a provider-aware per-model cost breakdown. Existing SQLite caches migrate automatically and retain historical Claude rows. - Codex session hooks:
ccdash --install-codex-hooksinstalls status-only Codex lifecycle hooks; token data continues to come from local rollout logs. - NEEDLE worker visibility: workers without tmux sessions or Claude hook records now appear in the sessions panel from the NEEDLE worker registry.
- Configurable token-cost alerting: the token panel can highlight spend after a configured threshold is crossed.
- Notification diagnostics:
ccdash --test-notifytests the configured notification webhook without waiting for a session transition. - Startup update notice: available releases are shown in a dismissible startup notice.
- Stale READY sessions: long-idle READY sessions are flagged and sorted so sessions needing attention are easier to identify.
- Multi-provider cache schema: token events and completed-file aggregates
now carry their source (
claudeorcodex) for correct parsing and pricing.
- Network I/O panel: reverted the dedicated 4th panel added in v0.9.x-era commit
57fd448(an autonomous NEEDLE worker pickup, bead bf-d0c, never approved as core scope). ccdash is back to the intended 3-panel layout (System Resources, Token Usage, Sessions) in every mode — the Net I/O summary line inside System Resources is unchanged.docs/plan.mdnow documents the 3-panel layout as a locked decision so it isn't re-added autonomously.
- Sessions blocked on human input showed
WORKINGinstead ofREADY: Claude Code firesNotification(needs permission, or idle 60s awaiting a reply) andPermissionRequest(a permission dialog is shown — covers tool approval,AskUserQuestion, andExitPlanModeplan approval) hooks whenever it needs a human, but ccdash never installed hooks for either event, so sessions stuck mid-turn on a question or approval kept reporting whatever statusUserPromptSubmitlast set (working).--install-hooksalready claimed to install these (cmd/ccdash/main.gohelp text advertised "Marks session as asking"), but no such hook scripts existed.- Added
~/.ccdash/hooks/notification.shand~/.ccdash/hooks/permission-request.sh, wired to theNotificationandPermissionRequestevents, which set session status to a new"waiting"value. HookSession.ToTmuxSession()maps"waiting"onto the existingStatusReady—READYalready means "waiting for human input" in ccdash's model, so this reuses it rather than adding a new status/color.- Added
~/.ccdash/hooks/post-tool-use.sh, wired toPostToolUse, which sets status back to"working"once a gated tool actually finishes (the earliest available signal that Claude resumed after approval/an answer).
- Added
- Token usage panel now displays billions with a
Bsuffix:FormatTokensCompactpreviously topped out atM, so counts over a billion rendered as thousands of millions (e.g.2500.0M). Values that exceed a billion now show asB(e.g.2.5B). Sub-billion counts are unchanged (M/K).
- Token metrics never loading with large JSONL archives:
Collect()was running the full file-scan and ingest loop (64,000+ files, ~50,000 previously unprocessed) synchronously on every UI refresh cycle. With ~10ms per file the loop took 500+ seconds — far exceeding the dashboard's 3-second timeout — soQueryTokensHybrid()was never reached and the token panel was always blank. Fixed by moving all file I/O into a background goroutine (startBackgroundIngestion) that starts immediately at collector creation and re-runs every 30 seconds.Collect()now only executes the fastQueryTokensHybrid()DB query, which completes in under 100ms regardless of corpus size.
- System and token panels blank with large project counts: With many JSONL files (64,000+), the token batch-ingest goroutine held the SQLite write mutex for several seconds per cycle. Because lease acquisition, cache reads, and cache writes all shared the same mutex, this blocked the fast gopsutil-based system metrics and cache reads, causing all three operations to time out. Fixed by splitting into two mutexes:
ingestMufor slow file-scan/DB-ingest operations, andmetaMufor fast lease/cache operations. Fast operations are now completely independent of ingestion and can never be blocked by it.
- Session working status not recognized: Hook-tracked sessions showing
WORKINGwere incorrectly downgraded toREADYby the hybrid tmux/hook merge logic. The merge was treating tmux pane content as authoritative, but⏵⏵ bypass permissions(always visible in Claude Code's UI chrome) causedisClaudeWaitingto return true even during active processing, triggering the downgrade. TheStophook is now the authoritative signal — tmux pane content can only confirm working, not negate it. - Long-running tasks marked stale mid-execution: Sessions with
status=workingwere overridden tostaleafter 5 minutes of inactivity (no newUserPromptSubmit). The stale threshold no longer applies toworkingsessions.
PreToolUsehook: New~/.ccdash/hooks/pre-tool-use.shrefresheslast_activityon every tool call, keeping the stale threshold from triggering during long multi-step tasks. Installed automatically byccdash hooks install.
- Multi-directory JSONL support: Token tracking now spans multiple Claude project root directories
--extra-dirs=<dirs>CLI flag accepts a comma-separated list of additional root directories to scanCCDASH_EXTRA_DIRSenvironment variable accepts colon-separated paths (stackable with--extra-dirs)- Both mechanisms stack on top of the default
~/.claude/projectsroot — no replacement, only addition - Useful for tracking usage across separate Claude Code installations or custom data locations
- Subagent JSONL inclusion: Token usage now includes costs from subagent sessions spawned via the Agent tool
- Recursively scans
<project>/<uuid>/subagents/agent-*.jsonlin addition to top-level JSONL files - Subagent JSONL format is identical to main session JSONL — no extra parsing needed
- Provides complete cost accounting for all Claude Code activity across all projects and sessions
- Recursively scans
- Multi-project JSONL aggregation: Token usage dashboard now aggregates costs and tokens across all Claude Code projects, not just the one matching the current working directory
- Scans all directories under
~/.claude/projects/*/automatically - Aggregates input/output/cache tokens and costs from every project session
- No configuration required — auto-discovery is the default behavior
- Updated error messages to reflect all-project scope
- Scans all directories under
- Disk usage monitoring: System Resources panel now shows root filesystem (/) space usage
- Displays used/total disk space with percentage bar (e.g., "Dsk [||||| 15.9%] 66.12 GB/444.00 GB")
- Uses same compact format as Memory and Swap for consistency
- Color-coded progress bar: Green<60%, Yellow 60-79%, Orange 80-94%, Red≥95%
- Updated help text to document the new disk usage metric
- Positioned between Swap and Disk I/O for logical resource grouping
- Session PID tracking bug: Session hooks now correctly track the Claude Code process PID instead of the hook script's PID
- Previously, hooks stored
$$(the hook script's PID) which became invalid immediately after the hook exited - This caused all sessions to show as "ready" even when actively running
- Now walks up the process tree to find the actual
claudeprocess and stores its PID
- Previously, hooks stored
- Stale session cleanup: Automatically removes old session files when Claude Code restarts in the same tmux window
- Prevents accumulation of orphaned session files with dead PIDs
- Ensures only the current active session is tracked per tmux window
- PID refresh in prompt-submit hook: Updates the PID when user submits a prompt
- Handles edge cases where the Claude process may have restarted
- Ensures PID stays current throughout the session lifecycle
- Session hooks now search for the parent
claudeprocess instead of using$$ session-start.shhook includes cleanup logic for old session files in the same tmux sessionprompt-submit.shhook now updates the PID field along with status and activity time
- Attached indicator (📎) disappearing with multiple clients: Fixed bug where the attachment indicator would disappear when connecting to a tmux session from a second computer
- Root cause:
#{session_attached}returns the count of attached clients, not a boolean - The check
attached == 1failed when 2+ clients were attached - Solution: Changed to
attached > 0to correctly detect any attached clients
- Root cause:
- Automatic cleanup of orphaned session files on startup: New
CleanupOrphanedSessions()method removes stale hook session files where:- The process (PID) is no longer running
- The tmux session no longer exists
- Cleanup runs silently on every ccdash startup, preventing accumulation of orphaned files
- Uses
tmux list-sessionsto detect which tmux sessions are still active - Uses
kill -0signal check to verify if PIDs are still running - Combined with v0.7.16, provides two-level protection against phantom sessions
- Phantom sessions displaying in dashboard: Fixed bug where hook session files from terminated tmux sessions would appear in the dashboard
- Root cause: Hook session files persist when sessions are killed abruptly (kill -9, terminal crash) without the session-end hook firing
- The merge logic unconditionally displayed all hook sessions regardless of whether a corresponding tmux session existed
- Solution: Skip hook sessions that don't have a matching live tmux session
- Modified
Collect()intmux.goto filter out hook sessions without tmux counterparts - Hook sessions are only displayed if
tmuxSessionMap[session.Name]exists
- SQLite WAL mode not activating: Fixed issue where WAL mode was not being enabled despite connection string parameter
- WAL mode is now explicitly set via
PRAGMA journal_mode=WALafter database open - Added backup
PRAGMA busy_timeout=30000to ensure timeout is set - Resolves lock contention when running multiple ccdash instances concurrently
- WAL mode is now explicitly set via
- Connection string WAL parameter (
?_journal_mode=WAL) doesn't always work with modernc.org/sqlite - Explicit PRAGMA execution ensures WAL mode is active (creates
.db-waland.db-shmfiles) - Two concurrent ccdash instances should now work reliably via leader election
- File pre-aggregation for complete sessions: Dramatically improves token metrics loading performance
- Files not modified in 30+ minutes are automatically detected as "complete"
- Complete files are aggregated once and stored in
file_aggregatestable - Future queries skip file I/O entirely for complete files, reading only pre-computed totals
- Individual events are deleted after aggregation to reduce database size
- Files that become active again are automatically reactivated and reprocessed
- Token queries now use hybrid approach: pre-aggregated totals + individual events
- Schema version bumped to 3 (automatic migration on first run)
- Reduced redundant file scanning - complete files checked via DB, not filesystem
- First load after restart: Pre-computed aggregates load instantly
- Typical session with 50+ old files: ~90% reduction in file I/O operations
- Database size: Reduced by removing individual events for complete files
- New
file_aggregatestable stores per-file totals with model breakdown (JSON) GetFileAggregate()/MarkFileComplete()/MarkFileActive()cache methodsQueryTokensHybrid()combines aggregates and events in single queryGetFileCompleteThreshold()returns 30-minute threshold (configurable constant)
0.6.28 - 2025-12-18
- Display bleed-through bug: Fixed issue where external process output (like Tailscale "wgengine: reconfig" logs) would appear at the bottom of the display
- Root cause: View() output didn't fill entire terminal height, leaving bottom rows unrendered
- Solution: Added padding in View() to ensure output always fills the full terminal height
- Resizing no longer required to clear stray log messages
0.6.0 - 2025-12-05
- SQLite-based token cache: Complete rewrite of caching system for better queryability
- Cache stored in
.ccdash/tokens.dbSQLite database with WAL mode - Directly queryable by DuckDB, SQLite CLI, or any SQLite-compatible tool
- Schema:
token_eventstable with timestamp indexes,file_statefor tracking - Batch insertions for improved performance
- Cache stored in
- Incremental ingestion: Smart processing of JSONL files
- Tracks last processed line per file to avoid reprocessing
- Automatic file invalidation on modification or truncation
- Deduplication via unique index on (source_file, line_number)
- SQL-based lookback queries: Efficient time-range filtering
- Uses indexed timestamp_unix column for fast range queries
- Per-model aggregation computed directly in SQL
- Recent events query for rate calculations
- Replaced JSON cache (
.ccdash/token_cache.json) with SQLite (.ccdash/tokens.db) - Token metrics now computed via SQL aggregation instead of in-memory iteration
- Updated help pane to document SQLite/DuckDB queryable cache
- New dependency:
modernc.org/sqlite(pure Go, no CGO required) - Cross-platform binaries without C compiler dependencies
- SQLite configured with WAL journal mode and NORMAL synchronous
TokenCachestruct provides thread-safe database access with RWMutexInsertTokenEventBatch()for efficient bulk insertsQueryTokensSince()returns aggregated metrics with per-model breakdownQueryRecentEvents()for rate calculation over last N seconds
0.5.0 - 2025-12-05
- Two-tier log file processing: Token metrics now use a two-tier system for efficiency
- Tier 1: Real-time processing of entries within the lookback window
- Tier 2: Cached processing of historical entries outside the lookback window
- Significantly reduces CPU usage when processing large JSONL files
- Persistent cache in .ccdash folder: Historical token data is now cached
- Cache stored in
.ccdash/token_cache.jsonin the working directory - Automatically invalidates when source files are modified
- Survives across sessions for faster startup
- Cache stored in
- Enhanced TMUX panel title: Now shows session count and status summary
- Title format: "📺 TMUX Sessions (N)" where N is total count
- Status summary right-justified: "🟢2 🔴1 🟡3" showing count per status
- Quick visual overview without scanning individual sessions
- Removed redundant "Total: X" line from TMUX panel (now in title)
- Token collector now initializes cache on creation
- Improved file processing with modification time tracking
- New
internal/metrics/cache.gofor persistent token caching - TokenCollector now includes cache and file line tracking
- Cache uses JSON serialization with version control for compatibility
- Two-tier processing prioritizes fresh data over cached historical data
0.3.0 - 2025-11-27
- Self-update functionality: ccdash now checks for updates automatically from GitHub releases
- Status bar shows "⬆ vX.X.X available! Press u to update" when a new version exists
- Press
uto download and apply the update in-place - Automatic version comparison with GitHub releases API
- Per-model cost tracking: Token panel now shows individual costs for each Claude model
- Displays model name with cost and token count
- Color-coded by model type (Opus=red, Sonnet=cyan, Haiku=green)
- Sorted by cost (highest first)
- Smart model name shortening (e.g., "claude-opus-4-5-20251101" → "Opus 4.5")
- Improved CPU core display alignment
- Square brackets now align consistently across all core displays
- Fixed-width labels ensure proper column alignment
- Consistent bar width calculation matching memory/swap lines
- CPU total bar now uses the same width calculation as Memory and Swap for visual consistency
- Status bar dynamically shows available shortcuts based on update availability
- Token panel now includes empty line separator before per-model breakdown
- New
internal/updaterpackage for update management - Added
ModelUsagestruct for per-model token and cost tracking - Updater uses GitHub API with 5-minute cache interval
- Self-update uses atomic file replacement with restart script
0.1.4 - 2025-11-21
- Fixed panel width calculation to properly account for padding, ensuring panels fit exactly in terminal width
- Panels now render correctly in 202-character wide terminals without right-side cutoff
- Adjusted panel width distribution to account for lipgloss padding (0,1)
- Updated width calculation: totalPanelWidth = d.width - 6 (to account for 2 chars padding per panel)
0.1.3 - 2025-11-21
- Narrowed tmux sessions panel by additional character to prevent overflow
- Improved panel border calculations
0.1.2 - 2025-11-21
- Narrowed tmux sessions panel by 3 characters to better fit terminal width
- Fixed right-side cutoff issues in ultra-wide mode
0.1.1 - 2025-11-21
- Version display in status bar (bottom left)
- Version now shows as "HH:MM:SS vX.X.X" format
- Updated help pane width calculation to match normal view (d.width - 2)
0.1.0 - 2025-11-21
- Initial release of ccdash
- Real-time system resource monitoring (CPU, memory, swap, disk I/O, load averages)
- Claude Code token usage tracking from ~/.claude/projects
- Tmux session monitoring with intelligent status detection
- Beautiful TUI with responsive layout modes:
- Ultra-wide mode (≥240 cols): 3 panels side-by-side
- Wide mode (120-239 cols, ≥30 lines): 2 panels top, 1 bottom
- Narrow mode (<120 cols): panels stacked vertically
- Help mode (press 'h') with cycling explanations for each panel
- Smart tmux session status detection:
- 🟢 WORKING - Claude Code actively processing
- 🔴 READY - Waiting for user input at prompt
- 🟡 ACTIVE - User actively in session
⚠️ ERROR - Error state or undefined condition
- Detection patterns from unified-dashboard:
- Working indicators: "Finagling...", "Puzzling...", "Listing...", etc.
- Prompt patterns: "⏵⏵ bypass permissions", "Claude Code" + "❯"
- Error detection in last 5 lines only
- Idle duration tracking for tmux sessions
- Dynamic CPU core display (≤6 cores: one per line, >6: multiple per line)
- 2-column help layout when text exceeds available lines
- Keyboard shortcuts:
- q, Ctrl+C: Quit
- r: Refresh metrics immediately
- h: Cycle through help mode
- Status bar with time, github link, dimensions, and shortcuts
- Color-coded metrics with 4-tier thresholds
- Unified-dashboard inspired styling with vertical bars and emojis
- Built with Bubble Tea TUI framework
- Uses lipgloss for terminal styling
- gopsutil for system metrics collection
- Captures last 15 lines of tmux panes for status detection
- Content change detection with timing rules
- 2-second refresh interval for metrics