A compact band above the prompt with context, 5-hour and weekly usage meters.

Personal Claude Code mods for a calmer, more readable terminal.
| Mod | What it does |
|---|---|
readable-width | Wraps replies and prompts at 80 columns; tables and code blocks that name a language keep the full width; an unlabelled or text block wraps like prose. Your prompts are bold and accent-colored, with a blank line above each message. |
usage-meters | One band above the prompt: ctx, 5h and 7d meters, green under 50%, yellow under 80%, red above; 5h also shows the time left until it resets. |
tool-log | Folds tool calls and their results in the chat into a dim › N tools line and keeps them in a side log grouped by prompt. Toggle it with [ panel ], /panel or a key bound to app:toggleDiffPreSession (ctrl+t in ~/.claude/keybindings.json, see below; this replaces the built-in todo list toggle, which the Todo tab covers); a click on a chat line opens it on that prompt's steps, until the next prompt or ✕ returns it to live. The chat line also sums file changes and failures (› 6 tools · 2 files changed +14 −3 · 1 failed). A turn longer than 30 s ends with a desktop notification. Successful Bash rows expand to show their command, and Edit/Write rows to show their diff. Inside a git repo a [ diff ] button opens the built-in /diff panel. The panel has four tabs: Log, Files (every file changed this session with its diff), Agents (subagents with status, time, tool count and their latest step) and Todo (Claude's plan); [ panel ] reopens it on the tab last in view. The active tab is orange, a tab with something new since you last looked is bright, the rest are dim. Replaces the spinner with a minimal one that names the current step and the plan's progress, e.g. ⠋ 12s · 3/7 · Reading register.tsx or ⠙ 3s · Thinking. |
turn-details | Replaces the Brewed for 34s · done 1:09 PM line with 📋 details ▸, which folds the turn's duration, finish time, tool calls and model open in place. |
From GitHub:
claude plugin marketplace add Tvrtk0/claude-code-mods
claude plugin install readable-width@local-mods
claude plugin install usage-meters@local-mods
claude plugin install tool-log@local-mods
claude plugin install turn-details@local-mods
From a local clone (edits apply after /reload-plugins, no reinstall):
claude plugin marketplace add ~/.claude/mods
claude plugin install readable-width@local-mods
claude plugin install usage-meters@local-mods
claude plugin install tool-log@local-mods
claude plugin install turn-details@local-mods
Installed from a local clone, plugins are read straight from the folder.
claude plugin validate ~/.claude/mods/<mod>
claude plugin test ~/.claude/mods/<mod>
A mod cannot add a key of its own, so the [ panel ] button borrows an engine action that only the /diff panel handles. Bind any key to it:
{
"$schema": "https://www.schemastore.org/claude-code-keybindings.json",
"bindings": [
{ "context": "Global", "bindings": { "ctrl+t": "app:toggleDiffPreSession" } }
]
}
ctrl+t hands the panel the keyboard (opening it if needed) and closes it when it already has it. Inside: h/l or 1–4 switch tabs, j/k move between rows (starting from the newest), enter opens one, d/u scroll half a window, g/b jump to the first or last row, esc returns to the prompt and leaves the panel open. The panel scrolls itself, so its tabs and keys stay at the top; Bottom is b, not G: a button hotkey cannot tell shift+g from g.
hooks/register.tsx 90 lines1import { atom, read, update } from 'claude-code'
2import type { EngineInterface, Register } from 'claude-code'
3
4const snapshot = atom({ plugin: 'usage-meters', key: 'snapshot' } as const, null)
5
6const BAR = 10
7
8const heat = (percent: number) =>
9 percent >= 80 ? 'error' : percent >= 50 ? 'warning' : 'success'
10
11async function refresh($: EngineInterface) {
12 const usage = await $.session.usage()
13 const limit = (kind: string) =>
14 usage.rateLimits.find(r => r.kind === kind)?.percentUsed
15 const resets = usage.rateLimits.find(r => r.kind === 'five_hour')?.resetsAt
16
17 await update($, snapshot, () => ({
18 context: usage.context.percent,
19 fiveHour: limit('five_hour'),
20 fiveHourResetsAt: resets === undefined ? undefined : Date.parse(resets),
21 sevenDay: limit('seven_day'),
22 }))
23}
24
25export const register: Register = on => {
26 on('session.start', async ($, e, next) => {
27 const started = await next(e)
28 await refresh($)
29
30 return started
31 })
32
33 on('turn.complete', async ($, e, next) => {
34 const done = await next(e)
35 await refresh($)
36
37 return done
38 })
39
40 on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
41 const s = await read($, snapshot)
42
43 if (e.props.hasSurvey || s === null) {
44 return next(e)
45 }
46
47 const { Box, Text } = $.ui.resolve(e)
48 const now = await $.clock.now()
49 const left = s.fiveHourResetsAt === undefined ? 0 : s.fiveHourResetsAt - now
50 // Time until the 5-hour window resets, as of this drawing.
51 const resetIn =
52 left > 0 ? `${Math.floor(left / 3_600_000)}h ${Math.floor((left % 3_600_000) / 60_000)}m` : ''
53
54 // One labelled meter: a bar of BAR cells and its percentage, both colored
55 // by how full it is.
56 const meter = (label: string, percent: number | undefined, isFirst = false, note = '') => {
57 if (percent === undefined) return null
58
59 const filled = Math.min(BAR, Math.round((percent / 100) * BAR))
60
61 return (
62 <Text>
63 {!isFirst && <Text dimColor> · </Text>}
64 <Text dimColor>{label} </Text>
65 <Text color={heat(percent)}>{'▰'.repeat(filled)}</Text>
66 <Text dimColor>{'▱'.repeat(BAR - filled)}</Text>
67 <Text bold color={heat(percent)}> {Math.round(percent)}%</Text>
68 {note !== '' && <Text dimColor> {note}</Text>}
69 </Text>
70 )
71 }
72
73 // Other mods' band content (the log toggle) is drawn beside this line.
74 const below = await next(e)
75
76 // A blank line above keeps the band off whatever the transcript ends with.
77 return (
78 <Box marginTop={1}>
79 <Box>
80 {meter('ctx', s.context ?? 0, true)}
81 {meter('5h', s.fiveHour, false, resetIn)}
82 {meter('7d', s.sevenDay)}
83 </Box>
84 <Text> </Text>
85 {below}
86 </Box>
87 )
88 })
89}
90types/index.d.ts 13 lines1export type Snapshot = {
2 context?: number
3 fiveHour?: number
4 fiveHourResetsAt?: number
5 sevenDay?: number
6}
7
8declare module 'claude-code' {
9 interface PluginState {
10 'usage-meters': { snapshot: Snapshot | null }
11 }
12}
13