05 - Cursor Hooks: Automatizujte pracovní postup pomocí AI Guardrails
Práce s autonomními agenty AI má strukturální problém: nemůžete ovládat každého akce v reálném čase, ale nemůžete ani slepě věřit tomu, co dělají. Agent, který píše kód, spouštět příkazy v terminálu a upravovat soubory v automatickém pořadí může být mimořádně produktivní, ale také nebezpečně nepředvídatelné, pokud neexistují žádné kontrolní mechanismy.
Odpověď kurzoru na tento problém se nazývá Háčky. Uvedeno s kurzorem 1,7 palce 2025 vám systém Hooks umožňuje vkládat vlastní logiku v přesných bodech životního cyklu agenta: před provedením příkazu shellu, po úpravě souboru, před voláním MCP, až jeho dílo skončí. Jsou to v podstatě bohové automatické zábradlí že můžeš nakonfigurovat jednou a které se spustí pokaždé, když agent pracuje ve vaší kódové základně.
Tento článek zkoumá systém Hooks do hloubky: od základní konfigurace až po vzory pokročilé funkce pro automatické formátování, ověřování zabezpečení, automatizaci testování a synchronizaci dokumentace. Na konci budete schopni vybudovat robustní pracovní postup, kde je agent AI produktivní, ale vždy pod vaší kontrolou.
Co se dozvíte v tomto článku
- Co je systém Cursor's Hooks a jak se liší od jednoduchých skriptů
- Dostupné typy háčků: beforeShellExecution, beforeMCPEexecution, beforeReadFile, afterFileEdit, stop
- Jak nastavit globální a projektové háčky pomocí hooks.json
- Jak vytvořit háčky, které blokují nebezpečné akce pomocí výstupního kódu a odezvy JSON
- Automatizace po úpravách: automatické formátování s Prettier/ESLint přes afterFileEdit
- Zastavovací háky pro konečné ověření a upozornění po dokončení úkolu
- Praktické vzory: bezpečnostní kontrola, automatický testovací běh, synchronizační dokumentace
- Jak se háčky integrují s režimem agenta pro úplný a bezpečný pracovní postup
- Ladicí háčky: výstupní kanál, protokolování, odstraňování běžných chyb
- Nejlepší postupy pro idempotentní, výkonné a udržovatelné háčky
Pozice v řadě: Cursor IDE a AI-Native Development
| # | Položka | Úroveň |
|---|---|---|
| 1 | Cursor IDE: Kompletní průvodce pro vývojáře | Začátečník |
| 2 | Pravidla kurzoru: Konfigurace AI pro váš projekt | Střední |
| 3 | Režim agenta: Upravte kódovou základnu pomocí příkazu | Střední |
| 4 | Režim plánu a agenti na pozadí | Moderní |
| 5 | Kurzorové háky: Automatizujte pracovní postup (jste zde) | Střední |
| 6 | MCP a kurzor: Připojte IDE k databázi a API | Moderní |
| 7 | Ladění pomocí kurzoru AI: 3x rychlejší | Střední |
| 8 | Cursor vs Windsurf vs Copilot v roce 2026 | Začátečník |
| 9 | Profesionální pracovní postup: Úhlový projekt s kurzorem | Moderní |
Co je háček v kurzoru
Háček v kurzoru a externí proces který běží automaticky jako odpověď na konkrétní události životního cyklu agenta. Když se agent chystá provést příkaz shellu, Kurzor volá váš hák a předává mu informace o akci v JSON přes stdin. Váš háček může analyzovat tyto informace a reagovat direktivou: povolit akci, zablokovat ji, popř požádat uživatele o potvrzení.
Je nezbytné pochopit, že háčky nejsou to jednoduché shellové skripty které se odevzdávají pozadí. Jsou to synchronní procesy integrované do cyklu agenta: agent se zastaví, čeká na hook response, poté se na základě této odpovědi rozhodne, jak postupovat. To z nich dělá nástroje výkonný pro ověřování, audit a automatizaci.
Háčky dostupné v Cursoru 1.7+ pokrývají tyto události životního cyklu:
- předShellExecution - Provádí se před každým příkazem v terminálu. Přijímá přesný příkaz, který se agent chystá provést. Může blokovat nebezpečné příkazy, protokolovat je nebo je upravovat.
- před provedením MCP - Provádí se před každým voláním na server MCP. Přijímá název nástroje a vstupní parametry. Užitečné pro auditování operací databáze nebo API.
- předReadFile - Spuštěno předtím, než agent přečte obsah souboru. Umožňuje řídit, ke kterým souborům může agent přistupovat, nebo je maskovat k citlivým informacím.
- afterFileEdit - Spouští se po každé změně souboru. Přijímá cestu soubor a provedené změny (starý_řetězec/nový_řetězec). Ideální pro automatické formátování a odstraňování vláken.
- zastávka - Provede se, když agent dokončí svůj úkol. Užitečné pro upozornění, automatické potvrzení, synchronizace dokumentace a akce čištění.
Funkce beta v Evoluci
Cursor Hooks byly představeny jako beta funkce s Cursor 1.7 v říjnu 2025. dokumentace a rozhraní API se mohou v následujících vydáních změnit. Před implementací háčků v produkčním prostředí zkontrolujte oficiální dokumentaci na adrese kurzor.com/docs/agent/hooks pro případné aktualizace.
Konfigurace: Globální a hooks.json na úrovni projektu
Háčky kurzoru se konfigurují pomocí souboru hooks.json. Existují dvě úrovně konfigurace, které fungují doplňkovým způsobem:
- Globální háčky (
~/.cursor/hooks.json) - Platí pro všechny otevřít projekty pomocí kurzoru na tomto počítači. Ideální pro zásady osobní bezpečnosti, upozornění nebo nástroje auditu, které chcete mít vždy aktivní. - Projektové háčky (
.cursor/hooks.jsonv kořenovém adresáři repo) - Ano vztahují se pouze na aktuální projekt a jsou zavázány k úložišti. Umožňují vám sdílet stejné automatizace s celým týmem.
Když existují oba soubory, háky jsou sloučeny: nejprve se spouštějí globální háky,
pak ty z projektu pro každou událost. Formát souboru je jednoduchý: objekt JSON s a
verze a objekt hooks který mapuje každou událost na řadu příkazů.
Zde je úplný příklad hooks.json který konfiguruje háčky pro všechny hlavní události:
// .cursor/hooks.json - Configurazione hooks a livello di progetto
{
"version": 1,
"hooks": {
"beforeShellExecution": [
{
"command": "node .cursor/hooks/security-check.js"
}
],
"beforeMCPExecution": [
{
"command": "node .cursor/hooks/mcp-audit.js"
}
],
"beforeReadFile": [
{
"command": "bash .cursor/hooks/file-access-policy.sh"
}
],
"afterFileEdit": [
{
"command": "node .cursor/hooks/auto-format.js"
},
{
"command": "node .cursor/hooks/run-tests.js"
}
],
"stop": [
{
"command": "node .cursor/hooks/final-check.js"
}
]
}
}
Pole command přijímá jakýkoli spustitelný příkaz: skript Node.js, bash, Python,
npm binární soubory. Kurzor spustí příkaz jako samostatný proces, předá data události pryč
stdin ve formátu JSON a přečte odezvu zavěšení stdout.
Chyby a diagnostické protokoly jdou nahoru stderr.
Základní struktura JSON, kterou kurzor posílá přes stdin do každého háku, zahrnuje tato společná pole:
// 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...
}
Hák před provedením: Blokujte nebezpečné akce
Háčky před jsou nejmocnější, protože dokážou zablokovat akce dříve, než k nim dojde. Nejbezprostřednějším případem použití je ochrana důležitých souborů nebo adresářů před náhodnými změnami agenta nebo blokovat destruktivní příkazy shellu.
Pro chlapa předShellExecution, JSON přijatý přes stdin obsahuje pole
command s přesným příkazem, který se agent chystá provést, a polem cwd
s aktuálním pracovním adresářem:
// Input stdin per beforeShellExecution
{
"conversation_id": "668320d2-2fd8-4888-b33c-2a466fec86e7",
"generation_id": "490b90b7-a2ce-4c2c-bb76-cb77b125df2f",
"command": "rm -rf ./dist",
"cwd": "/Users/dev/my-project",
"hook_event_name": "beforeShellExecution",
"workspace_roots": ["/Users/dev/my-project"]
}
Hák odpoví přes stdout JSON, který označuje, zda má být akce povolena nebo blokována.
Klíčové pole e permission s hodnotami "allow", "deny"
o "ask". Zde je háček Node.js, který blokuje potenciálně destruktivní příkazy:
// .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: ${command}\n`);
process.stdout.write(
JSON.stringify({
permission: 'deny',
userMessage: `Comando bloccato dalla security policy: ${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: ${command} - Procedi?`,
})
);
return;
}
// Comando sicuro: consenti
process.stdout.write(JSON.stringify({ permission: 'allow' }));
}
main().catch((err) => {
process.stderr.write(`[security-check] Errore: ${err.message}\n`);
process.stdout.write(JSON.stringify({ permission: 'allow' })); // fail-open
});
Con "permission": "ask", Kurzor zobrazí uživateli předtím dialog pro potvrzení
pokračovat a zobrazit zprávu definovanou v userMessage. Uživatel může schválit
nebo akci odmítnout. S "permission": "deny", akce je trvale zablokována
a zpráva se zobrazí jak uživateli, tak předá agentovi jako kontext.
Hák pro ochranu konkrétních souborů s beforeReadFile
Háček beforeReadFile obdrží pole file_path ve standardním JSON.
Můžete jej použít k tomu, abyste zabránili agentovi ve čtení souborů s pověřeními nebo tajnými údaji nebo do
maskovat obsah citlivých souborů před jejich předáním do modelu AI.
Hook afterFileEdit: Auto-Format a Auto Linting
Háček afterFileEdit a nejpoužívanější v každodenní praxi. Je proveden pokaždé, když agent zapíše nebo upraví soubor, a obdrží JSON s cestou k souboru přes stdin upraveno a řada změn s původními a novými řetězci:
// Input stdin per afterFileEdit
{
"conversation_id": "abc123",
"generation_id": "def456",
"file_path": "/Users/dev/my-project/src/app/services/user.service.ts",
"edits": [
{
"old_string": "const data = undefined",
"new_string": "const data: UserData | null = null"
}
],
"hook_event_name": "afterFileEdit",
"workspace_roots": ["/Users/dev/my-project"]
}
S těmito daty můžete automaticky spustit svůj oblíbený formátovač na souboru, který jste právě upravili. Zde je kompletní háček Node.js, který používá Prettier pro TypeScript/JavaScript a ESLint pro opravu automatické, respektující konfigurace již přítomné v projektu:
// .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 "${filePath}"`,
{
cwd: workspaceRoot,
stdio: ['ignore', 'pipe', 'pipe'],
timeout: 10000,
}
);
process.stderr.write(`[auto-format] Prettier OK: ${filePath}\n`);
} catch (e) {
process.stderr.write(
`[auto-format] Prettier warning: ${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 "${filePath}"`,
{
cwd: workspaceRoot,
stdio: ['ignore', 'pipe', 'pipe'],
timeout: 15000,
}
);
process.stderr.write(`[auto-format] ESLint --fix OK: ${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): ${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: ${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 nevyžaduje odpověď JSON
Na rozdíl od háčků před, háček afterFileEdit a určené pro efekty
zajištění: formátování, linting, aktualizace indexu. Nemělo by vrátit odpověď JSON
na stdout, protože akce (úprava souboru) již proběhla. Háček dělá svou práci a
končí; Kurzor pokračuje bez ohledu na kód ukončení.
Stop Hooks: Finální ověření a upozornění
Háček zastávka se provede, když agent považuje svůj úkol za dokončený. Ne
přijímá podrobnosti o konkrétních provedených akcích, ale má k nim přístup conversation_id a ai
workspace_roots. And the ideal time for closing actions such as notifications, commits
automatické aktualizace dokumentace nebo spouštění testů celé sady.
// Input stdin per stop hook
{
"conversation_id": "668320d2-2fd8-4888-b33c-2a466fec86e7",
"generation_id": "490b90b7-a2ce-4c2c-bb76-cb77b125df2f",
"hook_event_name": "stop",
"workspace_roots": ["/Users/dev/my-project"]
}
Zde jsou dva praktické příklady zastavovacích háčků: první odešle upozornění na plochu na macOS, druhý provede git ověření práce vytvořené agentem:
// .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 ${modifiedFiles.length} file:\n`
);
modifiedFiles.forEach((f) => process.stderr.write(` ${f}\n`));
} else {
process.stderr.write('[git-check] Nessuna modifica in staging\n');
}
} catch (e) {
process.stderr.write(`[git-check] Git non disponibile: ${e.message}\n`);
}
}
main();
Praktické příklady použití pro vývojové týmy
Háčky se stávají obzvláště cennými při práci v týmech nebo na projektech s požadavky přísný v kvalitě. Podívejme se na nejúčinnější vzorce vycházející z komunity Cursor v roce 2025.
Vzor 1: Bezpečnostní audit před provozem MCP
Pokud používáte MCP s databázemi nebo externími rozhraními API, hák beforeMCPExecution umožňuje přihlášení
každou operaci před jejím provedením. Vstupní JSON obsahuje název nástroje MCP a i
přesné parametry předané agentovi:
// .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: ${toolName}`,
agentMessage: `Il tool MCP '${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();
Vzor 2: Automatický testovací běh po změnách testu
Dalším velmi užitečným vzorem je automatické spouštění testů, když agent upravuje soubory specifikace nebo test. Tím je zajištěno, že změny nenaruší stávající sadu:
// .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: ${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="**/${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 ${path.basename(filePath)}\n`
);
process.stderr.write(e.stdout?.toString() || e.message);
}
}
main();
Vzor 3: Dokumentace v synchronizaci s Codebase
Velmi užitečný vzor pro technickou dokumentaci: když agent upravuje rozhraní TypeScript nebo směrovací soubor, stophák může zkontrolovat, zda je dokumentace aktuální nebo zpráva co potřebuje revizi:
// .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: ${interfaceFiles.length} file di interfacce modificati.\n` +
`Considera di aggiornare la documentazione in /docs:\n` +
interfaceFiles.map((f) => ` - ${f}`).join('\n') + '\n'
);
}
}
} catch (e) {
process.stderr.write(`[doc-sync] Errore: ${e.message}\n`);
}
}
main();
Režim háčků a agenta: Kompletní řídicí systém
Integrace mezi Hooks a Agent Mode vytváří zpětnou vazbu, díky které je agent bezpečnější a bezpečnější časem spolehlivý. Když agent pracuje autonomně na složitých úkolech, fungují háky jako záchranná síť, která působí na každém kroku realizačního cyklu.
Zvažte tento scénář ze skutečného světa: Používáte Agent Mode k refaktorování Angular modulu. Agent provádí desítky akcí za sebou: čte soubory, upravuje komponenty, aktualizuje směrování, spouští npm commands. Se správně nakonfigurovanými háčky je každý kritický krok zachycen:
- předShellExecution - Blokujte jakýkoli příkaz
npm installneschváleno nebo zatlačte na chráněné větve - předReadFile - Brání agentovi ve čtení souborů
.envnebo konfigurační soubory s tajnými kódy - afterFileEdit - Každý upravený soubor je automaticky naformátován pomocí Hezčí a zkontrolováno pomocí ESLint, než přejdete k dalšímu kroku
- zastávka - Když agent dokončí, obdržíte oznámení a shrnutí git změny ke kontrole
Zprávy agentMessage v háku jsou odpovědi předány modelu AI jako
další kontext. To agentovi umožňuje přizpůsobte své chování
na základě definovaných zásad: pokud hák zablokuje určitou akci s vysvětlující zprávou,
agent může zvolit alternativní přístup bez lidského zásahu.
Architektura kompletního pracovního postupu háčků
| Fáze | Háček | Akce |
|---|---|---|
| Před příkazy shellu | předShellExecution | Blokujte destruktivní příkazy, vyžadují potvrzení pro operace git |
| Před voláním MCP | před provedením MCP | Audit log, blokování nebezpečných operací DB |
| Před čtením souborů | předReadFile | Ochrana souborů .env, tajné maskování |
| Po každé změně | afterFileEdit | Prettier, ESLint --fix, index update |
| Ukončit úkol | zastávka | Oznámení na ploše, stav git, kontrola dokumentace |
Ladicí háčky: Výstupní kanál a odstraňování problémů
Jedna z prvních věcí, které zjistíte při práci s háčky, je, že problémem jsou tiché chyby běžnější. Hák, který se nespustí, chybně vytvořený JSON, neošetřený časový limit – to jsou všechny scénáře může způsobit, že chování agenta je nepředvídatelné. Kurzor poskytuje speciální nástroj k diagnostice těchto problémů.
Pro přístup k Háčky Výstupní kanál:
- Otevřete panel VS Code/Cursor Output (Zobrazit > Výstup nebo
Ctrl+Shift+U) - Z rozevírací nabídky na panelu panelů vyberte možnost „Háčky“.
- Zde uvidíte protokoly každého provedeného háku: který skript byl zavolán, ukončovací kód a vše co skript napsal do stderr
Nejčastější problémy a jak je řešit:
# 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
Užitečný vzor pro počáteční ladění a přidání háčku protokolování, který zaznamenává každou událost do souboru, takže máte kompletní přehled o tom, co agent udělal a v jakém pořadí:
// .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();
Nejlepší postupy pro robustní a udržovatelné háky
Poté, co se podíváme na konfiguraci, příklady a ladění, pojďme se podívat na principy, které vedou konstrukci háčků profesionální kvality.
Princip 1: Fail-Open nebo Fail-Safe, nikdy Fail-Silent
Musíte se vědomě rozhodnout, jak se zachováte v případě chyby. Pro bezpečnostní háčky (command lock, file protection), correct behavior and often bezporuchový: v případě chyby akci pro jistotu zablokujte. Pro automatizační háčky (formátování, lintování), dáváte přednost fail-open: Pokud formátovač není k dispozici, nechte akci projít bez blokování práce agenta. Nikdy nevybírejte bezobslužné: vždy zaznamenejte chybu do stderr.
Princip 2: Idempotence
Háčky lze volat vícekrát na stejném souboru, např afterFileEdit
v režimu agenta, který upravuje stejný soubor v postupných krocích. Vaše háčky musí být idempotentní:
spuštění Prettier dvakrát na stejném souboru by mělo dát stejný výsledek jako spuštění jednou.
Vyhněte se operacím, které připojují data nebo vytvářejí duplikáty.
Princip 3: Výkon a časové limity
Háky jsou ve smyčce agentů synchronní: pomalý hák zpomaluje celý pracovní postup. Některé praktické pokyny:
- Pro
beforeShellExecutionebeforeReadFile: Odpovězte do 2–3 sekund - Pro
afterFileEdit: Formátování a lincování by mělo být dokončeno během 10–15 sekund - Pro
stop: máte větší rezervu (30-60 sekund), ale upozornění a protokoly musí být rychlé - Vždy nastavte časový limit pro hovory
execSyncaby nedošlo k neomezenému blokování
Zásada 4: Oddělení odpovědností
Upřednostněte mnoho malých, soustředěných háčků před jediným háčkem, který zvládne vše. Každý háček v souboru
hooks.json a nezávislé: můžete přidat, odebrat nebo ladit každý bez
dotýkat se ostatních. Jeden háček pro formátování, jeden pro linting, jeden pro testování: každý skript má
jasná zodpovědnost.
Zásada 5: Bezpečnost samotných háčků
Háky provádějí libovolný kód: jsou možným vektorem útoku, pokud
hooks.json projektu je ohrožena. Nikdy do svého nezahrnujte pevně zakódovaná tajemství
hákové skripty. Použijte proměnné prostředí nebo konfigurační soubory mimo úložiště.
Zkontrolujte cesty souborů přijatých přes stdin před jejich použitím v příkazech shellu, abyste zabránili cestě
procházení.
// 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;
}
Pravidla háčků vs kurzoru: Kdy použít které
Le Pravidla kurzoru dávají pokyn agentovi o jako napsat kód (styl, konvence, vzory, které je třeba dodržovat). The Háčky kontrolují Co stane se to když agent provádí akce (automatické formátování, ověřování zabezpečení, upozornění). Jsou komplementární: použijte Pravidla pro generování kódu, Háky pro zaručení kvalitu a bezpečnost akcí.
Závěry
Systém Cursor's Hooks představuje kvantový skok v kontrole, kterou máte nad agentem AI. Už to není o doufání, že agent udělá správnou věc: můžete definovat přesné zásady, spolehlivá automatizace a bezpečnostní zábradlí, které přicházejí do hry automaticky, kdykoli relaci pro každého vývojáře v týmu.
Nejúčinnější vzorce vycházející z komunity v roce 2025 kombinují:
- předShellExecution pro přísné bezpečnostní zásady a ochranu větví git
- afterFileEdit pro automatickou kvalitu kódu (formátování + linting) bez závisí na disciplíně každého jednotlivého vývojáře
- zastávka pro zpětnou vazbu, upozornění a ověření po úloze
- Projektové háčky (
.cursor/hooks.json) zavázala se k úložišti sdílet stejné zásady s celým týmem
Přirozený další krok po Hooks and the MCP (Model Context Protocol): pokud Háky umožňují ovládat akce agenta, MCP umožňuje připojení agenta na externí zdroje dat, jako jsou databáze, REST API a nástroje třetích stran. V článku Dále uvidíme, jak nakonfigurovat servery MCP s kurzorem, aby se vytvořily pracovní postupy, které se integrují přímo PostgreSQL databáze, vzdálené souborové systémy a externí API.
Další kroky v sérii
- Předchozí článek: Režim plánu a agenti na pozadí - jak plánovat ed paralelně provádět složité úkoly
- Další článek: MCP a kurzor: Připojte IDE k databázi a API – integrujte databází a externích služeb přímo v agentovi
- Související: Úvod do MCP - série věnovaná Protokol kontextu modelu
- Související: Režim agenta - jak delegovat složité úkoly agentovi před konfigurací háčků







