Skip to main content

Hooks 参考

CSC hook 事件、配置模式、JSON 输入/输出格式、退出码、异步 hooks、HTTP hooks、提示 hooks 和 MCP 工具 hooks 的参考文档。

💡 提示: 有关快速入门指南和示例,请参阅使用 hooks 自动化工作流。

Hooks 是用户定义的 shell 命令、HTTP 端点或 LLM 提示,它们在 CSC 生命周期的特定点自动执行。使用此参考文档查找事件模式、配置选项、JSON 输入/输出格式以及异步 hooks、HTTP hooks 和 MCP 工具 hooks 等高级功能。如果你是首次设置 hooks,请先从指南开始。

Hook 生命周期​

Hooks 在 CSC 会话的特定点触发。当事件触发且匹配器匹配时,CSC 将有关事件的 JSON 上下文传递给你的 hook 处理程序。对于命令 hooks,输入通过 stdin 到达。对于 HTTP hooks,它作为 POST 请求体到达。你的处理程序随后可以检查输入、采取行动,并可选地返回决策。事件分为三种频率:每个会话一次(SessionStart、SessionEnd),每个轮次一次(UserPromptSubmit、Stop、StopFailure),以及智能体循环中的每次工具调用(PreToolUse、PostToolUse):

下表总结了每个事件的触发时机。Hook 事件部分记录了每个事件的完整输入模式和决策控制选项。

