Skip to content

feat(statusline): context row options and a session token totals row - #541

Merged
Mearman merged 11 commits into
mainfrom
feat/statusline-context
Oct 20, 2026
Merged

Mearman merged 11 commits into
mainfrom
feat/statusline-context

Conversation

@Mearman

@Mearman Mearman commented Oct 19, 2026

Copy link
Copy Markdown
Member

Gives the built-in statusline's context row claude-hud's context options. A new context settings group sets whether the row draws its bar or only its figures, the figure itself (tokens held over the window and the percentage, either alone, or the percentage left), separate warning and critical thresholds for the context bar, a window in tokens to measure against instead of the model's, what to estimate when the client reports no percentage (nothing, the raw share, or the share with the compaction reserve added), and the token breakdown of the last request once use reaches the critical threshold. A new tokens row sums the session's usage from its transcript.

Defaults draw byte for byte what they did. That shaped three choices that differ from claude-hud's own defaults: context.warnAt and context.criticalAt default to 0, which follows bands.warnAt and bands.criticalAt, so a layer that only sets bands still colours the context bar as before; context.estimate defaults to never, because claude-hud's estimate would draw a bar before the first response of a session where the line has always drawn nothing; and context.breakdown and the tokens row are off until asked for. The schema has no boolean kind and I did not add one here, so the two switches are named choices (shown/hidden, never/critical), and a 0 stands for "not set" in the three integer keys that need it.

claude-hud takes session token totals from its transcript parser, and the payload has no cumulative figures (total_input_tokens is the context window's current load, as the Claude Code statusline reference says). So the tokens row reads the transcript named by transcript_path through a new port. A response is written as several lines that repeat its usage, adjacent among the lines that record usage in every transcript I checked, so only the last response is remembered and each later line adds its growth. The reader is incremental: where the last read ended, and the open response, are kept in a small file per transcript under <home>/statusline-cache/ (written atomically, removed after the usage log's retention when a new session's file is first written), so a refresh reads only the bytes appended since. A transcript that shrinks is read again from the start. I compared the first read of a large transcript against a separate script that sums the same fields and the totals matched. Other groups that need transcript data (activity lines, cost) will want the same reader; I kept it behind StatuslinePorts.sessionTokens so they can fold it into whatever shape they share.

realStatuslinePorts now takes the cache directory, which the command derives from agent-shim's root.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
🔒 Security Review ⚠️ Failed 2026-10-19T21:58:40.753123Z e351a10 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

This security review couldn’t start because the paying account has exhausted its Codex allowance and has insufficient available credits. The payer can check their balance and add credits in the Codex usage dashboard, then try again. If you do not manage the paying account, contact this repository's admins.

A context settings group chooses whether the row draws its bar or only
its figures, the figure (tokens held against the window and the
percentage, either alone, or the percentage left), the warning and
critical thresholds of the context bar, a window to measure the tokens
held against, what to draw when the client reports no percentage, and a
breakdown of the last request once use reaches the critical threshold.

A threshold left at 0 follows the bands, and the estimate defaults to
never, so the default output is unchanged.
A transcript further ahead than one read took in was read from its end,
so every fact folded from it (compaction count, name, start, prompt
cache clock) was missing the part skipped. A refresh now reads one
window from where the cache ends and folds its lines in order, so the
facts are exact once the cache reaches the end of the file and marked
incomplete until then, with the work of each refresh still bounded by
one window.

A line bigger than a window is passed a window at a time without being
folded, which leaves the facts incomplete for good and the session's
start unknown if no timestamp had been seen.
The tokens row sums the usage of every response in the session's
transcript and draws the total with its input, output and cache shares.
It is a row a layout asks for, not one of the defaults.

The payload carries only the context window's current load, so the
totals come from the transcript, through the reader the session rows
share: a new key in its cache beside the session facts, folded from the
same lines. A response is written as several records that repeat its
usage, so only the last response is remembered and each later record
adds its growth. While the reader is catching up with a transcript too
big to read at once the total ends in a plus, as the compaction count
does.
… exact

A reported 0 beside tokens already held is kept when no estimate can be
made, instead of making the row vanish. A window set in the settings
draws nothing until the client reports the tokens held, rather than the
percentage against the client's own window. A tokens figure with no
bar and nothing to show draws nothing instead of a bare label.

The context thresholds and the window are now absent until set, not 0,
so a layer unsets them with --unset-statusline and they follow what
they fall back on. Optional keys are listed as having no default. The
estimate description states exactly when it applies.
…ayer set

An optional statusline setting (the context thresholds and window) may
be written as null in a layer, which removes it while layers merge, so
a deeper layer can return it to unset. profile set takes none for such
a setting, as against --unset-statusline, which only removes it from
that one layer. The layer schema, the key listing, the generated JSON
schemas and the docs state which settings have no default.
@Mearman
Mearman force-pushed the feat/statusline-context branch from e9beb5c to e014cf6 Compare October 20, 2026 11:35
…e catching up

While the reader catches up with a transcript bigger than a window,
the facts that are the newest value of something (the latest response,
the names, the prompt cache's clock) came from the first window and
stayed stale for as many refreshes as the catching up took, with
nothing to say so.

The session state is now two parts: the cumulative facts (start,
compactions), which are exact only once every record is read, and the
latest-state facts, which the end of the transcript settles. While the
cumulative part is behind, the latest-state part is also folded from
the last window of the transcript and carried in the cache, advanced
over what is appended, and its set facts replace the first part's in
what the reader reports. A refresh reads at most two windows.
…defaults behave

The compactions row says what is unread while the reader catches up and
what a skipped line leaves for good, and the statusline paragraph names
the keys that have no default without counting them.
The colour of the figure and the breakdown are checked at a percentage
exactly on the warning and critical thresholds and a hair under them,
and the bar glyph is written as an escape so the source stays ASCII.
…hing up

When the end of a long transcript holds a request but no cache write
that names a tier, the tier the first window showed is kept, as a full
read of the transcript keeps it, instead of falling back to the default
lifetime. The unused helper that listed the end's set facts is removed.
… at once

The latest-state facts are right at once only when the last window of
the transcript holds a record that sets them, and otherwise stay as the
part read so far has them until the reader reaches one. A refresh reads
at most two windows and a byte, beside the check of the first line.
The tier an early cache write showed, a title set only in the middle, a
last window that starts exactly on a line, and a transcript cut to a
length between the cumulative fold and the fold of the end are read as a
full scan would read them.
@Mearman
Mearman merged commit 3af5f56 into main Oct 20, 2026
31 checks passed
@Mearman
Mearman deleted the feat/statusline-context branch October 20, 2026 12:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant