Hooks: Automazione Event-Driven in Claude Code
Claude Code è uno strumento potente di per se, ma la sua vera forza emerge quando lo si estende con un sistema di automazione event-driven: gli Hooks. Gli hooks sono comandi shell che vengono eseguiti automaticamente in risposta a eventi specifici durante il ciclo di vita di una sessione Claude Code, trasformando un assistente AI reattivo in un sistema di sviluppo proattivo e personalizzato.
A differenza dei server MCP, che richiedono la creazione di processi separati e comunicazione tramite protocolli strutturati, gli hooks operano come semplici script shell eseguiti direttamente dal processo Claude Code. Questo li rende estremamente leggeri, veloci da implementare e perfetti per automazioni che non necessitano della complessità di un server dedicato. In questo articolo esploreremo in profondità ogni aspetto degli hooks: dalla configurazione alla creazione di automazioni complesse, dai casi d'uso reali alle best practice per un utilizzo efficace.
Cosa Imparerai
- Comprendere il sistema di hooks event-driven di Claude Code
- Configurare hooks nei file di settings JSON
- Padroneggiare tutti e 6 gli eventi hook disponibili
- Creare hooks personalizzati per automazioni complesse
- Utilizzare hook di community come Britfix, CC Notify e cchooks
- Gestire errori, performance e sicurezza negli hooks
- Implementare pattern avanzati di comunicazione tra agenti
- Applicare best practice per un ecosistema hooks robusto
Panoramica della Serie
| # | Articolo | Focus |
|---|---|---|
| 1 | Claude come Partner Tecnico | Setup e primi passi |
| 2 | Contesto e Setup del Progetto | CLAUDE.md e configurazione |
| 3 | Ideazione e Requisiti | MVP e user personas |
| 4 | Architettura Backend e Frontend | Spring Boot e Angular |
| 5 | Struttura del Codice | Organizzazione e convenzioni |
| 6 | Prompt Engineering Avanzato | Tecniche avanzate |
| 7 | Testing e qualità | Strategie e generazione test |
| 8 | Documentazione Professionale | README, API, ADR |
| 9 | Deploy e DevOps | Docker, CI/CD, monitoring |
| 10 | Evoluzione e Manutenzione | Refactoring e scalabilità |
| 11 | Integrazione Progetto Reale | Claude Code in produzione |
| 12 | CLI Avanzata e Comandi | Padronanza della riga di comando |
| 13 | Custom Slash Commands e Skills | Estendere Claude Code |
| 14 | Sub-Agenti e Delegazione | Orchestrare agenti specializzati |
| 15 | Hooks: Automazione Event-Driven (sei qui) | Automazione basata su eventi |
| 16 | Ralph Wiggum: Loop AI Autonomi | Sviluppo autonomo iterativo |
| 17 | BMAD Method: Sviluppo Agile AI | Metodologia agile AI-driven |
| 18 | Orchestrazione Multi-Agente | Coordinare team di agenti |
| 19 | Collaborazione e Coworking AI | Lavorare insieme a Claude |
| 20 | Sicurezza e Permessi | Proteggere il workflow |
| 21 | Monitoring e Ottimizzazione | Metriche e performance |
1. Cosa Sono gli Hooks di Claude Code
Gli hooks di Claude Code sono comandi shell definiti dall'utente che vengono eseguiti automaticamente in corrispondenza di eventi specifici durante una sessione. Funzionano in modo analogo ai git hooks o ai lifecycle hooks dei framework di sviluppo: si agganciano a momenti chiave del flusso di lavoro e permettono di iniettare logica personalizzata senza modificare il comportamento core dell'agente.
La filosofia alla base degli hooks è semplice ma potente: anziche richiedere a Claude di eseguire azioni tramite prompt (che l'agente potrebbe interpretare in modo diverso dal previsto), gli hooks garantiscono un'esecuzione deterministica e affidabile. Il comando viene eseguito esattamente come definito, ogni volta, senza variazioni o interpretazioni creative.
Hooks vs MCP Servers: Quando Usare Cosa
| Caratteristica | Hooks | MCP Servers |
|---|---|---|
| Complessità | Bassa - script shell | Alta - processo separato |
| Latenza | Minima - esecuzione diretta | Maggiore - comunicazione IPC |
| Controllo modello | No - l'LLM non decide | Si - l'LLM sceglie quando usarli |
| Determinismo | Totale - sempre eseguiti | Parziale - dipende dal contesto |
| Configurazione | JSON in settings.json | Server dedicato con SDK |
| Casi d'uso | Automazioni, notifiche, validazioni | Strumenti complessi, integrazioni API |
| Sicurezza | Esecuzione locale controllata | Permessi granulari per tool |
Architettura del Sistema Hooks
Il sistema hooks si inserisce nel ciclo di esecuzione di Claude Code come un layer di intercettazione trasparente. Ogni volta che si verifica un evento supportato, Claude Code verifica se esistono hooks configurati per quell'evento, prepara il contesto sotto forma di input JSON tramite stdin, esegue il comando shell specificato e processa l'output per determinare eventuali modifiche al flusso.
Gli hooks ricevono input strutturato in formato JSON tramite stdin e possono
restituire output JSON tramite stdout per influenzare il comportamento
dell'agente. Questo approccio basato su stream consente la composizione di pipeline
di elaborazione complesse mantenendo un'interfaccia semplice e universale.
┌──────────────────────┐
│ Evento Trigger │
│ (es. PreToolUse) │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ Preparazione Input │
│ (JSON via stdin) │
│ - tool_name │
│ - tool_input │
│ - session_id │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ Esecuzione Comando │ ← Script shell definito dall'utente
│ (shell command) │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ Elaborazione Output │
│ (JSON via stdout) │
│ - decision: allow │
│ - decision: block │
│ - decision: modify │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ Continuazione Flusso│
│ Claude Code │
└──────────────────────┘
2. Configurazione degli Hooks
Gli hooks vengono configurati nel file .claude/settings.json all'interno
della directory del progetto o nella configurazione globale dell'utente
(~/.claude/settings.json). La struttura di configurazione è un oggetto JSON
che mappa eventi a liste di hook, dove ogni hook specifica il comando da eseguire
e opzionalmente un matcher per filtrare su quali strumenti o condizioni attivare l'hook.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"command": "/path/to/script.sh"
}
],
"PostToolUse": [
{
"matcher": "Bash",
"command": "python3 /path/to/logger.py"
}
],
"Stop": [
{
"command": "notify-send 'Claude Code' 'Task completato!'"
}
]
}
}
Proprietà di un Hook
Campi di Configurazione
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
matcher | string (regex) | No | Pattern regex per filtrare l'attivazione. Supporta pipe per OR logico. |
command | string | Si | Comando shell da eseguire. Può essere un percorso assoluto o relativo. |
timeout | number | No | Timeout in millisecondi. Default: 60000 (60 secondi). |
Il campo matcher è particolarmente potente perchè supporta espressioni regolari
complete. Questo significa che si possono creare hook che rispondono solo a specifici
strumenti o combinazioni di strumenti, mantenendo il sistema reattivo e performante.
// Singolo tool
"matcher": "Write"
// Multipli tool con OR
"matcher": "Write|Edit|MultiEdit"
// Pattern regex complesso
"matcher": "^(Bash|Terminal)$"
// Tutti i tool (nessun matcher = wildcard)
// Omettere il campo matcher
// Tool che iniziano con "File"
"matcher": "^File.*"
// Escludere specifici tool (negazione regex)
"matcher": "^(?!Read).*$"
Livelli di Configurazione
Claude Code supporta tre livelli gerarchici di configurazione per gli hooks, permettendo di definire automazioni a diversi livelli di granularità:
- Progetto (
.claude/settings.json): Hooks specifici per il repository corrente. Ideali per automazioni legate al progetto come linting, formattazione o notifiche di team. - Utente (
~/.claude/settings.json): Hooks globali che si applicano a tutte le sessioni dell'utente. Perfetti per preferenze personali come notifiche desktop o logging. - Enterprise (
/etc/claude/settings.json): Hooks a livello di organizzazione. Usati per policy aziendali come audit logging o compliance checks.
Quando esistono hooks a più livelli per lo stesso evento, vengono eseguiti tutti in ordine di precedenza: prima enterprise, poi utente, infine progetto. Questo garantisce che le policy aziendali vengano sempre rispettate, indipendentemente dalle configurazioni locali.
3. I 6 Eventi Hook in Dettaglio
Claude Code espone sei eventi distinti a cui è possibile agganciare hooks. Ogni evento corrisponde a un momento preciso nel ciclo di vita di una sessione e fornisce un contesto specifico tramite l'input JSON. Comprendere ogni evento in profondità è fondamentale per progettare automazioni efficaci e robuste.
3.1 PreToolUse: Intercettare Prima dell'Esecuzione
L'evento PreToolUse si attiva prima che Claude Code esegua qualsiasi strumento. E' il punto di intercettazione più potente perchè consente di ispezionare, modificare o bloccare l'esecuzione di uno strumento prima che produca effetti nel sistema.
Input JSON per PreToolUse
| Campo | Tipo | Descrizione |
|---|---|---|
tool_name | string | Nome dello strumento (es. "Write", "Bash", "Edit") |
tool_input | object | Parametri passati allo strumento |
session_id | string | Identificativo univoco della sessione |
L'hook può restituire un JSON di output con tre possibili decisioni:
"decision": "allow"- Permette l'esecuzione dello strumento senza modifiche"decision": "block"- Blocca completamente l'esecuzione. Richiede un camporeasoncon la motivazione."decision": "modify"- Modifica i parametri dello strumento prima dell'esecuzione. Richiede un campotool_inputcon i parametri aggiornati.
#!/bin/bash
# Hook PreToolUse: blocca operazioni su file protetti
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')
TOOL_INPUT=$(echo "$INPUT" | jq -r '.tool_input')
# Blocca scrittura su file di configurazione critica
if [ "$TOOL_NAME" = "Write" ] || [ "$TOOL_NAME" = "Edit" ]; then
FILE_PATH=$(echo "$TOOL_INPUT" | jq -r '.file_path // .path // ""')
# Lista file protetti
PROTECTED_FILES=(".env" ".env.production" "secrets.json" "credentials.yaml")
for PROTECTED in "${PROTECTED_FILES[@]}"; do
if [[ "$FILE_PATH" == *"$PROTECTED"* ]]; then
echo "{\"decision\": \"block\", \"reason\": \"File protetto: $PROTECTED. Modifica manuale richiesta.\"}"
exit 0
fi
done
fi
# Blocca comandi Bash distruttivi
if [ "$TOOL_NAME" = "Bash" ]; then
COMMAND=$(echo "$TOOL_INPUT" | jq -r '.command // ""')
# Pattern pericolosi
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\": \"Comando potenzialmente distruttivo bloccato.\"}"
exit 0
fi
fi
# Permettere tutto il resto
echo '{"decision": "allow"}'
#!/bin/bash
# Hook PreToolUse: aggiunge automaticamente header ai nuovi file
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')
# Aggiungi header copyright ai file TypeScript
if [[ "$FILE_PATH" == *.ts ]]; then
HEADER="// Copyright $(date +%Y) - MyCompany\n// Auto-generated header by Claude Code hook\n\n"
MODIFIED_CONTENT="${HEADER}${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: Reagire Dopo l'Esecuzione
L'evento PostToolUse si attiva dopo che uno strumento ha completato la sua esecuzione. E' il punto ideale per logging, validazione dei risultati, trasformazione dell'output e azioni consequenti basate sul risultato dell'operazione.
A differenza di PreToolUse, PostToolUse non può bloccare o modificare l'operazione (che e già avvenuta), ma può influenzare il modo in cui Claude interpreta il risultato e può eseguire azioni collaterali come aggiornare log, inviare notifiche o attivare pipeline esterne.
Input JSON per PostToolUse
| Campo | Tipo | Descrizione |
|---|---|---|
tool_name | string | Nome dello strumento eseguito |
tool_input | object | Parametri usati per l'esecuzione |
tool_output | string | Risultato dello strumento |
session_id | string | Identificativo della sessione |
#!/usr/bin/env python3
"""Hook PostToolUse: logga tutte le operazioni di scrittura file."""
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")
# Logga solo operazioni di modifica file
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")
# Logga comandi Bash eseguiti
if tool_name == "Bash":
command = tool_input.get("command", "")
log_entry = {
"timestamp": datetime.now().isoformat(),
"session_id": session_id,
"tool": "Bash",
"command": command[:200], # Tronca per sicurezza
"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: Intercettare i Prompt Utente
L'evento UserPromptSubmit si attiva quando l'utente invia un prompt a Claude Code. Questo hook permette di pre-processare, arricchire o filtrare i messaggi dell'utente prima che raggiungano il modello. E' particolarmente utile per aggiungere contesto automatico, applicare template di prompt o implementare filtri di sicurezza sui messaggi in ingresso.
#!/bin/bash
# Hook UserPromptSubmit: aggiunge contesto Git automatico
INPUT=$(cat)
PROMPT=$(echo "$INPUT" | jq -r '.prompt')
# Aggiungi informazioni sul branch corrente
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "N/A")
LAST_COMMIT=$(git log --oneline -1 2>/dev/null || echo "N/A")
DIRTY_FILES=$(git status --porcelain 2>/dev/null | wc -l)
# Crea contesto arricchito
CONTEXT="[Git Context: branch=$BRANCH, last_commit=$LAST_COMMIT, dirty_files=$DIRTY_FILES]"
# Modifica il prompt aggiungendo il contesto
ENRICHED_PROMPT="$CONTEXT\n\n$PROMPT"
echo "$INPUT" | jq --arg prompt "$ENRICHED_PROMPT" \
'{decision: "modify", prompt: $prompt}'
3.4 Stop: Azioni alla Fine della Generazione
L'evento Stop si attiva quando Claude Code termina di generare una risposta o completa un task. E' il punto ideale per notifiche, cleanup, generazione di report e qualsiasi azione che debba avvenire al termine di un'interazione. Questo hook è particolarmente prezioso per task lunghi dove l'utente potrebbe non essere davanti al terminale.
#!/bin/bash
# Hook Stop: invia notifica desktop quando Claude completa
INPUT=$(cat)
STOP_REASON=$(echo "$INPUT" | jq -r '.stop_reason // "completed"')
# Notifica su Linux (notify-send)
if command -v notify-send &> /dev/null; then
notify-send "Claude Code" "Task completato: $STOP_REASON" \
--icon=dialog-information \
--urgency=normal
fi
# Notifica su macOS (osascript)
if command -v osascript &> /dev/null; then
osascript -e "display notification \"Task completato: $STOP_REASON\" with title \"Claude Code\""
fi
# Suono di completamento
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: Inizializzazione della Sessione
L'evento SessionStart si attiva all'inizio di ogni nuova sessione Claude Code. E' il momento ideale per setup dell'ambiente, caricamento di contesto aggiuntivo, validazione dei prerequisiti e preparazione delle risorse necessarie per la sessione di lavoro.
#!/bin/bash
# Hook SessionStart: prepara l'ambiente di sviluppo
# Verifica prerequisiti
echo "Verificando ambiente di sviluppo..." >&2
# Controlla che Node.js sia disponibile
if ! command -v node &> /dev/null; then
echo '{"warning": "Node.js non trovato. Alcune funzionalità potrebbero non funzionare."}' >&2
fi
# Controlla che il database di sviluppo sia raggiungibile
if command -v pg_isready &> /dev/null; then
if ! pg_isready -q 2>/dev/null; then
echo '{"warning": "PostgreSQL non raggiungibile. Avviare il database."}' >&2
fi
fi
# Carica variabili d'ambiente dal .env
if [ -f ".env" ]; then
export $(grep -v '^#' .env | xargs) 2>/dev/null
fi
# Registra inizio sessione
echo "{\"session_start\": \"$(date -Iseconds)\", \"cwd\": \"$(pwd)\"}" \
>> .claude/hooks/sessions.log 2>/dev/null
# Pulisci file temporanei di sessioni precedenti
find /tmp -name "claude-hook-*" -mtime +1 -delete 2>/dev/null
3.6 SessionEnd: Cleanup e Report Finale
L'evento SessionEnd si attiva alla chiusura di una sessione Claude Code. E' il punto perfetto per salvare lo stato, generare report di sessione, eseguire cleanup e archiviare informazioni utili per sessioni future.
#!/usr/bin/env python3
"""Hook SessionEnd: genera un report della sessione di lavoro."""
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")
# Leggi il log delle attivita
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
# Genera 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"
)
}
# Salva 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"Report sessione salvato: {report_file}", file=sys.stderr)
if __name__ == "__main__":
main()
Riepilogo Eventi Hook
| Evento | Quando si Attiva | Può Bloccare? | Può Modificare? |
|---|---|---|---|
| PreToolUse | Prima dell'esecuzione di un tool | Si | Si (parametri tool) |
| PostToolUse | Dopo l'esecuzione di un tool | No | No (solo side effects) |
| UserPromptSubmit | Quando l'utente invia un prompt | Si | Si (testo prompt) |
| Stop | Quando Claude finisce di generare | No | No |
| SessionStart | All'inizio di una nuova sessione | No | No |
| SessionEnd | Alla chiusura della sessione | No | No |
4. Hook della Community: Soluzioni Pronte all'Uso
La community di Claude Code ha sviluppato una serie di hook sofisticati che risolvono esigenze comuni degli sviluppatori. Questi progetti open-source dimostrano la versatilita del sistema hooks e possono essere utilizzati direttamente o come base per personalizzazioni.
4.1 Britfix: Conversione Automatica Inglese Americano-Britannico
Britfix è un hook PostToolUse che converte automaticamente l'ortografia inglese americana in ortografia britannica nei file scritti da Claude Code. E' stato creato per team che lavorano con standard britannici ma utilizzano un modello AI addestrato prevalentemente su testi americani.
L'hook intercetta ogni operazione di scrittura file, analizza il contenuto per parole con ortografia americana (come "color", "optimize", "center") e le sostituisce con le varianti britanniche ("colour", "optimise", "centre"). Il processo è trasparente e non richiede alcun intervento manuale.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"command": "npx britfix --auto-fix"
}
]
}
}
4.2 CC Notify: Notifiche Desktop Intelligenti
CC Notify è un sistema di notifiche desktop avanzato che va oltre la semplice notifica di completamento. Supporta notifiche differenziate per tipo di evento, storico delle notifiche, integrazione con sistemi di messaggistica (Slack, Discord, Telegram) e configurazione granulare per tipo di attivita.
Una funzionalità particolarmente utile di CC Notify è la capacità di stimare il tempo rimanente per task lunghi basandosi sullo storico delle sessioni precedenti, permettendo all'utente di pianificare il proprio tempo in modo più efficace mentre Claude Code lavora in background.
{
"hooks": {
"Stop": [
{
"command": "cc-notify --channel slack --sound chime --message 'Task completato'"
}
],
"SessionStart": [
{
"command": "cc-notify --channel desktop --message 'Sessione Claude avviata'"
}
],
"PreToolUse": [
{
"matcher": "Bash",
"command": "cc-notify --log-activity bash"
}
]
}
}
4.3 cchooks: SDK Python per Hook Professionali
cchooks è un SDK Python che semplifica drasticamente la creazione di hook complessi. Invece di scrivere script shell con parsing JSON manuale, cchooks fornisce decoratori Python tipizzati, gestione automatica dell'input/output e una libreria di utilita per operazioni comuni.
#!/usr/bin/env python3
"""Hook creato con cchooks SDK."""
from cchooks import hook, PreToolUseInput, HookDecision
@hook("PreToolUse")
def validate_file_size(event: PreToolUseInput) -> HookDecision:
"""Blocca la scrittura di file troppo grandi."""
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 troppo grande: {content.count(chr(10))} righe "
f"(max {max_lines}). Suddividi in file più piccoli."
)
return HookDecision.allow()
@hook("PostToolUse")
def auto_lint(event):
"""Esegue linting automatico dopo ogni modifica file."""
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: Comunicazione Real-Time tra Agenti
Claude Code Hook Comms abilita la comunicazione in tempo reale tra più istanze di Claude Code che lavorano sullo stesso progetto o su progetti correlati. Utilizza un sistema di messaggistica basato su file condivisi o socket per coordinare le attivita di agenti multipli, evitando conflitti e duplicazioni di lavoro.
Questo hook è particolarmente utile in scenari di sviluppo multi-agente dove diversi Claude Code lavorano su aspetti diversi di un progetto (ad esempio frontend e backend) e devono sincronizzarsi su modifiche alle interfacce condivise.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"command": "claude-hook-comms publish --topic file-changes --data-from-stdin"
}
],
"SessionStart": [
{
"command": "claude-hook-comms subscribe --topic file-changes --handler sync-handler.sh"
}
]
}
}
// sync-handler.sh - Gestisce le notifiche di cambio file
#!/bin/bash
INPUT=$(cat)
CHANGED_FILE=$(echo "$INPUT" | jq -r '.file_path')
AGENT_ID=$(echo "$INPUT" | jq -r '.agent_id')
echo "[SYNC] Agente $AGENT_ID ha modificato: $CHANGED_FILE" >> .claude/hooks/sync.log
5. Creare Hook Personalizzati: Guida Passo-Passo
Creare hook personalizzati richiede la comprensione del formato di input/output, la scelta del linguaggio di scripting appropriato e l'attenzione a robustezza ed efficienza. Questa sezione fornisce un percorso completo dalla creazione alla messa in produzione di hook professionali.
Step 1: Definire l'Obiettivo
Prima di scrivere codice, è essenziale definire chiaramente cosa deve fare l'hook:
- Quale evento? Identificare il momento esatto in cui l'hook deve intervenire
- Quale azione? Definire cosa deve fare (bloccare, modificare, loggare, notificare)
- Quali condizioni? Specificare quando l'hook deve attivarsi (matcher)
- Quale impatto? Valutare le conseguenze dell'hook sul workflow
Step 2: Scrivere lo Script
#!/bin/bash
set -euo pipefail
# Leggi input JSON da stdin
INPUT=$(cat)
# Estrai campi comuni
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""')
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // ""')
# ================================
# LOGICA DEL HOOK
# ================================
# Esempio: loggare l'operazione
echo "[$(date -Iseconds)] Hook triggered: tool=$TOOL_NAME session=$SESSION_ID" >&2
# ================================
# OUTPUT (solo per PreToolUse/UserPromptSubmit)
# ================================
# Opzione 1: Permettere
echo '{"decision": "allow"}'
# Opzione 2: Bloccare
# echo '{"decision": "block", "reason": "Motivo del blocco"}'
# Opzione 3: Modificare
# echo '{"decision": "modify", "tool_input": {...}}'
Step 3: Testare l'Hook
Il testing degli hook è fondamentale perchè errori nello script possono bloccare o rallentare l'intera sessione Claude Code. Ecco un approccio strutturato al testing:
#!/bin/bash
# Testa un hook simulando l'input JSON
HOOK_SCRIPT="./my-hook.sh"
# Simula input PreToolUse per Write
echo '{
"tool_name": "Write",
"tool_input": {
"file_path": "/tmp/test.ts",
"content": "console.log(\"hello\");"
},
"session_id": "test-session-001"
}' | bash "$HOOK_SCRIPT"
echo "---"
# Simula input PreToolUse per Bash con comando pericoloso
echo '{
"tool_name": "Bash",
"tool_input": {
"command": "rm -rf /"
},
"session_id": "test-session-001"
}' | bash "$HOOK_SCRIPT"
echo "---"
# Verifica exit code
echo "Exit code: $?"
Step 4: Registrare e Attivare
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"command": "bash .claude/hooks/pre-tool-guard.sh"
},
{
"matcher": "Bash",
"command": "bash .claude/hooks/bash-validator.sh"
}
],
"PostToolUse": [
{
"command": "python3 .claude/hooks/post-tool-logger.py"
}
],
"Stop": [
{
"command": "bash .claude/hooks/notify-complete.sh"
}
],
"SessionStart": [
{
"command": "bash .claude/hooks/session-init.sh"
}
],
"SessionEnd": [
{
"command": "python3 .claude/hooks/session-report.py"
}
]
}
}
6. Pattern Avanzati per Hooks
Oltre all'uso base, gli hooks possono essere combinati in pattern sofisticati che risolvono esigenze complesse di automazione. Questa sezione presenta alcuni dei pattern più utili emersi dall'esperienza della community.
Pattern: Auto-Commit su Checkpoint
Questo pattern crea automaticamente commit Git a intervalli regolari durante una sessione di sviluppo, garantendo che il lavoro non venga mai perso e fornendo punti di rollback granulari.
#!/bin/bash
# Hook PostToolUse: crea checkpoint automatici
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')
# Solo per operazioni di modifica file
if [ "$TOOL_NAME" = "Write" ] || [ "$TOOL_NAME" = "Edit" ]; then
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
# Controlla se ci sono abbastanza modifiche per un 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 dopo $NUM_FILES file modificati" \
--no-verify 2>/dev/null
echo "[CHECKPOINT] Commit automatico: $NUM_FILES file" >&2
fi
fi
Pattern: Quality Gate Pre-Commit
Questo pattern esegue automaticamente controlli di qualità (linting, type checking, test unitari) prima di permettere operazioni di scrittura, assicurando che ogni modifica rispetti gli standard del progetto.
#!/bin/bash
# Hook PreToolUse: quality gate per file TypeScript
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
# Scrivi il contenuto in un file temporaneo per analisi
TEMP_FILE=$(mktemp /tmp/claude-hook-XXXXXX.ts)
echo "$CONTENT" > "$TEMP_FILE"
# Esegui 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 fallito: $ERROR_COUNT errori TypeScript trovati. Correggi prima di scrivere.\"}"
exit 0
fi
fi
fi
echo '{"decision": "allow"}'
Pattern: Context Enrichment Pipeline
Questo pattern arricchisce automaticamente ogni prompt dell'utente con informazioni contestuali rilevanti, come lo stato del repository, i file modificati di recente, i test falliti e le metriche di progetto.
#!/bin/bash
# Hook UserPromptSubmit: pipeline di arricchimento contesto
INPUT=$(cat)
PROMPT=$(echo "$INPUT" | jq -r '.prompt')
# Raccogli contesto
CONTEXT_PARTS=()
# 1. Stato Git
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, File modificati: $DIRTY]")
fi
# 2. Test falliti recenti
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 test falliti]")
fi
fi
# 3. TODO in sospeso
TODO_COUNT=$(grep -r "TODO\|FIXME\|HACK" src/ --include="*.ts" 2>/dev/null | wc -l)
if [ "$TODO_COUNT" -gt 0 ]; then
CONTEXT_PARTS+=("[TODO aperti nel codice: $TODO_COUNT]")
fi
# Costruisci contesto aggregato
if [ ${#CONTEXT_PARTS[@]} -gt 0 ]; then
CONTEXT=$(printf "%s " "${CONTEXT_PARTS[@]}")
ENRICHED="$CONTEXT\n\n$PROMPT"
echo "$INPUT" | jq --arg p "$ENRICHED" '{decision: "modify", prompt: $p}'
else
echo '{"decision": "allow"}'
fi
7. Performance, Sicurezza e Best Practice
Gli hooks vengono eseguiti in modo sincrono nel ciclo di vita di Claude Code, il che significa che hook lenti o malfunzionanti possono degradare significativamente l'esperienza utente. Seguire le best practice di performance e sicurezza è essenziale per mantenere un workflow fluido e protetto.
Ottimizzazione delle Performance
Regole d'Oro per Hook Performanti
| Regola | Motivazione | Implementazione |
|---|---|---|
| Timeout aggressivi | Evitare blocchi della sessione | Impostare timeout a 5-10 secondi per hook critici |
| Matcher specifici | Ridurre esecuzioni non necessarie | Usare regex precise invece di wildcard |
| Operazioni async | Non bloccare per I/O lento | Delegare operazioni pesanti a processi background |
| Cache locale | Evitare ricalcoli costosi | Salvare risultati intermedi in file temporanei |
| Early exit | Minimizzare tempo di esecuzione | Controllare condizioni di uscita all'inizio dello script |
| Logging controllato | Evitare I/O disco eccessivo | Usare log rotation e livelli di severita |
Sicurezza degli Hooks
Gli hooks eseguono comandi shell con i permessi dell'utente corrente, il che rappresenta sia un vantaggio (pieno accesso al sistema) che un rischio (esecuzione non controllata). E' fondamentale applicare principi di sicurezza rigorosi:
- Principio del minimo privilegio: Ogni hook dovrebbe avere accesso solo alle risorse strettamente necessarie per il suo funzionamento
- Validazione input: Tutti i dati ricevuti tramite stdin devono essere validati e sanitizzati prima dell'uso
- Nessun segreto hardcoded: API key, password e token non devono mai essere inclusi direttamente negli script hook
- Audit trail: Mantenere un log di tutte le azioni eseguite dagli hooks per tracciabilita
- Review periodica: Rivedere regolarmente gli hook configurati per identificare rischi emergenti
Attenzione: Rischi di Sicurezza
Gli hooks di progetto (definiti in .claude/settings.json) vengono eseguiti
automaticamente quando si apre una sessione Claude Code nel repository. Questo significa
che un repository malevolo potrebbe includere hook dannosi. Verificare sempre
i file di configurazione prima di aprire sessioni in repository non fidati.
Gestione Errori
Un hook che fallisce silenziosamente è peggiore di un hook che non esiste. Implementare una gestione degli errori robusta è fondamentale per la manutenibilità del sistema:
#!/bin/bash
set -euo pipefail
# Trap per gestire errori e cleanup
cleanup() {
local exit_code=$?
if [ $exit_code -ne 0 ]; then
echo "[HOOK ERROR] Exit code: $exit_code" >&2
echo "{\"error\": \"Hook fallito con codice $exit_code\"}" >&2
fi
# Cleanup risorse temporanee
rm -f /tmp/claude-hook-$$.tmp 2>/dev/null
}
trap cleanup EXIT
# Timeout per l'intero script
TIMEOUT=10
(
sleep $TIMEOUT
echo "[HOOK TIMEOUT] Script superato $TIMEOUT secondi" >&2
kill $$ 2>/dev/null
) &
TIMEOUT_PID=$!
# Logica principale con error handling
INPUT=$(cat) || {
echo '{"decision": "allow"}' # Fallback sicuro
exit 0
}
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""' 2>/dev/null) || TOOL_NAME=""
# ... logica del hook ...
# Cancella il timer di timeout
kill $TIMEOUT_PID 2>/dev/null || true
echo '{"decision": "allow"}'
8. Esempio Completo: Ecosistema Hooks per un Progetto Angular
Per concludere, presentiamo un esempio completo di ecosistema hooks configurato per un progetto Angular con SSR, coprendo tutti e sei gli eventi e dimostrando come gli hooks collaborano per creare un ambiente di sviluppo robusto e automatizzato.
{
"hooks": {
"SessionStart": [
{
"command": "bash .claude/hooks/check-dependencies.sh",
"timeout": 15000
}
],
"PreToolUse": [
{
"matcher": "Write|Edit",
"command": "bash .claude/hooks/validate-angular-conventions.sh",
"timeout": 5000
},
{
"matcher": "Bash",
"command": "bash .claude/hooks/block-dangerous-commands.sh",
"timeout": 3000
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"command": "bash .claude/hooks/auto-lint-format.sh",
"timeout": 10000
},
{
"command": "python3 .claude/hooks/activity-tracker.py",
"timeout": 5000
}
],
"UserPromptSubmit": [
{
"command": "bash .claude/hooks/enrich-with-project-context.sh",
"timeout": 5000
}
],
"Stop": [
{
"command": "bash .claude/hooks/desktop-notification.sh"
}
],
"SessionEnd": [
{
"command": "python3 .claude/hooks/generate-session-report.py",
"timeout": 10000
}
]
}
}
#!/bin/bash
# PreToolUse: valida convenzioni Angular per nuovi file
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
# Verifica naming convention per componenti Angular
if [[ "$FILE_PATH" == *.component.ts ]]; then
# Controlla che il nome segua kebab-case
FILENAME=$(basename "$FILE_PATH" .component.ts)
if ! echo "$FILENAME" | grep -qE '^[a-z][a-z0-9]*(-[a-z0-9]+)*$'; then
echo "{\"decision\": \"block\", \"reason\": \"Naming convention violata: i componenti Angular devono usare kebab-case. File: $FILENAME\"}"
exit 0
fi
fi
# Verifica che i file service siano nella directory services/
if [[ "$FILE_PATH" == *.service.ts ]] && [[ "$FILE_PATH" != */services/* ]]; then
echo "{\"decision\": \"block\", \"reason\": \"I service Angular devono essere nella directory services/\"}"
exit 0
fi
echo '{"decision": "allow"}'
9. Debug e Troubleshooting degli Hooks
Quando un hook non funziona come previsto, la diagnosi può essere complicata dalla natura asincrona e dall'esecuzione implicita del sistema. Ecco le strategie più efficaci per identificare e risolvere problemi.
Tecniche di Debug
- Logging su stderr: Tutto ciò che viene scritto su
stderrviene visualizzato nel terminale Claude Code. Usareecho "debug info" >&2per messaggi di debug. - File di log dedicati: Scrivere output dettagliato in file di log nella directory
.claude/hooks/logs/per analisi post-hoc. - Test isolati: Testare ogni hook indipendentemente simulando l'input JSON tramite pipe.
- Dry run mode: Implementare una modalità dry-run negli hook che logga le azioni senza eseguirle.
#!/bin/bash
# Wrapper che aggiunge debug a qualsiasi hook
HOOK_SCRIPT="$1"
LOG_DIR=".claude/hooks/logs"
mkdir -p "$LOG_DIR"
# Cattura input
INPUT=$(cat)
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
# Logga input
echo "=== $TIMESTAMP ===" >> "$LOG_DIR/debug.log"
echo "Hook: $HOOK_SCRIPT" >> "$LOG_DIR/debug.log"
echo "Input: $INPUT" >> "$LOG_DIR/debug.log"
# Esegui hook e cattura output
OUTPUT=$(echo "$INPUT" | bash "$HOOK_SCRIPT" 2>> "$LOG_DIR/debug.log")
EXIT_CODE=$?
# Logga output
echo "Output: $OUTPUT" >> "$LOG_DIR/debug.log"
echo "Exit: $EXIT_CODE" >> "$LOG_DIR/debug.log"
echo "" >> "$LOG_DIR/debug.log"
# Ritorna output
echo "$OUTPUT"
exit $EXIT_CODE
Problemi Comuni e Soluzioni
Troubleshooting Rapido
| Problema | Causa Probabile | Soluzione |
|---|---|---|
| Hook non si attiva | Matcher non corrisponde | Verificare il nome esatto del tool e il pattern regex |
| Hook rallenta Claude | Timeout troppo alto o operazione lenta | Ridurre timeout, delegare a processo background |
| Output non processato | JSON malformato su stdout | Validare output con jq e verificare encoding |
| Permessi negati | Script non eseguibile | Eseguire chmod +x script.sh |
| Variabili ambiente mancanti | Shell non eredita ambiente | Caricare esplicitamente .env nello script |
| Hook funziona manualmente ma non da Claude | PATH diverso nell'ambiente hook | Usare percorsi assoluti per tutti i comandi |
Conclusione
Gli hooks di Claude Code rappresentano il ponte tra un assistente AI reattivo è un sistema di sviluppo proattivo e personalizzato. Attraverso sei punti di intercettazione strategici, è possibile automatizzare validazioni, arricchire il contesto, proteggere il progetto da operazioni pericolose e coordinare flussi di lavoro complessi.
La vera potenza degli hooks emerge dalla loro composizione: singolarmente sono script semplici, ma combinati creano un ecosistema di automazione che trasforma radicalmente il modo in cui si interagisce con l'agente AI. Dall'auto-linting alla notifica di completamento, dal quality gate alla comunicazione inter-agente, gli hooks rendono ogni sessione Claude Code più efficiente, sicura e produttiva.
Punti Chiave
- 6 eventi coprono l'intero ciclo: Da SessionStart a SessionEnd, ogni momento è intercettabile
- Determinismo garantito: A differenza dei prompt, gli hooks eseguono sempre esattamente ciò che si definisce
- Community attiva: Britfix, CC Notify, cchooks e Hook Comms risolvono esigenze comuni
- Composizione potente: Hook semplici combinati creano automazioni sofisticate
- Sicurezza prima di tutto: Validare input, usare timeout e verificare hook di repository esterni
- Performance critica: Hook sincroni richiedono attenzione a timeout e early exit
Nel prossimo articolo esploreremo la tecnica Ralph Wiggum: come configurare Claude Code per eseguire loop di sviluppo completamente autonomi, iterando su un progetto senza intervento umano fino al raggiungimento degli obiettivi definiti.

Commenti
Caricamento commenti...
Accedi per commentare