事件触发时机
SessionStart当会话开始或恢复时
UserPromptSubmit当你提交提示时,在 CSC 处理它之前
PreToolUse在工具调用执行之前。可以阻止它
PermissionRequest当权限对话框出现时
PermissionDenied当工具调用被自动模式分类器拒绝时。返回 {retry: true} 告知模型可以重试被拒绝的工具调用
PostToolUse在工具调用成功之后
PostToolUseFailure在工具调用失败之后
Notification当 CSC 发送通知时
SubagentStart当子代理被创建时
SubagentStop当子代理完成时
TaskCreated当通过 TaskCreate 创建任务时
TaskCompleted当任务被标记为完成时
Stop当 CSC 完成响应时
StopFailure当轮次因 API 错误而结束时。输出和退出码被忽略
TeammateIdle当代理团队的队友即将进入空闲状态时
InstructionsLoaded当 AGENTS.md 或 .costrict/rules/*.md 文件被加载到上下文中时。在会话开始时以及文件在会话期间被延迟加载时触发
ConfigChange当配置文件在会话期间更改时
CwdChanged当工作目录更改时,例如当 CSC 执行 cd 命令时。适用于与 direnv 等工具的响应式环境管理
FileChanged当监视的文件在磁盘上更改时。matcher 字段指定要监视的文件名
WorktreeCreate当通过 --worktree 或 isolation: "worktree" 创建工作树时。替换默认的 git 行为
WorktreeRemove当工作树被移除时,要么在会话退出时,要么在子代理完成时
PreCompact在上下文压缩之前
PostCompact在上下文压缩完成之后
Elicitation当 MCP 服务器在工具调用期间请求用户输入时
ElicitationResult在用户响应 MCP 请求后,响应发送回服务器之前
SessionEnd当会话终止时

Hook 如何解析​

要了解这些部分如何组合在一起,请考虑这个阻止破坏性 shell 命令的 PreToolUse hook。matcher 缩小到 Bash 工具调用,if 条件进一步缩小到以 rm 开头的命令,因此 block-rm.sh 仅在两个过滤器都匹配时才会启动:

{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(rm *)",
"command": "\"$CLAUDE_PROJECT_DIR\"/.costrict/hooks/block-rm.sh"
}
]
}
]
}
}

脚本从 stdin 读取 JSON 输入,提取命令,如果包含 rm -rf 则返回 "deny" 的 permissionDecision:

#!/bin/bash
# .costrict/hooks/block-rm.sh
COMMAND=$(jq -r '.tool_input.command')

if echo "$COMMAND" | grep -q 'rm -rf'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Destructive command blocked by hook"
}
}'
else
exit 0 # allow the command
fi

现在假设 CSC 决定运行 Bash "rm -rf /tmp/build"。以下是发生的事情:

1. 事件触发

PreToolUse 事件触发。CSC 通过 stdin 将工具输入作为 JSON 发送给 hook:

{ "tool_name": "Bash", "tool_input": { "command": "rm -rf /tmp/build" }, ... }

2. 匹配器检查

匹配器 "Bash" 匹配工具名称,因此此 hook 组被激活。如果省略匹配器或使用 "*",则该组在事件的每次发生时都会被激活。

3. If 条件检查

if 条件 "Bash(rm *)" 匹配,因为命令以 rm 开头,因此此处理程序启动。如果命令是 npm test,if 检查将失败,block-rm.sh 将永远不会运行,从而避免了进程启动开销。if 字段是可选的;没有它,匹配组中的每个处理程序都会运行。

4. Hook 处理程序运行

脚本检查完整命令并找到 rm -rf,因此它将决策输出到 stdout:

{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Destructive command blocked by hook"
}
}

如果命令是更安全的 rm 变体,如 rm file.txt,脚本将执行 exit 0,这告诉 CSC 允许工具调用,无需进一步操作。

5. CSC 根据结果采取行动

CSC 读取 JSON 决策,阻止工具调用,并向 CSC 显示原因。

下面的配置部分记录了完整的模式,每个 hook 事件部分记录了你的命令接收什么输入以及可以返回什么输出。

配置​

Hooks 定义在 JSON 设置文件中。配置有三个层级的嵌套:

  1. 选择一个要响应的 hook 事件,如 PreToolUse 或 Stop
  2. 添加一个匹配器组来过滤何时触发,如"仅用于 Bash 工具"
  3. 定义一个或多个在匹配时运行的 hook 处理程序

请参阅上面的 Hook 如何解析以获取带有注释示例的完整演练。

注意: 本页面对每个层级使用特定术语:hook 事件指生命周期点,匹配器组指过滤器,hook 处理程序指运行的 shell 命令、HTTP 端点、提示或代理。"Hook" 本身指的是通用功能。

Hook 位置​

你在哪里定义 hook 决定了它的作用范围:

位置作用范围可共享
~/.costrict/settings.json你的所有项目否,仅限本机
.costrict/settings.json单个项目是,可以提交到仓库
.costrict/settings.local.json单个项目否,被 gitignore
托管策略设置组织范围是,由管理员控制
插件 hooks/hooks.json当插件启用时是,随插件打包
技能或代理 frontmatter当组件处于活动状态时是,定义在组件文件中

有关设置文件解析的详细信息,请参阅设置。企业管理员可以使用 allowManagedHooksOnly 来阻止用户、项目和插件 hooks。在托管设置 enabledPlugins 中强制启用的插件 hooks 豁免,因此管理员可以通过组织市场分发经过审查的 hooks。请参阅 Hook 配置。

匹配器模式​

matcher 字段过滤 hooks 何时触发。匹配器的评估方式取决于它包含的字符:

匹配器值评估为示例
"*"、"" 或省略匹配所有在事件的每次发生时触发
仅包含字母、数字、_ 和 |精确字符串,或 | 分隔的精确字符串列表Bash 仅匹配 Bash 工具;Edit|Write 精确匹配任一工具
包含任何其他字符JavaScript 正则表达式^Notebook 匹配任何以 Notebook 开头的工具;mcp__memory__.* 匹配 memory 服务器的每个工具

FileChanged 事件在构建其监视列表时不遵循这些规则。请参阅 FileChanged。

每个事件类型匹配不同的字段:

事件匹配器过滤的内容匹配器值示例
PreToolUse、PostToolUse、PostToolUseFailure、PermissionRequest、PermissionDenied工具名称Bash、Edit|Write、mcp__.*
SessionStart会话如何启动startup、resume、clear、compact
SessionEnd会话为何结束clear、resume、logout、prompt_input_exit、bypass_permissions_disabled、other
Notification通知类型permission_prompt、idle_prompt、auth_success、elicitation_dialog
SubagentStart代理类型Bash、Explore、Plan 或自定义代理名称
PreCompact、PostCompact什么触发了压缩manual、auto
SubagentStop代理类型与 SubagentStart 相同的值
ConfigChange配置来源user_settings、project_settings、local_settings、policy_settings、skills
CwdChanged不支持匹配器每次目录更改时都触发
FileChanged要监视的字面文件名(请参阅 FileChanged).envrc|.env
StopFailure错误类型rate_limit、authentication_failed、billing_error、invalid_request、server_error、max_output_tokens、unknown
InstructionsLoaded加载原因session_start、nested_traversal、path_glob_match、include、compact
ElicitationMCP 服务器名称你配置的 MCP 服务器名称
ElicitationResultMCP 服务器名称与 Elicitation 相同的值
UserPromptSubmit、Stop、TeammateIdle、TaskCreated、TaskCompleted、WorktreeCreate、WorktreeRemove不支持匹配器每次发生时都触发

匹配器针对 CSC 通过 stdin 发送给你的 hook 的 JSON 输入中的字段运行。对于工具事件,该字段是 tool_name。每个 hook 事件部分列出了该事件的完整匹配器值集和输入模式。

此示例仅在 CSC 写入或编辑文件时运行 lint 脚本:

{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "/path/to/lint-check.sh"
}
]
}
]
}
}

UserPromptSubmit、Stop、TeammateIdle、TaskCreated、TaskCompleted、WorktreeCreate、WorktreeRemove 和 CwdChanged 不支持匹配器,每次发生时都会触发。如果你为这些事件添加 matcher 字段,它会被静默忽略。

对于工具事件,你可以通过在单个 hook 处理程序上设置 if 字段来更精确地过滤。if 使用权限规则语法来匹配工具名称和参数,因此 "Bash(git *)" 仅对 git 命令运行,"Edit(*.ts)" 仅对 TypeScript 文件运行。

匹配 MCP 工具​

MCP 服务器工具在工具事件(PreToolUse、PostToolUse、PostToolUseFailure、PermissionRequest、PermissionDenied)中显示为常规工具,因此你可以像匹配任何其他工具名称一样匹配它们。

MCP 工具遵循命名模式 mcp__<server>__<tool>,例如:

  • mcp__memory__create_entities:Memory 服务器的创建实体工具
  • mcp__filesystem__read_file:Filesystem 服务器的读取文件工具
  • mcp__github__search_repositories:GitHub 服务器的搜索工具

要匹配服务器中的每个工具,请在服务器前缀后追加 .*。.* 是必需的:像 mcp__memory 这样的匹配器仅包含字母和下划线,因此它作为精确字符串进行比较,不匹配任何工具。

  • mcp__memory__.* 匹配 memory 服务器的所有工具
  • mcp__.*__write.* 匹配任何服务器中名称以 write 开头的任何工具

此示例记录所有 memory 服务器操作并验证来自任何 MCP 服务器的写入操作:

{
"hooks": {
"PreToolUse": [
{
"matcher": "mcp__memory__.*",
"hooks": [
{
"type": "command",
"command": "echo 'Memory operation initiated' >> ~/mcp-operations.log"
}
]
},
{
"matcher": "mcp__.*__write.*",
"hooks": [
{
"type": "command",
"command": "/home/user/scripts/validate-mcp-write.py"
}
]
}
]
}
}

Hook 处理程序字段​

内部 hooks 数组中的每个对象都是一个 hook 处理程序:当匹配器匹配时运行的 shell 命令、HTTP 端点、LLM 提示或代理。有四种类型:

  • 命令 hooks(type: "command"):运行 shell 命令。你的脚本通过 stdin 接收事件的 JSON 输入,并通过退出码和 stdout 传回结果。
  • HTTP hooks(type: "http"):将事件的 JSON 输入作为 HTTP POST 请求发送到 URL。端点使用与命令 hooks 相同的 JSON 输出格式通过响应体传回结果。
  • 提示 hooks(type: "prompt"):向 CSC 模型发送提示进行单轮评估。模型以 JSON 返回是/否决策。请参阅基于提示的 hooks。
  • 代理 hooks(type: "agent"):生成可以使用 Read、Grep 和 Glob 等工具的子代理,在返回决策之前验证条件。请参阅基于代理的 hooks。

通用字段​

这些字段适用于所有 hook 类型:

字段必需描述
type是"command"、"http"、"prompt" 或 "agent"
if否权限规则语法,用于过滤此 hook 何时运行,如 "Bash(git *)" 或 "Edit(*.ts)"。hook 仅在工具调用匹配模式时才启动。仅在工具事件上评估:PreToolUse、PostToolUse、PostToolUseFailure、PermissionRequest 和 PermissionDenied。在其他事件上,设置了 if 的 hook 永远不会运行。使用与权限规则相同的语法
timeout否取消前的秒数。默认值:命令 600,提示 30,代理 60
statusMessage否hook 运行时显示的自定义加载消息
once否如果为 true,每个会话仅运行一次然后被移除。仅限技能,不适用于代理。请参阅技能和代理中的 hooks

命令 hook 字段​

除通用字段外,命令 hooks 还接受这些字段:

字段必需描述
command是要执行的 shell 命令
async否如果为 true,在后台运行而不阻塞。请参阅在后台运行 hooks
shell否用于此 hook 的 shell。接受 "bash"(默认)或 "powershell"。设置 "powershell" 在 Windows 上通过 PowerShell 运行命令。不需要 CLAUDE_CODE_USE_POWERSHELL_TOOL,因为 hooks 直接启动 PowerShell

HTTP hook 字段​

除通用字段外,HTTP hooks 还接受这些字段:

字段必需描述
url是发送 POST 请求的 URL
headers否作为键值对的额外 HTTP 头。值支持使用 $VAR_NAME 或 ${VAR_NAME} 语法的环境变量插值。仅解析 allowedEnvVars 中列出的变量
allowedEnvVars否可以插入到头值中的环境变量名称列表。对未列出变量的引用被替换为空字符串。任何环境变量插值都需要此项才能工作

CSC 将 hook 的 JSON 输入作为带有 Content-Type: application/json 的 POST 请求体发送。响应体使用与命令 hooks 相同的 JSON 输出格式。

错误处理与命令 hooks 不同:非 2xx 响应、连接失败和超时都会产生非阻塞错误,允许执行继续。要阻止工具调用或拒绝权限,请返回 2xx 响应,其 JSON 体包含 decision: "block" 或带有 permissionDecision: "deny" 的 hookSpecificOutput。

此示例将 PreToolUse 事件发送到本地验证服务,使用 MY_TOKEN 环境变量中的令牌进行身份验证:

{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "http",
"url": "http://localhost:8080/hooks/pre-tool-use",
"timeout": 30,
"headers": {
"Authorization": "Bearer $MY_TOKEN"
},
"allowedEnvVars": ["MY_TOKEN"]
}
]
}
]
}
}

提示和代理 hook 字段​

除通用字段外,提示和代理 hooks 还接受这些字段:

字段必需描述
prompt是发送给模型的提示文本。使用 $ARGUMENTS 作为 hook 输入 JSON 的占位符
model否用于评估的模型。默认为快速模型

所有匹配的 hooks 并行运行,相同的处理程序会自动去重。命令 hooks 按命令字符串去重,HTTP hooks 按 URL 去重。处理程序在当前目录中使用 CSC 的环境运行。$CLAUDE_CODE_REMOTE 环境变量在远程 Web 环境中设置为 "true",在本地 CLI 中未设置。

通过路径引用脚本​

使用环境变量相对于项目或插件根目录引用 hook 脚本,无论 hook 运行时的工作目录如何:

  • $CLAUDE_PROJECT_DIR:项目根目录。用引号包裹以处理包含空格的路径。
  • ${CLAUDE_PLUGIN_ROOT}:插件的安装目录,用于随插件打包的脚本。每次插件更新时更改。
  • ${CLAUDE_PLUGIN_DATA}:插件的持久数据目录,用于应在插件更新后保留的依赖和状态。

项目脚本​

此示例使用 $CLAUDE_PROJECT_DIR 在任何 Write 或 Edit 工具调用后从项目的 .costrict/hooks/ 目录运行样式检查器:

{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.costrict/hooks/check-style.sh"
}
]
}
]
}
}

插件脚本​

在 hooks/hooks.json 中定义插件 hooks,带有可选的顶级 description 字段。当插件启用时,其 hooks 会与你的用户和项目 hooks 合并。

此示例运行随插件打包的格式化脚本:

{
"description": "Automatic code formatting",
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh",
"timeout": 30
}
]
}
]
}
}

有关创建插件 hooks 的详细信息,请参阅插件组件参考。

技能和代理中的 Hooks​

除了设置文件和插件外,hooks 还可以直接在使用 frontmatter 的技能和子代理中定义。这些 hooks 的作用范围限定在组件的生命周期内,仅在该组件处于活动状态时运行。

支持所有 hook 事件。对于子代理,Stop hooks 会自动转换为 SubagentStop,因为那是子代理完成时触发的事件。

Hooks 使用与基于设置的 hooks 相同的配置格式,但作用范围限定在组件的生命周期内,并在组件完成时清理。

此技能定义了一个 PreToolUse hook,在每个 Bash 命令之前运行安全验证脚本:

---
name: secure-operations
description: Perform operations with security checks
hooks:
PreToolUse:
- matcher: "Bash"
hooks:
- type: command
command: "./scripts/security-check.sh"
---

代理在其 YAML frontmatter 中使用相同的格式。

/hooks 菜单​

在 CSC 中输入 /hooks 以打开配置 hooks 的只读浏览器。菜单显示每个 hook 事件及其已配置 hooks 的计数,让你可以深入查看匹配器,并显示每个 hook 处理程序的完整详细信息。使用它来验证配置、检查 hook 来自哪个设置文件,或检查 hook 的命令、提示或 URL。

菜单显示所有四种 hook 类型:command、prompt、agent 和 http。每个 hook 都标有 [type] 前缀和指示其定义位置的来源:

  • User:来自 ~/.costrict/settings.json
  • Project:来自 .costrict/settings.json
  • Local:来自 .costrict/settings.local.json
  • Plugin:来自插件的 hooks/hooks.json
  • Session:在当前会话的内存中注册
  • Built-in:由 CSC 内部注册

选择一个 hook 会打开一个详细视图,显示其事件、匹配器、类型、源文件以及完整的命令、提示或 URL。菜单是只读的:要添加、修改或移除 hooks,请直接编辑设置 JSON 或要求 CSC 进行更改。

禁用或移除 hooks​

要移除 hook,请从设置 JSON 文件中删除其条目。

要在不移除的情况下临时禁用所有 hooks,请在设置文件中设置 "disableAllHooks": true。无法在保留配置中的单个 hook 的同时禁用它。

disableAllHooks 设置遵循托管设置的层级结构。如果管理员已通过托管策略设置配置了 hooks,则在用户、项目或本地设置中设置的 disableAllHooks 无法禁用那些托管 hooks。只有在托管设置级别设置的 disableAllHooks 才能禁用托管 hooks。

对设置文件中 hooks 的直接编辑通常会由文件监视器自动检测。

Hook 输入和输出​

命令 hooks 通过 stdin 接收 JSON 数据,并通过退出码、stdout 和 stderr 传回结果。HTTP hooks 接收相同的 JSON 作为 POST 请求体,并通过 HTTP 响应体传回结果。本节涵盖所有事件共有的字段和行为。Hook 事件下每个事件的部分包括其特定的输入模式和决策控制选项。

通用输入字段​

Hook 事件除了每个 hook 事件部分中记录的事件特定字段外,还接收这些字段作为 JSON。对于命令 hooks,此 JSON 通过 stdin 到达。对于 HTTP hooks,它作为 POST 请求体到达。

字段描述
session_id当前会话标识符
transcript_path对话 JSON 的路径
cwdhook 被调用时的当前工作目录
permission_mode当前权限模式:"default"、"plan"、"acceptEdits"、"auto"、"dontAsk" 或 "bypassPermissions"。并非所有事件都接收此字段:请查看下面每个事件的 JSON 示例来检查
hook_event_name触发的事件名称

当使用 --agent 运行或在子代理内部时,会包含两个额外的字段:

字段描述
agent_id子代理的唯一标识符。仅在 hook 在子代理调用内部触发时存在。使用它来区分子代理 hook 调用和主线程调用。
agent_type代理名称(例如,"Explore" 或 "security-reviewer")。当会话使用 --agent 或 hook 在子代理内部触发时存在。对于子代理,子代理的类型优先于会话的 --agent 值。

例如,Bash 命令的 PreToolUse hook 通过 stdin 接收以下内容:

{
"session_id": "abc123",
"transcript_path": "/home/user/.costrict/projects/.../transcript.jsonl",
"cwd": "/home/user/my-project",
"permission_mode": "default",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "npm test"
}
}

tool_name 和 tool_input 字段是事件特定的。每个 hook 事件部分记录了该事件的额外字段。

退出码输出​

hook 命令的退出码告诉 CSC 操作是否应继续、被阻止或被忽略。

退出码 0 表示成功。CSC 解析 stdout 以获取 JSON 输出字段。JSON 输出仅在退出码 0 时处理。对于大多数事件,stdout 写入调试日志但不显示在记录中。例外是 UserPromptSubmit 和 SessionStart,其中 stdout 作为 CSC 可以看到并据此行动的上下文添加。

退出码 2 表示阻塞错误。CSC 忽略 stdout 及其中的任何 JSON。相反,stderr 文本作为错误消息反馈给 CSC。效果取决于事件:PreToolUse 阻止工具调用,UserPromptSubmit 拒绝提示,等等。请参阅每个事件的退出码 2 行为以获取完整列表。

任何其他退出码 对于大多数 hook 事件是非阻塞错误。记录中显示 <hook name> hook error 通知,后跟 stderr 的第一行,因此你可以在不使用 --debug 的情况下识别原因。执行继续,完整的 stderr 写入调试日志。

例如,一个阻止危险 Bash 命令的 hook 命令脚本:

#!/bin/bash
# Reads JSON input from stdin, checks the command
command=$(jq -r '.tool_input.command' < /dev/stdin)

if [[ "$command" == rm* ]]; then
echo "Blocked: rm commands are not allowed" >&2
exit 2 # Blocking error: tool call is prevented
fi

exit 0 # Success: tool call proceeds

⚠️ 警告: 对于大多数 hook 事件,只有退出码 2 会阻止操作。CSC 将退出码 1 视为非阻塞错误并继续执行操作,即使 1 是传统的 Unix 失败码。如果你的 hook 旨在强制执行策略,请使用 exit 2。例外是 WorktreeCreate,其中任何非零退出码都会中止工作树创建。

每个事件的退出码 2 行为​

退出码 2 是 hook 发出"停止,不要这样做"信号的方式。效果取决于事件,因为有些事件代表可以被阻止的操作(如尚未发生的工具调用),而其他事件代表已经发生或无法阻止的事情。

Hook 事件可以阻止?退出码 2 时发生什么
PreToolUse是阻止工具调用
PermissionRequest是拒绝权限
UserPromptSubmit是阻止提示处理并清除提示
Stop是阻止 CSC 停止,继续对话
SubagentStop是阻止子代理停止
TeammateIdle是阻止队友进入空闲状态(队友继续工作)
TaskCreated是回滚任务创建
TaskCompleted是阻止任务被标记为完成
ConfigChange是阻止配置更改生效(policy_settings 除外)
StopFailure否输出和退出码被忽略
PostToolUse否向 CSC 显示 stderr(工具已运行)
PostToolUseFailure否向 CSC 显示 stderr(工具已失败)
PermissionDenied否退出码和 stderr 被忽略(拒绝已发生)。使用 JSON hookSpecificOutput.retry: true 告知模型可以重试
Notification否仅向用户显示 stderr
SubagentStart否仅向用户显示 stderr
SessionStart否仅向用户显示 stderr
SessionEnd否仅向用户显示 stderr
CwdChanged否仅向用户显示 stderr
FileChanged否仅向用户显示 stderr
PreCompact否仅向用户显示 stderr
PostCompact否仅向用户显示 stderr
Elicitation是拒绝请求
ElicitationResult是阻止响应(操作变为拒绝)
WorktreeCreate是任何非零退出码都会导致工作树创建失败
WorktreeRemove否失败仅在调试模式下记录
InstructionsLoaded否退出码被忽略

HTTP 响应处理​

HTTP hooks 使用 HTTP 状态码和响应体代替退出码和 stdout:

  • 2xx 且响应体为空:成功,等同于退出码 0 且无输出
  • 2xx 且响应体为纯文本:成功,文本作为上下文添加
  • 2xx 且响应体为 JSON:成功,使用与命令 hooks 相同的 JSON 输出模式解析
  • 非 2xx 状态:非阻塞错误,执行继续
  • 连接失败或超时:非阻塞错误,执行继续

与命令 hooks 不同,HTTP hooks 无法仅通过状态码发出阻塞错误信号。要阻止工具调用或拒绝权限,请返回 2xx 响应,其 JSON 体包含适当的决策字段。

JSON 输出​

退出码让你允许或阻止,但 JSON 输出给你更细粒度的控制。与其以退出码 2 退出来阻止,不如退出码 0 并将 JSON 对象打印到 stdout。CSC 从该 JSON 中读取特定字段来控制行为,包括用于阻止、允许或升级给用户的决策控制。

注意: 你必须为每个 hook 选择一种方法,而不是两者兼用:要么仅使用退出码进行信号传递,要么退出码 0 并打印 JSON 进行结构化控制。CSC 仅在退出码 0 时处理 JSON。如果你退出码 2,任何 JSON 都会被忽略。

你的 hook 的 stdout 必须仅包含 JSON 对象。如果你的 shell 配置文件在启动时打印文本,它可能会干扰 JSON 解析。请参阅故障排除指南中的 JSON 验证失败。

注入到上下文中的 hook 输出(additionalContext、systemMessage 或纯 stdout)上限为 10,000 个字符。超过此限制的输出会保存到文件并替换为预览和文件路径,与大型工具结果的处理方式相同。

JSON 对象支持三种字段:

  • 通用字段 如 continue 适用于所有事件。这些列在下表中。
  • 顶级 decision 和 reason 被某些事件用于阻止或提供反馈。
  • hookSpecificOutput 是需要更丰富控制的事件的嵌套对象。它需要一个设置为事件名称的 hookEventName 字段。
字段默认值描述
continuetrue如果为 false,CSC 在 hook 运行后完全停止处理。优先于任何事件特定的决策字段
stopReason无当 continue 为 false 时向用户显示的消息。不向 CSC 显示
suppressOutputfalse如果为 true,从调试日志中省略 stdout
systemMessage无向用户显示的警告消息

要完全停止 CSC,无论事件类型如何:

{ "continue": false, "stopReason": "Build failed, fix errors before continuing" }

决策控制​

并非每个事件都支持通过 JSON 阻止或控制行为。支持的事件各自使用不同的字段集来表达该决策。在编写 hook 之前,使用此表作为快速参考:

事件决策模式关键字段
UserPromptSubmit, PostToolUse, PostToolUseFailure, Stop, SubagentStop, ConfigChange顶级 decisiondecision: "block"、reason
TeammateIdle, TaskCreated, TaskCompleted退出码或 continue: false退出码 2 通过 stderr 反馈阻止操作。JSON {"continue": false, "stopReason": "..."} 也会完全停止队友,匹配 Stop hook 行为
PreToolUsehookSpecificOutputpermissionDecision(allow/deny/ask/defer)、permissionDecisionReason
PermissionRequesthookSpecificOutputdecision.behavior(allow/deny)
PermissionDeniedhookSpecificOutputretry: true 告知模型可以重试被拒绝的工具调用
WorktreeCreate路径返回命令 hook 在 stdout 上打印路径;HTTP hook 返回 hookSpecificOutput.worktreePath。Hook 失败或缺少路径会导致创建失败
ElicitationhookSpecificOutputaction(accept/decline/cancel)、content(accept 的表单字段值)
ElicitationResulthookSpecificOutputaction(accept/decline/cancel)、content(表单字段值覆盖)
WorktreeRemove, Notification, SessionEnd, PreCompact, PostCompact, InstructionsLoaded, StopFailure, CwdChanged, FileChanged无无决策控制。用于日志记录或清理等副作用

以下是每种模式的实际示例:

顶级 decision​

用于 UserPromptSubmit、PostToolUse、PostToolUseFailure、Stop、SubagentStop 和 ConfigChange。唯一的值是 "block"。要允许操作继续,请从 JSON 中省略 decision,或者退出码 0 且不带任何 JSON:

{
"decision": "block",
"reason": "Test suite must pass before proceeding"
}

PreToolUse​

使用 hookSpecificOutput 进行更丰富的控制:允许、拒绝或升级给用户。你还可以在工具运行之前修改工具输入或为 CSC 注入额外的上下文。请参阅 PreToolUse 决策控制以获取完整的选项集。

{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Database writes are not allowed"
}
}

PermissionRequest​

使用 hookSpecificOutput 代表用户允许或拒绝权限请求。允许时,你还可以修改工具的输入或应用权限规则,这样用户就不会再次被提示。请参阅 PermissionRequest 决策控制以获取完整的选项集。

{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "allow",
"updatedInput": {
"command": "npm run lint"
}
}
}
}

有关扩展示例,包括 Bash 命令验证、提示过滤和自动批准脚本,请参阅指南中的你可以自动化什么以及 Bash 命令验证器参考实现。

Hook 事件​

每个事件对应 CSC 生命周期中可以运行 hooks 的一个点。以下部分按生命周期顺序排列:从会话设置到智能体循环再到会话结束。每个部分描述事件何时触发、支持哪些匹配器、接收什么 JSON 输入,以及如何通过输出控制行为。

SessionStart​

当 CSC 开始新会话或恢复现有会话时运行。适用于加载开发上下文,如现有问题或代码库的最近更改,或设置环境变量。对于不需要脚本的静态上下文,请改用 AGENTS.md。

SessionStart 在每个会话上运行,因此请保持这些 hooks 快速。仅支持 type: "command" hooks。

匹配器值对应于会话的启动方式:

匹配器触发时机
startup新会话
resume--resume、--continue 或 /resume
clear/clear
compact自动或手动压缩

SessionStart 输入​

除通用输入字段外,SessionStart hooks 接收 source、model 和可选的 agent_type。source 字段指示会话如何启动:"startup" 表示新会话,"resume" 表示恢复的会话,"clear" 表示 /clear 之后,"compact" 表示压缩之后。model 字段包含模型标识符。如果你使用 csc --agent <name> 启动 CSC,agent_type 字段包含代理名称。

{
"session_id": "abc123",
"transcript_path": "/Users/.../.costrict/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "SessionStart",
"source": "startup",
"model": "claude-sonnet-4-6"
}

SessionStart 决策控制​

你的 hook 脚本打印到 stdout 的任何文本都会作为 CSC 的上下文添加。除了所有 hooks 可用的 JSON 输出字段外,你还可以返回这些事件特定的字段:

字段描述
additionalContext添加到 CSC 上下文的字符串。多个 hooks 的值会连接起来
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "My additional context here"
}
}

持久化环境变量​

SessionStart hooks 可以访问 CLAUDE_ENV_FILE 环境变量,它提供了一个文件路径,你可以在其中为后续 Bash 命令持久化环境变量。

要设置单个环境变量,请将 export 语句写入 CLAUDE_ENV_FILE。使用追加(>>)来保留其他 hooks 设置的变量:

#!/bin/bash

if [ -n "$CLAUDE_ENV_FILE" ]; then
echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
echo 'export DEBUG_LOG=true' >> "$CLAUDE_ENV_FILE"
echo 'export PATH="$PATH:./node_modules/.bin"' >> "$CLAUDE_ENV_FILE"
fi

exit 0

要从设置命令中捕获所有环境更改,请比较之前和之后的导出变量:

#!/bin/bash

ENV_BEFORE=$(export -p | sort)

# Run your setup commands that modify the environment
source ~/.nvm/nvm.sh
nvm use 20

if [ -n "$CLAUDE_ENV_FILE" ]; then
ENV_AFTER=$(export -p | sort)
comm -13 <(echo "$ENV_BEFORE") <(echo "$ENV_AFTER") >> "$CLAUDE_ENV_FILE"
fi

exit 0

写入此文件的任何变量将在 CSC 在会话期间执行的所有后续 Bash 命令中可用。

注意: CLAUDE_ENV_FILE 可用于 SessionStart、CwdChanged 和 FileChanged hooks。其他 hook 类型无法访问此变量。

InstructionsLoaded​

当 AGENTS.md 或 .costrict/rules/*.md 文件被加载到上下文中时触发。此事件在会话开始时为急切加载的文件触发,稍后当文件被延迟加载时再次触发,例如当 CSC 访问包含嵌套 AGENTS.md 的子目录或具有 paths: frontmatter 的条件规则匹配时。该 hook 不支持阻止或决策控制。它出于可观察性目的异步运行。

匹配器针对 load_reason 运行。例如,使用 "matcher": "session_start" 仅在会话开始时为文件触发,或使用 "matcher": "path_glob_match|nested_traversal" 仅在延迟加载时触发。

InstructionsLoaded 输入​

除通用输入字段外,InstructionsLoaded hooks 接收这些字段:

字段描述
file_path被加载的指令文件的绝对路径
memory_type文件的范围:"User"、"Project"、"Local" 或 "Managed"
load_reason文件被加载的原因:"session_start"、"nested_traversal"、"path_glob_match"、"include" 或 "compact"。"compact" 值在压缩事件后重新加载指令文件时触发
globs文件 paths: frontmatter 中的路径 glob 模式(如果有)。仅在 path_glob_match 加载时存在
trigger_file_path触发此加载的文件访问路径,用于延迟加载
parent_file_path包含此文件的父指令文件路径,用于 include 加载
{
"session_id": "abc123",
"transcript_path": "/Users/.../.costrict/projects/.../transcript.jsonl",
"cwd": "/Users/my-project",
"hook_event_name": "InstructionsLoaded",
"file_path": "/Users/my-project/AGENTS.md",
"memory_type": "Project",
"load_reason": "session_start"
}

InstructionsLoaded 决策控制​

InstructionsLoaded hooks 没有决策控制。它们无法阻止或修改指令加载。使用此事件进行审计日志记录、合规跟踪或可观察性。

UserPromptSubmit​

当用户提交提示时运行,在 CSC 处理它之前。这允许你根据提示/对话添加额外的上下文、验证提示或阻止某些类型的提示。

UserPromptSubmit 输入​

除通用输入字段外,UserPromptSubmit hooks 接收包含用户提交文本的 prompt 字段。

{
"session_id": "abc123",
"transcript_path": "/Users/.../.costrict/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "UserPromptSubmit",
"prompt": "Write a function to calculate the factorial of a number"
}

UserPromptSubmit 决策控制​

UserPromptSubmit hooks 可以控制用户提示是否被处理并添加上下文。所有 JSON 输出字段都可用。

在退出码 0 时有两种方法可以向对话添加上下文:

  • 纯文本 stdout:写入 stdout 的任何非 JSON 文本都作为上下文添加
  • 带有 additionalContext 的 JSON:使用下面的 JSON 格式进行更多控制。additionalContext 字段作为上下文添加

纯 stdout 在记录中显示为 hook 输出。additionalContext 字段更隐蔽地添加。

要阻止提示,请返回 decision 设置为 "block" 的 JSON 对象:

字段描述
decision"block" 阻止提示被处理并将其从上下文中删除。省略以允许提示继续
reason当 decision 为 "block" 时向用户显示。不添加到上下文
additionalContext添加到 CSC 上下文的字符串
sessionTitle设置会话标题,与 /rename 效果相同。用于根据提示内容自动命名会话
{
"decision": "block",
"reason": "Explanation for decision",
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": "My additional context here",
"sessionTitle": "My session title"
}
}

注意: JSON 格式对于简单用例不是必需的。要添加上下文,你可以在退出码 0 时将纯文本打印到 stdout。当你需要阻止提示或想要更结构化的控制时使用 JSON。

PreToolUse​

在 CSC 创建工具参数之后、处理工具调用之前运行。按工具名称匹配:Bash、Edit、Write、Read、Glob、Grep、Agent、WebFetch、WebSearch、AskUserQuestion、ExitPlanMode 以及任何 MCP 工具名称。

使用 PreToolUse 决策控制来允许、拒绝、询问或延迟工具调用。

PreToolUse 输入​

除通用输入字段外,PreToolUse hooks 接收 tool_name、tool_input 和 tool_use_id。tool_input 字段取决于工具:

Bash​

执行 shell 命令。

字段类型示例描述
commandstring"npm test"要执行的 shell 命令
descriptionstring"Run test suite"命令功能的可选描述
timeoutnumber120000可选的超时时间(毫秒)
run_in_backgroundbooleanfalse是否在后台运行命令
Write​

创建或覆盖文件。

字段类型示例描述
file_pathstring"/path/to/file.txt"要写入的文件的绝对路径
contentstring"file content"要写入文件的内容
Edit​

替换现有文件中的字符串。

字段类型示例描述
file_pathstring"/path/to/file.txt"要编辑的文件的绝对路径
old_stringstring"original text"要查找和替换的文本
new_stringstring"replacement text"替换文本
replace_allbooleanfalse是否替换所有出现
Read​

读取文件内容。

字段类型示例描述
file_pathstring"/path/to/file.txt"要读取的文件的绝对路径
offsetnumber10可选的起始读取行号
limitnumber50可选的读取行数
Glob​

查找匹配 glob 模式的文件。

字段类型示例描述
patternstring"**/*.ts"用于匹配文件的 glob 模式
pathstring"/path/to/dir"可选的搜索目录。默认为当前工作目录
Grep​

