Minimal band: model, context tokens and estimated session cost, coloured by context fill

A curated collection of lightweight, high-impact plugins for Claude Code designed to give you real-time visibility into session consumption and automatically optimize your token usage and API costs.
| Plugin | Description | Key Highlight |
|---|---|---|
usage-meter | Real-time prompt header displaying model, context window capacity, and session cost. | Color-coded context fill alerts & instant post-compaction estimates. |
model-router | Autonomous model routing for spawned sub-agents based on task complexity. | Keeps the main thread on your preferred model while routing sub-tasks to the cheapest capable tier. |
usage-meterAdds an always-visible, minimal status bar positioned directly above the input prompt (AbovePrompt).
opus-5-5 · 84k/1.0M (42%) · $1.23
< 40%): Safe zone; ample room for extended context.40% - 69%): Moderate usage; considerations for upcoming compaction.≥ 70%): Heavy fill; approaching window limits.~31k/200k (~16%)) right after /compact without waiting for the next turn measurement./model is changed.model-routerOptimizes costs and execution speed by dynamically routing sub-agents to the most cost-effective Claude model tier (opus, sonnet, or haiku).
/model you configure. This prevents cache thrashing and ensures high-level problem understanding stays consistent.haiku: Fast, lightweight lookups, file/symbol searches, listing usages, reading config files, summarizing command output, formatting, and one-line changes.sonnet: Standard feature work, bug fixes with clear causes, writing unit tests, code reviews, and routine refactorings.opus: Open-ended architecture, multi-file refactors, subtle concurrency/performance debugging, security-critical changes, and complex multi-step planning./route)Manage sub-agent routing behaviour in Claude Code using the /route command:
# Set routing mode
/route auto # Haiku intelligently picks the model per sub-agent (default)
/route off # Disables routing; sub-agents inherit the main thread model
/route sonnet # Force all sub-agents to a specific tier (opus | sonnet | haiku)
# Check status & telemetry
/route status # Displays the current mode and quick help
/route stats # Shows spawn counts, token breakdowns, and recent decisions
/route reset-stats # Clears historical routing statistics
Example stats output:
Sub-agent routing stats (all sessions)
opus 3 spawns in 12.4k out 1.8k cache read 48.2k cache write 6.1k
sonnet 18 spawns in 64.1k out 14.2k cache read 180.5k cache write 12.0k
haiku 42 spawns in 89.3k out 8.7k cache read 310.0k cache write 4.2k
Latest decisions (model, why, agent):
haiku router general-purpose: Find call sites of parseConfig
sonnet router general-purpose: Add unit tests for auth middleware
This repository is organized as a Claude Code plugin marketplace (mike-mods) defined in .claude-plugin/marketplace.json.
claude-mods/
├── .claude-plugin/
│ └── marketplace.json # Marketplace manifest declaring both plugins
├── model-router/
│ ├── hooks/
│ │ ├── hooks.json # Hook entrypoints (register.ts)
│ │ ├── register.ts # Router implementation & command handling
│ │ └── router.test.ts # Comprehensive test suite
│ └── types/
│ └── index.d.ts # TypeScript declarations & state augmentations
└── usage-meter/
├── hooks/
│ ├── hooks.json # Hook entrypoints (register.tsx)
│ ├── register.tsx # UI hook & event subscriptions
│ ├── format.ts # Formatting & color thresholds
│ └── format.test.ts # Unit tests
└── types/
└── index.d.ts # TypeScript declarations
You can enable these plugins in your Claude Code environment either via marketplace or by pointing directly to the plugin directories.
Add this repository directory as a plugin source in your Claude Code configuration:
# Add this directory as a plugin marketplace or install individual plugins
claude plugin add ./model-router
claude plugin add ./usage-meter
To enable these plugins automatically for a specific project, you can reference them in your workspace's .claude/config.json or plugin manifest:
{
"plugins": [
"/path/to/claude-mods/model-router",
"/path/to/claude-mods/usage-meter"
]
}
Both plugins include test suites using the claude-code/testing framework:
model-router/hooks/router.test.ts: Verifies routing logic, explicit model overrides, fork handling, toast triggering, mode toggling, and stats accumulation.usage-meter/hooks/format.test.ts: Verifies context string formatting, post-compaction estimate indicators, cost formatting, and color threshold calculations. and cost icon )hooks/register.tsx 87 lines1import { atom, read, update } from "claude-code";
2import type { EngineInterface, Register, SessionUsage } from "claude-code";
3
4import { colour, meter } from "./format";
5
6const current = atom({ plugin: "usage-meter", key: "meter" } as const, null);
7
8// The size after a compaction, shown until the next response measures the real one.
9let estimate: number | undefined;
10
11const refresh = async (
12 $: EngineInterface,
13 usage?: Pick<SessionUsage, "context" | "cost">,
14) => {
15 const u = usage ?? (await $.session.usage());
16 const context =
17 estimate === undefined
18 ? u.context
19 : {
20 ...u.context,
21 tokens: estimate,
22 percent: Math.round((estimate / u.context.window) * 100),
23 };
24 const next = meter(
25 await $.session.model(),
26 { ...u, context },
27 estimate !== undefined,
28 );
29 await update($, current, () => next);
30};
31
32export const register: Register = (on) => {
33 on("session.start", async ($, e, next) => {
34 const result = await next(e);
35 estimate = undefined;
36 $.ui.status(undefined);
37 await refresh($);
38 return result;
39 });
40
41 on("session.measure", async ($, e, next) => {
42 estimate = undefined;
43 await refresh($, e);
44 return next(e);
45 });
46
47 // /compact sends no response, so the last measured size is stale until the next one.
48 on("session.compact", async ($, e, next) => {
49 const result = await next(e);
50 if (
51 e.trigger === "precompute" ||
52 result.skip !== undefined ||
53 result.tokensAfter === undefined
54 )
55 return result;
56 estimate = result.tokensAfter;
57 try {
58 await refresh($);
59 } catch {
60 $.ui.log("usage-meter: could not refresh after compaction", {
61 to: "debug",
62 });
63 }
64 return result;
65 }).catch(($, e, next) => next(e));
66
67 // A /model switch shows on the next prompt, before any response arrives.
68 on("prompt.submit", async ($, e, next) => {
69 await refresh($);
70 return next(e);
71 }).catch(($, e, next) => next(e));
72
73 on("ui.render", { component: "AbovePrompt" }, async ($, e, next) => {
74 const m = await read($, current);
75 if (e.props.hasSurvey || m === null) return next(e);
76
77 const { Text } = $.ui.resolve(e);
78 const c = colour(m.percent);
79
80 return c ? (
81 <Text color={c}>{m.text}</Text>
82 ) : (
83 <Text dimColor>{m.text}</Text>
84 );
85 });
86};
87hooks/format.ts 48 lines1import type { SessionUsage, ThemeKey } from "claude-code";
2
3import type { Meter } from "../types";
4
5const compact = (n: number) =>
6 n >= 1_000_000
7 ? `${(n / 1_000_000).toFixed(1)}M`
8 : n >= 1000
9 ? `${Math.round(n / 1000)}k`
10 : `${n}`;
11
12const shortModel = (id: string) =>
13 ` ${id}`
14 .replace(/^claude-/, "")
15 .replace(/-\d{8}$/, "")
16 .replace(/\[1m\]$/, " 1M");
17
18// `estimated` marks a size the engine has not measured yet, such as just after a compaction.
19export const meter = (
20 model: string,
21 usage: Pick<SessionUsage, "context" | "cost">,
22 estimated = false,
23): Meter => {
24 const { tokens, window, percent } = usage.context;
25 const parts = [shortModel(model)];
26 const mark = estimated ? "~" : "";
27 parts.push(
28 tokens === undefined
29 ? `–/${compact(window)}`
30 : `${mark}${compact(tokens)}/${compact(window)} (${mark}${percent ?? 0}%)`,
31 );
32 if (usage.cost) parts.push(` ${usage.cost.usd.toFixed(2)}`);
33 return {
34 text: parts.join(" · "),
35 percent: tokens === undefined ? -1 : (percent ?? 0),
36 };
37};
38
39// Green below 40%, amber from 40% to below 70%, red from 70%; undefined before the first response.
40export const colour = (percent: number): ThemeKey | undefined =>
41 percent < 0
42 ? undefined
43 : percent < 40
44 ? "success"
45 : percent < 70
46 ? "warning"
47 : "error";
48types/index.d.ts 8 lines1export type Meter = { text: string; percent: number }
2
3declare module 'claude-code' {
4 interface PluginState {
5 'usage-meter': { meter: Meter | null }
6 }
7}
8