npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@timoaus/define-claude-code-hooks

v1.4.6

Published

Type-safe hook definitions for Claude Code

Downloads

9

Readme

define-claude-code-hooks

Type-safe hook definitions for Claude Code with automatic settings management.

Quick Start

Option 1: Use the Interactive Init Command (Recommended)

npx @timoaus/define-claude-code-hooks --init

This interactive command will:

  • Let you choose between project or local hooks
  • Install predefined hooks (logging, security, announcements)
  • Install the package as a dev dependency
  • Add the claude:hooks script to your package.json
  • Set up your hooks automatically

Option 2: Manual Setup

1. Install the package

npm install --save-dev @timoaus/define-claude-code-hooks
# or
yarn add --dev @timoaus/define-claude-code-hooks
# or
pnpm add --save-dev @timoaus/define-claude-code-hooks
# or
bun add --dev @timoaus/define-claude-code-hooks

2. Add a package.json script

Add this script to your package.json to easily update your hooks:

{
  "scripts": {
    "claude:hooks": "define-claude-code-hooks"
  }
}

3. Create a simple hook

You can create hooks in two different files within .claude/hooks/:

  • hooks.ts - Project hooks (updates .claude/settings.json)
  • hooks.local.ts - Local hooks (updates .claude/settings.local.json)

For example, create .claude/hooks/hooks.ts:

import { defineHooks } from "@timoaus/define-claude-code-hooks";

const preventEditingEnvFile = defineHook("PreToolUse", {
  matcher: "Write|Edit|MultiEdit",
  handler: async (input) => {
    const filePath = input.tool_input.file_path;
    if (filePath && filePath.endsWith(".env")) {
      return {
        decision: "block",
        reason:
          "Direct editing of .env files is not allowed for security reasons",
      };
    }
  },
});

export default defineHooks({
  PreToolUse: [preventEditingEnvFile],
});

4. Add a predefined hook

Extend your hooks with built-in logging utilities:

import {
  defineHooks,
  logPreToolUseEvents,
} from "@timoaus/define-claude-code-hooks";

export default defineHooks({
  PreToolUse: [logPreToolUseEvents({ maxEventsStored: 100 })],
});

Creates a log file in your project root: hook-log.tool-use.json

5. Activate your hooks

Run the script to update your settings:

npm run claude:hooks

The CLI will automatically detect which hook files exist and update the corresponding settings files. Your hooks are now active! Claude Code will respect your rules and log tool usage.

Predefined Hooks

The library includes several predefined hook utilities for common logging scenarios:

| Hook Function | Options | |--------------|---------| | logPreToolUseEventsLogs tool uses before execution | • Optional first param: matcher (regex pattern, defaults to '.' for all tools)maxEventsStored (default: 100)logFileName (default: 'hook-log.tool-use.json')includeToolInput (default: true) | | logPostToolUseEventsLogs tool uses after execution | • Optional first param: matcher (regex pattern, defaults to '.' for all tools)maxEventsStored (default: 100)logFileName (default: 'hook-log.tool-use.json')includeToolInput (default: true)includeToolResponse (default: true) | | logStopEventsLogs main agent stop events | • maxEventsStored (default: 100)logFileName (default: 'hook-log.stop.json') | | logSubagentStopEventsLogs subagent stop events | • maxEventsStored (default: 100)logFileName (default: 'hook-log.stop.json') | | logNotificationEventsLogs notification messages | • maxEventsStored (default: 100)logFileName (default: 'hook-log.notification.json') | | blockEnvFilesBlocks access to .env files | No options - blocks all .env file variants except example files | | announceStopAnnounces task completion via TTS | • message (default: 'Task completed')voice (system-specific voice name)rate (speech rate in WPM)customCommand (custom TTS command)suppressOutput (default: false) | | announceSubagentStopAnnounces subagent completion via TTS | Same options as announceStop | | announcePreToolUseAnnounces before tool execution | • First param: matcher (regex pattern, defaults to '.')message (default: 'Using {toolName}')voice, rate, customCommand, suppressOutput | | announcePostToolUseAnnounces after tool execution | • First param: matcher (regex pattern, defaults to '.')message (default: '{toolName} completed')voice, rate, customCommand, suppressOutput | | announceNotificationSpeaks notification messages | • message (default: '{message}')voice, rate, customCommand, suppressOutput |

All predefined hooks:

  • Create JSON log files in your current working directory
  • Automatically rotate logs when reaching maxEventsStored limit (keeping most recent events)
  • Include timestamps, session IDs, and transcript paths in log entries
  • Handle errors gracefully without interrupting Claude Code

