Counts Claude's tool calls in the status line and adds a /my-mod command.

A starting point for your own Claude Code mod: a status line, a tool-call hook, a slash command, an option and tests.
npx degit Singh-AP/awesome-claude-mods/templates/mod-template my-mod
cd my-mod
claude plugin validate --strict . # what it hooks and calls
claude plugin test . # run the tests
claude --plugin-dir . # load it in a session
Edit hooks/register.ts while the session runs: the mod hot-reloads when you save.
hooks/register.ts 27 lines1import type { Register } from 'claude-code'
2
3import { describeCounts } from './counts'
4
5// Module state starts over on every hot reload; keep what must survive in
6// $.state (this session) or $.store (across sessions).
7const counts = new Map<string, number>()
8
9export const register: Register = (on, options) => {
10 const label = String(options.label ?? 'tools')
11
12 on('session.start', async ($, e, next) => {
13 await $.command.register({ name: 'my-mod', description: 'Show tool calls by tool' })
14 return next(e)
15 })
16
17 // Every tool call, main thread and subagents. `next(e)` runs it.
18 on('tool.call', async ($, e, next) => {
19 counts.set(e.tool, (counts.get(e.tool) ?? 0) + 1)
20 const total = [...counts.values()].reduce((a, b) => a + b, 0)
21 $.ui.status(`${label}: ${total}`)
22 return next(e)
23 })
24
25 on('command.run', { command: 'my-mod' }, async () => ({ text: describeCounts(counts) }))
26}
27hooks/counts.ts 10 lines1// Pure helpers (no `$`) are easy to unit test.
2
3export function describeCounts(counts: ReadonlyMap<string, number>): string {
4 if (counts.size === 0) return 'No tool calls yet.'
5 return [...counts.entries()]
6 .sort((a, b) => b[1] - a[1])
7 .map(([tool, n]) => `${tool}: ${n}`)
8 .join('\n')
9}
10