Visual Studio Code 中的 Agent 钩子(预览版)

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

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

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

注意

Agent 钩子目前处于预览阶段。配置格式和行为在未来版本中可能会发生变化。

重要

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

提示

使用 聊天自定义编辑器(预览版)在一个地方发现、创建和管理所有聊天自定义项。从命令面板运行 Chat: Open Chat Customizations(聊天:打开聊天自定义项)。

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

为什么要使用钩子?

钩子提供确定性的、代码驱动的自动化功能。与引导智能体行为的指令或自定义提示词不同,钩子在特定的生命周期点执行你的代码,并保证产生预期的结果。

  • 执行安全策略:在危险命令(如 rm -rfDROP TABLE)执行前将其拦截,无论智能体收到了什么提示词。

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

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

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

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

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

以下示例创建了一个在每次文件编辑后运行 Prettier 的钩子。在你的工作区中创建一个 .github/hooks/format.json 文件:

{
  "hooks": {
    "PostToolUse": [
      {
        "type": "command",
        "command": "npx prettier --write \"$TOOL_INPUT_FILE_PATH\""
      }
    ]
  }
}

保存此文件后,VS Code 会自动加载该钩子。下次智能体编辑文件时,Prettier 将在更改后的文件上运行。请检查 GitHub Copilot Chat Hooks 输出通道以验证钩子是否已执行。

有关使用自定义脚本的更复杂钩子,请参阅使用场景

钩子生命周期事件

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 中打开 设置来定制加载哪些钩子文件。你可以指定文件夹路径(VS Code 会加载文件夹中的所有 *.json 文件)或指向单个 .json 文件的路径。仅支持相对路径和波浪号 (~) 路径。

默认值包含上述位置。

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

要添加自定义位置,请向此设置添加条目。

"chat.hookFilesLocations": {
  "custom/hooks": true,
  "~/my-hooks/security.json": true
}

将路径设置为 false 可禁用从该位置(包括默认位置)加载钩子。例如,停止从 Claude Code 配置文件中加载钩子:

"chat.hookFilesLocations": {
  ".claude/settings.json": false,
  ".claude/settings.local.json": false,
  "~/.claude/settings.json": false
}

智能体作用域钩子

注意

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

你可以直接在自定义智能体的 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.

钩子配置格式

创建一个包含 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 \"$TOOL_INPUT_FILE_PATH\""
      }
    ]
  }
}

钩子命令属性

每个钩子条目必须具有 type: "command" 以及至少一个命令属性:

属性 类型 描述
type 字符串 必须为 "command"
command 字符串 运行的默认命令(跨平台)
windows 字符串 Windows 专用命令覆盖
linux 字符串 Linux 专用命令覆盖
osx 字符串 macOS 专用命令覆盖
cwd 字符串 工作目录(相对于仓库根目录)
env 对象 附加环境变量
timeout number 超时(秒,默认:30)
注意

操作系统专用命令是根据扩展宿主平台选择的。在远程开发场景(SSH、容器、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 属性。

钩子输入和输出

钩子通过 stdin(输入)和 stdout(输出)使用 JSON 与 VS Code 通信。

常见输入字段

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

{
  "timestamp": "2026-02-09T10:30:00.000Z",
  "cwd": "/path/to/workspace",
  "sessionId": "session-identifier",
  "hookEventName": "PreToolUse",
  "transcript_path": "/path/to/transcript.json"
}

常见输出格式

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

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

退出代码

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

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

选择返回数据的方式

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

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

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

PreToolUse(工具使用前)

PreToolUse 钩子在智能体调用工具之前触发。

PreToolUse 输入

除了常见字段外,PreToolUse 钩子还会接收:

{
  "tool_name": "editFiles",
  "tool_input": { "files": ["src/main.ts"] },
  "tool_use_id": "tool-123"
}

PreToolUse 输出

PreToolUse 钩子可以通过 hookSpecificOutput 对象控制工具执行:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Destructive command blocked by policy",
    "updatedInput": { "files": ["src/safe.ts"] },
    "additionalContext": "User has read-only access to production files"
  }
}
字段 描述
permissionDecision "allow", "deny", "ask" 控制工具审批
permissionDecisionReason 字符串 显示给用户的原因
updatedInput 对象 修改后的工具输入(可选)
additionalContext 字符串 给模型的额外上下文

