Skip to content

feat(statusline): draw the session from its transcript, as claude-hud does - #540

Merged
Mearman merged 4 commits into
mainfrom
feat/statusline-session
Oct 20, 2026
Merged

Mearman merged 4 commits into
mainfrom
feat/statusline-session

Conversation

@Mearman

@Mearman Mearman commented Oct 19, 2026

Copy link
Copy Markdown
Member

Adds the session group of claude-hud's statusline to the built-in renderer: the segments sessionName, duration and speed and the rows sessionTime, promptCache and compactions, all drawn from the session transcript the client names in transcript_path. None of them is in the default layout, so default output is unchanged, and a statusline that lists none of them never opens the transcript.

The transcript is read by a new transcript/ module that later readers (tools, agents, todos) can fold into beside the session facts. The statusline is a new process per refresh and transcripts reach many megabytes, so the reader keeps its folded state and the offset it has read to in <home>/statusline-cache/, reads only the bytes appended since, and bounds every read by bytes: a transcript never read, or grown by more than one read takes in, is read from its end, with the start time taken from its first lines. When that skips the earlier part the result says so, and the compactions count shows a trailing plus rather than claiming a figure it cannot back. That needed ports for a bounded range read, an atomic write and pruning (so the cache forgets sessions long over), and a cache directory in the layout paths.

Speed needs a sample from the previous refresh, so it is kept in the same cache, one file per transcript, as claude-hud does. The measure is the client's output token count for the latest request, falling back to an estimate from the transcript's growth for providers that fill in no usage. The prompt cache row follows claude-hud's anchoring (the cache's life starts with the request, not the response, and subagents are left out) and shows the expiry as a clock time, since a countdown would freeze between turns. The clock time is always 24-hour in the machine's time zone, so the usage group's hourCycle and showClockSeconds do not reach it yet; the parity note says so.

display.showSessionStartDate and display.showLastResponseAt share one row in claude-hud, so they map to sessionTime.parts rather than to row membership.

@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:53:42.441227Z 60750bf 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.

@Mearman
Mearman force-pushed the feat/statusline-session branch from 7381039 to 52a10d5 Compare October 20, 2026 10:47
…lements

Session facts (title, start, last response, compactions, the prompt cache's clock and
tier) are folded from the transcript Claude Code writes, which grows to many megabytes
while the statusline refreshes often. The reader keeps the folded state and the offset it
has read to in the statusline's state directory, reads only what was appended since, and
bounds every read by bytes: a transcript never read, or grown by more than a read takes
in, is read from its end, with its start time taken from its first lines when they hold
one. A digest of the transcript's first line tells a replaced file, and a line longer than
a read is passed so later refreshes read only what is new. The result says whether the
whole transcript was read, so a count over the session can say it may fall short. Cache
files not written to for long are removed when a transcript's cache is first written.

Elements reach the transcript through the render context's transcript() accessor, which
reads on the first call and shares the result, so a statusline that lists no element that
needs it never touches the file. The facts sit under a session key so a reader of other
facts can fold the same lines beside it. A session name is reduced to the text a person
would see, and a name that is empty falls through to the next.
… does

Header segments sessionName, duration and speed, and rows sessionTime, promptCache and
compactions, none of them in the default layout. The name is the title /rename gave the
session, else the generated one; the duration counts from the transcript's first
timestamp; sessionTime.parts chooses the start and the time since the last response.

The prompt cache row shows the clock time the cache expires and not a countdown, because
the statusline only refreshes while Claude Code works and a countdown would freeze while
the cache drains. It is counted from the request that last refreshed the cache, in the
tier the transcript's cache writes show, and written under the clock settings;
promptCache.ttlSeconds is the lifetime until a write has shown one.

Speed is measured between this refresh and the previous one, whose sample is kept in the
statusline's state directory, from the client's output token count, or the transcript's
growth when the client reports none. The parity map accounts for the session keys.
…written first

The directory scan that removes the cache files of sessions that are over ran only when a
transcript's reading cache was first created, so when the only session element listed was
speed, its sample files were never pruned. Every kind of cache file now goes through one
save, which scans when a new file first appears. The docs and the state directory's
comment name both kinds of file.
The start time was written by a formatter of its own, so it ignored clock.hourCycle and
clock.seconds that every other wall-clock time follows. It now goes through the shared
wall-clock formatter, with its day first when it was not today. The display glyphs are
written as escapes so the source stays ASCII. Tests start a session in the hour after
midnight under h23 and h24 and pin the boundary at which the prompt cache is called
running out.
@Mearman
Mearman force-pushed the feat/statusline-session branch from 52a10d5 to 713e346 Compare October 20, 2026 11:01
@Mearman
Mearman merged commit df550b8 into main Oct 20, 2026
31 checks passed
@Mearman
Mearman deleted the feat/statusline-session branch October 20, 2026 11:08
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