Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
test:
Expand Down
46 changes: 46 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,51 @@
# Changelog

## v0.20.0 — 2026-07-28 — the waste ledger: see what you paid for and didn't use

**New: `statusline.sh report [--days N]` — the waste ledger.** The
advisor (v0.19) prevents waste prospectively; this proves it
retroactively. It replays the usage log the statusline has been writing
all along, detects every window close (consecutive samples disagreeing
on `resets_at`, normalized to the minute — the ratio learner's identity
rule), and ledgers each closed 7d window as used% / expired%, converted
into 5h-windows-worth via the learned `pct_per_window` exchange rate:

```
7d windows closed: 1
Tue 07-28 00:00 used 51% expired 49% (~4.7 x 5h windows unused)
5h windows closed: 3 avg 95% at close 2 hit the cap
exchange rate: one full 5h window = ~10.46% of the week (~9.6 windows/week, learned)
week in progress: 5% used, resets Mon 08-03 23:59
```

"week in progress" runs the same learned walk the advisor's heading
uses — the surfaces cannot disagree. Honest limits are documented: the
final utilization is the last sample before a reset, so usage from other
clients after your last local render is invisible, and never-sampled
windows don't appear.

**Widened snapshots: log today what the learner needs next month.**
Every `usage` line in usage.jsonl now also records `limits[]` (scoped
per-model weekly caps, verbatim), `model` (the id active in the logging
session), and `predicted_end` — the learned walk's end-of-week
projection *at sample time*. That last one is the calibration seed: once
windows close, predictions can be scored against observed finals, and
the forecast's accuracy becomes measurable instead of assumed. Learning
lags logging by weeks; fields absent today are patterns that can't be
learned next month.

**Fixed: `catch .` gave every empty `resets_at` one shared fake window
identity.** jq's `catch .` yields the error *message*, not the original
input — so snapshots with an empty/unparseable `resets_at` all
normalized to the same error string and could pair across real windows
in the ratio learner, quietly skewing `pct_per_window` (observed on real
data: phantom window closes and a drifted ratio). Both norm sites now
fall back to the raw input string and treat empty as empty.

Mechanics: subcommands dispatch after all function definitions, take no
stdin, and are read-only against the state dir. 327 tests (315
statusline + 12 installer).

## v0.19.0 — 2026-07-28 — smart advisor line, claude-watch retired

**New: the advisor — a second statusline row that interprets the badges.**
Expand Down
32 changes: 31 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,36 @@ notifications), see `claude.py --watch-usage` in
[claudex](https://github.com/thevibeworks/claudex) — it consumes the same
state dir this script maintains (see `docs/api/state-dir.md`).

## The waste ledger

The advisor prevents waste prospectively; `report` proves it
retroactively. It replays the usage log the statusline has been writing
all along and ledgers every closed window — what you used, what expired:

```bash
$ ~/.claude/statusline.sh report # or --days 90
usage report - work (last 28d, 79 samples)

7d windows closed: 1
Tue 07-28 00:00 used 51% expired 49% (~4.7 x 5h windows unused)

5h windows closed: 3 avg 95% at close 2 hit the cap
exchange rate: one full 5h window = ~10.46% of the week (~9.6 windows/week, learned)

week in progress: 5% used, resets Mon 08-03 23:59
```

That "expired 49%" line is the subscription math nobody shows you: half
a week of paid capacity, gone. The windows-worth figure uses the same
learned `pct_per_window` ratio the advisor's feasibility check uses, and
"week in progress" runs the same learned projection — the surfaces
cannot disagree.

Honest limits: a window's final utilization is the last sample before
its reset, so usage from other devices after your last local render is
invisible, and a week you never opened a session in never appears at
all. The ledger reports what the log observed, nothing more.

<details><summary>OAuth and API behavior</summary>

Quota, profile, and extra-usage requests use Claude Code's OAuth credentials
Expand Down Expand Up @@ -332,7 +362,7 @@ run. Setting only `CLAUDE_CACHE_DIR` keeps the legacy single-dir behavior.
npm exec --yes bats -- t/
```

321 tests across `t/statusline.bats` (309 statusline + integration) and
327 tests across `t/statusline.bats` (315 statusline + integration) and
`t/install.bats` (12 installer). CI runs on push and PR to `main`.

## Project Structure
Expand Down
18 changes: 16 additions & 2 deletions docs/api/state-dir.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ is the ONLY writer; everything here is safe to read concurrently.

- Contract version: **1** (bump on any breaking layout/field change; this
file is the changelog)
- Synced with: statusline.sh v0.19.0
- Synced with: statusline.sh v0.20.0
- Permissions: the script runs under `umask 077` — files are owner-only.
Caches hold account PII (email, uuid, org names).

Expand Down Expand Up @@ -91,13 +91,27 @@ Append-only, one JSON object per line, three event types:

| `type` | Emitted | Payload |
|--------|---------|---------|
| `usage` | every successful usage fetch | `session_id`, `timestamp`, `user{email,name,uuid,…}`, `organization{…}`, `five_hour`, `seven_day`, `seven_day_opus`, `extra_usage` |
| `usage` | every successful usage fetch | `session_id`, `timestamp`, `user{email,name,uuid,…}`, `organization{…}`, `five_hour`, `seven_day`, `seven_day_opus`, `extra_usage`, `limits[]`, `model`, `predicted_end` |
| `session_start` | first fetch of a new 5h window | `session_id`, `timestamp`, `five_hour_window_end`, `seven_day_window_end` |
| `session_end` | 5h window rolled while a different session was last | `session_id`, `timestamp` |

Since v0.20.0 each `usage` line also records what the learner will need
later (learning lags logging — a field absent today is a pattern that
cannot be learned next month):

| Field | Type | Meaning |
|-------|------|---------|
| `limits[]` | array | scoped limits verbatim (per-model weekly caps) |
| `model` | string \| null | model id active in the logging session |
| `predicted_end` | int \| null | the learned walk's end-of-week projection at sample time; null until the profile is warm. Compare against the window's observed final to measure forecast accuracy. |

Rotation keeps exactly one `.1` backup; readers wanting full history read
`usage.jsonl.1` then `usage.jsonl`.

The `report` subcommand (`statusline.sh report [--days N]`) is the
reference consumer: it replays this log and ledgers what each closed
window expired unused.

## Consumer rules

1. Read-only. Locks, TTLs, and rotation are the writer's job.
Expand Down
6 changes: 3 additions & 3 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Claude Code Statusline — quota, context, cost. Live, in one bash file.</title>
<meta name="description" content="Live 5h/7d quota with reset times, per-model weekly limits, context pressure, session cost, prompt-cache expiry detection, git activity, and a usage-advisor second row in your Claude Code prompt. One bash file, 321 tests, no daemon, no telemetry.">
<meta name="description" content="Live 5h/7d quota with reset times, per-model weekly limits, context pressure, session cost, prompt-cache expiry detection, git activity, and a usage-advisor second row in your Claude Code prompt. One bash file, 327 tests, no daemon, no telemetry.">
<style>
:root {
--bg: #0b0c0e;
Expand Down Expand Up @@ -130,7 +130,7 @@
<h1>Claude Code Statusline</h1>
<p class="sub">Quota, context, cost, model, and git — live in your Claude Code prompt.
Know when the 5h window resets, how deep the weekly limit runs, and what this session costs, without leaving the terminal.</p>
<p class="facts">one bash file &middot; 321 tests &middot; deps: jq + curl &middot; no daemon &middot; no telemetry</p>
<p class="facts">one bash file &middot; 327 tests &middot; deps: jq + curl &middot; no daemon &middot; no telemetry</p>

<div class="term">
<div class="bar"><i></i><i></i><i></i><span>claude</span></div>
Expand Down Expand Up @@ -183,7 +183,7 @@ <h2>Built like it matters</h2>
<p>Corporate mitm proxy or a local tracer: it builds a combined CA bundle from <code>NODE_EXTRA_CA_CERTS</code> automatically, so fetches survive where plain curl dies.</p></div>
<div class="val"><h3>Documented wire contract</h3>
<p>The <a href="https://github.com/thevibeworks/claude-code-statusline/blob/main/docs/api/oauth-usage.md">observed <code>/api/oauth/usage</code> schema</a> is versioned in-repo and re-synced against CLI captures, so parser changes check against ground truth.</p></div>
<div class="val"><h3>321 tests on one bash file</h3>
<div class="val"><h3>327 tests on one bash file</h3>
<p>bats suite covering quota math, lock contention (20-contender single-winner), forecast pacing, ANSI rendering, and the installer. CI on every push.</p></div>
<div class="val"><h3>Nothing running in the background</h3>
<p>No daemon, no telemetry, no npm. It runs when Claude Code renders the prompt, reads stdin + one cached API call, prints a line, exits.</p></div>
Expand Down
7 changes: 4 additions & 3 deletions llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
> per-model weekly limits), context pressure, session cost, prompt-cache
> health, and git activity in Claude Code's statusLine hook — plus an
> advisor second row that interprets the numbers (cap projections,
> expiring-surplus and underuse advice, fleet relief). 321 bats tests,
> deps are jq + curl, no daemon, no telemetry.
> expiring-surplus and underuse advice, fleet relief) and a
> `report` subcommand that ledgers what each closed window expired
> unused. 327 bats tests, deps are jq + curl, no daemon, no telemetry.

Key facts for agents:
- Everything lives in `statusline.sh` (~3400 lines, plain bash). Install
Expand All @@ -32,7 +33,7 @@ Key facts for agents:

- [statusline.sh](https://github.com/thevibeworks/claude-code-statusline/blob/main/statusline.sh): the entire product
- [install.sh](https://github.com/thevibeworks/claude-code-statusline/blob/main/install.sh): installer (curl | bash entrypoint)
- [t/statusline.bats](https://github.com/thevibeworks/claude-code-statusline/blob/main/t/statusline.bats): 309 statusline tests
- [t/statusline.bats](https://github.com/thevibeworks/claude-code-statusline/blob/main/t/statusline.bats): 315 statusline tests
- [t/install.bats](https://github.com/thevibeworks/claude-code-statusline/blob/main/t/install.bats): 12 installer tests

## Optional
Expand Down
Loading
Loading