SLOPSHOPPER

spinner-counter

Claude Code skill that builds, debugs, audits and packages Claude Code mods from a plain-language description

newspinnerguard
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · spinner-counter
› 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 ✻ Thinking · 9 tool calls… ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts
README

spinner-counter

Leia em português

Adds a live count of tool calls next to the word Claude shows while it is thinking.

What is a mod?

Claude Code is Anthropic's assistant for working with code, in the terminal or in the Claude desktop app. A mod is a small add-on for it: it can add a panel, a bar above the box where you type, a new /command, or a rule that checks what Claude is about to do. This folder is one complete mod you can install and use as it is, or copy as a starting point.

What it does

While Claude works, Claude Code shows an animated word such as "Thinking". It does not tell you how much is going on behind it. This mod adds the number of tools Claude has used in the current answer, so you can tell a quick reply from a long job at a glance.

What you see

  • The usual animated word, followed by the count: "Thinking · 3 tool calls". The number goes back to zero on each new turn.

How to use it

  1. Install the mod. It shows up on its own the next time Claude works.

Keyboard shortcuts

None of its own.

Commands

None. It works on its own.

Settings

Claude Code asks for these when you enable the mod. To change them later, open /config and find the mod by name.

SettingWhat it meansDefault
language (Language)Language of the text the mod shows: en (English) or pt-BR (Brazilian Portuguese).en

Good to know

  • It keeps Claude Code's own animation and only adds text after it.

Install

You need Claude Code 2.1.287 or newer in the terminal, or 2.1.286 or newer in the desktop app.

  1. Copy this folder anywhere on your computer.
  2. Inside the copy, rename the folder _claude-plugin to .claude-plugin (with a dot at the start). It ships without the dot so the examples never load by accident.
  3. Start Claude Code with the mod:
claude --plugin-dir path/to/spinner-counter

To keep it installed for good, ask the claude-mod-forge skill to package it ("package this mod").

Check it

From the skill folder (skills/claude-mod-forge), this runs the manifest check, a strict type check and the 2 automated tests of this mod:

node scripts/check-mod.mjs examples/spinner-counter

The tests cover the mod's logic and what it draws in the terminal and the desktop app. How it looks in your own window is worth a quick look in a real session.

For developers: the examples table lists the hooks and API calls this mod demonstrates.

Source 3 files
hooks/register.ts 31 lines
1import { atom, read, update } from 'claude-code'
2import type { Register } from 'claude-code'
3
4import { strings } from './i18n'
5
6// Rewrites a native render site instead of replacing it: next() with changed props keeps
7// Claude Code's spinner (animation, word, mode) and only adds text after the word.
8const calls = atom({ plugin: 'spinner-counter', key: 'calls' } as const, 0)
9
10export const register: Register = (on, options) => {
11  const t = strings(options.language)
12
13  on('turn.start', async ($, e, next) => {
14    await update($, calls, () => 0)
15    return next(e)
16  })
17
18  on('tool.call', async ($, e, next) => {
19    if (e.agentId === undefined) await update($, calls, n => n + 1) // main conversation only
20    return next(e)
21  })
22
23  // Spinner is raised on the terminal and the desktop. Other surfaces simply never call this.
24  on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
25    const n = await read($, calls)
26    if (n === 0) return next(e) // nothing to add: leave the native spinner untouched
27    const suffix = `${e.props.suffix} · ${t.toolCalls(n)}`
28    return next({ ...e, props: { ...e.props, suffix } })
29  })
30}
31
hooks/i18n.ts 20 lines
1// The text this mod shows, per language. Pure: no $ calls, so tests can cover it directly.
2// Anything other than a known language falls back to English.
3
4export type Language = 'en' | 'pt-BR'
5
6const en = {
7  toolCalls: (n: number) => `${n} tool call${n === 1 ? '' : 's'}`,
8}
9
10const ptBR: typeof en = {
11  toolCalls: (n: number) => `${n} ferramenta${n === 1 ? '' : 's'}`,
12}
13
14export type Strings = typeof en
15const TABLE: Record<Language, Strings> = { en, 'pt-BR': ptBR }
16
17export function strings(language: unknown): Strings {
18  return language === 'pt-BR' ? TABLE['pt-BR'] : TABLE.en
19}
20
types/index.d.ts 8 lines
1export type SpinnerCalls = number
2
3declare module 'claude-code' {
4  interface PluginState {
5    'spinner-counter': { calls: SpinnerCalls }
6  }
7}
8