Agent skill
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 this agent skill to your Project
npx add-skill https://github.com/hgeldenhuys/claude-hooks-sdk/tree/main/.claude-plugin/plugins/skill-claude-hooks-sdk
SKILL.md
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
logEvents: trueis set - 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:
trackEdits: truerequired
Handler timeout
const manager = new HookManager({
handlerTimeout: 30000, // 30 seconds
});
Resources
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
claude-code-hooks
Guide for implementing Claude Code hooks - automated scripts that execute at specific workflow points. Use when building hooks, understanding hook events, or troubleshooting hook configuration.
handoff
Compact the current conversation into a handoff document for another agent to pick up.
git-guardrails-claude-code
Set up Claude Code hooks to block dangerous git commands (push, reset --hard, clean, branch -D, etc.) before they execute. Use when user wants to prevent destructive git operations, add git safety hooks, or block git push/reset in Claude Code.
obsidian-vault
Search, create, and manage notes in the Obsidian vault with wikilinks and index notes. Use when user wants to find, create, or organize notes in Obsidian.
setup-pre-commit
Set up Husky pre-commit hooks with lint-staged (Prettier), type checking, and tests in the current repo. Use when user wants to add pre-commit hooks, set up Husky, configure lint-staged, or add commit-time formatting/typechecking/testing.
edit-article
Edit and improve articles by restructuring sections, improving clarity, and tightening prose. Use when user wants to edit, revise, or improve an article draft.
Didn't find tool you were looking for?