Pular para o conteúdo principal
As políticas personalizadas permitem que você escreva regras para qualquer comportamento do agente: aplicar convenções do projeto, evitar desvios, bloquear operações destrutivas, detectar agentes travados ou integrar com Slack, fluxos de aprovação e muito mais. Elas utilizam o mesmo sistema de eventos de hook e as decisões allow, deny, instruct das políticas integradas.

Exemplo 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();
  },
});
Instale com:
failproofai policies --install --custom ./my-policies.js

Duas formas de carregar políticas personalizadas

Opção 1: Baseada em convenção (recomendada)

Coloque arquivos *policies.{js,mjs,ts} na pasta .failproofai/policies/ e eles serão carregados automaticamente — sem flags ou alterações de configuração. Funciona como git hooks: basta adicionar o arquivo e pronto.
# Nível do projeto — commitado no git, compartilhado com a equipe
.failproofai/policies/security-policies.mjs
.failproofai/policies/workflow-policies.mjs

# Nível do usuário — pessoal, aplicado a todos os projetos
~/.failproofai/policies/my-policies.mjs
Como funciona:
  • Os diretórios do projeto e do usuário são verificados (união — não por primeiro escopo encontrado)
  • Os arquivos são carregados em ordem alfabética dentro de cada diretório. Use prefixos como 01-, 02- para controlar a ordem
  • Apenas arquivos que correspondam a *policies.{js,mjs,ts} são carregados; os demais são ignorados
  • Cada arquivo é carregado de forma independente (fail-open por arquivo)
  • Funciona junto com --custom explícito e políticas integradas
As políticas por convenção são a forma mais fácil de estabelecer um padrão de qualidade para sua organização. Faça commit de .failproofai/policies/ no git e todos os membros da equipe receberão as mesmas regras automaticamente — sem configuração individual. À medida que sua equipe descobre novos modos de falha, adicione uma política e faça push. Com o tempo, isso se torna um padrão de qualidade vivo que melhora a cada contribuição.

Opção 2: Caminho de arquivo explícito

# Instala com um arquivo de políticas personalizado
failproofai policies --install --custom ./my-policies.js

# Substitui o caminho do arquivo de políticas
failproofai policies --install --custom ./new-policies.js

# Remove o caminho de políticas personalizadas da configuração
failproofai policies --uninstall --custom
O caminho absoluto resolvido é armazenado em policies-config.json como customPoliciesPath. O arquivo é carregado novamente a cada evento de hook — não há cache entre eventos.

Usando ambas juntas

As políticas por convenção e o arquivo --custom explícito podem coexistir. Ordem de carregamento:
  1. Arquivo customPoliciesPath explícito (se configurado)
  2. Arquivos de convenção do projeto ({cwd}/.failproofai/policies/, em ordem alfabética)
  3. Arquivos de convenção do usuário (~/.failproofai/policies/, em ordem alfabética)

API

Importação

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

customPolicies.add(hook)

Registra uma política. Chame quantas vezes forem necessárias para múltiplas políticas no mesmo arquivo.
customPolicies.add({
  name: string;                         // obrigatório - identificador único
  description?: string;                 // exibido na saída de `failproofai policies`
  match?: { events?: HookEventType[] }; // filtra por tipo de evento; omita para corresponder a todos
  fn: (ctx: PolicyContext) => PolicyResult | Promise<PolicyResult>;
});

Helpers de decisão

FunçãoEfeitoUse quando
allow()Permite a operação silenciosamenteA ação é segura, sem necessidade de mensagem
deny(message)Bloqueia a operaçãoO agente não deve executar esta ação
instruct(message)Adiciona contexto sem bloquearFornece contexto extra ao agente para mantê-lo no caminho certo
deny(message) - a mensagem aparece para Claude com o prefixo "Blocked by failproofai:". Um único deny interrompe toda avaliação subsequente. instruct(message) - a mensagem é anexada ao contexto de Claude para a chamada de ferramenta atual. Todas as mensagens instruct são acumuladas e entregues juntas.
Você pode adicionar orientações extras a qualquer mensagem deny ou instruct incluindo um campo hint em policyParams — sem necessidade de alteração no código. Isso também funciona para políticas personalizadas (custom/), de convenção do projeto (.failproofai-project/) e de convenção do usuário (.failproofai-user/). Consulte Configuração → hint para mais detalhes.

Mensagens informativas de allow

allow(message) permite a operação e envia uma mensagem informativa de volta para Claude. A mensagem é entregue como additionalContext na resposta stdout do handler do hook — o mesmo mecanismo usado por instruct, mas semanticamente diferente: é uma atualização de status, não um aviso.
FunçãoEfeitoUse quando
allow(message)Permite e envia contexto para ClaudeConfirma que uma verificação passou ou explica por que foi ignorada
Casos de uso:
  • Confirmações de status: allow("All CI checks passed.") — informa Claude que tudo está em ordem
  • Explicações de fail-open: allow("GitHub CLI not installed, skipping CI check.") — informa Claude por que uma verificação foi pulada, dando contexto completo
  • Múltiplas mensagens se acumulam: se várias políticas retornarem allow(message), todas as mensagens são unidas com quebras de linha e entregues juntas
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.");

    // ... verifica o status do branch ...
    if (allPushed) {
      return allow("Branch is up to date with remote.");
    }
    return deny("Unpushed changes detected.");
  },
});

