Visual Studio Code 中的智能体钩子 (预览版)

钩子允许你在智能体(Agent)会话的关键生命周期节点执行自定义的 Shell 命令。使用钩子可以自动化工作流、强制执行安全策略、验证操作以及集成外部工具。

钩子被设计为跨各种智能体类型(包括本地智能体、后台智能体和云端智能体)工作。每个钩子都会接收结构化的 JSON 输入,并可以返回 JSON 输出以影响智能体的行为。

有关钩子如何融入 AI 自定义框架的背景信息,请参阅 自定义概念

本文介绍了如何在 VS Code 中配置和使用钩子。

注意

智能体钩子目前处于预览版(Preview)。配置格式和行为在未来的版本中可能会有所更改。

重要事项

你的组织可能已在 VS Code 中禁用钩子的使用。请联系你的管理员了解更多信息。详情请参阅 企业策略

为什么要使用钩子?

钩子提供确定性的、代码驱动的自动化。与引导智能体行为的指令或自定义提示词不同,钩子在特定的生命周期点执行你的代码,且结果有保证。钩子的一些常见用例包括:

  • 强制执行安全策略:在执行诸如 rm -rfDROP TABLE 等危险命令之前阻止它们,无论智能体收到了什么提示词。

  • 自动化代码质量:在修改文件后自动运行格式化程序、代码检查工具(linter)或测试。

  • 创建审计跟踪:记录每一次工具调用、命令执行或文件更改,以用于合规性和调试。

  • 注入上下文:添加特定于项目的信息、API 密钥或环境细节,以帮助智能体做出更好的决策。

  • 控制审批:自动批准安全的操作,同时对敏感操作要求确认。

快速入门:你的第一个钩子

以下示例创建了一个钩子,在智能体使用工具(例如编辑文件)后运行 Prettier。在你的工作区中创建一个 .github/hooks/format.json 文件:

{
  "hooks": {
    "PostToolUse": [
      {
        "type": "command",
        "command": "npx prettier --write ."
      }
    ]
  }
}

保存此文件后,VS Code 会自动加载该钩子。下次智能体编辑文件时,Prettier 将格式化你的工作区。你可以通过查看智能体调试日志来检查钩子是否已执行(运行 Developer: Show Agent Debug Logs 命令)。

此钩子运行单个命令并忽略其输入。要构建对智能体行为作出反应的钩子(例如仅格式化已更改的文件),请参阅 钩子是如何工作的使用场景

钩子是如何工作的

当钩子事件触发时,VS Code 会运行你的命令,并将关于该事件的信息作为 JSON 对象通过标准输入(stdin)传递。你的命令可以向标准输出(stdout)写入一个 JSON 对象,以将上下文传回给智能体,或控制接下来发生的事情(例如阻塞工具调用)。

一个钩子包含三个部分:

  • 事件:决定钩子何时运行(参见 钩子生命周期事件)。
  • 命令:当事件触发时 VS Code 运行的命令。
  • 可选的 JSON 输入和输出:允许命令读取事件详情并影响智能体。

基础钩子(如快速入门示例)会忽略输入并直接运行命令。更高级的钩子则会从 stdin 读取 JSON 来做出决策。有关输入和输出字段的完整集合,请参阅 钩子输入与输出

钩子生命周期事件

VS Code 支持八个在智能体会话期间特定点触发的钩子事件:

钩子事件 触发时机 常见用例
SessionStart 用户提交新会话的第一个提示词 初始化资源、记录会话开始、验证项目状态
UserPromptSubmit 用户提交提示词 审计用户请求、注入系统上下文
PreToolUse 在智能体调用任何工具之前 阻止危险操作、要求审批、修改工具输入
PostToolUse 工具成功完成之后 运行格式化程序、记录结果、触发后续操作
PreCompact 在对话上下文被压缩之前 导出重要上下文、在截断前保存状态
SubagentStart 衍生出子智能体时 跟踪嵌套智能体的使用情况、初始化子智能体资源
SubagentStop 子智能体完成时 汇总结果、清理子智能体资源
Stop(停止) 智能体会话结束 生成报告、清理资源、发送通知

