はじめに
本記事は「2026年4月 AIエージェント開発の最前線」シリーズの一部として執筆している。シリーズ全体の概観はこちらの記事を参照してほしい。
アーキテクチャ編では、everything-claude-code(ECC)の五層構造を俯瞰し、フックシステムの設計思想を概説した。本記事はシリーズ第3弾として、32フック全件の動作、run-with-flags.js の実行モデル、主要フックのコード解説、クロスハーネスでのフック適応パターンを扱う。
フックシステムは ECC の「ガードレール」を担うレイヤーだ。エージェントやスキルが「何をするか」を決めるのに対し、フックは「いつ自動で検証・記録するか」を決める。開発サイクルの各ポイントにチェックを埋め込むことで、AIエージェントの動作を人間が設定した品質基準内に保つ仕組みになっている。
最初に押さえるべき設計原則は3つだ。
- イベント駆動 — 複数のイベントタイプ(ツール実行前後・レスポンス完了・セッション開始/終了など)に反応する
- プロファイル累積型 — minimal → standard → strict の順に有効フックが増える。セキュリティ上重要なフックはプロファイル外で常時有効
-
2パス実行エンジン —
module.exports + runパターンのフックはプロセス生成なしで直接ロードされ、フック数が多い ECC でも実用的な速度を維持する
注記: 本記事の情報は2026年4月時点のものであり、各リポジトリの機能・数値は変動する可能性がある。数値は v1.10.0 HEAD(commit: 125d5e6)時点で検証した。最新の情報は各リポジトリの README を参照してほしい。
フックシステムの全体像
hooks.json による一元管理
ECC のフック定義は hooks/hooks.json に一元集約されている。v1.10.0 HEAD 時点で 32フック、7イベントタイプ を持つ。
フック定義の基本形は以下の通りだ。
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "npx block-no-verify@1.1.2"
}
],
"description": "Block git hook-bypass flag...",
"id": "pre:bash:block-no-verify"
}
matcher はフックが反応するツール名を指定する。"Bash" は Bash ツール使用時のみ、"Edit|Write|MultiEdit" は複数ツールにマッチし、"*" は全ツールに反応する。
7つのイベントタイプ
| イベントタイプ | 発火タイミング | フック数 | 主な役割 |
|---|---|---|---|
| PreToolUse | ツール実行前 | 11 | ブロック・警告・観測 |
| PostToolUse | ツール実行後 | 11 | ログ・品質チェック・学習 |
| Stop | レスポンス完了時 | 6 | バッチ処理・コスト記録・通知 |
| SessionStart | セッション開始時 | 1 | コンテキスト復元・Instinct 注入 |
| PreCompact | コンパクション前 | 1 | 状態保存 |
| PostToolUseFailure | ツール実行失敗時 | 1 | MCP ヘルス追跡 |
| SessionEnd | セッション終了時 | 1 | ライフサイクルマーカー |
開発中のイベントフローを時系列で示すと次のようになる。
SessionStart
→ PreToolUse → [ツール実行] → PostToolUse(成功時)
→ PostToolUseFailure(失敗時)
→ ... (繰り返し)
→ PreCompact → [コンパクション](必要時)
→ Stop(レスポンス完了ごと)
SessionEnd
全フックリファレンス(v1.10.0 HEAD 時点で32件)
32フックを「どのプロファイルで有効か」で分類する。プロファイルの詳細は次節で解説するが、minimal → standard → strict の順に含まれるフックが増える累積型だ。
常時有効フック(5件)
run-with-flags.js を経由せず、プロファイルに関係なく常に実行される。
| ID | イベント | matcher | 役割 |
|---|---|---|---|
pre:bash:block-no-verify |
PreToolUse | Bash |
git --no-verify の実行をブロック |
pre:bash:auto-tmux-dev |
PreToolUse | Bash | tmux で dev サーバーを自動起動 |
session:start |
SessionStart | * | コンテキスト復元・Instinct 注入・PM 検出 |
post:bash:command-log-audit |
PostToolUse | Bash | Bash コマンドを監査ログに記録 |
post:bash:command-log-cost |
PostToolUse | Bash | Bash ツール使用をタイムスタンプ付き記録 |
block-no-verify と session:start が常時有効なのは設計上の意図がある。前者は CI バイパスを防ぐセキュリティガードであり、後者はセッション継続性の基盤だ。これらをプロファイルで無効化できてしまうと、ECC の基本的な安全性が損なわれる。
minimal プロファイルで追加されるフック(4件)
セッションのライフサイクル管理とコスト記録を担う。
| ID | イベント | 役割 |
|---|---|---|
stop:session-end |
Stop | セッション状態を永続化 |
stop:evaluate-session |
Stop | 継続学習用のパターン抽出トリガー |
stop:cost-tracker |
Stop | トークンコストを JSONL で記録 |
session:end:marker |
SessionEnd | セッション終了のライフサイクルマーカー |
standard プロファイルで追加されるフック(20件)
日常開発で有用な品質チェック・ガイダンスの層。20件のうち、ECC の設計思想が色濃く出ている5件を紹介する。
pre:config-protection — エージェントが ESLint や Prettier の設定を「緩める」方向に書き換えることをブロックし、コード側の修正を促す。AIが「設定を変えれば通る」という安易な解決策に走ることを防ぐ設計だ。
post:quality-gate — ファイル編集後に拡張子を見て Biome/Prettier/gofmt/ruff を自動選択し、品質チェックを実行する。詳細は「主要フック詳解」で後述する。
pre/post:observe:continuous-learning(2件) — ツール使用の入出力を非同期で観測し、Instinct システムの学習データを蓄積する。フック自体は軽量な記録係だが、先進機能編で解説する継続学習の基盤になっている。
stop:format-typecheck — post:edit:accumulator が蓄積した編集ファイルを、レスポンス完了時に一括でフォーマット・型チェックする。Edit のたびに走らせるのではなく Stop で1回だけ走らせる「蓄積→バッチ」パターンだ。
残り15件は役割で分けると4カテゴリに整理できる。
-
ガバナンス・セキュリティ(
pre/post:governance-capture等) — secrets 検出やポリシー違反のキャプチャ -
コード品質(
post:edit:console-warn、stop:check-console-log、post:edit:design-quality-check等) —console.logの残留警告やテンプレ的 UI の検出 -
セッション管理(
post:session-activity-tracker、post:edit:accumulator、pre:compact等) — ツール操作の追跡と Stop 時バッチ処理用の蓄積 -
開発支援(
pre/post:mcp-health-check、post:bash:pr-created、stop:desktop-notify等) — MCP ヘルスチェック・PR 通知・デスクトップ通知
全件の ID と説明は hooks.json を参照してほしい。
strict プロファイルで追加されるフック(3件)
コードレビューや品質重視のセッション向け。
| ID | イベント | 役割 |
|---|---|---|
pre:bash:tmux-reminder |
PreToolUse | 長時間コマンドの tmux 使用を促す |
pre:bash:git-push-reminder |
PreToolUse | git push 前の変更レビューを促す |
pre:bash:commit-quality |
PreToolUse | lint・フォーマット・secrets 検出・コミットメッセージ検証 |
プロファイルの選び方
| プロファイル | 有効フック数 | 推奨用途 |
|---|---|---|
minimal |
9 | CI/自動化環境。セッション管理とコスト記録のみ |
standard(デフォルト) |
29 | 日常開発。品質チェック・ガイダンス込み |
strict |
32 | コードレビュー・品質重視のセッション |
# プロファイル設定(環境変数)
export ECC_HOOK_PROFILE=strict
# プロファイルを変えずに特定フックだけ無効化
export ECC_DISABLED_HOOKS=pre:bash:tmux-reminder,stop:desktop-notify
プロファイルとフック個別無効化は直交する。ECC_DISABLED_HOOKS に指定されたフックは、プロファイルに関係なく常に無効化される。
実運用での使い分けとしては、CI パイプラインやバッチスクリプトでは minimal で余計なガイダンスを省き、日常の対話開発では standard(デフォルト)をそのまま使うのが自然だ。strict はレビュー駆動のセッションや、品質基準を厳格に保ちたいチームプロジェクトで効果を発揮する。迷ったらまず standard で始め、desktop-notify のような好みの分かれるフックは ECC_DISABLED_HOOKS で個別に外すのが実践的だろう。
主要フック詳解
session-start.js — セッション開始の司令塔
ECC で最大のフックスクリプトで、セッション開始時に5つの処理を実行する。
1. セッションマッチング(3段階優先順位)
過去7日間のセッションファイルから、現在の作業に最適なものを選択する。
| 優先度 | マッチ条件 | 判定方法 |
|---|---|---|
| 最高 | ワークツリー完全一致 |
**Worktree:** フィールドと cwd の比較 |
| 中 | プロジェクト名一致 |
**Project:** フィールドの比較 |
| 最低 | 最新のセッション | 更新日時順のフォールバック |
パス比較には fs.realpathSync を使い、シンボリックリンクや大文字小文字の差異(macOS/Windows)を吸収している。
2. Instinct 注入
前セッションで学習したパターン(Instinct)を新セッションに引き継ぐ。
const INSTINCT_CONFIDENCE_THRESHOLD = 0.7;
const MAX_INJECTED_INSTINCTS = 6;
Instinct は YAML frontmatter 付きの Markdown/YAML ファイルで、confidence フィールドを持つ。0.7 以上のものを最大6個選択し、以下のルールで優先順位を決める。
- confidence が高いものを優先
- 同 confidence なら project スコープを global より優先
- 同スコープなら ID のアルファベット順
注入された Instinct はセッション開始時のプロンプトに Active instincts: として追加される。Instinct システムの詳細は先進機能編を参照してほしい。
3. パッケージマネージャ検出
npm/pnpm/yarn/bun を自動検出し、セッション内で一貫したパッケージマネージャを使用する。CLAUDE_PACKAGE_MANAGER 環境変数でオーバーライドも可能。
4. プロジェクトタイプ検出
使用言語・フレームワークを検出してセッションコンテキストに追加する。
5. セッション有効期限管理
ECC_SESSION_RETENTION_DAYS(デフォルト30日)を超えたセッションファイルを自動削除する。
quality-gate.js — 編集後の品質ゲート
ファイル編集後に自動でフォーマッタ・リンタを実行する。module.exports = { run } パターンの実例でもある。
// quality-gate.js(抜粋)
function run(rawInput) {
const input = JSON.parse(rawInput);
const filePath = String(input.tool_input?.file_path || '');
maybeRunQualityGate(filePath);
return rawInput; // パススルー
}
module.exports = { run };
run() 関数は stdin の JSON を受け取り、ファイルパスの拡張子に応じて適切なフォーマッタを呼び出す。
| 拡張子 | フォーマッタ | 備考 |
|---|---|---|
.ts, .tsx, .js, .jsx
|
Biome → Prettier | Biome 検出時は post-edit-format.js に委譲(重複回避) |
.json, .md
|
Biome → Prettier | |
.go |
gofmt | |
.py |
ruff format |
ECC_QUALITY_GATE_FIX=true で自動修正、ECC_QUALITY_GATE_STRICT=true で失敗時のログ出力を有効にできる。
cost-tracker.js — トークンコスト記録
モデル別の概算コスト表を内蔵し、セッションごとのトークン使用量を JSONL 形式で記録する。
const table = {
'haiku': { in: 0.8, out: 4.0 },
'sonnet': { in: 3.0, out: 15.0 },
'opus': { in: 15.0, out: 75.0 },
};
出力先は ~/.claude/metrics/costs.jsonl で、各行に timestamp、session_id、model、input_tokens、output_tokens、estimated_cost_usd を含む。ECC 2.0 のダッシュボードはこのデータを可視化する(詳細は自律ループ編で解説)。
evaluate-session.js — 継続学習トリガー
セッション中のユーザーメッセージ数をカウントし、閾値(デフォルト10件)以上であればパターン抽出を促す。
// Stop イベントで実行(セッション完了時に1回だけ)
const messageCount = countInFile(transcriptPath, /"type"\s*:\s*"user"/g);
if (messageCount < minSessionLength) process.exit(0);
ソースコードのコメントに Why Stop hook instead of UserPromptSubmit の記述があり、「Stop はセッション完了時に1回だけ実行されるため軽量。UserPromptSubmit はメッセージごとに実行されるため、レイテンシへの影響が大きい」という設計判断が明示されている。
run-with-flags.js — フック実行エンジン
scripts/hooks/run-with-flags.js は全フックの実行制御を担うラッパーだ。3つの責務を持つ。
プロファイル判定
hooks.json のコマンド文字列から、フックの有効プロファイルが渡される。
node run-with-flags.js "pre:bash:commit-quality" "scripts/hooks/pre-bash-commit-quality.js" "strict"
第3引数 "strict" が「このフックは strict プロファイルでのみ有効」を意味する。判定ロジックは scripts/lib/hook-flags.js の isHookEnabled() が担う。
// scripts/lib/hook-flags.js(抜粋)
function isHookEnabled(hookId, options = {}) {
const id = normalizeId(hookId);
const disabled = getDisabledHookIds(); // ECC_DISABLED_HOOKS 環境変数
if (disabled.has(id)) return false;
const profile = getHookProfile(); // ECC_HOOK_PROFILE 環境変数
const allowedProfiles = parseProfiles(options.profiles);
return allowedProfiles.includes(profile);
}
プロファイルが合致しない場合、フックスクリプトを実行せずに stdin をそのまま stdout にパススルーする。
2パス実行モデル
フックスクリプトの実行方式を自動判定し、可能な限り高速に実行する。
// run-with-flags.js(抜粋)
const src = fs.readFileSync(scriptPath, 'utf8');
const hasRunExport = /\bmodule\.exports\b/.test(src) && /\brun\b/.test(src);
if (hasRunExport) {
hookModule = require(scriptPath); // Direct require — 高速パス
}
if (hookModule && typeof hookModule.run === 'function') {
const output = hookModule.run(raw, { truncated, maxStdin: MAX_STDIN });
process.exit(emitHookResult(raw, output));
}
// Legacy path: stdin リスナーで副作用を持つフック
const result = spawnSync(process.execPath, [scriptPath], { input: raw, ... });
| 実行方式 | 対象 | 性能差 |
|---|---|---|
| Direct require() |
module.exports + run 関数をエクスポート |
Node.js プロセス生成を回避(~50-100ms 節約/フック) |
| Legacy spawnSync | モジュールスコープで stdin リスナーを持つフック | 子プロセスを生成 |
ソースコードの正規表現 /\bmodule\.exports\b/ と /\brun\b/ で判定している。実際に quality-gate.js は末尾に module.exports = { run }; を持ち、高速パスが適用される。一方、evaluate-session.js は process.stdin.on('data', ...) でデータを読むレガシーパターンで、子プロセスが必要になる。
セキュリティ対策
run-with-flags.js は2つのセキュリティ対策を実装している。
パストラバーサル保護: スクリプトパスがプラグインルートの外を指していないか検証する。
if (!scriptPath.startsWith(resolvedRoot + path.sep)) {
process.stderr.write(`[Hook] Path traversal rejected for ${hookId}: ${scriptPath}\n`);
process.stdout.write(raw);
process.exit(0);
}
stdin サイズ制限: 入力を 1MB (MAX_STDIN = 1024 * 1024) で切り詰める。フックに巨大な入力が渡されることによる DoS を防ぐ。
Stop フックのプラグインルート解決
Stop / SessionEnd イベントのフックは hooks.json 内で node -e "..." のインラインスクリプトとして定義されている。これは Claude Code のプラグインアーキテクチャ上の制約に由来する。Stop イベントでは CLAUDE_PLUGIN_ROOT が確実に設定されない場合があるため、フック自身がプラグインルートを探索する必要がある。
探索順序は以下の通りだ。
-
CLAUDE_PLUGIN_ROOT環境変数 -
~/.claude/直下 -
~/.claude/plugins/ecc等の既知パス(6候補) -
~/.claude/plugins/cache/{slug}/{org}/{version}の動的探索
この探索ロジックが node -e にインライン化されているため、hooks.json 上では長い1行のスクリプトになっている。
クロスハーネスでのフック適応
Cursor: DRY アダプタパターン
.cursor/hooks/adapter.js が Cursor の stdin JSON を Claude Code フォーマットに変換し、ECC の既存フックスクリプトに委任する。
// .cursor/hooks/adapter.js(抜粋)
function transformToClaude(cursorInput, overrides = {}) {
return {
tool_input: {
command: cursorInput.command || cursorInput.args?.command || '',
file_path: cursorInput.path || cursorInput.file || cursorInput.args?.filePath || '',
},
tool_output: {
output: cursorInput.output || cursorInput.result || '',
},
_cursor: { conversation_id, hook_event_name, workspace_roots, model },
};
}
function runExistingHook(scriptName, stdinData) {
execFileSync('node', [path.join(getPluginRoot(), 'scripts', 'hooks', scriptName)], {
input: typeof stdinData === 'string' ? stdinData : JSON.stringify(stdinData),
timeout: 15000,
});
}
hookEnabled() 関数も adapter.js 内に持ち、同じプロファイルシステムを再利用する。フック実装のコードは一切複製していない。
OpenCode: TypeScript ESM プラグイン
.opencode/plugins/ecc-hooks.ts が OpenCode のイベントモデルに ECC フックをマッピングする。v1.10.0 HEAD 時点で OpenCode は20以上のイベントタイプを持ち、Claude Code とは異なるイベント名を使うため、変換テーブルで対応する(例: PreToolUse → tool.execute.before)。加えて、changed-files、format-code、security-audit などの7つのネイティブツールを独自に提供する。
Kiro / Codex の制約
Kiro は .kiro/hooks/ に10個の .kiro.hook ファイル(JSON 形式、v1.10.0 HEAD 時点)を持つ。ECC フックスクリプトを直接呼ぶのではなく、Kiro の IDE フック形式で独自に実装している。
Codex はフック機構を持たないため、AGENTS.md による指示ベースの品質担保と、6つの MCP サーバー接続(v1.10.0 HEAD 時点)で補完する設計だ。
カスタムフック作成パターン
ECC にフックを追加する場合、module.exports + run パターンを推奨する。run-with-flags.js の2パス判定で高速パスが適用される。
// scripts/hooks/my-custom-hook.js
'use strict';
function run(rawInput) {
try {
const input = JSON.parse(rawInput);
// ここにフックロジックを実装
} catch {
// パースエラーは無視(フックはツール実行をブロックしない)
}
return rawInput; // stdin をパススルー
}
module.exports = { run };
hooks.json への追加は以下の形式になる。
{
"matcher": "Edit|Write",
"hooks": [{
"type": "command",
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hooks/run-with-flags.js\" \"post:my-hook\" \"scripts/hooks/my-custom-hook.js\" \"standard,strict\""
}],
"description": "カスタムフックの説明",
"id": "post:my-hook"
}
守るべき原則は3つある。
-
非ブロッキング: パースエラーで
process.exit(0)し、ツール実行を止めない - stdin 制限: 1MB を超える入力は切り詰める
-
タイムアウト: 15-30秒以内に完了する(
timeoutフィールドで制御可能)
まとめ
ECC のフックシステムは、32フックを7イベントタイプに配置し、3段階プロファイルで粒度を制御する構造になっている。
設計上の要点を整理する。
- プロファイルの累積性: minimal (9) → standard (29) → strict (32) と段階的にフックが増える。セキュリティ上重要なフックはプロファイル外で常時有効
-
2パス実行:
module.exports + runパターンのフックはプロセス生成なしで実行され、多数のフックが積み重なる ECC でも実用的な速度を維持する - クロスハーネス移植: Cursor は DRY アダプタでフック実装を再利用、OpenCode は TypeScript ESM でイベントモデルを変換、Codex は指示ベースで補完。各ハーネスの特性に合わせた適応が図られている
- session-start.js の中心的役割: コンテキスト復元と Instinct 注入により、セッション間の知識継承を実現する
フックシステムの上に構築される Instinct 学習・eval-harness の詳細は先進機能編で、自律ループの6パターンと ECC 2.0 は自律ループ編で解説している。
参考リンク
| リソース | 説明 |
|---|---|
| everything-claude-code | メインリポジトリ(MIT ライセンス) |
| hooks/hooks.json | フック定義の全体(32フック) |
| scripts/hooks/run-with-flags.js | フック実行ラッパー |
| scripts/lib/hook-flags.js | プロファイル判定ライブラリ |
| scripts/hooks/session-start.js | セッション開始フック |
| .cursor/hooks/adapter.js | Cursor DRY アダプタ |
| ECC アーキテクチャ編 | 五層構造の設計思想(シリーズ記事) |
| ECC 先進機能編 | Instinct・AgentShield・eval-harness(シリーズ記事) |
| ECC 自律ループ編 | 自律ループ6パターン・ECC 2.0(シリーズ記事) |
| Claude Code 公式ドキュメント | Claude Code のフック・エージェント仕様 |