HookStack
Back to catalogue
NotificationPostToolUse

Tool usage tracking

See exactly how hard each session works

Increments a counter per tool type after each use and logs the activity, providing a view of the agent's usage intensity per session.

What does the Tool usage tracking hook do?

Tool usage tracking is a Claude Code PostToolUse hook. It fires automatically at that lifecycle event — outside the model, so it can't be skipped or forgotten. See exactly how hard each session works.

As a PostToolUse hook it runs after the action, reacting to what just happened rather than blocking it. Because it is a deterministic Node.js script, it executes on every matching event without relying on the model to remember — the guarantee that makes agentic workflows safe to automate.

Use cases

  • Observability
  • Usage analysis
  • Agentic FinOps

Tags

#notification#observability#metrics#finops

settings.json fragment

{
  "hooks": {
    "PostToolUse": [
      {
        "hooks": [
          {
            "command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/tool-usage.mjs",
            "type": "command"
          }
        ]
      }
    ]
  }
}

Script · .claude/hooks/tool-usage.mjs

#!/usr/bin/env node
// @hookstack post-bash-cost-tracker
// Journalise les commandes Bash avec leur durée (PostToolUse Bash)
import { appendFileSync, mkdirSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { fileURLToPath } from "node:url";

export function run(
	input,
	{
		append = appendFileSync,
		mkdir = mkdirSync,
		projectDir = process.env.CLAUDE_PROJECT_DIR ?? process.cwd(),
		now = () => new Date().toISOString(),
	} = {},
) {
	const command = input.tool_input?.command ?? "";
	if (!command) return null;

	const logDir = join(projectDir, ".claude", "data");
	mkdir(logDir, { recursive: true });

	const entry = {
		ts: now(),
		cmd: command.slice(0, 500),
		exit: input.tool_response?.exit_code ?? null,
	};

	append(join(logDir, "bash-history.jsonl"), `${JSON.stringify(entry)}\n`);
	return entry;
}

/* v8 ignore next 4 */
if (process.argv[1] === fileURLToPath(import.meta.url)) {
	const input = JSON.parse(readFileSync(0, "utf8"));
	run(input);
}

Learn more

Related hooks