Cursor hook 完全指南,以及它们有时不触发的原因

Cursor hook 让脚本可以监视或拦截智能体的操作:每条 shell 命令、每次 MCP 调用、每次文件读取和编辑。它们功能强大,失败时却悄无声息。以下是我们在实际接入过程中总结的全部经验。

作者:Aslan,Pet Island 团队 · 更新于 2026年10月26日 · 阅读时长 7 分钟

简短回答

在 ~/.cursor/(所有项目)或仓库中的 .cursor/ 下放一个带 "version": 1 的 hooks.json,为每个事件指定一条命令,并向 stdout 输出 JSON。用 beforeShellExecution 和 beforeMCPExecution 来允许、拒绝或询问;如果希望 hook 出错时拦截命令而不是放行,请设置 failClosed: true。当 hook 似乎被忽略时,先打开 Hooks 输出通道:大多数问题出在路径、工作区未受信任、JSON 无效,或者 Cursor CLI 还不发送该事件。

什么是 Cursor hook

hook 是 Cursor 在智能体循环中某个固定节点运行的命令。Cursor 通过该命令的标准输入发送一个 JSON 对象,描述即将发生的操作,然后等待,并从标准输出读取 JSON 答复。有些 hook 只做观察(记录编辑、格式化文件),另一些则做决定:beforeShellExecution hook 可以放行命令、阻止命令,或先询问你。

因此,hook 适合以下三类工作:防护(没有人工确认绝不运行 rm -rf 或 git push --force)、自动化(每次编辑后格式化或运行 lint)以及可见性(记录或转发智能体的操作,让你知道它何时需要你)。

hooks.json 放在哪里

Cursor 从四个位置读取 hook,并全部运行。所有来源中匹配的 hook 都会运行,Cursor 会合并它们的答复。

级别文件说明
用户~/.cursor/hooks.json适用于所有项目。相对路径的命令以 ~/.cursor/ 为基准解析。
项目<project>/.cursor/hooks.json提交到仓库中。仅在受信任的工作区中运行。相对路径的命令以项目根目录为基准解析。
团队Cursor 网页控制台仅限 Enterprise,同步给所有成员。
企业C:\ProgramData\Cursor\hooks.json(Windows)、/Library/Application Support/Cursor/hooks.json(macOS)、/etc/cursor/hooks.json(Linux、WSL)由 IT 管理。

答复不一致时,最严格的答复生效:任何来源的 deny 都优先于 ask,ask 优先于 allow。最小的可用文件如下(取自 Cursor 文档中的示例):

~/.cursor/hooks.json复制
{
"version": 1,
"hooks": {
"afterFileEdit": [{ "command": "./hooks/format.sh" }]
}
}

version 必须为 1。Cursor 会监视该文件,保存时自动重新加载。如果修改没有生效,请重启 Cursor。仅这一步就能解决论坛上相当多的问题。

可以挂接的事件

以下是 Cursor 文档中的最新列表,按用途分组:

分组事件能否拦截?
Shell 和 MCPbeforeShellExecution、afterShellExecution、beforeMCPExecution、afterMCPExecution两个 before… hook:允许、拒绝或询问
任意工具preToolUse、postToolUse、postToolUseFailurepreToolUse:允许或拒绝(目前不强制执行 ask)
文件beforeReadFile、afterFileEditbeforeReadFile:允许或拒绝
提示词beforeSubmitPrompt可以,使用 continue: false
智能体循环stop、afterAgentResponse、afterAgentThought、preCompact不能(stop 可以发送一条后续消息)
子智能体subagentStart、subagentStopsubagentStart:允许或拒绝(ask 视为 deny)
会话和应用sessionStart、sessionEnd、workspaceOpen不能
Tab 补全beforeTabFileRead、afterTabFileEditbeforeTabFileRead:允许或拒绝

每个事件都会收到几个通用字段(conversation_id、generation_id、model、hook_event_name、cursor_version、workspace_roots),外加该事件自己的字段。beforeShellExecution 会收到完整的 command 及其 cwd;stop 会收到一个 status,值为 completed、aborted 或 error。

允许、拒绝和询问

做决定的 hook 会输出一个 JSON 对象。shell hook 的输出如下:

beforeShellExecution hook 的 stdout复制
{
"permission": "ask",
"user_message": "This pushes to main. Run it?",
"agent_message": "The user wants to confirm pushes."
}
  • permission 为 allow、deny 或 ask。
  • user_message 是你在 Cursor 中看到的内容。
  • agent_message 是告诉智能体的内容,让它可以调整方向,而不是反复尝试同一条命令。

