Creo applicazioni web moderne e strumenti digitali personalizzati per aiutare le attività a crescere attraverso l'innovazione tecnologica. La mia passione è unire informatica ed economia per generare valore reale.
La mia passione per l'informatica è nata tra i banchi dell'Istituto Tecnico Commerciale di Maglie, dove ho scoperto il potere della programmazione e il fascino di creare soluzioni digitali. Fin da subito, ho capito che l'informatica non era solo codice, ma uno strumento straordinario per trasformare idee in realtà.
Durante gli studi superiori in Sistemi Informativi Aziendali, ho iniziato a intrecciare informatica ed economia, comprendendo come la tecnologia possa essere il motore della crescita per qualsiasi attività. Questa visione mi ha accompagnato all'Università degli Studi di Bari, dove ho conseguito la Laurea in Informatica, approfondendo le mie competenze tecniche e la mia passione per lo sviluppo software.
Oggi metto questa esperienza al servizio di imprese, professionisti e startup, creando soluzioni digitali su misura che automatizzano processi, ottimizzano risorse e aprono nuove opportunità di business. Perché la vera innovazione inizia quando la tecnologia incontra le esigenze reali delle persone.
Le Mie Competenze
Analisi Dati & Modelli Previsionali
Trasformo i dati in insights strategici con analisi approfondite e modelli predittivi per decisioni informate
Automazione Processi
Creo strumenti personalizzati che automatizzano operazioni ripetitive e liberano tempo per attività a valore aggiunto
Sistemi Custom
Sviluppo sistemi software su misura, dalle integrazioni tra piattaforme alle dashboard personalizzate
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.
Il mio percorso accademico e le tecnologie che padroneggio
Certificazioni Professionali
8 certificazioni conseguite
Nuovo
Visualizza
Reinvention With Agentic AI Learning Program
Anthropic
Dicembre 2024
Nuovo
Visualizza
Agentic AI Fluency
Anthropic
Dicembre 2024
Nuovo
Visualizza
AI Fluency for Students
Anthropic
Dicembre 2024
Nuovo
Visualizza
AI Fluency: Framework and Foundations
Anthropic
Dicembre 2024
Nuovo
Visualizza
Claude with the Anthropic API
Anthropic
Dicembre 2024
Visualizza
Master SQL
RoadMap.sh
Novembre 2024
Visualizza
Oracle Certified Foundations Associate
Oracle
Ottobre 2024
Visualizza
People Leadership Credential
Connect
Settembre 2024
Linguaggi & Tecnologie
Java
Python
JavaScript
Angular
React
TypeScript
SQL
PHP
CSS/SCSS
Node.js
Docker
Git
💼
12/2024 - Presente
Custom Software Engineering Analyst
Accenture
Bari, Puglia, Italia · Ibrida
Analisi e sviluppo di sistemi informatici attraverso l'utilizzo di Java e Quarkus in Health and Public Sector. Formazione continua su tecnologie moderne per la creazione di soluzioni software personalizzate ed efficienti e sugli agenti.
💼
06/2022 - 12/2024
Analista software e Back End Developer Associate Consultant
Links Management and Technology SpA
Esperienza nell'analisi di sistemi software as-is e flussi ETL utilizzando PowerCenter. Formazione completata su Spring Boot per lo sviluppo di applicazioni backend moderne e scalabili. Sviluppatore Backend specializzato in Spring Boot, con esperienza in progettazione di database, analisi, sviluppo e testing dei task assegnati.
💼
02/2021 - 10/2021
Programmatore software
Adesso.it (prima era WebScience srl)
Esperienza nell'analisi AS-IS e TO-BE, evoluzioni SEO ed evoluzioni website per migliorare le performance e l'engagement degli utenti.
🎓
2018 - 2025
Laurea in Informatica
Università degli Studi di Bari Aldo Moro
Bachelor's degree in Computer Science, focusing on software engineering, algorithms, and modern development practices.
📚
2013 - 2018
Diploma - Sistemi Informativi Aziendali
Istituto Tecnico Commerciale di 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.
05 - Cursor Hooks: Automatizza il Workflow con le Guardrail AI
C'e un problema strutturale nel lavorare con agenti AI autonomi: non puoi controllare ogni singola
azione in tempo reale, ma non puoi nemmeno fidarti ciecamente di ciò che fanno. Un agente che scrive
codice, esegue comandi nel terminale e modifica file in sequenza automatica può essere straordinariamente
produttivo, ma anche pericolosamente imprevedibile se non ci sono meccanismi di controllo in atto.
La risposta di Cursor a questo problema si chiama Hooks. Introdotto con Cursor 1.7 nel
2025, il sistema Hooks permette di iniettare logica personalizzata in punti precisi del ciclo di vita
dell'agente: prima che esegua un comando shell, dopo che modifica un file, prima di una chiamata MCP,
quando termina il suo lavoro. Sono, in sostanza, delle guardrail automatiche che puoi
configurare una volta e che si attivano ogni volta che l'agente lavora nella tua codebase.
Questo articolo esplora il sistema Hooks in profondità: dalla configurazione base fino ai pattern
avanzati per auto-formatting, security validation, test automation e documentation sync. Al termine
saprai costruire un workflow robusto dove l'agente AI e produttivo ma sempre sotto il tuo controllo.
Cosa Imparerai in Questo Articolo
Cos'è il sistema Hooks di Cursor e come si differenzia da semplici script
I tipi di hook disponibili: beforeShellExecution, beforeMCPExecution, beforeReadFile, afterFileEdit, stop
Come configurare hooks globali e a livello di progetto con hooks.json
Come costruire hook che bloccano azioni pericolose con exit code e JSON response
Automazione post-modifica: auto-format con Prettier/ESLint via afterFileEdit
Stop hooks per verifica finale e notifiche al completamento del task
Pattern pratici: security check, test runner automatico, documentazione in sync
Come i hooks integrano con Agent Mode per un workflow completo e sicuro
Debugging hooks: output channel, logging, troubleshooting errori comuni
Best practices per hook idempotenti, performanti e manutenibili
Posizione nella Serie: Cursor IDE e AI-Native Development
#
Articolo
Livello
1
Cursor IDE: Guida Completa per Developer
Beginner
2
Cursor Rules: Configurare AI per il tuo Progetto
Intermediate
3
Agent Mode: Modificare la Codebase con un Comando
Intermediate
4
Plan Mode e Background Agents
Advanced
5
Cursor Hooks: Automatizza il Workflow (sei qui)
Intermediate
6
MCP e Cursor: Connetti IDE a Database e API
Advanced
7
Debugging con Cursor AI: 3x Più Veloce
Intermediate
8
Cursor vs Windsurf vs Copilot nel 2026
Beginner
9
Workflow Professionale: Progetto Angular con Cursor
Advanced
Cos'è un Hook in Cursor
Un hook in Cursor e un processo esterno che viene eseguito automaticamente in risposta
a specifici eventi del ciclo di vita dell'agente. Quando l'agente sta per eseguire un comando shell,
Cursor chiama il tuo hook passandogli informazioni sull'azione in JSON via stdin. Il tuo hook può
analizzare queste informazioni e rispondere con una direttiva: consenti l'azione, bloccala, oppure
chiedi conferma all'utente.
E fondamentale capire che gli hook non sono semplici script di shell che girano in
background. Sono processi sincroni integrati nel ciclo dell'agente: l'agente si ferma, aspetta la
risposta dell'hook, poi decide come procedere in base a quella risposta. Questo li rende strumenti
potenti per validation, auditing e automazione.
Gli hook disponibili in Cursor 1.7+ coprono questi eventi del ciclo di vita:
beforeShellExecution - Eseguito prima di ogni comando nel terminale. Riceve il
comando esatto che l'agente sta per eseguire. Può bloccare comandi pericolosi, loggarli o modificarli.
beforeMCPExecution - Eseguito prima di ogni chiamata a un server MCP. Riceve
il nome del tool e i parametri in input. Utile per auditing delle operazioni su database o API.
beforeReadFile - Eseguito prima che l'agente legga il contenuto di un file.
Permette di controllare quali file l'agente può accedere o mascherare informazioni sensibili.
afterFileEdit - Eseguito dopo ogni modifica a un file. Riceve il percorso del
file e le modifiche effettuate (old_string/new_string). Ideale per auto-formatting e linting.
stop - Eseguito quando l'agente termina il suo task. Utile per notifiche,
commit automatici, sincronizzazione documentazione e azioni di cleanup.
Feature Beta in Evoluzione
Gli Hooks di Cursor sono stati introdotti come feature beta con Cursor 1.7 nell'ottobre 2025. La
documentazione e le API potrebbero cambiare nelle versioni successive. Prima di implementare hooks
in un ambiente di produzione, verifica la documentazione ufficiale su
cursor.com/docs/agent/hooks per eventuali aggiornamenti.
Configurazione: hooks.json Globale e a Livello di Progetto
Gli hook di Cursor si configurano tramite file hooks.json. Esistono due livelli di
configurazione, che funzionano in modo complementare:
Hooks globali (~/.cursor/hooks.json) - Si applicano a tutti i
progetti aperti con Cursor su quella macchina. Ideali per policy di sicurezza personali, notifiche
o strumenti di audit che vuoi sempre attivi.
Hooks di progetto (.cursor/hooks.json nella root del repo) - Si
applicano solo al progetto corrente e vengono committati nel repository. Permettono di condividere
le stesse automazioni con tutto il team.
Quando entrambi i file esistono, i hooks vengono uniti: prima vengono eseguiti gli hook globali,
poi quelli del progetto per ogni evento. Il formato del file e semplice: un oggetto JSON con una
versione e un oggetto hooks che mappa ogni evento a un array di comandi.
Ecco un esempio completo di hooks.json che configura hook per tutti i principali eventi:
Il campo command accetta qualsiasi comando eseguibile: script Node.js, bash, Python,
binari npm. Cursor esegue il comando come processo separato, passa i dati dell'evento via
stdin in formato JSON, e legge la risposta dell'hook via stdout.
Gli errori e i log diagnostici vanno su stderr.
La struttura base del JSON che Cursor invia via stdin a ogni hook include questi campi comuni:
// Struttura JSON inviata via stdin a ogni hook
{
"conversation_id": "668320d2-2fd8-4888-b33c-2a466fec86e7",
"generation_id": "490b90b7-a2ce-4c2c-bb76-cb77b125df2f",
"hook_event_name": "beforeShellExecution", // nome dell'evento
"workspace_roots": ["/path/to/project"], // root del workspace
// campi specifici per ogni tipo di hook...
}
Hook di Pre-Esecuzione: Blocca Azioni Pericolose
Gli hook before sono i più potenti perchè possono bloccare azioni prima che avvengano.
Il caso d'uso più immediato e proteggere file o directory critiche da modifiche accidentali da parte
dell'agente, oppure bloccare comandi shell distruttivi.
Per il tipo beforeShellExecution, il JSON ricevuto via stdin include il campo
command con il comando esatto che l'agente sta per eseguire e il campo cwd
con la directory di lavoro corrente:
L'hook risponde via stdout con un JSON che indica se l'azione deve essere consentita o bloccata.
Il campo chiave e permission con valori "allow", "deny"
o "ask". Ecco un hook Node.js che blocca comandi potenzialmente distruttivi:
// .cursor/hooks/security-check.js
// Hook: blocca comandi shell pericolosi
const readline = require('readline');
async function readStdin() {
return new Promise((resolve) => {
const rl = readline.createInterface({ input: process.stdin });
let data = '';
rl.on('line', (line) => { data += line; });
rl.on('close', () => resolve(data));
});
}
// Pattern di comandi che devono essere bloccati
const DANGEROUS_PATTERNS = [
/rm\s+-rf\s+\//, // rm -rf / (root filesystem)
/rm\s+-rf\s+~\//, // rm -rf ~/
/sudo\s+rm\s+-rf/, // sudo rm -rf qualsiasi cosa
/DROP\s+DATABASE/i, // SQL DROP DATABASE
/DROP\s+TABLE.*--drop/i, // SQL DROP con flag pericolose
/git\s+push.*--force\s+origin\s+main/, // force push su main
];
// File e directory protetti dall'agente
const PROTECTED_PATHS = [
'.env',
'.env.production',
'secrets/',
'firebase.json',
'.firebaserc',
];
async function main() {
const rawInput = await readStdin();
let input;
try {
input = JSON.parse(rawInput);
} catch (e) {
// Se non riusciamo a parsare, lasciamo passare (fail-open)
process.stdout.write(JSON.stringify({ permission: 'allow' }));
return;
}
const command = input.command || '';
// Controlla comandi pericolosi
const isDangerous = DANGEROUS_PATTERNS.some((pattern) =>
pattern.test(command)
);
if (isDangerous) {
process.stderr.write(`[security-check] Comando bloccato:
#123;command}\n`);
process.stdout.write(
JSON.stringify({
permission: 'deny',
userMessage: `Comando bloccato dalla security policy: #123;command}`,
agentMessage:
'Questo comando e stato bloccato dalla policy di sicurezza del progetto. ' +
'Non eseguire comandi rm -rf su percorsi di sistema o force push su branch protetti.',
})
);
return;
}
// Controlla se il comando tocca file protetti
const touchesProtectedPath = PROTECTED_PATHS.some((path) =>
command.includes(path)
);
if (touchesProtectedPath) {
process.stdout.write(
JSON.stringify({
permission: 'ask',
userMessage: `Stai per eseguire: #123;command} - Procedi?`,
})
);
return;
}
// Comando sicuro: consenti
process.stdout.write(JSON.stringify({ permission: 'allow' }));
}
main().catch((err) => {
process.stderr.write(`[security-check] Errore: #123;err.message}\n`);
process.stdout.write(JSON.stringify({ permission: 'allow' })); // fail-open
});
Con "permission": "ask", Cursor mostra all'utente una dialog di conferma prima di
procedere, visualizzando il messaggio definito in userMessage. L'utente può approvare
o rifiutare l'azione. Con "permission": "deny", l'azione viene bloccata definitivamente
e il messaggio viene mostrato sia all'utente che passato all'agente come contesto.
Hook per Proteggere File Specifici con beforeReadFile
Il hook beforeReadFile riceve un campo file_path nel JSON di stdin.
Puoi usarlo per impedire all'agente di leggere file con credenziali o segreti, oppure per
mascherare il contenuto di file sensibili prima che venga passato al modello AI.
Hook afterFileEdit: Auto-Format e Linting Automatico
Il hook afterFileEdit e il più utilizzato nella pratica quotidiana. Viene eseguito
ogni volta che l'agente scrive o modifica un file, e riceve via stdin un JSON con il percorso del file
modificato e un array di modifiche con le stringhe originali e nuove:
Con questi dati, puoi eseguire automaticamente il formatter preferito sul file appena modificato.
Ecco un hook Node.js completo che applica Prettier per TypeScript/JavaScript e ESLint per fix
automatici, rispettando le configurazioni già presenti nel progetto:
// .cursor/hooks/auto-format.js
// Hook: auto-format e linting dopo ogni modifica dell'agente
const readline = require('readline');
const { execSync } = require('child_process');
const path = require('path');
const fs = require('fs');
async function readStdin() {
return new Promise((resolve) => {
const rl = readline.createInterface({ input: process.stdin });
let data = '';
rl.on('line', (line) => { data += line; });
rl.on('close', () => resolve(data));
});
}
// Estensioni supportate dal formatter
const PRETTIER_EXTENSIONS = new Set([
'.ts', '.tsx', '.js', '.jsx',
'.json', '.css', '.scss',
'.html', '.md', '.yaml', '.yml',
]);
const ESLINT_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx']);
function runFormatter(filePath, workspaceRoot) {
const ext = path.extname(filePath);
// Prettier: formatta il file se l'estensione e supportata
if (PRETTIER_EXTENSIONS.has(ext)) {
const prettierConfig = path.join(workspaceRoot, '.prettierrc');
const hasPrettierConfig =
fs.existsSync(prettierConfig) ||
fs.existsSync(path.join(workspaceRoot, 'prettier.config.js')) ||
fs.existsSync(path.join(workspaceRoot, '.prettierrc.json'));
if (hasPrettierConfig || fs.existsSync(
path.join(workspaceRoot, 'node_modules', '.bin', 'prettier')
)) {
try {
execSync(
`npx prettier --write "#123;filePath}"`,
{
cwd: workspaceRoot,
stdio: ['ignore', 'pipe', 'pipe'],
timeout: 10000,
}
);
process.stderr.write(`[auto-format] Prettier OK: #123;filePath}\n`);
} catch (e) {
process.stderr.write(
`[auto-format] Prettier warning: #123;e.message}\n`
);
}
}
}
// ESLint --fix: applica fix automatici per TS/JS
if (ESLINT_EXTENSIONS.has(ext)) {
const hasEslint = fs.existsSync(
path.join(workspaceRoot, 'node_modules', '.bin', 'eslint')
);
const hasEslintConfig =
fs.existsSync(path.join(workspaceRoot, '.eslintrc.js')) ||
fs.existsSync(path.join(workspaceRoot, '.eslintrc.json')) ||
fs.existsSync(path.join(workspaceRoot, 'eslint.config.js'));
if (hasEslint && hasEslintConfig) {
try {
execSync(
`npx eslint --fix "#123;filePath}"`,
{
cwd: workspaceRoot,
stdio: ['ignore', 'pipe', 'pipe'],
timeout: 15000,
}
);
process.stderr.write(`[auto-format] ESLint --fix OK: #123;filePath}\n`);
} catch (e) {
// ESLint esce con codice 1 se ci sono errori non fixabili: non e un failure dell'hook
process.stderr.write(
`[auto-format] ESLint warning (unfixable errors): #123;path.basename(filePath)}\n`
);
}
}
}
}
async function main() {
const rawInput = await readStdin();
let input;
try {
input = JSON.parse(rawInput);
} catch (e) {
process.stderr.write(`[auto-format] JSON parse error: #123;e.message}\n`);
return; // afterFileEdit non richiede risposta JSON
}
const filePath = input.file_path;
const workspaceRoot = input.workspace_roots?.[0] || process.cwd();
if (!filePath) {
process.stderr.write('[auto-format] Nessun file_path ricevuto\n');
return;
}
// Salta file nelle node_modules o .cursor
if (filePath.includes('node_modules') || filePath.includes('.cursor')) {
return;
}
runFormatter(filePath, workspaceRoot);
}
main();
afterFileEdit Non Richiede Risposta JSON
A differenza degli hook before, l'hook afterFileEdit e pensato per effetti
collaterali: formatting, linting, aggiornamento di indici. Non deve restituire una risposta JSON
su stdout perchè l'azione (la modifica al file) e già avvenuta. L'hook esegue il suo lavoro e
termina; Cursor continua a prescindere dall'exit code.
Stop Hooks: Verifica Finale e Notifiche
L'hook stop viene eseguito quando l'agente considera completato il suo task. Non
riceve dettagli sulle azioni specifiche svolte, ma ha accesso al conversation_id e ai
workspace_roots. E il momento ideale per azioni di chiusura come notifiche, commit
automatici, aggiornamento della documentazione o esecuzione di test sulla suite completa.
Ecco due esempi pratici di stop hook: il primo invia una notifica desktop su macOS, il secondo
esegue una verifica git del lavoro prodotto dall'agente:
// .cursor/hooks/notify-completion.sh
# Stop hook: notifica desktop al completamento del task
#!/bin/bash
# Leggi il JSON da stdin (anche se non lo usiamo qui)
INPUT=$(cat)
# Notifica su macOS
if [[ "$OSTYPE" == "darwin"* ]]; then
osascript -e 'display notification "Il task e completato!" with title "Cursor Agent" subtitle "Controlla le modifiche nel repository"'
fi
# Notifica su Linux con notify-send
if command -v notify-send &>/dev/null; then
notify-send "Cursor Agent" "Task completato! Controlla le modifiche."
fi
# Log su file per audit
echo "$(date '+%Y-%m-%d %H:%M:%S') - Agent task completed" >> ~/.cursor/agent-log.txt
exit 0
// .cursor/hooks/git-status-check.js
// Stop hook: mostra un riepilogo delle modifiche git dell'agente
const readline = require('readline');
const { execSync } = require('child_process');
async function readStdin() {
return new Promise((resolve) => {
const rl = readline.createInterface({ input: process.stdin });
let data = '';
rl.on('line', (line) => { data += line; });
rl.on('close', () => resolve(data));
});
}
async function main() {
const rawInput = await readStdin();
const input = JSON.parse(rawInput);
const workspaceRoot = input.workspace_roots?.[0] || process.cwd();
try {
// Mostra quanti file sono stati modificati dall'agente
const status = execSync('git status --short', {
cwd: workspaceRoot,
encoding: 'utf8',
});
const modifiedFiles = status
.split('\n')
.filter((line) => line.trim().length > 0);
if (modifiedFiles.length > 0) {
process.stderr.write(
`[git-check] Agent ha modificato #123;modifiedFiles.length} file:\n`
);
modifiedFiles.forEach((f) => process.stderr.write(` #123;f}\n`));
} else {
process.stderr.write('[git-check] Nessuna modifica in staging\n');
}
} catch (e) {
process.stderr.write(`[git-check] Git non disponibile: #123;e.message}\n`);
}
}
main();
Casi d'Uso Pratici per Team di Sviluppo
I hooks diventano particolarmente preziosi quando si lavora in team o su progetti con requisiti
rigorosi di qualità. Vediamo i pattern più efficaci emersi dalla community di Cursor nel 2025.
Pattern 1: Security Audit Prima di Operazioni MCP
Se usi MCP con database o API esterne, il hook beforeMCPExecution permette di loggare
ogni operazione prima che venga eseguita. Il JSON di input include il nome del tool MCP e i
parametri esatti passati all'agente:
// .cursor/hooks/mcp-audit.js
// Audit log per ogni chiamata MCP dell'agente
const readline = require('readline');
const fs = require('fs');
const path = require('path');
async function readStdin() {
return new Promise((resolve) => {
const rl = readline.createInterface({ input: process.stdin });
let data = '';
rl.on('line', (line) => { data += line; });
rl.on('close', () => resolve(data));
});
}
// Operazioni MCP che devono essere bloccate in produzione
const BLOCKED_MCP_TOOLS = [
'delete_database',
'drop_table',
'truncate_table',
'execute_raw_sql', // troppo generico, meglio bloccare e richiedere conferma
];
async function main() {
const rawInput = await readStdin();
const input = JSON.parse(rawInput);
const toolName = input.tool_name || 'unknown';
const toolInput = input.tool_input || '{}';
const workspaceRoot = input.workspace_roots?.[0] || '.';
// Log dell'operazione MCP
const auditEntry = {
timestamp: new Date().toISOString(),
tool: toolName,
input: toolInput,
conversation_id: input.conversation_id,
};
const auditFile = path.join(workspaceRoot, '.cursor', 'mcp-audit.log');
fs.appendFileSync(
auditFile,
JSON.stringify(auditEntry) + '\n'
);
// Blocca tool distruttivi
if (BLOCKED_MCP_TOOLS.includes(toolName)) {
process.stdout.write(
JSON.stringify({
permission: 'deny',
userMessage: `Operazione MCP bloccata: #123;toolName}`,
agentMessage: `Il tool MCP '#123;toolName}' e bloccato dalla policy di progetto. ` +
`Usa operazioni più specifiche o chiedi conferma manuale all'utente.`,
})
);
return;
}
// Tutto il resto e consentito
process.stdout.write(JSON.stringify({ permission: 'allow' }));
}
main();
Pattern 2: Test Runner Automatico Dopo Modifiche ai Test
Un altro pattern molto utile e eseguire automaticamente i test quando l'agente modifica file di
spec o di test. Questo garantisce che le modifiche non rompano la suite esistente:
// .cursor/hooks/run-tests.js
// Esegue i test quando l'agente modifica file .spec.ts o .test.ts
const readline = require('readline');
const { execSync } = require('child_process');
const path = require('path');
async function readStdin() {
return new Promise((resolve) => {
const rl = readline.createInterface({ input: process.stdin });
let data = '';
rl.on('line', (line) => { data += line; });
rl.on('close', () => resolve(data));
});
}
async function main() {
const rawInput = await readStdin();
const input = JSON.parse(rawInput);
const filePath = input.file_path || '';
const workspaceRoot = input.workspace_roots?.[0] || process.cwd();
// Esegui test solo per file spec/test
const isTestFile = filePath.includes('.spec.') || filePath.includes('.test.');
const isServiceOrComponent =
filePath.endsWith('.service.ts') || filePath.endsWith('.component.ts');
if (!isTestFile && !isServiceOrComponent) {
return; // Nessun test da eseguire per questo file
}
process.stderr.write(`[run-tests] Esecuzione test per: #123;path.basename(filePath)}\n`);
try {
// Per Angular: esegui solo i test del file modificato (più veloce)
const relativePath = path.relative(workspaceRoot, filePath)
.replace(/\\/g, '/')
.replace(/\.ts$/, '');
execSync(
`npx ng test --include="**/#123;path.basename(relativePath)}.spec.ts" --watch=false --browsers=ChromeHeadless`,
{
cwd: workspaceRoot,
stdio: ['ignore', 'pipe', 'pipe'],
timeout: 60000,
}
);
process.stderr.write('[run-tests] Test passati\n');
} catch (e) {
// I test falliti vengono loggati ma non bloccano l'hook
process.stderr.write(
`[run-tests] ATTENZIONE: alcuni test falliti per #123;path.basename(filePath)}\n`
);
process.stderr.write(e.stdout?.toString() || e.message);
}
}
main();
Pattern 3: Documentazione in Sync con la Codebase
Un pattern molto utile per documentazione tecnica: quando l'agente modifica un'interfaccia TypeScript
o un file di routing, lo stop hook può verificare se la documentazione e aggiornata o segnalare
che necessità revisione:
// .cursor/hooks/doc-sync-check.js
// Verifica se la documentazione e allineata dopo modifiche alle interfacce
const readline = require('readline');
const { execSync } = require('child_process');
const path = require('path');
const fs = require('fs');
async function readStdin() {
return new Promise((resolve) => {
const rl = readline.createInterface({ input: process.stdin });
let data = '';
rl.on('line', (line) => { data += line; });
rl.on('close', () => resolve(data));
});
}
async function main() {
const rawInput = await readStdin();
const input = JSON.parse(rawInput);
const workspaceRoot = input.workspace_roots?.[0] || process.cwd();
try {
// Cerca file di interfacce modificati nell'ultimo commit
const changedFiles = execSync(
'git diff --name-only HEAD~1 HEAD 2>/dev/null || git diff --name-only --cached',
{ cwd: workspaceRoot, encoding: 'utf8' }
).split('\n').filter(Boolean);
const interfaceFiles = changedFiles.filter(
(f) => f.includes('.model.ts') || f.includes('.interface.ts') || f.includes('types.ts')
);
if (interfaceFiles.length > 0) {
const docsDir = path.join(workspaceRoot, 'docs');
const hasDocsDir = fs.existsSync(docsDir);
if (hasDocsDir) {
process.stderr.write(
`[doc-sync] ATTENZIONE: #123;interfaceFiles.length} file di interfacce modificati.\n` +
`Considera di aggiornare la documentazione in /docs:\n` +
interfaceFiles.map((f) => ` - #123;f}`).join('\n') + '\n'
);
}
}
} catch (e) {
process.stderr.write(`[doc-sync] Errore: #123;e.message}\n`);
}
}
main();
Hooks e Agent Mode: Un Sistema di Controllo Completo
L'integrazione tra Hooks e Agent Mode crea un loop di feedback che rende l'agente più sicuro e
affidabile nel tempo. Quando l'agente lavora in modo autonomo su task complessi, i hooks funzionano
come una rete di sicurezza che agisce a ogni passo del ciclo di esecuzione.
Considera questo scenario reale: stai usando Agent Mode per refactorare un modulo Angular. L'agente
esegue decine di azioni in sequenza: legge file, modifica componenti, aggiorna routing, esegue
comandi npm. Con i hooks configurati correttamente, ogni step critico viene intercettato:
beforeShellExecution - Blocca qualsiasi comando npm install non approvato
o push a branch protetti
beforeReadFile - Impedisce all'agente di leggere file .env
o file di configurazione con segreti
afterFileEdit - Ogni file modificato viene automaticamente formattato con
Prettier e controllato con ESLint prima di passare al passo successivo
stop - Quando l'agente completa, ricevi una notifica e un riepilogo delle
modifiche git da revisionare
I messaggi agentMessage nelle risposte degli hook vengono passati al modello AI come
contesto aggiuntivo. Questo permette all'agente di adattare il proprio comportamento
in base alle policy definite: se un hook blocca una certa azione con un messaggio esplicativo,
l'agente può scegliere un approccio alternativo senza intervento umano.
Architettura di un Workflow Hooks Completo
Fase
Hook
Azione
Prima dei comandi shell
beforeShellExecution
Blocca comandi distruttivi, richiede conferma per operazioni git
Debugging degli Hooks: Output Channel e Troubleshooting
Una delle prime cose che scopri lavorando con gli hooks e che gli errori silenziosi sono il problema
più comune. Un hook che non si avvia, un JSON malformato, un timeout non gestito: tutti scenari che
possono rendere il comportamento dell'agente imprevedibile. Cursor fornisce uno strumento dedicato
per diagnosticare questi problemi.
Per accedere al Hooks Output Channel:
Apri il pannello Output di VS Code/Cursor (View > Output o Ctrl+Shift+U)
Seleziona "Hooks" dal menu a tendina nella barra del pannello
Qui vedrai i log di ogni hook eseguito: quale script e stato chiamato, l'exit code, e tutto
ciò che lo script ha scritto su stderr
I problemi più comuni e come risolverli:
# Problema 1: Hook non trovato
# Errore: "Cannot find command: node .cursor/hooks/auto-format.js"
# Causa: il path e relativo alla workspace root, non alla posizione di hooks.json
# Soluzione: verifica che il path nel command sia relativo alla root del progetto
# Problema 2: JSON malformato nella risposta
# Errore: "Malformed JSON response from hook, silently allowing"
# Causa: output non-JSON su stdout (print di debug, output di npm, ecc.)
# Soluzione: usa SEMPRE process.stderr per i log, process.stdout SOLO per la risposta JSON
# Problema 3: Hook troppo lento (timeout)
# Causa: operazione bloccante (es. test completo della suite) senza timeout
# Soluzione: aggiungi timeout alle chiamate execSync
try {
execSync(command, {
cwd: workspaceRoot,
timeout: 10000, // 10 secondi max
stdio: ['ignore', 'pipe', 'pipe'],
});
} catch (e) {
if (e.signal === 'SIGTERM') {
process.stderr.write('[hook] Timeout raggiunto, operazione saltata\n');
}
}
# Problema 4: Hook che fallisce per mancanza di node_modules
# Causa: il progetto non ha installato le dipendenze
# Soluzione: controlla sempre l'esistenza dei binari prima di usarli
const hasPrettier = fs.existsSync(
path.join(workspaceRoot, 'node_modules', '.bin', 'prettier')
);
if (!hasPrettier) return; // Salta silenziosamente se non disponibile
Un pattern utile per il debugging iniziale e aggiungere un hook di logging che registra ogni evento
su file, in modo da avere una traccia completa di cosa l'agente ha fatto e in quale ordine:
// .cursor/hooks/debug-logger.js
// Hook universale per debug: logga tutti gli eventi su file
const readline = require('readline');
const fs = require('fs');
const path = require('path');
async function readStdin() {
return new Promise((resolve) => {
const rl = readline.createInterface({ input: process.stdin });
let data = '';
rl.on('line', (line) => { data += line; });
rl.on('close', () => resolve(data));
});
}
async function main() {
const rawInput = await readStdin();
try {
const input = JSON.parse(rawInput);
const logEntry = {
time: new Date().toISOString(),
event: input.hook_event_name,
command: input.command, // per beforeShellExecution
file_path: input.file_path, // per beforeReadFile/afterFileEdit
tool_name: input.tool_name, // per beforeMCPExecution
conversation_id: input.conversation_id,
};
const logFile = path.join(
input.workspace_roots?.[0] || '.',
'.cursor',
'hooks-debug.log'
);
fs.appendFileSync(logFile, JSON.stringify(logEntry) + '\n');
} catch (e) {
// Silenzioso in caso di errore di parsing
}
}
main();
Best Practices per Hook Robusti e Manutenibili
Dopo aver visto configurazione, esempi e debugging, vediamo i principi che guidano la costruzione
di hook di qualità professionale.
Principio 1: Fail-Open o Fail-Safe, mai Fail-Silent
Devi decidere consapevolmente come comportarti in caso di errore. Per hook di sicurezza
(blocco comandi, protezione file), il comportamento corretto e spesso fail-safe:
in caso di errore, blocca l'azione per sicurezza. Per hook di automazione (formatting, linting),
preferisci fail-open: se il formatter non e disponibile, lascia passare l'azione
senza bloccare il lavoro dell'agente. Non scegliere mai fail-silent: registra sempre l'errore su stderr.
Principio 2: Idempotenza
Gli hook possono essere chiamati più volte sullo stesso file, specialmente afterFileEdit
in Agent Mode che modifica lo stesso file in passi successivi. I tuoi hook devono essere idempotenti:
eseguire Prettier due volte sullo stesso file deve dare lo stesso risultato di eseguirlo una volta.
Evita operazioni che appendono dati o creano duplicati.
Principio 3: Performance e Timeout
Gli hook sono sincroni nel ciclo dell'agente: un hook lento rallenta tutto il workflow. Alcune
linee guida pratiche:
Per beforeShellExecution e beforeReadFile: rispondi entro 2-3 secondi
Per afterFileEdit: formatting e linting dovrebbero completare entro 10-15 secondi
Per stop: hai più margine (30-60 secondi), ma notifiche e log devono essere rapidi
Imposta sempre un timeout nelle chiamate execSync per evitare blocchi indefiniti
Principio 4: Separazione Responsabilità
Preferisci molti hook piccoli e focalizzati a un singolo hook che fa tutto. Ogni hook nel file
hooks.json e indipendente: puoi aggiungere, rimuovere o debuggare ciascuno senza
toccare gli altri. Un hook per il formatting, uno per il linting, uno per i test: ogni script ha
una responsabilità chiara.
Principio 5: Sicurezza degli Hook Stessi
Gli hook eseguono codice arbitrario: sono un possibile vettore di attacco se il file
hooks.json del progetto viene compromesso. Non includere mai segreti hardcoded negli
script degli hook. Usa variabili d'ambiente o file di configurazione esterni al repository.
Verifica i path dei file ricevuti via stdin prima di usarli in comandi shell per prevenire path
traversal.
// Esempio: validazione del path per prevenire path traversal
function isPathSafe(filePath, workspaceRoot) {
const resolvedPath = require('path').resolve(filePath);
const resolvedRoot = require('path').resolve(workspaceRoot);
// Verifica che il file sia dentro la workspace root
return resolvedPath.startsWith(resolvedRoot + require('path').sep);
}
// Usa sempre questa validazione prima di operazioni su file
if (!isPathSafe(input.file_path, workspaceRoot)) {
process.stderr.write('[hook] Path non sicuro, operazione saltata\n');
return;
}
Hooks vs Cursor Rules: Quando Usare Quale
Le Cursor Rules istruiscono l'agente su come scrivere codice (stile,
convenzioni, pattern da seguire). Gli Hooks controllano cosa succede
quando l'agente esegue azioni (formatting automatico, validazione sicurezza, notifiche).
Sono complementari: usa le Rules per guidare la generazione del codice, gli Hooks per garantire
la qualità e la sicurezza delle azioni.
Conclusioni
Il sistema Hooks di Cursor rappresenta un salto qualitativo nel controllo che hai sull'agente AI.
Non si tratta più di sperare che l'agente faccia la cosa giusta: puoi definire policy precise,
automazioni affidabili e guardrail di sicurezza che entrano in gioco automaticamente, ad ogni
sessione, per ogni sviluppatore del team.
I pattern più efficaci emersi dalla community nel 2025 combinano:
beforeShellExecution per security policy rigorose e protezione dei branch git
afterFileEdit per code quality automatica (formatting + linting) senza
dipendere dalla disciplina di ogni singolo sviluppatore
stop per feedback, notifiche e verifica post-task
Hooks di progetto (.cursor/hooks.json) committed nel repository per condividere
le stesse policy con tutto il team
Il passo successivo naturale dopo gli Hooks e il MCP (Model Context Protocol): se
gli Hooks ti permettono di controllare le azioni dell'agente, MCP ti permette di connettere
l'agente a fonti di dati esterne come database, API REST e strumenti di terze parti. Nell'articolo
successivo vedremo come configurare server MCP con Cursor per creare workflow che integrano
direttamente database PostgreSQL, filesystem remoti e API esterne.
Prossimi Passi nella Serie
Articolo Precedente: Plan Mode e Background Agents - come pianificare ed
eseguire task complessi in parallelo
Articolo Successivo: MCP e Cursor: Connetti IDE a Database e API - integra
database e servizi esterni direttamente nell'agente
Correlato:Introduzione a MCP - la serie dedicata al
Model Context Protocol
Correlato:Agent Mode - come delegare task complessi
all'agente prima di configurare i hooks