跳转到主要内容
Cascade Hooks 使你能够在 Cascade 的工作流关键阶段执行自定义 shell 命令。这个强大的扩展能力可用于记录操作、实施防护策略、运行验证检查,或与外部系统集成。
Hooks 面向需要对 Cascade 行为进行精细化控制的高级用户和 Enterprise 团队。使用它们需要具备基础的 shell 脚本知识。

你可以构建什么

Hooks 可解锁广泛的自动化与治理能力:
  • 日志与 Analytics:为合规与使用分析,跟踪 Cascade 读取的每个文件、进行的代码更改、执行的命令、用户请求或 Cascade 响应
  • 安全控制:阻止 Cascade 访问敏感文件、运行高风险命令,或处理违反策略的请求
  • 质量保障:在代码修改后自动运行 linter、formatter 或测试
  • 自定义工作流:与缺陷/事项跟踪器、通知系统或部署流水线集成
  • 团队标准化:在组织范围内强制执行编码规范与最佳实践

钩子如何工作

钩子是在发生特定 Cascade 操作时自动运行的 shell 命令。每个钩子:
  1. 通过标准输入以 JSON 格式接收上下文 (有关正在执行的操作的详细信息)
  2. 执行你的脚本——Python、Bash、Node.js,或任何可执行文件
  3. 通过退出码和输出流返回结果
对于在操作之前执行的预钩子,你的脚本可以通过以退出码 2 退出来阻止该操作。这使预钩子非常适合实施安全策略或验证检查。

配置

Hook 通过 JSON 文件进行配置,这些文件可以放在三个不同层级。Cascade 会从所有位置加载并合并 Hook,赋予 Teams 在分发和管理 Hook 配置上的更大灵活性。

系统级

系统级钩子非常适合在共享开发机器上强制执行全组织范围的策略。例如,你可以用它们来落实安全策略、合规要求或强制性的代码审查流程。Enterprise 团队还可以通过云端控制面板配置这些钩子,而无需管理本地文件。
  • macOS: /Library/Application Support/Windsurf/hooks.json
  • Linux/WSL: /etc/windsurf/hooks.json
  • Windows: C:\ProgramData\Windsurf\hooks.json

用户级

用户级钩子非常适合个性化偏好和可选的工作流程。
  • Windsurf IDE~/.codeium/windsurf/hooks.json
  • JetBrains 插件~/.codeium/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 可接受以下参数:
参数类型说明
commandstring要执行的 shell 命令。可以是任何带参数的有效可执行文件。
show_outputboolean是否在用户可见的 Cascade 界面上显示该 hook 的 stdout/stderr 输出。对调试很有帮助。
working_directorystring可选。从该目录执行命令。默认为你的工作区根目录。
关于 working_directory 参数:
  • 在多仓库工作区中,默认值为当前正在处理的仓库根目录
  • 相对路径将从默认位置 (工作区或仓库根目录) 解析
  • 支持绝对路径
  • 不支持使用 ~ 进行用户主目录展开

Hook 事件

Cascade 提供了十二个 Hook 事件,涵盖智能体工作流中的关键操作。

通用输入结构

所有 hook 都会接收一个包含以下通用字段的 JSON 对象:
字段类型描述
agent_action_namestringhook 事件名称 (例如:“pre_read_code”、“post_write_code”)
trajectory_idstring整个 Cascade 会话的唯一标识符
execution_idstring单次代理回合的唯一标识符
timestampstring触发该 hook 时的 ISO 8601 时间戳
tool_infoobject与事件相关的特定信息 (因 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"
  }
}

pre_mcp_tool_use

在 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"
  }
}

pre_user_prompt

在 Cascade 处理用户提示文本之前触发。如果该钩子以退出码 2 结束,则可能会阻止该操作。 使用场景:记录所有用户提示用于审计,拦截潜在有害或违反策略的提示 输入 JSON
{
  "agent_action_name": "pre_user_prompt",
  "tool_info": {
    "user_prompt": "你能运行 echo hello 命令吗"
  }
}
show_output 配置选项不适用于此钩子。

post_cascade_response

