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

# Benutzerdefinierte Richtlinien

> Schreibe eigene Richtlinien in JavaScript – Konventionen durchsetzen, Drift verhindern, Fehler erkennen, externe Systeme integrieren

Benutzerdefinierte Richtlinien ermöglichen es dir, Regeln für beliebiges Agentenverhalten zu schreiben: Projektkonventionen durchsetzen, Drift verhindern, destruktive Operationen kontrollieren, feststeckende Agenten erkennen oder Integrationen mit Slack, Genehmigungsworkflows und mehr umsetzen. Sie verwenden dasselbe Hook-Ereignissystem und die gleichen `allow`-, `deny`- und `instruct`-Entscheidungen wie eingebaute Richtlinien.

***

## Schnellbeispiel

```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();
  },
});
```

Installieren:

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

***

## Zwei Wege zum Laden benutzerdefinierter Richtlinien

### Option 1: Konventionsbasiert (empfohlen)

Lege `*policies.{js,mjs,ts}`-Dateien in `.failproofai/policies/` ab und sie werden automatisch geladen – keine Flags oder Konfigurationsänderungen erforderlich. Das funktioniert wie Git-Hooks: Datei ablegen, fertig.

```
# Projektebene — ins Git eingecheckt, wird mit dem Team geteilt
.failproofai/policies/security-policies.mjs
.failproofai/policies/workflow-policies.mjs

# Benutzerebene — persönlich, gilt für alle Projekte
~/.failproofai/policies/my-policies.mjs
```

**So funktioniert es:**

* Sowohl Projekt- als auch Benutzerverzeichnisse werden durchsucht (Vereinigung – kein Gewinnen durch den ersten Scope)
* Dateien werden innerhalb jedes Verzeichnisses alphabetisch geladen. Mit `01-`, `02-` etc. lässt sich die Reihenfolge steuern
* Nur Dateien, die `*policies.{js,mjs,ts}` entsprechen, werden geladen; andere Dateien werden ignoriert
* Jede Datei wird unabhängig geladen (fail-open pro Datei)
* Funktioniert neben explizitem `--custom` und eingebauten Richtlinien

<Tip>
  Konventionsbasierte Richtlinien sind der einfachste Weg, einen Qualitätsstandard für deine Organisation aufzubauen. Checke `.failproofai/policies/` ins Git ein und jedes Teammitglied erhält automatisch dieselben Regeln – kein individuelles Setup nötig. Wenn dein Team neue Fehlermuster entdeckt, füge einfach eine Richtlinie hinzu und pushe. Mit der Zeit werden diese zu einem lebendigen Qualitätsstandard, der sich mit jedem Beitrag weiterentwickelt.
</Tip>

### Option 2: Expliziter Dateipfad

```bash theme={null}
# Mit einer benutzerdefinierten Richtliniendatei installieren
failproofai policies --install --custom ./my-policies.js

# Den Richtliniendateipfad ersetzen
failproofai policies --install --custom ./new-policies.js

# Den benutzerdefinierten Richtlinienpfad aus der Konfiguration entfernen
failproofai policies --uninstall --custom
```

Der aufgelöste absolute Pfad wird in `policies-config.json` als `customPoliciesPath` gespeichert. Die Datei wird bei jedem Hook-Ereignis frisch geladen – es gibt kein Caching zwischen Ereignissen.

### Beide Varianten zusammen verwenden

Konventionsbasierte Richtlinien und die explizite `--custom`-Datei können nebeneinander existieren. Ladereihenfolge:

1. Explizite `customPoliciesPath`-Datei (sofern konfiguriert)
2. Projektkonventionsdateien (`{cwd}/.failproofai/policies/`, alphabetisch)
3. Benutzerkonventionsdateien (`~/.failproofai/policies/`, alphabetisch)

***

## API

### Import

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

### `customPolicies.add(hook)`

Registriert eine Richtlinie. Kann beliebig oft aufgerufen werden, um mehrere Richtlinien in derselben Datei zu definieren.

```ts theme={null}
customPolicies.add({
  name: string;                         // erforderlich - eindeutiger Bezeichner
  description?: string;                 // wird in der `failproofai policies`-Ausgabe angezeigt
  match?: { events?: HookEventType[] }; // nach Ereignistyp filtern; weglassen für alle Ereignisse
  fn: (ctx: PolicyContext) => PolicyResult | Promise<PolicyResult>;
});
```

### Entscheidungs-Hilfsfunktionen

| Funktion            | Wirkung                               | Wann verwenden                                                 |
| ------------------- | ------------------------------------- | -------------------------------------------------------------- |
| `allow()`           | Operation lautlos zulassen            | Die Aktion ist sicher, keine Meldung nötig                     |
| `deny(message)`     | Operation blockieren                  | Der Agent sollte diese Aktion nicht ausführen                  |
| `instruct(message)` | Kontext hinzufügen ohne zu blockieren | Dem Agenten zusätzlichen Kontext geben, um auf Kurs zu bleiben |

