> ## Documentation Index
> Fetch the complete documentation index at: https://ccsafetynet.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# CC Safety Net を自作のエージェントやハーネスに組み込む

> ハーネスやツールを作る人向けのガイドです。hook をインストールする代わりに checkCommand をプロセス内で呼ぶべき場面、組み込み時のポリシー解決、マイナーバージョン間で保証される契約、そしてプラグインホストの実装例をまとめます。

このページは、コマンドを実行する側そのものを作っている人のためのものです。エージェント、タスクランナー、CI のハーネス、shell ツールを持つプラグインホストなどが該当します。そうしたプログラムに hook をインストールすることはありません。チェックは自分で呼び出します。

組み込みに必要なのは 1 つの関数だけです。`cc-safety-net/api` サブパスからエクスポートされる `checkCommand` です。型、投げられる `TypeError` のメッセージ、1 回の呼び出しが読むものは[ライブラリ API](/docs/ja/reference/library-api) がリファレンスとして持っています。このページは、その周りの判断を扱います。

## プロセス内のチェックか、インストール済みの hook か

どちらも同じ guard を通ります。違うのは、誰が呼び出しを所有するかです。

|            | インストール済みの連携                                  | プロセス内の `checkCommand`    |
| ---------- | -------------------------------------------- | ------------------------ |
| 誰が組み込むか    | `cc-safety-net install <target>` をマシンごとに 1 回 | 自分のコードが、コマンドを実行するすべての箇所で |
| 判定が起きる場所   | hook のサブプロセス、またはエージェントが読み込むプラグイン             | 自分のプロセス内、同期的に            |
| 監査ログ       | すべての判定について記録され、`logs` と GUI で読める             | なし。必要なものを自分で記録する         |
| 対象         | 対応する 13 のエージェント CLI                          | 任意の Node.js ホスト          |
| 1 回あたりのコスト | stdin hook 方式のエージェントではプロセス起動                 | 関数呼び出し                   |

判断の目安は最後の行から導けますが、本質は所有権です。ユーザーが対応エージェント CLI のいずれかを使っているなら、インストールコマンドを案内して連携に任せてください。各エージェントの組み込み方は[連携アーキテクチャ](/docs/ja/guides/integration-architecture)にあります。自分自身がコマンドを実行するプログラムなら、関数を呼んでください。自分のランタイム向けに hook 設定のジェネレーターを作るのは、すでに存在する連携を作り直すことになります。

<Note>
  パッケージのルートエクスポートはエンジンではなく OpenCode のプラグインオブジェクトです。`checkCommand` は `cc-safety-net/api` から import してください。
</Note>

## 組み込み時のポリシー解決

組み込み専用の設定はありません。呼び出しが読むファイルは hook が読むものと同じで、どのプロジェクトのファイルになるかは `cwd` 引数が決めます。

