Saltar al contenido principal
Las políticas personalizadas te permiten escribir reglas para cualquier comportamiento del agente: aplicar convenciones del proyecto, prevenir desviaciones, controlar operaciones destructivas, detectar agentes bloqueados o integrarte con Slack, flujos de aprobación y mucho más. Utilizan el mismo sistema de eventos de hooks y las mismas decisiones allow, deny, instruct que las políticas integradas.

Ejemplo rápido

// 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();
  },
});
Instálala:
failproofai policies --install --custom ./my-policies.js

Dos formas de cargar políticas personalizadas

Opción 1: Basada en convención (recomendada)

Coloca archivos *policies.{js,mjs,ts} en .failproofai/policies/ y se cargarán automáticamente, sin necesidad de flags ni cambios de configuración. Funciona como los git hooks: suelta un archivo y listo.
# Nivel de proyecto — se incluye en git, compartido con el equipo
.failproofai/policies/security-policies.mjs
.failproofai/policies/workflow-policies.mjs

# Nivel de usuario — personal, aplica a todos los proyectos
~/.failproofai/policies/my-policies.mjs
Cómo funciona:
  • Se escanean tanto el directorio del proyecto como el del usuario (unión — no gana el primero en alcance)
  • Los archivos se cargan en orden alfabético dentro de cada directorio. Usa prefijos 01-, 02- para controlar el orden
  • Solo se cargan los archivos que coincidan con *policies.{js,mjs,ts}; los demás se ignoran
  • Cada archivo se carga de forma independiente (fail-open por archivo)
  • Funciona junto con --custom explícito y las políticas integradas
Las políticas de convención son la forma más sencilla de establecer un estándar de calidad para tu organización. Incluye .failproofai/policies/ en git y cada miembro del equipo obtendrá las mismas reglas automáticamente, sin configuración individual. A medida que el equipo descubra nuevos modos de fallo, agrega una política y haz push. Con el tiempo, estas se convierten en un estándar de calidad vivo que mejora con cada contribución.

Opción 2: Ruta de archivo explícita

# Instalar con un archivo de políticas personalizadas
failproofai policies --install --custom ./my-policies.js

# Reemplazar la ruta del archivo de políticas
failproofai policies --install --custom ./new-policies.js

# Eliminar la ruta de políticas personalizadas de la configuración
failproofai policies --uninstall --custom
La ruta absoluta resuelta se almacena en policies-config.json como customPoliciesPath. El archivo se carga de nuevo en cada evento de hook; no hay caché entre eventos.

Usar ambas opciones juntas

Las políticas de convención y el archivo --custom explícito pueden coexistir. Orden de carga:
  1. Archivo customPoliciesPath explícito (si está configurado)
  2. Archivos de convención del proyecto ({cwd}/.failproofai/policies/, orden alfabético)
  3. Archivos de convención del usuario (~/.failproofai/policies/, orden alfabético)

API

Importación

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

customPolicies.add(hook)

Registra una política. Llama a este método tantas veces como necesites para múltiples políticas en el mismo archivo.
customPolicies.add({
  name: string;                         // requerido - identificador único
  description?: string;                 // se muestra en la salida de `failproofai policies`
  match?: { events?: HookEventType[] }; // filtra por tipo de evento; omitir para coincidir con todos
  fn: (ctx: PolicyContext) => PolicyResult | Promise<PolicyResult>;
});

Funciones de decisión

FunciónEfectoÚsala cuando
allow()Permite la operación silenciosamenteLa acción es segura, no se necesita mensaje
deny(message)Bloquea la operaciónEl agente no debería realizar esta acción
instruct(message)Agrega contexto sin bloquearDale al agente contexto adicional para mantenerse en curso
deny(message) — el mensaje aparece ante Claude con el prefijo "Blocked by failproofai:". Un único deny interrumpe toda evaluación posterior. instruct(message) — el mensaje se agrega al contexto de Claude para la llamada de herramienta actual. Todos los mensajes instruct se acumulan y se entregan juntos.
Puedes añadir orientación adicional a cualquier mensaje deny o instruct agregando un campo hint en policyParams, sin necesidad de modificar el código. Esto funciona también para políticas personalizadas (custom/), de convención de proyecto (.failproofai-project/) y de convención de usuario (.failproofai-user/). Consulta Configuración → hint para más detalles.

Mensajes allow informativos

allow(message) permite la operación y envía un mensaje informativo a Claude. El mensaje se entrega como additionalContext en la respuesta stdout del handler del hook, el mismo mecanismo que usa instruct, pero con significado diferente: es una actualización de estado, no una advertencia.
FunciónEfectoÚsala cuando
allow(message)Permite y envía contexto a ClaudeConfirma que una verificación pasó, o explica por qué se omitió
Casos de uso:
  • Confirmaciones de estado: allow("All CI checks passed.") — le indica a Claude que todo está bien
  • Explicaciones de fail-open: allow("GitHub CLI not installed, skipping CI check.") — le indica a Claude por qué se omitió una verificación para que tenga contexto completo
  • Los mensajes múltiples se acumulan: si varias políticas devuelven allow(message), todos los mensajes se unen con saltos de línea y se entregan juntos
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.");
  },
});

Campos de PolicyContext

CampoTipoDescripción
eventTypestring"PreToolUse", "PostToolUse", "Notification", "Stop"
toolNamestring | undefinedLa herramienta que se está llamando (p. ej. "Bash", "Write", "Read")
toolInputRecord<string, unknown> | undefinedLos parámetros de entrada de la herramienta
payloadRecord<string, unknown>Carga útil completa del evento raw de Claude Code
sessionSessionMetadata | undefinedContexto de sesión (ver más abajo)

Campos de SessionMetadata

CampoTipoDescripción
sessionIdstringIdentificador de sesión de Claude Code
cwdstringDirectorio de trabajo de la sesión de Claude Code
transcriptPathstringRuta al archivo de transcripción JSONL de la sesión

Tipos de eventos

EventoCuándo se disparaContenido de toolInput
PreToolUseAntes de que Claude ejecute una herramientaLa entrada de la herramienta (p. ej. { command: "..." } para Bash)
PostToolUseDespués de que una herramienta terminaLa entrada de la herramienta + tool_result (la salida)
NotificationCuando Claude envía una notificación{ message: "...", notification_type: "idle" | "permission_prompt" | ... } — los hooks siempre deben devolver allow(), no pueden bloquear notificaciones
StopCuando la sesión de Claude terminaVacío

Orden de evaluación

Las políticas se evalúan en este orden:
  1. Políticas integradas (en orden de definición)
  2. Políticas personalizadas explícitas de customPoliciesPath (en orden de .add())
  3. Políticas de convención del proyecto en .failproofai/policies/ (archivos en orden alfabético, orden de .add() dentro de cada uno)
  4. Políticas de convención del usuario en ~/.failproofai/policies/ (archivos en orden alfabético, orden de .add() dentro de cada uno)
El primer deny interrumpe todas las políticas posteriores. Todos los mensajes instruct se acumulan y se entregan juntos.

Importaciones transitivas

Los archivos de políticas personalizadas pueden importar módulos locales usando rutas relativas:
// 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");
  },
});
Se resuelven todas las importaciones relativas alcanzables desde el archivo de entrada. Esto se implementa reescribiendo las importaciones from "failproofai" a la ruta real de dist y creando archivos .mjs temporales para garantizar la compatibilidad con ESM.

Filtrado por tipo de evento

Usa match.events para limitar cuándo se dispara una política:
customPolicies.add({
  name: "require-summary-on-stop",
  match: { events: ["Stop"] },
  fn: async (ctx) => {
    // Solo se dispara cuando termina la sesión
    // ctx.session.transcriptPath contiene el registro completo de la sesión
    return allow();
  },
});
Omite match por completo para disparar en cada tipo de evento.

Manejo de errores y modos de fallo

Las políticas personalizadas son fail-open: los errores nunca bloquean las políticas integradas ni hacen fallar el handler del hook.
FalloComportamiento
customPoliciesPath no configuradoNo se ejecutan políticas personalizadas explícitas; las políticas de convención y las integradas continúan normalmente
Archivo no encontradoSe registra una advertencia en ~/.failproofai/hook.log; las políticas integradas continúan
Error de sintaxis/importación (explícito)El error se registra en ~/.failproofai/hook.log; se omiten las políticas personalizadas explícitas
Error de sintaxis/importación (convención)El error se registra; ese archivo se omite, los demás archivos de convención se siguen cargando
fn lanza un error en tiempo de ejecuciónEl error se registra; ese hook se trata como allow; los demás hooks continúan
fn tarda más de 10 segundosSe registra el timeout; se trata como allow
Directorio de convención inexistenteNo se ejecutan políticas de convención; sin error
Para depurar errores de políticas personalizadas, observa el archivo de log:
tail -f ~/.failproofai/hook.log

Ejemplo completo: múltiples políticas

// 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 };

Ejemplos

El directorio examples/ contiene archivos de políticas listos para ejecutar:
ArchivoContenido
examples/policies-basic.jsCinco políticas iniciales que cubren los modos de fallo más comunes de los agentes
examples/policies-advanced/index.jsPatrones avanzados: importaciones transitivas, llamadas asíncronas, limpieza de salida y hooks al final de sesión
examples/convention-policies/security-policies.mjsPolíticas de seguridad basadas en convención (bloquear escrituras en .env, prevenir reescritura del historial de git)
examples/convention-policies/workflow-policies.mjsPolíticas de flujo de trabajo basadas en convención (recordatorios de tests, auditoría de escrituras de archivos)

Usar los ejemplos con archivo explícito

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

Usar los ejemplos basados en convención

# Copiar al nivel de proyecto
mkdir -p .failproofai/policies
cp examples/convention-policies/*.mjs .failproofai/policies/

# O copiar al nivel de usuario
mkdir -p ~/.failproofai/policies
cp examples/convention-policies/*.mjs ~/.failproofai/policies/
No se necesita ningún comando de instalación: los archivos se detectan automáticamente en el siguiente evento de hook.