Shows the context window fill and the 5-hour and weekly usage limits, as a status line under the prompt.

A Claude Code mod that keeps a status line under the prompt with how full the context window is and how much of your usage limits you have used.
Context █████░░░░░ 45% · 90k/200k · 55% left │ 5h 34% (resets 2h10m) · Week 82.5% ! (resets 3d4h)
| Part | Meaning |
|---|---|
Context … 45% | Tokens the last reply was answered over, against the model's context window. (compact soon) shows at 80 % or more. |
5h 34% | Use of the 5-hour usage window. |
Week 82.5% | Use of the 7-day usage window. ! shows at 80 % or more. |
Spend … | A gateway spend limit, when your account has one. |
(resets …) | Time left until that window starts again. |
The line updates after each turn, and when a usage window moves by a whole percent. The usage windows show only on a Claude subscription (Pro or Max), after the first reply of a session; with an API key only the context part shows. The mod sends nothing anywhere: it reads the same figures as Claude Code's own status line.
At the prompt of a Claude Code terminal session:
/plugin install context-status --marketplace anchorsprint/claude-mods
From a shell:
claude plugin install context-status --marketplace anchorsprint/claude-mods
claude plugin validate plugins/context-status
claude plugin test plugins/context-status
claude --plugin-dir plugins/context-statushooks/register.ts 60 lines1import type { Register, SessionContextUsage, SessionRateLimit } from 'claude-code'
2
3const BAR_CELLS = 10
4const LIMIT_LABELS: Record<string, string> = { five_hour: '5h', seven_day: 'Week', spend_limit: 'Spend' }
5
6const compact = (n: number) =>
7 n >= 1_000_000 ? `${(n / 1_000_000).toFixed(1)}M` : `${Math.round(n / 1000)}k`
8
9const bar = (percent: number) => {
10 const filled = Math.max(0, Math.min(BAR_CELLS, Math.round((percent / 100) * BAR_CELLS)))
11 return '█'.repeat(filled) + '░'.repeat(BAR_CELLS - filled)
12}
13
14export const resetsIn = (resetsAt: string | undefined, now: number) => {
15 const at = resetsAt === undefined ? NaN : Date.parse(resetsAt)
16 if (Number.isNaN(at)) return ''
17 const minutes = Math.max(0, Math.round((at - now) / 60_000))
18 const days = Math.floor(minutes / 1440)
19 const hours = Math.floor((minutes % 1440) / 60)
20 const left = days > 0 ? `${days}d${hours}h` : hours > 0 ? `${hours}h${minutes % 60}m` : `${minutes}m`
21 return ` (resets ${left})`
22}
23
24export const contextText = (context: SessionContextUsage) => {
25 const window = compact(context.window)
26 if (context.tokens === undefined || context.percent === undefined) {
27 return `Context: no reading yet (${window} window)`
28 }
29 const left = Math.max(0, 100 - context.percent)
30 const warn = context.percent >= 80 ? ' (compact soon)' : ''
31 return `Context ${bar(context.percent)} ${context.percent}% · ${compact(context.tokens)}/${window} · ${left}% left${warn}`
32}
33
34export const limitText = (limit: SessionRateLimit, now: number) => {
35 const label = LIMIT_LABELS[limit.kind] ?? limit.kind
36 const warn = limit.percentUsed >= 80 ? ' !' : ''
37 return `${label} ${limit.percentUsed}%${warn}${resetsIn(limit.resetsAt, now)}`
38}
39
40export const statusText = (context: SessionContextUsage, rateLimits: readonly SessionRateLimit[], now: number) =>
41 [contextText(context), rateLimits.map(limit => limitText(limit, now)).join(' · ')]
42 .filter(part => part !== '')
43 .join(' │ ')
44
45export const register: Register = on => {
46 on('session.start', async ($, e, next) => {
47 const started = await next(e)
48 const { context, rateLimits } = await $.session.usage()
49 $.ui.status(statusText(context, rateLimits, await $.clock.now()))
50 return started
51 })
52
53 on('session.measure', async ($, e, next) => {
54 if (e.changed.includes('context') || e.changed.includes('rateLimits')) {
55 $.ui.status(statusText(e.context, e.rateLimits, await $.clock.now()))
56 }
57 return next(e)
58 })
59}
60