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

# Cascade 钩子

> 通过前置和后置钩子，在 Cascade 工作流程的关键环节执行自定义 shell 命令，用于日志记录、安全控制、验证和企业级治理。

Cascade 钩子让你能够在 Cascade 工作流程的关键环节执行自定义 shell 命令。这一强大的扩展能力可用于记录操作、实施护栏、运行验证检查，或与外部系统集成。

<Note>
  钩子专为需要细粒度控制 Cascade 行为的高级用户和企业团队设计。使用它需要具备基本的 shell 脚本知识。
</Note>

<div id="what-you-can-build">
  ## 你可以实现什么
</div>

钩子可实现广泛的自动化和治理能力：

* **日志记录与分析**：跟踪每次文件读取、代码更改、执行的命令、用户提示或 Cascade 响应，用于合规和用量分析
* **安全控制**：阻止 Cascade 访问敏感文件、运行危险命令或处理违反策略的提示
* **质量保证**：在代码修改后自动运行 lint 工具、格式化工具或测试
* **自定义工作流程**：与问题跟踪系统、通知系统或部署流水线集成
* **团队标准化**：在你的组织内统一执行编码标准和最佳实践

<div id="how-hooks-work">
  ## 钩子如何工作
</div>

钩子是在特定 Cascade 操作发生时自动运行的 shell 命令。每个钩子都会：

1. **接收上下文** (有关当前执行操作的详细信息) ，以 JSON 格式通过标准输入传入
2. **执行你的脚本**——Python、Bash、Node.js 或任何可执行程序
3. **返回结果**，通过退出码和输出流返回

对于 **前置钩子** (在操作执行前运行) ，你的脚本可以通过以退出码 `2` 退出来**阻止该操作**。因此，前置钩子非常适合用于实现安全策略或执行验证检查。

<div id="configuration">
  ## 配置
</div>

钩子通过 JSON 文件进行配置，这些文件可放在三个不同层级。Cascade 会从所有位置按层级加载并合并钩子，让团队能够灵活地分发和管理钩子配置。

<div id="system-level">
  #### 系统级
</div>

