#!/usr/bin/env node /** * Session Logger Hook - PostToolUse[*] hook * Logs all tool usage and feeds data to shadow learner */ const fs = require('fs-extra'); const path = require('path'); // Add lib directory to require path const libPath = path.join(__dirname, '..', 'lib'); const { ShadowLearner } = require(path.join(libPath, 'shadow-learner')); const { SessionStateManager } = require(path.join(libPath, 'session-state')); const { ContextMonitor } = require(path.join(libPath, 'context-monitor')); async function logExecution(execution) { /** * Log execution to file for debugging and analysis */ try { const logDir = path.join('.claude_hooks', 'logs'); await fs.ensureDir(logDir); // Create daily log file const date = new Date().toISOString().slice(0, 10).replace(/-/g, ''); const logFile = path.join(logDir, `executions_${date}.jsonl`); // Append execution record await fs.appendFile(logFile, JSON.stringify(execution) + '\n'); // Clean up old log files (keep last 7 days) await cleanupOldLogs(logDir); } catch (error) { // Don't let logging errors break the hook } } async function cleanupOldLogs(logDir) { /** * Clean up log files older than 7 days */ try { const cutoffTime = Date.now() - (7 * 24 * 60 * 60 * 1000); // 7 days ago const files = await fs.readdir(logDir); const logFiles = files.filter(file => file.match(/^executions_\d{8}\.jsonl$/)); for (const logFile of logFiles) { const filePath = path.join(logDir, logFile); const stats = await fs.stat(filePath); if (stats.mtime.getTime() < cutoffTime) { await fs.unlink(filePath); } } } catch (error) { // Ignore cleanup errors } } async function main() { try { let inputData = ''; // Handle stdin input if (process.stdin.isTTY) { // If called directly for testing inputData = JSON.stringify({ tool: 'Bash', parameters: { command: 'echo test' }, success: true, execution_time: 0.1 }); } else { // Read from stdin process.stdin.setEncoding('utf8'); for await (const chunk of process.stdin) { inputData += chunk; } } const input = JSON.parse(inputData); // Extract tool execution data const tool = input.tool || ''; const parameters = input.parameters || {}; const success = input.success !== undefined ? input.success : true; const error = input.error || ''; const executionTime = input.execution_time || 0.0; // Create tool execution record const execution = { timestamp: new Date(), tool, parameters, success, errorMessage: error || null, executionTime, context: {} }; // Initialize components const shadowLearner = new ShadowLearner(); const sessionManager = new SessionStateManager(); const contextMonitor = new ContextMonitor(); // Feed execution to shadow learner shadowLearner.learnFromExecution(execution); // Update session state await sessionManager.updateFromToolUse(input); // Update context monitor contextMonitor.updateFromToolUse(input); // Save learned patterns periodically // (Only save every 10 executions to avoid too much disk I/O) if (contextMonitor.toolExecutions % 10 === 0) { await shadowLearner.saveDatabase(); } // Log execution to file for debugging (optional) await logExecution(execution); // Always allow - this is a post-execution hook const response = { allow: true, message: `Logged ${tool} execution` }; console.log(JSON.stringify(response)); process.exit(0); } catch (error) { // Post-execution hooks should never block const response = { allow: true, message: `Logging error: ${error.message}` }; console.log(JSON.stringify(response)); process.exit(0); } } // Handle unhandled promise rejections process.on('unhandledRejection', (error) => { const response = { allow: true, message: `Logging error: ${error.message}` }; console.log(JSON.stringify(response)); process.exit(0); }); main();