使用正则表达式搜索文件内容。

字段类型示例描述
patternstring"TODO.*fix"要搜索的正则表达式模式
pathstring"/path/to/dir"可选的搜索文件或目录
globstring"*.ts"可选的用于过滤文件的 glob 模式
output_modestring"content""content"、"files_with_matches" 或 "count"。默认为 "files_with_matches"
-ibooleantrue不区分大小写搜索
multilinebooleanfalse启用多行匹配
WebFetch​

获取并处理 Web 内容。

字段类型示例描述
urlstring"https://example.com/api"获取内容的 URL
promptstring"Extract the API endpoints"对获取的内容运行的提示
WebSearch​

搜索 Web。

字段类型示例描述
querystring"react hooks best practices"搜索查询
allowed_domainsarray["docs.example.com"]可选:仅包含来自这些域的结果
blocked_domainsarray["spam.example.com"]可选:排除来自这些域的结果
Agent​

生成子代理。

字段类型示例描述
promptstring"Find all API endpoints"代理要执行的任务
descriptionstring"Find API endpoints"任务的简短描述
subagent_typestring"Explore"要使用的专业代理类型
modelstring"sonnet"可选的模型别名以覆盖默认值
AskUserQuestion​

向用户提出一到四个多选题。

字段类型示例描述
questionsarray[{"question": "Which framework?", "header": "Framework", "options": [{"label": "React"}], "multiSelect": false}]要呈现的问题,每个问题包含 question 字符串、简短的 header、options 数组和可选的 multiSelect 标志
answersobject{"Which framework?": "React"}可选。将问题文本映射到选定的选项标签。多选答案用逗号连接标签。CSC 不会设置此字段;通过 updatedInput 提供它以编程方式回答

