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.

Stars 163
Forks 31

Install this agent skill to your Project

npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/development/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

bash
bun add claude-hooks-sdk
# or
npm install claude-hooks-sdk

Basic Setup

typescript
#!/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

typescript
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

typescript
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

typescript
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

typescript
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

typescript
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

typescript
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

typescript
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:

typescript
#!/usr/bin/env bun
import { createUserPromptSubmitHook } from 'claude-hooks-sdk';

createUserPromptSubmitHook();

With customization:

typescript
createUserPromptSubmitHook({
  format: (name, id) => `Session: ${name} [${id.slice(0, 8)}]`,
  customContext: async (input) => `Branch: ${getBranch()}`,
});

Failure Queue

typescript
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

typescript
const manager = new HookManager({
  blockOnFailure: false,
});

Blocking: Hook failures stop Claude Code

typescript
const manager = new HookManager({
  blockOnFailure: true,
});

Common Patterns

API Integration

typescript
manager.onStop(async (input, context) => {
  await fetch('https://api.example.com/events', {
    method: 'POST',
    body: JSON.stringify({ event: input, context }),
  });
  return success();
});

Command Blocking

typescript
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

typescript
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: true is 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: true required

Handler timeout

typescript
const manager = new HookManager({
  handlerTimeout: 30000,  // 30 seconds
});

Resources

Didn't find tool you were looking for?

Be as detailed as possible for better results