メインコンテンツへスキップ
カスタムポリシーを使えば、あらゆるエージェントの動作に対してルールを定義できます。プロジェクト規約の強制、ドリフトの防止、破壊的な操作のゲート制御、スタックしたエージェントの検出、Slackや承認ワークフローとの連携などが実現できます。組み込みポリシーと同じフックイベントシステムおよび allowdenyinstruct の判定を使用します。

クイック例

// 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();
  },
});
インストール:
failproofai policies --install --custom ./my-policies.js

カスタムポリシーの読み込み方法(2通り)

方法1: 規約ベース(推奨)

*policies.{js,mjs,ts} ファイルを .failproofai/policies/ に置くだけで自動的に読み込まれます。フラグや設定変更は不要です。gitフックと同じ仕組みです。ファイルを置けばそのまま動きます。
# プロジェクトレベル — gitにコミットしてチームと共有
.failproofai/policies/security-policies.mjs
.failproofai/policies/workflow-policies.mjs

# ユーザーレベル — 個人用、全プロジェクトに適用
~/.failproofai/policies/my-policies.mjs
動作の仕組み:
  • プロジェクトディレクトリとユーザーディレクトリの両方がスキャンされます(どちらか一方ではなく、両方が適用されます)
  • 各ディレクトリ内ではファイルがアルファベット順に読み込まれます。順序を制御するには 01-02- などのプレフィックスを付けてください
  • *policies.{js,mjs,ts} に一致するファイルのみ読み込まれ、それ以外のファイルは無視されます
  • 各ファイルは独立して読み込まれます(ファイル単位でフェイルオープン)
  • 明示的な --custom や組み込みポリシーと共存できます
規約ポリシーはチーム全体でポリシーを共有する最も簡単な方法です。.failproofai/policies/ をgitにコミットすれば、すべてのチームメンバーが自動的に適用されます。

方法2: 明示的なファイルパス

# カスタムポリシーファイルを指定してインストール
failproofai policies --install --custom ./my-policies.js

# ポリシーファイルのパスを変更
failproofai policies --install --custom ./new-policies.js

# 設定からカスタムポリシーのパスを削除
failproofai policies --uninstall --custom
解決された絶対パスは policies-config.jsoncustomPoliciesPath に保存されます。ファイルはフックイベントごとに新たに読み込まれます。イベント間でのキャッシュはありません。

両方を併用する

規約ポリシーと明示的な --custom ファイルは共存できます。読み込み順序:
  1. 明示的な customPoliciesPath ファイル(設定されている場合)
  2. プロジェクトの規約ファイル({cwd}/.failproofai/policies/、アルファベット順)
  3. ユーザーの規約ファイル(~/.failproofai/policies/、アルファベット順)

API

インポート

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

customPolicies.add(hook)

ポリシーを登録します。同一ファイル内で複数のポリシーを登録するために何度でも呼び出せます。
customPolicies.add({
  name: string;                         // 必須 - 一意の識別子
  description?: string;                 // `failproofai policies` の出力に表示される
  match?: { events?: HookEventType[] }; // イベントタイプでフィルタリング。省略するとすべてにマッチ
  fn: (ctx: PolicyContext) => PolicyResult | Promise<PolicyResult>;
});

判定ヘルパー

関数効果使用場面
allow()操作を静かに許可するアクションが安全でメッセージが不要な場合
deny(message)操作をブロックするエージェントがこのアクションを取るべきでない場合
instruct(message)ブロックせずにコンテキストを追加するエージェントに追加のコンテキストを与えて軌道を維持する場合
deny(message) — メッセージは "Blocked by failproofai:" というプレフィックス付きでClaudeに表示されます。一つの deny が発生すると、以降の評価はすべてスキップされます。 instruct(message) — メッセージは現在のツール呼び出しに対するClaudeのコンテキストに追記されます。すべての instruct メッセージは蓄積されてまとめて配信されます。
policyParamshint フィールドを追加することで、denyinstruct のメッセージに追加のガイダンスを付け加えられます。コードの変更は不要です。これはカスタム(custom/)、プロジェクト規約(.failproofai-project/)、ユーザー規約(.failproofai-user/)ポリシーでも機能します。詳細は 設定 → hint を参照してください。

情報提供用のallowメッセージ

allow(message) は操作を許可しつつ、情報提供のメッセージをClaudeに送信します。メッセージはフックハンドラーのstdoutレスポンスの additionalContext として配信されます。これは instruct と同じ仕組みですが、意味的に異なります。警告ではなくステータスの更新として扱われます。
関数効果使用場面
allow(message)許可してClaudeにコンテキストを送信するチェックが通ったことを確認する、またはチェックがスキップされた理由を説明する場合
ユースケース:
  • ステータス確認: allow("All CI checks passed.") — すべて正常であることをClaudeに伝える
  • フェイルオープンの説明: allow("GitHub CLI not installed, skipping CI check.") — チェックがスキップされた理由をClaudeに伝え、完全なコンテキストを提供する
  • 複数メッセージの蓄積: 複数のポリシーがそれぞれ allow(message) を返した場合、すべてのメッセージが改行で結合されてまとめて配信されます
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.");

    // ... ブランチのステータスを確認 ...
    if (allPushed) {
      return allow("Branch is up to date with remote.");
    }
    return deny("Unpushed changes detected.");
  },
});