权限决定优先级:当同一工具调用运行多个钩子时,限制性最强的决定获胜:

  1. deny(限制性最强):阻止工具执行
  2. ask:要求用户确认
  3. allow(限制性最弱):自动批准执行

updatedInput 格式:要确定 updatedInput 的格式,请打开智能体日志并找到记录的工具模式(schema)。如果 updatedInput 与预期模式不匹配,它将被忽略。

PostToolUse(工具使用后)

PostToolUse 钩子在工具成功完成后触发。

PostToolUse 输入

除了常见字段外,PostToolUse 钩子还会接收:

{
  "tool_name": "editFiles",
  "tool_input": { "files": ["src/main.ts"] },
  "tool_use_id": "tool-123",
  "tool_response": "File edited successfully"
}

PostToolUse 输出

PostToolUse 钩子可以向模型提供额外上下文,或阻止进一步处理:

{
  "decision": "block",
  "reason": "Post-processing validation failed",
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "The edited file has lint errors that need to be fixed"
  }
}
字段 描述
decision "block" 阻止进一步处理(可选)
reason 字符串 阻止的原因(显示给模型)
hookSpecificOutput.additionalContext 字符串 注入对话的额外上下文

UserPromptSubmit(用户提交提示)

UserPromptSubmit 钩子在用户提交提示词时触发。

UserPromptSubmit 输入

除了常见字段外,UserPromptSubmit 钩子还会接收一个包含用户提交文本的 prompt 字段。

UserPromptSubmit 钩子仅使用常见的输出格式。

SessionStart(会话开始)

SessionStart 钩子在新的智能体会话开始时触发。

SessionStart 输入

除了常见字段外,SessionStart 钩子还会接收:

{
  "source": "new"
}
字段 类型 描述
source 字符串 会话的启动方式。目前总是 "new"

SessionStart 输出

SessionStart 钩子可以向智能体的对话注入额外上下文:

{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "Project: my-app v2.1.0 | Branch: main | Node: v20.11.0"
  }
}
字段 类型 描述
additionalContext 字符串 添加到智能体对话中的上下文

Stop(停止)

Stop 钩子在智能体会话结束时触发。当作用于自定义智能体时,Stop 钩子也被视为 SubagentStop

Stop 输入

除了常见字段外,Stop 钩子还会接收:

{
  "stop_hook_active": false
}
字段 类型 描述
stop_hook_active 布尔值 当智能体因之前的停止钩子而正在继续运行时为 true。检查此值以防止智能体无限运行。

Stop 输出

Stop 钩子可以防止智能体停止:

{
  "hookSpecificOutput": {
    "hookEventName": "Stop",
    "decision": "block",
    "reason": "Run the test suite before finishing"
  }
}
字段 描述
decision "block" 防止智能体停止
reason 字符串 当决定为 "block" 时必需。告诉智能体为何应该继续。
重要

Stop 钩子阻止智能体停止时,智能体将继续运行,且额外的轮次会消耗高级请求次数。请务必检查 stop_hook_active 字段,以防止智能体无限运行。

SubagentStart(子智能体启动)

SubagentStart 钩子在启动子智能体时触发。

SubagentStart 输入

除了常见字段外,SubagentStart 钩子还会接收:

{
  "agent_id": "subagent-456",
  "agent_type": "Plan"
}
字段 类型 描述
agent_id 字符串 子智能体的唯一标识符
agent_type 字符串 智能体名称(例如内置智能体为 "Plan" 或自定义智能体名称)

SubagentStart 输出

