Your status line (model, session/weekly usage, context bar) above the prompt

Claude Code mods, as a plugin marketplace.
Use in a repo (cloud sessions included) by adding to its .claude/settings.json:
{
"extraKnownMarketplaces": {
"whaleson-mods": { "source": { "source": "github", "repo": "whalewhalewhaleson/claude-mods" } }
},
"enabledPlugins": { "context-bar@whaleson-mods": true }
}hooks/register.tsx 133 lines1import { atom, read, update } from 'claude-code'
2import type { EngineInterface, Register } from 'claude-code'
3
4import type { Snapshot } from '../types'
5
6const snap = atom({ plugin: 'context-bar', key: 'snap' } as const, null)
7const compactions = atom({ plugin: 'context-bar', key: 'compactions' } as const, 0)
8
9const WIDTH = 24
10
11const C = {
12 model: '#c4a7e7',
13 label: '#908caa',
14 value: '#e0def4',
15 ok: '#9ccfd8',
16 warn: '#f6c177',
17 hot: '#eb6f92',
18 empty: '#403d52',
19}
20
21export const shade = (percent: number) =>
22 percent >= 80 ? C.hot : percent >= 50 ? C.warn : C.ok
23
24export const k = (n: number) => (n >= 1000 ? `${Math.round(n / 1000)}k` : `${n}`)
25
26export const countdown = (resetsAt: string, now: number) => {
27 const mins = Math.max(0, Math.round((Date.parse(resetsAt) - now) / 60000))
28 const h = Math.floor(mins / 60)
29 return h > 0 ? `${h}h ${mins % 60}m` : `${mins}m`
30}
31
32async function refresh($: EngineInterface) {
33 const usage = await $.session.usage()
34 const limit = (kind: string) => usage.rateLimits.find(r => r.kind === kind)
35 const five = limit('five_hour')
36 const week = limit('seven_day')
37 const now: Snapshot = {
38 model: await $.session.model(),
39 compactions: await read($, compactions),
40 tokens: usage.context.tokens,
41 window: usage.context.window,
42 percent: usage.context.percent,
43 session: five?.percentUsed,
44 weekly: week?.percentUsed,
45 resetsAt: five?.resetsAt,
46 usd: usage.cost?.usd,
47 }
48 await update($, snap, () => now)
49}
50
51export const register: Register = on => {
52 on('session.start', async ($, e, next) => {
53 void refresh($).catch(() => {})
54 return next(e)
55 })
56
57 on('session.compact', async ($, e, next) => {
58 const done = await next(e)
59 await update($, compactions, n => n + 1)
60 void refresh($).catch(() => {})
61 return done
62 })
63
64 on('tool.call', async ($, e, next) => {
65 const ran = await next(e)
66 void refresh($).catch(() => {})
67 return ran
68 })
69
70 on('turn.complete', async ($, e, next) => {
71 void refresh($).catch(() => {})
72 return next(e)
73 })
74
75 on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
76 if (e.props.hasSurvey) return next(e)
77 const s = await read($, snap)
78 if (s === null) return next(e)
79
80 const { Box, Text } = $.ui.resolve(e)
81 const sep = <Text color={C.empty}> · </Text>
82 const clock = await $.clock.now()
83 const stat = (label: string, n?: number) => (
84 <Box>
85 <Text color={C.label}>{label} </Text>
86 {n === undefined ? (
87 <Text color={C.empty}>–</Text>
88 ) : (
89 <Text color={shade(n)}>{Math.round(n)}%</Text>
90 )}
91 </Box>
92 )
93 const full = s.percent === undefined ? 0 : Math.round((s.percent / 100) * WIDTH)
94
95 return (
96 <Box flexDirection="column">
97 <Box>
98 <Text color={C.model} bold>✦ {s.model}</Text>
99 {sep}
100 <Text color={C.label}>compactions </Text>
101 <Text color={C.value}>{s.compactions}</Text>
102 {s.usd !== undefined && sep}
103 {s.usd !== undefined && <Text color={C.value}>${s.usd.toFixed(2)}</Text>}
104 </Box>
105 <Box>
106 {stat('session', s.session)}
107 {sep}
108 {stat('weekly', s.weekly)}
109 {s.resetsAt !== undefined && sep}
110 {s.resetsAt !== undefined && <Text color={C.label}>resets in </Text>}
111 {s.resetsAt !== undefined && <Text color={C.value}>{countdown(s.resetsAt, clock)}</Text>}
112 </Box>
113 <Box>
114 <Text color={C.label}>context </Text>
115 {s.percent === undefined ? (
116 <Box>
117 <Text color={C.empty}>{'━'.repeat(WIDTH)}</Text>
118 <Text color={C.label} italic> waiting for first reply…</Text>
119 </Box>
120 ) : (
121 <Box>
122 <Text color={shade(s.percent)}>{'━'.repeat(full)}</Text>
123 <Text color={C.empty}>{'━'.repeat(WIDTH - full)}</Text>
124 <Text color={shade(s.percent)} bold> {s.percent}%</Text>
125 <Text color={C.label}> {k(s.tokens ?? 0)} / {k(s.window)}</Text>
126 </Box>
127 )}
128 </Box>
129 </Box>
130 )
131 })
132}
133types/index.d.ts 18 lines1export type Snapshot = {
2 model: string
3 compactions: number
4 tokens?: number
5 window: number
6 percent?: number
7 session?: number
8 weekly?: number
9 resetsAt?: string
10 usd?: number
11}
12
13declare module 'claude-code' {
14 interface PluginState {
15 'context-bar': { snap: Snapshot | null; compactions: number }
16 }
17}
18