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

# カスタムポリシー

> JavaScriptで独自のポリシーを記述する — 規約の適用、ドリフトの防止、障害の検出、外部システムとの連携

カスタムポリシーを使うと、エージェントのあらゆる動作に対してルールを記述できます。プロジェクトの規約を強制する、ドリフトを防ぐ、破壊的な操作にゲートをかける、スタックしたエージェントを検出する、Slack・承認ワークフローなどと連携する、といった用途に活用できます。組み込みポリシーと同じフックイベントシステムおよび `allow`、`deny`、`instruct` の判定を使用します。

***

## クイックサンプル

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

インストール:

```bash theme={null}
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` フラグや組み込みポリシーと併用できます

<Tip>
  規約ポリシーは、組織の品質基準を構築する最も簡単な方法です。`.failproofai/policies/` をgitにコミットすれば、すべてのチームメンバーが自動的に同じルールを適用できます — 開発者ごとのセットアップは不要です。チームが新たな障害パターンを発見するたびにポリシーを追加してプッシュすれば、コントリビューションのたびに改善されていくリビングな品質基準になります。
</Tip>

### オプション2: 明示的なファイルパス

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

# ポリシーファイルのパスを置き換え
failproofai policies --install --custom ./new-policies.js

# 設定からカスタムポリシーパスを削除
failproofai policies --uninstall --custom
```

解決された絶対パスは `policies-config.json` の `customPoliciesPath` として保存されます。ファイルはフックイベントのたびに新たに読み込まれ、イベント間でのキャッシュはありません。

### 両方を併用する

規約ポリシーと明示的な `--custom` ファイルは共存できます。読み込み順序:

1. 明示的な `customPoliciesPath` ファイル（設定されている場合）
2. プロジェクト規約ファイル（`{cwd}/.failproofai/policies/`、アルファベット順）
3. ユーザー規約ファイル（`~/.failproofai/policies/`、アルファベット順）

***

## API

### インポート

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

### `customPolicies.add(hook)`

ポリシーを登録します。同一ファイルに複数のポリシーを定義する場合は、必要な回数だけ呼び出せます。

```ts theme={null}
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 に表示されます。1つの `deny` が発生すると、それ以降の評価はすべて短絡します。

`instruct(message)` — メッセージは現在のツール呼び出しに対する Claude のコンテキストに追記されます。すべての `instruct` メッセージは蓄積されて一括して配信されます。

<Tip>
  `deny` または `instruct` メッセージに追加のガイダンスを付け加えるには、`policyParams` の `hint` フィールドを使います — コード変更は不要です。カスタム（`custom/`）、プロジェクト規約（`.failproofai-project/`）、ユーザー規約（`.failproofai-user/`）ポリシーでも機能します。詳細は[設定 → hint](/ja/configuration#hint-cross-cutting)を参照してください。
</Tip>

### 情報提供用の 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)` を返した場合、すべてのメッセージは改行で結合されて一括配信されます

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

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

### `PolicyContext` フィールド

| フィールド       | 型                                      | 説明                                                       |
| ----------- | -------------------------------------- | -------------------------------------------------------- |
| `eventType` | `string`                               | `"PreToolUse"`、`"PostToolUse"`、`"Notification"`、`"Stop"` |
| `toolName`  | `string \| undefined`                  | 呼び出されるツール（例: `"Bash"`、`"Write"`、`"Read"`）                |
| `toolInput` | `Record<string, unknown> \| undefined` | ツールの入力パラメータ                                              |
| `payload`   | `Record<string, unknown>`              | Claude Code からの完全な生イベントペイロード                             |
| `session`   | `SessionMetadata \| undefined`         | セッションコンテキスト（下記参照）                                        |

### `SessionMetadata` フィールド

| フィールド            | 型        | 説明                            |
| ---------------- | -------- | ----------------------------- |
| `sessionId`      | `string` | Claude Code セッション識別子          |
| `cwd`            | `string` | Claude Code セッションの作業ディレクトリ    |
| `transcriptPath` | `string` | セッションの JSONL トランスクリプトファイルへのパス |

### イベントタイプ

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

***

## 評価順序

ポリシーは以下の順序で評価されます:

1. 組み込みポリシー（定義順）
2. `customPoliciesPath` からの明示的なカスタムポリシー（`.add()` の順序）
3. プロジェクト `.failproofai/policies/` の規約ポリシー（ファイルはアルファベット順、ファイル内は `.add()` の順序）
4. ユーザー `~/.failproofai/policies/` の規約ポリシー（ファイルはアルファベット順、ファイル内は `.add()` の順序）

<Note>
  最初の `deny` が発生すると以降のポリシーはすべて短絡します。すべての `instruct` メッセージは蓄積されて一括配信されます。
</Note>

***

## 推移的インポート

カスタムポリシーファイルは相対パスを使用してローカルモジュールをインポートできます:

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

エントリファイルから到達可能なすべての相対インポートが解決されます。これは `from "failproofai"` のインポートを実際の dist パスに書き換え、ESM 互換性を確保するために一時的な `.mjs` ファイルを作成することで実装されています。

***

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

`match.events` を使用してポリシーが発火するタイミングを限定できます:

```js theme={null}
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` として扱われる                           |
| 規約ディレクトリが存在しない            | 規約ポリシーは実行されない。エラーなし                                 |

<Tip>
  カスタムポリシーのエラーをデバッグするには、ログファイルを監視します:

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

***

## 完全なサンプル: 複数のポリシー

```js theme={null}
// 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` | 規約ベースのワークフローポリシー（テストリマインダー、ファイル書き込みの監査）            |

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

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

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

```bash theme={null}
# プロジェクトレベルにコピー
mkdir -p .failproofai/policies
cp examples/convention-policies/*.mjs .failproofai/policies/

# またはユーザーレベルにコピー
mkdir -p ~/.failproofai/policies
cp examples/convention-policies/*.mjs ~/.failproofai/policies/
```

インストールコマンドは不要です — 次のフックイベント時に自動的にファイルが読み込まれます。
