Skip to content

infiniV/claude-usage-waybar

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

claude-usage-waybar

A Waybar module that shows your Claude Code rate limits, today's tokens and spend, and live session stats — on the same bar you already have open.

Waybar hover tooltip with compact usage panel
Hover — compact Pango tooltip anchored to the bar icon

GTK4 libadwaita popover with full usage details
Click — GTK4 + libadwaita popover with live session stats

Why this exists

Claude Code's /usage is in-terminal only; close the shell and the numbers are gone. ccusage is a good CLI but it doesn't live on your panel. The nice menu-bar app for this on macOS is CCSeva, which has no Linux build. So: a waybar module that reads the rate_limits block from Claude Code's official statusline stdin (v2.1.80+, shipped 2026-03-19) and pairs it with ccusage daily --json for today's tokens and dollar cost.

Built on Arch Linux / Hyprland / Omarchy; nothing is Omarchy-specific except the optional theme colour harvest.

What shows up

The bar label is 󰧑 41%·11% — five-hour on the left, seven-day on the right, green while you're fine, amber near 80%, rust past 100%.

Hover for a Pango panel: progress bars, reset countdowns, today's tokens and dollar cost, and the active session's cost / context % / lines added or removed. Click for a GTK4 + libadwaita popover that anchors under the icon, refreshes every second, and has a Settings link that opens config.toml in $EDITOR.

The status chip at the top (LIVE, IDLE, CRIT, OVER) comes from real state — no guessed plan tier, no synthesised "Pro" badge.

Chrome colours follow your Omarchy theme: the popover reads ~/.config/omarchy/current/theme/waybar.css and matches whatever you have active. Accent colours stay baked because harvesting them from alacritty's ANSI slots painted healthy usage red on themes that don't invert the semantics. That bug was real; this is the fix.

Install

git clone https://github.com/infiniV/claude-usage-waybar.git
cd claude-usage-waybar
./install.sh

Or the one-liner once the repo is public:

curl -fsSL https://raw.githubusercontent.com/infiniV/claude-usage-waybar/main/install.sh | bash

The installer is idempotent. All edits to your existing waybar/config.jsonc, waybar/style.css, and ~/.claude/settings.json are guarded by >>> claude-usage-waybar >>> markers so ./uninstall.sh can back them out cleanly. If you already had a statusLine.command, it's chained (our emitter pipes the same stdin to it) and restored on uninstall.

Requirements

Tool Required Why
waybar yes the host
python3 ≥ 3.11 yes emitter + popup
jq yes config patching
Claude Code ≥ 2.1.80 yes earlier builds don't emit rate_limits
A Nerd Font yes for the 󰧑 glyph
python-gobject + gtk4 + libadwaita optional only for the click popover
gtk4-layer-shell optional anchors the popover under the bar icon instead of centring it
ccusage (or bunx / pnpm / npx) optional fills in today's tokens + cost; without it the Today row stays empty

On fresh Arch: sudo pacman -S waybar jq python python-gobject gtk4 libadwaita gtk4-layer-shell ttf-jetbrains-mono-nerd.

Recommended GitHub topics

If you fork: waybar, waybar-module, claude-code, anthropic, omarchy, hyprland, arch-linux, ccusage.

Configuration

~/.config/claude-usage/config.toml. Every key is optional; defaults live in bin/claude_usage.py's DEFAULT_CONFIG.

[display]
format = "󰧑 {five_hour}%·{seven_day}%"   # waybar label; placeholders: {icon}, {five_hour}, {seven_day}
icon   = "󰧑"
critical_threshold  = 80                   # % that flips the bar to `.critical`
exhausted_threshold = 100                  # % that flips the bar to `.exhausted`

[behavior]
stale_after_seconds = 300                  # after this long with no statusline turn, grey out + "idle"

[tooltip]
show_progress_bars = true
show_today         = true
show_updated_ago   = true
bar_width          = 10

[statusline]
# Recorded automatically if you had a pre-existing statusLine.command.
chained_command = ""

