tencent cloud

Hook Feature Documentation

Download
Focus Mode
Font Size
Last updated: 2026-10-08 10:00:54
AI-Translated

Overview

The Hook feature allows you to insert custom scripts at key 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

Multi-Event Support: Supports 7 key events (SessionStart, SessionEnd, PreToolUse, PostToolUse, UserPromptSubmit, Stop, PreCompact)
Tool Interception: Validates, modifies, or blocks tool execution before or after it occurs
Context Injection: Dynamically injects additional context at different stages of a session
Parallel Execution: Multiple hooks run automatically in parallel to improve performance.
Automatic Deduplication: Identical commands are automatically deduplicated to prevent duplicate execution.
Flexible Configuration: Supports regex matching, timeout control, and project-level/user-level configuration.
Session Tracking: Intelligently identifies session changes to avoid duplicate SessionStart triggers.
Secure and Reliable: Comprehensive error handling and timeout mechanisms

Supported Hook Events

1. SessionStart - Session Start

Trigger Timing: At session start (triggered only once per new session)
Trigger Logic:
The system determines whether it is a new session by comparing the conversationId.
Multiple requests within the same session will not be triggered repeatedly.
It will be triggered again after a new session is switched to or the session is cleared.
Use Cases:
Initialize the project environment.
Inject project-specific context.
Set session-level configurations.
Load project specifications and documentation.
Matcher Field: source
startup - First-time startup (currently the only supported value)
Input Data (stdin JSON):
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.txt",
"cwd": "/project/path",
"hook_event_name": "SessionStart",
"source": "startup"
}
Output Data (stdout JSON):
{
"continue": true,
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "The project uses TypeScript + React. Prioritize using function components."
}
}
Example configuration:
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"type": "command",
"command": "/path/to/session_start.py",
"timeout": 30
}
]
}
]
}
}

2. SessionEnd - Session End

Trigger Timing: At session termination
Use Cases:
Clean up temporary resources.
Save the session state.
Generate a session report.
Matcher Field: reason
other - Session ended (currently only this value is supported, including scenarios such as switching sessions, deleting sessions, and clearing sessions)
Input Data:
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.txt",
"cwd": "/project/path",
"hook_event_name": "SessionEnd",
"reason": "other"
}
Output Data:
{
"continue": true,
"systemMessage": "The session has been cleaned up and temporary files have been deleted."
}

3. PreToolUse - Before Tool Execution

Trigger Timing: Before any tool execution
Use Cases:
Verify tool parameters.
Modify the tool input.
Block dangerous operations.
Permission check
Record audit logs.
Matcher Field: tool_name
Example: execute_command, write_to_file, read_file
Supports regular expressions: write_to_file|replace_in_file
Match all: * or an empty string.
Input Data:
{
"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:
{
"continue": true,
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow"
}
}
Output Data - Modify Parameters:
{
"continue": true,
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "Added the --legacy-peer-deps parameter",
"modifiedInput": {
"command": "npm install --legacy-peer-deps",
"requires_approval": false
}
}
}

Output Data - Block Execution:

{
"continue": false,
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Dangerous command detected: rm -rf /"
}
}
Output Data - Request User Confirmation:
{
"continue": true,
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "ask",
"permissionDecisionReason": "git push --force detected. Continue?"
}
}

4. PostToolUse - After Tool Execution

Trigger Timing: After tool execution completes
Use Cases:
Log tool execution
Post-process tool output
Trigger follow-up operations
Send notifications
Matcher Field: tool_name
Input Data:
{
"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:
{
"continue": true,
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "Tests have passed. You can continue development."
}
}

5. UserPromptSubmit - User Prompt Submission

Trigger Timing: When a user submits a message
Use Cases:
Preprocess user input
Add context information
Detect specific keywords
Input validation
Matcher: Not used (triggered on all submissions)
Input Data:
{
"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:
{
"continue": true,
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": "Note: The project has already integrated the JWT authentication library. It is recommended to use it."
}
}
Block Input:
{
"continue": false,
"stopReason": "Input contains sensitive information and has been blocked"
}

6. Stop - Agent Stops Responding