PreToolUse 决策控制​

PreToolUse hooks 可以控制工具调用是否继续。与使用顶级 decision 字段的其他 hooks 不同,PreToolUse 在 hookSpecificOutput 对象内返回其决策。这给了它更丰富的控制:四种结果(允许、拒绝、询问或延迟)加上在执行之前修改工具输入的能力。

字段描述
permissionDecision"allow" 跳过权限提示。"deny" 阻止工具调用。"ask" 提示用户确认。"defer" 优雅退出以便稍后恢复工具。当 hook 返回 "allow" 时,拒绝和询问规则仍然适用
permissionDecisionReason对于 "allow" 和 "ask",向用户显示但不向 CSC 显示。对于 "deny",向 CSC 显示。对于 "defer",忽略
updatedInput在执行之前修改工具的输入参数。替换整个输入对象,因此请包含未更改的字段以及修改后的字段。与 "allow" 结合以自动批准,或与 "ask" 结合以向用户显示修改后的输入。对于 "defer",忽略
additionalContext在工具执行之前添加到 CSC 上下文的字符串。对于 "defer",忽略

当多个 PreToolUse hooks 返回不同的决策时,优先级为 deny > defer > ask > allow。

当 hook 返回 "ask" 时,向用户显示的权限提示包含标识 hook 来源的标签:例如,[User]、[Project]、[Plugin] 或 [Local]。这有助于用户了解哪个配置源正在请求确认。

{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "My reason here",
"updatedInput": {
"field_to_modify": "new value"
},
"additionalContext": "Current environment: production. Proceed with caution."
}
}

AskUserQuestion 和 ExitPlanMode 需要用户交互,通常在使用 -p 标志的非交互模式下会阻塞。返回 permissionDecision: "allow" 加上 updatedInput 可以满足该要求:hook 从 stdin 读取工具的输入,通过你自己的 UI 收集答案,并在 updatedInput 中返回它,这样工具无需提示即可运行。仅返回 "allow" 对这些工具是不够的。对于 AskUserQuestion,回显原始的 questions 数组并添加一个 answers 对象,将每个问题的文本映射到所选答案。

注意: PreToolUse 以前使用顶级的 decision 和 reason 字段,但这些在此事件中已弃用。请改用 hookSpecificOutput.permissionDecision 和 hookSpecificOutput.permissionDecisionReason。已弃用的值 "approve" 和 "block" 分别映射到 "allow" 和 "deny"。其他事件如 PostToolUse 和 Stop 继续使用顶级 decision 和 reason 作为其当前格式。

延迟工具调用以便稍后处理​

"defer" 适用于将 csc -p 作为子进程运行并读取其 JSON 输出的集成,例如 Agent SDK 应用程序或基于 CSC 构建的自定义 UI。它让调用进程可以在工具调用处暂停 CSC,通过自己的界面收集输入,并从停下的地方恢复。CSC 仅在使用 -p 标志的非交互模式下接受此值。在交互会话中,它会记录警告并忽略 hook 结果。

注意: defer 值需要 CSC v2.1.89 或更高版本。早期版本无法识别它,工具会通过正常的权限流程继续。

AskUserQuestion 工具是典型情况:CSC 想问用户一些事情,但没有终端可以回答。往返过程如下:

  1. CSC 调用 AskUserQuestion。PreToolUse hook 触发。
  2. hook 返回 permissionDecision: "defer"。工具不执行。进程以 stop_reason: "tool_deferred" 退出,待处理的工具调用保留在记录中。
  3. 调用进程从 SDK 结果中读取 deferred_tool_use,在其自己的 UI 中显示问题,并等待答案。
  4. 调用进程运行 csc -p --resume <session-id>。相同的工具调用再次触发 PreToolUse。
  5. hook 返回 permissionDecision: "allow" 并在 updatedInput 中附带答案。工具执行,CSC 继续。

