Cline Hooks 完全指南:在 Agent 工作流的 8 个关键时机注入自定义脚本
Cline 的 Hooks(钩子)机制允许开发者在 Agentic 工作流的特定时机执行自定义脚本:任务开始/恢复/取消/完成、用户提交提示词、工具调用前后、上下文压缩前。本文以 Cline 仓库中的官方文档 .clinerules/hooks/README.md 为主体,逐条覆盖钩子类型、脚本格式、I/O 协议、执行限制与并发合并规则,并结合 hook-factory.ts、HookProcess.ts 等源码印证底层实现,读完后你可以编写可阻断、可注入上下文的钩子脚本,并在多根工作区中正确组织全局与项目级规则。
一、机制总览:钩子在哪里、如何生效
Hooks 让 Cline 在特定流程节点自动执行你的脚本,脚本可以阻断操作(返回 cancel: true)或向对话注入上下文(contextModification),从而影响模型后续的决策。钩子有两个存放位置:
- 全局钩子目录:
~/Documents/Cline/Hooks/(对所有工作区生效,组织级策略、个人通用规则) - 工作区钩子目录:
.clinerules/hooks/(仅对仓库所在工作区生效,项目专属规则、团队约定)
钩子在启用后自动运行,无需手动触发。从源码看,扩展启动时会确保全局钩子目录存在:disk.ts 中的 ensureHooksDirectoryExists() 会创建 ~/Documents/Cline/Hooks,创建失败时回退到该路径并"优雅降级"(后续发现不到脚本即静默跳过);getAllHooksDirs() 则按「运行时目录(可选)→ 全局目录 → 各工作区 .clinerules/hooks」的顺序汇总全部钩子目录。
启用步骤
- 在 VS Code 中打开 Cline 设置;
- 进入 Feature Settings(功能设置)区;
- 勾选 "Enable Hooks" 复选框;
- 确保钩子文件可执行(Unix/Linux/macOS 下
chmod +x hookname)。
二、完整钩子类型一览
| 钩子 | 触发时机 | 典型用途 | 全局位置 | 工作区位置 |
|---|---|---|---|---|
| TaskStart | 启动新任务时(恢复任务不触发) | 初始化任务上下文、校验任务要求、准备环境 | ~/Documents/Cline/Hooks/TaskStart |
.clinerules/hooks/TaskStart |
| TaskResume | 恢复已有任务时(用户点击 resume 后) | 校验恢复状态、还原上下文、检查上次运行以来的变更 | ~/Documents/Cline/Hooks/TaskResume |
.clinerules/hooks/TaskResume |
| TaskCancel | 任务被取消、或用户中止了某个钩子(仅当存在实际活跃工作时) | 清理资源、记录取消、保存状态 | ~/Documents/Cline/Hooks/TaskCancel |
.clinerules/hooks/TaskCancel |
| TaskComplete | 任务被标记为完成时(文档标注 coming soon) | 记录完成状态、最终清理、生成报告 | ~/Documents/Cline/Hooks/TaskComplete |
.clinerules/hooks/TaskComplete |
| UserPromptSubmit | 用户提交提示词/消息时(初始任务、恢复、反馈均触发) | 校验用户输入、预处理提示词、为用户消息附加上下文 | ~/Documents/Cline/Hooks/UserPromptSubmit |
.clinerules/hooks/UserPromptSubmit |
| PreToolUse | 工具执行之前 | 校验参数、阻断执行、附加上下文 | ~/Documents/Cline/Hooks/PreToolUse |
.clinerules/hooks/PreToolUse |
| PostToolUse | 工具执行完成之后 | 观察结果、追踪模式、附加上下文 | ~/Documents/Cline/Hooks/PostToolUse |
.clinerules/hooks/PostToolUse |
| PreCompact | 对话上下文被压缩/截断之前(文档标注 coming soon) | 观察压缩事件、记录上下文管理、追踪 token 用量 | ~/Documents/Cline/Hooks/PreCompact |
.clinerules/hooks/PreCompact |
两个要点值得注意:
- TaskCancel 钩子本身不可被取消(NOT cancellable),保证清理逻辑总能执行;
- 从源码结构看,hook-factory.ts 中的
Hooks接口还定义了一个 Notification 数据键(NotificationData),它未出现在上述文档表格中,属于源码层面额外暴露的钩子载荷类型,具体触发面文档未展开。
三、跨平台钩子格式:git 式约定
Cline 采用与 git hooks 一致的跨平台设计:
钩子文件规范(全平台)
- 无文件扩展名:钩子必须精确命名为
PreToolUse、PostToolUse等(不加.bat、.cmd、.sh后缀); - Shebang 必需:第一行必须是 shebang,例如
#!/usr/bin/env bash或#!/usr/bin/env node; - Unix 下必须可执行:
chmod +x PreToolUse; - Windows:官方文档原文写明 "Not currently supported"(当前不支持)。
源码演进提示:当前仓库代码中 hook-factory.ts 已出现平台分支逻辑——
findUnixHook检查无扩展名候选文件且要求可执行位(fs.access(candidate, fs.constants.X_OK)),而findWindowsHook则专门查找<HookName>.ps1文件(明确忽略无扩展名文件);HookProcess.ts 中 Windows 分支通过powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File <script>启动钩子,并对 PowerShell 可执行文件路径做了 5 分钟缓存。也就是说,就当前源码而言 Windows 支持已在推进中,脚本作者可以以官方 README 的"暂不支持"表述为保守预期,以源码为实际行为参照。
工作原理
与 git hooks 相同,Cline 通过 shell 执行钩子文件并解释 shebang 行:
- Unix/Linux/macOS:原生 shell 执行,支持 shebang;
- 因此:同一份钩子脚本可跨平台使用(write once, run anywhere),可以使用任意脚本语言(bash、node、python 等)。
创建钩子(Unix/Linux/macOS)
# 创建钩子文件
nano ~/Documents/Cline/Hooks/PreToolUse
# 赋予可执行权限
chmod +x ~/Documents/Cline/Hooks/PreToolUse
四、I/O 协议:stdin 进 JSON,stdout 出 JSON
4.1 输入(stdin,JSON)
Cline 通过标准输入向每个钩子传入一份 JSON。所有钩子都会收到如下公共字段 + 按类型挂载的专属字段:
{
"clineVersion": "string",
"hookName": "TaskStart" | "TaskResume" | "TaskCancel" | "TaskComplete" | "UserPromptSubmit" | "PreToolUse" | "PostToolUse" | "PreCompact",
"timestamp": "string",
"taskId": "string",
"workspaceRoots": ["string"],
"userId": "string",
"taskStart": {
"taskMetadata": { "taskId": "string", "ulid": "string", "initialTask": "string" }
},
"taskResume": {
"taskMetadata": { "taskId": "string", "ulid": "string" },
"previousState": { "lastMessageTs": "string", "messageCount": "string", "conversationHistoryDeleted": "string" }
},
"taskCancel": {
"taskMetadata": { "taskId": "string", "ulid": "string", "completionStatus": "string" }
},
"taskComplete": {
"taskMetadata": { "taskId": "string", "ulid": "string" }
},
"userPromptSubmit": { "prompt": "string", "attachments": ["string"] },
"preToolUse": { "toolName": "string", "parameters": {} },
"postToolUse": { "toolName": "string", "parameters": {}, "result": "string", "success": "boolean", "executionTimeMs": "number" },
"preCompact": { "contextSize": "number", "messagesToCompact": "number", "compactionStrategy": "string" }
}
各 taskStart / taskResume / taskCancel / taskComplete / userPromptSubmit / preToolUse / postToolUse / preCompact 字段仅在对应钩子出现。从源码看,hook-factory.ts 的 completeParams() 负责补齐公共元数据(clineVersion、hookName、timestamp、workspaceRoots、userId),并在其上额外附带一个 model 字段(provider / slug,未知时置为 "unknown")——该字段未在 README 的示例中列出,编写解析脚本时可以容错处理。另外,README 中的输入示例未包含顶层 taskId,而源码会随 params 一并序列化传入,脚本可按需读取。
4.2 输出(stdout,JSON)
钩子必须向 stdout 返回如下结构的 JSON:
{
"cancel": false,
"contextModification": "可选:注入给模型后续决策的上下文",
"errorMessage": "可选:阻断时给用户的错误详情"
}
cancel 字段语义:
false(或省略):允许执行继续;true:阻断执行,并向用户展示errorMessage。
源码层面的两个容错细节值得了解:
- 旧字段拒绝:hook-factory.ts 的
validateHookOutput()检测到已废弃的shouldContinue字段时会直接判为无效输出,并给出迁移指引({ shouldContinue: false, ... }→{ cancel: true, ... });cancel若提供必须是布尔值,contextModification/errorMessage若提供必须是字符串; - JSON 提取容错:即使 stdout 中混有调试输出,解析器也会从 stdout 末尾向前扫描最后一个完整的 JSON 对象(按花括号配对),提取后再校验(见 hook-factory.ts)。因此钩子脚本可以先
echo调试信息,最后再打印 JSON 响应。
五、上下文注入时机:影响"下一次"决策,而非当前调用
重要:钩子注入的上下文影响的是模型未来的决策,不是当前这次工具执行。
为什么?当钩子运行时:
- 模型已经决定了要调用哪个工具、用什么参数;
- 钩子无法修改这些参数;
- 钩子的上下文被追加进对话;
- 模型在下一次 API 请求中才能看到它,并据此调整后续决策。
PreToolUse 流程
1. 模型决定:"我将使用 write_to_file,参数如下"
2. PreToolUse 钩子运行 → 可阻断或注入上下文
3. 若放行,工具以原始参数执行
4. 上下文被加入对话
5. 下一次 API 请求携带该上下文
6. 模型基于上下文调整后续决策
PostToolUse 流程
1. 工具执行完成
2. PostToolUse 钩子运行 → 观察结果
3. 钩子针对结果注入上下文
4. 上下文被加入对话
5. 下一次 API 请求携带该上下文
6. 模型可以从结果中学习
六、执行限制与引擎细节
| 限制项 | 默认值 | 源码依据 |
|---|---|---|
| 超时 | 30 秒(HOOK_EXECUTION_TIMEOUT_MS) |
hook-factory.ts const HOOK_EXECUTION_TIMEOUT_MS = 30000 |
| 上下文修改大小 | 50KB(MAX_CONTEXT_MODIFICATION_SIZE) |
同上 const MAX_CONTEXT_MODIFICATION_SIZE = 50000,超限截断并追加 [... context truncated due to size limit ...] |
| 子进程输出上限 | stdout + stderr 合计 1MB | HookProcess.ts MAX_HOOK_OUTPUT_SIZE = 1024 * 1024,超限丢弃后续输出并标记截断 |
错误处理约定(与源码一致):
- 预期错误静默处理:文件不存在(
ENOENT)、权限拒绝(EACCES,例如用户放了不想执行的钩子)、非目录(ENOTDIR)都会被 hook-factory.ts 的isExpectedHookError()静默吞掉;其他文件系统错误(EIO、EMFILE 等)会向上抛出; - Fail-open 语义:钩子脚本自身出错(非零退出码)并不会自动阻断工具,只有显式的
cancel: trueJSON 响应才会阻断;超时/用户取消则以"失败"状态呈现于 UI(EXIT_CODE_SIGINT = 130对应 Unix 下128 + SIGINT的取消约定); - 子进程管理:HookProcess 通过
spawn启动脚本,Unix 下以detached进程组方式运行,terminate()会先对整个进程组发SIGTERM优雅退出、2 秒后未退出再SIGKILL,防止钩子遗留后台子进程。
七、多钩子并发与结果合并(全局 × 工作区 × 多根)
当同一钩子类型同时存在全局钩子与工作区钩子(或多根工作区中每个根目录都有钩子)时:
- 所有钩子通过
Promise.all并发执行,执行顺序不保证; - 全部返回
cancel: false才放行;任何一个返回cancel: true即阻断; - cancel:任一为
true→ 合并结果为true; - contextModification:所有非空上下文以双换行
\n\n拼接; - errorMessage:所有非空错误信息以单换行
\n拼接。
这一合并逻辑在 CombinedHookRunner 中实现(results.some(r => r.cancel)、join("\n\n")、join("\n"))。
钩子工作目录(cwd)选择
从源码看,工作目录规则与钩子来源绑定(hook-factory.ts):
- 工作区钩子(
<root>/.clinerules/hooks/):以其所属工作区根目录为 cwd(嵌套根时取最长匹配,即最内层根),因此脚本内相对路径相对于该项目解析; - 全局钩子(
~/Documents/Cline/Hooks/):以主工作区根目录(第一个工作区文件夹)为 cwd; - 若 cwd 目录不存在,HookProcess 会直接失败并给出明确错误,而不是让相对路径落到宿主进程目录上。
示例:全局 + 工作区钩子组合
全局钩子(~/Documents/Cline/Hooks/PreToolUse,对所有项目生效):
#!/usr/bin/env bash
# 通用规则:绝不修改 package.json
input=$(cat)
tool_name=$(echo "$input" | jq -r '.preToolUse.toolName')
path=$(echo "$input" | jq -r '.preToolUse.parameters.path // ""')
if [[ "$tool_name" == "write_to_file" && "$path" == *"package.json"* ]]; then
echo '{"cancel": true, "errorMessage": "Global policy: Cannot modify package.json"}'
exit 0
fi
echo '{"cancel": false}'
工作区钩子(.clinerules/hooks/PreToolUse,仅对当前项目生效):
#!/usr/bin/env bash
# 项目规则:只允许 TypeScript 文件
input=$(cat)
tool_name=$(echo "$input" | jq -r '.preToolUse.toolName')
path=$(echo "$input" | jq -r '.preToolUse.parameters.path // ""')
if [[ "$tool_name" == "write_to_file" && "$path" == *.js ]]; then
echo '{"cancel": true, "errorMessage": "Project rule: Use .ts files only"}'
exit 0
fi
echo '{"cancel": false}'
所有钩子都必须放行,工具才会执行;多钩子之间可能并发运行。多根工作区场景下同样适用上述合并规则,且不同目录的钩子之间不保证执行顺序。
八、常见用例(可直接复制的钩子脚本)
1. 校验——阻断非法操作
#!/usr/bin/env bash
input=$(cat)
tool_name=$(echo "$input" | jq -r '.preToolUse.toolName')
path=$(echo "$input" | jq -r '.preToolUse.parameters.path // ""')
if [[ "$tool_name" == "write_to_file" && "$path" == *.js ]]; then
cat <<EOF
{
"cancel": true,
"errorMessage": "Cannot create .js files in TypeScript project",
"contextModification": "Use .ts/.tsx extensions only"
}
EOF
exit 0
fi
echo '{"cancel": false}'
2. 上下文构建——从操作中学习
#!/usr/bin/env bash
input=$(cat)
tool_name=$(echo "$input" | jq -r '.postToolUse.toolName')
success=$(echo "$input" | jq -r '.postToolUse.success')
path=$(echo "$input" | jq -r '.postToolUse.parameters.path // ""')
if [[ "$tool_name" == "write_to_file" && "$success" == "true" ]]; then
cat <<EOF
{
"cancel": false,
"contextModification": "Created '$path'. Maintain consistency with this file's patterns in future operations."
}
EOF
else
echo '{"cancel": false}'
fi
3. 性能监控
#!/usr/bin/env bash
input=$(cat)
execution_time=$(echo "$input" | jq -r '.postToolUse.executionTimeMs')
tool_name=$(echo "$input" | jq -r '.postToolUse.toolName')
if [[ "$execution_time" -gt 5000 ]]; then
cat <<EOF
{
"cancel": false,
"contextModification": "Tool '$tool_name' took ${execution_time}ms. Consider optimizing future similar operations."
}
EOF
else
echo '{"cancel": false}'
fi
4. 日志与遥测
#!/usr/bin/env bash
input=$(cat)
# 记录到文件
echo "$input" >> ~/.cline/hook-logs/tool-usage.jsonl
# 放行执行
echo '{"cancel": false}'
仓库 sdk/examples/hooks/ 目录还随附了更多语言的钩子示例(PreToolUse.py、PreToolUse.sh、PostToolUse.ts、PreToolUse_BlockDestructive.sh、SessionShutdown.sh、TaskComplete.sh 等),可作为现成模板参考,并配合 sdk/examples/hooks/README.md 阅读。
九、故障排查
钩子没有运行
- 确认 "Enable Hooks" 设置已勾选;
- 确认钩子文件可执行(
chmod +x hookname); - 检查脚本语法错误;
- 查看 VS Code Output 面板(Cline 通道)中的报错。
钩子超时
- 降低脚本复杂度;
- 避免昂贵操作(网络调用、重计算);
- 把复杂逻辑移到后台进程(注意 30 秒超时由
HOOK_EXECUTION_TIMEOUT_MS控制,超时后子进程会被SIGTERM)。
上下文没有影响模型行为
- 记住:上下文影响的是未来决策,不是当前工具调用;
- 确保注入的上下文清晰、可操作;
- 检查上下文是否被截断(50KB 限制,截断后会出现
[... context truncated due to size limit ...]标记)。
十、安全注意事项与最佳实践
安全(钩子以 VS Code 相同权限运行):
- 对来自不可信来源的钩子保持警惕;
- 启用前审阅钩子脚本;
- 可考虑用
.gitignore避免提交敏感钩子逻辑; - 钩子可访问工作区内所有文件与环境变量。
最佳实践
- 保持快速——目标执行时间 < 100ms;
- 让上下文可操作——明确告诉模型该做什么;
- 使用结构化前缀——帮助模型对上下文分类;
- 优雅处理错误——始终返回合法 JSON;
- 记录日志便于排障——保留钩子执行日志;
- 渐进式测试——从简单钩子起步,再逐步增加复杂度;
- 为钩子写注释——说明目的与逻辑。
十一、延伸阅读
- 钩子实现核心:hook-factory.ts(Runner 抽象、JSON 解析与校验、并发合并)、HookProcess.ts(子进程、超时、流式输出)、HookDiscoveryCache.ts(钩子发现缓存);
- 单元与集成测试:hook-factory.test.ts、hookprocess.test.ts 及 fixtures 下按钩子类型组织的样例脚本(success / blocking / error / context-injection 等场景);
- 用户文档:docs/customization/hooks.mdx。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00