Benutzerdefinierte Richtlinien ermöglichen es Ihnen, Regeln für jedes Agentenverhalten zu definieren: Projektkonventionen durchsetzen, Drift verhindern, destruktive Operationen absichern, feststeckende Agenten erkennen oder Slack, Genehmigungsworkflows und mehr integrieren. Sie verwenden dasselbe Hook-Ereignissystem und dieselben allow-, deny- und instruct-Entscheidungen wie eingebaute Richtlinien.
Schnellbeispiel
// 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();
},
});
Installation:
failproofai policies --install --custom ./my-policies.js
Zwei Wege zum Laden benutzerdefinierter Richtlinien
Option 1: Konventionsbasiert (empfohlen)
Legen Sie *policies.{js,mjs,ts}-Dateien in .failproofai/policies/ ab – sie werden automatisch geladen, ohne Flags oder Konfigurationsänderungen. Das funktioniert wie Git-Hooks: Datei ablegen, fertig.
# Projektebene – per Git eingecheckt, 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 Erster-gewinnt-Prinzip pro Scope)
- Dateien werden innerhalb jedes Verzeichnisses alphabetisch geladen. Präfixe wie
01-, 02- steuern die Reihenfolge
- 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 zusammen mit explizitem
--custom und eingebauten Richtlinien
Konventionsbasierte Richtlinien sind der einfachste Weg, Richtlinien im Team zu teilen. Checken Sie .failproofai/policies/ per Git ein, und jedes Teammitglied erhält sie automatisch.
Option 2: Expliziter Dateipfad
# 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 neu geladen – es gibt kein Caching zwischen Ereignissen.
Beide Methoden kombinieren
Konventionsbasierte Richtlinien und die explizite --custom-Datei können nebeneinander existieren. Ladereihenfolge:
- Explizite
customPoliciesPath-Datei (falls konfiguriert)
- Projekt-Konventionsdateien (
{cwd}/.failproofai/policies/, alphabetisch)
- Benutzer-Konventionsdateien (
~/.failproofai/policies/, alphabetisch)
API
Import
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.
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 | Verwendung |
|---|
allow() | Operation lautlos zulassen | Die Aktion ist sicher, keine Meldung erforderlich |
deny(message) | Operation blockieren | Der Agent soll 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 gegenüber Claude mit dem Präfix "Blocked by failproofai:". Ein einzelnes deny bricht die gesamte weitere Auswertung ab.
instruct(message) – die Nachricht wird dem Claude-Kontext für den aktuellen Tool-Aufruf angehängt. Alle instruct-Nachrichten werden gesammelt und gemeinsam zugestellt.
Sie können jeder deny- oder instruct-Nachricht zusätzliche Hinweise hinzufügen, indem Sie ein hint-Feld in policyParams setzen – ohne Codeänderung. Das funktioniert auch für benutzerdefinierte (custom/), Projekt-Konventions- (.failproofai-project/) und Benutzer-Konventionsrichtlinien (.failproofai-user/). Siehe Konfiguration → hint für Details.
allow(message) lässt die Operation zu und sendet eine informative Nachricht an Claude zurück. Die Nachricht wird als additionalContext in der stdout-Antwort des Hook-Handlers zugestellt – derselbe Mechanismus wie bei instruct, jedoch semantisch anders: Es handelt sich um eine Statusmeldung, nicht um eine Warnung.
| Funktion | Wirkung | Verwendung |
|---|
allow(message) | Zulassen und Kontext an Claude senden | Bestätigen, dass eine Prüfung bestanden wurde, 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.") – erklärt Claude, warum eine Prüfung übersprungen wurde, damit er den vollen Kontext hat
- Mehrere Nachrichten werden gesammelt: Wenn mehrere Richtlinien jeweils
allow(message) zurückgeben, werden alle Nachrichten mit Zeilenumbrüchen verbunden und gemeinsam zugestellt
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 Ereignis-Payload von Claude Code |
session | SessionMetadata | undefined | Sitzungskontext (siehe unten) |
| 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 | Inhalt von toolInput |
|---|
PreToolUse | Bevor Claude ein Tool ausführt | Die Eingabe des Tools (z. B. { command: "..." } für Bash) |
PostToolUse | Nachdem ein Tool abgeschlossen hat | Die Eingabe des Tools + tool_result (die Ausgabe) |
Notification | Wenn Claude eine Benachrichtigung sendet | { message: "...", notification_type: "idle" | "permission_prompt" | ... } – Hooks müssen immer allow() zurückgeben, Benachrichtigungen können nicht blockiert werden |
Stop | Wenn die Claude-Sitzung endet | Leer |
Auswertungsreihenfolge
Richtlinien werden in dieser Reihenfolge ausgewertet:
- Eingebaute Richtlinien (in Definitionsreihenfolge)
- Explizite benutzerdefinierte Richtlinien aus
customPoliciesPath (in .add()-Reihenfolge)
- Konventionsrichtlinien aus dem Projekt
.failproofai/policies/ (Dateien alphabetisch, .add()-Reihenfolge innerhalb)
- Konventionsrichtlinien aus dem Benutzerverzeichnis
~/.failproofai/policies/ (Dateien alphabetisch, .add()-Reihenfolge innerhalb)
Das erste deny bricht alle nachfolgenden Richtlinien ab. Alle instruct-Nachrichten werden gesammelt und gemeinsam zugestellt.
Transitive Importe
Benutzerdefinierte Richtliniendateien können lokale Module über relative Pfade importieren:
// 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 relativen Importe, die von der Einstiegsdatei aus erreichbar sind, werden aufgelöst. Dies wird implementiert, indem from "failproofai"-Importe auf den tatsächlichen dist-Pfad umgeschrieben und temporäre .mjs-Dateien erstellt werden, um ESM-Kompatibilität sicherzustellen.
Ereignistypfilterung
Verwenden Sie match.events, um einzuschränken, wann eine Richtlinie ausgelöst wird:
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();
},
});
Lassen Sie match vollständig weg, um bei jedem Ereignistyp auszulösen.
Fehlerbehandlung und Fehlermodi
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 10s | Timeout wird protokolliert; als allow behandelt |
| Konventionsverzeichnis fehlt | Keine Konventionsrichtlinien werden ausgeführt; kein Fehler |
Um Fehler in benutzerdefinierten Richtlinien zu debuggen, beobachten Sie die Log-Datei:tail -f ~/.failproofai/hook.log
Vollständiges Beispiel: mehrere Richtlinien
// my-policies.js
import { customPolicies, allow, deny, instruct } from "failproofai";
// Verhindert, dass der Agent in das secrets/-Verzeichnis 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();
},
});
// Agenten auf Kurs halten: Tests vor dem Commit prü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();
},
});
// Ungeplante Abhängigkeitsänderungen während des Freeze verhindern
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 ausführbare Richtliniendateien:
| Datei | Inhalt |
|---|
examples/policies-basic.js | Fünf Starter-Richtlinien für häufige Agenten-Fehlermodi |
examples/policies-advanced/index.js | Fortgeschrittene Muster: transitive Importe, asynchrone Aufrufe, Ausgabe-Bereinigung und Sitzungsende-Hooks |
examples/convention-policies/security-policies.mjs | Konventionsbasierte Sicherheitsrichtlinien (blockiert .env-Schreibzugriffe, verhindert Git-History-Umschreibung) |
examples/convention-policies/workflow-policies.mjs | Konventionsbasierte Workflow-Richtlinien (Test-Erinnerungen, Audit-Datei-Schreibzugriffe) |
Explizite Dateibeispiele verwenden
failproofai policies --install --custom ./examples/policies-basic.js
Konventionsbasierte Beispiele verwenden
# 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.