A band above the prompt showing your 5-hour and weekly rate-limit usage, with warnings as you approach them.

Four Claude Code mods (function-hook plugins) that save tokens and keep you aware of your limits.
| Mod | What it does |
|---|---|
smart-model-router | Picks a heavy or light model for each turn |
usage-bar | Shows 5-hour and weekly rate-limit usage above the prompt |
context-bar | Shows how full the context window is, by category, above the prompt |
research-offloader | Keeps research work out of the main context |
Kaizen is continuous improvement through small changes, each removing one source of waste (muda). These mods apply it to a Claude Code workflow:
| Waste | Mod that removes it |
|---|---|
| The most expensive model doing routine work | smart-model-router |
| Raw web pages filling the main context | research-offloader |
| Hitting a rate limit by surprise | usage-bar (and the router's usage guard) |
| Context filling up unnoticed | context-bar |
None of these is a big idea; each is one small fix. Run the cycle on your own usage:
usage-bar.usageGuard, the model options and the research trigger phrases, then repeat.What you can see, you can improve.
claude --plugin-dir D:\ai\cc-mods\smart-model-router --plugin-dir D:\ai\cc-mods\usage-bar --plugin-dir D:\ai\cc-mods\context-bar --plugin-dir D:\ai\cc-mods\research-offloader
Saving a file reloads the mod in a running session.
/plugin install smart-model-router --marketplace timothylok/ClaudeCodeModsByTimLok
/plugin install usage-bar --marketplace timothylok/ClaudeCodeModsByTimLok
/plugin install context-bar --marketplace timothylok/ClaudeCodeModsByTimLok
/plugin install research-offloader --marketplace timothylok/ClaudeCodeModsByTimLok
Answer y to add the marketplace, then choose a scope. Each mod is active immediately.
Mods with options show them as rows in /config, or you can set them in settings.json under pluginConfigs.<mod>.options. Changing one reloads the mod.
Chooses a model for every turn and sends the main thread's requests to it. Subagents keep their own model.
How a model is chosen, in order:
heavy or light, use that model.auto mode, a prompt that mentions architecture, designing a system, multi-file work, rewriting the whole thing, deep reasoning, trade-offs, migrations, root cause, or planning, or one longer than 2,500 characters, gets the heavy model. Anything else gets the light model.The status line shows the choice and the reason, e.g. router: claude-opus-5-5 (complex prompt).
| Command | Effect |
|---|---|
/route | Show the current mode |
/route auto | Pick per prompt (default) |
/route heavy | Always use the heavy model |
/route light | Always use the light model |
/route off | Router does nothing; the session's own model is used |
The mode is remembered across sessions.
| Option | Default | Meaning |
|---|---|---|
heavyModel | claude-opus-5-5 | Model for complex turns |
lightModel | claude-sonnet-5-5 | Model for everyday turns |
usageGuard | 85 | Usage % above which the light model is always used |
| Prompt | Model |
|---|---|
Design the architecture for a multi-tenant billing service | heavy |
Plan out a migration from REST to gRPC, with the trade-offs | heavy |
Find the root cause of this flaky test | heavy |
fix the typo in README | light |
write a unit test for parseDate | light |
Switching models starts a new prompt cache, so flipping often costs extra input tokens. Use /route heavy or /route light to pin a model for a stretch of work.
A band above the prompt with a meter for each rate-limit window Claude Code reports:
Usage 5h █████░░░░░ 42% 2h30m week █████████░ 91% 3d
| Command | Effect |
|---|---|
/usage-bar | Hide or show the band (toggle) |
None.
A stacked bar above the prompt showing the context window, one colour per /context category, with a legend of the biggest categories:
████████▒▒▒▒░░░░░░░░░░░░░░░░░░░░ 38% · 76k/200k
■ Messages 41k ■ System tools 18k ■ Memory files 6k
█ is used space, ▒ is the autocompact buffer, ░ is free space.usage-bar and stacks with it. It is hidden while a survey is showing.| Command | Effect |
|---|---|
/context-bar | Hide or show the bar (toggle) |
None.
When you type a research-style prompt, the mod keeps the page fetching and reading out of the main thread, so only a summary enters your context.
What counts as research: prompts containing "research", "look up", "summarize this url/page/article/docs", "compare sources", "read this page/article/docs", "what is the latest", or "search the web". It only reacts to prompts you type yourself.
Two modes, depending on the endpoint option:
{ "query": "<your prompt>" } to it, expects { "summary": "..." } back, and gives Claude that summary as context. If the endpoint fails or returns no summary, it falls back to the subagent mode below.subagentModel.A toast tells you which mode was used.
| Option | Default | Meaning |
|---|---|---|
endpoint | empty | URL of your own research bridge (for example a NotebookLM wrapper). Leave empty to use subagents |
subagentModel | haiku | Model for research subagents: haiku, sonnet or opus |
| Prompt | Result |
|---|---|
Research the best Rust ORMs in 2026 | offloaded |
Can you look up how Vite handles HMR? | offloaded |
summarize this page for me: https://example.com | offloaded |
compare sources on WebGPU support | offloaded |
fix the failing test in auth.ts | untouched |
NotebookLM has no public API, so the endpoint is yours to provide. Any service that takes {query} and returns {summary} works.
usage-bar hid other bands above the promptusage-bar installed, a separate context bar disappeared.ui.render on AbovePrompt). The outermost hook returned its own tree without calling next(e), so the hook beneath it never ran.usage-bar now calls next(e) and stacks what comes back under its own row. context-bar does the same from 0.1.4, so the two show together in either order.ui.render hook on a shared site must await next(e) and include the result in its tree. In tests, add a stand-in ui.render hook beneath the mod so next has something to return.Each mod has hooks/register.ts(x), a manifest in .claude-plugin/plugin.json, and tests in tests/.
claude plugin validate <mod folder>
claude plugin test <mod folder>
hooks/register.tsx 91 lines1import { atom, read, update } from 'claude-code'
2import type { Register } from 'claude-code'
3
4import type { UsageWindow } from '../types'
5
6const windows = atom({ plugin: 'usage-bar', key: 'windows' } as const, [])
7const isHidden = atom({ plugin: 'usage-bar', key: 'isHidden' } as const, false)
8
9const LABELS: Record<string, string> = { five_hour: '5h', seven_day: 'week', spend_limit: 'spend' }
10const WARN_AT = [80, 95]
11
12export const meter = (pct: number, width = 10): string => {
13 const filled = Math.max(0, Math.min(width, Math.round((pct / 100) * width)))
14
15 return '█'.repeat(filled) + '░'.repeat(width - filled)
16}
17
18export const colorFor = (pct: number) => (pct >= 90 ? 'error' : pct >= 70 ? 'warning' : 'success')
19
20const resetsIn = (iso: string | undefined, now: number): string => {
21 if (iso === undefined) return ''
22 const mins = Math.max(0, Math.round((Date.parse(iso) - now) / 60000))
23
24 return mins >= 60 * 24 ? ` ${Math.round(mins / 60 / 24)}d` : mins >= 60 ? ` ${Math.floor(mins / 60)}h${mins % 60}m` : ` ${mins}m`
25}
26
27export const register: Register = on => {
28 // Highest warning threshold already toasted per window, so each fires once.
29 const warned: Record<string, number> = {}
30
31 on('session.start', async ($, e, next) => {
32 await $.command.register({ name: 'usage-bar', description: 'Show or hide the usage band' })
33 const { rateLimits } = await $.session.usage()
34 await update($, windows, () => rateLimits)
35
36 return next(e)
37 })
38
39 on('command.run', { command: 'usage-bar' }, async $ => {
40 const hidden = await update($, isHidden, v => !v)
41
42 return { text: hidden ? 'Usage band hidden.' : 'Usage band shown.' }
43 })
44
45 on('session.measure', async ($, e, next) => {
46 if (e.changed.includes('rateLimits')) {
47 await update($, windows, () => e.rateLimits)
48 for (const w of e.rateLimits) {
49 const crossed = WARN_AT.filter(t => w.percentUsed >= t).pop()
50 if (crossed !== undefined && (warned[w.kind] ?? 0) < crossed) {
51 warned[w.kind] = crossed
52 $.ui.toast(`Claude ${LABELS[w.kind] ?? w.kind} usage at ${w.percentUsed}%`)
53 }
54 }
55 }
56
57 return next(e)
58 })
59
60 on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
61 const list: UsageWindow[] = await read($, windows)
62 if (e.props.hasSurvey || list.length === 0 || (await read($, isHidden))) {
63 return next(e)
64 }
65
66 const { Box, Text } = $.ui.resolve(e)
67 const now = await $.clock.now()
68 // Other mods (e.g. context-bar) draw in this band too; answering without
69 // next() would replace them, so stack whatever sits beneath us.
70 const below = await next(e)
71
72 return (
73 <Box flexDirection="column">
74 <Box>
75 <Text dimColor>Usage </Text>
76 {list.map(w => (
77 <Text key={w.kind}>
78 <Text dimColor>{LABELS[w.kind] ?? w.kind} </Text>
79 <Text color={colorFor(w.percentUsed)}>
80 {meter(w.percentUsed)} {w.percentUsed}%
81 </Text>
82 <Text dimColor>{resetsIn(w.resetsAt, now)} </Text>
83 </Text>
84 ))}
85 </Box>
86 {below}
87 </Box>
88 )
89 })
90}
91types/index.d.ts 8 lines1export type UsageWindow = { kind: string; percentUsed: number; resetsAt?: string }
2
3declare module 'claude-code' {
4 interface PluginState {
5 'usage-bar': { windows: UsageWindow[]; isHidden: boolean }
6 }
7}
8