Vai al contenuto principale
Le policy personalizzate ti permettono di scrivere regole per qualsiasi comportamento dell’agente: applicare convenzioni di progetto, prevenire deviazioni, bloccare operazioni distruttive, rilevare agenti bloccati, o integrarti con Slack, workflow di approvazione e altro ancora. Utilizzano lo stesso sistema di hook event e le stesse decisioni allow, deny, instruct delle policy integrate.

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 policy personalizzate

Opzione 1: Basata su convenzione (consigliato)

Inserisci file *policies.{js,mjs,ts} in .failproofai/policies/ e verranno caricati automaticamente — nessun flag o cambio di configurazione necessario. Funziona come i git hooks: inserisci un file e basta.
# Livello di progetto — committato su 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 first-scope-wins)
  • I file vengono caricati alfabeticamente all’interno di ogni directory. Utilizza il prefisso 01-, 02- per controllare l’ordine
  • Vengono caricati solo i file che corrispondono a *policies.{js,mjs,ts}; gli altri file vengono ignorati
  • Ogni file viene caricato in modo indipendente (fail-open per file)
  • Funziona insieme alle policy --custom esplicite e alle policy integrate
Le policy di convenzione sono il modo più semplice per costruire uno standard di qualità per la tua organizzazione. Committi .failproofai/policies/ su git e ogni membro del team otterrà automaticamente le stesse regole — nessuna configurazione per sviluppatore necessaria. Mentre il tuo team scopre nuovi modi di fallimento, aggiungi una policy e fai il push. Nel tempo questi diventano uno standard di qualità dinamico che continua a migliorare con ogni contributo.

Opzione 2: Percorso file esplicito

# Installa con un file di policy personalizzate
failproofai policies --install --custom ./my-policies.js

# Sostituisci il percorso del file di policy
failproofai policies --install --custom ./new-policies.js

# Rimuovi il percorso delle policy personalizzate dalla configurazione
failproofai policies --uninstall --custom
Il percorso assoluto risolto viene memorizzato in policies-config.json come customPoliciesPath. Il file viene caricato fresh su ogni hook event - non c’è caching tra gli eventi.

Utilizzare entrambi insieme

Le policy di convenzione e il file --custom esplicito possono coesistere. Ordine di caricamento:
  1. File customPoliciesPath esplicito (se configurato)
  2. File di convenzione di progetto ({cwd}/.failproofai/policies/, alfabetico)
  3. File di convenzione utente (~/.failproofai/policies/, alfabetico)

API

Import

import { customPolicies, allow, deny, instruct } from "failproofai";

customPolicies.add(hook)

Registra una policy. Chiama questa funzione tutte le volte necessarie per più policy nello stesso file.
customPolicies.add({
  name: string;                         // required - unique identifier
  description?: string;                 // shown in `failproofai policies` output
  match?: { events?: HookEventType[] }; // filter by event type; omit to match all
  fn: (ctx: PolicyContext) => PolicyResult | Promise<PolicyResult>;
});

Helper per decisioni

FunzioneEffettoUsare quando
allow()Permetti l’operazione silenziosamenteL’azione è sicura, nessun messaggio necessario
deny(message)Blocca l’operazioneL’agente non dovrebbe eseguire questa azione
instruct(message)Aggiungi contesto senza bloccareDai all’agente contesto aggiuntivo per rimanere in traccia
deny(message) - il messaggio appare a Claude con prefisso "Blocked by failproofai:". Un singolo deny cortocircuita tutta la valutazione successiva. 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 — nessun cambio di codice necessario. Questo funziona anche per le policy personalizzate (custom/), di convenzione di progetto (.failproofai-project/), e di convenzione utente (.failproofai-user/). Vedi Configuration → hint per i dettagli.

Messaggi allow informativi

allow(message) permette l’operazione e invia un messaggio informativo indietro a Claude. Il messaggio viene consegnato come additionalContext nella risposta stdout del gestore hook — lo stesso meccanismo utilizzato da instruct, ma semanticamente diverso: è un aggiornamento di stato, non un avvertimento.
FunzioneEffettoUsare quando
allow(message)Permetti e invia contesto a ClaudeConferma 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ù policy restituiscono ciascuna allow(message), tutti i messaggi vengono 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

CampoTipoDescrizione
eventTypestring"PreToolUse", "PostToolUse", "Notification", "Stop"
toolNamestring | undefinedLo strumento in fase di chiamata (es. "Bash", "Write", "Read")
toolInputRecord<string, unknown> | undefinedI parametri di input dello strumento
payloadRecord<string, unknown>Payload evento grezzo completo da Claude Code
sessionSessionMetadata | undefinedContesto sessione (vedi sotto)