* **ユーザースコープ**：`~/.cc-safety-net/policy.json` と `~/.cc-safety-net/rules/`。`CC_SAFETY_NET_HOME` が設定されていればその場所に移ります。これは運用者自身のベースラインで、すべての呼び出しに適用されます。
* **プロジェクトスコープ**：`<cwd>/.cc-safety-net/policy.json` と `<cwd>/.cc-safety-net/rules/`。渡したパスそのものから解決され、親ディレクトリをさかのぼる探索は行いません。セッションのサブディレクトリを追跡するホストは、リポジトリのルートを `cwd` として渡してください。そうしないと、コミットされたプロジェクトポリシーが読み込まれません。
* **マージ**：プロジェクトファイルはユーザーファイルの上にフィールド単位で重なります。両スコープの保護対象パスのリストは和集合になり、プロジェクトファイルの `audit` は無視され、プロジェクトが緩めたフィールドは黙って適用されるのではなく報告されます。マージの仕様は[ポリシー](/docs/ja/configuration/policy#project-policy)が持っています。
* **環境変数**：`CC_SAFETY_NET_LEVEL` と各機能のスイッチは呼び出しのたびに `process.env` から読まれ、レベルはポリシーファイルの設定より引き上げることしかできません。[環境変数](/docs/ja/configuration/environment)を参照してください。

プロジェクトごとの差は、そのまま呼び出しごとの差になります。`cwd` がそれを選ぶからです。複数のチェックアウトを同時に扱うホストは、正しいディレクトリを渡すだけでそれぞれに正しいポリシーを得られます。追加の API は要りません。

呼び出し側を書く前に知っておく価値のある挙動が 2 つあります。読み取りと検索が可能なディレクトリとして stat できない `cwd` は、例外ではなく fail-closed の deny を返します。そのため、パスの打ち間違いは別のプロジェクトを解析するのではなくコマンドのブロックになります。もう 1 つ、`cwd` の欠落や文字列でない command といった不正な入力は `TypeError` を投げます。これはコマンドに対する判定ではなく、呼び出し側のバグです。

## API が保証するもの

次の点はマイナーバージョンをまたいで保たれるので、ホストはこれを前提に実装できます。

* 入力の形 `{ command, cwd }`（`cwd` は絶対パスのディレクトリ）と、2 つの結果の kind、すなわち `allow` と、`reason` 文字列および省略可能な `ruleId` を持つ `deny`。
* `deny` はコマンドを実行してはいけないという意味で、throw も同じ意味です。throw が allow になることはありません。
* `cwd` がポリシー解決の基準になります。コマンド内の相対パスも、プロジェクトの `.cc-safety-net/` 設定も、渡した `cwd` を基準に解決され、`process.cwd()` は使われません。
* API のパスは監査レコードを書かず、ネットワークリクエストも行いません。呼び出しが読むのはローカルのポリシーファイル、ファイルシステムの状態、環境設定で、マシンの外には何も出ません。

ルールカタログは、どのマイナーバージョンでも変わり得ます。ブロック対象のコマンドはリリースごとに増え、それに伴って `reason` の文言も変わります。そのため、アップグレードによって、テストのフィクスチャが依存していたコマンドが allow から deny に変わることもあります。分岐は `kind` で行ってください。`reason` と `ruleId` は突き合わせの対象ではなく、人間とログのために残すものです。挙動をテストしている他の依存関係と同じように、バージョンは lockfile で固定してください。

## 実装例：プラグインホスト

プラグイン機構を持つホストは、たいてい実行前の hook を公開していて、そこでツール呼び出しを拒否できます。連携点はそれだけです。プラグインはホストのイベントを `{ command, cwd }` に対応付け、`checkCommand` を呼び、deny をホスト側の拒否表現に変換します。

```ts theme={"dark"}
import { checkCommand } from 'cc-safety-net/api';

export function registerSafetyNet(host: PluginHost) {
  host.beforeToolCall((call, ctx) => {
    if (call.toolName !== 'shell') return undefined;

    if (typeof call.input.command !== 'string' || call.input.command.trim() === '') {
      return { block: true, reason: 'Malformed shell tool call.' };
    }

    try {
      const result = checkCommand({ command: call.input.command, cwd: ctx.projectRoot });
      if (result.kind === 'deny') return { block: true, reason: result.reason };
      return undefined;
    } catch (error) {
      console.error('CC Safety Net could not check the command', error);
      return { block: true, reason: 'Command check failed. The command was not executed.' };
    }
  });
}
```

このハンドラーで重要な判断は 4 つです。

* **チェックするのはコマンド系ツールだけです。**`checkCommand` が解析するのはシェルコマンドの文字列です。ホストの read、write、search といったツールはコマンド系ツールではないので、shell ツールだけを渡し、他はそのまま通してください。
* \*\*`cwd` は絶対パスのプロジェクトルートです。\*\*ホスト側の呼び名が何であれ、その値がプロジェクトポリシーを選び、コマンド内の `.env` のような相対パスの基準になります。
* **throw はブロックです。**`catch` は契約の fail-closed 側です。投げられたエラーをそのまま実行に進ませるホストは、壊れたチェックを allow に変えてしまいます。
* \*\*不正なイベントもブロックです。\*\*同梱のプロセス内連携はまさにこう動きます。command フィールドが欠けている、あるいは文字列でないイベントは、スキップではなくブロックされます。解析する対象が存在しないからです。

ホストの拒否表現がオブジェクトの返却ではなく例外なら、拒否メッセージを返す代わりに投げてください。形は変わりますが、判断は変わりません。

## 運用上の注意

* \*\*ログは自分のものです。\*\*このパスでは監査レコードが書かれないので、ホストが記録しない拒否は何も残りません。`logs` コマンドと GUI のダッシュボードが表示するのは hook とプラグインの判定であって、組み込み実行のものではありません。
* \*\*運用者は通常のツールで設定します。\*\*組み込みホストも同じポリシーファイルを読むため、`status`、`doctor`、`explain` はそのままホストの挙動の説明になります。ただし、`cwd` として渡すのと同じディレクトリで実行した場合に限ります。なぜブロックされたのかを聞かれたら、[explain トレース](/docs/ja/reference/explain-trace)を案内してください。
* \*\*`ruleId` は診断用です。\*\*表示する、記録する、ルールを引くために使う。いずれも問題ありませんが、分岐条件にはしないでください。
* \*\*呼び出しは同期です。\*\*まとめて処理したいならループで回してください。非同期版もバッチ版もありませんし、追加したところで呼び出しがすでに行っているファイル読み取りを隠すだけです。
* **バージョンはパッケージから読めます。**`cc-safety-net/package.json` はエクスポートされたサブパスなので、依存関係のバージョンを報告するホストはこれも含められます。

## 関連ページ

* [ライブラリ API](/docs/ja/reference/library-api)：`checkCommand` の完全なリファレンス。
* [連携アーキテクチャ](/docs/ja/guides/integration-architecture)：ホストが自分でコマンドを実行する場合に、この関数が置き換えるインストール済み連携。
* [ポリシー](/docs/ja/configuration/policy)と[環境変数](/docs/ja/configuration/environment)：呼び出しが読むものすべて。
* [ブロックされるコマンド](/docs/ja/reference/blocked-commands)：deny になり得るものの一覧。