Trigger Timing: When the Agent completes its response
Use Cases:
Provide feedback to the Agent.
Record the execution status.
Trigger follow-up tasks.
Matcher: Not used
Input Data:
{
"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):
{
"continue": false,
"stopReason": "Please verify whether the code has passed the unit tests"
}

7. PreCompact - Before Context Compression

Trigger Timing: When the context is about to be compacted
Use Cases:
Save important information.
Provide compression guidance.
Back up the complete context.
Matcher Field: trigger
manual - The user manually triggers /summarize
auto - Automatic compaction
Input Data:
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.txt",
"cwd": "/project/path",
"hook_event_name": "PreCompact",
"trigger": "auto",
"custom_instructions": "Keep all discussions related to API design"
}
Output Data (exit code 0):
{
"continue": true,
"hookSpecificOutput": {
"hookEventName": "PreCompact",
"additionalContext": "Important: Keep the database table schema design"
}
}
Note:
When the Exit code is 0, the content of stdout is added as additional compression guidance.

Hook Script Specification

Input Format

The Hook script receives input data in JSON format through stdin.
Common Fields:
{
"session_id": "Session ID",
"transcript_path": "Path to the conversation transcript file",
"cwd": "Current working directory",
"hook_event_name": "Event name"
}
Event-specific Fields:
SessionStart: source
SessionEnd: reason
PreToolUse/PostToolUse: tool_name, tool_input, tool_response
UserPromptSubmit: prompt
PreCompact: trigger, custom_instructions
Stop: stop_hook_active

Output Format

The Hook script returns output in JSON format through stdout.
Basic Structure:
{
"continue": true,
"suppressOutput": false,
"systemMessage": "Optional system message",
"stopReason": "The reason for blocking (when continue=false)",
"hookSpecificOutput": {
"hookEventName": "Event name",
"permissionDecision": "allow|deny|ask",
"permissionDecisionReason": "Decision reason",
"modifiedInput": {},
"additionalContext": "Additional context"
}
}
Field Description:
continue: Whether to allow the operation to continue (false means to block it)
suppressOutput: Whether to hide stdout output
systemMessage: The system message displayed to the user
stopReason: The reason for blocking
hookSpecificOutput: Event-specific output data

Exit Code Specification

Exit Code
Description
Action
0
Executed successfully
Allows the operation to continue. stdout may be processed.
1
Non-blocking error
Display stderr as a warning and allow the operation to continue.
2
Blocking error
Block the operation. stderr is passed to the Agent/model.
Other
Non-blocking error
Same as exit code 1.
Special rules:
PreToolUse: Exit code 2 prevents tool execution.
Stop: Exit code 2 indicates that feedback is provided, and stderr is injected into the next message.
PreCompact: When the exit code is 0, stdout is used as additional compression guidance.

Environment Variable

The following environment variables are accessible to Hook scripts during execution:
CLAUDE_PROJECT_DIR: The project root directory (compatible with Claude Code)
CODEBUDDY_PROJECT_DIR: The project root directory (specific to CodeBuddy)

Configuration Instructions

Configuration File Location

Priority (from high to low):
1. Project level: <workspace>/.codebuddy/settings.json
2. User level: ~/.codebuddy/settings.json
Project-level configuration overrides user-level configuration.

Configuration File Structure