Campos do PolicyContext

CampoTipoDescrição
eventTypestring"PreToolUse", "PostToolUse", "Notification", "Stop"
toolNamestring | undefinedA ferramenta sendo chamada (ex.: "Bash", "Write", "Read")
toolInputRecord<string, unknown> | undefinedOs parâmetros de entrada da ferramenta
payloadRecord<string, unknown>Payload completo do evento bruto do Claude Code
sessionSessionMetadata | undefinedContexto da sessão (veja abaixo)

Campos do SessionMetadata

CampoTipoDescrição
sessionIdstringIdentificador da sessão do Claude Code
cwdstringDiretório de trabalho da sessão do Claude Code
transcriptPathstringCaminho para o arquivo de transcrição JSONL da sessão

Tipos de evento

EventoQuando disparaConteúdo de toolInput
PreToolUseAntes de Claude executar uma ferramentaA entrada da ferramenta (ex.: { command: "..." } para Bash)
PostToolUseApós a conclusão de uma ferramentaA entrada da ferramenta + tool_result (a saída)
NotificationQuando Claude envia uma notificação{ message: "...", notification_type: "idle" | "permission_prompt" | ... } - hooks devem sempre retornar allow(), não podem bloquear notificações
StopQuando a sessão do Claude encerraVazio

Ordem de avaliação

As políticas são avaliadas nesta ordem:
  1. Políticas integradas (na ordem de definição)
  2. Políticas personalizadas explícitas de customPoliciesPath (na ordem de .add())
  3. Políticas de convenção do projeto em .failproofai/policies/ (arquivos em ordem alfabética, ordem de .add() dentro de cada arquivo)
  4. Políticas de convenção do usuário em ~/.failproofai/policies/ (arquivos em ordem alfabética, ordem de .add() dentro de cada arquivo)
O primeiro deny interrompe todas as políticas subsequentes. Todas as mensagens instruct são acumuladas e entregues juntas.

Importações transitivas

Arquivos de políticas personalizadas podem importar módulos locais usando caminhos relativos:
// 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");
  },
});
Todas as importações relativas acessíveis a partir do arquivo de entrada são resolvidas. Isso é implementado reescrevendo as importações de from "failproofai" para o caminho real do dist e criando arquivos .mjs temporários para garantir compatibilidade com ESM.

Filtragem por tipo de evento

Use match.events para limitar quando uma política é acionada:
customPolicies.add({
  name: "require-summary-on-stop",
  match: { events: ["Stop"] },
  fn: async (ctx) => {
    // Só dispara quando a sessão encerra
    // ctx.session.transcriptPath contém o log completo da sessão
    return allow();
  },
});
Omita match completamente para disparar em todos os tipos de evento.

Tratamento de erros e modos de falha

As políticas personalizadas são fail-open: erros nunca bloqueiam as políticas integradas nem causam falha no handler do hook.
FalhaComportamento
customPoliciesPath não definidoNenhuma política personalizada explícita é executada; políticas de convenção e integradas continuam normalmente
Arquivo não encontradoAviso registrado em ~/.failproofai/hook.log; políticas integradas continuam
Erro de sintaxe/importação (explícito)Erro registrado em ~/.failproofai/hook.log; políticas personalizadas explícitas são ignoradas
Erro de sintaxe/importação (convenção)Erro registrado; aquele arquivo é ignorado, outros arquivos de convenção ainda são carregados
fn lança erro em tempo de execuçãoErro registrado; aquele hook tratado como allow; outros hooks continuam
fn demora mais de 10sTimeout registrado; tratado como allow
Diretório de convenção ausenteNenhuma política de convenção é executada; sem erro
Para depurar erros de políticas personalizadas, monitore o arquivo de log:
tail -f ~/.failproofai/hook.log

Exemplo completo: múltiplas políticas

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

// Impede o agente de escrever no diretório 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();
  },
});

// Mantém o agente no caminho certo: verificar testes antes de commitar
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();
  },
});

// Evita alterações não planejadas de dependências durante o período de congelamento
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 };

Exemplos

O diretório examples/ contém arquivos de políticas prontos para uso:
ArquivoConteúdo
examples/policies-basic.jsCinco políticas iniciais cobrindo modos comuns de falha do agente
examples/policies-advanced/index.jsPadrões avançados: importações transitivas, chamadas assíncronas, limpeza de saída e hooks de fim de sessão
examples/convention-policies/security-policies.mjsPolíticas de segurança baseadas em convenção (bloqueio de escrita em .env, prevenção de reescrita do histórico git)
examples/convention-policies/workflow-policies.mjsPolíticas de fluxo de trabalho baseadas em convenção (lembretes de teste, auditoria de escrita em arquivos)

Usando exemplos com arquivo explícito

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

Usando exemplos baseados em convenção

# Copia para o nível do projeto
mkdir -p .failproofai/policies
cp examples/convention-policies/*.mjs .failproofai/policies/

# Ou copia para o nível do usuário
mkdir -p ~/.failproofai/policies
cp examples/convention-policies/*.mjs ~/.failproofai/policies/
Nenhum comando de instalação é necessário — os arquivos são detectados automaticamente no próximo evento de hook.