claude-code-ultimate-guide/examples/hooks/bash/session-logger.sh
Florian BRUNIAUX 8a4d116e2e feat(docs): add LLM Handbook + Google Whitepaper integration v3.3.0
Advanced Guardrails:
- prompt-injection-detector.sh (PreToolUse)
- output-validator.sh (PostToolUse heuristics)
- claudemd-scanner.sh (SessionStart injection detection)
- output-secrets-scanner.sh (PostToolUse secrets leak prevention)

Observability & Monitoring:
- session-logger.sh (JSONL activity logging)
- session-stats.sh (cost tracking & analysis)
- guide/observability.md (full documentation)

LLM-as-a-Judge Evaluation:
- output-evaluator.md agent (Haiku)
- /validate-changes command
- pre-commit-evaluator.sh (opt-in git hook)

Google Agent Whitepaper Integration:
- Context Triage Guide (Section 2.2.4)
- CLAUDE.md Injection Warning (Section 3.1.3)
- Agent Validation Checklist (Section 4.2.4)
- MCP Security: Tool Shadowing & Confused Deputy (Section 8.6)
- Session vs Memory patterns (Section 3.3.3)

Stats: 10 new files, 8 modified, 5 new guide sections

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 21:00:49 +01:00

102 lines
2.8 KiB
Bash
Executable file

#!/bin/bash
# Hook: PostToolUse - Log all Claude Code operations for monitoring
# Exit 0 = allow (always)
#
# This hook logs all tool operations to JSONL files for later analysis.
# Use session-stats.sh to analyze the logs.
#
# Logs are stored in: ~/.claude/logs/activity-YYYY-MM-DD.jsonl
#
# Environment variables:
# CLAUDE_LOG_DIR - Override log directory (default: ~/.claude/logs)
# CLAUDE_LOG_TOKENS - Enable token estimation (default: true)
# CLAUDE_SESSION_ID - Session identifier (auto-generated if not set)
#
# Place in: .claude/hooks/session-logger.sh
# Register in: .claude/settings.json under PostToolUse event
set -e
# Configuration
LOG_DIR="${CLAUDE_LOG_DIR:-$HOME/.claude/logs}"
ENABLE_TOKENS="${CLAUDE_LOG_TOKENS:-true}"
SESSION_ID="${CLAUDE_SESSION_ID:-$(date +%s)-$$}"
# Ensure log directory exists
mkdir -p "$LOG_DIR"
# Log file for today
LOG_FILE="$LOG_DIR/activity-$(date +%Y-%m-%d).jsonl"
# Read JSON from stdin
INPUT=$(cat)
# Extract tool information
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // "unknown"')
TOOL_INPUT=$(echo "$INPUT" | jq -c '.tool_input // {}')
TOOL_OUTPUT=$(echo "$INPUT" | jq -r '.tool_output // ""')
# Get timestamp
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
# Extract relevant details based on tool type
FILE_PATH=""
COMMAND=""
case "$TOOL_NAME" in
Read|Write|Edit)
FILE_PATH=$(echo "$TOOL_INPUT" | jq -r '.file_path // .path // ""')
;;
Bash)
COMMAND=$(echo "$TOOL_INPUT" | jq -r '.command // ""' | head -c 200)
;;
Grep|Glob)
FILE_PATH=$(echo "$TOOL_INPUT" | jq -r '.path // .pattern // ""')
;;
esac
# Estimate tokens (rough heuristic: ~4 chars per token)
TOKENS_INPUT=0
TOKENS_OUTPUT=0
if [[ "$ENABLE_TOKENS" == "true" ]]; then
INPUT_LEN=${#TOOL_INPUT}
OUTPUT_LEN=${#TOOL_OUTPUT}
TOKENS_INPUT=$((INPUT_LEN / 4))
TOKENS_OUTPUT=$((OUTPUT_LEN / 4))
fi
# Get project directory (if available)
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
PROJECT_NAME=$(basename "$PROJECT_DIR")
# Build log entry
LOG_ENTRY=$(jq -n \
--arg timestamp "$TIMESTAMP" \
--arg session_id "$SESSION_ID" \
--arg tool "$TOOL_NAME" \
--arg file "$FILE_PATH" \
--arg command "$COMMAND" \
--arg project "$PROJECT_NAME" \
--argjson tokens_in "$TOKENS_INPUT" \
--argjson tokens_out "$TOKENS_OUTPUT" \
'{
timestamp: $timestamp,
session_id: $session_id,
tool: $tool,
file: (if $file != "" then $file else null end),
command: (if $command != "" then $command else null end),
project: $project,
tokens: {
input: $tokens_in,
output: $tokens_out,
total: ($tokens_in + $tokens_out)
}
} | with_entries(select(.value != null))'
)
# Append to log file
echo "$LOG_ENTRY" >> "$LOG_FILE"
# Always allow
exit 0