import { existsSync, mkdirSync } from 'fs'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; import { HookError, CompressionError, Logger, FileLogger } from './types.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); export class ErrorHandler { private logger: Logger; private logDir: string; // 7.1 ==================================== constructor(enableDebug = false) { this.logDir = join(__dirname, '..', 'logs'); this.ensureLogDirectory(); const logFile = join( this.logDir, `claude-mem-${new Date().toISOString().slice(0, 10)}.log` ); this.logger = new FileLogger(logFile, enableDebug); } // ======================================= // 7.2 ==================================== private ensureLogDirectory(): void { if (!existsSync(this.logDir)) { mkdirSync(this.logDir, { recursive: true }); } } // ======================================= // 7.3 ==================================== handleHookError(error: Error, hookType: string, payload?: unknown): never { // 7.3a ==================================== const hookError = error instanceof HookError ? error : new HookError( error.message, hookType, payload as any, 'HOOK_EXECUTION_ERROR' ); // ======================================= this.logger.error(`Hook execution failed in ${hookType}`, hookError, { hookType, payload: payload ? JSON.stringify(payload) : undefined, }); console.log( JSON.stringify({ continue: false, stopReason: `Hook error: ${hookError.message}`, error: { type: hookError.name, message: hookError.message, code: hookError.code, }, }) ); process.exit(1); } // ======================================= // 7.4 ==================================== handleCompressionError( error: Error, transcriptPath: string, stage: string ): never { // 7.4a ==================================== const compressionError = error instanceof CompressionError ? error : new CompressionError(error.message, transcriptPath, stage as any); // ======================================= this.logger.error(`Compression failed during ${stage}`, compressionError, { transcriptPath, stage, }); console.error(`Compression error: ${compressionError.message}`); console.error(`Stage: ${stage}`); console.error(`Transcript: ${transcriptPath}`); process.exit(1); } // ======================================= // 7.5 ==================================== handleValidationError( message: string, context?: Record ): never { this.logger.error('Validation error', undefined, { message, context }); console.error(`Validation error: ${message}`); // 7.5a ==================================== if (context) { console.error('Context:', JSON.stringify(context, null, 2)); } // ======================================= process.exit(1); } // ======================================= // 7.6 ==================================== logSuccess(operation: string, details?: Record): void { this.logger.info(`Operation successful: ${operation}`, details); } // ======================================= // 7.7 ==================================== logWarning(message: string, details?: Record): void { this.logger.warn(message, details); } // ======================================= // 7.8 ==================================== logDebug(message: string, details?: Record): void { this.logger.debug(message, details); } // ======================================= } // 7.9 ==================================== export function parseStdinJson(input: string): T { try { return JSON.parse(input) as T; } catch (error) { throw new Error( `Failed to parse JSON input: ${error instanceof Error ? error.message : 'Unknown error'}` ); } } // ======================================= // 7.10 =================================== export async function safeExecute( operation: () => Promise, errorHandler: ErrorHandler, context: string ): Promise { try { return await operation(); } catch (error) { const message = `Safe execution failed in ${context}: ${error instanceof Error ? error.message : String(error)}`; errorHandler.handleValidationError(message, { context, error }); throw error; } } // ======================================= // 7.11 =================================== export function validateFileExists( filePath: string, errorHandler: ErrorHandler ): void { if (!existsSync(filePath)) { errorHandler.handleValidationError(`File not found: ${filePath}`, { filePath, }); } } // ======================================= // 7.12 =================================== /** * Creates a standardized hook response using HookTemplates * @deprecated Use HookTemplates.createHookSuccessResponse or createHookErrorResponse instead * This function is maintained for backward compatibility but should be replaced with HookTemplates. */ export function createHookResponse( success: boolean, data?: Record ): string { // Log deprecation warning in development mode if (process.env.NODE_ENV === 'development') { console.warn('createHookResponse in error-handler.ts is deprecated. Use HookTemplates.createHookSuccessResponse or createHookErrorResponse instead.'); } const response = { continue: success, suppressOutput: true, // Add standard suppressOutput field for Claude Code compatibility ...data, }; return JSON.stringify(response); } // ======================================= export const globalErrorHandler = new ErrorHandler( process.env.DEBUG === 'true' );