This guide shows how to configure Waybar (for Wayland/Hyprland) with live status displays for Claude Code and Codex. The modules show current usage and rate limits in real-time in the status bar.
📸 Example
Waybar displays two modules:
CC (Claude Code): Shows 5h-block status with progress bar
COD (Codex): Shows rate-limit status with progress bar
Example output:
CC 🕐2h45m 67% ████████░ │ COD 🕐4h12m 23% ██░░░░░░
🔧 Prerequisites
System
Waybar (for Wayland/Hyprland)
Linux (Arch Linux, Ubuntu, etc.)
Bash Shell
jq (JSON Processor)
Node.js Tools
One of the following package managers:
npx (comes with Node.js)
bunx (Bun)
pnpm
Claude Code & Codex Tools
ccusage: npm install -g ccusage or via npx ccusage
@ccusage/codex: npm install -g@ccusage/codex or via npx @ccusage/codex
📁 File Structure
~/.config/waybar/
├── config.jsonc # Waybar main configuration
├── style.css # Waybar styling
└── scripts/
├── ccusage-statusline.sh # Claude Code status script
├── codex-statusline.sh # Codex status script
└── api-config.sh (optional) # API configuration
📝 Step 1: Waybar Configuration
Open ~/.config/waybar/config.jsonc and add the two modules:
I think it is great that you’re transparent about your AI usage. I’d assume most of the down votes are because most people don’t want to see it on their Lemmy feed. What is the added benefit? If they wanted an AI generated config they’d generate it themselves, not search online for an AI generated one.
So I don’t believe that the generally ‘negative’ reponse reflects on you, just most users’ desire to not have AI content on their feed.
Waybar with Claude Code & Codex Usage Display
This guide shows how to configure Waybar (for Wayland/Hyprland) with live status displays for Claude Code and Codex. The modules show current usage and rate limits in real-time in the status bar.
📸 Example
Waybar displays two modules:
Example output:
🔧 Prerequisites
System
jq(JSON Processor)Node.js Tools
One of the following package managers:
npx(comes with Node.js)bunx(Bun)pnpmClaude Code & Codex Tools
npm install -g ccusageor vianpx ccusagenpm install -g @ccusage/codexor vianpx @ccusage/codex📁 File Structure
~/.config/waybar/ ├── config.jsonc # Waybar main configuration ├── style.css # Waybar styling └── scripts/ ├── ccusage-statusline.sh # Claude Code status script ├── codex-statusline.sh # Codex status script └── api-config.sh (optional) # API configuration📝 Step 1: Waybar Configuration
Open
~/.config/waybar/config.jsoncand add the two modules:1.1 Add modules to modules-center
Explanation:
exec: Path to the script that provides the datainterval: Update interval in seconds (300s = 5min)format: Display format (here directly the script output)on-click: Clicking triggers a manual script update📝 Step 2: Waybar Styling
Add the styling for the modules in
~/.config/waybar/style.css:#custom-ccusage { background-color: rgba(40, 42, 54, 0.8); padding: 4px 10px; margin: 2px 4px; border-radius: 8px; font-family: 'JetBrainsMono Nerd Font Mono'; font-size: 11px; color: #ff6ac1; /* Pink for Claude Code */ } #custom-codex-usage { background-color: rgba(40, 42, 54, 0.8); padding: 4px 10px; margin: 2px 4px; border-radius: 8px; font-family: 'JetBrainsMono Nerd Font Mono'; font-size: 11px; color: #6ef2d0; /* Turquoise for Codex */ }Customizations:
color: Text color (can be changed as desired)font-family: Nerd Font for icons (install JetBrainsMono Nerd Font)font-size: Adjust font size📝 Step 3: Create Scripts
3.1 Create script directory
mkdir -p ~/.config/waybar/scripts chmod +x ~/.config/waybar/scripts/*.sh3.2 Claude Code Script
Create
~/.config/waybar/scripts/ccusage-statusline.sh:#!/bin/bash # Compact ccusage summary for Waybar (daily totals). CONFIG_FILE="$HOME/.config/waybar/scripts/api-config.sh" [ -f "$CONFIG_FILE" ] && source "$CONFIG_FILE" # Prefer explicitly set binary, then cached npx installs, then package runners. choose_runner() { if [ -n "$CCUSAGE_BIN" ] && command -v "$CCUSAGE_BIN" >/dev/null 2>&1; then echo "$CCUSAGE_BIN" return fi # Reuse first cached npx install if present to avoid network lookups. CACHE_BIN=$(find "$HOME/.npm/_npx" -path '*/node_modules/.bin/ccusage' -maxdepth 5 -print -quit 2>/dev/null) if [ -n "$CACHE_BIN" ]; then echo "$CACHE_BIN" return fi if command -v ccusage >/dev/null 2>&1; then echo "ccusage" return fi if command -v bunx >/dev/null 2>&1; then echo "bunx ccusage@latest" return fi if command -v pnpm >/dev/null 2>&1; then echo "pnpm dlx ccusage" return fi if command -v npx >/dev/null 2>&1; then echo "npx --yes ccusage@latest" return fi } RUNNER=$(choose_runner) [ -z "$RUNNER" ] && { echo "CC ?"; exit 0; } # Run command with a timeout to avoid hanging on network fetches. run_cmd() { local cmd=$1 if command -v timeout >/dev/null 2>&1; then timeout 20s bash -lc "$cmd" else bash -lc "$cmd" fi } # Helper: call ccusage command and emit cost/tokens TSV. get_totals() { local cmd=$1 local json json=$(NO_COLOR=1 run_cmd "$RUNNER $cmd --json --offline" 2>/dev/null) if [ -z "$json" ]; then return 1 fi printf '%s' "$json" | jq -r '(.totals.totalCost // .totals.costUSD // 0) as $c | (.totals.totalTokens // 0) as $t | [$c, $t] | @tsv' 2>/dev/null } format_tokens_short() { local tokens=$1 if awk -v t="$tokens" 'BEGIN { exit (t >= 1000000000) ? 0 : 1 }'; then awk -v t="$tokens" 'BEGIN { printf("%.1fB", t/1000000000) }' elif awk -v t="$tokens" 'BEGIN { exit (t >= 1000000) ? 0 : 1 }'; then awk -v t="$tokens" 'BEGIN { printf("%.1fM", t/1000000) }' elif awk -v t="$tokens" 'BEGIN { exit (t >= 1000) ? 0 : 1 }'; then awk -v t="$tokens" 'BEGIN { printf("%.0fk", t/1000) }' else printf "%s" "$tokens" fi } build_progress_bar() { local percent=$1 local segments=8 local filled=$((percent * segments / 100)) local empty=$((segments - filled)) [ $filled -gt $segments ] && filled=$segments [ $empty -lt 0 ] && empty=0 local bar="" for ((i = 0; i < filled; i++)); do bar+="█"; done for ((i = 0; i < empty; i++)); do bar+="░"; done printf "%s" "$bar" } block_progress() { # Show current active 5h billing block status local json json=$(NO_COLOR=1 run_cmd "$RUNNER blocks --json --offline" 2>/dev/null) if [ -z "$json" ]; then return 1 fi # Extract active block info: cost, end time local block_info block_info=$(printf '%s' "$json" | jq -r ' .blocks[] | select(.isActive == true and .isGap == false) | [.costUSD, .endTime, .tokenCounts.inputTokens + .tokenCounts.outputTokens] | @tsv ' 2>/dev/null) || return 1 if [ -z "$block_info" ]; then # No active block, show "idle" printf "⏸ idle" return 0 fi local cost end_time tokens read -r cost end_time tokens <<<"$block_info" # Estimate block limit: Claude Code typically allows ~$15-20 per 5h block # We'll use $15 as baseline (adjustable via CLAUDE_BLOCK_LIMIT_USD) local block_limit="${CLAUDE_BLOCK_LIMIT_USD:-15}" local percent percent=$(awk -v c="$cost" -v l="$block_limit" 'BEGIN { if (l <= 0) { print 0 } else { printf("%.0f", (c / l) * 100) } }') || percent=0 [ -z "$percent" ] && percent=0 [ "$percent" -gt 100 ] && percent=100 [ "$percent" -lt 0 ] && percent=0 # Calculate time remaining until block ends local now_epoch end_epoch time_remaining now_epoch=$(date +%s) end_epoch=$(date -d "$end_time" +%s 2>/dev/null) if [ -n "$end_epoch" ] && [ "$end_epoch" -gt "$now_epoch" ]; then time_remaining=$((end_epoch - now_epoch)) local hours=$((time_remaining / 3600)) local mins=$(( (time_remaining % 3600) / 60 )) local time_str="${hours}h${mins}m" else local time_str="ending" fi local bar bar=$(build_progress_bar "$percent") printf "🕐%s %3d%% %s" "$time_str" "$percent" "$bar" } format_line() { local label=$1 local cost=$2 printf "%s $%.2f" "$label" "$cost" } BLOCK_STATUS=$(block_progress) if [ -z "$BLOCK_STATUS" ]; then echo "CC …" exit 0 fi echo "CC $BLOCK_STATUS"3.3 Codex Script
Create
~/.config/waybar/scripts/codex-statusline.sh:#!/bin/bash # Compact @ccusage/codex summary for Waybar (daily + monthly). CONFIG_FILE="$HOME/.config/waybar/scripts/api-config.sh" [ -f "$CONFIG_FILE" ] && source "$CONFIG_FILE" choose_runner() { # Explicit override. if [ -n "$CCUSAGE_CODEX_BIN" ] && command -v "$CCUSAGE_CODEX_BIN" >/dev/null 2>&1; then echo "$CCUSAGE_CODEX_BIN" return fi # Local install via npm prefix. LOCAL_BIN="$HOME/.local/share/ccusage-codex/node_modules/.bin/ccusage-codex" if [ -x "$LOCAL_BIN" ]; then echo "$LOCAL_BIN" return fi # Cached npx install. CACHE_BIN=$(find "$HOME/.npm/_npx" -path '*@ccusage/codex*/node_modules/.bin/codex' -maxdepth 5 -print -quit 2>/dev/null) [ -z "$CACHE_BIN" ] && CACHE_BIN=$(find "$HOME/.npm/_npx" -path '*@ccusage/codex*/node_modules/.bin/ccusage-codex' -maxdepth 5 -print -quit 2>/dev/null) if [ -n "$CACHE_BIN" ]; then echo "$CACHE_BIN" return fi if command -v bunx >/dev/null 2>&1; then echo "bunx @ccusage/codex@latest" return fi if command -v pnpm >/dev/null 2>&1; then echo "pnpm dlx @ccusage/codex" return fi if command -v npx >/dev/null 2>&1; then echo "npm_config_offline=true npx --yes @ccusage/codex@latest" return fi # Last resort: plain codex in PATH (may collide with other tools). if command -v codex >/dev/null 2>&1; then echo "codex" return fi } RUNNER=$(choose_runner) [ -z "$RUNNER" ] && { echo "codex ?"; exit 0; } run_cmd() { local cmd=$1 if command -v timeout >/dev/null 2>&1; then timeout 20s bash -lc "$cmd" else bash -lc "$cmd" fi } get_totals() { local cmd=$1 local json json=$(NO_COLOR=1 run_cmd "$RUNNER $cmd --json --offline" 2>/dev/null) if [ -z "$json" ]; then return 1 fi printf '%s' "$json" | jq -r '(.totals.totalCost // .totals.costUSD // 0) as $c | (.totals.totalTokens // 0) as $t | [$c, $t] | @tsv' 2>/dev/null } format_tokens_short() { local tokens=$1 if awk -v t="$tokens" 'BEGIN { exit (t >= 1000000000) ? 0 : 1 }'; then awk -v t="$tokens" 'BEGIN { printf("%.1fB", t/1000000000) }' elif awk -v t="$tokens" 'BEGIN { exit (t >= 1Did you write this post with AI too? What’s with the emojis?
Yes but its all checked. I’m transparent into this. Even if a lot of people dont like it. I gonna tell others when i use AI and when not.
That’s good :3 I don’t personally like using AI, but I think that people and content that is AI generated should be labeled as such.
According to the reviews, I should lie next time. A lot of nerdy people here don’t understand the backlash their behavior causes.
I think it is great that you’re transparent about your AI usage. I’d assume most of the down votes are because most people don’t want to see it on their Lemmy feed. What is the added benefit? If they wanted an AI generated config they’d generate it themselves, not search online for an AI generated one. So I don’t believe that the generally ‘negative’ reponse reflects on you, just most users’ desire to not have AI content on their feed.
What other people Write about is Not their descision. Maybe lemmy shoud have a checkbox for AI content?