介绍
钩子是在会话的特定生命周期节点执行的外部命令,用于实现自定义自动化、安全控制和集成。
支持挂钩的图面有两个:Copilot、Copilot 命令行界面(CLI) 和 Copilot云代理。 大多数配置格式和事件有效负载相同,但执行环境和可以触发的事件集有所不同。
在本文中,两个界面之间的行为差异在“仅限 CLI”和“仅限云代理”说明中指出。 未标记的任何内容都适用于这两者。
挂钩位置
挂钩运行的位置以及存储挂钩配置文件的位置取决于表面:
-
**Copilot 命令行界面(CLI) ** — 挂钩在开发人员的本地计算机上运行,其 shell 与 CLI 相同。 CLI 支持本文中所述的所有挂钩事件。
钩子按照顺序从以下来源加载并合并:用户、项目,然后是插件。 当同一事件出现在多个源中时,将运行来自所有源的所有挂钩条目。
- 存储库级挂钩文件 -
.github/hooks/*.json在存储库根目录中。 - 用户级挂钩文件 -
*.json用户级挂钩目录中的文件。 默认情况下,macOS 和 Linux 上的~/.copilot/hooks/,或 Windows 上的%USERPROFILE%\.copilot\hooks\。 如果COPILOT_HOME已设置,则为$COPILOT_HOME/hooks/. - 存储库设置中的内联
hooks块 -hooks存储库中顶级.github/copilot/settings.json(Git 提交)或.github/copilot/settings.local.json(通常是 gitignored 和用户特定)的字段。 还会读取存储库中的.claude/settings.json和.claude/settings.local.json文件及其他跨工具文件。 - 用户级配置中的内联
hooks块 -hooks位于顶层的~/.copilot/settings.json字段。 - 由已安装的插件提供的挂钩 - 由每个插件在其安装目录内的
hooks.json(或hooks/hooks.json底下)中声明。
- 存储库级挂钩文件 -
-
**Copilot云代理 ** — 挂钩在云代理为每个作业预配的临时 Linux 沙盒内运行。 沙盒是非交互式的,具有受约束的网络,并在作业结束时销毁。 触发事件子集,并且仅
bash(或command)条目有效。挂钩配置被加载自克隆存储库中的
.github/hooks/*.json文件。
云代理执行环境
本节仅适用于Copilot云代理。 它描述了影响编写挂钩脚本和配置云代理作业挂钩条目的方式的约束。
| 财产 | 值 |
|---|---|
| 操作系统 | Linux。 |
`bash` 仅遵循命令挂钩上的字段。将忽略 `powershell` 中的条目。 跨平台 `command` 字段被视为备用方案。 |
| 工作目录 |
/workspace 克隆存储库时,否则为 /root。 在挂钩条目上设置 cwd 或从脚本引用文件时使用此路径。 |
| Filesystem | 短暂。 作业结束时,将丢弃由钩子写入的文件(例如日志、CSV 和转录文件)。 若要保留挂钩输出,请通过 http 挂钩条目发送它。 |
| 出站网络 | 受云代理防火墙限制。 默认情况下,只有GitHub和Copilot主机名可访问;访问任何其他主机(例如https://hooks.example.com)需要管理员配置的防火墙允许规则。 |
| 可用的环境变量 |
GITHUB_COPILOT_API_TOKEN 和 GITHUB_COPILOT_GIT_TOKEN 被设置在沙盒中。
COPILOT_AGENT_PROMPT 保存调用作业的提示。
HOME 被设置为 /root,因此任何解析 ~/... 路径的挂钩脚本会写入到临时沙箱中。
GITHUB_TOKEN 未设置。 |
| 交互性 | 完全非交互式。 代理使用预先授予的所有工具权限运行,因此不会显示任何权限对话框,并且不会向用户显示任何通知。 |
| 配置识别 | 在云代理作业中,默认存在的唯一挂钩配置位于 .github/hooks/*.json 克隆的存储库中。 沙盒不附带用户级挂钩文件、settings.json``config.json或已安装的插件。 |
钩子配置格式
挂钩配置文件使用 JSON 格式和版本 1。
指令钩
命令挂钩运行 shell 脚本,在所有挂钩类型上都受支持。
注意
仅限云代理。 云代理在 Linux 沙盒中运行挂钩。 仅采纳bash字段;powershell条目将被忽略。 跨平台 command 字段被视为备用。
{
"version": 1,
"hooks": {
"preToolUse": [
{
"type": "command",
"bash": "your-bash-command",
"powershell": "your-powershell-command",
"cwd": "optional/working/directory",
"env": { "VAR": "value" },
"timeoutSec": 30
}
]
}
}
| 领域 | 类型 | 必需 | Description |
|---|---|---|---|
bash | 字符串 |
`bash`、`powershell`或 `command` 之一 | Unix 的 Shell 命令。 |
| command | 字符串 |
bash、powershell或 command 之一 | 当当前平台既未设置bash也未设置powershell时使用的跨平台回退。 |
| cwd | 字符串 | 否 | 命令的工作目录(相对于存储库根目录或绝对目录)。 |
| env | 对象 | 否 | 要设置的环境变量(支持变量扩展)。 |
| powershell | 字符串 |
bash、powershell或 command 之一 | 适用于 Windows 的 Shell 命令。 |
| timeoutSec | number | 否 | 超时时间(以秒为单位) 默认值:30。 |
| type | "command" | 是的 | 必须是 "command"。 |
HTTP 钩子
HTTP 挂钩将输入有效负载作为 JSON POST 发送到 URL。
注意
仅限云代理。 来自沙盒的出站网络受云代理防火墙的限制,因此 url 必须面向在允许列表中的主机。
{
"version": 1,
"hooks": {
"postToolUse": [
{
"type": "http",
"url": "https://hooks.example.com/copilot",
"headers": { "X-Source": "copilot-cli" },
"allowedEnvVars": ["GITHUB_TOKEN"],
"timeoutSec": 30
}
]
}
}
| 领域 | 类型 | 必需 | Description |
|---|---|---|---|
allowedEnvVars | string[] | 否 | 环境变量名称可在 headers 值内扩展。 设置时, url 必须使用 https://。 |
headers | 对象 | 否 | 请求中要包含的标头。 |
timeoutSec | number | 否 | 超时时间(以秒为单位) 默认值:30。 |
type | "http" | 是的 | 必须是 "http"。 |
url | 字符串 | 是的 | 目标 URL。 必须使用http:或https:。 对于 preToolUse 和 permissionRequest,必须使用 https:// ,因为响应可以授予工具权限。 |
提示钩子
提示挂钩自动提交文本,就好像用户键入了它一样。 它们仅受支持于sessionStart。 文本可以是自然语言提示或斜杠命令。
注意
**
Copilot 命令行界面(CLI) 仅限于此。** 提示挂钩仅在新的交互会话中触发。 它们不会在恢复时触发,也不会在非交互式提示模式下触发(-p)。
注意
云代理。 云代理作业以非交互方式运行(类似于 -p),因此 prompt 挂钩条目可能不会触发。 在依赖它们之前,请确认它们在您的环境中的行为。
{
"version": 1,
"hooks": {
"sessionStart": [
{
"type": "prompt",
"prompt": "Your prompt text or /slash-command"
}
]
}
}
| 领域 | 类型 | 必需 | Description |
|---|---|---|---|
type | "prompt" | 是的 | 必须是 "prompt"。 |
prompt | 字符串 | 是的 | 要提交的文本可以是自然语言消息或斜杠命令。 |
挂钩事件
下表列出了每个受支持的事件。 “云代理”列显示事件是否在云代理下触发,并记录任何行为差异。
| 事件 | 触发时间 | 已处理的输出 | 云代理 |
|---|---|---|---|
agentStop | 主代理完成回合。 | 是 — 可以阻止和强制继续。 | 火灾。 |
`decision: "block"` 强制执行另一个轮次,该轮次仍然计入作业的超时。 |
| errorOccurred | 执行期间发生错误。 | 否 | 火灾。 |
| notification | 每当 CLI 发出系统通知(如 shell 完成、代理完成或空闲状态、权限提示、引发对话框)时,都会异步触发。 触发和忘记:从不阻止会话。 支持 matcher 正则表达式适用于 notification_type. | 可选:可以将 additionalContext 注入到会话中。 |
不触发。 云代理不会向用户显示通知(请参阅上面的云代理执行环境表中的 交互 行)。 |
| permissionRequest | 在权限服务运行之前触发(规则引擎、会话审批、自动允许/自动拒绝和用户提示)。 如果合并挂钩输出返回 behavior: "allow" 或 "deny",该决策会使正常权限流短路。 支持 matcher 正则表达式适用于 toolName. | 是 — 可以以编程方式允许或拒绝。 | 工具调用已预先批准,因此此挂钩要么不触发,要么不产生效果。 改为使用 preToolUse 来做权限决策。 |
| postToolUse | 每个工具成功完成操作后。 | 否 | 火灾。 |
| postToolUseFailure | 工具以失败告终。 | 是 — 可以通过(命令挂钩的退出代码additionalContext)提供恢复指导2。 | 火灾。 |
| preCompact | 上下文压缩即将开始(手动或自动)。 支持 matcher 按触发器("manual" 或 "auto") 进行筛选。 | 否 - 仅通知。 | 仅在满足 trigger: "auto" 条件时触发。 没有用户请求手动压缩。 |
| preToolUse | 在每个工具执行之前。 | 是 — 可以允许、拒绝或修改。 | 火灾。 由于没有用户可以回答,"ask" 的决策被视为 "deny"。 |
| sessionEnd | 会话终止。 | 否 | 每个作业触发一次。
reason 通常是 "complete", "error"或 "timeout"; "abort" ,并且 "user_exit" 不需要,因为没有用户。 |
| sessionStart | 新的或已恢复的会话开始。 | 可选:可以将 additionalContext 注入到会话中。 | 为每个作业触发一次,作为新会话(而不是恢复)。 有关云代理下条目的行为, prompt 请参阅上面的提示挂钩说明。 |
| subagentStart | 子代理在运行之前生成。 支持 matcher 按代理名称进行筛选。 | 可选 - 无法阻止创建,但 additionalContext 追加到子代理的提示符。 | 火灾。 |
| subagentStop | 子代理完成。 | 是 — 可以阻止和强制继续。 | 火灾。 |
| userPromptSubmitted | 用户提交提示。 | 否 | 针对提供给作业的提示,最多触发一次。 没有后续用户输入。 |
钩子事件输入负载
每个挂钩事件将 JSON 有效负载传递到挂钩处理程序。 支持两种负载格式,可根据挂钩配置中使用的事件名称进行选择。
- camelCase 格式 - 在 camelCase 中配置事件名称(例如)。
sessionStart字段使用 camelCase。 - VS Code兼容格式 - 在 PascalCase 中配置事件名称(例如)。
SessionStart字段使用snake_case来匹配 VS CodeCopilot 扩展格式。
sessionStart / SessionStart
**camelCase 输入:**
{
sessionId: string;
timestamp: number; // Unix timestamp in milliseconds
cwd: string;
source: "startup" | "resume" | "new";
initialPrompt?: string;
}
**
VS Code 兼容的输入:**
{
hook_event_name: "SessionStart";
session_id: string;
timestamp: string; // ISO 8601 timestamp
cwd: string;
source: "startup" | "resume" | "new";
initial_prompt?: string;
}
sessionEnd / SessionEnd
**camelCase 输入:**
{
sessionId: string;
timestamp: number;
cwd: string;
reason: "complete" | "error" | "abort" | "timeout" | "user_exit";
}
**
VS Code 兼容的输入:**
{
hook_event_name: "SessionEnd";
session_id: string;
timestamp: string; // ISO 8601 timestamp
cwd: string;
reason: "complete" | "error" | "abort" | "timeout" | "user_exit";
}
userPromptSubmitted / UserPromptSubmit
**camelCase 输入:**
{
sessionId: string;
timestamp: number;
cwd: string;
prompt: string;
}
**
VS Code 兼容的输入:**
{
hook_event_name: "UserPromptSubmit";
session_id: string;
timestamp: string; // ISO 8601 timestamp
cwd: string;
prompt: string;
}
preToolUse / PreToolUse
**camelCase 输入:**
{
sessionId: string;
timestamp: number;
cwd: string;
toolName: string;
toolArgs: unknown;
}
**
VS Code 兼容的输入:**
配置使用 PascalCase 事件名称PreToolUse时,数据载荷使用 snake_case 字段名称以匹配 VS CodeCopilot 扩展格式。
{
hook_event_name: "PreToolUse";
session_id: string;
timestamp: string; // ISO 8601 timestamp
cwd: string;
tool_name: string;
tool_input: unknown; // Tool arguments (parsed from JSON string when possible)
}
postToolUse / PostToolUse
**camelCase 输入:**
{
sessionId: string;
timestamp: number;
cwd: string;
toolName: string;
toolArgs: unknown;
toolResult: {
resultType: "success";
textResultForLlm: string;
}
}
**
VS Code 兼容的输入:**
{
hook_event_name: "PostToolUse";
session_id: string;
timestamp: string; // ISO 8601 timestamp
cwd: string;
tool_name: string;
tool_input: unknown;
tool_result: {
result_type: "success";
text_result_for_llm: string;
}
}
postToolUseFailure / PostToolUseFailure
**camelCase 输入:**
{
sessionId: string;
timestamp: number;
cwd: string;
toolName: string;
toolArgs: unknown;
error: string;
}
**
VS Code 兼容的输入:**
{
hook_event_name: "PostToolUseFailure";
session_id: string;
timestamp: string; // ISO 8601 timestamp
cwd: string;
tool_name: string;
tool_input: unknown;
error: string;
}
agentStop / Stop
**camelCase 输入:**
{
sessionId: string;
timestamp: number;
cwd: string;
transcriptPath: string;
stopReason: "end_turn";
}
**
VS Code 兼容的输入:**
{
hook_event_name: "Stop";
session_id: string;
timestamp: string; // ISO 8601 timestamp
cwd: string;
transcript_path: string;
stop_reason: "end_turn";
}
subagentStart
**输入:**
{
sessionId: string;
timestamp: number;
cwd: string;
transcriptPath: string;
agentName: string;
agentDisplayName?: string;
agentDescription?: string;
}
subagentStop / SubagentStop
**camelCase 输入:**
{
sessionId: string;
timestamp: number;
cwd: string;
transcriptPath: string;
agentName: string;
agentDisplayName?: string;
stopReason: "end_turn";
}
**
VS Code 兼容的输入:**
{
hook_event_name: "SubagentStop";
session_id: string;
timestamp: string; // ISO 8601 timestamp
cwd: string;
transcript_path: string;
agent_name: string;
agent_display_name?: string;
stop_reason: "end_turn";
}
errorOccurred / ErrorOccurred
**camelCase 输入:**
{
sessionId: string;
timestamp: number;
cwd: string;
error: {
message: string;
name: string;
stack?: string;
};
errorContext: "model_call" | "tool_execution" | "system" | "user_input";
recoverable: boolean;
}
**
VS Code 兼容的输入:**
{
hook_event_name: "ErrorOccurred";
session_id: string;
timestamp: string; // ISO 8601 timestamp
cwd: string;
error: {
message: string;
name: string;
stack?: string;
};
error_context: "model_call" | "tool_execution" | "system" | "user_input";
recoverable: boolean;
}
preCompact / PreCompact
**camelCase 输入:**
{
sessionId: string;
timestamp: number;
cwd: string;
transcriptPath: string;
trigger: "manual" | "auto";
customInstructions: string;
}
**
VS Code 兼容的输入:**
{
hook_event_name: "PreCompact";
session_id: string;
timestamp: string; // ISO 8601 timestamp
cwd: string;
transcript_path: string;
trigger: "manual" | "auto";
custom_instructions: string;
}
preToolUse 决策控制
挂钩 preToolUse 可以通过将 JSON 对象写入 stdout 来控制工具执行。
| 领域 | 价值观 | Description |
|---|---|---|
permissionDecision |
`"allow"`、`"deny"`、`"ask"` | 工具是否已执行? 空输出使用默认行为。 在云代理下,由于没有用户可以回答,`"ask"` 被视为 `"deny"`。 |
| permissionDecisionReason | 字符串 | 显示给代理的原因。 决策 "deny"时是必要的。 |
| modifiedArgs | 对象 | 替换工具参数以使用,而不是使用原始参数。 |
agentStop
/
`subagentStop` 决策控制
| 领域 | 价值观 | Description |
|---|---|---|
decision |
`"block"`、`"allow"` |
`"block"` 强制另一个代理将 `reason` 作为提示。 |
| reason | 字符串 | 当decision是"block"时,提示下一轮。 |
permissionRequest 决策控制
注意
**
Copilot 命令行界面(CLI) 仅限。**
permissionRequest挂钩不适用于Copilot云代理,工具调用已获得预先批准(请参阅云代理执行环境表中交互行)。 使用preToolUse在云代理中进行权限决策。
该 permissionRequest 挂钩在权限服务运行前触发—在规则检查、会话审批、自动允许/自动拒绝和用户提示之前触发。 如果挂钩返回 behavior: "allow" 或 "deny",该决策会使正常权限流短路。 不返回任何结果时,将继续执行正常的权限处理。 使用它以编程方式批准或拒绝工具调用-特别适用于 CLI 管道模式(-p)和其他 CLI CI 用法,其中没有交互式提示可用。 它不适用于云代理。
所有配置的permissionRequest挂钩都针对每个请求运行(权限类型除外read``hook,这些挂钩在挂钩前短路)。 钩子输出会合并,后续钩子输出会覆盖早期的钩子输出。
**匹配器:** 可选正则表达式针对 `toolName` 进行测试。 定位为 `^(?:pattern)$`;必须与完整的工具名称匹配。 设置时,挂钩仅针对匹配的工具名称触发。
将 JSON 输出到 stdout 以控制权限决策:
| 领域 | 价值观 | Description |
|---|---|---|
behavior |
`"allow"`、`"deny"` | 是否批准或拒绝工具调用请求。 |
| message | 字符串 | 拒绝时,原因会反馈给 LLM。 |
| interrupt | boolean | 当true与"deny"结合使用时,将完全停止代理。 |
返回空输出或{}以继续正常权限流程。 对于命令钩子,退出代码 2 被视为拒绝执行,stdout JSON(如果有)与 {"behavior":"deny"} 合并,而 stderr 会被忽略。
notification 钩子
注意
**
Copilot 命令行界面(CLI) 仅限。** 钩子notification没有在Copilot云代理触发。
当 notification CLI 发出系统通知时,挂钩会异步触发。 这些挂钩是即发即弃型的:它们永远不会阻塞会话,并且记录并跳过任何错误。
**输入:**
{
sessionId: string;
timestamp: number;
cwd: string;
hook_event_name: "Notification";
message: string; // Human-readable notification text
title?: string; // Short title (e.g., "Permission needed", "Shell completed")
notification_type: string; // One of the types listed below
}
**通知类型:**
| 类型 | 当它触发时 |
|---|---|
shell_completed | 后台异步 shell 命令完成 |
shell_detached_completed | 断开连接的 shell 会话完成 |
agent_completed | 后台子代理完成(已完成或失败) |
agent_idle | 后台代理完成一个循环并进入空闲状态(正在等待 write_agent) |
permission_prompt | 代理请求执行工具的权限 |
elicitation_dialog | 代理从用户请求其他信息 |
**Output:**
{
additionalContext?: string; // Injected into the session as a user message
}
如果 additionalContext 返回,文本将作为追加的用户消息注入到会话中。 如果会话处于空闲状态,可能会触发进一步的代理处理。 返回 {} 或空输出以不执行任何操作。
**匹配器:** 在 `notification_type` 上可选的正则表达式。 该模式被定位为 `^(?:pattern)$`。 省略 `matcher` 以接收所有通知类型。
匹配器筛选
多个事件在每个挂钩条目上接受一个可选的 matcher 正则表达式,用于筛选触发该挂钩的调用。 该模式定位为^(?:matcher)$,并且必须与完整值匹配。 无效的正则表达式会导致跳过挂钩条目。
| 事件 |
matcher 被匹配到 |
|-------|------------------------------|
| notification | notification_type |
| permissionRequest | toolName |
| preCompact |
trigger("manual" 或 "auto") |
| preToolUse | toolName |
| subagentStart | agentName |
挂钩匹配工具名称
| 工具名称 | Description |
|---|---|
ask_user | 询问用户一个澄清的问题。 在云代理下,没有用户,因此 ask_user 不会产生有用的结果。 |
bash | 执行 shell 命令(Unix)。 |
create | 创建新文件。 |
edit | 修改文件内容。 |
glob | 按模式查找文件。 |
grep | 搜索文件内容。 |
powershell | 执行 shell 命令(Windows)。 不会显示在云代理(Linux 沙盒)下。 |
task | 运行子代理任务。 |
view | 读取文件内容。 |
web_fetch | 抓取网页。 |
如果配置了同一类型的多个挂钩,则它们按顺序执行。 对于 preToolUse,如果任何挂钩返回 "deny",则阻止该工具。 挂钩失败(非 2 的非零退出代码或超时)会被记录并跳过,它们永远不会阻止代理执行。
命令挂钩的退出代码
| 退出代码 | Meaning |
|---|---|
0 | 成功。 |
`stdout` 如果存在,则分析为挂钩输出 JSON。 |
| 2 | 默认情况下被视为警告。
stderr 显示给用户,但运行仍在继续。 对于 permissionRequest,将退出 2 视为 {"behavior":"deny"} 并合并任何 stdout JSON。 对于postToolUseFailure,退出2被视为additionalContext,并且stdout被追加到展示给代理的失败信息中。 |
| 其他非零 | 记录为钩子失败。 运行将继续(失败打开)。 |
禁用所有挂钩
当您想在磁盘上保留挂钩配置但阻止其运行时,使用 disableAllHooks。例如:
- 正在处理调试问题时,如果想确认某个挂钩是否是导致问题的原因,而无需删除配置文件。
- 在敏感任务(代码评审、发布分支、使用机密)期间暂停自动化,而不会丢失设置。 (Copilot 命令行界面(CLI) 仅。)
- 在源代码管理中传送挂钩文件,参与者可以通过在其存储库
settings.json中设置选项来选择退出本地。 (Copilot 命令行界面(CLI) 仅限。) - 在交互式会话期间,暂时静音缓慢或噪声大的挂钩。 (Copilot 命令行界面(CLI) 仅。)
设置为disableAllHooks``true顶层可跳过文件中的每个挂钩,而无需删除它。
{
"version": 1,
"disableAllHooks": false,
"hooks": {
"preToolUse": [ /* hook entries */ ]
}
}
行为取决于设置标志的位置:
- 在单个
.github/hooks/*.json文件中,仅跳过该文件中声明的钩子。 Copilot 命令行界面(CLI) 和 Copilot云代理 的荣誉。 - 仅限在存储库
settings.json的顶层**—Copilot 命令行界面(CLI)。** 对于该存储库中的会话,来自每个源(存储库文件、用户文件、插件和内联挂钩块)的所有挂钩都会被跳过。 云代理不会加载settings.json。