Files
wehub-resource-sync bb5c75ce05
Component Security Validation / Security Audit (push) Has been cancelled
Deploy to Cloudflare Pages / deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:38:58 +08:00

86 lines
2.4 KiB
Python

#!/usr/bin/env python3
"""
Change Logger Hook
Logs every file mutation (Edit, Write, Bash) to a CSV file for demo prep and session review.
Output: .claude/critical_log_changes.csv
"""
import csv
import json
import os
import sys
from datetime import datetime
# Read-only commands that should not be logged
READONLY_COMMANDS = {
"cat", "head", "tail", "less", "more",
"ls", "dir", "tree", "pwd", "which", "where", "whereis",
"echo", "printf",
"grep", "rg", "find", "fd", "ag",
"git status", "git log", "git diff", "git show", "git branch",
"git remote", "git stash list", "git tag",
"node -e", "python -c", "ruby -e",
"type", "file", "wc", "du", "df",
}
CSV_PATH = ".claude/critical_log_changes.csv"
CSV_HEADERS = ["timestamp", "tool", "file_path", "action", "details"]
def is_readonly_command(command):
"""Check if a bash command is read-only and should be skipped."""
cmd_stripped = command.strip()
for ro_cmd in READONLY_COMMANDS:
if cmd_stripped.startswith(ro_cmd):
return True
return False
def log_change(tool_name, file_path, action, details=""):
"""Append a row to the CSV change log."""
os.makedirs(os.path.dirname(CSV_PATH), exist_ok=True)
write_header = not os.path.exists(CSV_PATH)
with open(CSV_PATH, "a", newline="", encoding="utf-8") as f:
writer = csv.writer(f, quoting=csv.QUOTE_MINIMAL)
if write_header:
writer.writerow(CSV_HEADERS)
writer.writerow([
datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
tool_name,
file_path,
action,
details[:200],
])
def main():
try:
data = json.load(sys.stdin)
except (json.JSONDecodeError, EOFError):
sys.exit(0)
tool_name = data.get("tool_name", "")
tool_input = data.get("tool_input", {})
if tool_name in ("Edit", "MultiEdit"):
file_path = tool_input.get("file_path", "unknown")
log_change(tool_name, file_path, "modified")
elif tool_name == "Write":
file_path = tool_input.get("file_path", "unknown")
log_change(tool_name, file_path, "created")
elif tool_name == "Bash":
command = tool_input.get("command", "")
if command and not is_readonly_command(command):
log_change(tool_name, "-", "executed", command[:200])
# Never block tool execution
sys.exit(0)
if __name__ == "__main__":
main()