SubagentStart 钩子可以向子智能体的对话注入额外上下文:

{
  "hookSpecificOutput": {
    "hookEventName": "SubagentStart",
    "additionalContext": "This subagent should follow the project coding guidelines"
  }
}
字段 类型 描述
additionalContext 字符串 添加到子智能体对话中的上下文

SubagentStop(子智能体停止)

SubagentStop 钩子在子智能体完成时触发。

SubagentStop 输入

除了常见字段外,SubagentStop 钩子还会接收:

{
  "agent_id": "subagent-456",
  "agent_type": "Plan",
  "stop_hook_active": false
}
字段 类型 描述
agent_id 字符串 子智能体的唯一标识符
agent_type 字符串 智能体名称(例如内置智能体为 "Plan" 或自定义智能体名称)
stop_hook_active 布尔值 当子智能体因之前的停止钩子而正在继续运行时为 true。检查此值以防止子智能体无限运行。

SubagentStop 输出

SubagentStop 钩子可以防止子智能体停止:

{
  "decision": "block",
  "reason": "Verify subagent results before completing"
}
字段 描述
decision "block" 防止子智能体停止
reason 字符串 当决定为 "block" 时必需。告诉子智能体为何应该继续。

PreCompact(压缩前)

PreCompact 钩子在对话上下文压缩之前触发。

PreCompact 输入

除了常见字段外,PreCompact 钩子还会接收:

{
  "trigger": "auto"
}
字段 类型 描述
trigger 字符串 压缩的触发方式。当对话过长超出提示词预算时为 "auto"

PreCompact 钩子仅使用常见的输出格式。

通过 UI 配置钩子

你可以通过多种方式使用交互式 UI 配置钩子:

  • 在聊天输入框中输入 /hooks 并按下 Enter
  • 打开命令面板 (⇧⌘P(Windows, Linux 为 Ctrl+Shift+P) 并运行 Chat: Configure Hooks
  • 选择 Chat 视图顶部的 设置 图标 (),然后选择 Hooks

在配置钩子菜单中:

  1. 从列表中选择一个钩子事件类型。

  2. 选择现有钩子进行编辑,或选择 Add new hook 进行创建。

  3. 选择或创建一个钩子配置文件。

该命令会在编辑器中打开钩子文件,光标位于 command 字段,可直接进行编辑。

使用 AI 生成钩子

你可以使用 AI 生成钩子配置。在聊天中输入 /create-hook 并描述你想要的自动化操作(例如“在每次文件编辑后运行 ESLint”)。智能体会提出澄清问题,并生成包含合适事件类型、命令和设置的钩子配置文件。

使用场景

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

拦截危险的终端命令

创建一个 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

常见问题

钩子未执行:验证钩子文件是否位于 .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 对文件操作使用 WriteEdit,而 VS Code 使用如 create_filereplace_string_in_file 等工具名称。请检查 tool_name 输入字段中的工具名称并相应更新你的钩子逻辑。
  • 忽略匹配器:像 "Edit|Write" 这样的钩子匹配器会被解析但不会被应用。无论匹配器中的工具名称如何,所有钩子都会在每个匹配事件上运行。

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

VS Code 会解析 Copilot CLI 钩子配置,并将 lowerCamelCase 的钩子事件名称(如 preToolUse)转换为 VS Code 使用的 PascalCase 格式 (PreToolUse)。bashpowershell 命令属性映射到操作系统专用命令:powershell 映射到 windowsbash 映射到 osxlinux

安全注意事项

注意

钩子以与 VS Code 相同的权限执行 Shell 命令。请仔细审查钩子配置,尤其是在使用来自不可信来源的钩子时。

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

  • 限制钩子权限:使用最小权限原则。钩子应仅具备其所需的功能权限。

  • 验证输入:钩子脚本接收来自智能体的输入。验证并清理所有输入以防止注入攻击。

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

© . This site is unofficial and not affiliated with Microsoft.