教程 搜索 / 原生 Mac IDE / 添加自定义钩子
📝 文字 ● 中级 更新于 2026-05-13

如何在 LingCode 中添加自定义钩子?

TL;DR:编辑 ~/.claude/settings.json(或项目中的 .claude/settings.json),添加一个 hooks 字段,将事件(PreToolUseUserPromptSubmitSessionStartStop)映射到 shell 命令。运行命令的是框架本身,而非 Agent——适合用来设置守护规则和自动化流程。

钩子是 LingCode 在 Agent 生命周期特定时刻执行的 shell 命令——工具调用之前、提交提示词之后、会话结束时。钩子负责执行策略:不是写在 CLAUDE.md 里的"请不要做 X",而是真正说"不"的代码。

影响 Agent 行为的方式有两种。一种是告诉它:写在 CLAUDE.md 里、写成技能、或直接写进提示词。这种方式大多数时候都管用,但偶尔会失效——因为 Agent 是模型,而模型是概率性的。对于那些绝对必须发生、或绝对不能发生的事情,"告诉"就不够了。你写下的规则可能在压力下被忽略,在新的对话里被完全遗忘,或者当 Agent 判断当前情况与你描述的不同时被悄悄绕过。

另一种方式是强制执行:在 Agent 工作时、围绕其动作运行的代码。PreToolUse 钩子在任何工具调用之前触发,可以否决它。UserPromptSubmit 钩子在你发送消息时触发,可以记录、修改或拦截消息。PostToolUse 钩子在工具调用成功后触发。SessionEnd 钩子在对话关闭时触发。每个钩子都是一条 shell 命令——你的代码——通过环境变量获取上下文,并通过退出码和输出决定后续行为。

当错误结果的代价高到概率性保障无法接受时,就应该使用钩子。"绝对不能让 Agent 推送到 main 分支。""提交前必须运行 linter。""记录今天 Agent 执行的每条 shell 命令以供审计。"这些是策略问题,不是感觉问题——钩子就是将它们变成代码的方式。

你将学到

第一步:四种事件类型

1

各事件的作用

  • UserPromptSubmit — 每次你点击发送时触发。可获取提示词文本,可记录、修改或拦截提交。常见用途:过滤用户不小心输入的敏感信息。
  • PreToolUse — 任何工具调用前触发。可获取工具名称和参数,可批准、记录或否决。常见用途:拒绝匹配危险模式的 shell 命令。
  • PostToolUse — 工具调用成功后触发。可获取工具名称、参数和返回结果。常见用途:写入审计日志;触发下游通知。
  • SessionEnd — 对话关闭或 Agent 退出时触发。常见用途:清理资源、汇总指标、发送摘要。

这些事件在 Mac 应用的对话界面和 CLI 中保持一致。你编写的钩子在两种环境中都能生效。

第二步:hooks.json 格式

2

每个钩子对应一个条目的 JSON 文件

在项目中创建 .claude/hooks/hooks.json(或在用户全局目录创建 ~/.claude/hooks/hooks.json):

{
  "PreToolUse": [
    {
      "matcher": "shell",
      "command": "./.claude/hooks/block-dangerous-shell.sh"
    }
  ],
  "UserPromptSubmit": [
    {
      "command": "./.claude/hooks/log-prompt.sh"
    }
  ]
}

每个事件对应一个钩子条目数组。每个条目至少包含一个 command(要运行的 shell 命令),以及可选的 matcher(仅在工具调用匹配该模式时触发)。命令可以是脚本路径、内联表达式,或任何你的 shell 能接受的内容。

第三步:钩子接收的信息

3

包含 Agent 所知信息的环境变量

钩子运行时,LingCode 会设置一组以 LINGCODE_ 为前缀的环境变量(同时以 CLAUDE_ 为别名,兼容 Claude Code)。常用变量如下:

  • LINGCODE_EVENT — 事件名称(如 PreToolUse 等)
  • LINGCODE_TOOL_NAME — 对于工具相关事件,为工具名称
  • LINGCODE_TOOL_ARGS — 工具参数的 JSON 格式
  • LINGCODE_PROMPT — 对于 UserPromptSubmit,为原始提示词文本
  • LINGCODE_SESSION_ID — 当前对话的稳定 ID,可用于日志关联
  • LINGCODE_CWD — 项目的工作目录

钩子读取这些变量、执行操作后退出。关于标准流:stdin 为空,stdout 被捕获用于上下文,stderr 在钩子阻止事件时会显示在对话面板中。

第四步:退出码 0 表示允许,1 表示阻止

4

最简单的约定

钩子以 0 退出表示允许事件继续,以非零退出表示阻止。如果 PreToolUse 钩子以 1 退出,工具调用将被拒绝,Agent 会看到拒绝信息并调整策略。如果 UserPromptSubmit 钩子以 1 退出,消息将不会发送。阻止时,钩子向 stderr 写入的内容会作为拒绝原因传给 Agent——请写一条清晰的单行说明。

对于修改场景(重写提示词、脱敏参数),钩子将新值写入 stdout 并以 0 退出。LingCode 会用钩子的 stdout 替换原始内容。"脱敏敏感信息"钩子就是这样工作的。

第五步:具体示例

5

阻止向 main 分支执行 git push --force

创建 .claude/hooks/block-force-push.sh

#!/usr/bin/env bash
set -euo pipefail

if [[ "${LINGCODE_TOOL_NAME}" == "shell" ]]; then
  cmd=$(echo "$LINGCODE_TOOL_ARGS" | jq -r '.command // ""')
  if echo "$cmd" | grep -qE 'git push (-f|--force).*main\b'; then
    echo "Refused: --force push to main is not allowed. Open a PR instead." >&2
    exit 1
  fi
fi
exit 0

然后在 .claude/hooks/hooks.json 中添加:

{
  "PreToolUse": [
    {
      "matcher": "shell",
      "command": "./.claude/hooks/block-force-push.sh"
    }
  ]
}

为脚本添加可执行权限(chmod +x)。从此,Agent 任何向 main 强制推送的尝试都会在工具边界被拒绝——Agent 会看到 stderr 中的拒绝原因并改换策略。这条规则无法被"说服"绕过,因为它现在是代码。

钩子优先于权限模式:阻止某个工具的钩子,在所有权限模式下都会阻止它。设置 acceptEdits 并不能绕过钩子。这是有意为之——钩子是策略,模式是便利。

第六步:钩子的三种存放位置

6

项目级、用户全局,或插件内置

  • 项目本地:项目根目录下的 .claude/hooks/。纳入 git 管理,团队所有成员共享同一规则。
  • 用户全局:~/.claude/hooks/。适合个人习惯(例如"在我所有项目中,提交前始终运行 lint")。
  • 插件内置:放在插件hooks/ 目录中,可复用地分发钩子——但安装此类插件时会弹出授权提示,因为钩子可以执行 shell。
不要在失败时放行。如果你的钩子脚本有 bug,对每个事件都以非零退出,实际上就等于完全阻止了 Agent。在部署前先单独测试钩子脚本(手动设置环境变量后运行 .sh 文件)。一个有缺陷的钩子比没有钩子更糟糕。

下一步