#!/usr/bin/env node /** * Hook Approval Pattern Tracker - Enforces inst_061 * Persists user approval for "don't ask again for similar commands" in session * * When user selects option: "Yes, and don't ask again for similar commands in [directory]" * This hook ensures Claude Code remembers and doesn't ask again during the same session. */ const fs = require('fs'); const path = require('path'); const SESSION_STATE = path.join(__dirname, '../session-state.json'); const APPROVAL_CACHE = path.join(__dirname, '../approval-cache.json'); function loadApprovalCache() { if (!fs.existsSync(APPROVAL_CACHE)) { return { sessionId: null, approvals: [] }; } return JSON.parse(fs.readFileSync(APPROVAL_CACHE, 'utf8')); } function saveApprovalCache(cache) { fs.writeFileSync(APPROVAL_CACHE, JSON.stringify(cache, null, 2)); } function getCurrentSession() { if (!fs.existsSync(SESSION_STATE)) { return null; } const state = JSON.parse(fs.readFileSync(SESSION_STATE, 'utf8')); return state.sessionId; } function recordApproval(commandType, directory) { const cache = loadApprovalCache(); const currentSession = getCurrentSession(); // Reset cache if session changed if (cache.sessionId !== currentSession) { cache.sessionId = currentSession; cache.approvals = []; } // Add approval cache.approvals.push({ timestamp: new Date().toISOString(), commandType, directory, sessionId: currentSession }); saveApprovalCache(cache); console.log(`\nāœ… Approval cached (inst_061)`); console.log(` Command: ${commandType}`); console.log(` Directory: ${directory}`); console.log(` Session: ${currentSession}\n`); } function checkApproval(commandType, directory) { const cache = loadApprovalCache(); const currentSession = getCurrentSession(); // If session changed, cache is stale if (cache.sessionId !== currentSession) { return false; } // Check for matching approval const match = cache.approvals.find(a => a.commandType === commandType && (a.directory === directory || directory.startsWith(a.directory)) ); return !!match; } function listApprovals() { const cache = loadApprovalCache(); const currentSession = getCurrentSession(); if (cache.sessionId !== currentSession) { console.log('\nāœ… No approvals cached for current session\n'); return; } if (cache.approvals.length === 0) { console.log('\nāœ… No approvals cached for current session\n'); return; } console.log(`\nšŸ“‹ Cached Approvals (Session: ${currentSession})\n`); console.log('━'.repeat(70)); cache.approvals.forEach((approval, idx) => { console.log(`\n${idx + 1}. ${approval.commandType} in ${approval.directory}`); console.log(` Approved: ${new Date(approval.timestamp).toLocaleString()}`); }); console.log('\n' + '━'.repeat(70)); console.log('\nThese commands will not trigger approval prompts again this session.\n'); } function main() { const command = process.argv[2]; if (!command || command === 'list') { listApprovals(); return; } if (command === 'record') { const commandType = process.argv[3]; const directory = process.argv[4]; if (!commandType || !directory) { console.log('Usage: track-approval-patterns.js record '); console.log(''); console.log('Example:'); console.log(' track-approval-patterns.js record "git status" /home/user/project'); process.exit(1); } recordApproval(commandType, directory); } else if (command === 'check') { const commandType = process.argv[3]; const directory = process.argv[4]; if (!commandType || !directory) { console.log('Usage: track-approval-patterns.js check '); process.exit(1); } const approved = checkApproval(commandType, directory); if (approved) { console.log(`āœ… Approval cached - skip prompt`); process.exit(0); } else { console.log(`āŒ No approval cached - show prompt`); process.exit(1); } } else { console.log('Usage: track-approval-patterns.js [list|record|check]'); console.log(''); console.log('Commands:'); console.log(' list - List cached approvals'); console.log(' record - Record approval'); console.log(' check - Check if approved'); } } main();