A band above the prompt showing the last turn's and the session's list-price cost estimate.

Small Claude Code mods from the MateAI channel.
A band above the prompt that shows what the last turn and the whole session would cost at API list prices:
Last turn $0.042 · Session $0.318 · list-price estimate, not a bill
The mod reads the session cost Claude Code already computes (the same number as "Total cost" in /usage). It contains no price table, so there are no hard-coded rates to go stale.
Check your version with claude --version (terminal) or /status (Desktop, local session).
From a terminal:
claude plugin marketplace add mategog/mateai-mods
claude plugin install cost-band@mateai-mods
Or inside a Claude Code session:
/plugin marketplace add mategog/mateai-mods
/plugin install cost-band@mateai-mods
Then restart the session. The band appears above the prompt after the first completed turn.
claude plugin marketplace update mateai-mods
claude plugin disable cost-band@mateai-mods
claude plugin marketplace remove mateai-mods
Mods are not sandboxed. A mod runs inside Claude Code with your permissions, so it could read your files and environment variables, start programs and use the network. Only install mods from people you trust, and read the code first. This one is short:
| File | What it does |
|---|---|
plugins/cost-band/hooks/register.tsx | On turn.complete it reads session.usage(); on ui.render it draws the band. |
plugins/cost-band/hooks/cost.ts | Pure functions: last-turn math and $ formatting. |
plugins/cost-band/hooks/hooks.json | Registers the module. |
It makes no network calls and starts no processes. You can also check what any mod registers without running it:
claude plugin validate plugins/cost-band
Cost estimate unavailable in this session./clear the session total starts over, and so does the last-turn figure..claude-plugin/marketplace.json marketplace "mateai-mods"
plugins/cost-band/ the plugin
.claude-plugin/plugin.json
hooks/{hooks.json,register.tsx,cost.ts}
hooks/*.test.ts unit tests for the cost math and registration
types/index.d.ts
MIT. See LICENSE.
hooks/register.tsx 36 lines1import { atom, read, update } from 'claude-code'
2import type { Register } from 'claude-code'
3
4import { formatUsd, nextCost, START } from './cost'
5
6const cost = atom({ plugin: 'cost-band', key: 'cost' } as const, START)
7
8export const register: Register = on => {
9 on('turn.complete', async ($, e, next) => {
10 const usage = await $.session.usage()
11 await update($, cost, prev => nextCost(prev, usage.cost?.usd))
12
13 return next(e)
14 })
15
16 on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
17 const { turns, total, last } = await read($, cost)
18
19 if (e.props.hasSurvey || turns === 0) {
20 return next(e)
21 }
22
23 const { Box, Text } = $.ui.resolve(e)
24
25 return (
26 <Box>
27 <Text dimColor>
28 {total === null || last === null
29 ? 'Cost estimate unavailable in this session'
30 : `Last turn ${formatUsd(last)} · Session ${formatUsd(total)} · list-price estimate, not a bill`}
31 </Text>
32 </Box>
33 )
34 })
35}
36hooks/cost.ts 17 lines1import type { Cost } from '../types'
2
3export const START: Cost = { turns: 0, total: null, last: null }
4
5// `usd` is the session's running total; a total below the previous one means /clear started over.
6export function nextCost(prev: Cost, usd: number | undefined): Cost {
7 if (usd === undefined) return { turns: prev.turns + 1, total: null, last: null }
8
9 const before = prev.total === null || usd < prev.total ? 0 : prev.total
10
11 return { turns: prev.turns + 1, total: usd, last: usd - before }
12}
13
14export function formatUsd(n: number): string {
15 return '$' + n.toFixed(n < 1 ? 3 : 2)
16}
17types/index.d.ts 8 lines1export type Cost = { turns: number; total: number | null; last: number | null }
2
3declare module 'claude-code' {
4 interface PluginState {
5 'cost-band': { cost: Cost }
6 }
7}
8