Cascade Hooks 使你能够在 Cascade 的工作流关键阶段执行自定义 shell 命令。这个强大的扩展能力可用于记录操作、实施防护策略、运行验证检查,或与外部系统集成。
测试版发布:Cascade Hooks 目前处于测试阶段并在积极开发中。功能和 API 可能会变更。请联系 Windsurf Support 提供反馈或报告问题。
Hooks 面向需要对 Cascade 行为进行精细化控制的高级用户和 Enterprise 团队。使用它们需要具备基础的 shell 脚本知识。
Hooks 可解锁广泛的自动化与治理能力:
- 日志与 Analytics:为合规与使用分析,跟踪 Cascade 读取的每个文件、进行的代码更改或执行的命令
- 安全控制:阻止 Cascade 访问敏感文件或运行高风险命令
- 质量保障:在代码修改后自动运行 linter、formatter 或测试
- 自定义工作流:与缺陷/事项跟踪器、通知系统或部署流水线集成
- 团队标准化:在组织范围内强制执行编码规范与最佳实践
钩子是在发生特定 Cascade 操作时自动运行的 shell 命令。每个钩子:
- 通过标准输入以 JSON 格式接收上下文(有关正在执行的操作的详细信息)
- 执行你的脚本——Python、Bash、Node.js,或任何可执行文件
- 通过退出码和输出流返回结果
对于在操作之前执行的预钩子,你的脚本可以通过以退出码 2 退出来阻止该操作。这使预钩子非常适合实施安全策略或验证检查。
Hook 通过 JSON 文件进行配置,这些文件可以放在三个不同层级。Cascade 会从所有位置加载并合并 Hook,赋予 Teams 在分发和管理 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
来自这三个位置的钩子会被合并。如果同一钩子事件在多个位置配置,所有钩子将按顺序执行: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
}
]
}
}
每个 hook 可接受以下参数:
| 参数 | 类型 | 说明 |
command | string | 要执行的 shell 命令。可以是任何带参数的有效可执行文件。 |
show_output | boolean | 是否在用户可见的 Cascade 界面上显示该 hook 的 stdout/stderr 输出。对调试很有帮助。 |
working_directory | string | 可选。从该目录执行命令。默认为你的工作区根目录。 |
Cascade 提供了八个 Hook 事件,涵盖智能体工作流中的关键操作。
所有 hook 都会接收一个包含以下通用字段的 JSON 对象:
| 字段 | 类型 | 描述 |
agent_action_name | string | hook 事件名称(例如:“pre_read_code”、“post_write_code”) |
trajectory_id | string | 整个 Cascade 会话的唯一标识符 |
execution_id | string | 单次代理回合的唯一标识符 |
timestamp | string | 触发该 hook 时的 ISO 8601 时间戳 |
tool_info | object | 与事件相关的特定信息(因 hook 类型而异) |
在以下示例中,为简洁起见省略了通用字段。共有八类主要的 hook 事件:
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": "Bug 报告",
"body": "Bug 描述信息"
},
"mcp_tool_name": "create_issue"
}
}
post_mcp_tool_use
在 Cascade 成功调用 MCP(模型上下文协议,Model Context Protocol)工具之后触发。
使用场景:记录 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_read_code、pre_write_code、pre_run_command、pre_mcp_tool_use)可以使用退出码 2 来阻止操作。后置钩子无法阻止,因为操作已完成。
请注意,如果 show_output 为 true,用户可以在 Cascade UI 中看到钩子生成的标准输出和标准错误。
为审计需求跟踪 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()
该脚本会将每次 hook 调用附加到日志文件中,形成所有 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"命令已阻止:出于安全考虑,不允许使用 '{dangerous_cmd}'。", file=sys.stderr)
sys.exit(2) # 退出代码 2 会阻止该命令
print(f"命令已批准: {command}", file=sys.stdout)
except json.JSONDecodeError as e:
print(f"解析 JSON 时出错: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
此钩子会扫描命令中的危险模式,并在执行前予以拦截。
在 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 会在你的用户账户下、以完整权限自动执行 shell 命令。你对自己配置的代码承担全部责任。设计不当或恶意的 hooks 可能修改文件、删除数据、泄露凭证,或危及你的系统。
- 验证所有输入:切勿在未经验证的情况下信任输入的 JSON,尤其是文件路径和命令。
- 使用绝对路径:在 hook 配置中始终使用绝对路径以避免歧义。
- 保护敏感数据:避免记录 API 密钥或凭证等敏感信息。
- 审查权限:确保你的 hook 脚本具有适当的文件系统权限。
- 部署前审计:在将每条 hook 命令和脚本加入配置前进行审计。
- 隔离测试:在主力开发机器上启用前,先在测试环境中运行 hooks。
- 确保 hooks 足够快:缓慢的 hooks 会影响 Cascade 的响应速度。尽量将执行时间控制在 100ms 以内。
- 使用异步操作:对于非阻塞的 hooks,可将日志异步写入队列或数据库。
- 提前过滤:在脚本开头检查操作类型,避免不必要的处理。
- 务必验证 JSON:使用 try-catch 代码块,优雅地处理格式不正确的输入。
- 正确记录错误:将错误写到
stderr,以便在启用 show_output 时可见。
- 安全降级:如果你的 hook 遇到错误,考虑是应阻止该操作,还是允许其继续执行。
- 从日志入手:先实现一个简单的日志 hook,了解数据流向。
- 使用
show_output: true:在开发过程中启用输出,以便查看 hooks 的执行情况。
- 测试阻塞行为:验证在前置 hooks 中,退出码 2 能正确阻止操作。
- 检查所有代码路径:在脚本中同时测试成功与失败两种情形。
Enterprise 组织需要强制执行安全策略、合规要求和开发标准,且个人用户不得绕过。Cascade Hooks 通过系统级配置实现此目标;该配置具有最高优先级,且除非拥有 root 权限,终端用户无法禁用。
将必需的 hooks.json 配置部署到以下各操作系统的指定位置:
macOS:
/Library/Application Support/Windsurf/hooks.json
Linux/WSL:
Windows:
C:\ProgramData\Windsurf\hooks.json
将你的 hook 脚本放在相应的系统目录中(例如,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 - 利用你现有的基础设施自动化
- 自定义部署脚本 - Shell 脚本、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 "添加代码格式化的工作区钩子"
这有助于团队统一开发实践。请务必将关键的安全策略保留在系统层面,并避免将任何敏感信息提交到版本控制中。