Skip to content

Releases

v0.6.8

Choose a tag to compare

@ankit-arora-369 ankit-arora-369 released this 27 Oct 18:51
Immutable release. Only release title and notes can be modified.

Fixed

  • Other icons were drawn over the menu bar title. 0.6.7 dropped to the glyph
    alone whenever macOS hid the item, and restored the title on every app switch.
    macOS widened the item to the right, over icons it had already packed into the
    space the glyph had freed, then pushed it back under the notch — the app's own
    log shows it flipping every few seconds, and a screenshot shows two other icons
    across "100% ~$302". The automatic shrinking is gone; hidden spells are still
    logged and reported by doctor.
  • The title was re-applied every two seconds even when unchanged, each time an
    invitation to lay the menu bar out again. It is now set only when it changes.
  • The item's width changes as rarely as the text allows. It grows at once,
    since the text must fit, and shrinks only once a narrower title has lasted ten
    minutes, so a percentage falling at a window reset does not reshuffle the bar.
    Checked on a real menu bar with the new --probe-width (84 → 118 → held at 118;
    the old behaviour sprang back to 84), which also runs in CI.

v0.6.7

Choose a tag to compare

@ankit-arora-369 ankit-arora-369 released this 19 Oct 14:05
Immutable release. Only release title and notes can be modified.

