Files
daniel 043f37342e feat: add interview toolkit (6 components)
Codebase explorer agent, deadline countdown statusline, change logger
hook, deadline prep skill, commit smart skill, and test detect skill.
Designed for rapid onboarding on unfamiliar codebases with time pressure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 14:38:47 -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()