{
"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

A regular expression used to match specific conditions.
Syntax:
An empty string "" or "*": matches all.
A single value: "execute_command"
Multiple values: "write_to_file|replace_in_file"
Regular expression: "read.*|search.*"
Matching Targets for Different Events:
PreToolUse/PostToolUse: matches tool_name
SessionStart: matches source
SessionEnd: matches reason
PreCompact: matches trigger
UserPromptSubmit/Stop: Do not use matcher

command

The path to the Hook script.
Requirements:
It is recommended to use absolute paths.
Supports environment variables: "$CODEBUDDY_PROJECT_DIR/.codebuddy/hooks/script.py"
Can include an interpreter: "python3 /path/to/script.py"
Ensure that the script has execute permission.

timeout

The timeout for Hook execution, in seconds.

Default value: 60 seconds
Recommended setting: Adjust based on script complexity.
Simple validation: 5-10 seconds
File operations: 15-30 seconds
Network requests: 30-60 seconds

Complete Example

Example 1: Command Security Verification

Scenario: Block dangerous rm -rf commands
Hook Script (validate_command.py):
#!/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:
{
"hooks": {
"PreToolUse": [
{
"matcher": "execute_command",
"hooks": [
{
"type": "command",
"command": "/path/to/validate_command.py",
"timeout": 10
}
]
}
]
}
}

Example 2: Intelligently Modifying Command Parameters

Scenario: Automatically add the --legacy-peer-deps parameter to npm install
Hook Script (modify_npm.py):
#!/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 whether it is 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 the --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 Backup Before File Modification

Scenario: Automatically create a backup before modifying files
Hook Script (backup_files.py):
#!/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 a 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 a backup file name.
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 a 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:
{
"hooks": {
"PreToolUse": [
{
"matcher": "write_to_file|replace_in_file",
"hooks": [
{
"type": "command",
"command": "/path/to/backup_files.py",
"timeout": 15
}
]
}
]
}
}

Example 4: Injecting Project Context at Session Startup

Scenario: Automatically inject project configuration information at the start of a session.
Hook Script (session_start.py):
#!/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', '')

# Load 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}
Launch 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: Saving Important Information Before Context Compression

Scenario: Save the complete conversation history before automatic compression.
Hook Script (save_context.py):
#!/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())

# Process automatic compaction only.
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 a save directory.
save_dir = os.path.join(project_dir, '.codebuddy', 'context_history')
os.makedirs(save_dir, exist_ok=True)

# Save the 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:
{
"hooks": {
"PreCompact": [
{
"matcher": "auto",
"hooks": [
{
"type": "command",
"command": "/path/to/save_context.py",
"timeout": 20
}
]
}
]
}
}

Practical Guide

Quick Start - Configuring Your First Hook in 5 Minutes

Step 1: Create a configuration file
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
}
]
}
]
}
}
EOF
Step 2: Restart the Agent
Start a new session. If you see "Hook configured successfully!", the configuration has taken effect.
Step 3: Create your first real Hook
# Create a directory for Hook scripts.
mkdir -p ~/.codebuddy/hooks

# Create a 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 execute permission.
chmod +x ~/.codebuddy/hooks/my_first_hook.py
Step 4: Update the configuration file
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"type": "command",
"command": "/Users/YOUR_USERNAME/.codebuddy/hooks/my_first_hook.py",
"timeout": 10
}
]
}
]
}
}

Advanced Debugging Techniques

Tip 1: Debug with log files
import sys

def debug_log(message):
"""Write debug logs without affecting stdout."""
with open('/tmp/hook_debug.log', 'a') as f:
f.write(f"{message}\\n")

# Use in Hook scripts.
debug_log(f"Received input: {json.dumps(input_data)}")
Tip 2: Verify the JSON output format
# Test whether the JSON output by the script is valid.
echo '{"hook_event_name":"SessionStart"}' | python3 your_hook.py | jq .
Tip 3: Monitor Hook execution
# View Hook logs in real time.
tail -f ~/.codebuddy/logs/agent-craft.log | grep -i hook
Tip 4: Use environment variables to pass information
import os

# Obtain the project directory in a Hook.
project_dir = os.environ.get('CODEBUDDY_PROJECT_DIR', '')
claude_dir = os.environ.get('CLAUDE_PROJECT_DIR', '') # Compatible with Claude Code

Common Hook Patterns

Mode 1: Allowlist verification
ALLOWED_COMMANDS = [
'npm install',
'npm test',
'git status',
'git diff'
]

def is_allowed(command):
return any(command.startswith(allowed) for allowed in ALLOWED_COMMANDS)
Mode 2: Parameter enhancement
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 command
Mode 3: Conditional routing
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: Prevent 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, "Important files cannot be deleted"

# 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, None

Recommended Project Templates

Node.js Project Hook Configuration
{
"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
{
"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
}
]
}
]
}
}

Usage Recommendations

1. Script Development