Fixed

  • The menu bar icon kept disappearing. On a MacBook, macOS places menu bar
    icons that do not fit under the notch, where nothing is drawn — the app keeps
    running and its window still reports itself visible, so it looks like a crash.
    The room changes with the frontmost app's menus and with which display's menu
    bar is active, so the icon came and went. The app now checks where it actually
    is on every poll (under or left of the notch, or past a display's edge), drops to
    the glyph alone when the full title does not fit, and tries the full title again
    when you switch apps or displays. Verified on a real menu bar: an over-wide title
    placed 415pt past the display edge was detected, and the 34pt glyph that
    replaced it was back on screen.

  • A position you dragged the icon to was forgotten on every launch. The status
    item had no autosave name, so macOS returned it to the leftmost spot — the first
    one hidden. ⌘-dragging it toward Control Center now sticks.

  • --probe-menubar changed your menu bar style and left it changed. It wrote
    each style to the preference to measure it; it now restores yours.

  • The smoke test could pass a real credential leak. Its privacy checks piped
    strings into grep -q under pipefail; grep exiting at its first match made
    the pipeline fail with SIGPIPE, which the if read as "nothing found". Shown
    with a key-shaped string at the start of a 4 MB file: reported clean. Every
    script now captures, then matches, and a test rejects the pattern anywhere.

Added

  • scripts/hook-e2e.sh, now in the repository and in CI: the status line hook
    installed into a real settings file, run through the exact command Claude Code
    runs, then uninstalled; subagent and local-model accounting; and the snapshot
    the menu bar reads. It refuses to report a pass unless every check ran.

  • doctor says when macOS has been hiding the icon, from a short log the app
    keeps of the transitions (positions and display widths only), and how to stop it.

  • --probe-fit forces an over-wide title on the real menu bar and reports
    whether the app detected it and whether the glyph then fit.

  • doctor called a stopped source "ready". The Claude desktop app changed in
    October 2026: it now fetches your usage at launch and keeps polling only if its
    menu bar icon was opened within a server-set number of hours. On the machine
    this was found on, its record stopped at the app's launch and stayed silent for
    six days, while doctor reported it ready and the Claude percentage froze.
    doctor now shows silent since … for a plan-usage file with no sample in
    two hours, and says how to resume it.

v0.6.6

Choose a tag to compare

@ankit-arora-369 ankit-arora-369 released this 02 Oct 08:20
Immutable release. Only release title and notes can be modified.

Fixed

  • "Today" began at UTC midnight, so the menu bar could show nothing. Every
    day boundary — today, yesterday, this month, the daily report and the hourly
    chart — was cut in UTC. In India that put midnight at 05:30: usage at 01:21 was
    filed under yesterday, and at 13:30 the menu bar showed — for a day with real
    usage. The hourly chart's bars were labelled in UTC too, five and a half hours
    off. All of them now follow your own time zone, including on clock-change days.
  • A few cents of spend showed as $0.0. The compact menu bar title rounded to
    one decimal, so a light day read as nothing spent. Amounts under a dollar keep
    their cents, and anything above zero that would still round to zero shows as
    <$0.01.
  • The menu bar app could lose its engine on brew upgrade. The app runs the
    engine with a GUI app's minimal PATH, so the engine recorded its own Homebrew
    Cellar path — one naming a version brew upgrade deletes. It now records the
    stable /opt/homebrew/bin path, as it already did from a terminal.

Added

  • Tests that run the engine in other time zones, and CI runs the whole suite
    under Asia/Kolkata and Pacific/Kiritimati as well as UTC — the one zone where
    this bug could not show.
  • scripts/menubar-e2e.sh: a transcript on disk, a real scan and the title
    the compiled app computes, in four zones and both title styles. It runs in CI
    against every build, and fails on 0.6.5. The smoke test now also checks that
    "today" starts at the machine's own midnight.

Changed

  • The README leads with the install that gives you the menu bar app. It now
    opens with ./install.sh — app, command line, background scanning and login
    item in one command — and puts pipx install burn-o-meter second, labelled as
    the command line only. A reader who took the one-liner and waited for a menu bar
    icon that could never appear was the likeliest first experience.

Fixed

  • install.sh told you to look for 🔥 in the menu bar. The status item has
    shown the meter-and-flame glyph since 0.6.2, so the one thing it said to look for
    was not there.

v0.6.5

Choose a tag to compare

@ankit-arora-369 ankit-arora-369 released this 28 Sep 21:32
Immutable release. Only release title and notes can be modified.

Added

  • On PyPI. pipx install burn-o-meter (or uv tool install burn-o-meter) is
    now the whole install for the CLI, needing neither git nor a compiler. Published
    through Trusted Publishing — PyPI trusts this repository's workflow directly, so
    no upload token exists — and the files on PyPI are the ones attached to the
    GitHub release, not a separate build. PyPI gets a short project page of its own,
    because the README's relative links and images all break there.

Fixed

  • statusline install said it took effect only in new Claude Code sessions.
    Running sessions pick it up on their next turn, as observed twice while
    releasing 0.6.4; the message now says so.

v0.6.4

Choose a tag to compare

@ankit-arora-369 ankit-arora-369 released this 28 Sep 20:09
Immutable release. Only release title and notes can be modified.

Upgrading: brew upgrade burn-o-meter (or reinstall with pipx/uv), then run burn-o-meter statusline install once to get Claude's rate-limit percentage live from Claude Code. Existing data is corrected automatically on the first scan: subagent usage is added, and Codex sessions on a local model are relabelled not metered.

Added

  • Claude's rate-limit percentage, live from Claude Code. Claude Code receives
    the service's own 5-hour and weekly figures on every response and passes them to
    its status line command; burn-o-meter statusline install makes that command
    burn-o-meter, which keeps the two percentages and their reset times, discards the
    rest of the payload (working directory, transcript path, session id), and prints
    5h 43% · 7d 19% as the status line. An existing status line is kept and still
    shown; uninstall restores it, and so does ./uninstall.sh, so removing
    burn-o-meter never leaves Claude Code calling a deleted binary. Opt-in:
    ./install.sh suggests it rather than editing Claude Code's settings for you.
    The reset time is the service's own rather than derived. Found because the menu bar read 0% on a window Claude Code had taken
    to 43%: the desktop app's last sample predated the window, and it wrote no
    other for over half an hour.
  • today shows Claude's percentage, with its age and reset time, alongside
    Codex's. It had shown only Codex's, and told Claude users no quota was stored.

Fixed

  • Subagent usage was never counted. Claude Code writes a subagent's turns to
    <session>/subagents/agent-<id>.jsonl, not to the session's transcript — none of
    their messages appear in the parent file — and the adapter read only the latter.
    Found by comparing stored totals against Claude Code's own per-session figures.
    The pattern names the transcript exactly, so the .meta.json, tool output and
    notes beside it stay unread.
  • …and a subagent message kept its first, partial copy. Unlike a session's
    transcript, a subagent's is written while a message streams, so the first copy
    carries a fraction of the output: across real sessions, 49 output tokens in
    first copies against 17,686 in the finished ones. The copy with the most output
    now wins, within a scan and across scans, and a later copy with less never
    lowers a stored figure.
  • Codex pointed at a local model was reported as unpriced. Codex records who
    served a session (model_provider) and the adapter did not read it, so a model
    on Ollama or LM Studio said "rate unknown" where the truth is "no rate exists".
    It is now not metered, like the same model through OpenCode or Kimi Code.
    Verified by running Codex 0.156 against Ollama; sessions already scanned are
    re-read once on upgrade and corrected in place.
  • Any unrecognised option started a second copy of the app. The app treated
    a flag it did not know as no flag at all, so burn-o-meter.app/…/burn-o-meter --help put a second meter in the menu bar and never returned. --help now
    lists the options, and an unknown one exits with an error.
  • A reset time was claimed for a window nobody had opened. With the five-hour
    figure at 0%, the gap between two idle desktop samples was taken for first use,
    and a reset five hours after it was reported for a window that did not exist.
    A 0% reading now carries no reset time.
  • python -m burnometer did not run, though the engine records it as the
    fallback way to invoke itself when no burnometer executable is on the PATH.

v0.6.3 — a warning before the limit

Choose a tag to compare

@ankit-arora-369 ankit-arora-369 released this 23 Sep 19:34
Immutable release. Only release title and notes can be modified.

The menu bar warns before a limit runs out

The rate-limit reading in the menu bar turns red at 90% — the same colour, at the same
threshold, as its row in the popover, now read from one constant so the two can never
disagree. Only the percentage changes colour, never the spend beside it.

It is on by default: a colour needs no permission and cannot interrupt, so there is
nothing to opt into. Menu Bar Shows → Colour the Limit at 90% turns it off.

90% was measured, not chosen. Against a month of real plan-usage history it flagged
every window that went on to run out, with the fewest false alarms of the rules tried —
including a cleverer one projecting whether a window would run out before its reset,
which did no better. No rule can warn more than about fifteen minutes ahead, because
Claude records usage only that often. The full reasoning is in
docs/design-budgets-and-alerts.md.

Claude Opus 5.5 is priced

It arrived unpriced, so its usage showed as —. It is now priced exactly as Anthropic
publishes it, as is Claude Fable 5.1. All twelve current Claude models match Anthropic's
pricing page exactly, including cache reads, which are most of a Claude Code bill and
which nothing previously checked. Opus 5.5 charges 0.05× input for cache reads rather
than the usual 0.1×; that was confirmed against Anthropic's own page before it shipped.

Three fixes make sure the new rates actually arrive:

  • An upgrade now reaches people who have run pricing refresh. A refreshed pricing
    file used to win simply by existing, so an older one kept shadowing the newer rates an
    upgrade shipped. The more recently generated snapshot now wins.
  • Retired models keep their rates. Moonshot retired its K2 generation. A model
    leaving the catalogue does not change what its tokens cost while it was sold, so a
    refresh now keeps those rates instead of retroactively blanking past costs.
  • A refresh can never price less than the install it replaces.

Also

  • macos/make-app.sh recognises the failure you get building on macOS 27 with only the
    Command Line Tools — SwiftUI's macros ship only with Xcode — and prints the two
    commands that fix it, instead of a wall of compiler errors.
  • CodeQL moves to v4 and download-artifact to v8, both pinned to verified commit SHAs.
  • Two CodeQL notes cleared.

Full notes in CHANGELOG.md.

Upgrading

brew update && brew upgrade devopsinside/burn-o-meter/burn-o-meter

The menu bar colour is in the app bundle, not the CLI, so rebuild and reinstall the app:

git pull && macos/make-app.sh --install && open /Applications/burn-o-meter.app

On macOS 27 that needs full Xcode selected. The bundle is ad-hoc signed and still needs
Developer ID notarisation for distribution.

v0.6.2 — the meter in the menu bar

Choose a tag to compare

@ankit-arora-369 ankit-arora-369 released this 15 Sep 19:14
Immutable release. Only release title and notes can be modified.

The menu bar carries the meter, not a flame emoji

The status item now shows the same meter-and-flame as the app icon and the org
avatar, as a template image — so macOS tints it for the bar it lands in and inverts
it while the item is held down.

Getting it to sit level took four attempts and a reader's screenshots, because the
reference was wrong: the glyph was being aligned to its own adjacent text rather
than to the bar. Apple's SF Symbols settle it — gauge.medium, speedometer and
flame.fill all centre their ink at 0.498–0.500 and fill ~0.90 of their box. The
image is now built from the shape: the drawing is rasterised, its ink measured,
and the image sized and placed so that ink fills it and is centred, which is what
every other item in the bar does. The title then gets a one-point baseline offset,
because digits and a percent sign have no descender and otherwise ride high.

--check-layout measures Apple's symbols as well as ours, so the target fails
loudly if their convention moves rather than quietly going stale.

Two pricing bugs, one of them live

A refresh that returned nothing was saved. A refreshed snapshot shadows the
packaged one completely, so a file written with zero models left a real machine
unable to price anything — the model carrying 97% of that user's spend rendered as
an em dash, with no error and no symptom but a cost that had stopped existing.
Upstream was healthy when checked, so the bad response was transient and we
persisted it. refresh_snapshot now refuses a result below a plausibility floor
and leaves the existing file alone, and a refreshed snapshot has to earn its
precedence on every read — because refusing to write only helps machines that do
not already have a bad file.

reprice relabelled locally-served events. It made the pricing decision itself
instead of calling the function the scan path calls, so it never learned about
not_metered: one burn-o-meter reprice turned "there is no rate, it ran on your
own hardware" into "we do not know the rate". Invisible, because both render as an
em dash. The decision now lives in one function both callers use.

Supply chain

22 Semgrep findings to none. Every action is pinned to a commit SHA — a tag can be
repointed by its owner, which is how the trivy-action and kics-github-action
compromises reached their users. Dependabot keeps the pins current, now with a
seven-day cooldown. Workflow inputs reach shell scripts through env rather than
${{ }} interpolation, which is substituted before the shell sees the text at all.

The pricing refresh pins both scheme and host: SECURITY.md has always said there is
exactly one egress, and that is now a property of the code, enforced by tests.

Also

  • Rate-limit rows show a reading's age whenever the source samples periodically,
    not only once it has aged — which is what makes a figure differing from the
    Claude app's explicable rather than suspicious. The Claude app asks Anthropic
    directly; we read what it writes to disk, about every 15 minutes.
  • A test asserts the macOS app knows every cost basis the engine can write. It
    decodes unknown values as unpriced, which would be a specific and wrong claim.
  • The · between percentage and spend is gone; it read as a stray full stop.

Full notes in CHANGELOG.md.

Upgrading

brew update && brew upgrade devopsinside/burn-o-meter/burn-o-meter

The menu bar app is a separate bundle — the glyph and alignment work is in it,
not the CLI:

git pull && macos/make-app.sh --install && open /Applications/burn-o-meter.app

--install matters: macOS will not register a login item for an app outside
/Applications, so one built in place never returns after a reboot. The bundle is
ad-hoc signed and still needs Developer ID notarisation for distribution, which
this project does not yet have.

v0.6.1 — the menu bar updates itself

Choose a tag to compare

@ankit-arora-369 ankit-arora-369 released this 09 Sep 21:07
Immutable release. Only release title and notes can be modified.

Two menu bar bugs, both reported from real use, both invisible to every check the
project had.

The number only moved when you clicked it

The poll timer fired every two seconds and re-read the payload — but nothing
regenerated it. The only code path that scans ran on launch, on opening the
popover, and from Scan now. The timer assumed the background agent was
keeping that file current, and that agent is optional and off by default, so on
most machines nothing was. The title showed whatever was true when the popover
last opened.

The timer now scans for itself whenever the payload is older than a minute, and
skips it otherwise — which is exactly the case when the agent is running, so the
two never duplicate work. A scan is 15–250ms against a warm database.

A stale reading was presented as current

A quota's age came from the payload's own age_seconds, which the engine computes
once, when it writes the file, and never revises. With nothing rewriting the
file that number stayed frozen — so a reading hours past its 15-minute sampling
interval still classified as current, still led the menu bar, and still had its
percentage rendered as a statement about now. Reported as the percentage sitting
at an old value after the limit had actually been reached.

Age is now derived from observed_at against the clock, so it grows, and a
reading that ages out is drawn back instead of trusted. The "as of X ago" labels
and the stale banner read the same frozen number and are fixed by the same change.

The source was never at fault: Claude's own history does reach 100, and 100%
renders correctly in every menu bar style.

Guarding both

--check-freshness joins --check-layout as a CI self-check on the app binary.
It pins the freshness boundaries, asserts a stale reading never reaches the menu
bar and that 100% does, covers a payload with no observed_at so an older engine
still works, and covers a future timestamp so clock skew cannot produce a negative
age. Reintroducing either bug fails it.

Full notes in CHANGELOG.md.

Upgrading

brew update && brew upgrade devopsinside/burn-o-meter/burn-o-meter

If you run the menu bar app, rebuild and reinstall it — the fix is in the app
bundle, not the CLI:

git pull && macos/make-app.sh --install && open /Applications/burn-o-meter.app

--install matters: macOS will not register a login item for an app outside
/Applications, so one built in place never comes back after a reboot. The bundle is
ad-hoc signed and still needs Developer ID notarisation for distribution, which
this project does not yet have.

v0.6.0 — Kimi Code

Choose a tag to compare

@ankit-arora-369 ankit-arora-369 released this 29 Aug 07:44
Immutable release. Only release title and notes can be modified.

Kimi Code is now measured, alongside Claude Code, Codex CLI and OpenCode.

Read from ~/.kimi-code/sessions/*/*/agents/*/wire.jsonl. Like OpenCode it routes
to any provider, so it covers Moonshot's own models and anything you run locally.

What reading the real logs turned up

  • Only usage.record is billable. Every turn also writes two token_counting
    records carrying a tokens field that looks like usage and is not — across 6/6
    real turns it equals inputOther + output exactly. Summing them would have
    roughly doubled every figure. That identity now ships as the adapter's integrity
    check.
  • Usage is per turn, not cumulative. A three-turn session recorded outputs of
    276, 239 and 202 — falling, so not a running total. Checked because Codex looked
    identical and was cumulative.
  • Ollama truncates a prompt larger than num_ctx and reports the tokens it
    actually processed. 8,000 tokens against the default 4,096-token window are
    reported as 2,050; the same prompt at num_ctx=16384 reports 8,011. The figure
    is honest — it is what the model read — and Kimi records it faithfully. It just
    is not the size of what you typed.

Pricing

The packaged snapshot now carries 287 models from 16 vendors, up from 132 from
6. DEFAULT_VENDORS had been extended with Moonshot, Zhipu, Alibaba, MiniMax and
the inference hosts, but the file was never regenerated — so a fresh install could
not price Kimi, GLM, Qwen or MiniMax at all, while the docs said it could. Three
tests now hold the file to the vendor list, to pricing one model per supported
agent, and to the count printed in the docs.

deepseek-chat and deepseek-reasoner no longer carry a rate: models.dev dropped
both in favour of versioned names, and no stale figure was carried forward. Set
your own in ~/.burn-o-meter/pricing.toml if you need them.

Fixed

  • billing.<provider> now works for every agent, not just the two named as
    dataclass fields — doctor was printing advice to set a key the loader ignored.
  • A config.toml omitting any key could not be loaded at all, because the
    defaults were read off slots=True dataclasses, where the class attribute is a
    slot descriptor rather than the default value.
  • The network-egress table no longer lets a narrow terminal elide the one URL it
    exists to disclose.
  • Report tests no longer depend on the width of the terminal running them.

Full notes in CHANGELOG.md.

Install

brew tap devopsinside/burn-o-meter https://github.com/devopsinside/burn-o-meter
brew install devopsinside/burn-o-meter/burn-o-meter

The macOS app bundle is ad-hoc signed: it runs on the machine that builds it, and
distribution needs Developer ID notarisation, which this project does not yet have.

v0.5.0 — local models, told apart from unknown ones

Choose a tag to compare

@ankit-arora-369 ankit-arora-369 released this 28 Aug 19:05
Immutable release. Only release title and notes can be modified.

Upgrading migrates your database on first open. Nothing is lost; see below.

Local models are now not_metered, not unpriced

A model running on your own hardware was reported the same way as a hosted model whose rate we simply do not have. Both show no dollar figure, but the claims differ: one says we are ignorant, the other there is nothing to know. doctor and the scan summary now count them apart.

not metered   1 (qwen3:0.6b) — no per-token rate exists
unpriced      1 (hy3-free)

Schema version 2, and the project's first migration

usage_events gains upstream_provider: a router like OpenCode reports OpenAI, DeepSeek and a local model through one adapter, and only this distinguishes them.

CREATE TABLE IF NOT EXISTS never alters an existing table, so without a migration an upgrade would keep the old shape and fail on the first insert. It runs on open, is keyed on user_version, and is idempotent.

Existing OpenCode rows are dropped so the next scan rebuilds them with the field set — they cannot be repaired in place, because inserts are DO NOTHING by design. Nothing is lost: every row derives from logs your agents still hold. Scan offsets are deliberately untouched.

Verified on a real 2,349-event database: version 1 → 2, column added, offsets kept, stable across repeated opens.

Fixed

  • The OpenCode test fixture was never committed. .gitignore carries *.db to protect your real database and caught the fixture too, so those tests passed locally and errored on every CI run. A test now asserts the fixture is tracked.
  • Six tests decayed with the calendar. The Claude plan-usage fixture used absolute timestamps while the adapter drops samples older than seven days — so they were always going to expire, and did, exactly a week later. Nothing in the product was wrong.

Install

git clone https://github.com/devopsinside/burn-o-meter
cd burn-o-meter && ./install.sh

Full history in CHANGELOG.md.