Hook User Guide
📑 Table of Contents
- Overview
- Features
- Supported Hook Events
- Hook Script Specifications
- Configuration Guide
- Complete Examples
- Practical Guide
- Best Practices
- Performance Optimization Tips
- Security Best Practices
- Advanced Usage
- FAQ
- Appendix
Overview
The Hook feature allows you to insert custom scripts at critical points during AI Agent execution, enabling fine-grained control over Agent behavior. The Hook mechanism is fully compatible with the Claude Code Hooks specification, providing a powerful and flexible extension method.
Features
- ✅ Multiple Event Support: Supports 7 key events (SessionStart, SessionEnd, PreToolUse, PostToolUse, UserPromptSubmit, Stop, PreCompact)
- ✅ Tool Interception: Validate, modify, or block before and after tool execution
- ✅ Context Injection: Dynamically inject additional context at different session stages
- ✅ Parallel Execution: Multiple Hooks execute automatically in parallel for improved performance
- ✅ Automatic Deduplication: Identical commands are automatically deduplicated to avoid repeated execution
- ✅ Flexible Configuration: Supports regex matching, timeout control, project-level/user-level configuration
- ✅ Session Tracking: Intelligently detects session changes to avoid duplicate SessionStart triggers
- ✅ Safe and Reliable: Complete error handling and timeout mechanisms
Supported Hook Events
1. SessionStart - Session Startup
Trigger Timing: When a session starts (triggers only once per new session)
Trigger Logic:
- The system determines if it's a new session by comparing
conversationId - Multiple requests within the same session will not trigger repeatedly
- Switching to a new session or clearing a session will trigger again
Use Cases:
- Initialize project environment
- Inject project-specific context
- Set session-level configurations
- Load project specifications and documentation
Matcher Field: source
startup- Initial startup (currently only this value is supported)
Input Data (stdin JSON):
json
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.txt",
"cwd": "/project/path",
"hook_event_name": "SessionStart",
"source": "startup"
}Output Data (stdout JSON):
json
{
"continue": true,
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "Project uses TypeScript + React, prefer functional components"
}
}Example Configuration:
json
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"type": "command",
"command": "/path/to/session_start.py",
"timeout": 30
}
]
}
]
}
}2. SessionEnd - Session End
Trigger Timing: When a session terminates
Use Cases:
- Clean up temporary resources
- Save session state
- Generate session reports
Matcher Field: reason
other- Session end (currently only this value is supported, including scenarios like switching sessions, deleting sessions, clearing sessions, etc.)
Input Data:
json
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.txt",
"cwd": "/project/path",
"hook_event_name": "SessionEnd",
"reason": "other"
}Output Data:
json
{
"continue": true,
"systemMessage": "Session cleaned up, temporary files deleted"
}3. PreToolUse - Before Tool Execution
Trigger Timing: Before any tool executes
Use Cases:
- Validate tool parameters
- Modify tool input
- Block dangerous operations
- Permission checks
- Record audit logs
Matcher Field: tool_name
- Examples:
execute_command,write_to_file,read_file - Supports regex:
write_to_file|replace_in_file - Match all:
*or empty string
Input Data:
json
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.txt",
"cwd": "/project/path",
"hook_event_name": "PreToolUse",
"tool_name": "execute_command",
"tool_input": {
"command": "npm install",
"requires_approval": false
}
}Output Data - Allow Execution:
json
{
"continue": true,
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow"
}
}Output Data - Modify Parameters:
json
{
"continue": true,
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "Added --legacy-peer-deps parameter",
"modifiedInput": {
"command": "npm install --legacy-peer-deps",
"requires_approval": false
}
}
}Output Data - Block Execution:
json
{
"continue": false,
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Dangerous command detected: rm -rf /"
}
}Output Data - Request User Confirmation:
json
{
"continue": true,
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "ask",
"permissionDecisionReason": "Detected git push --force, do you want to continue?"
}
}4. PostToolUse - After Tool Execution
Trigger Timing: After a tool completes execution
Use Cases:
- Record tool execution logs
- Post-process tool output
- Trigger follow-up actions
- Send notifications
Matcher Field: tool_name
Input Data:
json
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.txt",
"cwd": "/project/path",
"hook_event_name": "PostToolUse",
"tool_name": "execute_command",
"tool_input": {
"command": "npm test"
},
"tool_response": {
"exitCode": 0,
"stdout": "All tests passed",
"stderr": ""
}
}Output Data:
json
{
"continue": true,
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "Tests passed, you can continue development"
}
}5. UserPromptSubmit - User Input Submission
Trigger Timing: When user submits a message
Use Cases:
- Pre-process user input
- Add contextual information
- Detect specific keywords
- Input validation
Matcher: Not used (triggers for all submissions)
Input Data:
json
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.txt",
"cwd": "/project/path",
"hook_event_name": "UserPromptSubmit",
"prompt": "Help me implement a login feature"
}Output Data:
json
{
"continue": true,
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": "Tip: The project has integrated JWT authentication library, recommend using it"
}
}Block Input:
json
{
"continue": false,
"stopReason": "Input contains sensitive information, blocked"
}6. Stop - Agent Stops Responding
Trigger Timing: When Agent completes its response
Use Cases:
- Provide feedback to Agent
- Record execution status
- Trigger follow-up tasks
Matcher: Not used
Input Data:
json
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.txt",
"cwd": "/project/path",
"hook_event_name": "Stop",
"stop_hook_active": false
}Output Data - Provide Feedback (exit code 2):
json
{
"continue": false,
"stopReason": "Please verify if the code passed unit tests"
}7. PreCompact - Before Context Compaction
Trigger Timing: When context is about to be compacted
Use Cases:
- Save important information
- Provide compaction guidance
- Backup complete context
Matcher Field: trigger
manual- User manually triggers/summarizeauto- Automatic compaction
Input Data:
json
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.txt",
"cwd": "/project/path",
"hook_event_name": "PreCompact",
"trigger": "auto",
"custom_instructions": "Preserve all API design discussions"
}Output Data (exit code 0):
json
{
"continue": true,
"hookSpecificOutput": {
"hookEventName": "PreCompact",
"additionalContext": "Important: Preserve database table structure design"
}
}Note: When exit code is 0, stdout content will be added as additional compaction guidance
Hook Script Specifications
Input Format
Hook scripts receive JSON-formatted input data via stdin.
Common Fields:
json
{
"session_id": "Session ID",
"transcript_path": "Path to conversation transcript file",
"cwd": "Current working directory",
"hook_event_name": "Event name"
}Event-Specific Fields:
SessionStart:sourceSessionEnd:reasonPreToolUse/PostToolUse:tool_name,tool_input,tool_responseUserPromptSubmit:promptPreCompact:trigger,custom_instructionsStop:stop_hook_active
Output Format
Hook scripts return JSON-formatted output via stdout.
Basic Structure:
json
{
"continue": true,
"suppressOutput": false,
"systemMessage": "Optional system message",
"stopReason": "Reason for blocking (when continue=false)",
"hookSpecificOutput": {
"hookEventName": "Event name",
"permissionDecision": "allow|deny|ask",
"permissionDecisionReason": "Decision reason",
"modifiedInput": {},
"additionalContext": "Additional context"
}
}Field Descriptions:
continue: Whether to allow the operation to continue (falsemeans block)suppressOutput: Whether to hide stdout outputsystemMessage: System message displayed to the userstopReason: Reason for blockinghookSpecificOutput: Event-specific output data
Exit Code Specifications
| Exit Code | Meaning | Behavior |
|---|---|---|
| 0 | Success | Allow operation to continue, stdout may be processed |
| 1 | Non-blocking error | Display stderr as warning, allow to continue |
| 2 | Blocking error | Block operation, stderr passed to Agent/model |
| Other | Non-blocking error | Same as exit code 1 |
Special Rules:
- PreToolUse: Exit code 2 blocks tool execution
- Stop: Exit code 2 indicates feedback, stderr is injected into the next message
- PreCompact: When exit code is 0, stdout serves as additional compaction guidance
Environment Variables
Hook scripts have access to the following environment variables during execution:
CLAUDE_PROJECT_DIR: Project root directory (Claude Code compatible)CODEBUDDY_PROJECT_DIR: Project root directory (CodeBuddy specific)
Configuration Guide
Configuration File Locations
Priority (high to low):
- Project-level:
<workspace>/.codebuddy/settings.json - User-level:
~/.codebuddy/settings.json
Project-level configuration overrides user-level configuration.
Configuration File Structure
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "execute_command",
"hooks": [
{
"type": "command",
"command": "/absolute/path/to/script.py",
"timeout": 10
}
]
},
{
"matcher": "write_to_file|replace_in_file",
"hooks": [
{
"type": "command",
"command": "/path/to/backup_script.sh",
"timeout": 20
}
]
}
],
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"type": "command",
"command": "/path/to/init.py",
"timeout": 30
}
]
}
]
}
}Configuration Field Descriptions
matcher (Matcher)
Regular expression used to match specific conditions.
Syntax:
- Empty string
""or"*": Match all - Single value:
"execute_command" - Multiple values:
"write_to_file|replace_in_file" - Regular expression:
"read.*|search.*"
Match Targets for Different Events:
PreToolUse/PostToolUse: Matchestool_nameSessionStart: MatchessourceSessionEnd: MatchesreasonPreCompact: MatchestriggerUserPromptSubmit/Stop: Matcher not used
command (Command Path)
Path to the Hook script.
Requirements:
- ✅ Absolute paths are recommended
- ✅ Supports environment variables:
"$CODEBUDDY_PROJECT_DIR/.codebuddy/hooks/script.py" - ✅ Can include interpreter:
"python3 /path/to/script.py" - ⚠️ Ensure the script has execution permissions
timeout (Timeout)
Hook execution timeout, unit: seconds.
- Default value:
60seconds - Recommended settings: Adjust based on script complexity
- Simple validation: 5-10 seconds
- File operations: 15-30 seconds
- Network requests: 30-60 seconds
Complete Examples
Example 1: Command Security Validation
Scenario: Block dangerous rm -rf commands
Hook Script (validate_command.py):
python
#!/usr/bin/env python3
import json
import sys
DANGEROUS_COMMANDS = ['rm -rf /', 'dd if=/dev/zero', 'mkfs']
def main():
input_data = json.loads(sys.stdin.read())
if input_data.get('tool_name') != 'execute_command':
print(json.dumps({"continue": True}))
return 0
command = input_data.get('tool_input', {}).get('command', '')
for dangerous in DANGEROUS_COMMANDS:
if dangerous in command:
output = {
"continue": False,
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": f"Dangerous command detected: {dangerous}"
}
}
print(json.dumps(output, ensure_ascii=False))
return 0
print(json.dumps({"continue": True}))
return 0
if __name__ == "__main__":
sys.exit(main())Configuration:
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "execute_command",
"hooks": [
{
"type": "command",
"command": "/path/to/validate_command.py",
"timeout": 10
}
]
}
]
}
}Example 2: Smart Command Parameter Modification
Scenario: Automatically add --legacy-peer-deps parameter to npm install
Hook Script (modify_npm.py):
python
#!/usr/bin/env python3
import json
import sys
import re
def main():
input_data = json.loads(sys.stdin.read())
if input_data.get('tool_name') != 'execute_command':
print(json.dumps({"continue": True}))
return 0
tool_input = input_data.get('tool_input', {})
command = tool_input.get('command', '')
# Check if it's npm install
if re.match(r'^npm\s+(i|install)\b', command.strip()):
# If --legacy-peer-deps is not present, add it
if '--legacy-peer-deps' not in command:
modified_command = command.strip() + ' --legacy-peer-deps'
output = {
"continue": True,
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "Automatically added --legacy-peer-deps parameter",
"modifiedInput": {
"command": modified_command,
"requires_approval": tool_input.get('requires_approval', False)
}
}
}
print(json.dumps(output, ensure_ascii=False))
return 0
print(json.dumps({"continue": True}))
return 0
if __name__ == "__main__":
sys.exit(main())Example 3: Automatic File Backup Before Modification
Scenario: Automatically create backups before modifying files
Hook Script (backup_files.py):
python
#!/usr/bin/env python3
import json
import sys
import os
import shutil
from datetime import datetime
def main():
input_data = json.loads(sys.stdin.read())
tool_name = input_data.get('tool_name', '')
# Only process file write tools
if tool_name not in ['write_to_file', 'replace_in_file']:
print(json.dumps({"continue": True}))
return 0
tool_input = input_data.get('tool_input', {})
file_path = tool_input.get('filePath')
if not file_path or not os.path.exists(file_path):
print(json.dumps({"continue": True}))
return 0
# Create backup directory
project_dir = os.environ.get('CODEBUDDY_PROJECT_DIR', '')
backup_dir = os.path.join(project_dir, '.codebuddy', 'backups')
os.makedirs(backup_dir, exist_ok=True)
# Generate backup filename
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
backup_name = f"{os.path.basename(file_path)}.{timestamp}.bak"
backup_path = os.path.join(backup_dir, backup_name)
# Create backup
shutil.copy2(file_path, backup_path)
output = {
"continue": True,
"systemMessage": f"Backed up to: {backup_path}"
}
print(json.dumps(output, ensure_ascii=False))
return 0
if __name__ == "__main__":
sys.exit(main())Configuration:
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "write_to_file|replace_in_file",
"hooks": [
{
"type": "command",
"command": "/path/to/backup_files.py",
"timeout": 15
}
]
}
]
}
}Example 4: Inject Project Context at Session Startup
Scenario: Automatically inject project configuration information when a session starts
Hook Script (session_start.py):
python
#!/usr/bin/env python3
import json
import sys
import os
def main():
input_data = json.loads(sys.stdin.read())
project_dir = os.environ.get('CODEBUDDY_PROJECT_DIR', '')
# Read project configuration
config_file = os.path.join(project_dir, '.codebuddy', 'project.json')
project_info = ""
if os.path.exists(config_file):
with open(config_file, 'r') as f:
config = json.load(f)
project_info = f"""
Project Name: {config.get('name', 'Unknown')}
Tech Stack: {', '.join(config.get('tech_stack', []))}
Coding Standard: {config.get('coding_standard', 'Standard')}
"""
output = {
"continue": True,
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": f"""
Session started!
Project Directory: {project_dir}
Startup Source: {input_data.get('source', 'unknown')}
{project_info}
"""
}
}
print(json.dumps(output, ensure_ascii=False))
return 0
if __name__ == "__main__":
sys.exit(main())Example 5: Save Important Information Before Context Compaction
Scenario: Save complete conversation history before automatic compaction
Hook Script (save_context.py):
python
#!/usr/bin/env python3
import json
import sys
import os
import shutil
from datetime import datetime
def main():
input_data = json.loads(sys.stdin.read())
# Only process automatic compaction
if input_data.get('trigger') != 'auto':
print(json.dumps({"continue": True}))
return 0
project_dir = os.environ.get('CODEBUDDY_PROJECT_DIR', '')
transcript_path = input_data.get('transcript_path', '')
if not transcript_path or not os.path.exists(transcript_path):
print(json.dumps({"continue": True}))
return 0
# Create save directory
save_dir = os.path.join(project_dir, '.codebuddy', 'context_history')
os.makedirs(save_dir, exist_ok=True)
# Save conversation history
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
save_path = os.path.join(save_dir, f'transcript_{timestamp}.txt')
shutil.copy2(transcript_path, save_path)
output = {
"continue": True,
"systemMessage": f"Context saved to: {save_path}"
}
print(json.dumps(output, ensure_ascii=False))
return 0
if __name__ == "__main__":
sys.exit(main())Configuration:
json
{
"hooks": {
"PreCompact": [
{
"matcher": "auto",
"hooks": [
{
"type": "command",
"command": "/path/to/save_context.py",
"timeout": 20
}
]
}
]
}
}Practical Guide
Quick Start - Configure Your First Hook in 5 Minutes
Step 1: Create Configuration File
bash
mkdir -p ~/.codebuddy
cat > ~/.codebuddy/settings.json << 'EOF'
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"type": "command",
"command": "/usr/bin/env python3 -c \"import json,sys; print(json.dumps({'continue': True, 'hookSpecificOutput': {'hookEventName': 'SessionStart', 'additionalContext': 'Hook configured successfully!'}}))\"",
"timeout": 5
}
]
}
]
}
}
EOFStep 2: Restart Agent
Start a new session. If you see "Hook configured successfully!" the configuration is working.
Step 3: Create Your First Real Hook
bash
# Create Hook script directory
mkdir -p ~/.codebuddy/hooks
# Create test script
cat > ~/.codebuddy/hooks/my_first_hook.py << 'EOF'
#!/usr/bin/env python3
import json
import sys
def main():
input_data = json.loads(sys.stdin.read())
output = {
"continue": True,
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": f"Welcome to Agent-Craft! Current project: {input_data.get('cwd', 'unknown')}"
}
}
print(json.dumps(output, ensure_ascii=False))
return 0
if __name__ == "__main__":
sys.exit(main())
EOF
# Add execution permission
chmod +x ~/.codebuddy/hooks/my_first_hook.pyStep 4: Update Configuration File
json
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"type": "command",
"command": "/Users/YOUR_USERNAME/.codebuddy/hooks/my_first_hook.py",
"timeout": 10
}
]
}
]
}
}Advanced Debugging Tips
Tip 1: Use Log Files for Debugging
python
import sys
def debug_log(message):
"""Write to debug log without affecting stdout"""
with open('/tmp/hook_debug.log', 'a') as f:
f.write(f"{message}\n")
# Use in Hook script
debug_log(f"Received input: {json.dumps(input_data)}")Tip 2: Validate JSON Output Format
bash
# Test if script output JSON is valid
echo '{"hook_event_name":"SessionStart"}' | python3 your_hook.py | jq .Tip 3: Monitor Hook Execution
bash
# Watch Hook logs in real-time
tail -f ~/.codebuddy/logs/agent-craft.log | grep -i hookTip 4: Use Environment Variables to Pass Information
python
import os
# Get project directory in Hook
project_dir = os.environ.get('CODEBUDDY_PROJECT_DIR', '')
claude_dir = os.environ.get('CLAUDE_PROJECT_DIR', '') # Claude Code compatibleCommon Hook Patterns
Pattern 1: Whitelist Validation
python
ALLOWED_COMMANDS = [
'npm install',
'npm test',
'git status',
'git diff'
]
def is_allowed(command):
return any(command.startswith(allowed) for allowed in ALLOWED_COMMANDS)Pattern 2: Parameter Enhancement
python
def enhance_command(command):
"""Automatically add common parameters"""
enhancements = {
'npm install': ' --legacy-peer-deps',
'git push': ' --dry-run', # Safe mode
}
for prefix, suffix in enhancements.items():
if command.startswith(prefix) and suffix not in command:
return command + suffix
return commandPattern 3: Conditional Routing
python
def should_block(input_data):
"""Determine whether to block based on multiple conditions"""
tool_name = input_data.get('tool_name')
tool_input = input_data.get('tool_input', {})
# Rule 1: Block deletion of important files
if tool_name == 'delete_files':
file_path = tool_input.get('target_file', '')
if any(important in file_path for important in ['.git', 'package.json']):
return True, "Cannot delete important files"
# Rule 2: Block dangerous commands
if tool_name == 'execute_command':
command = tool_input.get('command', '')
if 'rm -rf /' in command or 'dd if=' in command:
return True, "Dangerous command detected"
return False, NoneRecommended Project Templates
Node.js Project Hook Configuration
json
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"type": "command",
"command": "node ~/.codebuddy/hooks/nodejs-init.js",
"timeout": 15
}
]
}
],
"PreToolUse": [
{
"matcher": "execute_command",
"hooks": [
{
"type": "command",
"command": "python3 ~/.codebuddy/hooks/npm-safety-check.py",
"timeout": 5
}
]
}
]
}
}Python Project Hook Configuration
json
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"type": "command",
"command": "python3 ~/.codebuddy/hooks/python-env-check.py",
"timeout": 10
}
]
}
],
"PostToolUse": [
{
"matcher": "write_to_file|replace_in_file",
"hooks": [
{
"type": "command",
"command": "python3 ~/.codebuddy/hooks/python-lint.py",
"timeout": 20
}
]
}
]
}
}Best Practices
1. Script Development
- ✅ Use Absolute Paths: Place Hook scripts in a fixed directory, reference using absolute paths
- ✅ Error Handling: Ensure scripts have complete exception handling to avoid blocking the main flow
- ✅ Fast Execution: Hooks should complete quickly, avoid time-consuming operations
- ✅ Idempotency: Hooks may be called multiple times, ensure consistent results across multiple executions
- ✅ Logging: Use
sys.stderrfor debug output, don't pollute stdout
2. Security
- ⚠️ Input Validation: Always validate input data integrity
- ⚠️ Whitelist Over Blacklist: Use whitelist mechanisms for permission control
- ⚠️ Avoid Code Injection: Never directly execute user input
- ⚠️ Least Privilege: Hook scripts should run with minimum necessary permissions
3. Performance Optimization
- ⏱ Set Reasonable Timeouts: Adjust timeout based on script complexity
- ⏱ Design for Parallelism: Avoid dependencies between Hooks, fully utilize parallel execution
- ⏱ Cache Results: Consider caching results for repeated calculations
4. Debugging Tips
Manually Test Hook Scripts:
bash
echo '{"hook_event_name":"PreToolUse","tool_name":"execute_command","tool_input":{"command":"npm install"}}' | \
python3 /path/to/your_hook.pyDebug Output:
python
# Output debug information in Hook script
import sys
sys.stderr.write(f"[DEBUG] Processing command: {command}\n")
sys.stderr.flush()5. Configuration Management
- 📁 Project-Specific Hooks: Place in
<workspace>/.codebuddy/, version control with project - 📁 Personal Hooks: Place in
~/.codebuddy/, reuse across projects
Performance Optimization Tips
1. Reduce Hook Execution Time
- ✅ Use Fast Languages: Shell scripts typically start faster than Python
- ✅ Avoid Repeated Work: Cache computation results
- ✅ Async Processing: Use background tasks for non-critical operations
- ✅ Early Exit: Determine early if processing is needed
Example:
python
# Bad practice: Load large file every time
def main():
with open('huge_config.json', 'r') as f:
config = json.load(f) # Read every time
# ... processing logic
# Good practice: Cache configuration
CONFIG_CACHE = None
def get_config():
global CONFIG_CACHE
if CONFIG_CACHE is None:
with open('huge_config.json', 'r') as f:
CONFIG_CACHE = json.load(f)
return CONFIG_CACHE2. Optimize Matcher Configuration
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "execute_command",
"hooks": [
{
"type": "command",
"command": "/path/to/fast_check.sh",
"timeout": 3
}
]
},
{
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "/path/to/general_check.py",
"timeout": 10
}
]
}
]
}
}3. Parallel Execution Considerations
- Multiple Hooks execute in parallel, don't rely on execution order
- Avoid file write conflicts between Hooks
- Use file locks or atomic operations for shared resources
python
import fcntl
def safe_append_log(message):
"""Thread-safe log writing"""
with open('/tmp/hook.log', 'a') as f:
fcntl.flock(f.fileno(), fcntl.LOCK_EX)
f.write(message + '\n')
fcntl.flock(f.fileno(), fcntl.LOCK_UN)Security Best Practices
1. Input Validation
Never trust input data:
python
def validate_input(input_data):
"""Validate input data integrity"""
required_fields = ['hook_event_name', 'session_id']
for field in required_fields:
if field not in input_data:
raise ValueError(f"Missing required field: {field}")
# Validate field types
if not isinstance(input_data.get('tool_input'), dict):
raise ValueError("tool_input must be a dictionary")
return True2. Command Injection Protection
Never directly execute user input:
python
# ❌ Dangerous: Direct execution
os.system(f"echo {user_input}")
# ✅ Safe: Use parameterized approach
import subprocess
subprocess.run(['echo', user_input], check=True)3. Path Traversal Protection
python
import os
def safe_file_access(file_path, project_dir):
"""Ensure file path is within project directory"""
abs_path = os.path.abspath(file_path)
abs_project = os.path.abspath(project_dir)
if not abs_path.startswith(abs_project):
raise ValueError("Path traversal detected")
return abs_path4. Privilege Minimization
python
# Hook scripts should run with minimum privileges
# Avoid using sudo or root permissions
# ✅ Check privileges
if os.geteuid() == 0:
print("Warning: Running as root is not recommended", file=sys.stderr)Advanced Usage
Conditional Execution
Decide whether to process based on conditions in the Hook script:
python
def main():
input_data = json.loads(sys.stdin.read())
# Only process under specific conditions
if not should_process(input_data):
print(json.dumps({"continue": True}))
return 0
# Execute processing logic
...Multi-Rule Combination
Implement multiple rules in a single Hook script:
python
def main():
input_data = json.loads(sys.stdin.read())
# Apply multiple rules
for rule in RULES:
if rule.matches(input_data):
return rule.apply(input_data)
# Default behavior
print(json.dumps({"continue": True}))
return 0External Service Integration
Hooks can call external APIs or services:
python
import requests
def check_with_external_service(command):
response = requests.post('https://api.example.com/validate',
json={'command': command},
timeout=5)
return response.json()FAQ
Q1: Hook is not being executed?
Checklist:
- ✅ Configuration file path is correct (
settings.jsonin.codebuddydirectory) - ✅
hooksfield is configured correctly, JSON format is valid - ✅
matcherregex can match the target - ✅ Hook script has execution permission (
chmod +x script.py) - ✅ Script path is correct (absolute paths recommended)
- ✅ Script has correct shebang on first line (
#!/usr/bin/env python3)
Q2: Hook execution timeout?
Solutions:
- Increase
timeoutconfiguration value - Optimize Hook script performance
- Check for infinite loops or blocking operations
Q3: How to debug Hook scripts?
Debugging Steps:
- Use
echoto manually pass test data - Use
sys.stderrfor debug output in the script - Verify JSON format is correct
Q4: Execution order of multiple Hooks?
Answer:
- Hooks execute in parallel, execution order is not guaranteed
- If sequential execution is needed, merge logic into a single Hook script
- Identical commands are automatically deduplicated
Q5: Modified parameters not taking effect?
Check Points:
- Ensure
modifiedInputfield is returned - Ensure
permissionDecisionisallow - Check if field names match tool parameters
- Verify JSON format is correct
- Ensure
continueistrue
Q6: SessionStart Hook triggers on every request?
Cause: SessionStart should only trigger once per new session
Solutions:
- System tracks sessions via
conversationId - Multiple requests within the same session will not trigger repeatedly
- If issues persist, check if session ID changes in logs
Appendix
A. Complete HookInput Interface Definition
typescript
interface HookInput {
// Common fields
session_id?: string; // Session ID
transcript_path?: string; // Conversation transcript path
cwd?: string; // Current working directory
hook_event_name: string; // Hook event name
// SessionStart specific (currently only 'startup' is supported)
source?: 'startup';
// UserPromptSubmit specific
prompt?: string; // User input content
// PreToolUse/PostToolUse specific
tool_name?: string; // Tool name
tool_input?: Record<string, any>; // Tool input parameters
tool_response?: any; // Tool response (PostToolUse only)
// Stop specific
stop_hook_active?: boolean; // Whether Stop Hook is activated
// PreCompact specific
trigger?: 'manual' | 'auto'; // Trigger method
custom_instructions?: string; // Custom compaction instructions
}B. Complete HookOutput Interface Definition
typescript
interface HookOutput {
// Basic control
continue?: boolean; // Whether to continue execution (default true)
stopReason?: string; // Stop reason
suppressOutput?: boolean; // Whether to hide output
systemMessage?: string; // System message
// Hook-specific output
hookSpecificOutput?: {
hookEventName: string; // Hook event name
// PreToolUse specific
permissionDecision?: 'allow' | 'deny' | 'ask';
permissionDecisionReason?: string;
modifiedInput?: Record<string, any>;
// SessionStart/UserPromptSubmit/PostToolUse specific
additionalContext?: string; // Additional context
};
}C. Environment Variables List
| Environment Variable | Description | Example Value |
|---|---|---|
CODEBUDDY_PROJECT_DIR | Project root directory | /path/to/project |
CLAUDE_PROJECT_DIR | Project root directory (Claude Code compatible) | /path/to/project |
D. Exit Code Details
| Exit Code | Meaning | stdout | stderr | Behavior |
|---|---|---|---|---|
| 0 | Success | Processed as result | Ignored | Continue execution, may inject context |
| 1 | Warning | Ignored | Displayed as warning | Continue execution |
| 2 | Block/Feedback | Ignored | Passed to Agent | PreToolUse: Block execution Stop: Provide feedback |
| Other | Error | Ignored | Displayed as warning | Continue execution |
E. Common Tool Names List
File Operation Tools:
read_file- Read filewrite_to_file- Write to filereplace_in_file- Replace file contentdelete_files- Delete fileslist_files- List filessearch_file- Search files
Code Operation Tools:
search_content- Search contentread_lints- Read Lint errors
Execution Tools:
execute_command- Execute commandpreview_url- Preview URL
Other Tools:
task- Create subtaskweb_search- Web searchweb_fetch- Fetch web contentask_followup_question- Ask user
Summary
The Hook feature provides powerful extension capabilities, allowing you to insert custom logic at critical points of the AI Agent. By using Hooks properly, you can:
- 🛡️ Enhance Security: Validate and block dangerous operations
- 🔧 Automate Workflows: Intelligently modify parameters, automatically backup files
- 📊 Monitor and Audit: Record tool execution logs
- 🎯 Customize Behavior: Inject project-specific context
Start using the Hook feature to make your AI Agent smarter, safer, and more aligned with your project needs!
Happy Hooking! 🎣