#!/usr/bin/env python3 """ Session Logger Hook - PostToolUse[*] hook Logs all tool usage and feeds data to shadow learner """ import sys import json import os from datetime import datetime from pathlib import Path # Add lib directory to path sys.path.insert(0, str(Path(__file__).parent.parent / "lib")) from shadow_learner import ShadowLearner from session_state import SessionStateManager from context_monitor import ContextMonitor from models import ToolExecution def main(): """Main hook entry point""" try: # Read input from Claude Code input_data = json.loads(sys.stdin.read()) # Extract tool execution data tool = input_data.get("tool", "") parameters = input_data.get("parameters", {}) success = input_data.get("success", True) error = input_data.get("error", "") execution_time = input_data.get("execution_time", 0.0) # Create tool execution record execution = ToolExecution( timestamp=datetime.now(), tool=tool, parameters=parameters, success=success, error_message=error if error else None, execution_time=execution_time, context={} ) # Initialize components shadow_learner = ShadowLearner() session_manager = SessionStateManager() context_monitor = ContextMonitor() # Feed execution to shadow learner shadow_learner.learn_from_execution(execution) # Update session state session_manager.update_from_tool_use(input_data) # Update context monitor context_monitor.update_from_tool_use(input_data) # Save learned patterns periodically # (Only save every 10 executions to avoid too much disk I/O) if context_monitor.tool_executions % 10 == 0: shadow_learner.save_database() # Log execution to file for debugging (optional) log_execution(execution) # Always allow - this is a post-execution hook response = { "allow": True, "message": f"Logged {tool} execution" } print(json.dumps(response)) sys.exit(0) except Exception as e: # Post-execution hooks should never block response = { "allow": True, "message": f"Logging error: {str(e)}" } print(json.dumps(response)) sys.exit(0) def log_execution(execution: ToolExecution): """Log execution to file for debugging and analysis""" try: log_dir = Path(".claude_hooks/logs") log_dir.mkdir(parents=True, exist_ok=True) # Create daily log file log_file = log_dir / f"executions_{datetime.now().strftime('%Y%m%d')}.jsonl" # Append execution record with open(log_file, 'a') as f: f.write(json.dumps(execution.to_dict()) + "\n") # Clean up old log files (keep last 7 days) cleanup_old_logs(log_dir) except Exception: # Don't let logging errors break the hook pass def cleanup_old_logs(log_dir: Path): """Clean up log files older than 7 days""" try: import time cutoff_time = time.time() - (7 * 24 * 3600) # 7 days ago for log_file in log_dir.glob("executions_*.jsonl"): if log_file.stat().st_mtime < cutoff_time: log_file.unlink() except Exception: pass if __name__ == "__main__": main()