有关每个事件的完整输入和输出模式,请参阅 钩子参考文档

配置钩子

钩子配置在存储在你的工作区或用户目录中的 JSON 文件中。

钩子文件位置

VS Code 在这些位置搜索钩子配置文件:

提示

在单一代码仓库(Monorepo)中,启用 chat.useCustomizationsInParentRepositories 在 VS Code 中打开 在 VS Code Insiders 中打开 以从父仓库根目录发现钩子。了解有关 父仓库发现 的更多信息。

范围 默认文件位置
工作区 .github/hooks/*.json
工作区 (Claude 格式) .claude/settings.json, .claude/settings.local.json
用户 ~/.copilot/hooks, ~/.claude/settings.json
自定义智能体 .agent.md Frontmatter 中的 hooks 字段(参见 智能体作用域钩子
插件 hooks.jsonhooks/hooks.json,具体取决于插件格式(参见 插件中的钩子

对于相同的事件类型,工作区钩子的优先级高于用户钩子。

使用 chat.hookFilesLocations 在 VS Code 中打开 在 VS Code Insiders 中打开 设置来自定义加载哪些文件。指定文件夹(加载文件夹中的所有 *.json 文件)或单独的 .json 文件,可使用相对路径或波浪号(~)路径。默认值包含以下位置:

"chat.hookFilesLocations": {
  ".github/hooks": true,
  ".claude/settings.local.json": true,
  ".claude/settings.json": true,
  "~/.claude/settings.json": true
}

若要自定义,请为新位置添加条目,或者将某个路径设置为 false 以禁用该位置(包括默认位置)。

"chat.hookFilesLocations": {
  "custom/hooks": true,              // load all *.json files in a folder
  "~/my-hooks/security.json": true,  // load a specific file
  ".claude/settings.json": false     // stop loading Claude Code hooks
}

钩子配置格式

创建一个包含 hooks 对象的 JSON 文件,该对象包含针对每个事件类型的钩子命令数组。为了兼容性,VS Code 使用与 Claude Code 和 Copilot CLI 相同的钩子格式。

{
  "hooks": {
    "PreToolUse": [
      {
        "type": "command",
        "command": "./scripts/validate-tool.sh",
        "timeout": 15
      }
    ],
    "PostToolUse": [
      {
        "type": "command",
        "command": "npx prettier --write ."
      }
    ]
  }
}

钩子命令属性

每个钩子条目必须指定 type: "command" 以及要运行的命令。你还可以配置工作目录(cwd)、环境变量(env)、超时时间(timeout)以及特定于操作系统的覆盖项(windowslinuxosx)。有关属性的完整列表,请参阅 钩子命令属性参考

注意

特定于操作系统的命令是根据扩展宿主平台选择的。在远程开发场景(SSH、Containers、WSL)中,这可能与你的本地操作系统不同。

特定于操作系统的命令

为每个操作系统指定不同的命令

{
  "hooks": {
    "PostToolUse": [
      {
        "type": "command",
        "command": "./scripts/format.sh",
        "windows": "powershell -File scripts\\format.ps1",
        "linux": "./scripts/format-linux.sh",
        "osx": "./scripts/format-mac.sh"
      }
    ]
  }
}

执行服务会根据你的操作系统选择适当的命令。如果未定义特定于操作系统的命令,则会回退到 command 属性。

智能体作用域钩子

注意

智能体作用域钩子目前处于预览阶段。

你可以直接在自定义智能体的 YAML frontmatter 中定义钩子。智能体作用域钩子仅在该自定义智能体处于活动状态时运行(无论是被用户选中还是作为子智能体调用)。智能体作用域钩子与为同一事件配置的任何工作区或用户级钩子一起运行。

要启用智能体作用域钩子,请将 chat.useCustomAgentHooks 在 VS Code 中打开 在 VS Code Insiders 中打开 设置为 true

在智能体 frontmatter 中添加一个 hooks 字段,其结构与钩子配置文件相同:事件名称映射到钩子命令对象数组。

---
name: "Strict Formatter"
description: "Agent that auto-formats code after every edit"
hooks:
  PostToolUse:
    - type: command
      command: "./scripts/format-changed-files.sh"
---

You are a code editing agent. After making changes, files are automatically formatted.

创建和编辑钩子

你有多种创建和编辑钩子的选项。你可以手动在某个支持的位置创建钩子配置文件,使用命令创建新钩子,或使用 AI 生成钩子。

  • 手动管理钩子文件:

    1. 在支持的位置创建或编辑 .json 文件(例如 .github/hooks/security.json)并添加你的钩子配置。
    2. 保存文件后,VS Code 会自动加载它。
  • 使用命令管理钩子

    1. 从命令面板运行 Chat: Configure Hooks 命令(⇧⌘P(Windows、Linux Ctrl+Shift+P)。

      你还可以在聊天输入框中输入 /hooks 并按下 Enter 键来打开配置钩子菜单。

    2. 按照提示选择事件类型、选择文件位置并配置命令。

    3. 该命令会创建一个新的钩子文件并在编辑器中将其打开供你自定义。保存文件以加载钩子。

  • 使用智能体自定义编辑器:

    1. 通过运行 Chat: Open Customizations 命令打开智能体自定义编辑器。

      或者,选择聊天视图顶部的 Open Customizations(齿轮图标)。

    2. 选择 Hooks 选项卡以查看和管理你的钩子。

    3. 从下拉按钮中选择 Configure Hooks

    4. 按照提示选择事件类型、选择文件位置并配置命令。

    5. 该命令会创建一个新的钩子文件并在编辑器中将其打开供你自定义。保存文件以加载钩子。

  • 使用 AI 生成钩子:

    1. 在聊天中输入 /create-hook 并描述你想要的自动化(例如 /create-hook run ESLint after every file edit)。

      或者,从命令面板运行 Chat: Generate Hook 命令(⇧⌘P(Windows、Linux Ctrl+Shift+P)或在智能体自定义编辑器中选择 Generate Hook

    2. 智能体会提出澄清问题,并生成一个带有适当事件类型、命令和设置的钩子配置文件。

钩子输入与输出

钩子通过 JSON 格式的 stdin(输入)和 stdout(输出)与 VS Code 进行通信。

常见输入字段

每个钩子都会通过 stdin 接收一个包含这些常见字段的 JSON 对象:

字段 类型 描述
timestamp string 钩子触发时的 ISO 8601 时间戳
cwd string (可选)智能体会话的工作目录
session_id string (可选)当前智能体会话的唯一标识符
hook_event_name string 钩子事件的名称(例如 PreToolUse
transcript_path string (可选)包含会话对话记录的文件的绝对路径
注意

提供 transcript_path 是为了方便起见——例如用于日志记录、审计或轻量级检查(如检查会话期间是否读取了某个文件)。对话记录文件格式不是稳定的钩子 API,可能会在未来的 VS Code 版本中更改。尽可能优先使用文档化的钩子输入字段(tool_nametool_inputprompt 等)。

常见输出格式

钩子可以通过 stdout 返回 JSON 以影响智能体的行为。所有钩子都支持这些输出字段:

{
  "continue": true,
  "stopReason": "Security policy violation",
  "systemMessage": "Unit tests failed"
}
字段 类型 描述
continue boolean 设置为 false 以停止处理(默认值:true
stopReason string continuefalse 时停止的原因(显示给用户)
systemMessage string 显示给用户的警告消息

退出码

钩子的退出码决定了 VS Code 如何处理结果:

退出码 行为
0 成功:将 stdout 解析为 JSON
2 阻塞错误:停止处理并向模型显示错误
其他 非阻塞警告:向用户显示警告,继续处理

选择如何返回数据

钩子有几种控制智能体行为的方法:退出码、顶级输出字段(continuestopReason)以及钩子特定的输出字段(hookSpecificOutput)。请按如下方式组合使用它们:

  • 退出码 2 是阻塞操作最简单的方法。钩子的 stderr 会作为上下文显示给模型。不需要 JSON 输出。
  • JSON 输出中的 continue: false 会停止整个智能体会话。使用 stopReason 告诉用户原因。这比阻塞单个工具调用更为激进。
  • hookSpecificOutput 提供针对每个钩子事件的细粒度控制。例如,PreToolUse 钩子使用 permissionDecision 来允许、拒绝或提示单个工具调用,而无需停止会话。
  • systemMessage 会在聊天中向用户显示警告,无论其他决策如何。

当多个控制机制一起使用时,限制性最强的机制生效。例如,如果钩子返回 continue: falsepermissionDecision: "allow",会话仍然会停止。

每个事件的输入与输出

每个钩子事件都提供其自己的输入字段,并支持事件特定的输出。有关每个事件的完整输入和输出模式(包括 PreToolUsePostToolUseSessionStartStop 等),请参阅 钩子参考文档

使用场景

以下示例演示了常见的钩子模式。

阻止危险的终端命令

创建一个防止破坏性命令的 PreToolUse 钩子

.github/hooks/security.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "type": "command",
        "command": "./scripts/block-dangerous.sh",
        "timeoutSec": 5
      }
    ]
  }
}

scripts/block-dangerous.sh:

#!/bin/bash
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')
TOOL_INPUT=$(echo "$INPUT" | jq -r '.tool_input')

if [ "$TOOL_NAME" = "runTerminalCommand" ]; then
  COMMAND=$(echo "$TOOL_INPUT" | jq -r '.command // empty')

  if echo "$COMMAND" | grep -qE '(rm\s+-rf|DROP\s+TABLE|DELETE\s+FROM)'; then
    echo '{"hookSpecificOutput":{"permissionDecision":"deny","permissionDecisionReason":"Destructive command blocked by security policy"}}'
    exit 0
  fi
fi

echo '{"continue":true}'
编辑后自动格式化代码

在任何文件修改后自动运行 Prettier

.github/hooks/formatting.json:

{
  "hooks": {
    "PostToolUse": [
      {
        "type": "command",
        "command": "./scripts/format-changed-files.sh",
        "windows": "powershell -File scripts\\format-changed-files.ps1",
        "timeout": 30
      }
    ]
  }
}

scripts/format-changed-files.sh:

#!/bin/bash
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')

if [ "$TOOL_NAME" = "editFiles" ] || [ "$TOOL_NAME" = "createFile" ]; then
  FILES=$(echo "$INPUT" | jq -r '.tool_input.files[]? // .tool_input.path // empty')

  for FILE in $FILES; do
    if [ -f "$FILE" ]; then
      npx prettier --write "$FILE" 2>/dev/null
    fi
  done
fi

echo '{"continue":true}'
记录工具使用情况以进行审计

创建所有工具调用的审计跟踪

.github/hooks/audit.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "type": "command",
        "command": "./scripts/log-tool-use.sh",
        "env": {
          "AUDIT_LOG": ".github/hooks/audit.log"
        }
      }
    ]
  }
}

scripts/log-tool-use.sh:

#!/bin/bash
INPUT=$(cat)
TIMESTAMP=$(echo "$INPUT" | jq -r '.timestamp')
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')
SESSION_ID=$(echo "$INPUT" | jq -r '.sessionId')

echo "[$TIMESTAMP] Session: $SESSION_ID, Tool: $TOOL_NAME" >> "${AUDIT_LOG:-audit.log}"
echo '{"continue":true}'
需要对特定工具进行审批

强制要求对修改基础设施的工具进行手动确认

.github/hooks/approval.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "type": "command",
        "command": "./scripts/require-approval.sh"
      }
    ]
  }
}

scripts/require-approval.sh:

#!/bin/bash
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')

# Tools that should always require approval
SENSITIVE_TOOLS="runTerminalCommand|deleteFile|pushToGitHub"

if echo "$TOOL_NAME" | grep -qE "^($SENSITIVE_TOOLS)$"; then
  echo '{"hookSpecificOutput":{"permissionDecision":"ask","permissionDecisionReason":"This operation requires manual approval"}}'
else
  echo '{"hookSpecificOutput":{"permissionDecision":"allow"}}'
fi
在会话开始时注入项目上下文

在会话开始时提供特定于项目的信息

.github/hooks/context.json:

{
  "hooks": {
    "SessionStart": [
      {
        "type": "command",
        "command": "./scripts/inject-context.sh"
      }
    ]
  }
}

scripts/inject-context.sh:

#!/bin/bash
PROJECT_INFO=$(cat package.json 2>/dev/null | jq -r '.name + " v" + .version' || echo "Unknown project")
BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")

cat <<EOF
{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "Project: $PROJECT_INFO | Branch: $BRANCH | Node: $(node -v 2>/dev/null || echo 'not installed')"
  }
}
EOF

安全性

如果智能体有权编辑由钩子运行的脚本,那么它就有能力在其自身运行期间修改这些脚本,并执行它所编写的代码。我们建议使用 chat.tools.edits.autoApprove 设置来禁止智能体在未经人工审批的情况下编辑钩子脚本。

疑难解答

查看钩子诊断信息

若要查看加载了哪些钩子并检查配置错误:

  1. 选择 View Logs 以查看所有日志。

  2. 查找 "Load Hooks" 以查看加载的钩子以及它们是从哪个位置加载的。

查看钩子输出

若要审查钩子输出和错误:

  1. 打开 Output 面板。

  2. 从通道列表中选择 GitHub Copilot Chat Hooks

提示

你还可以运行 Developer: Show Agent Debug Logs 命令在智能体调试日志中查看钩子的输入和输出。

常见问题

钩子未执行:验证钩子文件是否位于 .github/hooks/ 中且具有 .json 扩展名。检查 type 属性是否设置为 "command"

权限拒绝错误:确保你的钩子脚本具有执行权限(chmod +x script.sh)。

超时错误:增加 timeout 值或优化你的钩子脚本。默认值为 30 秒。

JSON 解析错误:验证你的钩子脚本是否向 stdout 输出有效的 JSON。使用 jq 或 JSON 库来构造输出。

常见问题

VS Code 如何处理 Claude Code 钩子配置?

默认情况下,VS Code 从 .claude/settings.json.claude/settings.local.json~/.claude/settings.json 读取钩子配置。VS Code 会解析 Claude Code 的钩子配置格式,包括匹配器(matcher)语法。目前,VS Code 会忽略匹配器值,因此无论匹配器如何,钩子都会在所有工具调用时运行。

如果你要将 Claude Code 钩子适配到 VS Code,请注意以下区别:

  • 工具输入属性名称:Claude Code 对工具输入属性使用蛇形命名法(snake_case,例如 tool_input.file_path),而 VS Code 工具使用驼峰命名法(camelCase,例如 tool_input.filePath)。更新你的钩子脚本以读取正确的属性名称。
  • 工具名称:Claude Code 和 VS Code 使用不同的工具名称。例如,Claude Code 使用 Write and Edit 进行文件操作,而 VS Code 使用诸如 create_filereplace_string_in_file 等工具名称。检查 tool_name 输入字段中的工具名称,并相应地更新你的钩子逻辑。
  • 匹配器被忽略:像 "Edit|Write" 这样的钩子匹配器会被解析但不会被应用。所有钩子都会在每个匹配的事件上运行,而不管匹配器中的工具名称如何。

VS Code 如何处理 Copilot CLI 钩子配置?

VS Code 会解析 Copilot CLI 钩子配置,并将小驼峰命名法的钩子事件名称(如 preToolUse)转换为 VS Code 使用的帕斯卡命名法格式(PascalCase,即 PreToolUse)。bashpowershell 命令属性会映射到特定于操作系统的命令:powershell 映射到 windows,而 bash 映射到 osxlinux

安全注意事项

注意

钩子以与 VS Code 相同的权限执行 shell 命令。请仔细审查钩子配置,特别是在使用来自不受信任来源的钩子时。

  • 审查钩子脚本:在启用所有钩子脚本之前进行检查,尤其是在共享代码仓库中。

  • 限制钩子权限:遵循最小权限原则。钩子应该只拥有它们所需内容的访问权限。

  • 验证输入:钩子脚本从智能体接收输入。验证并净化所有输入以防止注入攻击。

  • 保护凭据:切勿在钩子脚本中硬编码机密信息。应使用环境变量或安全的凭据存储。

English 한국어 中文(简体) 中文(繁體)
© . This website operates independently and is not affiliated with or endorsed by Microsoft. All brand names, logos, and trademarks are the property of their respective owners.