Cascade Hooks を使うと、Cascade のワークフローの要所でカスタムシェルコマンドを実行できます。この強力な拡張機能により、操作ログの記録、ガードレールの適用、検証の実行、外部システムとの連携が可能になります。
ベータ版リリース: Cascade Hooks は現在ベータ版で、開発中です。機能や API は変更される可能性があります。フィードバックや不具合の報告は Windsurf Support までご連絡ください。
Hooks は、Cascade の動作をきめ細かく制御する必要があるパワーユーザーや Enterprise のチーム向けに設計されています。基本的なシェルスクリプトの知識が必要です。
Hooks は幅広い自動化とガバナンス機能を提供します:
- Logging & Analytics: コンプライアンスと利用分析のために、Cascade によるすべてのファイル読み取り、コード変更、コマンド実行を追跡
- セキュリティコントロール: Cascade の機密ファイルへのアクセスや危険なコマンドの実行をブロック
- 品質保証: コード変更後にリンター、フォーマッター、テストを自動実行
- カスタムワークフロー: 課題トラッカー、通知システム、デプロイパイプラインと連携
- チーム標準化: 組織全体でコーディング規約とベストプラクティスを遵守させる
Hooks は、特定の Cascade のアクションが発生した際に自動で実行されるシェルコマンドです。各 Hook は次の処理を行います:
- 標準入力で JSON を受け取り、実行中のアクションに関する詳細なコンテキストを取得
- Python、Bash、Node.js など、任意の実行可能ファイルでスクリプトを実行
- 終了コードと標準出力/標準エラーを通じて結果を返す
プレフック(アクション実行前に起動)では、スクリプトを終了コード 2 で終了させることでアクションをブロックできます。これにより、プレフックはセキュリティポリシーやバリデーションの実装に最適です。
Hooks は JSON ファイルで設定でき、3 つの異なるレベルに配置できます。Cascade はすべての場所から Hooks を読み込んで統合し、チームが Hook 設定の配布と管理を柔軟に行えるようにします。
システムレベルのフックは、共有開発マシンで組織全体に適用されるポリシーの実施に最適です。たとえば、セキュリティポリシーやコンプライアンス要件の順守、必須のコードレビューのワークフローを強制するために使用できます。
- macOS:
/Library/Application Support/Windsurf/hooks.json
- Linux/WSL:
/etc/windsurf/hooks.json
- Windows:
C:\ProgramData\Windsurf\hooks.json
ユーザー レベルのフックは、個人の嗜好や任意のワークフローに最適です。
- 場所:
~/.codeium/windsurf/hooks.json
ワークスペースレベルのフックにより、チームはプロジェクト固有のポリシーをコードと併せてバージョン管理できます。これには、カスタム検証ルール、プロジェクト固有のインテグレーション、チーム固有のワークフローなどが含まれます。
- 場所: ワークスペースのルートにある
.windsurf/hooks.json
3 つの場所のフックはすべて**統合(マージ)**されます。同じフックイベントが複数の場所で設定されている場合、すべてのフックは次の順序で実行されます: system → user → workspace。
以下は、hooks 設定の基本的な構成例です。
{
"hooks": {
"pre_read_code": [
{
"command": "python3 /path/to/your/script.py",
"show_output": true
}
],
"post_write_code": [
{
"command": "python3 /path/to/another/script.py",
"show_output": true
}
]
}
}
各フックは次のパラメータを受け付けます。
| Parameter | Type | Description |
command | string | 実行するシェルコマンド。引数付きの有効な実行可能ファイルであれば任意のものを指定できます。 |
show_output | boolean | フックの stdout/stderr の出力をユーザー向けの Cascade の UI に表示するかどうか。デバッグに便利です。 |
working_directory | string | 省略可。コマンドを実行するディレクトリ。既定はワークスペースのルートです。 |
Cascade は、エージェントのワークフローにおける主要なアクションを網羅する 8 つのフックイベントを提供します。
すべてのフックは、次の共通フィールドを含む JSON オブジェクトを受け取ります:
| Field | Type | Description |
agent_action_name | string | フックイベント名(例:「pre_read_code」「post_write_code」) |
trajectory_id | string | Cascade 全体の会話に対する一意の識別子 |
execution_id | string | 単一のエージェントのターンに対する一意の識別子 |
timestamp | string | フックがトリガーされた時刻の ISO 8601 形式タイムスタンプ |
tool_info | object | イベント固有の情報(フック種別によって異なる) |
以下の例では、簡潔にするため共通フィールドを省略しています。フックイベントには 8 種類の主要タイプがあります:
pre_read_code
Cascade がコードファイルを読み取る前にトリガーされます。フックが終了コード 2 で終了した場合、このアクションはブロックされることがあります。
ユースケース: ファイルアクセスの制限、読み取り操作のログ記録、権限チェック
入力 JSON:
{
"agent_action_name": "pre_read_code",
"tool_info": {
"file_path": "/Users/yourname/project/file.py"
}
}
Cascade がディレクトリを再帰的に読み込む場合、この file_path はディレクトリのパスになることがあります。
post_read_code
Cascade がコードファイルの読み取りに成功した後にトリガーされます。
ユースケース: 成功した読み取りの記録、ファイルアクセスパターンの追跡
入力 JSON:
{
"agent_action_name": "post_read_code",
"tool_info": {
"file_path": "/Users/yourname/project/file.py"
}
}
Cascade がディレクトリを再帰的に読み込む場合、この file_path はディレクトリのパスになることがあります。
pre_write_code
Cascade がコードファイルを作成または変更する前にトリガーされます。フックが終了コード 2 で終了した場合、この操作はブロックされる可能性があります。
ユースケース: 保護対象ファイルへの変更を防止、変更前にファイルをバックアップ
入力 JSON:
{
"agent_action_name": "pre_write_code",
"tool_info": {
"file_path": "/Users/yourname/project/file.py",
"edits": [
{
"old_string": "def old_function():\n pass",
"new_string": "def new_function():\n return True"
}
]
}
}
post_write_code
Cascade がコードファイルを作成または変更した後にトリガーされます。
ユースケース: リンター、フォーマッター、テストの実行/コード変更の記録
入力 JSON:
{
"agent_action_name": "post_write_code",
"tool_info": {
"file_path": "/Users/yourname/project/file.py",
"edits": [
{
"old_string": "import os",
"new_string": "import os\nimport sys"
}
]
}
}
pre_run_command
Cascade がターミナルコマンドを実行する前にトリガーされます。フックが終了コード 2 で終了した場合、このアクションはブロックされます。
ユースケース: 危険なコマンドのブロック、すべてのコマンド実行のログ取得、安全性チェックの追加
入力 JSON:
{
"agent_action_name": "pre_run_command",
"tool_info": {
"command_line": "npm install package-name",
"cwd": "/Users/yourname/project"
}
}
post_run_command
Cascade がターミナルコマンドを実行した後にトリガーされます。
ユースケース: コマンド結果の記録、後続アクションの起動
入力 JSON:
{
"agent_action_name": "post_run_command",
"tool_info": {
"command_line": "npm install package-name",
"cwd": "/Users/yourname/project"
}
}
Cascade が MCP(Model Context Protocol)ツールを呼び出す前にトリガーされます。フックがコード 2 で終了した場合、この操作はブロックされることがあります。
ユースケース: MCP の使用状況の記録、利用可能な MCP ツールの制限
入力 JSON:
{
"agent_action_name": "pre_mcp_tool_use",
"tool_info": {
"mcp_server_name": "github",
"mcp_tool_arguments": {
"owner": "code-owner",
"repo": "my-cool-repo",
"title": "バグ報告",
"body": "バグの詳細をここに記述"
},
"mcp_tool_name": "create_issue"
}
}
post_mcp_tool_use
Cascade が MCP ツールの呼び出しに成功した後にトリガーされます。
ユースケース: MCP の操作ログ、API 使用状況の追跡、MCP 結果の確認
入力 JSON:
{
"agent_action_name": "post_mcp_tool_use",
"tool_info": {
"mcp_result": "...",
"mcp_server_name": "github",
"mcp_tool_arguments": {
"owner": "code-owner",
"perPage": 1,
"repo": "my-cool-repo",
"sha": "main"
},
"mcp_tool_name": "list_commits"
}
}
フックスクリプトは終了コードで結果を伝達します:
| 終了コード | 意味 | 影響 |
0 | 成功 | アクションは通常どおり続行 |
2 | ブロッキングエラー | Cascade エージェントは stderr のエラーメッセージを表示します。pre-hook の場合、このコードでアクションをブロックします。 |
| その他 | エラー | アクションは通常どおり続行 |
pre-hook(pre_read_code、pre_write_code、pre_run_command、pre_mcp_tool_use)のみが、終了コード 2 によってアクションをブロックできます。post-hook は、アクションがすでに実行済みのためブロックできません。
show_output が true の場合、ユーザーは Cascade の UI でフックが生成した標準出力と標準エラーを確認できます。
すべての Cascade アクションをログに記録する
監査のために、Cascade が行うすべてのアクションを追跡します。
設定 (~/.codeium/windsurf/hooks.json):
{
"hooks": {
"post_read_code": [
{
"command": "python3 /Users/yourname/hooks/log_input.py",
"show_output": true
}
],
"post_write_code": [
{
"command": "python3 /Users/yourname/hooks/log_input.py",
"show_output": true
}
],
"post_run_command": [
{
"command": "python3 /Users/yourname/hooks/log_input.py",
"show_output": true
}
],
"post_mcp_tool_use": [
{
"command": "python3 /Users/yourname/hooks/log_input.py",
"show_output": true
}
]
}
}
スクリプト(log_input.py):
#!/usr/bin/env python3
import sys
import json
def main():
# 標準入力からJSONデータを読み込む
input_data = sys.stdin.read()
# JSONをパースする
try:
data = json.loads(input_data)
# フォーマット済みJSONをファイルに書き込む
with open("/Users/yourname/hooks/input.txt", "a") as f:
f.write('\n' + '='*80 + '\n')
f.write(json.dumps(data, indent=2, separators=(',', ': ')))
f.write('\n')
print(json.dumps(data, indent=2))
except json.JSONDecodeError as e:
print(f"JSONパースエラー: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
このスクリプトは、すべてのフック呼び出しをログファイルに追記し、すべてのCascadeアクションに対する監査証跡を作成します。入力データの変換や、必要に応じた独自ロジックの実行も可能です。
Cascade が特定のディレクトリ外のファイルを読み取らないようにします。
設定(~/.codeium/windsurf/hooks.json):
{
"hooks": {
"pre_read_code": [
{
"command": "python3 /Users/yourname/hooks/block_read_access.py",
"show_output": true
}
]
}
}
スクリプト(block_read_access.py):
#!/usr/bin/env python3
import sys
import json
ALLOWED_PREFIX = "/Users/yourname/my-project/"
def main():
# 標準入力からJSONデータを読み込む
input_data = sys.stdin.read()
# JSONをパースする
try:
data = json.loads(input_data)
if data.get("agent_action_name") == "pre_read_code":
tool_info = data.get("tool_info", {})
file_path = tool_info.get("file_path", "")
if not file_path.startswith(ALLOWED_PREFIX):
print(f"アクセス拒否: Cascadeは{ALLOWED_PREFIX}配下のファイルのみ読み取り可能です", file=sys.stderr)
sys.exit(2) # 終了コード2でアクションをブロック
print(f"アクセス許可: {file_path}", file=sys.stdout)
except json.JSONDecodeError as e:
print(f"JSONパースエラー: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
Cascade が許可ディレクトリ外のファイルを読み込もうとすると、このフックが処理をブロックし、エラーメッセージを表示します。
Cascade が潜在的に有害なコマンドを実行しないようにします。
設定 (~/.codeium/windsurf/hooks.json):
{
"hooks": {
"pre_run_command": [
{
"command": "python3 /Users/yourname/hooks/block_dangerous_commands.py",
"show_output": true
}
]
}
}
スクリプト(block_dangerous_commands.py):
#!/usr/bin/env python3
import sys
import json
DANGEROUS_COMMANDS = ["rm -rf", "sudo rm", "format", "del /f"]
def main():
# 標準入力からJSONデータを読み込む
input_data = sys.stdin.read()
# JSONをパースする
try:
data = json.loads(input_data)
if data.get("agent_action_name") == "pre_run_command":
tool_info = data.get("tool_info", {})
command = tool_info.get("command_line", "")
for dangerous_cmd in DANGEROUS_COMMANDS:
if dangerous_cmd in command:
print(f"Commandがブロックされました: '{dangerous_cmd}' は安全上の理由により許可されていません。", file=sys.stderr)
sys.exit(2) # 終了コード2でCommandをブロック
print(f"Commandが承認されました: {command}", file=sys.stdout)
except json.JSONDecodeError as e:
print(f"JSONパースエラー: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
このフックはCommand内の危険なパターンを検出し、実行前にブロックします。
Cascade がファイルを変更した後に、コードを自動で整形します。
設定 (~/.codeium/windsurf/hooks.json):
{
"hooks": {
"post_write_code": [
{
"command": "bash /Users/yourname/hooks/format_code.sh",
"show_output": false
}
]
}
}
スクリプト(format_code.sh):
#!/bin/bash
# 標準入力からJSONを読み込む
input=$(cat)
# jqを使用してファイルパスを抽出する
file_path=$(echo "$input" | jq -r '.tool_info.file_path')
# ファイル拡張子に基づいてフォーマットする
if [[ "$file_path" == *.py ]]; then
black "$file_path" 2>&1
echo "Pythonファイルをフォーマットしました: $file_path"
elif [[ "$file_path" == *.js ]] || [[ "$file_path" == *.ts ]]; then
prettier --write "$file_path" 2>&1
echo "JS/TSファイルをフォーマットしました: $file_path"
elif [[ "$file_path" == *.go ]]; then
gofmt -w "$file_path" 2>&1
echo "Goファイルをフォーマットしました: $file_path"
fi
exit 0
このフックは、各編集のたびにファイルの種類に応じて適切なフォーマッタを自動実行します。
Cascade Hooks の使用は自己責任です: Hooks は、あなたのユーザーアカウントに付与された権限の範囲でシェルコマンドを自動実行します。設定したコードの結果については、全てあなたの責任となります。設計不備や悪意のある hook により、ファイルの改変、データの削除、認証情報の漏えい、システムの侵害が発生するおそれがあります。
- すべての入力を検証する: 特にファイルパスやコマンドに関して、検証されていない入力 JSON を信用しないでください。
- 絶対パスを使用する: あいまいさを避けるため、hook の設定では常に絶対パスを使用してください。
- 機微情報を保護する: API キーや資格情報などの機微情報はログに記録しないでください。
- 権限を確認する: hook スクリプトに適切なファイルシステム権限が設定されていることを確認してください。
- 導入前に精査する: 設定に追加する前に、すべての hook コマンドとスクリプトをレビューしてください。
- 隔離環境でテストする: メインの開発マシンで有効化する前に、テスト環境で hook を実行してください。
- フックは高速に: フックが遅いと Cascade の応答性に影響します。実行時間は 100ms 未満を目指してください。
- 非同期処理の活用: ブロッキングを避けるため、ログはキューやデータベースに非同期で記録することを検討してください。
- 早期フィルタリング: 不要な処理を避けるため、スクリプト冒頭でアクションタイプを確認してください。
- 常に JSON を検証する: 不正な入力に優雅に対処できるよう、try-catch ブロックを使用してください。
- エラーを適切にログ出力する:
show_output が有効な場合に見えるよう、エラーは stderr に出力してください。
- 安全に失敗する: フックでエラーが発生した場合、そのアクションをブロックすべきか、処理を続行させるべきかを検討してください。
- ログから始める: データフローを把握するため、まずはシンプルなロギングフックを実装します。
show_output: true を使用する: 開発中は出力を有効にして、フックの動作を確認します。
- ブロック動作をテストする: pre-hook で終了コード 2 によってアクションが正しくブロックされることを検証します。
- すべてのコードパスを確認する: スクリプトで成功と失敗の両ケースをテストします。
Enterprise 組織では、個々のユーザーが回避できないセキュリティポリシー、コンプライアンス要件、開発標準を徹底する必要があります。Cascade Hooks は、システムレベルの設定によりこれをサポートします。これは優先され、エンドユーザーは root 権限なしに無効化できません。
必須の hooks.json 設定を、次の OS 固有の場所にデプロイしてください:
macOS:
/Library/Application Support/Windsurf/hooks.json
Linux/WSL:
Windows:
C:\ProgramData\Windsurf\hooks.json
フック用スクリプトは、対応するシステムディレクトリ(例:Unix システムでは /usr/local/share/windsurf-hooks/)に配置してください。
Enterprise の IT チームは、標準的なツールとワークフローを用いてシステムレベルのフックをデプロイできます。
モバイルデバイス管理(MDM)
- Jamf Pro(macOS)- 構成プロファイルまたはスクリプトでデプロイ
- Microsoft Intune(Windows/macOS)- PowerShell スクリプトまたはポリシー配布を使用
- Workspace ONE、Google Endpoint Management、およびその他の MDM ソリューション
構成管理
- Ansible、Puppet、Chef、SaltStack - 既存のインフラ自動化基盤を活用
- カスタムデプロイスクリプト - シェルスクリプト、PowerShell、またはお使いのツール
デプロイ後は、フックが正しくインストールされており、回避できないことを確認してください。
# システムフックの存在を確認
ls -la /etc/windsurf/hooks.json # Linux
ls -la "/Library/Application Support/Windsurf/hooks.json" # macOS
# フックの実行をテスト(Cascadeにフックの出力が表示されます)
# 開発者に該当するCascadeアクションを実行させてください
# ユーザーがシステムフックを変更できないことを確認
sudo chown root:root /etc/windsurf/hooks.json
sudo chmod 644 /etc/windsurf/hooks.json
重要: システムレベルのフックは、IT またはセキュリティチームが一元的に管理します。Windsurf はシステムレベルのパスにファイルを配置・管理しません。組織のポリシーに従い、内部チームがデプロイ、更新、コンプライアンス対応を適切に実施してください。
プロジェクト固有の規約に対しては、チームはバージョン管理でワークスペースレベルのフックを利用できます。
# リポジトリに追加する
.windsurf/
├── hooks.json
└── scripts/
└── format-check.py
# gitにコミットする
git add .windsurf/
git commit -m "コードフォーマット用のワークスペースフックを追加"
これにより、チームは開発プロセスを標準化できます。セキュリティ上重要なポリシーは必ずシステムレベルで管理し、機密情報をバージョン管理にコミット(登録)しないよう十分注意してください。