> ## Documentation Index
> Fetch the complete documentation index at: https://docs.befailproof.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Políticas personalizadas

> Escribe tus propias políticas en JavaScript: aplica convenciones del proyecto, prevén desviaciones, detecta fallos e intégrate con sistemas externos

Las políticas personalizadas te permiten definir reglas para cualquier comportamiento del agente: aplicar convenciones del proyecto, prevenir desviaciones, bloquear operaciones destructivas, detectar agentes atascados o integrarte con Slack, flujos de aprobación y más. Utilizan el mismo sistema de eventos de hook y las mismas decisiones `allow`, `deny`, `instruct` que las políticas integradas.

***

## Ejemplo rápido

```js theme={null}
// 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álalo:

```bash theme={null}
failproofai policies --install --custom ./my-policies.js
```

***

## Dos formas de cargar políticas personalizadas

### Opción 1: Basada en convenciones (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: basta con añadir el archivo y ya está.

```
# Nivel de proyecto — incluido 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 analizan 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 como `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 (fallo abierto por archivo)
* Funciona junto con `--custom` explícito y las políticas integradas

<Tip>
  Las políticas por 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 todos los miembros del equipo recibirán las mismas reglas automáticamente, sin configuración individual. A medida que el equipo detecte nuevos modos de fallo, añade una política y haz push. Con el tiempo, esto se convierte en un estándar de calidad vivo que mejora con cada contribución.
</Tip>

### Opción 2: Ruta de archivo explícita

```bash theme={null}
# Instalar con un archivo de políticas personalizado
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 personalizada 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 por 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/`, alfabético)
3. Archivos de convención del usuario (`~/.failproofai/policies/`, alfabético)

***

## API

### Importación

```js theme={null}
import { customPolicies, allow, deny, instruct } from "failproofai";
```

### `customPolicies.add(hook)`

Registra una política. Llámalo tantas veces como necesites para definir múltiples políticas en el mismo archivo.

```ts theme={null}
customPolicies.add({
  name: string;                         // obligatorio - identificador único
  description?: string;                 // se muestra en la salida de `failproofai policies`
  match?: { events?: HookEventType[] }; // filtrar por tipo de evento; omitir para coincidir con todos
  fn: (ctx: PolicyContext) => PolicyResult | Promise<PolicyResult>;
});
```

### Funciones de decisión

| Función             | Efecto                                    | Cuándo usarla                                                             |
| ------------------- | ----------------------------------------- | ------------------------------------------------------------------------- |
| `allow()`           | Permite la operación sin mostrar mensajes | La acción es segura y no necesita notificación                            |
| `deny(message)`     | Bloquea la operación                      | El agente no debe realizar esta acción                                    |
| `instruct(message)` | Añade contexto sin bloquear               | Proporciona contexto adicional al agente para que siga el camino correcto |

`deny(message)` — el mensaje aparece ante Claude con el prefijo `"Blocked by failproofai:"`. Un solo `deny` interrumpe toda evaluación posterior.

`instruct(message)` — el mensaje se añade al contexto de Claude para la llamada a la herramienta actual. Todos los mensajes `instruct` se acumulan y se entregan juntos.

<Tip>
  Puedes añadir orientación adicional a cualquier mensaje `deny` o `instruct` mediante el 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](/es/configuration#hint-cross-cutting) para más detalles.
</Tip>

### 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 un significado diferente: es una actualización de estado, no una advertencia.

| Función          | Efecto                            | Cuándo usarla                                                    |
| ---------------- | --------------------------------- | ---------------------------------------------------------------- |
| `allow(message)` | Permite y envía contexto a Claude | Confirmar que una verificación pasó o explicar por qué se omitió |

Casos de uso:

* **Confirmaciones de estado:** `allow("All CI checks passed.")` — informa a Claude de que todo está en orden
* **Explicaciones de fallo abierto:** `allow("GitHub CLI not installed, skipping CI check.")` — indica a Claude por qué se omitió una verificación para que tenga el 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

```js theme={null}
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`

| Campo       | Tipo                                   | Descripción                                                                |
| ----------- | -------------------------------------- | -------------------------------------------------------------------------- |
| `eventType` | `string`                               | `"PreToolUse"`, `"PostToolUse"`, `"Notification"`, `"Stop"`                |
| `toolName`  | `string \| undefined`                  | La herramienta que se está llamando (p. ej. `"Bash"`, `"Write"`, `"Read"`) |
| `toolInput` | `Record<string, unknown> \| undefined` | Los parámetros de entrada de la herramienta                                |
| `payload`   | `Record<string, unknown>`              | Payload completo del evento raw de Claude Code                             |
| `session`   | `SessionMetadata \| undefined`         | Contexto de sesión (ver a continuación)                                    |

### Campos de `SessionMetadata`

| Campo            | Tipo     | Descripción                                         |
| ---------------- | -------- | --------------------------------------------------- |
| `sessionId`      | `string` | Identificador de sesión de Claude Code              |
| `cwd`            | `string` | Directorio de trabajo de la sesión de Claude Code   |
| `transcriptPath` | `string` | Ruta al archivo de transcripción JSONL de la sesión |

### Tipos de eventos

| Evento         | Cuándo se dispara                           | Contenido de `toolInput`                                                                                                                                      |
| -------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PreToolUse`   | Antes de que Claude ejecute una herramienta | La entrada de la herramienta (p. ej. `{ command: "..." }` para Bash)                                                                                          |
| `PostToolUse`  | Después de que una herramienta finaliza     | La entrada de la herramienta + `tool_result` (la salida)                                                                                                      |
| `Notification` | Cuando Claude envía una notificación        | `{ message: "...", notification_type: "idle" \| "permission_prompt" \| ... }` — los hooks siempre deben devolver `allow()`, no pueden bloquear notificaciones |
| `Stop`         | Cuando la sesión de Claude termina          | Vací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 `.failproofai/policies/` (archivos en orden alfabético, orden de `.add()` dentro de cada archivo)
4. Políticas de convención del usuario `~/.failproofai/policies/` (archivos en orden alfabético, orden de `.add()` dentro de cada archivo)

<Note>
  El primer `deny` interrumpe todas las políticas siguientes. Todos los mensajes `instruct` se acumulan y se entregan juntos.
</Note>

***

## Importaciones transitivas

Los archivos de políticas personalizadas pueden importar módulos locales usando rutas relativas:

```js theme={null}
// 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 activa una política:

```js theme={null}
customPolicies.add({
  name: "require-summary-on-stop",
  match: { events: ["Stop"] },
  fn: async (ctx) => {
    // Solo se activa cuando la sesión finaliza
    // ctx.session.transcriptPath contiene el registro completo de la sesión
    return allow();
  },
});
```

Omite `match` completamente para que se active en todos los tipos de eventos.

***

## Manejo de errores y modos de fallo

Las políticas personalizadas son de **fallo abierto**: los errores nunca bloquean las políticas integradas ni hacen que el handler del hook falle.

| Fallo                                      | Comportamiento                                                                                                         |
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------- |
| `customPoliciesPath` no configurado        | No se ejecutan políticas personalizadas explícitas; las políticas de convención y las integradas continúan normalmente |
| Archivo no encontrado                      | Advertencia registrada en `~/.failproofai/hook.log`; las integradas continúan                                          |
| Error de sintaxis/importación (explícito)  | Error registrado en `~/.failproofai/hook.log`; las políticas personalizadas explícitas se omiten                       |
| Error de sintaxis/importación (convención) | Error registrado; ese archivo se omite, los demás archivos de convención siguen cargándose                             |
| `fn` lanza un error en tiempo de ejecución | Error registrado; ese hook se trata como `allow`; los demás hooks continúan                                            |
| `fn` tarda más de 10s                      | Timeout registrado; se trata como `allow`                                                                              |
| Directorio de convención no existente      | No se ejecutan políticas de convención; sin error                                                                      |

<Tip>
  Para depurar errores en políticas personalizadas, monitorea el archivo de log:

  ```bash theme={null}
  tail -f ~/.failproofai/hook.log
  ```
</Tip>

***

## Ejemplo completo: múltiples políticas

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

// Evitar que el agente escriba en el directorio 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();
  },
});

// Mantener al agente en el camino correcto: verificar los tests antes de hacer commit
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();
  },
});

// Prevenir cambios de dependencias no planificados durante el período de congelación
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 usar:

| Archivo                                              | Contenido                                                                                                               |
| ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `examples/policies-basic.js`                         | Cinco políticas iniciales que cubren los modos de fallo más comunes del agente                                          |
| `examples/policies-advanced/index.js`                | Patrones avanzados: importaciones transitivas, llamadas asíncronas, limpieza de salida y hooks de fin de sesión         |
| `examples/convention-policies/security-policies.mjs` | Políticas de seguridad basadas en convenciones (bloquear escrituras en .env, prevenir reescritura del historial de git) |
| `examples/convention-policies/workflow-policies.mjs` | Políticas de flujo de trabajo basadas en convenciones (recordatorios de tests, auditoría de escrituras de archivos)     |

### Usar los ejemplos con archivo explícito

```bash theme={null}
failproofai policies --install --custom ./examples/policies-basic.js
```

### Usar los ejemplos basados en convenciones

```bash theme={null}
# 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.
