claude-hook-sdk
v1.1.0
Published
TypeScript SDK for building Claude Code hooks
Maintainers
Readme
Claude Hook SDK (TypeScript)
A TypeScript SDK for building Claude Code hooks, providing a clean and fluent API for handling hook events and responses.
Installation
npm install claude-hook-sdkQuick Start
Basic Usage
import { createHook } from 'claude-hook-sdk'
// Create a hook from stdin
const hook = await createHook()
// Build a response
hook.response()
.approve('Tool usage approved')
.continue()Hook Types
The SDK supports all Claude Code hook events:
- PreToolUse: Executed before tool calls
- PostToolUse: Executed after tool calls
- Notification: Handles notification events
- Stop: Handles stop events
- SubagentStop: Handles subagent stop events
Example Hook Script
#!/usr/bin/env node
import { createHook, PreToolUseHook } from 'claude-hook-sdk'
async function main() {
const hook = await createHook()
if (hook instanceof PreToolUseHook) {
const toolName = hook.toolName()
const toolInput = hook.toolInput()
// Block dangerous operations
if (toolName === 'Bash' && toolInput.command?.includes('rm -rf')) {
hook.response()
.block('Dangerous command blocked')
.continue()
return
}
}
// Default: approve
hook.response()
.approve('Tool usage approved')
.continue()
}
main()API Reference
Factory Functions
createHook(): Promise<AnyHook>- Create hook from stdincreateHookFromString(json: string): AnyHook- Create hook from JSON stringcreateHookFromPayload(payload: HookPayload): AnyHook- Create hook from parsed payload
Hook Classes
Base Hook Class
All hooks inherit from the base Hook class:
abstract class Hook {
getSessionId(): string
getTranscriptPath(): string
getRawData(): HookPayload
transcript(): any[]
response(): ResponseBuilder
error(message: string): never
success(message: string): never
abstract eventName(): string
}PreToolUseHook
class PreToolUseHook extends Hook {
toolName(): string
toolInput(): Record<string, any>
toolInput<T>(key: string): T | undefined
toolInput<T>(key: string, defaultValue: T): T
estimatedTokens(): number
}PostToolUseHook
class PostToolUseHook extends Hook {
toolName(): string
toolInput(): Record<string, any>
toolInput<T>(key: string): T | undefined
toolInput<T>(key: string, defaultValue: T): T
toolResponse(): Record<string, any>
toolResponse<T>(key: string): T | undefined
toolResponse<T>(key: string, defaultValue: T): T
}NotificationHook
class NotificationHook extends Hook {
message(): string
title(): string
}StopHook & SubagentStopHook
class StopHook extends Hook {
stopHookActive(): boolean
}
class SubagentStopHook extends Hook {
stopHookActive(): boolean
}ResponseBuilder
Fluent API for building hook responses:
class ResponseBuilder {
approve(reason?: string): this
block(reason: string): this
suppressOutput(): this
merge(fields: Record<string, any>): this
continue(): never // Exits with code 0
stop(reason: string): never // Exits with code 1
}CLI Tools
Basic Hook Runner
echo '{"session_id":"test","transcript_path":"/tmp/test.jsonl","hook_event_name":"Notification","message":"Hello"}' | claude-hookRouters
The SDK includes multiple routing options:
Deterministic Router (Legacy)
# Basic usage (defaults to Gemini CLI routing)
deterministic-router
# Configurable via environment variables
ROUTER_TARGET=my-cli ROUTER_NAME="My CLI" deterministic-routerConfigurable Router (Recommended)
# Gemini router (default)
ROUTER_TYPE=gemini configurable-router
# Claude router
ROUTER_TYPE=claude configurable-router
# Multi-model router
ROUTER_TYPE=multi configurable-router
# Custom router
ROUTER_TYPE=custom ROUTER_TARGET=my-tool ROUTER_NAME="My Tool" configurable-routerRouter Configuration
Environment variables for customization:
ROUTER_TYPE: Router type (gemini,claude,multi,custom)ROUTER_TARGET: Target delegate name (e.g.,gemini-cli,claude-opus)ROUTER_NAME: Human-readable name for routing decisionsROUTER_MAX_FILE_SIZE: Maximum file size in bytes (default: 10MB)ROUTER_MAX_FILES: Maximum number of files (default: 3)ROUTER_MAX_TOKENS: Maximum estimated tokens (default: 50000)
Programmatic Router Usage
import { ConfigurableRouter, PreToolUseHook } from 'claude-hook-sdk'
// Create a custom router
const router = new ConfigurableRouter({ maxFiles: 5, maxTokens: 100000 })
.addSizeBasedRule('Large Model', 'claude-opus')
.addToolBasedRule('Code Assistant', 'claude-sonnet', ['Edit', 'Write'])
.addCustomRule('Special Case', 'custom-tool', (hook) => {
return hook.toolInput('special') === true
})
// Use with a hook
if (hook instanceof PreToolUseHook) {
router.route(hook)
}
// Or evaluate without routing
const result = router.evaluate(hook)
if (result.shouldRoute) {
console.log(`Would route to: ${result.rule?.target}`)
}Configuration
Example Claude Code settings:
{
"hooks": [
{
"event": "PreToolUse",
"matcher": "mcp__gemini-cli__.*",
"command": "deterministic-router"
},
{
"event": "PreToolUse",
"matcher": ".*",
"command": "configurable-router",
"env": {
"ROUTER_TYPE": "multi",
"ROUTER_MAX_FILES": "5"
}
},
{
"event": "PostToolUse",
"matcher": ".*",
"command": "claude-hook"
}
]
}Testing
npm test # Run tests
npm run test:watch # Watch mode
npm run test:coverage # Coverage reportDevelopment
npm run build # Build TypeScript
npm run dev # Watch modeLicense
MIT
