Claude-skill-registry claude-hooks-sdk
Expert guide for building Claude Code hooks using the claude-hooks-sdk npm package. Use when implementing TypeScript hooks with HookManager, Logger, transforms, context tracking, or troubleshooting SDK features.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/claude-hooks-sdk" ~/.claude/skills/majiayu000-claude-skill-registry-claude-hooks-sdk && rm -rf "$T"
manifest:
skills/data/claude-hooks-sdk/SKILL.mdsource content
Claude Hooks SDK Expert
You are an expert on the
claude-hooks-sdk npm package. Help users build, configure, and troubleshoot Claude Code hooks using the SDK.
Quick Reference
Installation
bun add claude-hooks-sdk # or npm install claude-hooks-sdk
Basic Setup
#!/usr/bin/env bun import { HookManager, success, block, createLogger } from 'claude-hooks-sdk'; const logger = createLogger('my-hook'); const manager = new HookManager({ logEvents: true, clientId: 'my-hook', enableContextTracking: true, trackEdits: true, }); manager.onPreToolUse(async (input, context) => { if (input.tool_name === 'Bash' && input.tool_input.command.includes('rm -rf /')) { return block('Dangerous command blocked'); } return success(); }); manager.run();
Configuration Options
interface HookManagerOptions { // Logging logEvents?: boolean; clientId?: string; logDir?: string; debug?: boolean; // Context & Tracking enableContextTracking?: boolean; trackEdits?: boolean; // Error Handling blockOnFailure?: boolean; enableFailureQueue?: boolean; maxRetries?: number; handlerTimeout?: number; }
Logger Utility
import { createLogger } from 'claude-hooks-sdk'; const logger = createLogger('my-hook'); logger.info('Always shown'); logger.logDebug('Only shown when DEBUG=1'); logger.warn('Warning message'); logger.error(new Error('Something failed'));
Constants
import { DEFAULT_HANDLER_TIMEOUT_MS, // 30000 DEFAULT_MAX_RETRIES, // 3 EXIT_CODE_SUCCESS, // 0 EXIT_CODE_ERROR, // 1 EXIT_CODE_BLOCK, // 2 } from 'claude-hooks-sdk';
Transforms
ConversationLogger
import { ConversationLogger } from 'claude-hooks-sdk'; const conversationLogger = new ConversationLogger(); manager.onUserPromptSubmit((input) => { conversationLogger.recordUserPrompt(input); return success(); }); manager.onStop(async (input, context) => { const turn = await conversationLogger.recordStop(input, context); console.log(`Turn ${turn.turn_number} recorded`); return success(); });
FileChangeTracker
import { FileChangeTracker } from 'claude-hooks-sdk'; const fileTracker = new FileChangeTracker(); manager.onPostToolUse((input) => { fileTracker.recordChange(input); return success(); }); manager.onStop(async (input) => { const changes = fileTracker.getBatch(input.session_id); console.log(`${changes.total_files} files modified`); return success(); });
Hook Event Handlers
manager.onSessionStart(async (input, context) => { console.log('Session started:', input.session_id); return success(); }); manager.onUserPromptSubmit(async (input, context) => { // Add context to Claude's prompt return success(); }); manager.onPreToolUse(async (input, context) => { // Block dangerous operations if (shouldBlock(input)) { return block('Reason for blocking'); } return success(); }); manager.onPostToolUse(async (input, context) => { // React to tool completion return success(); }); manager.onStop(async (input, context) => { // Session ending - access context.editedFiles console.log('Edited files:', context.editedFiles); return success(); });
Context Tracking
interface EventContext { transactionId: string; conversationId: string; promptId?: string; project_dir?: string; git?: { user: string; email: string; repo: string; branch: string; commit: string; dirty: boolean; }; editedFiles?: string[]; // When trackEdits: true }
Session Context Injection
One-liner to inject session info into Claude's context:
#!/usr/bin/env bun import { createUserPromptSubmitHook } from 'claude-hooks-sdk'; createUserPromptSubmitHook();
With customization:
createUserPromptSubmitHook({ format: (name, id) => `Session: ${name} [${id.slice(0, 8)}]`, customContext: async (input) => `Branch: ${getBranch()}`, });
Failure Queue
const manager = new HookManager({ enableFailureQueue: true, maxRetries: 3, onErrorQueueNotEmpty: (size) => { console.warn(`${size} events queued for retry`); }, });
Blocking vs Non-Blocking
Non-blocking (default): Hook failures don't stop Claude Code
const manager = new HookManager({ blockOnFailure: false, });
Blocking: Hook failures stop Claude Code
const manager = new HookManager({ blockOnFailure: true, });
Common Patterns
API Integration
manager.onStop(async (input, context) => { await fetch('https://api.example.com/events', { method: 'POST', body: JSON.stringify({ event: input, context }), }); return success(); });
Command Blocking
manager.onPreToolUse(async (input) => { if (input.tool_name !== 'Bash') return success(); const dangerous = [/rm\s+-rf\s+\//, /dd\s+if=\/dev\/zero/]; for (const pattern of dangerous) { if (pattern.test(input.tool_input.command)) { return block('Blocked dangerous command'); } } return success(); });
File Change Notifications
manager.onStop(async (input, context) => { if (context.editedFiles?.length > 10) { await sendSlackMessage({ text: `Large changeset: ${context.editedFiles.length} files`, }); } return success(); });
Troubleshooting
Logs not appearing
- Check
is setlogEvents: true - Log path:
.claude/hooks/{clientId}/logs/events.jsonl
Context not enriched
- Verify
enableContextTracking: true - For git metadata: must be in a git repo
- For editedFiles:
requiredtrackEdits: true
Handler timeout
const manager = new HookManager({ handlerTimeout: 30000, // 30 seconds });