deferred_tool_use 字段携带工具的 id、name 和 input。input 是 CSC 为工具调用生成的参数,在执行之前捕获:

{
"type": "result",
"subtype": "success",
"stop_reason": "tool_deferred",
"session_id": "abc123",
"deferred_tool_use": {
"id": "toolu_01abc",
"name": "AskUserQuestion",
"input": { "questions": [{ "question": "Which framework?", "header": "Framework", "options": [{"label": "React"}, {"label": "Vue"}], "multiSelect": false }] }
}
}

没有超时或重试限制。会话保留在磁盘上,直到你恢复它。如果恢复时答案还没准备好,hook 可以再次返回 "defer",进程以相同方式退出。调用进程通过最终从 hook 返回 "allow" 或 "deny" 来控制何时打破循环。

"defer" 仅在 CSC 在轮次中发出单个工具调用时有效。如果 CSC 同时发出多个工具调用,"defer" 会被忽略并显示警告,工具通过正常的权限流程继续。存在此约束是因为恢复只能重新运行一个工具:没有办法从一批调用中延迟一个而不让其他调用未解决。

如果延迟的工具在你恢复时不再可用,进程会在 hook 触发之前以 stop_reason: "tool_deferred_unavailable" 和 is_error: true 退出。当提供该工具的 MCP 服务器未为恢复的会话连接时会发生这种情况。deferred_tool_use 负载仍会包含,以便你识别哪个工具缺失了。

⚠️ 警告: --resume 不会从之前的会话恢复权限模式。在恢复时传递与工具被延迟时活动的相同的 --permission-mode 标志。如果模式不同,CSC 会记录警告。

PermissionRequest​

当用户看到权限对话框时运行。使用 PermissionRequest 决策控制代表用户允许或拒绝。

按工具名称匹配,与 PreToolUse 相同的值。

PermissionRequest 输入​

PermissionRequest hooks 接收与 PreToolUse hooks 相同的 tool_name 和 tool_input 字段,但没有 tool_use_id。可选的 permission_suggestions 数组包含用户通常在权限对话框中看到的"始终允许"选项。区别在于 hook 何时触发:PermissionRequest hooks 在权限对话框即将显示给用户时运行,而 PreToolUse hooks 在工具执行之前运行,无论权限状态如何。

{
"session_id": "abc123",
"transcript_path": "/Users/.../.costrict/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "PermissionRequest",
"tool_name": "Bash",
"tool_input": {
"command": "rm -rf node_modules",
"description": "Remove node_modules directory"
},
"permission_suggestions": [
{
"type": "addRules",
"rules": [{ "toolName": "Bash", "ruleContent": "rm -rf node_modules" }],
"behavior": "allow",
"destination": "localSettings"
}
]
}

PermissionRequest 决策控制​

PermissionRequest hooks 可以允许或拒绝权限请求。除了所有 hooks 可用的 JSON 输出字段外,你的 hook 脚本可以返回带有这些事件特定字段的 decision 对象:

字段描述
behavior"allow" 授予权限,"deny" 拒绝权限
updatedInput仅用于 "allow":在执行之前修改工具的输入参数。替换整个输入对象,因此请包含未更改的字段以及修改后的字段
updatedPermissions仅用于 "allow":要应用的权限更新条目数组,例如添加允许规则或更改会话权限模式
message仅用于 "deny":告诉 CSC 为什么权限被拒绝
interrupt仅用于 "deny":如果为 true,停止 CSC
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "allow",
"updatedInput": {
"command": "npm run lint"
}
}
}
}

权限更新条目​

updatedPermissions 输出字段和 permission_suggestions 输入字段都使用相同的条目对象数组。每个条目有一个 type 来决定其其他字段,以及一个 destination 来控制更改写入的位置。

type字段效果
addRulesrules、behavior、destination添加权限规则。rules 是 {toolName, ruleContent?} 对象的数组。省略 ruleContent 以匹配整个工具。behavior 是 "allow"、"deny" 或 "ask"
replaceRulesrules、behavior、destination用提供的 rules 替换 destination 处给定 behavior 的所有规则
removeRulesrules、behavior、destination移除给定 behavior 的匹配规则
setModemode、destination更改权限模式。有效模式为 default、acceptEdits、dontAsk、bypassPermissions 和 plan
addDirectoriesdirectories、destination添加工作目录。directories 是路径字符串数组
removeDirectoriesdirectories、destination移除工作目录

每个条目上的 destination 字段决定更改是保留在内存中还是持久化到设置文件。

destination写入位置
session仅在内存中,会话结束时丢弃
localSettings.costrict/settings.local.json
projectSettings.costrict/settings.json
userSettings~/.costrict/settings.json

hook 可以将其接收的 permission_suggestions 之一作为自己的 updatedPermissions 输出回显,这等同于用户在对话框中选择该"始终允许"选项。

PostToolUse​

在工具成功完成后立即运行。

按工具名称匹配,与 PreToolUse 相同的值。

PostToolUse 输入​

PostToolUse hooks 在工具已经成功执行后触发。输入包括 tool_input(发送给工具的参数)和 tool_response(它返回的结果)。两者的确切模式取决于工具。

{
"session_id": "abc123",
"transcript_path": "/Users/.../.costrict/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "PostToolUse",
"tool_name": "Write",
"tool_input": {
"file_path": "/path/to/file.txt",
"content": "file content"
},
"tool_response": {
"filePath": "/path/to/file.txt",
"success": true
},
"tool_use_id": "toolu_01ABC123..."
}

PostToolUse 决策控制​

PostToolUse hooks 可以在工具执行后向 CSC 提供反馈。除了所有 hooks 可用的 JSON 输出字段外,你的 hook 脚本可以返回这些事件特定的字段:

字段描述
decision"block" 以 reason 提示 CSC。省略以允许操作继续
reason当 decision 为 "block" 时向 CSC 显示的解释
additionalContext供 CSC 考虑的额外上下文
updatedMCPToolOutput仅用于 MCP 工具:用提供的值替换工具的输出
{
"decision": "block",
"reason": "Explanation for decision",
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "Additional information for CSC"
}
}

PostToolUseFailure​

当工具执行失败时运行。此事件在抛出错误或返回失败结果的工具调用时触发。使用它来记录失败、发送警报或向 CSC 提供纠正反馈。

按工具名称匹配,与 PreToolUse 相同的值。

PostToolUseFailure 输入​

PostToolUseFailure hooks 接收与 PostToolUse 相同的 tool_name 和 tool_input 字段,以及作为顶级字段的错误信息:

{
"session_id": "abc123",
"transcript_path": "/Users/.../.costrict/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "PostToolUseFailure",
"tool_name": "Bash",
"tool_input": {
"command": "npm test",
"description": "Run test suite"
},
"tool_use_id": "toolu_01ABC123...",
"error": "Command exited with non-zero status code 1",
"is_interrupt": false
}
字段描述
error描述出了什么问题的字符串
is_interrupt可选布尔值,指示失败是否由用户中断引起

PostToolUseFailure 决策控制​

PostToolUseFailure hooks 可以在工具失败后向 CSC 提供上下文。除了所有 hooks 可用的 JSON 输出字段外,你的 hook 脚本可以返回这些事件特定的字段:

字段描述
additionalContext供 CSC 与错误一起考虑的额外上下文
{
"hookSpecificOutput": {
"hookEventName": "PostToolUseFailure",
"additionalContext": "Additional information about the failure for CSC"
}
}

PermissionDenied​

当自动模式分类器拒绝工具调用时运行。此 hook 仅在自动模式下触发:当你手动拒绝权限对话框、当 PreToolUse hook 阻止调用或当 deny 规则匹配时,它不会运行。使用它来记录分类器拒绝、调整配置或告知模型可以重试工具调用。

按工具名称匹配,与 PreToolUse 相同的值。

PermissionDenied 输入​

除通用输入字段外,PermissionDenied hooks 接收 tool_name、tool_input、tool_use_id 和 reason。

{
"session_id": "abc123",
"transcript_path": "/Users/.../.costrict/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "auto",
"hook_event_name": "PermissionDenied",
"tool_name": "Bash",
"tool_input": {
"command": "rm -rf /tmp/build",
"description": "Clean build directory"
},
"tool_use_id": "toolu_01ABC123...",
"reason": "Auto mode denied: command targets a path outside the project"
}
字段描述
reason分类器对工具调用为何被拒绝的解释

PermissionDenied 决策控制​

PermissionDenied hooks 可以告知模型可以重试被拒绝的工具调用。返回一个 hookSpecificOutput.retry 设置为 true 的 JSON 对象:

{
"hookSpecificOutput": {
"hookEventName": "PermissionDenied",
"retry": true
}
}

当 retry 为 true 时,CSC 向对话添加一条消息,告知模型可以重试工具调用。拒绝本身不会被撤回。如果你的 hook 不返回 JSON,或返回 retry: false,拒绝仍然有效,模型收到原始拒绝消息。

Notification​