`deny(message)` – die Nachricht erscheint bei Claude mit dem Präfix `"Blocked by failproofai:"`. Ein einzelnes `deny` schließt alle weiteren Auswertungen kurz.

`instruct(message)` – die Nachricht wird dem Claude-Kontext für den aktuellen Tool-Aufruf angehängt. Alle `instruct`-Nachrichten werden gesammelt und gemeinsam übermittelt.

<Tip>
  Du kannst jeder `deny`- oder `instruct`-Nachricht zusätzliche Hinweise hinzufügen, indem du ein `hint`-Feld in `policyParams` setzt – ohne Codeänderung. Das funktioniert auch für benutzerdefinierte (`custom/`), Projektkonventions- (`.failproofai-project/`) und Benutzerkonventions- (`.failproofai-user/`) Richtlinien. Siehe [Konfiguration → hint](/de/configuration#hint-cross-cutting) für Details.
</Tip>

### Informelle allow-Nachrichten

`allow(message)` erlaubt die Operation **und** sendet eine informelle Nachricht an Claude zurück. Die Nachricht wird als `additionalContext` in der stdout-Antwort des Hook-Handlers übermittelt – derselbe Mechanismus wie bei `instruct`, jedoch semantisch verschieden: Es handelt sich um ein Statusupdate, nicht um eine Warnung.

| Funktion         | Wirkung                               | Wann verwenden                                                                          |
| ---------------- | ------------------------------------- | --------------------------------------------------------------------------------------- |
| `allow(message)` | Erlauben und Kontext an Claude senden | Eine bestandene Prüfung bestätigen oder erklären, warum eine Prüfung übersprungen wurde |

Anwendungsfälle:

* **Statusbestätigungen:** `allow("All CI checks passed.")` – teilt Claude mit, dass alles in Ordnung ist
* **Fail-open-Erklärungen:** `allow("GitHub CLI not installed, skipping CI check.")` – teilt Claude mit, warum eine Prüfung übersprungen wurde, damit der Agent vollständigen Kontext hat
* **Mehrere Nachrichten häufen sich an:** Wenn mehrere Richtlinien jeweils `allow(message)` zurückgeben, werden alle Nachrichten mit Zeilenumbrüchen verbunden und gemeinsam übermittelt

```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.");

    // ... Branch-Status prüfen ...
    if (allPushed) {
      return allow("Branch is up to date with remote.");
    }
    return deny("Unpushed changes detected.");
  },
});
```

### `PolicyContext`-Felder

| Feld        | Typ                                    | Beschreibung                                                |
| ----------- | -------------------------------------- | ----------------------------------------------------------- |
| `eventType` | `string`                               | `"PreToolUse"`, `"PostToolUse"`, `"Notification"`, `"Stop"` |
| `toolName`  | `string \| undefined`                  | Das aufgerufene Tool (z. B. `"Bash"`, `"Write"`, `"Read"`)  |
| `toolInput` | `Record<string, unknown> \| undefined` | Die Eingabeparameter des Tools                              |
| `payload`   | `Record<string, unknown>`              | Vollständige rohe Ereignisnutzlast von Claude Code          |
| `session`   | `SessionMetadata \| undefined`         | Sitzungskontext (siehe unten)                               |

### `SessionMetadata`-Felder

| Feld             | Typ      | Beschreibung                               |
| ---------------- | -------- | ------------------------------------------ |
| `sessionId`      | `string` | Claude Code-Sitzungsbezeichner             |
| `cwd`            | `string` | Arbeitsverzeichnis der Claude Code-Sitzung |
| `transcriptPath` | `string` | Pfad zur JSONL-Transkriptdatei der Sitzung |

### Ereignistypen

| Ereignis       | Wann es ausgelöst wird                   | `toolInput`-Inhalt                                                                                                                                                       |
| -------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `PreToolUse`   | Bevor Claude ein Tool ausführt           | Die Tool-Eingabe (z. B. `{ command: "..." }` für Bash)                                                                                                                   |
| `PostToolUse`  | Nachdem ein Tool abgeschlossen ist       | Die Tool-Eingabe + `tool_result` (die Ausgabe)                                                                                                                           |
| `Notification` | Wenn Claude eine Benachrichtigung sendet | `{ message: "...", notification_type: "idle" \| "permission_prompt" \| ... }` – Hooks müssen immer `allow()` zurückgeben, sie können Benachrichtigungen nicht blockieren |
| `Stop`         | Wenn die Claude-Sitzung endet            | Leer                                                                                                                                                                     |

***

## Auswertungsreihenfolge

Richtlinien werden in dieser Reihenfolge ausgewertet:

1. Eingebaute Richtlinien (in Definitionsreihenfolge)
2. Explizite benutzerdefinierte Richtlinien aus `customPoliciesPath` (in `.add()`-Reihenfolge)
3. Konventionsbasierte Richtlinien aus dem Projektverzeichnis `.failproofai/policies/` (Dateien alphabetisch, `.add()`-Reihenfolge innerhalb)
4. Konventionsbasierte Richtlinien aus dem Benutzerverzeichnis `~/.failproofai/policies/` (Dateien alphabetisch, `.add()`-Reihenfolge innerhalb)

<Note>
  Das erste `deny` schließt alle nachfolgenden Richtlinien kurz. Alle `instruct`-Nachrichten werden gesammelt und gemeinsam übermittelt.
</Note>

***

## Transitive Importe

Benutzerdefinierte Richtliniendateien können lokale Module über relative Pfade importieren:

```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");
  },
});
```

Alle von der Einstiegsdatei aus erreichbaren relativen Importe werden aufgelöst. Dies wird durch das Umschreiben von `from "failproofai"`-Importen auf den tatsächlichen dist-Pfad und das Erstellen temporärer `.mjs`-Dateien implementiert, um ESM-Kompatibilität sicherzustellen.

***

## Ereignistypfilterung

Verwende `match.events`, um einzuschränken, wann eine Richtlinie ausgelöst wird:

```js theme={null}
customPolicies.add({
  name: "require-summary-on-stop",
  match: { events: ["Stop"] },
  fn: async (ctx) => {
    // Wird nur ausgelöst, wenn die Sitzung endet
    // ctx.session.transcriptPath enthält das vollständige Sitzungsprotokoll
    return allow();
  },
});
```

Lasse `match` vollständig weg, um bei jedem Ereignistyp auszulösen.

***

## Fehlerbehandlung und Ausfallverhalten

Benutzerdefinierte Richtlinien sind **fail-open**: Fehler blockieren niemals eingebaute Richtlinien und bringen den Hook-Handler nicht zum Absturz.

| Fehler                             | Verhalten                                                                                                                                  |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `customPoliciesPath` nicht gesetzt | Keine expliziten benutzerdefinierten Richtlinien werden ausgeführt; Konventionsrichtlinien und eingebaute Richtlinien laufen normal weiter |
| Datei nicht gefunden               | Warnung wird in `~/.failproofai/hook.log` protokolliert; eingebaute Richtlinien laufen weiter                                              |
| Syntax-/Importfehler (explizit)    | Fehler wird in `~/.failproofai/hook.log` protokolliert; explizite benutzerdefinierte Richtlinien werden übersprungen                       |
| Syntax-/Importfehler (Konvention)  | Fehler wird protokolliert; diese Datei wird übersprungen, andere Konventionsdateien werden weiterhin geladen                               |
| `fn` wirft zur Laufzeit            | Fehler wird protokolliert; dieser Hook wird als `allow` behandelt; andere Hooks laufen weiter                                              |
| `fn` dauert länger als 10 Sekunden | Timeout wird protokolliert; als `allow` behandelt                                                                                          |
| Konventionsverzeichnis fehlt       | Keine Konventionsrichtlinien werden ausgeführt; kein Fehler                                                                                |

<Tip>
  Um Fehler in benutzerdefinierten Richtlinien zu debuggen, beobachte die Protokolldatei:

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

***

## Vollständiges Beispiel: mehrere Richtlinien

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

// Verhindert, dass der Agent in das Verzeichnis secrets/ schreibt
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();
  },
});

