Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions .claude/hooks/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import {
defineHooks,
defineHook,
logPreToolUseEvents,
logPostToolUseEvents,
logStopEvents,
} from "../..";
import { defineHooks } from '../..';
import { logPreToolUseEvents, logPostToolUseEvents } from '../../src/hooks/logToolUseEvents';
import { blockEnvFiles } from '../../src/hooks/blockEnvFiles';
import { logStopEvents } from '../../src/hooks/logStopEvents';

export default defineHooks({
PreToolUse: [logPreToolUseEvents({ maxEventsStored: 100 })],
PostToolUse: [logPostToolUseEvents({ maxEventsStored: 100 })],
Stop: [logStopEvents({ maxEventsStored: 100 })],
});
defineHooks({
PreToolUse: [
logPreToolUseEvents(),
blockEnvFiles
],
PostToolUse: [logPostToolUseEvents()],
Stop: [logStopEvents()]
});
15 changes: 12 additions & 3 deletions .claude/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,16 @@
"hooks": [
{
"type": "command",
"command": "test -f \"./.hooks/hooks.js\" && node \"./.hooks/hooks.js\" __run_hook PreToolUse \".*\" \"0\" || (>&2 echo \"Error: Hook script not found at ./.hooks/hooks.js\" && >&2 echo \"Please run: npx define-claude-code-hooks\" && exit 1) # __managed_by_define_claude_code_hooks__"
"command": "test -f \"./.claude/hooks/hooks.ts\" && npx ts-node \"./.claude/hooks/hooks.ts\" __run_hook PreToolUse \".*\" \"0\" || (>&2 echo \"Error: Hook script not found at ./.claude/hooks/hooks.ts\" && >&2 echo \"Please run: npx define-claude-code-hooks\" && exit 1) # __managed_by_define_claude_code_hooks__"
}
]
},
{
"matcher": "Read|Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "test -f \"./.claude/hooks/hooks.ts\" && npx ts-node \"./.claude/hooks/hooks.ts\" __run_hook PreToolUse \"Read|Write|Edit|MultiEdit\" \"1\" || (>&2 echo \"Error: Hook script not found at ./.claude/hooks/hooks.ts\" && >&2 echo \"Please run: npx define-claude-code-hooks\" && exit 1) # __managed_by_define_claude_code_hooks__"
}
]
}
Expand All @@ -17,7 +26,7 @@
"hooks": [
{
"type": "command",
"command": "test -f \"./.hooks/hooks.js\" && node \"./.hooks/hooks.js\" __run_hook PostToolUse \".*\" \"0\" || (>&2 echo \"Error: Hook script not found at ./.hooks/hooks.js\" && >&2 echo \"Please run: npx define-claude-code-hooks\" && exit 1) # __managed_by_define_claude_code_hooks__"
"command": "test -f \"./.claude/hooks/hooks.ts\" && npx ts-node \"./.claude/hooks/hooks.ts\" __run_hook PostToolUse \".*\" \"0\" || (>&2 echo \"Error: Hook script not found at ./.claude/hooks/hooks.ts\" && >&2 echo \"Please run: npx define-claude-code-hooks\" && exit 1) # __managed_by_define_claude_code_hooks__"
}
]
}
Expand All @@ -27,7 +36,7 @@
"hooks": [
{
"type": "command",
"command": "test -f \"./.hooks/hooks.js\" && node \"./.hooks/hooks.js\" __run_hook Stop || (>&2 echo \"Error: Hook script not found at ./.hooks/hooks.js\" && >&2 echo \"Please run: npx define-claude-code-hooks\" && exit 1) # __managed_by_define_claude_code_hooks__"
"command": "test -f \"./.claude/hooks/hooks.ts\" && npx ts-node \"./.claude/hooks/hooks.ts\" __run_hook Stop || (>&2 echo \"Error: Hook script not found at ./.claude/hooks/hooks.ts\" && >&2 echo \"Please run: npx define-claude-code-hooks\" && exit 1) # __managed_by_define_claude_code_hooks__"
}
]
}
Expand Down
115 changes: 85 additions & 30 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,61 +4,72 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

