Skip to main content
All articles
Tool launch
ClaudeBar: A Native macOS Menu Bar App for Claude Code Usage, No Terminal Required

Tool launch

ClaudeBar: A Native macOS Menu Bar App for Claude Code Usage, No Terminal Required

We wanted to see our Claude Code token usage at a glance without opening a terminal. An afternoon later we had ClaudeBar: a tiny SwiftUI menu bar app that parses Claude's local transcripts directly, shows live burn rate, and ranks every session by tokens used. Open source.

Claude CodeDeveloper ToolsmacOSOpen SourceSwift
AuthorSantiago Lobo
RoleFounder & Lead Developer
Published2026-04-21
Reading time7 min
Sections7
Scroll to read

01 / READ

The Problem: ccusage Lives in a Terminal You Keep Closing

Claude Code writes a JSONL transcript of every session to `~/.claude/projects/`. `ccusage` is the community CLI that parses those files and tells you how many tokens you have burned in the current 5-hour block, today, this week. It works beautifully from a terminal.

The problem is that most of the day you are not in a terminal. You are in Figma, or a browser, or a Slack thread, or your editor. Alt-tabbing to a terminal just to type `ccusage blocks --active` is the same friction that drives people to ignore cost dashboards. We wanted a number in the menu bar that we glance at while the kettle boils.

An afternoon of Swift later, we had ClaudeBar: ~600 lines of SwiftUI, no Electron, no dependencies, no sign-in, ~1.6 MB binary. It reads Claude's local JSONL files directly and renders a tabbed popover with everything we would actually want to see.

ClaudeBar is open source at github.com/displace-agency/claudebar. MIT licensed. Download the DMG from Releases, drag to Applications, done.

ClaudeBar Overview tab showing today's token usage, active 5-hour block, and model breakdown
Overview tab. Tokens are the primary metric. The dollar figure is only shown as an API-equivalent hint.

By the numbers

~1.6 MB

Binary size

0

Dependencies at runtime

~20 ms

Refresh with mtime cache

100%

Local. No network calls.

Why Tokens, Not Dollars

Most Claude usage dashboards lead with a dollar figure. That makes sense if you are billed per API call. It makes no sense on a Pro or Max plan, where the dollar number is entirely hypothetical. On a subscription, tokens are the real resource: you have a weekly budget and the question you want answered is "am I on track to fill my 5-hour block, or do I have headroom?".

So ClaudeBar leads with tokens, and the dollar figure is muted alongside as `≈ $X API`. Costs are still useful as a sanity check — you want to know when you have torched 100 million output tokens on one project — but they are a secondary signal, not the headline.

That reframe unlocked a small but meaningful design decision: the Cache Hits stat. Cache reads look expensive in token counts (they dominate total tokens on any long session) but are billed at 10% of fresh input. Instead of burying cache reads in the totals, we surface them as a positive metric: the higher your cache hit rate, the more efficient you are being. 99% reuse? Great, you are keeping context warm.

Three Tabs, Three Questions

ClaudeBar answers three questions, one per tab. Overview: am I burning hot right now? Sessions: which conversations are eating my budget? History: what does my usage look like over time?

ClaudeBar Sessions tab showing a ranked list of sessions grouped by project folder with Recent and Biggest toggles
Sessions tab. Recent or Biggest toggle, one row per session, grouped by project folder with inferred names for home-dir sessions.

The Sessions tab was the one that surprised us. Claude Code encodes a session's working directory (`cwd`) into each JSONL event, so for project-scoped sessions the name is obvious: `website-todoweekend`, `tool-claude-bar`, `website-lomvok`. But most of our sessions ran from the home directory, because we had started Claude from `~` and named the project later in the first user message. 368 of our 389 sessions were labelled `Home` — which is useless.

The fix was a small inference pass. For any home-dir session, ClaudeBar scans the first 40 lines of the transcript for references to `/websites/<project>` or `CLIENTS/<client>` paths, filters out obvious placeholders, and uses the most-mentioned project as the display name. That pushed our recovery rate from 0% to 82%. The remaining 18% of home-dir sessions genuinely have no project context and stay as `Home`.

ClaudeBar History tab with last 7 days, last 30 days, and all-time totals plus a 12-week bar chart and a daily detail list
History tab. Last 7 / 30 days and all-time totals at the top, weekly bars for long-range trend, daily detail below.