// Hält den Agenten auf Kurs: Tests vor dem Commit überprüfen
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();
  },
});

// Verhindert ungeplante Abhängigkeitsänderungen während einer Freeze-Periode
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 };
```

***

## Beispiele

Das Verzeichnis `examples/` enthält sofort einsatzbereite Richtliniendateien:

| Datei                                                | Inhalt                                                                                                                    |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `examples/policies-basic.js`                         | Fünf Einstiegsrichtlinien für häufige Agenten-Fehlermuster                                                                |
| `examples/policies-advanced/index.js`                | Fortgeschrittene Muster: transitive Importe, asynchrone Aufrufe, Ausgabebereinigung und Sitzungsende-Hooks                |
| `examples/convention-policies/security-policies.mjs` | Konventionsbasierte Sicherheitsrichtlinien (Blockierung von .env-Schreibvorgängen, Verhinderung von Git-History-Rewrites) |
| `examples/convention-policies/workflow-policies.mjs` | Konventionsbasierte Workflow-Richtlinien (Test-Erinnerungen, Überwachung von Datei-Schreibvorgängen)                      |

### Explizite Dateibeispiele verwenden

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

### Konventionsbasierte Beispiele verwenden

```bash theme={null}
# Auf Projektebene kopieren
mkdir -p .failproofai/policies
cp examples/convention-policies/*.mjs .failproofai/policies/

# Oder auf Benutzerebene kopieren
mkdir -p ~/.failproofai/policies
cp examples/convention-policies/*.mjs ~/.failproofai/policies/
```

Kein Installationsbefehl erforderlich – die Dateien werden beim nächsten Hook-Ereignis automatisch erkannt.
