支持 /tally 的 Claude Code 工具调用计数器
统计 Claude 每轮和每个会话的工具调用次数,在加载指示器旁显示计数,并新增 /tally 查看明细。
权限
- 拦截工具调用能看到 Claude 发起的每一次工具调用(tool.call / tool.check),并可以阻止或修改它。
- 修改界面在 Claude Code 界面中绘制内容:横条、面板、状态文字、弹出通知(ui.render / $.ui.*)。
- 添加命令/工具注册新的斜杠命令或工具,供你或 Claude 调用($.command.register / $.tool.register)。
- 实测版本
- v2.1.291
- 最近实测
- 需要
- Claude Code ≥ 2.1.287
- 显示位置
- 加载动画、对话记录、状态栏
在 Claude Code 中安装:
/plugin install cm-tool-call-counter --marketplace rotbit/claudemods-marketplace这个插件能做什么?
工具调用计数器(Tool Call Counter)是一款 Claude Code 工具调用计数插件。Claude 工作时,加载指示器(spinner)那一行会多出一个后缀,显示本轮的调用次数,例如 · 7 tools,这样你不用翻看对话记录就能知道这一轮有多忙。随时输入 /tally,即可看到本会话迄今为止的统计表:
**Tool calls** · this turn: 7 · this session: 41 (2 errors)
| Tool | Calls | Errors |
| --- | ---: | ---: |
| Bash | 18 | 2 |
| Read | 15 | 0 |
| Edit | 8 | 0 |当你想知道某一轮为什么耗时很久、Claude 是否在反复读取相同的文件,或者它的 shell 命令多久失败一次时,它就很有用。你还可以选择让状态栏持续显示累计总数:🔧 41 tool calls (2 errors)。需要 Claude Code 2.1.287 或更高版本。
演示
录屏中,我们让 Claude 执行一个需要用到多个工具的任务。随着调用发生,加载指示器的后缀不断递增;之后运行 /tally,就会打印出按工具划分的统计表。
安装
使用安装区块中的任意一种方式安装。/tally 会在会话启动时注册。
$ install cm-tool-call-counter
需要 Claude Code ≥ 2.1.287
1.在 Claude Code 中一行安装
/plugin install cm-tool-call-counter --marketplace rotbit/claudemods-marketplace粘贴到正在运行的会话中(v2.1.275+)。Claude Code 会先询问是否添加该插件市场。
2.从终端安装
claude plugin marketplace add rotbit/claudemods-marketplace claude plugin install cm-tool-call-counter@claudemods然后在所有已打开的会话中运行 /reload-plugins。
3.免安装试用
git clone https://github.com/rotbit/claudemods-marketplace claude --plugin-dir ./claudemods-marketplace/cm-tool-call-counter仅在当前会话中加载该插件,不会写入你的设置。
工作原理
计数由一个观察所有工具的 tool.call 钩子完成。以下摘自 hooks/register.ts:
on('tool.call', async ($, e, next) => {
const tool = String(e.tool)
await update($, turn, t => addCall(t, tool))
const now = await update($, session, t => addCall(t, tool))
if (showInStatus) $.ui.status(statusText(now))
const ran = await next(e)
if (ran.deny !== undefined || ran.isError === true) {
await update($, turn, t => addError(t, tool))
const failed = await update($, session, t => addError(t, tool))
if (showInStatus) $.ui.status(statusText(failed))
}
return ran
}).catch(($, e, next) => next(e))
on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
if (!showInSpinner) return next(e)
const { count } = await read($, turn)
if (count === 0) return next(e)
return next({ ...e, props: { ...e.props, suffix: spinnerSuffix(e.props.suffix, count) } })
})每次调用在开始时就会被计入两个 $.state 原子:一个是由 turn.start 钩子重置的 turn,另一个是 session。随后钩子通过 next(e) 把调用原样传下去;如果结果被拒绝或带有 isError 标记,就记录一次错误。即使计数过程抛出异常,.catch 也会照常执行这次调用,所以插件永远不会阻塞任何工具。
针对 Spinner 渲染位置的 ui.render 钩子只改写 suffix 属性,其余内容全部交给 next,因此加载指示器仍由 Claude Code 自己绘制。由于计数保存在 $.state 中,数字一变,加载指示器就会立即重绘。/tally 是在 session.start 中通过 $.command.register 注册的,而针对 tally 的 command.run 钩子会返回由 hooks/tally.ts 中的 formatTable 生成的 { text }。
自定义
在 /config 中修改这些选项。加载指示器的文字来自 spinnerSuffix,状态栏文字来自 statusText,两者都在 hooks/tally.ts 中。若想让 /tally 表格换一种排序方式,请修改 formatTable 中的 sort 调用。
| 设置项 | 类型 | 默认值 | 作用 |
|---|---|---|---|
| showInSpinner | boolean | true | Show in spinner. Append " · N tools" (this turn's count) to the working spinner. |
| showInStatus | boolean | false | Show in status line. Also show the session's total tool calls and errors in the status line. |
在 Claude Code 中通过 /config 修改这些设置,插件会热重载。
权限与安全
之所以会出现拦截工具调用,是因为插件为所有工具挂接了 tool.call。它只做观察:从不拒绝或修改任何调用,除了工具名称之外也不查看其他参数。修改界面对应加载指示器后缀和可选的状态栏文字。添加命令/工具对应 /tally。
它不运行命令、不读取文件、不使用网络,也不会在会话之间保存任何数据。可以用 /plugin disable cm-tool-call-counter 停用它,或者用 claude --safe-mode 启动一个关闭所有插件的会话。
- 拦截工具调用
- 能看到 Claude 发起的每一次工具调用(tool.call / tool.check),并可以阻止或修改它。
- 修改界面
- 在 Claude Code 界面中绘制内容:横条、面板、状态文字、弹出通知(ui.render / $.ui.*)。
- 添加命令/工具
- 注册新的斜杠命令或工具,供你或 Claude 调用($.command.register / $.tool.register)。
兼容性与故障排除
已在 macOS 终端中使用 Claude Code 2.1.291 测试。如果你看不到计数:
- 确认
claude --version为 2.1.287 或更高版本,并且该插件已在/plugin中启用。 - 通过 shell 安装后,运行
/reload-plugins;会话重新启动该插件后,/tally就会出现。 - 只有当本轮至少发生过一次工具调用时,后缀才会显示。
/config中的showInSpinner可能处于关闭状态。
计数只覆盖插件在当前进程中加载以来的这段时间,所以重新加载后会归零。
常见问题
- 工具调用计数器会改变或拖慢工具调用吗?
- 不会。它只是记下这次调用,通过 next(e) 让调用原样运行,如果调用失败再记一次错误。即使计数本身出错,.catch 处理器也会照常执行这次调用。
- 在 /tally 中,什么算作错误?
- 结果带有 isError 的调用,或者被其他钩子拒绝的调用,例如某个安全插件拒绝了一条 Bash 命令。
- 会话计数在重启后还会保留吗?
- 不会。计数保存在 $.state 中,只在当前 Claude Code 进程中有效。新会话或重新加载插件都会从零开始。
- /tally 会调用模型吗?
- 不会。command.run 钩子直接根据插件收集到的计数作答,所以是即时的,也不消耗 token。
- 不打开 /tally 也能看到总数吗?
- 在 /config 中开启 showInStatus,即可让会话总数和错误数一直显示在状态栏中。
# 相关插件


