Append Every Tool Call to an Audit Log
Fires after every tool call completes and appends one JSON line — timestamp, session ID, tool name, truncated input — to .claude/audit.log. This is the concrete artifact a model-risk or SOX reviewer actually wants: proof of what an agent did in a session that doesn't depend on the transcript file still existing.
1. Save this as the hook script
#!/usr/bin/env python3
"""PostToolUse hook — appends one JSON line per tool call to a local audit log.
Wire this with no matcher (or matcher: "*") so it fires on every tool."""
import json, sys, datetime
from pathlib import Path
data = json.load(sys.stdin)
log_path = Path(data.get("cwd", ".")) / ".claude" / "audit.log"
log_path.parent.mkdir(parents=True, exist_ok=True)
entry = {
"ts": datetime.datetime.utcnow().isoformat() + "Z",
"session_id": data.get("session_id"),
"tool_name": data.get("tool_name"),
"tool_input_summary": json.dumps(data.get("tool_input", {}))[:300],
}
with open(log_path, "a") as f:
f.write(json.dumps(entry) + "\n")
sys.exit(0)
Save to a path inside your project, e.g. .claude/hooks/audit-log-tool-calls.sh, then chmod +x it.
2. Wire it into .claude/settings.json
{
"hooks": {
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{"type": "command", "command": "python3 .claude/hooks/audit-log-tool-calls.py"}
]
}
]
}
}
Before you use this
A hook runs with your local shell's permissions — review the script above before adding it, the same way you'd review any script you didn't write yourself. See our guide on hook security risks for the specific CVEs this category of feature has already produced.
Related hooks