claude-code-ts-hooks
v2.1.2
Published
TypeScript types and utilities for Claude Code hooks - providing type safety for hook implementations
Maintainers
Readme
claude-code-ts-hooks
Cross-platform TypeScript types and utilities for Claude Code hooks - Providing type safety and runtime validation for hook implementations across Node.js, Deno, and Bun.
Overview
This package provides comprehensive TypeScript support for Claude Code hooks with cross-platform compatibility, enabling developers to write type-safe hook implementations that work seamlessly across Node.js, Deno, and Bun runtimes. It includes:
- Complete type definitions for all Claude Code hook events
- Cross-platform runtime detection and compatibility layer
- Runtime validation using Zod schemas
- Utility functions for creating and managing hooks
- Type guards for runtime type checking
- Helper functions for common hook patterns
- Zero-config usage with Deno and Bun (no build step required)
Installation & Usage
Naming note: On npm the package is published as
claude-code-ts-hooks(unscoped), while the Deno/JSR distribution remains scoped as@lucacri/claude-code-ts-hooks.
Node.js (NPM/Yarn/PNPM)
npm install claude-code-ts-hooks
# or
yarn add claude-code-ts-hooks
# or
pnpm add claude-code-ts-hooksRequirements:
- Node.js 18+
- TypeScript 5.0+ (for development)
Deno
Import the scoped package directly from JSR:
import { runHook, log, type HookHandlers } from 'jsr:@lucacri/claude-code-ts-hooks';Or use the source distribution when you need a specific revision:
import { runHook, log, type HookHandlers } from 'https://deno.land/x/claude_code_ts_hooks/src/index.ts';
// or from your local copy
import { runHook, log, type HookHandlers } from './src/index.ts';Requirements:
- Deno 1.0+
- Run with:
deno run --allow-read --allow-env your-hook.ts
Bun
Use directly from source:
bun run your-hook.tsimport { runHook, log, type HookHandlers } from './src/index.ts';Requirements:
- Bun 1.0+
Quick Start
Node.js Example
import { runHook, log, type HookHandlers } from 'claude-code-ts-hooks';
const handlers: HookHandlers = {
preToolUse: async (payload) => {
log(`About to use tool: ${payload.tool_name}`);
// Block dangerous tools
if (payload.tool_name === 'dangerous_tool') {
return {
decision: 'block',
reason: 'Tool not allowed'
};
}
return { decision: 'approve' };
},
stop: async (payload) => {
log('🎉 Task completed!');
return {};
}
};
runHook(handlers);Deno Example
Create hook.ts:
#!/usr/bin/env -S deno run --allow-read --allow-env
import { runHook, log, type HookHandlers } from './src/index.ts';
const handlers: HookHandlers = {
preToolUse: async (payload) => {
log(`About to use tool: ${payload.tool_name}`);
return { decision: 'approve' };
},
stop: async (payload) => {
log('🎉 Task completed!');
return {};
}
};
runHook(handlers);Run with:
deno run --allow-read --allow-env hook.tsBun Example
Create hook.ts:
#!/usr/bin/env bun
import { runHook, log, type HookHandlers } from './src/index.ts';
const handlers: HookHandlers = {
preToolUse: async (payload) => {
log(`About to use tool: ${payload.tool_name}`);
return { decision: 'approve' };
},
stop: async (payload) => {
log('🎉 Task completed!');
return {};
}
};
runHook(handlers);Run with:
bun run hook.tsCross-Platform Features
Runtime Detection
The library automatically detects the runtime environment and adapts accordingly:
import { detectRuntime, getArgs, getEnv } from 'claude-code-ts-hooks';
const runtime = detectRuntime(); // 'node' | 'deno' | 'bun' | 'unknown'
const args = getArgs(); // Cross-platform command line arguments
const envVar = getEnv('MY_VAR'); // Cross-platform environment variable accessPlatform-Specific Examples
- Node.js:
examples/simple-hooks.ts - Deno:
examples/simple-hooks-deno.ts - Bun:
examples/simple-hooks-bun.ts
Hook Types
Supported Hook Events
| Hook Event | Description | Input Type | Output Type |
|------------|-------------|------------|-------------|
| PreToolUse | Before tool execution | PreToolUseHookInput | PreToolUseHookOutput |
| PostToolUse | After tool execution | PostToolUseHookInput | PostToolUseHookOutput |
| Stop | When Claude finishes | StopHookInput | StopHookOutput |
| UserPromptSubmit | When user submits prompt | UserPromptSubmitHookInput | UserPromptSubmitHookOutput |
| Notification | System notifications | NotificationHookInput | NotificationHookOutput |
| SubagentStop | When subagent stops | SubagentStopHookInput | SubagentStopHookOutput |
| PreCompact | Before compaction | PreCompactHookInput | PreCompactHookOutput |
Hook Input Structure
All hook inputs extend BaseHookInput:
interface BaseHookInput {
session_id: string;
transcript_path: string;
hook_event_name: string;
}Example PreToolUseHookInput:
interface PreToolUseHookInput extends BaseHookInput {
hook_event_name: 'PreToolUse';
tool_name: string;
tool_input: Record<string, unknown>;
}Hook Output Structure
All hook outputs extend BaseHookOutput:
interface BaseHookOutput {
continue?: boolean;
stopReason?: string;
suppressOutput?: boolean;
}Usage Examples
1. Creating Hook Handlers
import { runHook, log, type HookHandlers } from 'claude-code-ts-hooks';
const handlers: HookHandlers = {
preToolUse: async (payload) => {
log(`🔧 About to use tool: ${payload.tool_name}`);
log(`📋 Tool parameters:`, payload.tool_input);
return { decision: 'approve', reason: 'Tool usage approved' };
},
postToolUse: async (payload) => {
log(`✅ Tool ${payload.tool_name} completed successfully`);
return {};
},
stop: async (payload) => {
log('🎉 Claude finished processing!');
// You could play a sound, send a notification, etc.
return {};
},
userPromptSubmit: async (payload) => {
log(`👤 User submitted: ${payload.prompt.substring(0, 50)}...`);
return { decision: 'approve' };
}
};
runHook(handlers);2. Hook with Conditional Logic
import { runHook, type HookHandlers } from 'claude-code-ts-hooks';
const handlers: HookHandlers = {
preToolUse: async (payload) => {
// Block dangerous tools
const dangerousTools = ['rm', 'delete', 'format'];
if (dangerousTools.some(tool => payload.tool_name.includes(tool))) {
return {
decision: 'block',
reason: `Tool ${payload.tool_name} is not allowed`
};
}
// Log tool usage for audit
console.log(`📊 Audit: Tool ${payload.tool_name} used at ${new Date().toISOString()}`);
return { decision: 'approve' };
}
};
runHook(handlers);3. Input Validation
import {
validateHookInput,
type PreToolUsePayload
} from 'claude-code-ts-hooks';
// Sample input
const sampleInput: PreToolUsePayload = {
session_id: 'session-123',
transcript_path: '/path/to/transcript',
hook_type: 'PreToolUse',
tool_name: 'write_file',
tool_input: { path: './file.txt', content: 'Hello!' }
};
// General validation
const result = validateHookInput(sampleInput);
if (result.success) {
console.log('Valid hook input:', result.data);
} else {
console.error('Invalid input:', result.error);
}4. Complete Example
#!/usr/bin/env bun
import { runHook, log, type HookHandlers } from 'claude-code-ts-hooks';
const handlers: HookHandlers = {
preToolUse: async (payload) => {
log(`🔧 About to use: ${payload.tool_name}`);
// Safety check
if (payload.tool_name.includes('rm')) {
return { decision: 'block', reason: 'Dangerous command blocked' };
}
return { decision: 'approve' };
},
postToolUse: async (payload) => {
log(`✅ Completed: ${payload.tool_name}`);
return {};
},
stop: async (payload) => {
log('🎉 All done!');
return {};
}
};
runHook(handlers);5. Integration with Claude Code
The hooks are designed to work as standalone scripts that Claude Code calls:
#!/usr/bin/env bun
import { runHook, type HookHandlers } from 'claude-code-ts-hooks';
const handlers: HookHandlers = {
preToolUse: async (payload) => {
// Your hook logic here
console.log(`Tool: ${payload.tool_name}`);
return { decision: 'approve' };
}
};
runHook(handlers);Save this as a .ts file and configure it in your Claude Code settings.
API Reference
Types
- Input Types:
PreToolUseHookInput,PostToolUseHookInput,StopHookInput, etc. - Output Types:
PreToolUseHookOutput,PostToolUseHookOutput,StopHookOutput, etc. - Handler Types:
HookHandler,HookHandlerFor,HookRegistry - Utility Types:
ToolInput,ToolResponse,HookEventName,HookConfig
Validation Functions
validateHookInput(input)- Validate any hook inputvalidateHookOutput(output)- Validate any hook outputparseHookInput(input, eventName)- Parse and validate specific event inputsafeParseJSON(jsonString)- Safely parse JSON with validation
Type Guards
isHookInputOfType(input, eventName)- Generic type guardisPreToolUseInput(input)- Specific type guards for each eventisHookEventName(value)- Check if value is valid event name
Helper Functions
createHookHandler(eventName, handler)- Create type-safe handlercreateHookRegistry(handlers)- Create handler registryexecuteHook(handler, input, context)- Execute with error handlingwithLogging(handler, logger)- Add logging to handlerwithTimeout(handler, timeout)- Add timeout to handlercreateHookOutput.success()- Create success outputcreateHookOutput.block(reason)- Create blocking output
TypeScript Configuration
This package is built with TypeScript 5.0+ and uses modern features. Make sure your tsconfig.json includes:
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "node",
"strict": true,
"esModuleInterop": true
}
}Distribution & Publishing
This package is designed to work seamlessly across different JavaScript runtimes:
NPM Registry (Node.js)
Published to NPM for Node.js users:
npm install claude-code-ts-hooksThe npm release uses the unscoped name
claude-code-ts-hooks; earlier planning documents referenced a scoped variant, but the first public npm publish will be unscoped.
JSR (Deno)
The package can be published to JSR (JavaScript Registry) for Deno:
# Publish to JSR
deno publishDeno users can import directly using the scoped name:
import { runHook } from "jsr:@lucacri/claude-code-ts-hooks";Direct Source Usage
For maximum compatibility, users can clone this repository and import directly from source:
git clone https://github.com/lucacri/claude-code-ts-hooks.git
cd claude-code-ts-hooksThen import from source:
// Deno
import { runHook } from "./src/index.ts";
// Bun
import { runHook } from "./src/index.ts";
// Node.js (with TypeScript)
import { runHook } from "./src/index.ts";Development
Pre-commit Hooks
This project uses pre-commit hooks to ensure code quality. The hooks are automatically installed when you run npm install.
The pre-commit hook runs:
- Branch sync check (ensures local branch is up-to-date with
origin/main) - TypeScript type checking (
npm run typecheck) - ESLint linting (
npm run lint) - Vitest tests (
npm test) - Build verification (
npm run build) - JSR validation (slow types check)
Manual Setup:
npm run setup-hooksSkip Hooks (Emergency Only):
git commit --no-verify⚠️ Note: Skipping hooks should be avoided as it may push broken code to CI/CD.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
If you touch the build system, follow the Rollup patch maintenance guide to keep the patch-package fallback in sync with upstream releases.
License
MIT License - see the LICENSE file for details.
Related
- Claude Code SDK - Official Claude Code TypeScript SDK
- Zod - TypeScript-first schema validation library
Made with ❤️ for the Claude Code community