Example usage:

import {
  defineHooks,
  logPreToolUseEvents,
  logStopEvents,
} from "@timoaus/define-claude-code-hooks";

export default defineHooks({
  PreToolUse: [
    logPreToolUseEvents({ maxEventsStored: 200, logFileName: "my-tools.json" }),
  ],
  Stop: [logStopEvents()],
});

Full Usage Guide

1. Create a hook file

Choose where to create your hooks based on your needs (all in .claude/hooks/):

  • hooks.ts - Project-wide hooks (committed to git)
  • hooks.local.ts - Local-only hooks (not committed)

Example:

import { defineHooks } from "define-claude-code-hooks";

export default defineHooks({
  PreToolUse: [
    // Block grep commands and suggest ripgrep
    {
      matcher: "Bash",
      handler: async (input) => {
        if (input.tool_input.command?.includes("grep")) {
          return {
            decision: "block",
            reason: "Use ripgrep (rg) instead of grep for better performance",
          };
        }
      },
    },

    // Log all file writes
    {
      matcher: "Write|Edit|MultiEdit",
      handler: async (input) => {
        console.error(`Writing to file: ${input.tool_input.file_path}`);
      },
    },
  ],

  PostToolUse: [
    // Format TypeScript files after editing
    {
      matcher: "Write|Edit",
      handler: async (input) => {
        if (input.tool_input.file_path?.endsWith(".ts")) {
          const { execSync } = require("child_process");
          execSync(`prettier --write "${input.tool_input.file_path}"`);
        }
      },
    },
  ],

  Notification: [
    // Custom notification handler
    async (input) => {
      console.log(`Claude says: ${input.message}`);
    },
  ],
});

2. Update your Claude Code settings:

# Automatically detect and update all hook files
npx define-claude-code-hooks

# Remove all managed hooks
npx define-claude-code-hooks --remove

# Use a custom global settings path (if not in ~/.claude/settings.json)
npx define-claude-code-hooks --global-settings-path /path/to/settings.json

The CLI automatically detects which hook files exist and updates the corresponding settings:

  • hooks.ts.claude/settings.json (project settings, relative paths)
  • hooks.local.ts.claude/settings.local.json (local settings, relative paths)

API

defineHooks(hooks: HookDefinition)

Define multiple hooks. Returns the hook definition object.

  • For PreToolUse and PostToolUse: pass an array of objects with matcher and handler
  • For other hooks: pass an array of handler functions

defineHook(type: HookType, definition)

Define a single hook (for advanced use cases).

  • For PreToolUse and PostToolUse: pass an object with matcher and handler
  • For other hooks: pass just the handler function

Example:

// Tool hook
const bashHook = defineHook("PreToolUse", {
  matcher: "Bash",
  handler: async (input) => {
    /* ... */
  },
});

// Non-tool hook
const stopHook = defineHook("Stop", async (input) => {
  /* ... */
});

Hook Types

  • PreToolUse: Runs before tool execution, can block or approve
  • PostToolUse: Runs after tool execution
  • Notification: Handles Claude Code notifications
  • Stop: Runs when main agent stops
  • SubagentStop: Runs when subagent stops

Hook Outputs

Hooks can return structured responses:

interface HookOutput {
  // Common fields
  continue?: boolean; // Whether Claude should continue
  stopReason?: string; // Message when continue is false
  suppressOutput?: boolean; // Hide output from transcript

  // PreToolUse specific
  decision?: "approve" | "block";
  reason?: string; // Reason for decision
}

How It Works

  1. The CLI scans for hook files (hooks.ts, hooks.local.ts)
  2. For each file found, it updates the corresponding settings.json with commands that use ts-node to execute TypeScript directly
  3. Marks managed hooks so they can be safely removed later

TypeScript Support

This library is written in TypeScript and provides full type safety for all hook inputs and outputs.

Predefined Hook Utilities

The library includes several predefined hook utilities for common logging scenarios:

Stop Event Logging

import {
  defineHooks,
  logStopEvents,
  logSubagentStopEvents,
} from "@timoaus/define-claude-code-hooks";

export default defineHooks({
  Stop: [logStopEvents("hook-log.stop.json")],
  SubagentStop: [logSubagentStopEvents("hook-log.subagent.json")],
});

Notification Logging

import {
  defineHooks,
  logNotificationEvents,
} from "@timoaus/define-claude-code-hooks";

export default defineHooks({
  Notification: [logNotificationEvents("hook-log.notifications.json")],
});

Tool Use Logging