系统级钩子非常适合在共享开发机器上执行组织范围内的策略。例如，你可以用它们来落实安全策略、合规要求或强制性的代码审查工作流程。Enterprise 团队也可以通过[云仪表板](#cloud-dashboard-configuration)配置钩子，而无需管理本地文件。

* **macOS**: `/Library/Application Support/Windsurf/hooks.json`
* **Linux/WSL**: `/etc/windsurf/hooks.json`
* **Windows**: `C:\ProgramData\Windsurf\hooks.json`

<div id="user-level">
  #### 用户级
</div>

用户级钩子非常适合用于个人偏好设置和可选工作流程。

* **Devin Desktop IDE**: `~/.codeium/windsurf/hooks.json`
* **JetBrains Plugin**: `~/.codeium/hooks.json`

<div id="workspace-level">
  #### 工作区级
</div>

工作区级钩子让团队能够将项目特定的策略与代码一同纳入版本控制。它们可以包含自定义校验规则、项目专属的集成，或团队专属的工作流程。

* **位置**：工作区根目录中的 `.windsurf/hooks.json`

<Note>
  来自这三个位置的钩子会**合并**。如果同一钩子事件在多个位置都已配置，所有钩子都会按顺序执行：system → user → workspace。
</Note>

<div id="basic-structure">
  ### 基本结构
</div>

下面是钩子配置的基本结构示例：

```json theme={null}
{
  "hooks": {
    "pre_read_code": [
      {
        "command": "python3 /path/to/your/script.py",
        "powershell": "python3 C:\\path\\to\\your\\script.py",
        "show_output": true
      }
    ],
    "post_write_code": [
      {
        "command": "python3 /path/to/another/script.py",
        "show_output": true
      }
    ]
  }
}
```

在此示例中，`pre_read_code` 同时指定了 macOS/Linux 命令和 Windows PowerShell 命令。`post_write_code` 钩子仅指定了 `command`，因此它会在 macOS/Linux 上运行，并在 Windows 上自动切换为 PowerShell。

<div id="configuration-options">
  ### 配置选项
</div>

每个钩子都支持以下参数：

| 参数                  | 类型      | 说明                                                                                         |
| ------------------- | ------- | ------------------------------------------------------------------------------------------ |
| `command`           | string  | 在 **macOS/Linux** 上执行的 shell 命令 (通过 `bash -c` 运行) 。`command` 和 `powershell` 至少必须指定一个。      |
| `powershell`        | string  | 可选。在 **Windows** 上执行的命令 (通过 `powershell -Command` 运行) 。如果在 Windows 上未指定，则会回退为使用 `command`。 |
| `show_output`       | boolean | 是否在面向用户的 Cascade UI 中显示该钩子的 stdout/stderr 输出。可用于调试。                                        |
| `working_directory` | string  | 可选。执行命令时使用的目录。默认为你的工作区根目录。                                                                 |

<div id="cross-platform-behavior">
  #### 跨平台行为
</div>

`command` 和 `powershell` 字段让你可以在同一份配置中为不同平台定义合适的命令。这对于同时使用 macOS/Linux 和 Windows 的团队尤其有用。

| Platform    | `command` set | `powershell` set | Result                                   |
| ----------- | :-----------: | :--------------: | ---------------------------------------- |
| macOS/Linux |       ✓       |     (ignored)    | 通过 `bash -c` 运行 `command`                |
| macOS/Linux |       ✗       |         ✓        | 静默跳过该钩子                                  |
| Windows     |       ✓       |         ✗        | 回退为通过 `powershell -Command` 运行 `command` |
| Windows     |       ✗       |         ✓        | 通过 `powershell -Command` 运行 `powershell` |
| Windows     |       ✓       |         ✓        | 通过 `powershell -Command` 运行 `powershell` |
| Any         |       ✗       |         ✗        | 验证错误                                     |

<Note>
  **关于 `working_directory` 参数：**

  * 在多仓库工作区中，默认位置是当前正在处理的仓库根目录
  * 相对路径从默认位置 (工作区或仓库根目录) 开始解析
  * 支持绝对路径
  * 不支持使用 `~` 展开主目录
</Note>

<div id="hook-events">
  ## 钩子事件
</div>

Cascade 提供了 12 种钩子事件，涵盖 Agent 工作流程中最关键的操作。

<div id="common-input-structure">
  ### 通用输入结构
</div>

所有钩子都会接收一个 JSON 对象，其中包含以下通用字段：

| Field               | Type   | Description                                                                                                                                       |
| ------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `agent_action_name` | string | 钩子事件名称 (例如 `"pre_read_code"`、`"post_write_code"`)                                                                                                 |
| `trajectory_id`     | string | 整个 Cascade 对话的唯一标识符                                                                                                                               |
| `execution_id`      | string | 单次 Agent 回合的唯一标识符                                                                                                                                 |
| `timestamp`         | string | 钩子触发时的 ISO 8601 时间戳                                                                                                                               |
| `model_name`        | string | 与本次钩子调用关联的、便于识别的模型名称 (例如 `"Claude Sonnet 4"`、`"GPT 4.1"`) 。这与 Cascade 模型选择器中显示的标签相同。随着 Devin Desktop 更新模型显示名称，该值可能会随时间变化。当无法确定模型时，设为 `"Unknown"`。 |
| `tool_info`         | object | 特定于事件的信息 (因钩子类型而异)                                                                                                                                |

在下面的示例中，为简洁起见，省略了这些通用字段。钩子事件主要分为十二种类型：

<div id="pre_read_code">
  ### pre\_read\_code
</div>

在 Cascade 读取代码文件**之前**触发。如果该钩子以退出码 2 结束，则可能会阻止此操作。

**用例**：限制文件访问、记录读取操作、检查权限

**输入 JSON**：

```json theme={null}
{
  "agent_action_name": "pre_read_code",
  "tool_info": {
    "file_path": "/Users/yourname/project/file.py"
  }
}
```

当 Cascade 递归读取目录时，此 `file_path` 也可能是目录路径。

<div id="post_read_code">
  ### post\_read\_code
</div>

在 Cascade 成功读取代码文件后触发。

**用例**：记录成功的读取操作，跟踪文件访问情况

**输入 JSON**：

```json theme={null}
{
  "agent_action_name": "post_read_code",
  "tool_info": {
    "file_path": "/Users/yourname/project/file.py"
  }
}
```

当 Cascade 递归读取目录时，此 `file_path` 也可能是目录路径。

<div id="pre_write_code">
  ### pre\_write\_code
</div>

在 Cascade 写入或修改代码文件**之前**触发。如果该钩子以退出码 2 结束，可能会阻止此次操作。

**用例**：阻止修改受保护文件、在更改前备份文件

**输入 JSON**：

```json theme={null}
{
  "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"
      }
    ]
  }
}
```

<div id="post_write_code">
  ### post\_write\_code
</div>

在 Cascade 写入或修改代码文件**后**触发。

**用例**：运行代码检查工具、格式化工具或测试；记录代码修改

**输入 JSON**：

```json theme={null}
{
  "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"
      }
    ]
  }
}
```

<div id="pre_run_command">
  ### pre\_run\_command
</div>

在 Cascade 执行终端命令**之前**触发。如果该钩子以退出代码 2 结束，则可能会阻止该操作。

**用例**：阻止危险命令、记录所有命令执行、添加安全检查

**输入 JSON**：

```json theme={null}
{
  "agent_action_name": "pre_run_command",
  "tool_info": {
    "command_line": "npm install package-name",
    "cwd": "/Users/yourname/project"
  }
}
```

<div id="post_run_command">
  ### post\_run\_command
</div>

在 Cascade 执行终端命令**后**触发。

**用例**：记录命令结果，触发后续操作

**输入 JSON**：

```json theme={null}
{
  "agent_action_name": "post_run_command",
  "tool_info": {
    "command_line": "npm install package-name",
    "cwd": "/Users/yourname/project"
  }
}
```

<div id="pre_mcp_tool_use">
  ### pre\_mcp\_tool\_use
</div>

在 Cascade 调用 MCP (Model Context Protocol) 工具**之前**触发。如果该钩子以代码 2 退出，则可能会阻止该操作执行。

**用例**：记录 MCP 使用情况，限制可使用的 MCP 工具

**输入 JSON**：

```json theme={null}
{
  "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 report",
      "body": "Description of the bug here"
    },
    "mcp_tool_name": "create_issue"
  }
}
```

<div id="post_mcp_tool_use">
  ### post\_mcp\_tool\_use
</div>

在 Cascade 成功调用 MCP 工具**后**触发。

**用例**：记录 MCP 操作、跟踪 API 用量、查看 MCP 结果

**输入 JSON**：

```json theme={null}
{
  "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"
  }
}
```

<div id="pre_user_prompt">
  ### pre\_user\_prompt
</div>

在 Cascade 处理用户提示文本**之前**触发。如果该钩子以退出代码 2 结束，可能会阻止此次操作。

**用例**：记录所有用户提示以供审计；阻止可能有害或违反策略的提示

**输入 JSON**：

```json theme={null}
{
  "agent_action_name": "pre_user_prompt",
  "tool_info": {
    "user_prompt": "can you run the echo hello command"
  }
}
```

`show_output` 配置选项对此钩子不生效。

<div id="post_cascade_response">
  ### post\_cascade\_response
</div>

在 Cascade 完成对用户提示的响应**后**异步触发。此钩子会接收自用户上次输入以来的完整 Cascade 响应。

**用例**：记录所有 Cascade 响应以供审计、分析响应模式、将响应发送到外部系统进行合规审查

**输入 JSON**：

```json theme={null}
{
  "agent_action_name": "post_cascade_response",
  "tool_info": {
    "response": "### Planner Response\n\nI'll help you create that file.\n\n*Created file `/path/to/file.py`*\n\n### Planner Response\n\nThe file has been created successfully."
  }
}
```

`response` 字段包含自上次用户输入以来 Cascade 响应中的 Markdown 格式内容。其中包括 planner 响应、工具操作 (文件读取、写入、命令) 以及 Cascade 执行的任何其他步骤。它还包含哪些[规则](/zh/desktop/cascade/memories)被触发的信息。有关如何解析规则使用情况，请参阅[跟踪已触发规则](#tracking-triggered-rules)示例。

`show_output` 配置选项不适用于此钩子。

<Warning>
  `response` 内容派生自轨迹数据，可能包含你的代码库或对话中的敏感信息。请按照你的组织的安全和隐私政策处理这些数据。
</Warning>

<div id="post_cascade_response_with_transcript">
  ### post\_cascade\_response\_with\_transcript
</div>

在 Cascade 完成对用户提示的响应**后**异步触发，类似于 `post_cascade_response`。不同的是，此钩子不会直接以内联方式提供 Markdown 摘要，而是将完整的对话转录 (从对话开始时算起) 写入本地 JSONL 文件，并提供该文件路径。

**用例**：企业级审计与合规日志记录、跟踪 AI 生成的贡献、将转录内容提供给外部可观测性或分析工具

**输入 JSON**:

```json theme={null}
{
  "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](https://jsonlines.org/) 文件。每一行都是一个 JSON 对象，表示对话中的一个步骤，包含 `type` 和 `status` 字段，以及该步骤特有的数据。例如：

```jsonl theme={null}
{"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"}
```

该转录记录包含详细的客户自有数据，例如文件内容、命令输出、工具参数、搜索结果，以及已应用的[rules](/zh/desktop/cascade/memories)。请注意，每个步骤的具体结构在未来版本中可能会发生变化，因此请确保所有使用此钩子的程序都具备良好的兼容性。

转录文件将以 `0600` 权限写入。Devin Desktop 会自动将 transcripts 目录限制为 100 个文件，并按修改时间删除最旧的文件。

`show_output` 配置选项不适用于此钩子。

下表展示了 `post_cascade_response` 和 `post_cascade_response_with_transcript` 钩子之间的主要区别：

|                  | `post_cascade_response`             | `post_cascade_response_with_transcript`      |
| ---------------- | ----------------------------------- | -------------------------------------------- |
| **Data scope**   | 仅包含自上次用户输入以来的步骤                     | 从对话开始起的完整对话                                  |
| **Format**       | `tool_info.response` 中的 Markdown 摘要 | 位于 `tool_info.transcript_path` 的结构化 JSONL 文件 |
| **Detail level** | 经过压缩、便于人工阅读的摘要                      | 详细、可供机器读取的数据 (文件内容、命令输出等)                    |
| **Delivery**     | 通过 stdin JSON 内联传递                  | 磁盘上的文件 (`~/.windsurf/transcripts/`)          |

<Warning>
  转录文件会包含你的代码库中的敏感信息，包括文件内容、命令输出和对话历史。请按照你的组织的安全和隐私政策处理这些文件。
</Warning>

<div id="post_setup_worktree">
  ### post\_setup\_worktree
</div>

在新的 [git worktree](./worktrees) 创建并配置完成**后**触发。该钩子会在新的 **worktree** 目录中执行。

**用例**：将 `.env` 文件或其他未跟踪文件复制到 worktree 中，安装依赖，运行初始化脚本

**环境变量**：

| 变量                     | 描述                                        |
| ---------------------- | ----------------------------------------- |
| `$ROOT_WORKSPACE_PATH` | 原始工作区的绝对路径。可用它访问原始代码仓库中的文件，或基于原始代码仓库运行命令。 |

**输入 JSON**：

```json theme={null}
{
  "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"
  }
}
```

<div id="exit-codes">
  ## 退出码
</div>

你的钩子脚本通过退出码传递执行结果：

| Exit Code | Meaning | Effect                                               |
| --------- | ------- | ---------------------------------------------------- |
| `0`       | 成功      | 操作正常继续                                               |
| `2`       | 阻塞性错误   | Cascade Agent 会看到来自 stderr 的错误信息。对于前置钩子，这会**阻止**该操作。 |
| Any other | 错误      | 操作正常继续                                               |

<Warning>
  只有 **前置钩子** (pre\_user\_prompt、pre\_read\_code、pre\_write\_code、pre\_run\_command、pre\_mcp\_tool\_use) 可以通过退出码 2 阻止操作。后置钩子无法阻止，因为操作已发生。
</Warning>

请注意，如果 `show_output` 为 true，用户可以在 Cascade UI 中看到钩子生成的所有标准输出和标准错误。

<div id="example-use-cases">
  ## 用例示例
</div>

<div id="logging-all-cascade-actions">
  ### 记录 Cascade 的所有操作
</div>

记录 Cascade 执行的每一项操作，便于审计。

**配置**：

```json theme={null}
{
  "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`)：

```python theme={null}
#!/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"Error parsing JSON: {e}", file=sys.stderr)
        sys.exit(1)

if __name__ == "__main__":
    main()
```

此脚本会将每次钩子调用都追加到日志文件中，为所有 Cascade 操作留下审计记录。你可以按需转换输入数据或执行自定义逻辑。

<div id="restricting-file-access">
  ### 限制文件访问
</div>

防止 Cascade 读取指定目录之外的文件。

**配置**:

```json theme={null}
{
  "hooks": {
    "pre_read_code": [
      {
        "command": "python3 /Users/yourname/hooks/block_read_access.py",
        "show_output": true
      }
    ]
  }
}
```

**脚本** (`block_read_access.py`)：

```python theme={null}
#!/usr/bin/env python3

import sys
import json

ALLOWED_PREFIX = "/Users/yourname/my-project/"

def main():
    # 从 stdin 读取 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"Access denied: Cascade is only allowed to read files under {ALLOWED_PREFIX}", file=sys.stderr)
                sys.exit(2)  # 退出码 2 会阻止该操作
            
            print(f"Access granted: {file_path}", file=sys.stdout)

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

if __name__ == "__main__":
    main()
```

当 Cascade 尝试读取允许目录之外的文件时，此钩子会阻止该操作，并显示错误信息。

<div id="blocking-dangerous-commands">
  ### 阻止危险命令
</div>

防止 Cascade 执行可能带来风险的命令。

**配置**:

```json theme={null}
{
  "hooks": {
    "pre_run_command": [
      {
        "command": "python3 /Users/yourname/hooks/block_dangerous_commands.py",
        "show_output": true
      }
    ]
  }
}
```

**脚本** (`block_dangerous_commands.py`):

```python theme={null}
#!/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 blocked: '{dangerous_cmd}' is not allowed for safety reasons.", file=sys.stderr)
                    sys.exit(2)  # 退出码 2 表示阻止该命令
            
            print(f"Command approved: {command}", file=sys.stdout)

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

if __name__ == "__main__":
    main()
```

此钩子会扫描命令中的危险模式，并在执行前将其拦截。

<div id="blocking-policy-violating-prompts">
  ### 拦截违反组织政策的提示
</div>

防止用户提交违反组织政策的提示。

**配置**:

```json theme={null}
{
  "hooks": {
    "pre_user_prompt": [
      {
        "command": "python3 /Users/yourname/hooks/block_bad_prompts.py"
      }
    ]
  }
}
```

**脚本** (`block_bad_prompts.py`):

```python theme={null}
#!/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()
```

此钩子会在处理用户提示之前进行检查，并拦截任何包含禁止模式的提示。提示被拦截后，用户会在 Cascade UI 中看到一条错误消息。

<div id="logging-cascade-responses">
  ### 记录 Cascade 响应
</div>

记录所有 Cascade 响应，以便进行合规审计或分析。

**配置**:

```json theme={null}
{
  "hooks": {
    "post_cascade_response": [
      {
        "command": "python3 /Users/yourname/hooks/log_cascade_response.py"
      }
    ]
  }
}
```

**脚本** (`log_cascade_response.py`):

```python theme={null}
#!/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()
```

这个钩子会将每次 Cascade 响应记录到文件中，从而为所有 AI 生成的内容建立审计追踪记录。你还可以进一步扩展它，将数据发送到外部日志系统、数据库或合规平台。

<div id="tracking-triggered-rules">
  ### 跟踪已触发的规则
</div>

跟踪在 Cascade 交互过程中应用了哪些 [规则](/zh/desktop/cascade/memories)，以便进行可观测性分析和指标监控。

**配置**：

```json theme={null}
{
  "hooks": {
    "post_cascade_response": [
      {
        "command": "python3 /Users/yourname/hooks/track_rules.py"
      }
    ]
  }
}
```

**脚本** (`track_rules.py`)：

```python theme={null}
#!/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` - 将规则说明展示给模型，由模型决定是否应用的规则
* `Manual` - 在用户输入中通过 @ 明确提及的规则
* `Global` - 来自 `global_rules.md` 的全局规则
* `Glob` - 因访问匹配 glob 模式的文件而触发的规则

<Note>
  这里记录的是哪些规则被*呈现*给了模型，或因文件访问而被*触发*，但并不表示模型实际*遵循*了某条规则。对话中近期已经展示过的规则会被去重，可能不会立即再次出现。
</Note>

<div id="running-code-formatters-after-edits">
  ### 编辑后运行代码格式化工具
</div>

在 Cascade 修改代码文件后，自动将其格式化。

**配置**：

```json theme={null}
{
  "hooks": {
    "post_write_code": [
      {
        "command": "bash /Users/yourname/hooks/format_code.sh",
        "show_output": false
      }
    ]
  }
}
```

**脚本** (`format_code.sh`):

```bash theme={null}
#!/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 "Formatted Python file: $file_path"
elif [[ "$file_path" == *.js ]] || [[ "$file_path" == *.ts ]]; then
    prettier --write "$file_path" 2>&1
    echo "Formatted JS/TS file: $file_path"
elif [[ "$file_path" == *.go ]]; then
    gofmt -w "$file_path" 2>&1
    echo "Formatted Go file: $file_path"
fi

exit 0
```

此钩子会在每次编辑后根据文件类型自动运行相应的格式化工具。

<div id="setting-up-worktrees">
  ### 配置 worktree
</div>

在创建新的 worktree 时，复制环境文件并安装依赖。

**配置** (位于 `.windsurf/hooks.json`) ：

```json theme={null}
{
  "hooks": {
    "post_setup_worktree": [
      {
        "command": "bash $ROOT_WORKSPACE_PATH/hooks/setup_worktree.sh",
        "show_output": true
      }
    ]
  }
}
```

**脚本** (`hooks/setup_worktree.sh`):

```bash theme={null}
#!/bin/bash

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

if [ -f "$ROOT_WORKSPACE_PATH/.env.local" ]; then
    cp "$ROOT_WORKSPACE_PATH/.env.local" .env.local
    echo "Copied .env.local file"
fi

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

exit 0
```

此钩子可确保每个工作树都会自动完成所需的环境配置并安装依赖。

<div id="best-practices">
  ## 最佳实践
</div>

<div id="security">
  ### 安全
</div>

<Warning>
  **使用 Cascade 钩子的风险由你自行承担**：钩子会以你的用户账户的全部权限自动执行 shell 命令。你需对自己配置的代码负全部责任。设计不当或恶意的钩子可能会修改文件、删除数据、泄露凭据，或危及你的系统安全。
</Warning>

* **验证所有输入**：绝不要在未经验证的情况下信任输入的 JSON，尤其是涉及文件路径和命令时。
* **使用绝对路径**：始终在钩子配置中使用绝对路径，以避免歧义。
* **保护敏感数据**：避免记录 API key 或凭据等敏感信息。
* **检查权限**：确保你的钩子脚本具有适当的文件系统权限。
* **部署前审计**：将任何钩子命令和脚本加入配置前，都要先审查。
* **隔离测试**：在主开发机器上启用钩子之前，先在测试环境中运行。

<div id="performance-considerations">
  ### 性能注意事项
</div>

* **保持钩子高效**：过慢的钩子会影响 Cascade 的响应速度。尽量将执行时间控制在 100 毫秒以内。
* **使用异步操作**：对于非阻塞钩子，可考虑以异步方式将日志写入队列或数据库。
* **尽早过滤**：在脚本开头检查 action 类型，避免不必要的处理。

<div id="error-handling">
  ### 错误处理
</div>

* **始终验证 JSON**：使用 try-catch 块，妥善处理格式不正确的输入。
* **正确记录错误**：将错误写入 `stderr`，以便在启用 `show_output` 时能够看到。
* **安全地失败**：如果你的钩子遇到错误，请考虑是应该阻止该 action，还是允许其继续执行。

<div id="testing-your-hooks">
  ### 测试你的钩子
</div>

1. **从日志记录开始**：先实现一个简单的日志记录钩子，借此了解数据流。
2. **使用 `show_output: true`**：在开发过程中启用输出，以查看你的钩子在执行什么操作。
3. **测试拦截行为**：验证退出码 2 是否会在前置钩子中正确拦截操作。
4. **检查所有代码路径**：在脚本中同时测试成功和失败两种情况。

<div id="enterprise-distribution">
  ## 企业级分发
</div>

企业组织需要强制执行安全策略、合规要求和开发标准，而这些要求不能被个人用户绕过。Cascade Hooks 支持两种企业级分发方式：

1. **云端仪表板** - 通过 Devin Desktop 仪表板中的团队设置配置钩子
2. **系统级文件** - 通过 MDM 或配置管理工具部署钩子

这两种方式可以同时使用——来自所有来源的钩子会合并后按顺序执行。

<div id="cloud-dashboard-configuration">
  ### 云端仪表板配置
</div>

团队 Admin 可以直接在 Devin Desktop 仪表板中配置 Cascade 钩子。

**要求：**

* Enterprise 套餐
* `TEAM_SETTINGS_UPDATE` 权限

**配置方法：**

1. 在 Devin Desktop 仪表板中前往 **团队设置**
2. 找到 **Cascade Hooks** 部分
3. 以 JSON 格式输入你的钩子配置
4. 保存更改

通过仪表板配置的钩子会自动下发给所有团队成员，并在 Devin Desktop 启动时加载。云端配置的钩子会最先加载，随后依次加载系统级、用户级和工作区级钩子。

<Note>
  合并多个团队配置时，钩子会按 action 合并，而不是相互覆盖。这意味着所有适用团队配置中的钩子都会一起运行。
</Note>

<div id="system-level-file-deployment">
  ### 系统级文件部署
</div>

对于偏好文件式配置，或需要让钩子支持离线运行的组织，请将必需的 `hooks.json` 配置文件部署到以下操作系统对应的位置：

**macOS:**

```
/Library/Application Support/Windsurf/hooks.json
```

**Linux/WSL：**

```
/etc/windsurf/hooks.json
```

**Windows：**

```
C:\ProgramData\Windsurf\hooks.json
```

将你的钩子脚本放到对应的系统目录中 (例如，Unix 系统中的 `/usr/local/share/windsurf-hooks/`) 。

系统级钩子的优先级高于用户级和工作区钩子，最终用户在没有 root 权限的情况下无法将其禁用。

<div id="mdm-and-configuration-management">
  #### MDM 和配置管理
</div>

企业 IT 团队可以使用常用工具部署系统级钩子：

**移动设备管理 (MDM) **

* **Jamf Pro** (macOS) - 通过配置描述文件或脚本部署
* **Microsoft Intune** (Windows/macOS) - 使用 PowerShell 脚本或策略下发
* **Workspace ONE**、**Google Endpoint Management** 以及其他 MDM 解决方案

**配置管理**

* **Ansible**、**Puppet**、**Chef**、**SaltStack** - 利用你现有的基础架构自动化能力
* **自定义部署脚本** - Shell 脚本、PowerShell，或你常用的工具

<div id="verification-and-auditing">
  #### 验证与审计
</div>

部署后，请确认钩子已正确安装：

```bash theme={null}
# 验证系统钩子是否存在
ls -la /etc/windsurf/hooks.json  # Linux
ls -la "/Library/Application Support/Windsurf/hooks.json"  # macOS

# 测试钩子执行（应在 Cascade 中看到钩子输出）
# 让开发人员触发相关的 Cascade action

# 验证用户无法修改系统钩子
sudo chown root:root /etc/windsurf/hooks.json
sudo chmod 644 /etc/windsurf/hooks.json
```

<Note>
  **重要**：系统级钩子完全由你的 IT 或安全团队负责管理。Devin Desktop 不会在系统级路径下部署或管理文件。请确保你的内部团队按照你的组织的政策处理部署、更新和合规要求。
</Note>

<div id="workspace-hooks-for-team-projects">
  ### 团队项目的工作区钩子
</div>

针对项目特有的规范，团队可以在版本控制中使用工作区级钩子：

```bash theme={null}
# 添加到你的代码仓库
.windsurf/
├── hooks.json
└── scripts/
    └── format-check.py

# 提交到 git
git add .windsurf/
git commit -m "Add workspace hooks for code formatting"
```

这让团队能够统一开发实践。将安全关键策略保留在云端或系统级，并避免将敏感信息提交到版本控制系统中。

<div id="additional-resources">
  ## 其他资源
</div>

* **MCP 集成**：详细了解 [Devin Desktop 中的 Model Context Protocol](/zh/desktop/cascade/mcp)
* **工作流程**：了解如何将钩子与 [Cascade 工作流程](/zh/desktop/cascade/workflows) 结合使用
* **分析**：通过 [团队分析](/zh/desktop/accounts/analytics) 跟踪 Cascade 用量
