A live bar above the prompt showing which tools Claude used this session

A small, visual Claude Code mod: a live bar chart above the prompt showing which tools Claude has used this session.
Tool meter (this session) [ Reset ] [ Hide ]
Bash ████████████ 6
Read ██████ 3
Edit ██ 1
It also puts the last tool in the status line, pops up a notice every 10 tool calls, and adds a /tool-meter command that brings the chart back after you hide it.
The repo has a second job: it carries a skill that teaches any agent CLI how to build a mod of your own, and a page of use cases for what mods can do.
A mod is a plugin of TypeScript function hooks that runs inside a Claude Code session. It can see every tool call and prompt, block or change them, and draw on the screen: a pane, a band above the prompt, a status line entry, a toast. It reloads when you save it.
You need Claude Code (written against version 2.1.286; the mod API is early access).
git clone https://github.com/VerbodhDev/tool-meter.git
claude --plugin-dir ./tool-meter
Then ask Claude something that uses tools, for example "list the files here and read one". The chart appears above the prompt after the first tool call.
To check the files without running a session:
claude plugin validate ./tool-meter
Open Claude Code in this folder and ask for a mod, for example "make a mod that blocks git push --force". The skill in .claude/skills/build-a-mod/ loads and guides it.
With another agent CLI, paste SKILL.md into it and add one line saying what the mod should do. Only Claude Code can run the result.
tool-meter/
├── .claude-plugin/plugin.json the mod's name, version and type contract
├── hooks/hooks.json points Claude Code at the module
├── hooks/register.tsx the mod
├── types/index.d.ts the values the mod keeps in $.state
├── docs/USE-CASES.md what mods can do for a team
└── .claude/skills/build-a-mod/ the skill / prompt for building a mod
└── SKILL.md
Claude Code writes .claude-plugin/types/ and tsconfig.json on each machine when the mod loads. They are gitignored.
hooks/register.tsx 68 lines1import { atom, read, update } from 'claude-code'
2import type { Register } from 'claude-code'
3
4import type { Counts } from '../types'
5
6const counts = atom({ plugin: 'tool-meter', key: 'counts' } as const, {} as Counts)
7const isHidden = atom({ plugin: 'tool-meter', key: 'isHidden' } as const, false)
8
9const BAR_MAX = 12
10
11export const register: Register = on => {
12 on('session.start', async ($, e, next) => {
13 await $.command.register({ name: 'tool-meter', description: 'Show the tool meter again' })
14
15 return next(e)
16 })
17
18 on('command.run', { command: 'tool-meter' }, async $ => {
19 await update($, isHidden, () => false)
20
21 return { text: 'Tool meter is showing above the prompt.' }
22 })
23
24 on('tool.call', async ($, e, next) => {
25 const tool = e.tool.startsWith('mcp__') ? 'MCP' : e.tool
26 const nextCounts = await update($, counts, c => ({ ...c, [tool]: (c[tool] ?? 0) + 1 }))
27 const total = Object.values(nextCounts).reduce((a, b) => a + b, 0)
28
29 $.ui.status(`🛠 ${tool} · ${total} tool calls`)
30 if (total % 10 === 0) {
31 $.ui.toast(`Claude has used ${total} tools this session`)
32 }
33
34 return next(e)
35 })
36
37 on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
38 const c = await read($, counts)
39 const rows = Object.entries(c).sort((a, b) => b[1] - a[1]).slice(0, 5)
40
41 if (e.props.hasSurvey || rows.length === 0 || (await read($, isHidden))) {
42 return next(e)
43 }
44
45 const { Box, Button, Text } = $.ui.resolve(e)
46 const top = rows[0][1]
47
48 return (
49 <Box flexDirection="column">
50 <Box>
51 <Text bold color="cyan">Tool meter </Text>
52 <Text dimColor>(this session) </Text>
53 <Button key="reset" label="Reset" onPress={() => update($, counts, () => ({}))} />
54 <Text> </Text>
55 <Button key="hide" label="Hide" onPress={() => update($, isHidden, () => true)} />
56 </Box>
57 {rows.map(([tool, n]) => (
58 <Box key={tool}>
59 <Text>{tool.padEnd(8)} </Text>
60 <Text color="green">{'█'.repeat(Math.max(1, Math.round((n / top) * BAR_MAX)))}</Text>
61 <Text dimColor> {n}</Text>
62 </Box>
63 ))}
64 </Box>
65 )
66 })
67}
68types/index.d.ts 8 lines1export type Counts = Record<string, number>
2
3declare module 'claude-code' {
4 interface PluginState {
5 'tool-meter': { counts: Counts; isHidden: boolean }
6 }
7}
8