Use absolute paths: Place the Hook script in a fixed directory and reference it using an absolute path.
Error Handling: Ensure that the script has robust exception handling to avoid blocking the main process.
Fast Execution: Hooks should complete quickly and avoid time-consuming operations.
Idempotency: Hooks may be called multiple times, so ensure consistent results across repeated executions.
Logging: Use sys.stderr to output debug information and avoid polluting stdout.

2. Security

Input Validation: Always validate the legitimacy of input data.
Allowlist over blocklist: Use an allowlist mechanism for permission control.
Avoid Code Injection: Do not directly execute user input.
Least Privilege: Hook scripts should run with the minimum necessary permissions.

3. Performance Optimization

Set a Reasonable Timeout: Set the timeout based on script complexity.
Parallel Design: Avoid dependencies between hooks and fully leverage parallel execution.
Cache Results: Consider caching results for repeated computations.

4. Debugging Techniques

Manually Test Hook Scripts:
echo '{"hook_event_name":"PreToolUse","tool_name":"execute_command","tool_input":{"command":"npm install"}}' | \\
python3 /path/to/your_hook.py
Debug Output:
# Output debug information in Hook scripts.
import sys

sys.stderr.write(f"[DEBUG] Processing command: {command}\\n")
sys.stderr.flush()

5. Configuration Management

Project-Specific Hooks: Place them under <workspace>/.codebuddy/ and manage them with project version control.
Personal Hooks: Place them under ~/.codebuddy/ for reuse across projects.

Performance Optimization Suggestions

1. Reducing Hook Execution Time

Use a Fast Language: Shell scripts typically start faster than Python.
Avoid Duplicate Work: Cache computation results.
Asynchronous Processing: Use background tasks for non-critical operations.
Early Exit: Determine whether processing is needed as early as possible.
Example:
# Bad Practice: Loading Large Files Every Time
def main():
with open('huge_config.json', 'r') as f:
config = json.load(f) # Read every time
# ... Processing logic

# Good Practice: Cache Configurations
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_CACHE

2. Optimizing Matcher Configuration

{
"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. Considerations for Parallel Execution

Multiple hooks execute in parallel. Do not rely on their execution order.
Avoid file write conflicts between hooks.
Use file locks or atomic operations to handle shared resources.
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:
def validate_input(input_data):
"""Verify the integrity of input data."""
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}")

# Verify Field Types
if not isinstance(input_data.get('tool_input'), dict):
raise ValueError("tool_input must be a dictionary")

return True

2. Command Injection Prevention

Do not directly execute user input:
# ❌ Danger: Direct Execution
os.system(f"echo {user_input}")

# ✅ Safe: Use Parameterization
import subprocess
subprocess.run(['echo', user_input], check=True)

3. Path Traversal Prevention

import os

