SLOPSHOPPER

usage-meter

Shows your 5-hour and weekly Claude usage in the terminal's prompt footer

newspinnerstatustimer
v0.2.2MITupdated 2026-10-20ivanpasq/claude-usage-meter
A shopper browsing a rack in a slop shop
README

usage-meter

A Claude Code mod that shows, in the prompt footer, how much of your Claude subscription's 5-hour and weekly windows you have used, and how long until each one resets.

5h ━━━━━━━━ 49% ↻ 12m   7d ━━━━━━━━ 19% ↻ 4d 9h

The bar is green up to 70%, yellow up to 90%, and red beyond that. The time until reset refreshes every minute.

  • Terminal only: the desktop app already shows usage next to the model picker.
  • Updates by itself whenever either percentage rises by at least one point.
  • The figures arrive with the model's responses, so the line appears after the session's first message.
  • Without a Claude subscription (for example, with an API key) those figures are not available and the line does not appear.

Install

At the prompt of a Claude Code session in the terminal:

/plugin install usage-meter --marketplace ivanpasq/claude-usage-meter

From a shell:

claude plugin install usage-meter --marketplace ivanpasq/claude-usage-meter

In the desktop app, where /plugin is not available: click + next to the prompt box, then Plugins, Add marketplace with ivanpasq/claude-usage-meter, then Add plugin.

Update

claude plugin update usage-meter@claude-usage-meter

Uninstall

claude plugin uninstall usage-meter

License

MIT

Source 2 files
hooks/register.tsx 86 lines
1import { atom, read, update } from 'claude-code'
2import type { Color, Register } from 'claude-code'
3
4import type { Limits } from '../types'
5
6const limits = atom({ plugin: 'usage-meter', key: 'limits' } as const, [] as Limits)
7
8const WINDOWS = [
9  { kind: 'five_hour', label: '5h' },
10  { kind: 'seven_day', label: '7d' },
11] as const
12const BAR_WIDTH = 8
13const MINUTE = 60_000
14
15export const tone = (percent: number): Color =>
16  percent >= 90 ? 'error' : percent >= 70 ? 'warning' : 'success'
17
18export const split = (percent: number) => {
19  const filled = Math.round((Math.min(Math.max(percent, 0), 100) / 100) * BAR_WIDTH)
20  return { filled: '━'.repeat(filled), empty: '━'.repeat(BAR_WIDTH - filled) }
21}
22
23export const untilReset = (resetsAt: string | undefined, now: number) => {
24  if (resetsAt === undefined) return undefined
25  const minutes = Math.max(0, Math.round((Date.parse(resetsAt) - now) / MINUTE))
26  const days = Math.floor(minutes / 1440)
27  const hours = Math.floor((minutes % 1440) / 60)
28  if (days > 0) return `${days}d ${hours}h`
29  if (hours > 0) return `${hours}h ${minutes % 60}m`
30  return `${minutes}m`
31}
32
33export const register: Register = on => {
34  on('session.start', async ($, e, next) => {
35    const started = await next(e)
36    $.ui.status(undefined)
37    const usage = await $.session.usage()
38    await update($, limits, () => usage.rateLimits)
39    $.clock.every(MINUTE, () => $.ui.invalidate('ui.render'))
40    return started
41  })
42
43  on('session.measure', async ($, e, next) => {
44    if (e.changed.includes('rateLimits')) await update($, limits, () => e.rateLimits)
45    return next(e)
46  })
47
48  on('ui.render', { component: 'SessionMode' }, async ($, e, next) => {
49    // The desktop app already shows the usage beside the model picker.
50    if (e.surface !== 'terminal') return next(e)
51
52    const current = await read($, limits)
53    const shown = WINDOWS.flatMap(w => {
54      const limit = current.find(l => l.kind === w.kind)
55      return limit === undefined ? [] : [{ ...w, limit }]
56    })
57    if (shown.length === 0) return next(e)
58
59    const now = await $.clock.now()
60    const { Box, Text } = $.ui.resolve(e)
61
62    return (
63      <Box key="usage-meter" flexDirection="row" gap={2}>
64        {shown.map(({ kind, label, limit }) => {
65          const color = tone(limit.percentUsed)
66          const bar = split(limit.percentUsed)
67          const reset = untilReset(limit.resetsAt, now)
68          return (
69            <Text key={kind} wrap="truncate-end">
70              <Text dimColor>{label} </Text>
71              <Text color={color}>{bar.filled}</Text>
72              <Text color="subtle">{bar.empty}</Text>
73              <Text color={color} weight="bold" tabular>
74                {' '}
75                {limit.percentUsed}%
76              </Text>
77              {reset === undefined ? null : <Text dimColor> ↻ {reset}</Text>}
78            </Text>
79          )
80        })}
81        {e.props.modes.length > 0 ? <Text dimColor>{e.props.modes.join(' & ')}</Text> : null}
82      </Box>
83    )
84  })
85}
86
types/index.d.ts 9 lines
1export type Limit = { kind: string; percentUsed: number; resetsAt?: string }
2export type Limits = readonly Limit[]
3
4declare module 'claude-code' {
5  interface PluginState {
6    'usage-meter': { limits: Limits }
7  }
8}
9