We Almost Shipped with ccusage Under the Hood. Then It Hung.

The first version of ClaudeBar was a thin wrapper over `ccusage`: shell out to the Node CLI, parse its JSON, update the menu bar. From a terminal, `ccusage blocks --active` runs in under a second. From a macOS app context, the same command hung for 30 seconds or longer, with Node stuck in a filesystem callback loop. We tried six things: direct binary invocation, concurrent pipe draining, raising QoS, a battery of environment variables, /dev/null stdin, wrapping in a login shell. None of it helped.

At that point it was simpler to just reimplement the parser in Swift. The JSONL format is trivial: each line is a message event with `timestamp`, `message.model`, and `usage.{input,output,cache_creation,cache_read}_tokens`. Aggregate by day, by 5-hour window, by session, by model. Total Swift weight: 240 lines in `TranscriptParser.swift`. We also added two things ccusage does not: mtime-keyed file caching (so unchanged transcripts are skipped on every 60-second refresh) and deduplication by `(messageId, requestId)` to avoid double-counting resumed sessions.

swift
let cwd = (obj["cwd"] as? String).flatMap { $0.isEmpty ? nil : $0 }
let projectPath = cwd
    ?? Self.decodeProjectFolder(url.deletingLastPathComponent().lastPathComponent)

let messageId = (message["id"] as? String) ?? ""
let requestId = (obj["requestId"] as? String) ?? ""
let dedupKey = messageId.isEmpty && requestId.isEmpty
    ? "\(sessionId):\(timestampStr)"
    : "\(messageId):\(requestId)"

The payoff is that ClaudeBar has no runtime dependencies. macOS 13 or newer, drag to Applications, done. No Node, no ccusage, no auto-update nag, no telemetry.

The Menu Bar Title, Color-Tinted by Burn Rate

The menu bar title shows the active 5-hour block's token count, tinted green, amber, or red based on cost-per-hour burn rate. Green below $5/hr, amber below $15/hr, red above. We chose the thresholds empirically — watching the red tint appear during an ambitious refactor is a surprisingly effective nudge to pause and consider whether the next prompt is worth it.

  • Menu bar icon: SF Symbol (chart.bar.xaxis) as a template image, auto-adapts to dark/light menu bar
  • Title text: monospaced digits so the width does not jitter every refresh
  • Color tint: NSColor on contentTintColor + attributedTitle, so both icon and text recolor together
  • Left-click: opens the popover; right-click: context menu with Refresh and Quit
  • No dock icon (LSUIElement = true), so the app is invisible outside the menu bar

Build, Release, Install

The whole thing is a Swift Package — no Xcode project, no storyboards. `./scripts/build-app.sh [version]` produces a universal (arm64 + x86_64) `.app` bundle and ad-hoc signs it. A GitHub Actions release workflow does the same build on `macos-14` runners when you push a `v*` tag, and uploads a DMG to the release. End-to-end from `git tag v0.4.0 && git push --tags` to a downloadable DMG: about four minutes.

bash
# Build locally
./scripts/build-app.sh 0.4.0
open build/ClaudeBar.app

# Cut a release
git tag v0.4.0
git push --tags
# → GitHub Actions builds DMG and attaches it to the release

First-launch on an unsigned build needs the right-click → Open → Open dance to get past Gatekeeper. That is standard for ad-hoc-signed apps and we decided not to pay for a developer certificate for something this small.

Why Open-Source It

This is the fourth open-source tool we have shipped in four weeks, after x-bookmarks-exporter, FocusGuard, and copy-translate. The pattern keeps recurring: a small annoyance, an afternoon of code, a repo. The code rarely takes long. The discipline is pushing it out instead of leaving it in a one-off script on a laptop.

If you are on Claude Pro or Max and you have ever wondered "did I just blow through my 5-hour block?" — install this. If you are on the API and you want the dollar number in your menu bar, the feature is already there, just labeled as hypothetical. And if you want to extend it, the code is small enough to read in one sitting.

ClaudeBar is live at github.com/displace-agency/claudebar. MIT license. macOS 13+, universal binary, ~1.6 MB. Drag to Applications and watch your token burn rate from the menu bar.