Status line with context fill and rate-limit use: ctx 42% 84k/200k · 5h 63% · 7d 12%.

A status line under the prompt: ctx 42% 84k/200k · 5h 63% · 7d 12%.
Why: to see how full the context is and how much of the 5-hour and weekly rate limits is gone before starting an expensive run.
ctx is the last response's input tokens over the model's window.$.session.usage() and appear only on a subscription after the first API response (spend for a gateway spend limit).Limits: no figures until the first response (ctx -- --/200k); session cost is not shown.
hooks/register.ts 41 lines1import type { EngineInterface, Register, SessionUsage } from 'claude-code'
2
3const WINDOW_LABELS: Record<string, string> = { five_hour: '5h', seven_day: '7d', spend_limit: 'spend' }
4
5export function tokens(n: number): string {
6 if (n >= 1_000_000) {
7 return `${Number((n / 1_000_000).toFixed(1))}M`
8 }
9 return n >= 1000 ? `${Math.round(n / 1000)}k` : String(n)
10}
11
12// Every percentage is "used", so the context and rate-limit figures read the same way.
13export function usageLine(u: SessionUsage): string {
14 const { percent, tokens: used, window } = u.context
15 const ctx = `ctx ${percent === undefined ? '--' : `${percent}%`} ${used === undefined ? '--' : tokens(used)}/${tokens(window)}`
16 const limits = u.rateLimits.map(r => `${WINDOW_LABELS[r.kind] ?? r.kind} ${Math.round(r.percentUsed)}%`)
17 return [ctx, ...limits].join(' · ')
18}
19
20async function refresh($: EngineInterface): Promise<void> {
21 try {
22 $.ui.status(usageLine(await $.session.usage()))
23 } catch (error) {
24 $.ui.log(`usage-meter: ${String(error)}`, { to: 'debug' })
25 }
26}
27
28export const register: Register = on => {
29 on('session.start', async ($, e, next) => {
30 const started = await next(e)
31 await refresh($)
32 return started
33 })
34
35 on('turn.complete', async ($, e, next) => {
36 const done = await next(e)
37 await refresh($)
38 return done
39 })
40}
41