Vai al contenuto principale

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:
  1. File customPoliciesPath esplicito (se configurato)
  2. File di convenzione di progetto ({cwd}/.failproofai/policies/, alfabetici)
  3. 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

FunzioneEffettoUsa quando
allow()Consenti l’operazione silenziosamenteL’azione è sicura, nessun messaggio necessario
deny(message)Blocca l’operazioneL’agente non dovrebbe compiere questa azione
instruct(message)Aggiungi contesto senza bloccareDai 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.

Messaggi di allow informativi

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.
FunzioneEffettoUsa quando
allow(message)Consenti 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ù 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

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

Campi SessionMetadata

CampoTipoDescrizione
sessionIdstringIdentificatore di sessione Claude Code
cwdstringDirectory di lavoro della sessione Claude Code
transcriptPathstringPercorso al 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

I criteri vengono valutati in questo ordine:
  1. Criteri integrati (in ordine di definizione)
  2. Criteri personalizzati espliciti da customPoliciesPath (in ordine .add())
  3. Criteri di convenzione da .failproofai/policies/ di progetto (file alfabetici, ordine .add() interno)
  4. 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.
ErroreComportamento
customPoliciesPath non impostatoNessun criterio personalizzato esplicito viene eseguito; i criteri di convenzione e integrati continuano normalmente
File non trovatoAvviso 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 runtimeErrore registrato; quel hook trattato come allow; altri hook continuano
fn impiega più di 10sTimeout registrato; trattato come allow
Directory di convenzione mancanteNessun 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:
FileContenuti
examples/policies-basic.jsCinque criteri iniziali che coprono modalità di errore comuni dell’agente
examples/policies-advanced/index.jsSchemi avanzati: importazioni transitive, chiamate asincrone, scrubbing di output, e hook di fine sessione
examples/convention-policies/security-policies.mjsCriteri di sicurezza basati su convenzione (blocca scritture .env, previeni riscrittura della cronologia git)
examples/convention-policies/workflow-policies.mjsCriteri 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.