import {
  defineHooks,
  logPreToolUseEvents,
  logPostToolUseEvents,
} from "@timoaus/define-claude-code-hooks";

export default defineHooks({
  // Log all tool use
  PreToolUse: logPreToolUseEvents(), // Logs all tools by default
  PostToolUse: logPostToolUseEvents(), // Logs all tools by default
});

// Or log specific tools only
export default defineHooks({
  PreToolUse: logPreToolUseEvents("Bash|Write|Edit", {
    maxEventsStored: 200,
    logFileName: "tool-use.json",
  }),
  PostToolUse: logPostToolUseEvents("Bash|Write|Edit", {
    maxEventsStored: 200,
    logFileName: "tool-use.json",
  }),
});

Environment File Protection

import {
  defineHooks,
  blockEnvFiles,
} from "@timoaus/define-claude-code-hooks";

export default defineHooks({
  PreToolUse: [
    blockEnvFiles, // Blocks access to .env files while allowing .env.example
  ],
});

The blockEnvFiles hook:

  • Blocks reading or writing to .env files and variants (.env.local, .env.production, etc.)
  • Allows access to example env files (.env.example, .env.sample, .env.template, .env.dist)
  • Works with Read, Write, Edit, and MultiEdit tools
  • Provides clear error messages when access is blocked

Text-to-Speech Announcements

import {
  defineHooks,
  announceStop,
  announceSubagentStop,
  announcePreToolUse,
  announcePostToolUse,
  announceNotification,
} from "@timoaus/define-claude-code-hooks";

// Basic usage - announce all events
export default defineHooks({
  Stop: [announceStop()],
  SubagentStop: [announceSubagentStop()],
  PreToolUse: [announcePreToolUse()], // Announces all tools
  PostToolUse: [announcePostToolUse()], // Announces all tools
  Notification: [announceNotification()],
});

// Announce specific tools only
export default defineHooks({
  PreToolUse: [
    announcePreToolUse('Bash|Write|Edit', {
      message: "Running {toolName}"
    })
  ],
  PostToolUse: [
    announcePostToolUse('Bash|Write|Edit', {
      message: "{toolName} finished"
    })
  ],
});

// With custom voices and messages
export default defineHooks({
  Stop: [
    announceStop({
      message: "Claude has finished the task for session {sessionId}",
      voice: "Samantha", // macOS voice
      rate: 200,
    })
  ],
  Notification: [
    announceNotification({
      message: "Claude says: {message}",
      voice: "Daniel"
    })
  ],
});

// With custom TTS command (for Linux/Windows)
export default defineHooks({
  Stop: [
    announceStop({
      customCommand: "espeak -s 150 '{message}'", // Linux
      // or for Windows PowerShell:
      // customCommand: "powershell -Command \"(New-Object -ComObject SAPI.SpVoice).Speak('{message}')\""
    })
  ],
});

The announcement hooks:

  • Use text-to-speech to announce various Claude Code events
  • Support macOS (say), Linux (espeak), and Windows (PowerShell SAPI)
  • Allow custom messages with template variables:
    • {sessionId} - The session ID
    • {timestamp} - Current timestamp
    • {toolName} - Tool name (for PreToolUse/PostToolUse)
    • {message} - Notification message (for Notification hook)
  • Support voice selection and speech rate customization
  • Can use custom TTS commands for other systems
  • Run asynchronously without blocking Claude Code

Combining Multiple Hooks

import {
  defineHooks,
  logStopEvents,
  logPreToolUseEvents,
  logPostToolUseEvents,
  blockEnvFiles,
  announceStop,
} from "@timoaus/define-claude-code-hooks";

export default defineHooks({
  PreToolUse: [
    blockEnvFiles, // Security: prevent .env file access
    logPreToolUseEvents({ logFileName: "hook-log.tool-use.json" }),
    // Add your custom hooks here
    {
      matcher: "Bash",
      handler: async (input) => {
        // Custom logic
      },
    },
  ],

  PostToolUse: logPostToolUseEvents({ logFileName: "hook-log.tool-use.json" }),

  Stop: [
    logStopEvents("hook-log.stop.json"),
    announceStop({ message: "Task completed successfully!" }),
  ],
});

Log File Format

The predefined hooks create JSON log files with the following structure:

[
  {
    "timestamp": "2025-01-07T10:30:00.000Z",
    "event": "PreToolUse",
    "sessionId": "abc-123",
    "transcriptPath": "/path/to/transcript.jsonl",
    "toolName": "Bash",
    "toolInput": {
      "command": "ls -la"
    }
  }
]