退出码和 JSON 同样重要。退出码 0 表示“采用我的 JSON”。退出码 2 会拦截操作,效果与 deny 相同。其他任何退出码都会被视为 hook 失败,操作会继续执行,除非你设置了 failClosed。对于权限 hook,无效的 JSON 或不符合 schema 的 JSON 会拦截操作。

Cursor 文档称 beforeShellExecution 和 beforeMCPExecution 支持 ask。2026 年 3 月,Cursor 工作人员在论坛上表示,hook 只会强制执行 deny,allow 和 ask 则“basically ignored”(基本被忽略),开启命令允许列表或沙盒时尤其如此。我们没有找到修复公告。如果某项防护必须生效,请返回 deny 而不是 ask,并在你自己的版本中测试。

failClosed 和 timeout

每个 hook 条目都可以在 command 旁边附带选项:

.cursor/hooks.json复制
{
"version": 1,
"hooks": {
"beforeShellExecution": [
{
"command": "./.cursor/hooks/guard.sh",
"timeout": 30,
"failClosed": true
}
]
}
}
  • failClosed: true 会把崩溃、超时、非零退出或空输出变成拦截。默认值是 false:出错的防护会悄悄放行一切。对于安全类 hook,你几乎总是需要 true。
  • timeout 以秒为单位。如果你的 hook 需要等人响应,请留足时间:一个人阅读 diff 需要的时间远不止五秒。
  • matcher 用于筛选 hook 何时运行。对于 shell hook,它与完整的命令字符串进行匹配;对于工具 hook,则与工具类型匹配,例如 Shell、Read、Write 或 MCP:<tool_name>。

这里存在取舍。2026 年 7 月,一位 macOS 用户遇到了“MainThreadShellExec not initialized”:Cursor 自己的 shell 宿主尚未就绪,由于该 hook 设置了 fail-closed,智能体的 shell 被拦截了。Cursor 工作人员表示修复已经发布,并建议保留 failClosed: true。如果你在较旧的版本上看到这个错误,临时解决办法是执行“Developer: Reload Window”。

Windows 上的 hook

在 Windows 上,Cursor 桌面应用通过 PowerShell 运行 hook 命令,并通过管道传入 JSON。PowerShell 脚本从 $input 读取它;其他程序照常读取标准输入。有三件事容易让人踩坑:

  1. bash 脚本无法直接运行。 macOS 同事写的 guard.sh 在 PowerShell 下会失败。请使用 .ps1 脚本、编译好的程序,或者显式调用解释器(python .cursor/hooks/guard.py)。2026 年 1 月的一个论坛帖子显示,某个 Git Bash 配置报错 syntax error near unexpected token '[Convert]::FromBase64String',这就是 PowerShell 代码被交给了 bash。
  2. 执行策略。 如果 PowerShell 拒绝运行脚本,请用 powershell -NoProfile -ExecutionPolicy Bypass -File … 启动它们,输出 JSON,并以 exit 0 结束。
  3. GUI 程序返回得太早。 这个问题花了我们一整天。如果你的 hook 是为 GUI 子系统编译的 Windows 程序,PowerShell 启动它后不会等待,直接继续执行。Cursor 读到的是空输出,于是 hook 看起来什么都没做(如果设置了 failClosed,则会拦截一切)。请把 hook 编译为控制台程序,或者加一层包装,让 PowerShell 等它退出。

项目级 hook 还需要受信任的工作区。一位 Cursor 工作人员列出了 hook 毫无反应时要检查的内容:Hooks 日志中的“Hook N produced no output”一行、Settings → Hooks 中工作区是否显示为受信任,以及控制台中的 spawn、ENOENT、EPERM 或 ExecutionPolicy 等错误。

Cursor CLI 中的 hook

Cursor CLI(cursor-agent)读取同样的 ~/.cursor/hooks.json 和 .cursor/hooks.json,但它从未发送过编辑器发送的全部事件。2026 年 1 月时只有两个 shell hook 会触发。到 4 月,Cursor 工作人员确认 beforeShellExecution、afterShellExecution、afterFileEdit、postToolUse、stop 和 sessionStart 在 CLI 中可用,而 afterAgentResponse 和 afterAgentThought 仍然不可用(5 月再次确认)。对于不在此列表中的事件,依赖之前请先测试。

云端智能体的限制更严格:~/.cursor/hooks.json 中的用户级 hook 在那里根本不会运行。