## Project Overview

This is a TypeScript library that provides type-safe hook definitions for Claude Code with automatic settings management. It allows users to define hooks that run at various points in Claude Code's lifecycle without manually editing settings.json files. Hook files are automatically compiled from TypeScript to JavaScript for execution.
This is a TypeScript library that provides type-safe hook definitions for Claude Code with automatic settings management. It allows users to define hooks that run at various points in Claude Code's lifecycle without manually editing settings.json files. Hook files are executed directly using ts-node without compilation.

## Build Commands
## Development Commands

```bash
# Install dependencies
bun install

# Build the TypeScript project
bun run build

# Watch mode for development
bun run dev

# Install dependencies
bun install
# Run tests
bun run test:run

# Run tests with coverage
bun run test:coverage

# Type checking / linting
bun run typecheck
```

## Architecture

### Key Concepts

1. **Self-Executing Hooks**: Hook files act as both the hook definition and the runner. When `defineHooks` is called, it checks if it's being run as a CLI and handles two modes:

- `__generate_settings`: Outputs JSON information about defined hooks
- `__run_hook`: Executes the appropriate hook handler

2. **No Separate Runner**: Unlike traditional approaches, there's no separate runner.js. The compiled hooks file itself is executed directly by the generated commands in settings.json.
2. **No Separate Runner**: Unlike traditional approaches, there's no separate runner.js. The hooks file itself is executed directly by the generated commands in settings.json using ts-node.

3. **Automatic Compilation**: The CLI compiles TypeScript hook files to JavaScript in the `.hooks` directory:
- TypeScript hooks are compiled on-the-fly when running the CLI
- Compiled JavaScript files are placed in `.hooks/` (gitignored)
- No need for ts-node at runtime - hooks run as pure JavaScript
3. **Direct TypeScript Execution**: The CLI generates commands that use ts-node to execute TypeScript hooks directly:
- No compilation step needed
- TypeScript hooks are executed directly using ts-node
- No intermediate JavaScript files are generated

4. **Smart Settings Generation**: The CLI only creates settings entries for hooks that are actually defined:
- For PreToolUse/PostToolUse: One entry per matcher
- For other hooks (Stop, Notification, SubagentStop): One entry only if handlers exist
- Commands use `npx ts-node` to execute the TypeScript hooks directly

5. **Multiple Hook Files**: The system supports two different hook files, all located in `.claude/hooks/`:
- `hooks.ts` - Project hooks (compiles to `.hooks/hooks.js`, updates `.claude/settings.json`)
- `hooks.local.ts` - Local hooks (compiles to `.hooks/hooks.local.js`, updates `.claude/settings.local.json`)

The CLI automatically detects which files exist, compiles them, and updates the corresponding settings files.
- `hooks.ts` - Project hooks (updates `.claude/settings.json`)
- `hooks.local.ts` - Local hooks (updates `.claude/settings.local.json`)

### Core Components

- **src/index.ts**: Exports `defineHooks` and `defineHook` functions. Contains the self-execution logic that makes hooks files act as their own runners.

- **src/cli.ts**: The CLI that compiles TypeScript hooks to JavaScript and updates settings.json files. It automatically detects which hook files exist (hooks.ts, hooks.local.ts), compiles them to `.hooks/`, and updates the corresponding settings files.
- **src/cli.ts**: The CLI that updates settings.json files to execute TypeScript hooks using ts-node. Includes an interactive `--init` command for project setup.

- **src/types.ts**: TypeScript type definitions for all hook types, inputs, and outputs. Key distinction between tool hooks (PreToolUse/PostToolUse) that have matchers and non-tool hooks.