def safe_file_access(file_path, project_dir):
"""Ensure that file paths are within the 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_path

4. Principle of Least Privilege

# Hook scripts should run with the minimum necessary permissions.
# Avoid using sudo or root privileges.

# ✅ Check Permissions
if os.geteuid() == 0:
print("Warning: Running as root is not recommended", file=sys.stderr)

Advanced Usage

Conditional Execution

In the Hook script, determine whether to proceed based on conditions:
def main():
input_data = json.loads(sys.stdin.read())

# Process only under specific conditions.
if not should_process(input_data):
print(json.dumps({"continue": True}))
return 0

# Execute Processing Logic
...

Combining Multiple Rules

Implement multiple rules in a single Hook script:
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 0

External Service Integration

Hooks can call external APIs or services:
import requests

def check_with_external_service(command):
response = requests.post('https://api.example.com/validate',
json={'command': command},
timeout=5)
return response.json()

FAQs

Q1: Why Is the Hook Not Executed?

Checklist
1. The configuration file path is correct (settings.json is in the .codebuddy directory).
2. The hooks field is configured correctly, and the JSON format is valid.
3. The matcher regular expression can match the target.
4. The Hook script has execute permission (chmod +x script.py).
5. The script path is correct. It is recommended to use an absolute path.
6. The first line of the script has the correct shebang (#!/usr/bin/env python3).

Q2: Why Does the Hook Execution Time Out?

Solution:
Increase the timeout configuration value.
Optimize Hook script performance.
Check for infinite loops or blocking operations.

Q3: How to Debug Hook Scripts?

Debugging steps:
1. Use echo to manually pass in test data.
2. Use sys.stderr in the script to output debug information.
3. Verify that the JSON format is correct.

Q4: What Is the Execution Order of Multiple Hooks?

Answer:
Hooks are executed in parallel, and their execution order is not guaranteed.
To execute sequentially, combine the logic into a single Hook script.
Identical commands are automatically deduplicated.

Q5: Why Are the Parameters Modified by the Hook Not Taking Effect?

Checklist:
Ensure that the modifiedInput field is returned.
Ensure that permissionDecision is allow.
Check whether the field name matches the tool parameter.
Verify that the JSON format is correct.
Ensure that continue is true.

Q6: Does the SessionStart Hook Trigger on Every Request?

Reason: SessionStart should be triggered only once for a new session.
Solution:
The system tracks sessions by conversationId.
Multiple requests within the same session will not be triggered repeatedly.
If the issue persists, check whether the session ID in the logs has changed.

Appendix

A. Complete HookInput Interface Definition

interface HookInput {
// Common Fields
session_id?: string; // Session ID
transcript_path?: string; // Path to the conversation transcript
cwd?: string; // Current working directory
hook_event_name: string; // Hook event name

// Dedicated to SessionStart (currently only 'startup' is supported)
source?: 'startup';

// Dedicated to UserPromptSubmit
prompt?: string; // User input content

// Dedicated to PreToolUse/PostToolUse
tool_name?: string; // Tool name
tool_input?: Record<string, any>; // Tool input parameters
tool_response?: any; // Tool response (PostToolUse only)

// Dedicated to Stop
stop_hook_active?: boolean; // Whether the Stop Hook is activated

// Dedicated to PreCompact
trigger?: 'manual' | 'auto'; // Trigger method
custom_instructions?: string; // Custom compression instructions
}

B. Complete HookOutput Interface Definition

interface HookOutput {
// Basic control
continue?: boolean; // Whether to continue execution (defaults to true)
stopReason?: string; // Stop reason
suppressOutput?: boolean; // Whether to suppress output
systemMessage?: string; // System message

// Hook-specific output
hookSpecificOutput?: {
hookEventName: string; // Hook event name

// Dedicated to PreToolUse
permissionDecision?: 'allow' | 'deny' | 'ask';
permissionDecisionReason?: string;
modifiedInput?: Record<string, any>;

// Dedicated to SessionStart, UserPromptSubmit, and PostToolUse
additionalContext?: string; // Additional context
};
}

C. Environment Variable 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. Detailed Description of Exit Codes

Exit Code
Description
stdout
stderr
Action
0
Successful
Process as result
Ignored.
Continue execution and may inject into the context.
1
Warning
Ignored.
Display as a warning
Continue execution
2
Block/Feedback
Ignored.
Pass to Agent
PreToolUse: Block execution<br>Stop: Provide feedback
Other
Error
Ignored.
Display as a warning
Continue execution

E. List of Common Tool Names

File Operation Tool:
read_file - Read a file
write_to_file - Write to a file
replace_in_file - Replace file content
delete_files - Delete files
list_files - List files
search_file - Search for files
Code Operation Tool:
search_content - Search for content
read_lints - Read Lint errors
Execution Tool:
execute_command - Run the command
preview_url - Preview URL
Other tools:
task - Create a subtask
web_search - Web search
web_fetch - Fetch web content
ask_followup_question - Ask the user

Summary

The Hook feature provides powerful extension capabilities, allowing you to insert custom logic at key points in the AI Agent. By using Hooks properly, you can
Enhanced security: Verify and block dangerous operations
Automated workflow: Intelligently modify parameters and automatically back up files
Monitoring and auditing: Log tool execution
Customized behavior: Inject project-specific context
Start using the Hook feature to make your AI Agent smarter, safer, and more aligned with your project requirements!
Happy Hooking!

Help and Support

Was this page helpful?

Help us improve! Rate your documentation experience in 5 mins.

Feedback