Mod that shows the remaining 5-hour session, 7-day weekly and context window usage as percentages in a row under the prompt, turning yellow when one runs low.

A Claude Code mod that shows your remaining subscription and context usage as its own row under the prompt's hint line:
⏵⏵ auto mode on (shift+tab to cycle)
Claude Usage Remaining: session 94% · week 76% · context 63%
five_hour)seven_day)The row is drawn in the default color, and turns yellow when session or week drops below 10% remaining, or context below 15%. Auto-compact measures against its own limit, which can be smaller than the model's window, so it may fire while context still shows a little left.
The mod uses the function-hooks API (hooks/hooks.json → hooks/register.tsx):
ui.render hook on PromptHint draws the engine's hint line, then a row below it with the rate-limit windows and context fill read from $.session.usage().session.measure, which fires after each turn and whenever a rate-limit window or the context fill moves, it calls $.ui.invalidate('ui.render') to redraw the line.The figures come from the API's response to each turn, so the row appears after the first response of a session; context is also missing right after a compaction, until the next response. Off a subscription (API key, gateway) no rate-limit windows are reported and the row shows context alone.
hooks.json carries an empty "hooks": {} beside "modules" so skillsaw's claude-hooks-valid rule accepts it.
/plugin install usage-status@robbyt-claude-skills
Or load it from a checkout:
claude --plugin-dir plugins/usage-status
make test-plugin PLUGIN=usage-status
Runs claude plugin validate and claude plugin test against the plugin.
hooks/register.tsx 35 lines1import type { Register } from 'claude-code'
2
3import { formatUsage } from './format'
4
5export const register: Register = on => {
6 // Redraw the row whenever a rate-limit window or the context fill moves.
7 on('session.measure', ($, e, next) => {
8 if (e.changed.includes('rateLimits') || e.changed.includes('context')) {
9 $.ui.invalidate('ui.render')
10 }
11
12 return next(e)
13 })
14
15 // Draw the usage as its own row under the engine's hint line.
16 on('ui.render', { component: 'PromptHint' }, async ($, e, next) => {
17 const hint = await next(e)
18 const { rateLimits, context } = await $.session.usage()
19 const usage = formatUsage(rateLimits, context)
20
21 if (!usage) {
22 return hint
23 }
24
25 const { Box, Text } = $.ui.resolve(e)
26
27 return (
28 <Box flexDirection="column">
29 {hint}
30 <Text color={usage.isLow ? 'warning' : undefined}>{usage.text}</Text>
31 </Box>
32 )
33 })
34}
35hooks/format.ts 46 lines1import type { SessionContextUsage, SessionRateLimit } from 'claude-code'
2
3const LABELS: Record<string, string> = {
4 five_hour: 'session',
5 seven_day: 'week',
6}
7
8// Below this much remaining, the line turns yellow: a rate-limit window, or
9// the context window (higher, since auto-compact fires before it is full).
10export const LOW_LIMIT_PERCENT = 10
11export const LOW_CONTEXT_PERCENT = 15
12
13export type UsageLine = { text: string; isLow: boolean }
14
15type Remaining = { label: string; percent: number; low: number }
16
17const left = (percentUsed: number) =>
18 Math.max(0, Math.round(100 - percentUsed))
19
20// { text: "Claude Usage Remaining: session 77% · week 58% · context 63%", isLow },
21// or undefined when nothing has a reading yet.
22export const formatUsage = (
23 limits: readonly SessionRateLimit[],
24 context?: SessionContextUsage,
25): UsageLine | undefined => {
26 const parts: Remaining[] = Object.entries(LABELS).flatMap(([kind, label]) => {
27 const limit = limits.find(l => l.kind === kind)
28 return limit
29 ? [{ label, percent: left(limit.percentUsed), low: LOW_LIMIT_PERCENT }]
30 : []
31 })
32
33 if (context?.percent !== undefined) {
34 parts.push({ label: 'context', percent: left(context.percent), low: LOW_CONTEXT_PERCENT })
35 }
36
37 if (parts.length === 0) {
38 return undefined
39 }
40
41 return {
42 text: `Claude Usage Remaining: ${parts.map(p => `${p.label} ${p.percent}%`).join(' · ')}`,
43 isLow: parts.some(p => p.percent < p.low),
44 }
45}
46