cd /news/developer-tools/an-xbar-plugin-that-displays-your-cu… · home topics developer-tools article
[ARTICLE · art-10310] src=gist.github.com ↗ pub= topic=developer-tools verified=true sentiment=· neutral

An xbar plugin that displays your current Claude Code usage on your Mac's toolbar

This article describes a Python plugin for xbar, a macOS toolbar utility, that displays a user's daily Claude Code token usage. The script fetches usage statistics by running the `npx cusage@latest -j` command and formats the token count with human-readable suffixes like "K" or "M". If data is unavailable or an error occurs, the toolbar shows an error message or "0 tokens" for no usage.

read2 min views22 publishedJul 18, 2025

claude_tokens.5m.py

  This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.

Learn more about bidirectional Unicode characters

Show hidden characters

#!/usr/bin/env python3

import json

import subprocess

import os

import glob

from datetime import datetime

from typing import Any

def format_number(num):

    """Formats a number into a human-readable string with K/M suffixes."""

    if num >= 1000000:

        return f"{num / 1000000:.1f}M"

    if num >= 1000:

        return f"{num / 1000:.1f}K"

    return str(num)

def get_ccusage_data() -> dict[str, Any]:

    """Fetches Claude Code usage statistics using the `npx ccusage@latest -j` command."""

    try:


        env = os.environ.copy()


        common_paths = [

            "/usr/local/bin",

            "/usr/bin",

            "/bin",

            "/opt/homebrew/bin",  # Homebrew on Apple Silicon

            os.path.expanduser("~/.nvm/versions/node/*/bin"),  # NVM paths

            os.path.expanduser("~/node_modules/.bin"),  # Local node modules

        ]


        expanded_paths = []

        for path in common_paths:

            if "*" in path:

                expanded_paths.extend(glob.glob(path))

            elif os.path.exists(path):

                expanded_paths.append(path)


        current_path = env.get("PATH", "")

        for path in expanded_paths:

            if path not in current_path:

                current_path = f"{path}:{current_path}"

        env["PATH"] = current_path


        result = subprocess.run(

            ["npx", "ccusage@latest", "-j"],

            capture_output=True,

            text=True,

            timeout=30,

            check=False,

            env=env,

        )

        if result.returncode == 0:

            return json.loads(result.stdout)

        return {

            "error": f"Command failed with code {result.returncode}",

            "stderr": result.stderr,

            "stdout": result.stdout,

        }

    except subprocess.TimeoutExpired:

        return {"error": "Command timed out after 30 seconds"}

    except json.JSONDecodeError as e:

        return {"error": f"JSON decode error: {e}", "stdout": result.stdout}

    except FileNotFoundError:

        return {"error": "npx command not found - Node.js may not be installed"}

def main():

    """Main function to fetch and display Claude Code usage statistics."""


    today = datetime.now().strftime("%Y-%m-%d")


    data = get_ccusage_data()

    if not data or (isinstance(data, dict) and "error" in data):

        print("⚠️ Error")

        print("---")

        if isinstance(data, dict) and "error" in data:

            print(f"Error: {data['error']}")

            if "stderr" in data:

                print(f"Stderr: {data['stderr']}")

            if "stdout" in data:

                print(f"Stdout: {data['stdout']}")

        else:

            print("Failed to fetch usage data")

        return


    today_usage = None

    for day in data.get("daily", []):

        if day.get("date") == today:

            today_usage = day

            break

    if not today_usage:

        print("📊 0 tokens")

        print("---")

        print("No usage today")

        return


    total_tokens = today_usage.get("totalTokens", 0)

    total_cost = today_usage.get("totalCost", 0)

    print(f"📊 {format_number(total_tokens)}")

    print("---")

    print(f"Today's Usage ({today})")

    print(f"Total Tokens: {format_number(total_tokens)}")

    print(f"Input: {format_number(today_usage.get('inputTokens', 0))}")

    print(f"Output: {format_number(today_usage.get('outputTokens', 0))}")

    print(f"Cache Creation: {format_number(today_usage.get('cacheCreationTokens', 0))}")

    print(f"Cache Read: {format_number(today_usage.get('cacheReadTokens', 0))}")

    print(f"Cost: ${total_cost:.2f}")

    print("---")


    totals = data.get("totals", {})

    print("Total Usage (All Time)")

    print(f"Total Tokens: {format_number(totals.get('totalTokens', 0))}")

    print(f"Total Cost: ${totals.get('totalCost', 0):.2f}")

if __name__ == "__main__":

    main()
── more in #developer-tools 4 stories · sorted by recency
── more on @claude code 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/an-xbar-plugin-that-…] indexed:0 read:2min 2025-07-18 ·