title: Criteri Personalizzati
description: “Scrivi i tuoi criteri in JavaScript - applica convenzioni, previeni deviazioni, rilevai guasti, integra con sistemi esterni”
icon: code
I criteri personalizzati ti permettono di scrivere regole per qualsiasi comportamento dell’agente: applica convenzioni di progetto, previeni deviazioni, blocca operazioni distruttive, rileva agenti bloccati, o integra con Slack, flussi di approvazione e altro. Usano lo stesso sistema di eventi hook e le decisioni allow, deny, instruct dei criteri integrati.
Esempio rapido
// my-policies.js
import { customPolicies, allow, deny, instruct } from "failproofai";
customPolicies.add({
name: "no-production-writes",
description: "Block writes to paths containing 'production'",
match: { events: ["PreToolUse"] },
fn: async (ctx) => {
if (ctx.toolName !== "Write" && ctx.toolName !== "Edit") return allow();
const path = ctx.toolInput?.file_path ?? "";
if (path.includes("production")) {
return deny("Writes to production paths are blocked");
}
return allow();
},
});
Installalo:
failproofai policies --install --custom ./my-policies.js
Due modi per caricare criteri personalizzati
Opzione 1: Basata su convenzione (consigliato)
Inserisci i file *policies.{js,mjs,ts} in .failproofai/policies/ e vengono caricati automaticamente — nessun flag o modifica di configurazione necessaria. Funziona come i git hook: inserisci un file e funziona.
# Livello di progetto — incluso in git, condiviso con il team
.failproofai/policies/security-policies.mjs
.failproofai/policies/workflow-policies.mjs
# Livello utente — personale, si applica a tutti i progetti
~/.failproofai/policies/my-policies.mjs
Come funziona:
- Entrambe le directory di progetto e utente vengono scansionate (unione — non primo ambito vince)
- I file vengono caricati alfabeticamente all’interno di ogni directory. Aggiungi il prefisso
01-, 02- per controllare l’ordine
- Solo i file che corrispondono a
*policies.{js,mjs,ts} vengono caricati; gli altri file vengono ignorati
- Ogni file viene caricato indipendentemente (fail-open per file)
- Funziona insieme ai criteri espliciti
--custom e integrati
I criteri di convenzione sono il modo più semplice per condividere criteri tra un team. Includi .failproofai/policies/ in git e ogni membro del team li otterrà automaticamente.
Opzione 2: Percorso file esplicito
# Installa con un file di criteri personalizzati
failproofai policies --install --custom ./my-policies.js
# Sostituisci il percorso del file di criteri
failproofai policies --install --custom ./new-policies.js
# Rimuovi il percorso dei criteri personalizzati dalla configurazione
failproofai policies --uninstall --custom
Il percorso assoluto risolto viene memorizzato in policies-config.json come customPoliciesPath. Il file viene caricato di nuovo ad ogni evento hook - non c’è caching tra gli eventi.
Usare entrambi insieme
I criteri di convenzione e il file esplicito --custom possono coesistere. Ordine di caricamento:
- File
customPoliciesPath esplicito (se configurato)
- File di convenzione di progetto (
{cwd}/.failproofai/policies/, alfabetici)
- File di convenzione utente (
~/.failproofai/policies/, alfabetici)
API
Importazione
import { customPolicies, allow, deny, instruct } from "failproofai";
customPolicies.add(hook)
Registra un criterio. Chiama questo tutte le volte che necessario per più criteri nello stesso file.
customPolicies.add({
name: string; // obbligatorio - identificatore univoco
description?: string; // mostrato nell'output di `failproofai policies`
match?: { events?: HookEventType[] }; // filtra per tipo di evento; ometti per corrispondere a tutti
fn: (ctx: PolicyContext) => PolicyResult | Promise<PolicyResult>;
});
Helper di decisione
| Funzione | Effetto | Usa quando |
|---|
allow() | Consenti l’operazione silenziosamente | L’azione è sicura, nessun messaggio necessario |
deny(message) | Blocca l’operazione | L’agente non dovrebbe compiere questa azione |
instruct(message) | Aggiungi contesto senza bloccare | Dai all’agente contesto aggiuntivo per rimanere in traccia |
deny(message) - il messaggio viene visualizzato a Claude con prefisso "Blocked by failproofai:". Un singolo deny fa corto circuito di tutta la valutazione ulteriore.
instruct(message) - il messaggio viene aggiunto al contesto di Claude per la chiamata dello strumento corrente. Tutti i messaggi instruct vengono accumulati e consegnati insieme.
Puoi aggiungere guidance extra a qualsiasi messaggio deny o instruct aggiungendo un campo hint in policyParams — nessuna modifica di codice necessaria. Questo funziona per i criteri personalizzati (custom/), di convenzione di progetto (.failproofai-project/), e di convenzione utente (.failproofai-user/) anche. Vedi Configurazione → hint per i dettagli.
allow(message) consente l’operazione e invia un messaggio informativo indietro a Claude. Il messaggio viene consegnato come additionalContext nella risposta stdout del gestore dell’evento hook — lo stesso meccanismo usato da instruct, ma semanticamente diverso: è un aggiornamento di stato, non un avviso.
| Funzione | Effetto | Usa quando |
|---|
allow(message) | Consenti e invia contesto a Claude | Conferma che un controllo è passato, o spiega perché un controllo è stato saltato |
Casi d’uso:
- Conferme di stato:
allow("All CI checks passed.") — dice a Claude che tutto è verde
- Spiegazioni fail-open:
allow("GitHub CLI not installed, skipping CI check.") — dice a Claude perché un controllo è stato saltato così ha il contesto completo
- Più messaggi si accumulano: se più criteri ciascuno restituisce
allow(message), tutti i messaggi sono uniti con newline e consegnati insieme
customPolicies.add({
name: "confirm-branch-status",
match: { events: ["Stop"] },
fn: async (ctx) => {
const cwd = ctx.session?.cwd;
if (!cwd) return allow("No working directory, skipping branch check.");
// ... check branch status ...
if (allPushed) {
return allow("Branch is up to date with remote.");
}
return deny("Unpushed changes detected.");
},
});
Campi PolicyContext
| Campo | Tipo | Descrizione |
|---|
eventType | string | "PreToolUse", "PostToolUse", "Notification", "Stop" |
toolName | string | undefined | Lo strumento che viene chiamato (es. "Bash", "Write", "Read") |
toolInput | Record<string, unknown> | undefined | I parametri di input dello strumento |
payload | Record<string, unknown> | Payload di evento grezzo completo da Claude Code |
session | SessionMetadata | undefined | Contesto di sessione (vedi sotto) |
| Campo | Tipo | Descrizione |
|---|
sessionId | string | Identificatore di sessione Claude Code |
cwd | string | Directory di lavoro della sessione Claude Code |
transcriptPath | string | Percorso al file transcript JSONL della sessione |
Tipi di evento
| Evento | Quando si attiva | Contenuti toolInput |
|---|
PreToolUse | Prima che Claude esegua uno strumento | L’input dello strumento (es. { command: "..." } per Bash) |
PostToolUse | Dopo che uno strumento si completa | L’input dello strumento + tool_result (l’output) |
Notification | Quando Claude invia una notifica | { message: "...", notification_type: "idle" | "permission_prompt" | ... } - i hook devono sempre restituire allow(), non possono bloccare le notifiche |
Stop | Quando la sessione Claude termina | Vuoto |
Ordine di valutazione
I criteri vengono valutati in questo ordine:
- Criteri integrati (in ordine di definizione)
- Criteri personalizzati espliciti da
customPoliciesPath (in ordine .add())
- Criteri di convenzione da
.failproofai/policies/ di progetto (file alfabetici, ordine .add() interno)
- Criteri di convenzione da
~/.failproofai/policies/ utente (file alfabetici, ordine .add() interno)
Il primo deny fa corto circuito di tutti i criteri successivi. Tutti i messaggi instruct vengono accumulati e consegnati insieme.
Importazioni transitive
I file di criteri personalizzati possono importare moduli locali usando percorsi relativi:
// my-policies.js
import { isBlockedPath } from "./utils.js";
import { checkApproval } from "./approval-client.js";
customPolicies.add({
name: "approval-gate",
fn: async (ctx) => {
if (ctx.toolName !== "Bash") return allow();
const approved = await checkApproval(ctx.toolInput?.command, ctx.session?.sessionId);
return approved ? allow() : deny("Approval required for this command");
},
});
Tutte le importazioni relative raggiungibili dal file di entry vengono risolte. Questo viene implementato riscrivendo le importazioni from "failproofai" al percorso dist effettivo e creando file .mjs temporanei per assicurare la compatibilità ESM.
Filtraggio del tipo di evento
Usa match.events per limitare quando un criterio si attiva:
customPolicies.add({
name: "require-summary-on-stop",
match: { events: ["Stop"] },
fn: async (ctx) => {
// Si attiva solo quando la sessione termina
// ctx.session.transcriptPath contiene il log di sessione completo
return allow();
},
});
Ometti match interamente per attivarsi su ogni tipo di evento.
Gestione degli errori e modalità di errore
I criteri personalizzati sono fail-open: gli errori non bloccano mai i criteri integrati o causano crash del gestore dell’hook.
| Errore | Comportamento |
|---|
customPoliciesPath non impostato | Nessun criterio personalizzato esplicito viene eseguito; i criteri di convenzione e integrati continuano normalmente |
| File non trovato | Avviso registrato su ~/.failproofai/hook.log; i criteri integrati continuano |
| Errore di sintassi/importazione (esplicito) | Errore registrato su ~/.failproofai/hook.log; criteri personalizzati espliciti saltati |
| Errore di sintassi/importazione (convenzione) | Errore registrato; quel file saltato, altri file di convenzione ancora caricati |
fn lancia al runtime | Errore registrato; quel hook trattato come allow; altri hook continuano |
fn impiega più di 10s | Timeout registrato; trattato come allow |
| Directory di convenzione mancante | Nessun criterio di convenzione viene eseguito; nessun errore |
Per eseguire il debug degli errori di criteri personalizzati, guarda il file di log:tail -f ~/.failproofai/hook.log
Esempio completo: più criteri
// my-policies.js
import { customPolicies, allow, deny, instruct } from "failproofai";
// Evita che l'agente scriva nella directory secrets/
customPolicies.add({
name: "block-secrets-dir",
description: "Prevent agent from writing to secrets/ directory",
match: { events: ["PreToolUse"] },
fn: async (ctx) => {
if (!["Write", "Edit"].includes(ctx.toolName ?? "")) return allow();
const path = ctx.toolInput?.file_path ?? "";
if (path.includes("secrets/")) return deny("Writing to secrets/ is not permitted");
return allow();
},
});
// Tieni l'agente in traccia: verifica i test prima di committare
customPolicies.add({
name: "remind-test-before-commit",
description: "Keep the agent on track: verify tests pass before committing",
match: { events: ["PreToolUse"] },
fn: async (ctx) => {
if (ctx.toolName !== "Bash") return allow();
const cmd = ctx.toolInput?.command ?? "";
if (/git\s+commit/.test(cmd)) {
return instruct("Verify all tests pass before committing. Run `bun test` if you haven't already.");
}
return allow();
},
});
// Previeni modifiche non pianificate alle dipendenze durante il freeze
customPolicies.add({
name: "dependency-freeze",
description: "Prevent unplanned dependency changes during freeze period",
match: { events: ["PreToolUse"] },
fn: async (ctx) => {
if (ctx.toolName !== "Bash") return allow();
const cmd = ctx.toolInput?.command ?? "";
const isInstall = /^(npm install|yarn add|bun add|pnpm add)\s+\S/.test(cmd);
if (isInstall && process.env.DEPENDENCY_FREEZE === "1") {
return deny("Package installs are frozen. Unset DEPENDENCY_FREEZE to allow.");
}
return allow();
},
});
export { customPolicies };
Esempi
La directory examples/ contiene file di criteri pronti all’uso:
| File | Contenuti |
|---|
examples/policies-basic.js | Cinque criteri iniziali che coprono modalità di errore comuni dell’agente |
examples/policies-advanced/index.js | Schemi avanzati: importazioni transitive, chiamate asincrone, scrubbing di output, e hook di fine sessione |
examples/convention-policies/security-policies.mjs | Criteri di sicurezza basati su convenzione (blocca scritture .env, previeni riscrittura della cronologia git) |
examples/convention-policies/workflow-policies.mjs | Criteri di flusso di lavoro basati su convenzione (promemoria di test, file di scrittura di audit) |
Uso di esempi di file espliciti
failproofai policies --install --custom ./examples/policies-basic.js
Uso di esempi basati su convenzione
# Copia a livello di progetto
mkdir -p .failproofai/policies
cp examples/convention-policies/*.mjs .failproofai/policies/
# Oppure copia a livello di utente
mkdir -p ~/.failproofai/policies
cp examples/convention-policies/*.mjs ~/.failproofai/policies/
Nessun comando di installazione necessario — i file vengono prelevati automaticamente al prossimo evento hook.