SLOPSHOPPER

__NAME__

__DESCRIPTION__

newspinnerguardcommand
v0.1.0MITupdated 2026-10-09MDmubarak786/claude-mods/templates/mod
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · __NAME__
› fix the failing auth test and add an audit log call ⏺ Read(src/auth.ts) ⎿ Read 6 lines ⏺ Update(src/auth.ts) ⎿ Added 2 lines, removed 1 line ⏺ Bash(bun test) ⎿ 3 pass, 1 fail ● Done. refresh now rejects expired claims and logs an audit event. ✻ Worked for 42s · done 4:20 PM › /__NAME__ ⎿ __NAME__: Claude has made 9 tool calls since this mod loaded ✻ Thinking · tool calls: 9…… ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts
README

__NAME__

__DESCRIPTION__

<!-- Two or three sentences a stranger understands: what pain it removes, and what you see when it works. -->

Demo

<!-- Required if the mod draws anything. Put files in screenshots/ and reference them here. -->

Install

/plugin marketplace add MDmubarak786/claude-mods
/plugin install __NAME__@modhub

Try it for one session without installing:

claude --plugin-dir ./mods/__NAME__

Use it

<!-- Commands, keys, buttons. What happens in claude -p or the Desktop app if that differs. -->

  • /__NAME__ prints how many tool calls Claude has made since the mod loaded.

What it touches

Paste the lines from claude plugin validate ./mods/__NAME__ so users can see what the mod does without reading the code:

hooks: session.start, tool.call, command.run{command=__NAME__}, ui.render{component=Spinner}
calls: $.command.register, $.ui.invalidate, $.ui.log

<!-- If the mod reads env vars, runs processes, or makes network requests, say why here. -->

Tested with

  • Claude Code X.Y.Z in the terminal <!-- and/or the Desktop app -->

Limitations

<!-- What it can't catch, doesn't handle, or where it falls back. Honest limits build trust. -->

License

MIT, see the repository root.

Source 1 files
hooks/register.ts 44 lines
1// __NAME__: __DESCRIPTION__
2//
3// This starter counts the tool calls Claude makes, shows the count beside the
4// spinner, and adds a /__NAME__ command that prints it. Replace it with your mod.
5// Every hook gets ($, e, next): the mods API, the event, and the next handler.
6// Return next(e) to observe, next({ ...e, changed }) to rewrite, or an object to answer.
7
8// Shared by the hooks below. Resets when the module reloads; use $.state or $.store to keep it.
9let calls = 0
10
11export function register(on) {
12  // Runs once before the first prompt, and again after each reload.
13  on('session.start', async ($, e, next) => {
14    try {
15      await $.command.register({
16        name: '__NAME__',
17        description: 'Show how many tool calls Claude has made',
18      })
19    } catch (error) {
20      // A taken name throws. Catch it so the rest of the hook still runs.
21      $.ui.log('could not register /__NAME__: ' + error)
22    }
23    return next(e)
24  })
25
26  // Observe: runs before each tool call and lets it continue unchanged.
27  on('tool.call', async ($, e, next) => {
28    calls += 1
29    $.ui.invalidate('ui.render')
30    return next(e)
31  }).catch(async ($, e, next) => next(e))
32
33  // Answer: the command's reply prints in the transcript. No next(), so nothing else runs.
34  on('command.run', { command: '__NAME__' }, async () => {
35    return { text: 'Claude has made ' + calls + ' tool calls since this mod loaded' }
36  }).catch(async () => ({ text: '__NAME__: the command failed' }))
37
38  // Rewrite: keep Claude Code's spinner and add text after its word.
39  on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
40    if (calls === 0) return next(e)
41    return next({ ...e, props: { ...e.props, suffix: ' · tool calls: ' + calls + '…' } })
42  })
43}
44