Session (5h) and weekly (7d) usage bars under the Claude Code prompt: white, yellow from 75%, red from 90%.

Claude Code mod that keeps your session (5h) and weekly (7d) usage always visible, as thin bars on the hint line under the prompt:
▶▶ auto mode on · (shift+tab to cycle) Session ━━━━━━━─ 18% 3h 19m · Week ━─────── 2% 21h 19m
At the prompt of a Claude Code terminal session:
/plugin install usage-meter --marketplace FranderCT/usage-meter
Answer y to add the marketplace, then pick a scope (user to have it in every session). The mod is active right away, no restart needed.
From a shell:
claude plugin install usage-meter --marketplace FranderCT/usage-meter
In the desktop app /plugin does not run: click + next to the prompt box, then Plugins, Add marketplace with FranderCT/usage-meter, then Add plugin.
Edit hooks/register.tsx:
| What | Where |
|---|---|
| Bar length | BAR_WIDTH |
| Colors and thresholds | colorFor (white, yellow at 75, red at 90) |
| Labels | WINDOWS (Session, Week) |
/plugin uninstall usage-meter
The mod replaces the engine's hint line under the prompt (? for shortcuts, esc to interrupt): it repeats the same text first, but the clickable pills on that line stop being clickable while the mod is active.
hooks/register.tsx 95 lines1import { atom, read, update } from 'claude-code'
2import type { Register } from 'claude-code'
3
4import type { Limit } from '../types'
5
6const limits = atom({ plugin: 'usage-meter', key: 'limits' } as const, [] as Limit[])
7
8const WINDOWS = [
9 { kind: 'five_hour', label: 'Session' },
10 { kind: 'seven_day', label: 'Week' },
11]
12const BAR_WIDTH = 8
13
14const colorFor = (percent: number) => {
15 if (percent >= 90) {
16 return 'red'
17 }
18
19 return percent >= 75 ? 'yellow' : 'white'
20}
21
22const filledCells = (percent: number) =>
23 Math.min(BAR_WIDTH, Math.max(0, Math.round((percent / 100) * BAR_WIDTH)))
24
25const untilReset = (resetsAt: string | undefined, now: number) => {
26 if (resetsAt === undefined) {
27 return ''
28 }
29
30 const minutes = Math.max(0, Math.round((Date.parse(resetsAt) - now) / 60000))
31 const days = Math.floor(minutes / 1440)
32 const hours = Math.floor((minutes % 1440) / 60)
33
34 if (days > 0) {
35 return ` ${days}d ${hours}h`
36 }
37
38 return hours > 0 ? ` ${hours}h ${minutes % 60}m` : ` ${minutes}m`
39}
40
41export const register: Register = on => {
42 on('session.start', async ($, e, next) => {
43 $.ui.status(undefined)
44 const usage = await $.session.usage()
45 await update($, limits, () => usage.rateLimits)
46
47 return next(e)
48 })
49
50 on('session.measure', async ($, e, next) => {
51 if (e.changed.includes('rateLimits')) {
52 await update($, limits, () => e.rateLimits)
53 }
54
55 return next(e)
56 })
57
58 on('ui.render', { component: 'PromptHint' }, async ($, e, next) => {
59 const current = await read($, limits)
60 const rows = WINDOWS.flatMap(w => {
61 const found = current.find(limit => limit.kind === w.kind)
62
63 return found === undefined ? [] : [{ ...w, ...found }]
64 })
65
66 if (rows.length === 0) {
67 return next(e)
68 }
69
70 const now = await $.clock.now()
71 const { Text } = $.ui.resolve(e)
72
73 return (
74 <Text>
75 <Text dimColor>{e.props.hint === '' ? '' : `${e.props.hint} `}</Text>
76 {rows.map((row, index) => {
77 const color = colorFor(row.percentUsed)
78 const filled = filledCells(row.percentUsed)
79
80 return (
81 <Text key={row.kind}>
82 {index > 0 ? ' · ' : ''}
83 <Text color="white">{row.label} </Text>
84 <Text color={color}>{'━'.repeat(filled)}</Text>
85 <Text dimColor>{'─'.repeat(BAR_WIDTH - filled)}</Text>
86 <Text color={color}> {Math.round(row.percentUsed)}%</Text>
87 <Text dimColor>{untilReset(row.resetsAt, now)}</Text>
88 </Text>
89 )
90 })}
91 </Text>
92 )
93 })
94}
95types/index.d.ts 8 lines1export type Limit = { kind: string; percentUsed: number; resetsAt?: string }
2
3declare module 'claude-code' {
4 interface PluginState {
5 'usage-meter': { limits: Limit[] }
6 }
7}
8