- **src/hooks/**: Predefined hook utilities for common logging scenarios (logToolUseEvents, logStopEvents, logNotificationEvents)
- **src/hooks/**: Predefined hook utilities:
- `logToolUseEvents.ts`: Logs PreToolUse and PostToolUse events to JSON files
- `logStopEvents.ts`: Logs Stop and SubagentStop events
- `logNotificationEvents.ts`: Logs notification events
- `blockEnvFiles.ts`: Security hook that blocks access to .env files
- `announceHooks.ts`: Text-to-speech announcements for various events

### Hook Definition Structure

Tool hooks (PreToolUse/PostToolUse):

```typescript
{
matcher: string, // Regex pattern for tool names
Expand All @@ -67,7 +78,6 @@ Tool hooks (PreToolUse/PostToolUse):
```

Non-tool hooks (Stop, Notification, SubagentStop):

```typescript
async (input) => {
/* ... */
Expand All @@ -80,11 +90,32 @@ The CLI removes all hooks marked with `__managed_by_define_claude_code_hooks__`

```json
{
"command": "node \"./.hooks/hooks.js\" __run_hook PreToolUse \"Bash\" \"0\" # __managed_by_define_claude_code_hooks__"
"command": "npx ts-node \"./.claude/hooks/hooks.ts\" __run_hook PreToolUse \"Bash\" \"0\" # __managed_by_define_claude_code_hooks__"
}
```

Note that commands reference the compiled JavaScript files in `.hooks/`, not the original TypeScript files.
Commands use `npx ts-node` to execute the TypeScript files directly.

## Testing

The project uses Vitest for testing with comprehensive coverage:

```bash
# Run a single test file
bun run test src/hooks/__tests__/logToolUseEvents.test.ts

# Run tests in watch mode
bun run test

# Run tests with coverage report
bun run test:coverage
```

Test structure:
- Each hook has corresponding tests in `src/hooks/__tests__/`
- Test utilities in `src/__tests__/` (fixtures, mockFs, testUtils)
- Global test setup in `src/__tests__/setup.ts`
- Coverage configuration in `vitest.config.mjs`

## Development Notes

Expand All @@ -93,17 +124,10 @@ Note that commands reference the compiled JavaScript files in `.hooks/`, not the
- Hook source files are all located in `.claude/hooks/`:
- `hooks.ts` (project-wide hooks)
- `hooks.local.ts` (local-only hooks, not committed to git)
- Compiled JavaScript files are generated in `.hooks/`:
- `.hooks/hooks.js`
- `.hooks/hooks.local.js`
- The `.hooks/` directory is gitignored
- Compatible with npm, yarn, pnpm, and bun package managers
- The CLI no longer requires flags - it automatically detects which hook files exist, compiles them, and updates the appropriate settings files
- No runtime dependency on ts-node - hooks execute as pure JavaScript

### Testing

When testing the package, create test repositories in the `tmp/` folder in the project root. This folder is already in `.gitignore` so test projects won't be committed.
- The CLI automatically detects which hook files exist and updates the appropriate settings files
- Requires ts-node as a dependency for executing TypeScript hooks
- When testing the package, create test repositories in the `tmp/` folder (already in `.gitignore`)

## Release Process

Expand Down Expand Up @@ -153,3 +177,34 @@ For testing new features before a stable release:
The package is published under the `@timoaus` scope as `@timoaus/define-claude-code-hooks`.

Note: The repository must have the `NPM_TOKEN` secret configured for publishing.

## CLI Features

### Interactive Initialization

The CLI provides an `--init` command for easy project setup:

```bash
define-claude-code-hooks --init
```

This command:
- Uses interactive prompts to select predefined hooks
- Automatically installs the package if needed (including ts-node)
- Detects the package manager (npm/yarn/pnpm/bun)
- Adds a `claude:hooks` script to package.json
- Creates initial hook files with selected hooks

### Automatic Hook Detection

The CLI automatically:
- Scans for hook files in `.claude/hooks/`
- Generates commands that use ts-node to execute the TypeScript files
- Updates corresponding settings files
- Preserves user-defined hooks while managing its own

# important-instruction-reminders
Do what has been asked; nothing more, nothing less.
NEVER create files unless they're absolutely necessary for achieving your goal.
ALWAYS prefer editing an existing file to creating a new one.
NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.
Loading