Claude Code Hook System¶
This directory contains the hook system for Claude Code, which allows for customization and monitoring of the Claude Code runtime environment.
Overview¶
The hook system uses a unified HookProcessor base class that provides common functionality for all hooks, reducing code duplication and improving maintainability.
Hook Files¶
Core Infrastructure¶
hook_processor.py- Base class providing common functionality for all hooks- JSON input/output handling
- Logging to
~/.amplihack/.claude/runtime/logs/ - Metrics collection
- Error handling and graceful fallback
- Session data management
Active Hooks (Configured in .claude/settings.json)¶
session_start.py- Runs when a Claude Code session starts- Adds project context to the conversation
- Reads and applies user preferences from USER_PREFERENCES.md
-
Logs session start metrics
-
stop.py- Runs when a session ends - Checks for lock flag (
~/.amplihack/.claude/tools/amplihack/.lock_active) - Blocks stop if continuous work mode is enabled (lock active)
-
Logs stop attempts and lock status
-
post_tool_use.py- Runs after each tool use - Tracks tool usage metrics
- Validates tool execution results
-
Categorizes tool types for analytics
-
pre_compact.py- Runs before context compaction - Manages context and prepares for compaction
- Logs pre-compact events
Architecture¶
┌─────────────────┐
│ Claude Code │
└────────┬────────┘
│ JSON input
▼
┌─────────────────┐
│ Hook Script │
├─────────────────┤
│ HookProcessor │ ◄── Base class
│ - read_input │
│ - process │ ◄── Implemented by subclass
│ - write_output│
│ - logging │
│ - metrics │
└────────┬────────┘
│ JSON output
▼
┌─────────────────┐
│ Claude Code │
└─────────────────┘
Creating a New Hook¶
To create a new hook, extend the HookProcessor class:
#!/usr/bin/env python3
"""Your hook description."""
from typing import Any, Dict
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
from hook_processor import HookProcessor
class YourHook(HookProcessor):
"""Your hook processor."""
def __init__(self):
super().__init__("your_hook_name")
def process(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
"""Process the hook input.
Args:
input_data: Input from Claude Code
Returns:
Output to return to Claude Code
"""
# Your processing logic here
self.log("Processing something")
self.save_metric("metric_name", value)
return {"result": "success"}
def main():
"""Entry point."""
hook = YourHook()
hook.run()
if __name__ == "__main__":
main()
Data Storage¶
The hook system creates and manages several directories:
.claude/runtime/
├── logs/ # Log files for each hook
│ ├── session_start.log
│ ├── stop.log
│ └── post_tool_use.log
├── metrics/ # Metrics in JSONL format
│ ├── session_start_metrics.jsonl
│ ├── stop_metrics.jsonl
│ └── post_tool_use_metrics.jsonl
└── analysis/ # Session analysis files
└── session_YYYYMMDD_HHMMSS.json
Testing¶
Run tests to verify the hook system:
# Unit tests for HookProcessor
python -m pytest test_hook_processor.py -v
# Integration tests for all hooks
python test_integration.py
# Test Azure continuation hook
python test_stop_azure_continuation.py
# Test individual hooks manually
echo '{"prompt": "test"}' | python session_start.py
Metrics Collected¶
session_start¶
prompt_length- Length of the initial prompt
stop¶
lock_blocks- Count of stop attempts blocked by lock flag
post_tool_use¶
tool_usage- Name of tool used (with optional duration)bash_commands- Count of Bash executionsfile_operations- Count of file operations (Read/Write/Edit)search_operations- Count of search operations (Grep/Glob)
Error Handling¶
All hooks implement graceful error handling following Python best practices (see PRs #2407, #2409):
Error Handling Principles¶
- No Silent Failures - All exception blocks log errors with appropriate context
- Fail-Open by Default - Hooks never crash the Claude Code chain
- Appropriate Logging - Errors logged at DEBUG/WARNING/ERROR levels based on severity
- Sanitized Logging - Sensitive information (paths, tokens) sanitized before logging
Specific Error Cases¶
- Invalid JSON input - Returns error message in output
- Processing exceptions - Logs error at DEBUG level, returns empty dict
- File system errors - Logs warning, continues operation with defaults
- Missing fields - Uses defaults, logs at DEBUG level
- External API failures - Logs at DEBUG level, degrades gracefully
Exception Handling Examples¶
Standard Hook Pattern (Fail-Open):
try:
result = process_data(input_data)
return {"success": True, "result": result}
except Exception as e:
# Log but don't raise - fail-open
print(f"Hook processing failed: {e}", file=sys.stderr)
return {}
Power Steering Pattern (Sanitized Logging):
try:
validate_response(response)
except Exception as e:
# Sanitize before logging
_log_sdk_error("validation", e)
return False # Fail-open
This ensures that hook failures never break the Claude Code chain while providing full observability.
See Exception Handling How-To for complete implementation guidance.
Hook Configuration¶
Hooks are configured in ~/.amplihack/.claude/settings.json:
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/tools/amplihack/hooks/session_start.py"
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/tools/amplihack/hooks/stop.py"
}
]
}
],
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/tools/amplihack/hooks/post_tool_use.py"
}
]
}
],
"PreCompact": [
{
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/tools/amplihack/hooks/pre_compact.py"
}
]
}
]
}
}
Benefits of Unified Processor¶
- Reduced Code Duplication - Common functionality in one place
- Consistent Error Handling - All hooks handle errors the same way
- Unified Logging - Standardized logging across all hooks
- Easier Testing - Base functionality tested once
- Simplified Maintenance - Fix bugs in one place
- Better Metrics - Consistent metric collection
- Easier Extension - Simple to add new hooks
Continuous Work Mode (Lock System)¶
The stop hook supports continuous work mode via a lock flag:
- Lock file:
~/.amplihack/.claude/tools/amplihack/.lock_active - Enable: Use
/amplihack:lockslash command - Disable: Use
/amplihack:unlockslash command - Behavior: When locked, Claude continues working through all TODOs without stopping
This enables autonomous operation for complex multi-step tasks.