Nothing in that file is decorative; every key is read by at least one script. tests/test_no_fluff.py::test_config_keys_are_all_consumed fails CI if an orphan key sneaks back in.

How it works

Claude Code turn                       systemd --user timer
       │                                (every 5 min)
       │ stdin JSON                            │
       ▼                                       ▼
 claude-usage-statusline         claude-usage-today
  (writes rate_limits +           (runs ccusage daily --json
   session snapshot)               and writes today.tokens + cost)
       │                                       │
       └───────────┬───────────────────────────┘
                   ▼
      ~/.cache/claude-usage/state.json   (atomic writes, single source of truth)
                   │
       ┌───────────┴────────────┐
       ▼                        ▼
 claude-usage-bar        claude-usage-popup
 (waybar interval=15s)   (waybar on-click)

Two data sources, one state file. The statusline emitter fires every Claude Code turn (so the percentages are always fresh while you're using it); a systemd user timer runs ccusage every 5 minutes (so Today's cost doesn't only update when you open Claude Code). The bar and popup both read state.json — no timers of their own, no API calls, no network.

State schema

~/.cache/claude-usage/state.json:

{
  "schema": 1,
  "updated_at": 1776214800,
  "source": "statusline",
  "five_hour":  { "pct": 41, "resets_at": 1776225600 },
  "seven_day":  { "pct": 11, "resets_at": 1776517200 },
  "today": {
    "tokens": 13583603,
    "cost_usd": 13.92,
    "models": ["opus-4", "haiku-4-5"]
  },
  "session": {
    "id": "",
    "model_id": "claude-opus-4-6[1m]",
    "model_name": "Opus 4.6 (1M context)",
    "cost_usd": 14.49,
    "lines_added": 2913,
    "lines_removed": 272,
    "input_tokens": 94467,
    "output_tokens": 134075,
    "context_pct": 23
  }
}

Every field here is pulled verbatim from either Claude Code's statusline stdin (see bin/claude-usage-statusline:extract_session) or ccusage daily --json (bin/claude-usage-today). Fields you might expect but won't find — messages, plan_tier, cwd, session_duration — are absent because there's no honest source for them, not because we forgot.

Uninstall

./uninstall.sh

Reverts every marker-guarded edit, disables the systemd timer, and removes the scripts and cache. Your ~/.config/claude-usage/config.toml is left alone in case you're coming back; delete it by hand if you want to start fresh.

Development

python3 -m pytest tests -q       # 34 tests, zero network, pytest required
./install.sh --dry-run           # prints every action without writing anything
./bin/claude-usage-bar           # dumps waybar JSON against your current state
./bin/claude-usage-popup         # launches the popover out-of-band

Fixtures in tests/fixtures/ are verbatim captures of real Claude Code statusline stdin (v2.1.108) and real ccusage daily --json output (v18). If Anthropic changes the statusline schema or ccusage renames a field, the tests fail before your users do.

Two tests worth knowing about:

  • test_no_fluff.py — walks DEFAULT_CONFIG and EMPTY_STATE and fails if any key is documented but unread, or stored but unrendered. Also scans every user-facing file for a blocklist of words from previous-life features (messages, PRO plan, dead config knobs) so they can't creep back in. Backticked mentions like these are exempt; prose resurrection is not.
  • test_palette.py — feeds adversarial theme files to load_palette() and asserts the accent/warn/crit/danger colours never come from ANSI slots, no matter what the theme does. A past version harvested accents from alacritty and painted healthy usage red on non-IBM themes; this test exists so that never happens again.

PRs welcome. Anything that adds a state field should also add a consumer; test_no_fluff will fail otherwise.

License

MIT — see LICENSE.

About

Waybar module for Claude Code usage tracking on Linux. Shows 5-hour and 7-day rate limits, daily token usage and spend via ccusage, and live session stats in a GTK4/libadwaita popover. Built for Arch Linux, Hyprland, and Omarchy. A CCSeva alternative for Wayland.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages