> ## Documentation Index
> Fetch the complete documentation index at: https://exosphere-auto-translate-docs-20260626-1023.mintlify.site/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, previene desviaciones, detecta fallos e integra con sistemas externos

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

```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álala:

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

<Tip>
  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.
</Tip>

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

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

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

```ts theme={null}
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ón             | Efecto                               | Úsala cuando                                               |
| ------------------- | ------------------------------------ | ---------------------------------------------------------- |
| `allow()`           | Permite la operación silenciosamente | La acción es segura, no se necesita mensaje                |
| `deny(message)`     | Bloquea la operación                 | El agente no debería realizar esta acción                  |
| `instruct(message)` | Agrega contexto sin bloquear         | Dale 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.

<Tip>
  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](/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 significado diferente: es una actualización de estado, no una advertencia.

| Función          | Efecto                            | Úsala cuando                                                    |
| ---------------- | --------------------------------- | --------------------------------------------------------------- |
| `allow(message)` | Permite y envía contexto a Claude | Confirma 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

```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>`              | Carga útil completa del evento raw de Claude Code                          |
| `session`   | `SessionMetadata \| undefined`         | Contexto de sesión (ver más abajo)                                         |

### 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 termina      | 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 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)

<Note>
  El primer `deny` interrumpe todas las políticas posteriores. 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 dispara una política:

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

| 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                      | Se 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ón | El error se registra; ese hook se trata como `allow`; los demás hooks continúan                                        |
| `fn` tarda más de 10 segundos              | Se registra el timeout; se trata como `allow`                                                                          |
| Directorio de convención inexistente       | No se ejecutan políticas de convención; sin error                                                                      |

<Tip>
  Para depurar errores de políticas personalizadas, observa 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";

// 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:

| Archivo                                              | Contenido                                                                                                             |
| ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `examples/policies-basic.js`                         | Cinco políticas iniciales que cubren los modos de fallo más comunes de los agentes                                    |
| `examples/policies-advanced/index.js`                | Patrones avanzados: importaciones transitivas, llamadas asíncronas, limpieza de salida y hooks al final de sesión     |
| `examples/convention-policies/security-policies.mjs` | Políticas de seguridad basadas en convención (bloquear escrituras en .env, prevenir reescritura del historial de git) |
| `examples/convention-policies/workflow-policies.mjs` | Polí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

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

### Usar los ejemplos basados en convención

```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.