当 CSC 发送通知时运行。按通知类型匹配:permission_prompt、idle_prompt、auth_success、elicitation_dialog`。省略匹配器以为所有通知类型运行 hooks。

使用单独的匹配器根据通知类型运行不同的处理程序。此配置在 CSC 需要权限批准时触发特定于权限的警报脚本,在 CSC 空闲时触发不同的通知:

{
"hooks": {
"Notification": [
{
"matcher": "permission_prompt",
"hooks": [
{
"type": "command",
"command": "/path/to/permission-alert.sh"
}
]
},
{
"matcher": "idle_prompt",
"hooks": [
{
"type": "command",
"command": "/path/to/idle-notification.sh"
}
]
}
]
}
}

Notification 输入​

除通用输入字段外,Notification hooks 接收带有通知文本的 message、可选的 title 以及指示触发了哪种类型的 notification_type。

{
"session_id": "abc123",
"transcript_path": "/Users/.../.costrict/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "Notification",
"message": "CSC needs your permission to use Bash",
"title": "Permission needed",
"notification_type": "permission_prompt"
}

Notification hooks 无法阻止或修改通知。除了所有 hooks 可用的 JSON 输出字段外,你可以返回 additionalContext 以向对话添加上下文:

字段描述
additionalContext添加到 CSC 上下文的字符串

SubagentStart​

当通过 Agent 工具生成 CSC 子代理时运行。支持匹配器按代理类型名称过滤(内置代理如 Bash、Explore、Plan,或来自 .costrict/agents/ 的自定义代理名称)。

SubagentStart 输入​

除通用输入字段外,SubagentStart hooks 接收带有子代理唯一标识符的 agent_id 和带有代理名称的 agent_type(内置代理如 "Bash"、"Explore"、"Plan",或自定义代理名称)。

{
"session_id": "abc123",
"transcript_path": "/Users/.../.costrict/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "SubagentStart",
"agent_id": "agent-abc123",
"agent_type": "Explore"
}

SubagentStart hooks 无法阻止子代理创建,但它们可以向子代理注入上下文。除了所有 hooks 可用的 JSON 输出字段外,你可以返回:

字段描述
additionalContext添加到子代理上下文的字符串
{
"hookSpecificOutput": {
"hookEventName": "SubagentStart",
"additionalContext": "Follow security guidelines for this task"
}
}

SubagentStop​

当 CSC 子代理完成响应时运行。按代理类型匹配,与 SubagentStart 相同的值。

SubagentStop 输入​

除通用输入字段外,SubagentStop hooks 接收 stop_hook_active、agent_id、agent_type、agent_transcript_path 和 last_assistant_message。agent_type 字段是用于匹配器过滤的值。transcript_path 是主会话的记录,而 agent_transcript_path 是存储在嵌套 subagents/ 文件夹中的子代理自己的记录。last_assistant_message 字段包含子代理最终响应的文本内容,因此 hooks 可以访问它而无需解析记录文件。

{
"session_id": "abc123",
"transcript_path": "~/.costrict/projects/.../abc123.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "SubagentStop",
"stop_hook_active": false,
"agent_id": "def456",
"agent_type": "Explore",
"agent_transcript_path": "~/.costrict/projects/.../abc123/subagents/agent-def456.jsonl",
"last_assistant_message": "Analysis complete. Found 3 potential issues..."
}

SubagentStop hooks 使用与 Stop hooks 相同的决策控制格式。

TaskCreated​

当通过 TaskCreate 工具创建任务时运行。使用此来强制执行命名约定、要求任务描述或阻止创建某些任务。

当 TaskCreated hook 以退出码 2 退出时,任务不会被创建,stderr 消息作为反馈反馈给模型。要完全停止队友而不是重新运行它,请返回带有 {"continue": false, "stopReason": "..."} 的 JSON。TaskCreated hooks 不支持匹配器,每次发生时都会触发。

TaskCreated 输入​

除通用输入字段外,TaskCreated hooks 接收 task_id、task_subject 和可选的 task_description、teammate_name 和 team_name。

{
"session_id": "abc123",
"transcript_path": "/Users/.../.costrict/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "TaskCreated",
"task_id": "task-001",
"task_subject": "Implement user authentication",
"task_description": "Add login and signup endpoints",
"teammate_name": "implementer",
"team_name": "my-project"
}
字段描述
task_id正在创建的任务的标识符
task_subject任务的标题
task_description任务的详细描述。可能不存在
teammate_name创建任务的队友名称。可能不存在
team_name团队名称。可能不存在

TaskCreated 决策控制​

TaskCreated hooks 支持两种控制任务创建的方式:

  • 退出码 2:任务不会被创建,stderr 消息作为反馈反馈给模型。
  • JSON {"continue": false, "stopReason": "..."}:完全停止队友,匹配 Stop hook 行为。stopReason 向用户显示。

此示例阻止主题不符合所需格式的任务:

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

if [[ ! "$TASK_SUBJECT" =~ ^\[TICKET-[0-9]+\] ]]; then
echo "Task subject must start with a ticket number, e.g. '[TICKET-123] Add feature'" >&2
exit 2
fi

exit 0

TaskCompleted​

当任务被标记为完成时运行。这在两种情况下触发:当任何代理通过 TaskUpdate 工具明确将任务标记为完成时,或当代理团队队友在仍有进行中任务时完成其轮次时。使用此来在任务关闭之前强制执行完成标准,如通过测试或 lint 检查。

当 TaskCompleted hook 以退出码 2 退出时,任务不会被标记为完成,stderr 消息作为反馈反馈给模型。要完全停止队友而不是重新运行它,请返回带有 {"continue": false, "stopReason": "..."} 的 JSON。TaskCompleted hooks 不支持匹配器,每次发生时都会触发。

TaskCompleted 输入​

除通用输入字段外,TaskCompleted hooks 接收 task_id、task_subject 和可选的 task_description、teammate_name 和 team_name。

{
"session_id": "abc123",
"transcript_path": "/Users/.../.costrict/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "TaskCompleted",
"task_id": "task-001",
"task_subject": "Implement user authentication",
"task_description": "Add login and signup endpoints",
"teammate_name": "implementer",
"team_name": "my-project"
}
字段描述
task_id正在完成的任务的标识符
task_subject任务的标题
task_description任务的详细描述。可能不存在
teammate_name完成任务的队友名称。可能不存在
team_name团队名称。可能不存在

TaskCompleted 决策控制​

TaskCompleted hooks 支持两种控制任务完成的方式:

  • 退出码 2:任务不会被标记为完成,stderr 消息作为反馈反馈给模型。
  • JSON {"continue": false, "stopReason": "..."}:完全停止队友,匹配 Stop hook 行为。stopReason 向用户显示。

此示例运行测试并在测试失败时阻止任务完成:

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

# Run the test suite
if ! npm test 2>&1; then
echo "Tests not passing. Fix failing tests before completing: $TASK_SUBJECT" >&2
exit 2
fi

exit 0

Stop​

当主 CSC 代理完成响应时运行。如果停止是由于用户中断引起的,则不运行。API 错误会触发 StopFailure。

Stop 输入​

除通用输入字段外,Stop hooks 接收 stop_hook_active 和 last_assistant_message。当 CSC 已经作为 stop hook 的结果继续时,stop_hook_active 字段为 true。检查此值或处理记录以防止 CSC 无限期运行。last_assistant_message 字段包含 CSC 最终响应的文本内容,因此 hooks 可以访问它而无需解析记录文件。

{
"session_id": "abc123",
"transcript_path": "~/.costrict/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "Stop",
"stop_hook_active": true,
"last_assistant_message": "I've completed the refactoring. Here's a summary..."
}

Stop 决策控制​

Stop 和 SubagentStop hooks 可以控制 CSC 是否继续。除了所有 hooks 可用的 JSON 输出字段外,你的 hook 脚本可以返回这些事件特定的字段:

字段描述
decision"block" 阻止 CSC 停止。省略以允许 CSC 停止
reason当 decision 为 "block" 时必需。告诉 CSC 为什么应该继续
{
"decision": "block",
"reason": "Must be provided when CSC is blocked from stopping"
}

StopFailure​

当轮次因 API 错误结束时代替 Stop 运行。输出和退出码被忽略。使用此来在 CSC 由于速率限制、身份验证问题或其他 API 错误无法完成响应时记录失败、发送警报或采取恢复操作。

StopFailure 输入​

除通用输入字段外,StopFailure hooks 接收 error、可选的 error_details 和可选的 last_assistant_message。error 字段标识错误类型,用于匹配器过滤。

字段描述
error错误类型:rate_limit、authentication_failed、billing_error、invalid_request、server_error、max_output_tokens 或 unknown
error_details关于错误的额外详细信息(如果可用)
last_assistant_message对话中显示的渲染错误文本。与 Stop 和 SubagentStop 中此字段保存 CSC 的对话输出不同,对于 StopFailure,它包含 API 错误字符串本身,如 "API Error: Rate limit reached"
{
"session_id": "abc123",
"transcript_path": "/Users/.../.costrict/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "StopFailure",
"error": "rate_limit",
"error_details": "429 Too Many Requests",
"last_assistant_message": "API Error: Rate limit reached"
}

StopFailure hooks 没有决策控制。它们仅用于通知和日志记录目的。

TeammateIdle​

当代理团队的队友完成其轮次后即将进入空闲状态时运行。使用此在队友停止工作之前强制执行质量门,如要求通过 lint 检查或验证输出文件是否存在。

当 TeammateIdle hook 以退出码 2 退出时,队友接收 stderr 消息作为反馈并继续工作而不是进入空闲状态。要完全停止队友而不是重新运行它,请返回带有 {"continue": false, "stopReason": "..."} 的 JSON。TeammateIdle hooks 不支持匹配器,每次发生时都会触发。

TeammateIdle 输入​

除通用输入字段外,TeammateIdle hooks 接收 teammate_name 和 team_name。

{
"session_id": "abc123",
"transcript_path": "/Users/.../.costrict/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "TeammateIdle",
"teammate_name": "researcher",
"team_name": "my-project"
}
字段描述
teammate_name即将进入空闲状态的队友名称
team_name团队名称

TeammateIdle 决策控制​

TeammateIdle hooks 支持两种控制队友行为的方式:

  • 退出码 2:队友接收 stderr 消息作为反馈并继续工作而不是进入空闲状态。
  • JSON {"continue": false, "stopReason": "..."}:完全停止队友,匹配 Stop hook 行为。stopReason 向用户显示。

此示例在允许队友进入空闲状态之前检查构建产物是否存在:

#!/bin/bash

if [ ! -f "./dist/output.js" ]; then
echo "Build artifact missing. Run the build before stopping." >&2
exit 2
fi

exit 0

ConfigChange​

当配置文件在会话期间更改时运行。使用此来审计设置更改、强制执行安全策略或阻止对配置文件的未授权修改。

ConfigChange hooks 在设置文件、托管策略设置和技能文件更改时触发。输入中的 source 字段告诉你哪种类型的配置发生了更改,可选的 file_path 字段提供更改文件的路径。

匹配器按配置来源过滤:

匹配器触发时机
user_settings~/.costrict/settings.json 更改
project_settings.costrict/settings.json 更改
local_settings.costrict/settings.local.json 更改
policy_settings托管策略设置更改
skills.costrict/skills/ 中的技能文件更改

此示例记录所有配置更改以进行安全审计:

{
"hooks": {
"ConfigChange": [
{
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.costrict/hooks/audit-config-change.sh"
}
]
}
]
}
}

ConfigChange 输入​

除通用输入字段外,ConfigChange hooks 接收 source 和可选的 file_path。source 字段指示哪种配置类型发生了更改,file_path 提供被修改的特定文件的路径。

{
"session_id": "abc123",
"transcript_path": "/Users/.../.costrict/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "ConfigChange",
"source": "project_settings",
"file_path": "/Users/.../my-project/.costrict/settings.json"
}

ConfigChange 决策控制​

ConfigChange hooks 可以阻止配置更改生效。使用退出码 2 或 JSON decision 来阻止更改。被阻止时,新设置不会应用到正在运行的会话。

字段描述
decision"block" 阻止配置更改被应用。省略以允许更改
reason当 decision 为 "block" 时向用户显示的解释
{
"decision": "block",
"reason": "Configuration changes to project settings require admin approval"
}

policy_settings 更改无法被阻止。Hooks 仍然会为 policy_settings 来源触发,因此你可以使用它们进行审计日志记录,但任何阻止决策都会被忽略。这确保了企业管理的设置始终生效。

CwdChanged​

当工作目录在会话期间更改时运行,例如当 CSC 执行 cd 命令时。使用此来响应目录更改:重新加载环境变量、激活项目特定的工具链或自动运行设置脚本。与 FileChanged 配合使用,用于管理每个目录环境的 direnv 等工具。

CwdChanged hooks 可以访问 CLAUDE_ENV_FILE。写入该文件的变量持久化到会话的后续 Bash 命令中,就像 SessionStart hooks 中一样。仅支持 type: "command" hooks。

CwdChanged 不支持匹配器,每次目录更改时都会触发。

CwdChanged 输入​

除通用输入字段外,CwdChanged hooks 接收 old_cwd 和 new_cwd。

{
"session_id": "abc123",
"transcript_path": "/Users/.../.costrict/projects/.../transcript.jsonl",
"cwd": "/Users/my-project/src",
"hook_event_name": "CwdChanged",
"old_cwd": "/Users/my-project",
"new_cwd": "/Users/my-project/src"
}

CwdChanged 输出​

除了所有 hooks 可用的 JSON 输出字段外,CwdChanged hooks 可以返回 watchPaths 来动态设置 FileChanged 监视哪些文件路径:

字段描述
watchPaths绝对路径数组。替换当前的动态监视列表(来自你的 matcher 配置的路径总是被监视)。返回空数组会清除动态列表,这在进入新目录时是典型的

CwdChanged hooks 没有决策控制。它们无法阻止目录更改。

FileChanged​

当监视的文件在磁盘上更改时运行。适用于在项目配置文件被修改时重新加载环境变量。

此事件的 matcher 有两个作用:

  • 构建监视列表:值按 | 分割,每个段在工作目录中注册为字面文件名,因此 ".envrc|.env" 精确监视这两个文件。正则表达式模式在这里没有用:像 ^\.env 这样的值会监视一个名为 ^\.env 的文件。
  • 过滤哪些 hooks 运行:当监视的文件更改时,相同的值使用标准匹配器规则针对更改文件的基本名称过滤哪些 hook 组运行。

FileChanged hooks 可以访问 CLAUDE_ENV_FILE。写入该文件的变量持久化到会话的后续 Bash 命令中,就像 SessionStart hooks 中一样。仅支持 type: "command" hooks。

FileChanged 输入​

除通用输入字段外,FileChanged hooks 接收 file_path 和 event。

字段描述
file_path更改的文件的绝对路径
event发生了什么:"change"(文件被修改)、"add"(文件被创建)或 "unlink"(文件被删除)
{
"session_id": "abc123",
"transcript_path": "/Users/.../.costrict/projects/.../transcript.jsonl",
"cwd": "/Users/my-project",
"hook_event_name": "FileChanged",
"file_path": "/Users/my-project/.envrc",
"event": "change"
}

FileChanged 输出​

除了所有 hooks 可用的 JSON 输出字段外,FileChanged hooks 可以返回 watchPaths 来动态更新监视哪些文件路径:

字段描述
watchPaths绝对路径数组。替换当前的动态监视列表(来自你的 matcher 配置的路径总是被监视)。当你的 hook 脚本基于更改的文件发现要监视的额外文件时使用此功能

FileChanged hooks 没有决策控制。它们无法阻止文件更改的发生。

WorktreeCreate​

当你运行 csc --worktree 或子代理使用 isolation: "worktree" 时,CSC 使用 git worktree 创建一个隔离的工作副本。如果你配置了 WorktreeCreate hook,它会替换默认的 git 行为,让你可以使用不同的版本控制系统,如 SVN、Perforce 或 Mercurial。

由于 hook 完全替换了默认行为,.worktreeinclude 不会被处理。如果你需要将 .env 等本地配置文件复制到新的工作树中,请在 hook 脚本内部完成。

hook 必须返回创建的工作树目录的绝对路径。CSC 使用此路径作为隔离会话的工作目录。命令 hooks 在 stdout 上打印它;HTTP hooks 通过 hookSpecificOutput.worktreePath 返回它。

此示例创建一个 SVN 工作副本并打印路径供 CSC 使用。将仓库 URL 替换为你自己的:

{
"hooks": {
"WorktreeCreate": [
{
"hooks": [
{
"type": "command",
"command": "bash -c 'NAME=$(jq -r .name); DIR=\"$HOME/.costrict/worktrees/$NAME\"; svn checkout https://svn.example.com/repo/trunk \"$DIR\" >&2 && echo \"$DIR\"'"
}
]
}
]
}
}

hook 从 stdin 上的 JSON 输入中读取工作树 name,将新副本检出到新目录中,并打印目录路径。最后一行的 echo 是 CSC 读取为工作树路径的内容。将任何其他输出重定向到 stderr,以免干扰路径。

WorktreeCreate 输入​

除通用输入字段外,WorktreeCreate hooks 接收 name 字段。这是新工作树的 slug 标识符,由用户指定或自动生成(例如,bold-oak-a3f2)。

{
"session_id": "abc123",
"transcript_path": "/Users/.../.costrict/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "WorktreeCreate",
"name": "feature-auth"
}

WorktreeCreate 输出​

WorktreeCreate hooks 不使用标准的允许/阻止决策模型。相反,hook 的成功或失败决定了结果。hook 必须返回创建的工作树目录的绝对路径:

  • 命令 hooks(type: "command"):在 stdout 上打印路径。
  • HTTP hooks(type: "http"):在响应体中返回 { "hookSpecificOutput": { "hookEventName": "WorktreeCreate", "worktreePath": "/absolute/path" } }。

如果 hook 失败或未产生路径,工作树创建将失败并显示错误。

WorktreeRemove​

WorktreeCreate 的清理对应项。此 hook 在工作树被移除时触发,要么在你退出 --worktree 会话并选择移除它时,要么在具有 isolation: "worktree" 的子代理完成时。对于基于 git 的工作树,CSC 使用 git worktree remove 自动处理清理。如果你为非 git 版本控制系统配置了 WorktreeCreate hook,请配对一个 WorktreeRemove hook 来处理清理。没有的话,工作树目录将留在磁盘上。

CSC 将 WorktreeCreate 返回的路径作为 hook 输入中的 worktree_path 传递。此示例读取该路径并移除目录:

{
"hooks": {
"WorktreeRemove": [
{
"hooks": [
{
"type": "command",
"command": "bash -c 'jq -r .worktree_path | xargs rm -rf'"
}
]
}
]
}
}

WorktreeRemove 输入​

除通用输入字段外,WorktreeRemove hooks 接收 worktree_path 字段,即被移除的工作树的绝对路径。

{
"session_id": "abc123",
"transcript_path": "/Users/.../.costrict/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "WorktreeRemove",
"worktree_path": "/Users/.../my-project/.costrict/worktrees/feature-auth"
}

WorktreeRemove hooks 没有决策控制。它们无法阻止工作树移除,但可以执行清理任务,如移除版本控制状态或归档更改。Hook 失败仅在调试模式下记录。

PreCompact​

在 CSC 即将运行压缩操作之前运行。

匹配器值指示压缩是手动触发还是自动触发的:

匹配器触发时机
manual/compact
auto当上下文窗口已满时自动压缩

PreCompact 输入​

除通用输入字段外,PreCompact hooks 接收 trigger 和 custom_instructions。对于 manual,custom_instructions 包含用户传入 /compact 的内容。对于 auto,custom_instructions 为空。

{
"session_id": "abc123",
"transcript_path": "/Users/.../.costrict/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "PreCompact",
"trigger": "manual",
"custom_instructions": ""
}

PostCompact​

在 CSC 完成压缩操作后运行。使用此事件来响应新的压缩状态,例如记录生成的摘要或更新外部状态。

与 PreCompact 相同的匹配器值适用:

匹配器触发时机
manual/compact 之后
auto当上下文窗口已满时自动压缩之后

PostCompact 输入​

除通用输入字段外,PostCompact hooks 接收 trigger 和 compact_summary。compact_summary 字段包含由压缩操作生成的对话摘要。

{
"session_id": "abc123",
"transcript_path": "/Users/.../.costrict/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "PostCompact",
"trigger": "manual",
"compact_summary": "Summary of the compacted conversation..."
}

PostCompact hooks 没有决策控制。它们无法影响压缩结果,但可以执行后续任务。

SessionEnd​

当 CSC 会话结束时运行。适用于清理任务、记录会话统计信息或保存会话状态。支持匹配器按退出原因过滤。

hook 输入中的 reason 字段指示会话为何结束:

原因描述
clear使用 /clear 命令清除会话
resume通过交互式 /resume 切换会话
logout用户登出
prompt_input_exit提示输入可见时用户退出
bypass_permissions_disabled绕过权限模式被禁用
other其他退出原因

SessionEnd 输入​

除通用输入字段外,SessionEnd hooks 接收指示会话为何结束的 reason 字段。请参阅上面的原因表以获取所有值。

{
"session_id": "abc123",
"transcript_path": "/Users/.../.costrict/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "SessionEnd",
"reason": "other"
}

SessionEnd hooks 没有决策控制。它们无法阻止会话终止,但可以执行清理任务。

SessionEnd hooks 的默认超时为 1.5 秒。这适用于会话退出、/clear 和通过交互式 /resume 切换会话。如果你的 hooks 需要更多时间,请将 CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS 环境变量设置为更高的毫秒值。任何每个 hook 的 timeout 设置也受此值限制。

CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS=5000 csc

Elicitation​

当 MCP 服务器在任务中途请求用户输入时运行。默认情况下,CSC 显示一个交互式对话框供用户响应。Hooks 可以拦截此请求并以编程方式响应,完全跳过对话框。

匹配器字段针对 MCP 服务器名称匹配。

Elicitation 输入​

除通用输入字段外,Elicitation hooks 接收 mcp_server_name、message 和可选的 mode、url、elicitation_id 和 requested_schema 字段。

对于表单模式的请求(最常见的情况):

{
"session_id": "abc123",
"transcript_path": "/Users/.../.costrict/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "Elicitation",
"mcp_server_name": "my-mcp-server",
"message": "Please provide your credentials",
"mode": "form",
"requested_schema": {
"type": "object",
"properties": {
"username": { "type": "string", "title": "Username" }
}
}
}

对于 URL 模式的请求(基于浏览器的身份验证):

{
"session_id": "abc123",
"transcript_path": "/Users/.../.costrict/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "Elicitation",
"mcp_server_name": "my-mcp-server",
"message": "Please authenticate",
"mode": "url",
"url": "https://auth.example.com/login"
}

Elicitation 输出​

要以编程方式响应而不显示对话框,请返回带有 hookSpecificOutput 的 JSON 对象:

{
"hookSpecificOutput": {
"hookEventName": "Elicitation",
"action": "accept",
"content": {
"username": "alice"
}
}
}
字段值描述
actionaccept、decline、cancel是接受、拒绝还是取消请求
contentobject要提交的表单字段值。仅在 action 为 accept 时使用

退出码 2 拒绝请求并向用户显示 stderr。

ElicitationResult​

在用户响应 MCP 请求后运行。Hooks 可以在响应发送回 MCP 服务器之前观察、修改或阻止响应。

匹配器字段针对 MCP 服务器名称匹配。

ElicitationResult 输入​

除通用输入字段外,ElicitationResult hooks 接收 mcp_server_name、action 和可选的 mode、elicitation_id 和 content 字段。

{
"session_id": "abc123",
"transcript_path": "/Users/.../.costrict/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "ElicitationResult",
"mcp_server_name": "my-mcp-server",
"action": "accept",
"content": { "username": "alice" },
"mode": "form",
"elicitation_id": "elicit-123"
}

ElicitationResult 输出​

要覆盖用户的响应,请返回带有 hookSpecificOutput 的 JSON 对象:

{
"hookSpecificOutput": {
"hookEventName": "ElicitationResult",
"action": "decline",
"content": {}
}
}
字段值描述
actionaccept、decline、cancel覆盖用户的操作
contentobject覆盖表单字段值。仅在 action 为 accept 时有意义

退出码 2 阻止响应,将有效操作更改为 decline。

基于提示的 Hooks​

除了命令和 HTTP hooks 外,CSC 还支持基于提示的 hooks(type: "prompt"),使用 LLM 评估是否允许或阻止操作,以及代理 hooks(type: "agent"),生成带有工具访问权限的代理验证器。并非所有事件都支持每种 hook 类型。

支持所有四种 hook 类型(command、http、prompt 和 agent)的事件:

  • PermissionRequest
  • PostToolUse
  • PostToolUseFailure
  • PreToolUse
  • Stop
  • SubagentStop
  • TaskCompleted
  • TaskCreated
  • UserPromptSubmit

支持 command 和 http hooks 但不支持 prompt 或 agent 的事件:

  • ConfigChange
  • CwdChanged
  • Elicitation
  • ElicitationResult
  • FileChanged
  • InstructionsLoaded
  • Notification
  • PermissionDenied
  • PostCompact
  • PreCompact
  • SessionEnd
  • StopFailure
  • SubagentStart
  • TeammateIdle
  • WorktreeCreate
  • WorktreeRemove

SessionStart 仅支持 command hooks。

基于提示的 Hooks 如何工作​

与执行 Bash 命令不同,基于提示的 hooks:

  1. 将 hook 输入和你的提示发送给 CSC 模型,默认为 Haiku
  2. LLM 以包含决策的结构化 JSON 响应
  3. CSC 自动处理决策

提示 Hook 配置​

将 type 设置为 "prompt" 并提供 prompt 字符串而不是 command。使用 $ARGUMENTS 占位符将 hook 的 JSON 输入数据注入到你的提示文本中。CSC 将组合的提示和输入发送给快速的 CSC 模型,后者返回 JSON 决策。

此 Stop hook 要求 LLM 在允许 CSC 完成之前评估所有任务是否完成:

{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Evaluate if CSC should stop: $ARGUMENTS. Check if all tasks are complete."
}
]
}
]
}
}
字段必需描述
type是必须为 "prompt"
prompt是发送给 LLM 的提示文本。使用 $ARGUMENTS 作为 hook 输入 JSON 的占位符。如果 $ARGUMENTS 不存在,输入 JSON 会追加到提示
model否用于评估的模型。默认为快速模型
timeout否超时时间(秒)。默认:30

响应模式​

LLM 必须以包含以下内容的 JSON 响应:

{
"ok": true | false,
"reason": "Explanation for the decision"
}
字段描述
oktrue 允许操作,false 阻止操作
reason当 ok 为 false 时必需。向 CSC 显示的解释

示例:多条件 Stop hook​

此 Stop hook 使用详细的提示在允许 CSC 停止之前检查三个条件。如果 "ok" 为 false,CSC 继续工作,以提供的原因为下一步指令。SubagentStop hooks 使用相同的格式来评估子代理是否应该停止:

{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "You are evaluating whether CSC should stop working. Context: $ARGUMENTS\n\nAnalyze the conversation and determine if:\n1. All user-requested tasks are complete\n2. Any errors need to be addressed\n3. Follow-up work is needed\n\nRespond with JSON: {\"ok\": true} to allow stopping, or {\"ok\": false, \"reason\": \"your explanation\"} to continue working.",
"timeout": 30
}
]
}
]
}
}

基于代理的 Hooks​

基于代理的 hooks(type: "agent")类似于基于提示的 hooks,但具有多轮工具访问权限。与单次 LLM 调用不同,代理 hook 生成一个子代理,可以读取文件、搜索代码和检查代码库来验证条件。代理 hooks 支持与基于提示的 hooks 相同的事件。

代理 Hooks 如何工作​

当代理 hook 触发时:

  1. CSC 生成一个带有你的提示和 hook 的 JSON 输入的子代理
  2. 子代理可以使用 Read、Grep 和 Glob 等工具进行调查
  3. 最多 50 轮后,子代理返回结构化的 { "ok": true/false } 决策
  4. CSC 以与提示 hook 相同的方式处理决策

当验证需要检查实际文件或测试输出,而不仅仅是评估 hook 输入数据时,代理 hooks 很有用。

代理 Hook 配置​

将 type 设置为 "agent" 并提供 prompt 字符串。配置字段与提示 hooks 相同,但默认超时更长:

字段必需描述
type是必须为 "agent"
prompt是描述要验证什么的提示。使用 $ARGUMENTS 作为 hook 输入 JSON 的占位符
model否用于评估的模型。默认为快速模型
timeout否超时时间(秒)。默认:60

响应模式与提示 hooks 相同:{ "ok": true } 允许或 { "ok": false, "reason": "..." } 阻止。

此 Stop hook 在允许 CSC 完成之前验证所有单元测试是否通过:

{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "agent",
"prompt": "Verify that all unit tests pass. Run the test suite and check the results. $ARGUMENTS",
"timeout": 120
}
]
}
]
}
}

在后台运行 Hooks​

默认情况下,hooks 会阻塞 CSC 的执行直到完成。对于长时间运行的任务,如部署、测试套件或外部 API 调用,设置 "async": true 以在 CSC 继续工作的同时在后台运行 hook。异步 hooks 无法阻止或控制 CSC 的行为:响应字段如 decision、permissionDecision 和 continue 没有效果,因为它们本应控制的操作已经完成。

配置异步 Hook​

在命令 hook 的配置中添加 "async": true 以在后台运行而不阻塞 CSC。此字段仅在 type: "command" hooks 上可用。

此 hook 在每次 Write 工具调用后运行测试脚本。CSC 立即继续工作,而 run-tests.sh 执行最多 120 秒。当脚本完成时,其输出在下一个对话轮次中传递:

{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "/path/to/run-tests.sh",
"async": true,
"timeout": 120
}
]
}
]
}
}

timeout 字段设置后台进程的最大时间(秒)。如果未指定,异步 hooks 使用与同步 hooks 相同的 10 分钟默认值。

异步 Hooks 如何执行​

当异步 hook 触发时,CSC 启动 hook 进程并立即继续,不等待其完成。hook 通过 stdin 接收与同步 hook 相同的 JSON 输入。

后台进程退出后,如果 hook 生成了带有 systemMessage 或 additionalContext 字段的 JSON 响应,该内容将在下一个对话轮次中作为上下文传递给 CSC。

异步 hook 完成通知默认被抑制。要查看它们,请使用 Ctrl+O 启用详细模式或使用 --verbose 启动 CSC。

示例:文件更改后运行测试​

此 hook 在 CSC 写入文件时在后台启动测试套件,然后在测试完成时将结果报告给 CSC。将此脚本保存到项目中的 .costrict/hooks/run-tests-async.sh 并使用 chmod +x 使其可执行:

#!/bin/bash
# run-tests-async.sh

# Read hook input from stdin
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

# Only run tests for source files
if [[ "$FILE_PATH" != *.ts && "$FILE_PATH" != *.js ]]; then
exit 0
fi

# Run tests and report results via systemMessage
RESULT=$(npm test 2>&1)
EXIT_CODE=$?

if [ $EXIT_CODE -eq 0 ]; then
echo "{\"systemMessage\": \"Tests passed after editing $FILE_PATH\"}"
else
echo "{\"systemMessage\": \"Tests failed after editing $FILE_PATH: $RESULT\"}"
fi

然后将此配置添加到项目根目录的 .costrict/settings.json 中。async: true 标志让 CSC 在测试运行时继续工作:

{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.costrict/hooks/run-tests-async.sh",
"async": true,
"timeout": 300
}
]
}
]
}
}

限制​

异步 hooks 与同步 hooks 相比有几个约束:

  • 只有 type: "command" hooks 支持 async。基于提示的 hooks 无法异步运行。
  • 异步 hooks 无法阻止工具调用或返回决策。当 hook 完成时,触发操作已经继续。
  • Hook 输出在下一个对话轮次中传递。如果会话空闲,响应会等到下一次用户交互。
  • 每次执行都会创建一个单独的后台进程。同一个异步 hook 的多次触发之间没有去重。

安全注意事项​

免责声明​

命令 hooks 以你的系统用户的完整权限运行。

⚠️ 警告: 命令 hooks 以你的完整用户权限执行 shell 命令。它们可以修改、删除或访问你的用户帐户可以访问的任何文件。在将 hook 命令添加到配置之前,请审查并测试所有 hook 命令。

安全最佳实践​

在编写 hooks 时请记住这些做法:

  • 验证和清理输入:永远不要盲目信任输入数据
  • 始终引用 shell 变量:使用 "$VAR" 而不是 $VAR
  • 阻止路径遍历:检查文件路径中的 ..
  • 使用绝对路径:为脚本指定完整路径,使用 "$CLAUDE_PROJECT_DIR" 作为项目根目录
  • 跳过敏感文件:避免 .env、.git/、密钥等

Windows PowerShell 工具​

在 Windows 上,你可以通过在命令 hook 上设置 "shell": "powershell" 来在 PowerShell 中运行单个 hooks。Hooks 直接启动 PowerShell,因此无论是否设置了 CLAUDE_CODE_USE_POWERSHELL_TOOL,这都可以工作。CSC 自动检测 pwsh.exe(PowerShell 7+),回退到 powershell.exe(5.1)。

{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"shell": "powershell",
"command": "Write-Host 'File written'"
}
]
}
]
}
}

调试 Hooks​

Hook 执行详细信息,包括哪些 hooks 匹配、它们的退出码以及完整的 stdout 和 stderr,都写入调试日志文件。使用 csc --debug-file <path> 启动 CSC 以将日志写入已知位置,或运行 csc --debug 并在 ~/.costrict/debug/<session-id>.txt 处读取日志。--debug 标志不会打印到终端。

[DEBUG] Executing hooks for PostToolUse:Write
[DEBUG] Found 1 hook commands to execute
[DEBUG] Executing hook command: <Your command> with timeout 600000ms
[DEBUG] Hook command completed with status 0: <Your stdout>

要获取更细粒度的 hook 匹配详细信息,请设置 CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose 以查看额外的日志行,如 hook 匹配器计数和查询匹配。

有关常见问题的故障排除,如 hooks 未触发、无限 Stop hook 循环或配置错误,请参阅指南中的限制和故障排除。