hook 不触发时:检查清单

  1. 打开 Hooks 输出通道(View → Output → Hooks)或 Customize 中的 Hooks 选项卡。它会显示加载了哪些 hook、每次运行情况和错误。
  2. 检查 JSON 文件本身。 是否有 "version": 1,事件名称是否拼写完全正确(区分大小写),JSON 是否有效。
  3. 检查路径。 项目 hook 的相对路径命令以项目根目录为基准解析,用户 hook 则以 ~/.cursor/ 为基准。
  4. 信任工作区。 项目 hook 不会在未受信任的文件夹中运行。
  5. 手动运行 hook。 通过管道传入一段示例 JSON,查看 stdout。除了一个 JSON 对象之外的任何内容(多余的 echo、一条警告)都会导致解析失败。
  6. 重启 Cursor。 保存会重新加载文件,但论坛上有好几例问题只有完全重启才能解决。
  7. 了解已知缺口。 有报告称在启用允许列表和沙盒时 ask 和 allow 会被忽略;有报告称 subagentStart/subagentStop 在某些版本中不触发;CLI 会跳过部分事件。通过内置 Source Control 面板进行的提交不会被拦截:没有 pre-commit hook 事件。

hook、auto-run 与 Auto-review

hook 与 Cursor 自身的审批设置(Settings → Agents → Approvals & Execution)是分开的。auto-run 和 Auto-review 决定 Cursor 是否在其面板中询问你;你的 hook 会在此之上叠加自己的决定。这就是为什么“我的 hook 返回了 ask,命令却直接运行了”如此常见:另一条权限路径已经放行了它。

还有一点是我们吃过亏才学到的:启用 Auto-review 后,Cursor 可能会在自己的窗口中询问某些命令。hook 永远看不到这个提问,因为它发生在 Cursor 内部,而不在 hook 流水线中。

Pet Island 的用武之地

如果你希望 hook 做到“把每条有风险的命令都展示给我,让我不用切换窗口就能决定”,这正是 Pet Island 配合 Cursor 所做的事。它会替你安装 hook(beforeShellExecution 和 beforeMCPExecution,并开启 failClosed,确保辅助程序失效时绝不放行命令),并把每个请求连同完整命令放到屏幕顶部的小岛上。你在那里允许或拒绝;Cursor 的智能体会等待。

来自 Cursor 编辑器的命令和文件编辑都可以在小岛上处理。提问和计划会显示在小岛上,你在 Cursor 中回答。在 Cursor CLI 中,命令可以在小岛上处理;其他事件取决于 CLI 发送了什么。卸载时只会移除 Pet Island 添加的条目,你自己的 hook 会保留。

常见问题

Cursor hook 在 Windows 上能用吗?

能。桌面应用通过 PowerShell 运行它们,并通过标准输入传递 JSON(在 PowerShell 脚本中为 $input)。为 macOS 编写的 bash 脚本需要改写成 PowerShell 或 Python 版本,GUI 子系统程序需要加一层包装,让 PowerShell 等待它们。

为什么我的 hook 返回了 ask,命令却仍然运行?

Cursor 工作人员在 2026 年 3 月表示,hook 只能可靠地强制执行 deny;allow 和 ask 可能会被允许列表或沙盒覆盖。对于绝不能运行的操作请使用 deny,并查看 Hooks 输出通道,确认你的 hook 实际返回了什么。

failClosed 有什么作用?

将 failClosed 设为 true 后,崩溃、超时、非零退出或空输出都会拦截操作。不设置时,出错的 hook 会让操作继续执行。

在哪里查看 hook 错误?

在 Hooks 输出通道(View → Output → Hooks)和 Customize 中的 Hooks 选项卡里。

hook 在 Cursor CLI 中能用吗?

部分可用。CLI 读取同样的 hooks.json 文件。截至 2026 年 5 月,Cursor 工作人员确认 shell hook、afterFileEdit、postToolUse、stop 和 sessionStart 可用,但 afterAgentResponse 和 afterAgentThought 不可用。

hook 能阻止通过 Source Control 面板进行的 git 提交吗?

不能。Cursor 没有 pre-commit hook 事件,通过内置 Source Control 界面进行的提交不会被拦截。这种情况请使用普通的 git pre-commit hook。

参考来源

本页的每项设置和行为都已于 2026年10月26日 对照以下页面核实。智能体更新很快;如有不符,以官方页面为准。

继续阅读

在屏幕顶部回应每一个智能体。

Windows 和 Mac · 免费试用 7 天,之后一次性支付 8.99 美元 · 每笔销售拿出 1 美元为流浪动物提供食物

免费试用 Pet Island