I create modern web applications and custom digital tools to help businesses grow through technological innovation. My passion is combining computer science and economics to generate real value.
My passion for computer science was born at the Technical Commercial Institute of Maglie, where I discovered the power of programming and the fascination of creating digital solutions. From the start, I understood that computer science was not just code, but an extraordinary tool for turning ideas into reality.
During my studies in Business Information Systems, I began to interweave computer science and economics, understanding how technology can be the engine of growth for any business. This vision accompanied me to the University of Bari, where I obtained my degree in Computer Science, deepening my technical skills and passion for software development.
Today I put this experience at the service of businesses, professionals and startups, creating tailor-made digital solutions that automate processes, optimize resources and open new business opportunities. Because true innovation begins when technology meets the real needs of people.
My Skills
Data Analysis & Predictive Models
I transform data into strategic insights with in-depth analysis and predictive models for informed decisions
Process Automation
I create custom tools that automate repetitive operations and free up time for value-added activities
Custom Systems
I develop tailor-made software systems, from platform integrations to customized dashboards
Credo fermamente che l'informatica sia lo strumento più potente per trasformare le idee in realtà e migliorare la vita delle persone.
Democratizzare la Tecnologia
La mia missione è rendere l'informatica accessibile a tutti: dalle piccole imprese locali alle startup innovative, fino ai professionisti che vogliono digitalizzare la propria attività. Ogni realtà merita di sfruttare le potenzialità del digitale.
Unire Informatica ed Economia
Non è solo questione di scrivere codice: è capire come la tecnologia possa generare valore reale. Intrecciando competenze informatiche e visione economica, aiuto le attività a crescere, ottimizzare processi e raggiungere nuovi traguardi di efficienza e redditività.
Creare Soluzioni su Misura
Ogni attività è unica, e così devono esserlo le soluzioni. Sviluppo strumenti personalizzati che rispondono alle esigenze specifiche di ciascun cliente, automatizzando processi ripetitivi e liberando tempo per ciò che conta davvero: far crescere il business.
Trasforma la Tua Attività con la Tecnologia
Che tu gestisca un negozio, uno studio professionale o un'azienda, posso aiutarti a sfruttare le potenzialità dell'informatica per lavorare meglio, più velocemente e in modo più intelligente.
Bari, Puglia, Italy · Hybrid
Analysis and development of computer systems through the use of Java and Quarkus in Health and Public Sector. Continuous training on modern technologies for creating customized and efficient software solutions and on agents.
💼
06/2022 - 12/2024
Software analyst and Back End Developer Associate Consultant
Links Management and Technology SpA
Experience analyzing as-is software systems and ETL flows using PowerCenter. Completed Spring Boot training for developing modern and scalable backend applications. Backend developer specialized in Spring Boot, with experience in database design, analysis, development and testing of assigned tasks.
💼
02/2021 - 10/2021
Software programmer
Adesso.it (prima era WebScience srl)
Experience in AS-IS and TO-BE analysis, SEO evolutions and website evolutions to improve user performance and engagement.
🎓
2018 - 2025
Degree in Computer Science
University of Bari Aldo Moro
Bachelor's degree in Computer Science, focusing on software engineering, algorithms, and modern development practices.
📚
2013 - 2018
Diploma - Corporate Information Systems
Technical Commercial Institute of Maglie
Technical diploma specializing in Business Information Systems, combining IT knowledge with business management.
Contattami
Hai un progetto in mente? Parliamone! Compila il form qui sotto e ti risponderò al più presto.
* Campi obbligatori. I tuoi dati saranno utilizzati solo per rispondere alla tua richiesta.
Hooks: Event-Driven Automation in Claude Code
Claude Code is a powerful tool on its own, but its true strength emerges when you extend it
with an event-driven automation system: Hooks. Hooks are shell commands
that are automatically executed in response to specific events during the lifecycle
of a Claude Code session, transforming a reactive AI assistant into a
proactive and customized development system.
Unlike MCP servers, which require creating separate processes and communication
through structured protocols, hooks operate as simple shell scripts executed
directly by the Claude Code process. This makes them extremely lightweight, quick to
implement, and perfect for automations that do not require the complexity of a dedicated
server. In this article we will explore every aspect of hooks in depth: from
configuration to creating complex automations, from real-world use cases to best
practices for effective usage.
What You Will Learn
Understand the event-driven hook system of Claude Code
Configure hooks in JSON settings files
Master all 6 available hook events
Create custom hooks for complex automations
Use community hooks like Britfix, CC Notify, and cchooks
Manage errors, performance, and security in hooks
Implement advanced inter-agent communication patterns
Apply best practices for a robust hooks ecosystem
Series Overview
#
Article
Focus
1
Claude as Technical Partner
Setup and first steps
2
Context and Project Setup
CLAUDE.md and configuration
3
Ideation and Requirements
MVP and user personas
4
Backend and Frontend Architecture
Spring Boot and Angular
5
Code Structure
Organization and conventions
6
Advanced Prompt Engineering
Advanced techniques
7
Testing and Quality
Strategies and test generation
8
Professional Documentation
README, API, ADR
9
Deploy and DevOps
Docker, CI/CD, monitoring
10
Evolution and Maintenance
Refactoring and scalability
11
Real Project Integration
Claude Code in production
12
Claude Code CLI
Command line mastery
13
Agent Skills
Extending Claude Code
14
Specialized Subagents
Orchestrating specialized agents
15
Hooks and Automation (you are here)
Event-driven automation
16
Ralph Wiggum Method
Autonomous iterative development
17
BMAD Method
AI-driven agile methodology
18
Multi-Agent Orchestration
Coordinating agent teams
19
Claude Cowork
Working together with Claude
20
Security and Privacy
Protecting the workflow
21
Monitoring and Productivity
Metrics and performance
1. What Are Claude Code Hooks
Claude Code hooks are user-defined shell commands that are
automatically executed in correspondence with specific events during a session.
They work similarly to git hooks or development framework lifecycle hooks:
they attach to key moments in the workflow and allow injecting custom logic
without modifying the agent's core behavior.
The philosophy behind hooks is simple but powerful: instead of asking Claude
to execute actions through prompts (which the agent might interpret differently
than expected), hooks guarantee deterministic and reliable execution.
The command is executed exactly as defined, every time, without variations
or creative interpretations.
Hooks vs MCP Servers: When to Use What
Feature
Hooks
MCP Servers
Complexity
Low - shell scripts
High - separate process
Latency
Minimal - direct execution
Higher - IPC communication
Model control
No - the LLM does not decide
Yes - the LLM chooses when to use them
Determinism
Total - always executed
Partial - depends on context
Configuration
JSON in settings.json
Dedicated server with SDK
Use cases
Automations, notifications, validations
Complex tools, API integrations
Security
Controlled local execution
Granular permissions per tool
Hook System Architecture
The hook system fits into Claude Code's execution cycle as a transparent
interception layer. Each time a supported event occurs,
Claude Code checks whether hooks are configured for that event, prepares the
context as JSON input via stdin, executes the specified shell command,
and processes the output to determine any flow modifications.
Hooks receive structured input in JSON format via stdin and can
return JSON output via stdout to influence the agent's behavior.
This stream-based approach enables the composition of complex processing
pipelines while maintaining a simple and universal interface.
Hooks are configured in the .claude/settings.json file within
the project directory or in the user's global configuration
(~/.claude/settings.json). The configuration structure is a JSON object
that maps events to lists of hooks, where each hook specifies the command to execute
and optionally a matcher to filter which tools or conditions trigger the hook.
Regex pattern to filter activation. Supports pipe for logical OR.
command
string
Yes
Shell command to execute. Can be an absolute or relative path.
timeout
number
No
Timeout in milliseconds. Default: 60000 (60 seconds).
The matcher field is particularly powerful because it supports complete
regular expressions. This means you can create hooks that respond only to specific
tools or tool combinations, keeping the system reactive and performant.
Advanced Matcher Examples
// Single tool
"matcher": "Write"
// Multiple tools with OR
"matcher": "Write|Edit|MultiEdit"
// Complex regex pattern
"matcher": "^(Bash|Terminal)$"
// All tools (no matcher = wildcard)
// Omit the matcher field
// Tools starting with "File"
"matcher": "^File.*"
// Exclude specific tools (regex negation)
"matcher": "^(?!Read).*$"
Configuration Levels
Claude Code supports three hierarchical configuration levels for hooks,
allowing you to define automations at different levels of granularity:
Project (.claude/settings.json): Hooks specific to the current repository. Ideal for project-related automations like linting, formatting, or team notifications.
User (~/.claude/settings.json): Global hooks that apply to all user sessions. Perfect for personal preferences like desktop notifications or logging.
Enterprise (/etc/claude/settings.json): Organization-level hooks. Used for corporate policies like audit logging or compliance checks.
When hooks exist at multiple levels for the same event, they are all executed in
order of precedence: enterprise first, then user, finally project. This ensures
that corporate policies are always respected, regardless of local configurations.
3. The 6 Hook Events in Detail
Claude Code exposes six distinct events to which hooks can be attached. Each event
corresponds to a precise moment in the session lifecycle and provides a specific
context through JSON input. Understanding each event in depth is fundamental
for designing effective and robust automations.
3.1 PreToolUse: Intercept Before Execution
The PreToolUse event fires before Claude Code executes
any tool. It is the most powerful interception point because it allows you to
inspect, modify, or block the execution of a tool
before it produces effects in the system.
JSON Input for PreToolUse
Field
Type
Description
tool_name
string
Tool name (e.g. "Write", "Bash", "Edit")
tool_input
object
Parameters passed to the tool
session_id
string
Unique session identifier
The hook can return a JSON output with three possible decisions:
"decision": "allow" - Allows the tool to execute without modifications
"decision": "block" - Completely blocks execution. Requires a reason field with the justification.
"decision": "modify" - Modifies the tool's parameters before execution. Requires a tool_input field with updated parameters.
pre-tool-guard.sh - Block dangerous operations
#!/bin/bash
# PreToolUse hook: block operations on protected files
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')
TOOL_INPUT=$(echo "$INPUT" | jq -r '.tool_input')
# Block writing to critical configuration files
if [ "$TOOL_NAME" = "Write" ] || [ "$TOOL_NAME" = "Edit" ]; then
FILE_PATH=$(echo "$TOOL_INPUT" | jq -r '.file_path // .path // ""')
# Protected files list
PROTECTED_FILES=(".env" ".env.production" "secrets.json" "credentials.yaml")
for PROTECTED in "
#123;PROTECTED_FILES[@]}"; do
if [[ "$FILE_PATH" == *"$PROTECTED"* ]]; then
echo "{\"decision\": \"block\", \"reason\": \"Protected file: $PROTECTED. Manual modification required.\"}"
exit 0
fi
done
fi
# Block destructive Bash commands
if [ "$TOOL_NAME" = "Bash" ]; then
COMMAND=$(echo "$TOOL_INPUT" | jq -r '.command // ""')
# Dangerous patterns
if echo "$COMMAND" | grep -qE "rm\s+-rf\s+/|DROP\s+TABLE|DELETE\s+FROM.*WHERE\s+1=1|format\s+c:"; then
echo "{\"decision\": \"block\", \"reason\": \"Potentially destructive command blocked.\"}"
exit 0
fi
fi
# Allow everything else
echo '{"decision": "allow"}'
auto-format.sh - Modify parameters before execution
#!/bin/bash
# PreToolUse hook: automatically adds headers to new files
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')
if [ "$TOOL_NAME" = "Write" ]; then
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path')
CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content')
# Add copyright header to TypeScript files
if [[ "$FILE_PATH" == *.ts ]]; then
HEADER="// Copyright $(date +%Y) - MyCompany\n// Auto-generated header by Claude Code hook\n\n"
MODIFIED_CONTENT="#123;HEADER}#123;CONTENT}"
echo "$INPUT" | jq --arg content "$MODIFIED_CONTENT" \
'{decision: "modify", tool_input: (.tool_input + {content: $content})}'
exit 0
fi
fi
echo '{"decision": "allow"}'
3.2 PostToolUse: React After Execution
The PostToolUse event fires after a tool has completed
its execution. It is the ideal point for logging, result validation,
output transformation, and consequent actions based on the operation's result.
Unlike PreToolUse, PostToolUse cannot block or modify the operation
(which has already occurred), but it can influence how Claude interprets the result
and can execute side actions such as updating logs, sending notifications, or triggering
external pipelines.
#!/usr/bin/env python3
"""PostToolUse hook: logs all file write operations."""
import json
import sys
from datetime import datetime
from pathlib import Path
def main():
input_data = json.loads(sys.stdin.read())
tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})
session_id = input_data.get("session_id", "unknown")
# Log only file modification operations
if tool_name in ("Write", "Edit", "MultiEdit"):
log_entry = {
"timestamp": datetime.now().isoformat(),
"session_id": session_id,
"tool": tool_name,
"file": tool_input.get("file_path", "N/A"),
"operation": "write" if tool_name == "Write" else "edit"
}
log_file = Path(".claude/hooks/activity.log")
log_file.parent.mkdir(parents=True, exist_ok=True)
with open(log_file, "a") as f:
f.write(json.dumps(log_entry) + "\n")
# Log executed Bash commands
if tool_name == "Bash":
command = tool_input.get("command", "")
log_entry = {
"timestamp": datetime.now().isoformat(),
"session_id": session_id,
"tool": "Bash",
"command": command[:200], # Truncate for safety
"exit_code": "success"
}
log_file = Path(".claude/hooks/bash-history.log")
log_file.parent.mkdir(parents=True, exist_ok=True)
with open(log_file, "a") as f:
f.write(json.dumps(log_entry) + "\n")
if __name__ == "__main__":
main()
3.3 UserPromptSubmit: Intercept User Prompts
The UserPromptSubmit event fires when the user sends a prompt
to Claude Code. This hook allows you to pre-process, enrich, or filter
user messages before they reach the model. It is particularly useful for
adding automatic context, applying prompt templates, or implementing security
filters on incoming messages.
The Stop event fires when Claude Code finishes generating a
response or completes a task. It is the ideal point for notifications, cleanup,
report generation, and any action that should happen at the end
of an interaction. This hook is particularly valuable for long tasks where
the user might not be in front of the terminal.
notify-complete.sh - Desktop notification on completion
#!/bin/bash
# Stop hook: sends desktop notification when Claude completes
INPUT=$(cat)
STOP_REASON=$(echo "$INPUT" | jq -r '.stop_reason // "completed"')
# Notification on Linux (notify-send)
if command -v notify-send &> /dev/null; then
notify-send "Claude Code" "Task completed: $STOP_REASON" \
--icon=dialog-information \
--urgency=normal
fi
# Notification on macOS (osascript)
if command -v osascript &> /dev/null; then
osascript -e "display notification \"Task completed: $STOP_REASON\" with title \"Claude Code\""
fi
# Completion sound
if command -v aplay &> /dev/null; then
aplay /usr/share/sounds/freedesktop/stereo/complete.oga 2>/dev/null &
elif command -v afplay &> /dev/null; then
afplay /System/Library/Sounds/Glass.aiff &
fi
3.5 SessionStart: Session Initialization
The SessionStart event fires at the beginning of every new
Claude Code session. It is the ideal moment for environment setup, loading
additional context, validating prerequisites, and preparing the resources
needed for the work session.
session-init.sh - Environment setup at startup
#!/bin/bash
# SessionStart hook: prepares the development environment
# Verify prerequisites
echo "Verifying development environment..." >&2
# Check that Node.js is available
if ! command -v node &> /dev/null; then
echo '{"warning": "Node.js not found. Some features may not work."}' >&2
fi
# Check that the development database is reachable
if command -v pg_isready &> /dev/null; then
if ! pg_isready -q 2>/dev/null; then
echo '{"warning": "PostgreSQL not reachable. Start the database."}' >&2
fi
fi
# Load environment variables from .env
if [ -f ".env" ]; then
export $(grep -v '^#' .env | xargs) 2>/dev/null
fi
# Record session start
echo "{\"session_start\": \"$(date -Iseconds)\", \"cwd\": \"$(pwd)\"}" \
>> .claude/hooks/sessions.log 2>/dev/null
# Clean up temporary files from previous sessions
find /tmp -name "claude-hook-*" -mtime +1 -delete 2>/dev/null
3.6 SessionEnd: Cleanup and Final Report
The SessionEnd event fires when a Claude Code session is closed.
It is the perfect point for saving state, generating session reports,
performing cleanup, and archiving useful information for future sessions.
#!/usr/bin/env python3
"""SessionEnd hook: generates a work session report."""
import json
import sys
from datetime import datetime
from pathlib import Path
def main():
input_data = json.loads(sys.stdin.read())
session_id = input_data.get("session_id", "unknown")
# Read the activity log
log_file = Path(".claude/hooks/activity.log")
activities = []
if log_file.exists():
with open(log_file) as f:
for line in f:
try:
entry = json.loads(line.strip())
if entry.get("session_id") == session_id:
activities.append(entry)
except json.JSONDecodeError:
continue
# Generate report
report = {
"session_id": session_id,
"end_time": datetime.now().isoformat(),
"total_operations": len(activities),
"files_modified": list(set(
a.get("file", "") for a in activities
if a.get("tool") in ("Write", "Edit", "MultiEdit")
)),
"bash_commands": sum(
1 for a in activities if a.get("tool") == "Bash"
)
}
# Save report
reports_dir = Path(".claude/hooks/reports")
reports_dir.mkdir(parents=True, exist_ok=True)
report_file = reports_dir / f"session-{session_id[:8]}.json"
with open(report_file, "w") as f:
json.dump(report, f, indent=2)
print(f"Session report saved: {report_file}", file=sys.stderr)
if __name__ == "__main__":
main()
Hook Events Summary
Event
When It Fires
Can Block?
Can Modify?
PreToolUse
Before a tool executes
Yes
Yes (tool parameters)
PostToolUse
After a tool executes
No
No (side effects only)
UserPromptSubmit
When the user sends a prompt
Yes
Yes (prompt text)
Stop
When Claude finishes generating
No
No
SessionStart
At the beginning of a new session
No
No
SessionEnd
When the session closes
No
No
4. Community Hooks: Ready-to-Use Solutions
The Claude Code community has developed a series of sophisticated hooks that solve
common developer needs. These open-source projects demonstrate the versatility
of the hook system and can be used directly or as a base for customizations.
4.1 Britfix: Automatic American-to-British English Conversion
Britfix is a PostToolUse hook that automatically converts American
English spelling to British English spelling in files written by Claude Code. It was
created for teams that work with British standards but use an AI model
trained predominantly on American texts.
The hook intercepts every file write operation, analyzes the content for words
with American spelling (such as "color", "optimize", "center") and replaces them with
the British variants ("colour", "optimise", "centre"). The process is transparent
and requires no manual intervention.
CC Notify is an advanced desktop notification system that goes beyond
simple completion notifications. It supports differentiated notifications by event
type, notification history, integration with messaging systems
(Slack, Discord, Telegram), and granular configuration by activity type.
A particularly useful feature of CC Notify is its ability to estimate the remaining
time for long tasks based on the history of previous sessions, allowing
the user to plan their time more effectively while Claude Code
works in the background.
cchooks is a Python SDK that drastically simplifies the creation
of complex hooks. Instead of writing shell scripts with manual JSON parsing, cchooks
provides typed Python decorators, automatic input/output handling, and a
utility library for common operations.
Hook with cchooks SDK
#!/usr/bin/env python3
"""Hook created with cchooks SDK."""
from cchooks import hook, PreToolUseInput, HookDecision
@hook("PreToolUse")
def validate_file_size(event: PreToolUseInput) -> HookDecision:
"""Block writing files that are too large."""
if event.tool_name == "Write":
content = event.tool_input.get("content", "")
max_lines = 500
if content.count("\n") > max_lines:
return HookDecision.block(
reason=f"File too large: {content.count(chr(10))} lines "
f"(max {max_lines}). Split into smaller files."
)
return HookDecision.allow()
@hook("PostToolUse")
def auto_lint(event):
"""Runs automatic linting after every file modification."""
import subprocess
if event.tool_name in ("Write", "Edit"):
file_path = event.tool_input.get("file_path", "")
if file_path.endswith(".ts") or file_path.endswith(".js"):
subprocess.run(
["npx", "eslint", "--fix", file_path],
capture_output=True, timeout=30
)
elif file_path.endswith(".py"):
subprocess.run(
["python3", "-m", "black", file_path],
capture_output=True, timeout=30
)
4.4 Claude Code Hook Comms: Real-Time Inter-Agent Communication
Claude Code Hook Comms enables real-time communication between
multiple Claude Code instances working on the same project or related projects.
It uses a messaging system based on shared files or sockets to coordinate
the activities of multiple agents, avoiding conflicts and work duplication.
This hook is particularly useful in multi-agent development scenarios where different
Claude Code instances work on different aspects of a project (for example frontend and backend)
and need to synchronize on changes to shared interfaces.
Creating custom hooks requires understanding the input/output format,
choosing the appropriate scripting language, and paying attention to robustness and
efficiency. This section provides a complete path from creation to
production deployment of professional hooks.
Step 1: Define the Objective
Before writing code, it is essential to clearly define what the hook should do:
Which event? Identify the exact moment when the hook should intervene
Which action? Define what it should do (block, modify, log, notify)
Which conditions? Specify when the hook should activate (matcher)
What impact? Evaluate the consequences of the hook on the workflow
Testing hooks is fundamental because errors in the script can block
or slow down the entire Claude Code session. Here is a structured approach to testing:
Beyond basic usage, hooks can be combined into sophisticated patterns
that solve complex automation needs. This section presents some
of the most useful patterns that have emerged from community experience.
Pattern: Auto-Commit on Checkpoint
This pattern automatically creates Git commits at regular intervals during
a development session, ensuring that work is never lost and
providing granular rollback points.
auto-checkpoint.sh - Automatic commits
#!/bin/bash
# PostToolUse hook: creates automatic checkpoints
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')
# Only for file modification operations
if [ "$TOOL_NAME" = "Write" ] || [ "$TOOL_NAME" = "Edit" ]; then
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
# Check if there are enough changes for a checkpoint
CHANGES=$(git diff --stat 2>/dev/null | tail -1)
NUM_FILES=$(git diff --name-only 2>/dev/null | wc -l)
if [ "$NUM_FILES" -ge 3 ]; then
git add -A 2>/dev/null
git commit -m "checkpoint: auto-save after $NUM_FILES files modified" \
--no-verify 2>/dev/null
echo "[CHECKPOINT] Automatic commit: $NUM_FILES files" >&2
fi
fi
Pattern: Quality Gate Pre-Commit
This pattern automatically runs quality checks (linting, type checking,
unit tests) before allowing write operations, ensuring that every
modification meets the project's standards.
quality-gate.sh - Automatic quality checks
#!/bin/bash
# PreToolUse hook: quality gate for TypeScript files
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')
if [ "$TOOL_NAME" = "Write" ]; then
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path')
CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content')
if [[ "$FILE_PATH" == *.ts ]]; then
# Write the content to a temporary file for analysis
TEMP_FILE=$(mktemp /tmp/claude-hook-XXXXXX.ts)
echo "$CONTENT" > "$TEMP_FILE"
# Run type checking
ERRORS=$(npx tsc --noEmit --strict "$TEMP_FILE" 2>&1 || true)
rm -f "$TEMP_FILE"
if echo "$ERRORS" | grep -q "error TS"; then
ERROR_COUNT=$(echo "$ERRORS" | grep -c "error TS")
echo "{\"decision\": \"block\", \"reason\": \"Type check failed: $ERROR_COUNT TypeScript errors found. Fix before writing.\"}"
exit 0
fi
fi
fi
echo '{"decision": "allow"}'
Pattern: Context Enrichment Pipeline
This pattern automatically enriches every user prompt with relevant
contextual information, such as repository state, recently modified files,
failed tests, and project metrics.
context-pipeline.sh - Context enrichment
#!/bin/bash
# UserPromptSubmit hook: context enrichment pipeline
INPUT=$(cat)
PROMPT=$(echo "$INPUT" | jq -r '.prompt')
# Gather context
CONTEXT_PARTS=()
# 1. Git state
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
if [ -n "$BRANCH" ]; then
DIRTY=$(git status --porcelain 2>/dev/null | wc -l)
CONTEXT_PARTS+=("[Branch: $BRANCH, Modified files: $DIRTY]")
fi
# 2. Recent failed tests
if [ -f "test-results.json" ]; then
FAILED=$(jq '.numFailedTests // 0' test-results.json 2>/dev/null)
if [ "$FAILED" -gt 0 ]; then
CONTEXT_PARTS+=("[WARNING: $FAILED tests failed]")
fi
fi
# 3. Pending TODOs
TODO_COUNT=$(grep -r "TODO\|FIXME\|HACK" src/ --include="*.ts" 2>/dev/null | wc -l)
if [ "$TODO_COUNT" -gt 0 ]; then
CONTEXT_PARTS+=("[Open TODOs in code: $TODO_COUNT]")
fi
# Build aggregated context
if [ #123;#CONTEXT_PARTS[@]} -gt 0 ]; then
CONTEXT=$(printf "%s " "#123;CONTEXT_PARTS[@]}")
ENRICHED="$CONTEXT\n\n$PROMPT"
echo "$INPUT" | jq --arg p "$ENRICHED" '{decision: "modify", prompt: $p}'
else
echo '{"decision": "allow"}'
fi
7. Performance, Security, and Best Practices
Hooks are executed synchronously within Claude Code's lifecycle,
which means slow or malfunctioning hooks can significantly degrade
the user experience. Following performance and security best practices is essential
for maintaining a smooth and protected workflow.
Performance Optimization
Golden Rules for Performant Hooks
Rule
Motivation
Implementation
Aggressive timeouts
Avoid session blocking
Set timeout to 5-10 seconds for critical hooks
Specific matchers
Reduce unnecessary executions
Use precise regex instead of wildcards
Async operations
Do not block on slow I/O
Delegate heavy operations to background processes
Local cache
Avoid expensive recalculations
Save intermediate results in temporary files
Early exit
Minimize execution time
Check exit conditions at the beginning of the script
Controlled logging
Avoid excessive disk I/O
Use log rotation and severity levels
Hook Security
Hooks execute shell commands with the current user's permissions, which
represents both an advantage (full system access) and a risk (uncontrolled
execution). It is fundamental to apply rigorous security principles:
Principle of least privilege: Each hook should have access only to the resources strictly necessary for its operation
Input validation: All data received via stdin must be validated and sanitized before use
No hardcoded secrets: API keys, passwords, and tokens must never be included directly in hook scripts
Audit trail: Maintain a log of all actions performed by hooks for traceability
Periodic review: Regularly review configured hooks to identify emerging risks
Warning: Security Risks
Project hooks (defined in .claude/settings.json) are executed
automatically when a Claude Code session is opened in the repository. This means
a malicious repository could include harmful hooks. Always verify
configuration files before opening sessions in untrusted repositories.
Error Handling
A hook that fails silently is worse than a hook that does not exist. Implementing
robust error handling is fundamental for the system's maintainability:
robust-hook.sh - Hook with complete error handling
#!/bin/bash
set -euo pipefail
# Trap to handle errors and cleanup
cleanup() {
local exit_code=$?
if [ $exit_code -ne 0 ]; then
echo "[HOOK ERROR] Exit code: $exit_code" >&2
echo "{\"error\": \"Hook failed with code $exit_code\"}" >&2
fi
# Cleanup temporary resources
rm -f /tmp/claude-hook-$.tmp 2>/dev/null
}
trap cleanup EXIT
# Timeout for the entire script
TIMEOUT=10
(
sleep $TIMEOUT
echo "[HOOK TIMEOUT] Script exceeded $TIMEOUT seconds" >&2
kill $ 2>/dev/null
) &
TIMEOUT_PID=$!
# Main logic with error handling
INPUT=$(cat) || {
echo '{"decision": "allow"}' # Safe fallback
exit 0
}
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""' 2>/dev/null) || TOOL_NAME=""
# ... hook logic ...
# Cancel the timeout timer
kill $TIMEOUT_PID 2>/dev/null || true
echo '{"decision": "allow"}'
8. Complete Example: Hook Ecosystem for an Angular Project
To conclude, we present a complete hook ecosystem configured
for an Angular project with SSR, covering all six events and demonstrating
how hooks collaborate to create a robust and automated development environment.