#!/usr/bin/env node 'use strict'; const fs = require('fs'); const os = require('os'); const path = require('path'); const MAX_STDIN = 1024 * 1024; let raw = ''; const MODE_CONFIG = { audit: { fileName: 'bash-commands.log', format: command => `[${new Date().toISOString()}] ${command}`, }, cost: { fileName: 'cost-tracker.log', format: command => `[${new Date().toISOString()}] tool=Bash command=${command}`, }, }; function sanitizeCommand(command) { return String(command || '') .replace(/\n/g, ' ') .replace(/--token[= ][^ ]*/g, '--token=') .replace(/Authorization:[: ]*[^ ]*[: ]*[^ ]*/gi, 'Authorization:') .replace(/\bAKIA[A-Z0-9]{16}\b/g, '') .replace(/\bASIA[A-Z0-9]{16}\b/g, '') .replace(/password[= ][^ ]*/gi, 'password=') .replace(/\bghp_[A-Za-z0-9_]+\b/g, '') .replace(/\bgho_[A-Za-z0-9_]+\b/g, '') .replace(/\bghs_[A-Za-z0-9_]+\b/g, '') .replace(/\bgithub_pat_[A-Za-z0-9_]+\b/g, ''); } function appendLine(filePath, line) { fs.mkdirSync(path.dirname(filePath), { recursive: true }); fs.appendFileSync(filePath, `${line}\n`, 'utf8'); } // Claude Code redirects every profile-local path when CLAUDE_CONFIG_DIR is set. // Writing to ~/.claude regardless leaks this profile's data into the default one. function claudeConfigDir() { const configured = process.env.CLAUDE_CONFIG_DIR; if (configured && String(configured).trim().length > 0) { return path.resolve(String(configured).trim()); } return path.join(os.homedir(), '.claude'); } function run(rawInput, mode = 'audit') { const config = MODE_CONFIG[mode]; try { if (config) { const input = String(rawInput || '').trim() ? JSON.parse(String(rawInput)) : {}; const command = sanitizeCommand(input.tool_input?.command || '?'); appendLine(path.join(claudeConfigDir(), config.fileName), config.format(command)); } } catch { // Logging must never block the calling hook. } return typeof rawInput === 'string' ? rawInput : JSON.stringify(rawInput); } function main() { const mode = process.argv[2]; process.stdin.setEncoding('utf8'); process.stdin.on('data', chunk => { if (raw.length < MAX_STDIN) { const remaining = MAX_STDIN - raw.length; raw += chunk.substring(0, remaining); } }); process.stdin.on('end', () => { process.stdout.write(run(raw, mode)); }); } if (require.main === module) { main(); } module.exports = { run, sanitizeCommand, };