在 Cascade 完成对用户提示的响应之后异步触发。此 hook 会接收自上次用户输入以来的完整 Cascade 响应内容。 使用场景:记录所有 Cascade 响应用于审计、分析响应模式、将响应发送到外部系统进行合规审查 输入 JSON
{
  "agent_action_name": "post_cascade_response",
  "tool_info": {
    "response": "### 规划器响应\n\n我会帮你创建那个文件。\n\n*已创建文件 `/path/to/file.py`*\n\n### 规划器响应\n\n文件已成功创建。"
  }
}
response 字段包含自上次用户输入以来 Cascade 返回的、采用 Markdown 格式的内容。这包括规划器的响应、工具操作 (读取文件、写入、执行命令) 以及 Cascade 执行的任何其他步骤。它还包括哪些规则被触发的信息。关于如何解析规则使用情况,请参见 Tracking Triggered Rules 示例。 show_output 配置选项不适用于此钩子。
response 内容源自轨迹数据,可能包含来自你的代码库或对话的敏感信息。请根据你所在组织的安全和隐私政策来处理这些数据。

post_cascade_response_with_transcript

在 Cascade 完成对用户提示的响应之后异步触发,类似于 post_cascade_response。不同的是,这个 hook 不会内联提供 markdown 摘要,而是将整个会话的完整转录记录 (从会话开始到当前) 写入本地 JSONL 文件,并返回文件路径。 使用场景:Enterprise 审计与合规日志记录、追踪 AI 生成的贡献、将会话转录发送到外部可观测性或 Analytics 工具 输入 JSON
{
  "agent_action_name": "post_cascade_response_with_transcript",
  "tool_info": {
    "transcript_path": "/Users/yourname/.windsurf/transcripts/{trajectory_id}.jsonl"
  }
}
transcript_path 指向 ~/.windsurf/transcripts/{trajectory_id}.jsonl 里的一个 JSONL 文件。文件中的每一行都是一个 JSON 对象,表示对话中的单个步骤,包含 typestatus 字段以及该步骤特有的数据。例如:`
{"status":"done","type":"user_input","user_input":{"rules_applied":{"always_on":["my-rule.md"]},"user_response":"create a hello world file"}}
{"planner_response":{"response":"I'll create a hello world file for you."},"status":"done","type":"planner_response"}
{"code_action":{"new_content":"print('hello world')\n","path":"/path/to/file.py"},"status":"done","type":"code_action"}
{"planner_response":{"response":"I created the file for you."},"status":"done","type":"planner_response"}
转录内容包括详细的、归客户所有的数据,例如文件内容、命令输出、工具参数、搜索结果,以及已应用的规则。请注意,每一步的具体结构在未来版本中可能会发生变化,因此请确保你编写的任何 hook 消费方都具有良好的健壮性。 转录文件以 0600 权限写入。Windsurf 会自动将转录目录限制为 100 个文件,并按修改时间删除最旧的文件。 show_output 配置选项不适用于此 hook。 下表展示了 post_cascade_responsepost_cascade_response_with_transcript 这两个 hook 之间的关键差异:
post_cascade_responsepost_cascade_response_with_transcript
数据范围仅包含自上次用户输入以来的步骤从会话开始以来的完整对话
格式位于 tool_info.response 中的 Markdown 摘要位于 tool_info.transcript_path 的结构化 JSONL 文件
细节程度精简的人类可读摘要详细的机器可读数据 (文件内容、命令输出等)
交付方式通过 stdin JSON 内联传递磁盘上的文件 (~/.windsurf/transcripts/)
转录文件将包含你代码库中的敏感信息,包括文件内容、命令输出和会话历史。请根据你所在组织的安全和隐私策略来处理这些文件。

post_setup_worktree

在新的 git worktree 创建并配置完成之后触发。该钩子会在新的 worktree 目录中执行。 使用场景:将 .env 文件或其他未被 Git 跟踪的文件复制到 worktree 中、安装依赖、运行初始化脚本 环境变量
VariableDescription
$ROOT_WORKSPACE_PATH指向原始工作区的绝对路径。使用该变量可以访问文件或运行相对于原始仓库的命令。
输入 JSON
{
  "agent_action_name": "post_setup_worktree",
  "tool_info": {
    "worktree_path": "/Users/me/.windsurf/worktrees/my-repo/abmy-repo-c123",
    "root_workspace_path": "/Users/me/projects/my-repo"
  }
}

退出码

你的钩子脚本通过退出码传达结果:
退出码含义影响
0成功操作正常继续
2阻塞性错误Cascade 代理会看到来自 stderr 的错误消息。对于预钩子,这将阻止该操作。
其他任何值错误操作正常继续
只有预钩子 (pre_user_prompt、pre_read_code、pre_write_code、pre_run_command、pre_mcp_tool_use) 可以使用退出码 2 来阻止操作。后置钩子无法阻止,因为操作已完成。
请注意,如果 show_output 为 true,用户可以在 Cascade UI 中看到钩子生成的标准输出和标准错误。

示例用法

记录所有 Cascade 操作

为审计需求跟踪 Cascade 的每一次操作。 配置
{
  "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
      }
    ],
    "post_cascade_response": [
      {
        "command": "python3 /Users/yourname/hooks/log_input.py"
      }
    ]
  }
}
脚本 (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 读取指定目录之外的文件。 配置
{
  "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 执行潜在有害的命令。 配置
{
  "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()
此钩子会扫描命令中是否存在危险模式,并在执行前将其阻止。

阻止违反策略的提示词

防止用户提交违反组织策略的提示词。 配置
{
  "hooks": {
    "pre_user_prompt": [
      {
        "command": "python3 /Users/yourname/hooks/block_bad_prompts.py"
      }
    ]
  }
}
脚本 (block_bad_prompts.py) :
#!/usr/bin/env python3

import sys
import json

BLOCKED_PATTERNS = [
    "something dangerous",
    "bypass security",
    "ignore previous instructions"
]

def main():
    # 从标准输入读取 JSON 数据
    input_data = sys.stdin.read()

    # 解析 JSON
    try:
        data = json.loads(input_data)

        if data.get("agent_action_name") == "pre_user_prompt":
            tool_info = data.get("tool_info", {})
            user_prompt = tool_info.get("user_prompt", "").lower()

            for pattern in BLOCKED_PATTERNS:
                if pattern in user_prompt:
                    print(f"Prompt blocked: Contains prohibited content. The user cannot ask the agent to do bad things.", file=sys.stderr)
                    sys.exit(2)  # 退出码 2 将阻止该提示

    except json.JSONDecodeError as e:
        print(f"Error parsing JSON: {e}", file=sys.stderr)
        sys.exit(1)

if __name__ == "__main__":
    main()
此 hook 会在处理用户提示词之前先进行检查,并阻止任何包含被禁止模式的提示词。当提示词被拦截时,用户会在 Cascade 的界面中看到一条错误消息。

记录 Cascade 响应

跟踪所有 Cascade 响应,用于合规审计或 Analytics 数据分析。 配置
{
  "hooks": {
    "post_cascade_response": [
      {
        "command": "python3 /Users/yourname/hooks/log_cascade_response.py"
      }
    ]
  }
}
脚本 (log_cascade_response.py) :
#!/usr/bin/env python3

import sys
import json
from datetime import datetime

def main():
    # 从标准输入读取 JSON 数据
    input_data = sys.stdin.read()

    # 解析 JSON
    try:
        data = json.loads(input_data)

        if data.get("agent_action_name") == "post_cascade_response":
            tool_info = data.get("tool_info", {})
            cascade_response = tool_info.get("response", "")
            trajectory_id = data.get("trajectory_id", "unknown")
            timestamp = data.get("timestamp", datetime.now().isoformat())

            # 记录到文件
            with open("/Users/yourname/hooks/cascade_responses.log", "a") as f:
                f.write(f"\n{'='*80}\n")
                f.write(f"Timestamp: {timestamp}\n")
                f.write(f"Trajectory ID: {trajectory_id}\n")
                f.write(f"Response:\n{cascade_response}\n")

            print(f"Logged Cascade response for trajectory {trajectory_id}")

    except json.JSONDecodeError as e:
        print(f"Error parsing JSON: {e}", file=sys.stderr)
        sys.exit(1)

if __name__ == "__main__":
    main()
此 hook 会将每次 Cascade 响应记录到一个文件中,从而为所有 AI 生成内容创建审计轨迹。你可以在此基础上扩展,将数据发送到外部日志系统、数据库或合规平台。

跟踪已触发的规则

跟踪在 Cascade 交互过程中应用了哪些规则,用于可观测性与度量分析。 配置
{
  "hooks": {
    "post_cascade_response": [
      {
        "command": "python3 /Users/yourname/hooks/track_rules.py"
      }
    ]
  }
}
脚本 (track_rules.py) :
#!/usr/bin/env python3

import sys
import json
import re
from datetime import datetime

def extract_triggered_rules(response: str) -> dict:
    """
    从 Cascade 响应中解析触发的规则。
    规则格式为: - (Rule-Type) Triggered Rule: rule-filename.md
    """
    pattern = r"- \(([^)]+)\) Triggered Rule: (.+?)(?:\s*$)"
    rules = {}

    for match in re.finditer(pattern, response, re.MULTILINE):
        rule_type, rule_name = match.groups()
        if rule_type not in rules:
            rules[rule_type] = []
        rules[rule_type].append(rule_name)

    return rules

def main():
    input_data = sys.stdin.read()

    try:
        data = json.loads(input_data)

        if data.get("agent_action_name") == "post_cascade_response":
            response = data.get("tool_info", {}).get("response", "")
            trajectory_id = data.get("trajectory_id", "unknown")
            timestamp = data.get("timestamp", datetime.now().isoformat())

            rules = extract_triggered_rules(response)
            total_rules = sum(len(v) for v in rules.values())

            # 记录到文件
            with open("/Users/yourname/hooks/rules_usage.log", "a") as f:
                f.write(f"\n{'='*60}\n")
                f.write(f"Timestamp: {timestamp}\n")
                f.write(f"Trajectory: {trajectory_id}\n")
                f.write(f"Total rules triggered: {total_rules}\n")
                for rule_type, rule_list in rules.items():
                    if rule_list:
                        f.write(f"  {rule_type}: {', '.join(rule_list)}\n")

            print(f"Tracked {total_rules} triggered rules")

    except json.JSONDecodeError as e:
        print(f"Error parsing JSON: {e}", file=sys.stderr)
        sys.exit(1)

if __name__ == "__main__":
    main()
规则类型:
  • Always On - 始终生效的规则
  • Model Decision - 其描述会展示给 AI 模型,以便按条件决定是否应用的规则
  • Manual - 在用户输入中通过 @ 显式提及的规则
  • Global - 来自 global_rules.md 的全局规则
  • Glob - 通过匹配 glob 模式的文件访问而触发的规则
这里记录的是哪些规则被 展示 给了 AI 模型,或因文件访问而被 触发,但并不表示 AI 模型实际上 遵循 了某条规则。最近已经在对话中展示过的规则会被去重,可能要到之后才会再次出现。

编辑后运行代码格式化程序

在 Cascade 修改文件后,自动格式化代码文件。 配置
{
  "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
此钩子会在每次编辑后,基于文件类型自动运行相应的格式化工具。

设置工作树

在新建 worktree 时复制环境文件并安装依赖。 配置(位于 .windsurf/hooks.json 中):
{
  "hooks": {
    "post_setup_worktree": [
      {
        "command": "bash $ROOT_WORKSPACE_PATH/hooks/setup_worktree.sh",
        "show_output": true
      }
    ]
  }
}
脚本 (hooks/setup_worktree.sh):
#!/bin/bash

# 从原始工作区复制环境文件
if [ -f "$ROOT_WORKSPACE_PATH/.env" ]; then
    cp "$ROOT_WORKSPACE_PATH/.env" .env
    echo "已复制 .env 文件"
fi

if [ -f "$ROOT_WORKSPACE_PATH/.env.local" ]; then
    cp "$ROOT_WORKSPACE_PATH/.env.local" .env.local
    echo "已复制 .env.local 文件"
fi

# 安装依赖
if [ -f "package.json" ]; then
    npm install
    echo "已安装 npm 依赖"
fi

exit 0
此钩子可确保每个 worktree 都能自动完成所需的环境配置并安装依赖项。

最佳实践

安全

使用 Cascade Hooks 风险自负:Hooks 会在你的用户账户下、以完整权限自动执行 shell 命令。你对自己配置的代码承担全部责任。设计不当或恶意的 hooks 可能修改文件、删除数据、泄露凭证,或危及你的系统。
  • 验证所有输入:切勿在未经验证的情况下信任输入的 JSON,尤其是文件路径和命令。
  • 使用绝对路径:在 hook 配置中始终使用绝对路径以避免歧义。
  • 保护敏感数据:避免记录 API 密钥或凭证等敏感信息。
  • 审查权限:确保你的 hook 脚本具有适当的文件系统权限。
  • 部署前审计:在将每条 hook 命令和脚本加入配置前进行审计。
  • 隔离测试:在主力开发机器上启用前,先在测试环境中运行 hooks。

性能注意事项

  • 确保 hooks 足够快:缓慢的 hooks 会影响 Cascade 的响应速度。尽量将执行时间控制在 100ms 以内。
  • 使用异步操作:对于非阻塞的 hooks,可将日志异步写入队列或数据库。
  • 提前过滤:在脚本开头检查操作类型,避免不必要的处理。

错误处理

  • 务必验证 JSON:使用 try-catch 代码块,优雅地处理格式不正确的输入。
  • 正确记录错误:将错误写到 stderr,以便在启用 show_output 时可见。
  • 安全降级:如果你的 hook 遇到错误,考虑是应阻止该操作,还是允许其继续执行。

测试你的 Hooks

  1. 从日志入手:先实现一个简单的日志 hook,了解数据流向。
  2. 使用 show_output: true:在开发过程中启用输出,以便查看 hooks 的执行情况。
  3. 测试阻塞行为:验证在前置 hooks 中,退出码 2 能正确阻止操作。
  4. 检查所有代码路径:在脚本中同时测试成功与失败两种情形。

Enterprise 级分发

Enterprise 组织需要强制执行安全策略、合规要求和开发标准,且个人用户不得绕过。Cascade Hooks 支持两种 Enterprise 级分发方式:
  1. Cloud Dashboard - 通过 Windsurf Dashboard 中的 Team Settings 配置 hooks
  2. System-Level Files - 通过 MDM 或配置管理工具部署 hooks
这两种方式可以同时使用——来自所有来源的 hooks 会被合并并按顺序执行。

云端控制台配置

团队管理员可以直接在 Windsurf 控制台中配置 Cascade Hooks。 前提条件:
  • Enterprise 方案
  • TEAM_SETTINGS_UPDATE 权限
配置步骤:
  1. 在 Windsurf 控制台中进入 Team Settings
  2. 找到 Cascade Hooks 部分
  3. 以 JSON 格式输入 hooks 配置
  4. 保存更改
通过控制台配置的 hooks 会自动分发给所有团队成员,并在 Windsurf 启动时加载。云端配置的 hooks 会最先加载,其次是系统级、用户级和工作区级 hooks。
当合并多个团队配置时,hooks 会按每个 action 进行合并,而不是相互覆盖。这意味着来自所有适用团队配置的 hooks 会一同运行。

系统级文件部署

对于偏好基于文件配置或需要让 hooks 在离线环境中运行的组织,将必需的 hooks.json 配置部署到以下各操作系统的指定位置: macOS:
/Library/Application Support/Windsurf/hooks.json
Linux/WSL:
/etc/windsurf/hooks.json
Windows:
C:\ProgramData\Windsurf\hooks.json
将你的 hook 脚本放在相应的系统目录中 (例如,Unix 系统为 /usr/local/share/windsurf-hooks/) 。 系统级 hook 的优先级高于用户级和工作区级 hook,终端用户若无 root 权限将无法将其禁用。

MDM 和配置管理

Enterprise IT 团队可以使用标准工具部署系统级钩子: 移动设备管理 (MDM)
  • Jamf Pro (macOS) - 通过配置描述文件或脚本进行部署
  • Microsoft Intune (Windows/macOS) - 使用 PowerShell 脚本或策略进行部署
  • Workspace ONEGoogle Endpoint Management 以及其他 MDM 解决方案
配置管理
  • AnsiblePuppetChefSaltStack - 利用你现有的基础设施自动化
  • 自定义部署脚本 - 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 "添加代码格式化的工作区钩子"
这有助于团队统一开发实践。请将关键的安全策略保留在云端或系统层面,并避免将任何敏感信息提交到版本控制中。

其他资源