PolicyContext フィールド

フィールド説明
eventTypestring"PreToolUse""PostToolUse""Notification""Stop"
toolNamestring | undefined呼び出されるツール(例: "Bash""Write""Read"
toolInputRecord<string, unknown> | undefinedツールの入力パラメータ
payloadRecord<string, unknown>Claude Code からの生のイベントペイロード全体
sessionSessionMetadata | undefinedセッションコンテキスト(以下を参照)

SessionMetadata フィールド

フィールド説明
sessionIdstringClaude Code セッション識別子
cwdstringClaude Code セッションの作業ディレクトリ
transcriptPathstringセッションのJSONLトランスクリプトファイルへのパス

イベントタイプ

イベント発火タイミングtoolInput の内容
PreToolUseClaudeがツールを実行する前ツールの入力(例: Bashの場合 { command: "..." }
PostToolUseツールの完了後ツールの入力 + tool_result(出力)
NotificationClaudeが通知を送信するとき{ message: "...", notification_type: "idle" | "permission_prompt" | ... } - フックは常に allow() を返す必要があります。通知をブロックすることはできません
StopClaudeセッションが終了するとき

評価順序

ポリシーは以下の順序で評価されます:
  1. 組み込みポリシー(定義順)
  2. customPoliciesPath から読み込まれた明示的なカスタムポリシー(.add() の呼び出し順)
  3. プロジェクトの .failproofai/policies/ からの規約ポリシー(ファイルはアルファベット順、ファイル内は .add() の呼び出し順)
  4. ユーザーの ~/.failproofai/policies/ からの規約ポリシー(ファイルはアルファベット順、ファイル内は .add() の呼び出し順)
最初の deny が発生すると、以降のすべてのポリシーはスキップされます。すべての instruct メッセージは蓄積されてまとめて配信されます。

推移的インポート

カスタムポリシーファイルは相対パスを使ってローカルモジュールをインポートできます:
// 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");
  },
});
エントリファイルから到達可能なすべての相対インポートが解決されます。これは from "failproofai" のインポートを実際のdistパスに書き換え、ESM互換性を確保するために一時的な .mjs ファイルを作成することで実装されています。

イベントタイプのフィルタリング

match.events を使ってポリシーが発火するタイミングを制限できます:
customPolicies.add({
  name: "require-summary-on-stop",
  match: { events: ["Stop"] },
  fn: async (ctx) => {
    // セッション終了時にのみ発火する
    // ctx.session.transcriptPath にセッションの完全なログが含まれる
    return allow();
  },
});
match を完全に省略すると、すべてのイベントタイプで発火します。

エラーハンドリングと失敗モード

カスタムポリシーはフェイルオープンです。エラーが発生しても組み込みポリシーをブロックしたり、フックハンドラーをクラッシュさせたりすることはありません。
失敗動作
customPoliciesPath が未設定明示的なカスタムポリシーは実行されない。規約ポリシーと組み込みポリシーは通常通り継続
ファイルが見つからない~/.failproofai/hook.log に警告を記録。組み込みポリシーは継続
構文/インポートエラー(明示的)~/.failproofai/hook.log にエラーを記録。明示的なカスタムポリシーをスキップ
構文/インポートエラー(規約)エラーを記録。そのファイルをスキップし、他の規約ファイルは引き続き読み込まれる
fn が実行時に例外をスローエラーを記録。そのフックは allow として扱われる。他のフックは継続
fn が10秒以上かかるタイムアウトを記録。allow として扱われる
規約ディレクトリが存在しない規約ポリシーは実行されない。エラーなし
カスタムポリシーのエラーをデバッグするには、ログファイルを監視してください:
tail -f ~/.failproofai/hook.log

完全な例: 複数ポリシー

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

// エージェントが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();
  },
});

// エージェントの軌道維持: コミット前にテストを検証する
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();
  },
});

// フリーズ期間中の計画外の依存関係変更を防ぐ
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 };

サンプル

examples/ ディレクトリにはすぐに使えるポリシーファイルが含まれています:
ファイル内容
examples/policies-basic.jsエージェントのよくある失敗モードをカバーする5つのスターターポリシー
examples/policies-advanced/index.js高度なパターン: 推移的インポート、非同期呼び出し、出力のスクラビング、セッション終了フック
examples/convention-policies/security-policies.mjs規約ベースのセキュリティポリシー(.envへの書き込みをブロック、gitの履歴書き換えを防止)
examples/convention-policies/workflow-policies.mjs規約ベースのワークフローポリシー(テストのリマインダー、ファイル書き込みの監査)

明示的なファイルのサンプルを使用する

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

規約ベースのサンプルを使用する

# プロジェクトレベルにコピー
mkdir -p .failproofai/policies
cp examples/convention-policies/*.mjs .failproofai/policies/

# またはユーザーレベルにコピー
mkdir -p ~/.failproofai/policies
cp examples/convention-policies/*.mjs ~/.failproofai/policies/
インストールコマンドは不要です。次のフックイベント時に自動的にファイルが読み込まれます。