Campi SessionMetadata

CampoTipoDescrizione
sessionIdstringIdentificatore sessione Claude Code
cwdstringDirectory di lavoro della sessione Claude Code
transcriptPathstringPercorso del file transcript JSONL della sessione

Tipi di evento

EventoQuando si attivaContenuti toolInput
PreToolUsePrima che Claude esegua uno strumentoL’input dello strumento (es. { command: "..." } per Bash)
PostToolUseDopo che uno strumento si completaL’input dello strumento + tool_result (l’output)
NotificationQuando Claude invia una notifica{ message: "...", notification_type: "idle" | "permission_prompt" | ... } - i hook devono sempre restituire allow(), non possono bloccare le notifiche
StopQuando la sessione Claude terminaVuoto

Ordine di valutazione

Le policy vengono valutate in questo ordine:
  1. Policy integrate (in ordine di definizione)
  2. Policy personalizzate esplicite da customPoliciesPath (in ordine .add())
  3. Policy di convenzione da .failproofai/policies/ di progetto (file alfabetici, ordine .add() all’interno)
  4. Policy di convenzione da ~/.failproofai/policies/ utente (file alfabetici, ordine .add() all’interno)
Il primo deny cortocircuita tutte le policy successive. Tutti i messaggi instruct vengono accumulati e consegnati insieme.

Import transitivi

I file di policy personalizzate possono importare moduli locali utilizzando 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");
  },
});
Tutti gli import relativi raggiungibili dal file di ingresso vengono risolti. Questo viene implementato riscrivendo gli import from "failproofai" al percorso dist effettivo e creando file .mjs temporanei per garantire la compatibilità ESM.

Filtraggio per tipo di evento

Usa match.events per limitare quando una policy 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 completamente per attivarsi su ogni tipo di evento.

Gestione degli errori e modalità di fallimento

Le policy personalizzate sono fail-open: gli errori non bloccano mai le policy integrate o fanno crashare il gestore hook.
ErroreComportamento
customPoliciesPath non impostatoNessuna policy personalizzata esplicita viene eseguita; le policy di convenzione e le policy integrate continuano normalmente
File non trovatoAvvertimento registrato in ~/.failproofai/hook.log; le policy integrate continuano
Errore di sintassi/import (esplicito)Errore registrato in ~/.failproofai/hook.log; le policy personalizzate esplicite vengono saltate
Errore di sintassi/import (convenzione)Errore registrato; quel file viene saltato, gli altri file di convenzione continuano comunque a caricarsi
fn lancia un’eccezione a runtimeErrore registrato; questo hook viene trattato come allow; gli altri hook continuano
fn impiega più di 10sTimeout registrato; trattato come allow
Directory di convenzione mancanteNessuna policy di convenzione viene eseguita; nessun errore
Per eseguire il debug degli errori di policy personalizzate, osserva il file di log:
tail -f ~/.failproofai/hook.log

Esempio completo: multiple policy

// my-policies.js
import { customPolicies, allow, deny, instruct } from "failproofai";

// Prevent agent from writing to secrets/ directory
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();
  },
});

// Keep the agent on track: verify tests before committing
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();
  },
});

// Prevent unplanned dependency changes during 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 policy pronti per l’uso:
FileContenuti
examples/policies-basic.jsCinque policy iniziali che coprono i comuni modi di fallimento dell’agente
examples/policies-advanced/index.jsPattern avanzati: import transitivi, chiamate asincrone, scrubbing dell’output, e hook di fine sessione
examples/convention-policies/security-policies.mjsPolicy di convenzione sulla sicurezza (blocca scritture .env, previeni riscrittura della storia git)
examples/convention-policies/workflow-policies.mjsPolicy di convenzione sul workflow (reminder di test, audit delle scritture di file)

Utilizzare esempi di file espliciti

failproofai policies --install --custom ./examples/policies-basic.js

Utilizzare esempi basati su convenzione

# Copia a livello di progetto
mkdir -p .failproofai/policies
cp examples/convention-policies/*.mjs .failproofai/policies/

# O copia a livello di utente
mkdir -p ~/.failproofai/policies
cp examples/convention-policies/*.mjs ~/.failproofai/policies/
Nessun comando di installazione necessario — i file vengono racccolti automaticamente al prossimo hook event.