Salta al contenuto principale

15 - Hooks: Automazione Event-Driven in Claude Code

Automatizza il workflow con i 6 eventi hook di Claude Code: PreToolUse, PostToolUse, SessionStart e altri. Esempi reali con Britfix, CC Notify e cchooks.

Cosa ne pensi di questo articolo?

15 - Hooks: Automazione Event-Driven in Claude Code
ClaudeFoto:Lukas BlazekPexels

In Breve

Automatizza il workflow con i 6 eventi hook di Claude Code: PreToolUse, PostToolUse, SessionStart e altri. Esempi reali con Britfix, CC Notify e cchooks.

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

#ArticoloFocus
1Claude come Partner TecnicoSetup e primi passi
2Contesto e Setup del ProgettoCLAUDE.md e configurazione
3Ideazione e RequisitiMVP e user personas
4Architettura Backend e FrontendSpring Boot e Angular
5Struttura del CodiceOrganizzazione e convenzioni
6Prompt Engineering AvanzatoTecniche avanzate
7Testing e qualitàStrategie e generazione test
8Documentazione ProfessionaleREADME, API, ADR
9Deploy e DevOpsDocker, CI/CD, monitoring
10Evoluzione e ManutenzioneRefactoring e scalabilità
11Integrazione Progetto RealeClaude Code in produzione
12CLI Avanzata e ComandiPadronanza della riga di comando
13Custom Slash Commands e SkillsEstendere Claude Code
14Sub-Agenti e DelegazioneOrchestrare agenti specializzati
15Hooks: Automazione Event-Driven (sei qui)Automazione basata su eventi
16Ralph Wiggum: Loop AI AutonomiSviluppo autonomo iterativo
17BMAD Method: Sviluppo Agile AIMetodologia agile AI-driven
18Orchestrazione Multi-AgenteCoordinare team di agenti
19Collaborazione e Coworking AILavorare insieme a Claude
20Sicurezza e PermessiProteggere il workflow
21Monitoring e OttimizzazioneMetriche 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

CaratteristicaHooksMCP Servers
ComplessitàBassa - script shellAlta - processo separato
LatenzaMinima - esecuzione direttaMaggiore - comunicazione IPC
Controllo modelloNo - l'LLM non decideSi - l'LLM sceglie quando usarli
DeterminismoTotale - sempre eseguitiParziale - dipende dal contesto
ConfigurazioneJSON in settings.jsonServer dedicato con SDK
Casi d'usoAutomazioni, notifiche, validazioniStrumenti complessi, integrazioni API
SicurezzaEsecuzione locale controllataPermessi 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.

Flusso di Esecuzione Hook
┌──────────────────────┐
│    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.

.claude/settings.json - Struttura Base
{
  "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

CampoTipoObbligatorioDescrizione
matcherstring (regex)NoPattern regex per filtrare l'attivazione. Supporta pipe per OR logico.
commandstringSiComando shell da eseguire. Può essere un percorso assoluto o relativo.
timeoutnumberNoTimeout 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.

Esempi di Matcher Avanzati
// 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

CampoTipoDescrizione
tool_namestringNome dello strumento (es. "Write", "Bash", "Edit")
tool_inputobjectParametri passati allo strumento
session_idstringIdentificativo 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 campo reason con la motivazione.
  • "decision": "modify" - Modifica i parametri dello strumento prima dell'esecuzione. Richiede un campo tool_input con i parametri aggiornati.
pre-tool-guard.sh - Bloccare operazioni pericolose
#!/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"}'
auto-format.sh - Modificare parametri prima dell'esecuzione
#!/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

CampoTipoDescrizione
tool_namestringNome dello strumento eseguito
tool_inputobjectParametri usati per l'esecuzione
tool_outputstringRisultato dello strumento
session_idstringIdentificativo della sessione
post-tool-logger.py - Logging strutturato delle operazioni
#!/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.

enrich-prompt.sh - Arricchire i prompt con contesto
#!/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.

notify-complete.sh - Notifica desktop al completamento
#!/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.

session-init.sh - Setup ambiente alla partenza
#!/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.

session-report.py - Report automatico di fine sessione
#!/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

EventoQuando si AttivaPuò Bloccare?Può Modificare?
PreToolUsePrima dell'esecuzione di un toolSiSi (parametri tool)
PostToolUseDopo l'esecuzione di un toolNoNo (solo side effects)
UserPromptSubmitQuando l'utente invia un promptSiSi (testo prompt)
StopQuando Claude finisce di generareNoNo
SessionStartAll'inizio di una nuova sessioneNoNo
SessionEndAlla chiusura della sessioneNoNo

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.

Configurazione Britfix
{
  "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.

Configurazione CC Notify
{
  "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.

Hook con cchooks SDK
#!/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.

Esempio Comunicazione Inter-Agente
{
  "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

template-hook.sh - Template Base per Hook
#!/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:

test-hook.sh - Script di test per hook
#!/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

Registrazione in .claude/settings.json
{
  "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.

auto-checkpoint.sh - Commit automatici
#!/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.

quality-gate.sh - Controlli qualità automatici
#!/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.

context-pipeline.sh - Arricchimento contestuale
#!/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

RegolaMotivazioneImplementazione
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:

robust-hook.sh - Hook con gestione errori completa
#!/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.

.claude/settings.json - Configurazione Completa
{
  "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
      }
    ]
  }
}
validate-angular-conventions.sh
#!/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 stderr viene visualizzato nel terminale Claude Code. Usare echo "debug info" >&2 per 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.
debug-wrapper.sh - Wrapper di debug per hooks
#!/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

ProblemaCausa ProbabileSoluzione
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.

Prova Questi Strumenti

Strumenti gratuiti correlati a questo argomento.

Commenti

Caricamento commenti...

Accedi per commentare

Discuti questo articolo nella community

Unisciti al gruppo Telegram per discutere con altri sviluppatori, fare domande e condividere le tue esperienze.

Ti è piaciuto questo articolo?

Esplora altri contenuti sul blog o scopri i miei progetti