Developing Custom Hooks
Relevant source files
The following files were used as context for generating this wiki page:
Hooks are event-driven automations in the Everything Claude Code (ECC) ecosystem that execute before or after tool calls. They serve as critical guardrails for code quality, security, and workflow automation by intercepting the interaction loop between the user, the AI agent, and the local system tools.
Custom hooks allow developers to enforce specific project standards, such as file size limits, mandatory testing, or automatic code formatting. These automations are defined in a hooks.json file and can block execution, issue warnings, or perform background cleanup tasks depending on the lifecycle stage they target.
Sources: hooks/README.md:3-8, the-shortform-guide.md:39-41
Hook Architecture and Lifecycle
Hooks operate within a specific execution flow relative to tool calls. The system supports multiple trigger points throughout the lifecycle of a command execution.
Execution Flow
The following diagram illustrates how hooks intercept the tool execution process:
Hook Lifecycle Stages
| Stage | Trigger Timing | Primary Use Case |
|---|---|---|
| PreToolUse | Before the tool executes | Validation, blocking dangerous commands, reminders |
| PostToolUse | After the tool completes | Formatting, feedback loops, background analysis |
| Stop | After each Claude response | Final audits (e.g., checking for console.log), session state persistence |
| SessionStart | When a session begins | Loading context, environment detection |
| SessionEnd | When a session terminates | Lifecycle markers, cleanup, telemetry |
| PreCompact | Before context compaction | Saving state to prevent information loss |
Sources: hooks/README.md:12-21, the-shortform-guide.md:43-50, CONTRIBUTING.md:195-200
Implementing a Custom Hook
Custom hooks are essentially shell commands or scripts that process JSON data via stdin and return JSON via stdout.
Data Structure and Input Schema
Hooks receive a HookInput object. Developers must parse this JSON to access information about the tool being used and its arguments.
interface HookInput {
tool_name: string; // e.g., "Bash", "Edit", "Write"
tool_input: {
command?: string; // For Bash tools
file_path?: string; // For file-related tools
content?: string; // For Write tools
new_string?: string; // For Edit tools
};
tool_output?: { // Available only in PostToolUse
output?: string; // The result of the tool execution
};
}
Sources: hooks/README.md:144-159
Script Logic and Exit Codes
When writing a hook script (typically in JavaScript/Node.js for cross-platform compatibility), the exit code determines the system's behavior:
- Exit Code 0: Success. The execution continues normally.
- Exit Code 2: Block. In
PreToolUse, this prevents the tool from running. - Other Non-Zero: Error. The error is logged but execution is generally not blocked.
Sources: hooks/README.md:120-142, CONTRIBUTING.md:239-243
Example: File Size Guard
The following logic demonstrates a hook that blocks the creation of files exceeding 800 lines:
const fs = require('fs');
let data = '';
process.stdin.on('data', chunk => data += chunk);
process.stdin.on('end', () => {
const input = JSON.parse(data);
const content = input.tool_input?.content || '';
const lines = content.split('\n').length;
if (lines > 800) {
console.error('[Hook] BLOCKED: File exceeds 800 lines');
process.exit(2); // Block execution
}
console.log(data); // Pass through original data
});
Sources: hooks/README.md:173-181, rules/web/hooks.md:65-80
Configuration and Management
Hooks are managed via hooks.json. They use a "matcher" syntax to determine which tools or commands trigger the automation.
Matcher Syntax
Matchers allow for fine-grained control over when a hook executes.
| Pattern | Description |
|---|---|
tool == "Bash" | Matches only Bash tool calls |
tool_input.file_path matches "\\.tsx?$" | Matches edits to TypeScript files |
tool_input.command matches "npm install" | Matches specific shell commands |
Sources: CONTRIBUTING.md:218-228, the-shortform-guide.md:52-62
Runtime Control Profiles
The behavior of hooks can be adjusted using the ECC_HOOK_PROFILE environment variable:
- minimal: Essential lifecycle and safety hooks only.
- standard: Default balanced quality and safety checks.
- strict: Enables all guardrails and additional reminders.
Individual hooks can be suppressed by setting the ECC_DISABLED_HOOKS variable with a comma-separated list of hook IDs.
Sources: hooks/README.md:95-108
Best Practices for Web Projects
For web development, specific hooks are recommended to ensure environment stability and code health:
- Incremental Type Checking: Use
tsc --noEmit --incrementalinPostToolUseto avoid checking the entire project on every edit. - Dev Server Guard: Use a
PreToolUsehook to blocknpm run devcommands if not running insidetmux, ensuring log access is not lost. - Automatic Formatting: Trigger
prettier --writeoreslint --fixon modified files immediately after anEdittool completes. - Security Audits: Use
Stophooks to scan modified files forconsole.logor hardcoded secrets before the AI finalizes its response.
Sources: rules/web/hooks.md:13-56, hooks/README.md:46-77, the-shortform-guide.md:176-184
Summary
Developing custom hooks is the primary method for enforcing governance and quality in ECC. By leveraging the lifecycle stages—particularly PreToolUse for validation and PostToolUse for cleanup—developers can create a self-correcting development environment that reduces manual review overhead and prevents common errors from reaching the repository.