《Cursor文档》-钩子

钩子可让你通过自定义脚本观察、控制和扩展智能体循环。你可以在项目级或用户级的 hooks.json 文件中定义钩子,也可以通过 自定义 中的插件安装钩子。钩子是通过 stdio 使用 JSON 进行双向通信的子进程。它们会在智能体循环中定义的各个阶段之前或之后运行,并可观察、阻止或修改行为。

Media

借助钩子,你可以:

  • 在编辑后运行格式化工具
  • 为事件添加使用分析
  • 扫描 PII 或机密信息
  • 限制高风险操作 (例如 SQL 写入)
  • 控制子智能体 (Task tool) 的执行
  • 在会话开始时注入上下文

正在寻找现成可用的集成?请参阅合作伙伴集成,了解我们的生态合作伙伴提供的安全、治理和机密信息管理解决方案。

Cursor 支持从 Claude Code 等第三方工具加载钩子。有关兼容性和配置的详细信息,请参阅第三方钩子

钩子类别

钩子按触发条件分为三类:

智能体钩子 (Cmd+K/Agent Chat) 在智能体会话期间触发:

  • sessionStart / sessionEnd - 管理会话生命周期
  • preToolUse / postToolUse / postToolUseFailure - 通用工具使用钩子 (对所有工具触发)
  • subagentStart / subagentStop - 子智能体 (Task 工具) 生命周期
  • beforeShellExecution / afterShellExecution - 控制 shell 命令
  • beforeMCPExecution / afterMCPExecution - 控制 MCP 工具的使用
  • beforeReadFile / afterFileEdit - 控制文件访问和编辑
  • beforeSubmitPrompt - 在提交前验证提示词
  • preCompact - 监测上下文窗口压缩
  • stop - 处理智能体完成事件
  • afterAgentResponse / afterAgentThought - 跟踪智能体响应

**Tab 钩子 (行内补全) **在自主 Tab 操作期间触发:

  • beforeTabFileRead - 控制 Tab 补全的文件访问
  • afterTabFileEdit - 对 Tab 编辑进行后处理

应用生命周期钩子在任何智能体会话之外触发:

  • workspaceOpen - 在 Cursor 打开工作区以及每次工作区文件夹变更时触发。可返回要为当前工作区加载的额外插件路径。

这些独立的钩子入口可让您针对自主 Tab 操作、用户驱动的 Agent 操作和工作区启动应用不同的策略。

云端代理支持

云端代理会运行代码仓库中的基于命令的钩子。如果你在项目根目录的 .cursor/hooks.json 中定义了钩子,云端代理会自动加载并在工作过程中运行这些钩子。

在企业版方案中,云端代理还会运行通过网页仪表盘配置的团队钩子和由企业统一管理的钩子。

云端代理有时会在早期探索轮次中以只读环境启动。这些轮次不会运行钩子。智能体获得可写环境后,钩子便会开始运行。

支持的钩子

云端代理支持以下钩子:

钩子 是否支持
beforeShellExecution
afterShellExecution
beforeReadFile
afterFileEdit
preToolUse
postToolUse
postToolUseFailure
subagentStart
subagentStop
beforeSubmitPrompt
preCompact
afterAgentResponse
afterAgentThought
stop

云端代理不支持的钩子

由于执行环境不同,部分钩子不适用于云端代理:

钩子 原因
sessionStart 因云端代理仍可能在只读环境中启动,暂不支持。在该环境中钩子不会加载,因此云端 sessionStart 会在首次写入后才触发,而非在会话真正开始时。
sessionEnd 云端代理没有以编辑器生命周期为界的会话边界。sessionEnd 与 IDE 会话关联,而非云端代理聊天。
beforeMCPExecution / afterMCPExecution 因云端代理仍可能在钩子不会加载的只读环境中启动,且 MCP 钩子的触发时机尚不明确,暂不支持。
beforeTabFileRead / afterTabFileEdit Tab 补全是 IDE 功能,不会在云端代理中运行。
workspaceOpen 这是 IDE 生命周期钩子,不适用于云端代理。

配置来源

云端代理会从以下来源加载 hooks:

  • 项目 hooks (仓库中的 .cursor/hooks.json) :在云端代理执行任务时加载并运行。
  • 团队 hooks (企业版) :从仪表盘分发,并在云端代理中运行。
  • 企业版 hooks (企业版) :由系统统一管理,并在云端代理中运行。

用户级 hooks (~/.cursor/hooks.json) 无法在云端代理中使用。云端代理 VM 无法访问本地主目录中的配置。

执行类型限制

云端代理仅支持运行基于命令的钩子。基于提示词的钩子需要在钩子与智能体循环之间配置身份验证连接,而云端执行环境不提供此功能。

快速开始

创建 hooks.json 文件。你可以在项目级别 (<project>/.cursor/hooks.json) 或主目录 (~/.cursor/hooks.json) 中创建。项目 hooks 仅适用于特定项目,主目录 hooks 则全局适用。

用户 hooks (\~/.cursor/)

如需创建全局适用的用户级 hooks,请创建 ~/.cursor/hooks.json

{
  "version": 1,
  "hooks": {
    "afterFileEdit": [{ "command": "./hooks/format.sh" }]
  }
}

~/.cursor/hooks/format.sh 中创建钩子脚本:

#!/bin/bash
# 读取输入,执行操作,然后以 0 退出
cat > /dev/null
exit 0

将其设为可执行:

chmod +x ~/.cursor/hooks/format.sh

项目 hooks (.cursor/)

如需创建仅适用于特定代码仓库的项目 hooks,请创建 <project>/.cursor/hooks.json

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

注意:项目 hooks 从项目根目录运行,因此请使用 .cursor/hooks/format.sh (而非 ./hooks/format.sh) 。

<project>/.cursor/hooks/format.sh 中创建钩子脚本:

#!/bin/bash
# 读取输入,执行操作,然后以 0 退出
cat > /dev/null
exit 0

将其设为可执行:

chmod +x .cursor/hooks/format.sh

Cursor 会监视 hooks 配置文件并自动重新加载。每次编辑文件后,都会运行你的钩子。

钩子类型

钩子支持两种执行类型:基于命令 (默认) 和基于提示词 (由 LLM 评测) 。

基于命令的钩子

命令钩子会执行 shell 脚本,脚本通过 stdin 接收 JSON 输入,并通过 stdout 返回 JSON 输出。

{
  "hooks": {
    "beforeShellExecution": [
      {
        "command": "./scripts/approve-network.sh",
        "timeout": 30,
        "matcher": "curl|wget|nc"
      }
    ]
  }
}

退出码行为:

  • 退出码 0 - 钩子执行成功,使用 JSON 输出
  • 退出码 2 - 阻止该操作 (等同于返回 permission: "deny")
  • 其他退出码 - 钩子执行失败,操作仍会继续 (默认失败时放行)

基于提示词的钩子

提示词钩子使用 LLM 评估自然语言条件,适用于无需编写自定义脚本的策略执行。

{
  "hooks": {
    "beforeShellExecution": [
      {
        "type": "prompt",
        "prompt": "Does this command look safe to execute? Only allow read-only operations.",
        "timeout": 10
      }
    ]
  }
}

功能:

  • 返回结构化的 { ok: boolean, reason?: string } 响应
  • 使用快速模型进行快速评测
  • $ARGUMENTS 占位符会自动替换为钩子输入 JSON
  • 如果未提供 $ARGUMENTS,则会自动追加钩子输入
  • 可通过可选的 model 字段覆盖默认 LLM 模型

示例

以下示例使用 ./hooks/... 路径,适用于用户 hooks (~/.cursor/hooks.json) ,其脚本从 ~/.cursor/ 目录运行。对于项目 hooks (<project>/.cursor/hooks.json) ,由于脚本从项目根目录运行,请改用 .cursor/hooks/... 路径。
```json title=”hooks.json”
{
“version”: 1,
“hooks”: {
“sessionStart”: [
{
“command”: “./hooks/session-init.sh”
}
],
“sessionEnd”: [
{
“command”: “./hooks/audit.sh”
}
],
“beforeShellExecution”: [
{
“command”: “./hooks/audit.sh”
},
{
“command”: “./hooks/block-git.sh”
}
],
“beforeMCPExecution”: [
{
“command”: “./hooks/audit.sh”
}
],
“afterShellExecution”: [
{
“command”: “./hooks/audit.sh”
}
],
“afterMCPExecution”: [
{
“command”: “./hooks/audit.sh”
}
],
“afterFileEdit”: [
{
“command”: “./hooks/audit.sh”
}
],
“beforeSubmitPrompt”: [
{
“command”: “./hooks/audit.sh”
}
],
“preCompact”: [
{
“command”: “./hooks/audit.sh”
}
],
“stop”: [
{
“command”: “./hooks/audit.sh”
}
],
“beforeTabFileRead”: [
{
“command”: “./hooks/redact-secrets-tab.sh”
}
],
“afterTabFileEdit”: [
{
“command”: “./hooks/format-tab.sh”
}
]
}
}

```sh title="audit.sh"
#!/bin/bash

# audit.sh - 将所有 JSON 输入写入 /tmp/agent-audit.log 的钩子脚本
# 此脚本供 Cursor 的钩子系统调用,用于审计

# 从 stdin 读取 JSON 输入
json_input=$(cat)

# 为日志条目生成时间戳
timestamp=$(date '+%Y-%m-%d %H:%M:%S')

# 如果日志目录不存在,则创建该目录
mkdir -p "$(dirname /tmp/agent-audit.log)"

# 将带时间戳的 JSON 条目写入审计日志
echo "[$timestamp] $json_input" >> /tmp/agent-audit.log

# 正常退出
exit 0

```sh title=”block-git.sh”

!/bin/bash

阻止 git 命令,并引导改用 gh 工具的钩子

此钩子实现了 Cursor Hooks 规范中的 beforeShellExecution 钩子

初始化调试日志

echo “Hook execution started” >> /tmp/hooks.log

从 stdin 读取 JSON 输入

input=$(cat)
echo “Received input: $input” >> /tmp/hooks.log

从 JSON 输入中解析命令

command=\((echo "\)input” | jq -r ‘.command // empty’)
echo “Parsed command: ‘$command’” >> /tmp/hooks.log

检查命令是否包含“git”或“gh”

if [[ “\(command" =~ git[[:space:]] ]] || [[ "\)command” == “git” ]]; then
echo “Git command detected - blocking: ‘\(command'" >> /tmp/hooks.log # 阻止 git 命令,并提供改用 gh 工具的指引 cat << EOF { "continue": true, "permission": "deny", "user_message": "Git command blocked. Please use the GitHub CLI (gh) tool instead.", "agent_message": "The git command '\)command’ has been blocked by a hook. Instead of using raw git commands, please use the ‘gh’ tool which provides better integration with GitHub and follows best practices. For example:\n- Instead of ‘git clone’, use ‘gh repo clone’\n- Instead of ‘git push’, use ‘gh repo sync’ or the appropriate gh command\n- For other git operations, check if there’s an equivalent gh command or use the GitHub web interface\n\nThis helps maintain consistency and leverages GitHub’s enhanced tooling.”
}
EOF
elif [[ “\(command" =~ gh[[:space:]] ]] || [[ "\)command” == “gh” ]]; then
echo “GitHub CLI command detected - asking for permission: ‘$command’” >> /tmp/hooks.log
# 为 gh 命令请求权限
cat << EOF
{
“continue”: true,
“permission”: “ask”,
“user_message”: “GitHub CLI command requires permission: \(command", "agent_message": "The command '\)command’ uses the GitHub CLI (gh) which can interact with your GitHub repositories and account. Please review and approve this command if you want to proceed.”
}
EOF
else
echo “Non-git/non-gh command detected - allowing: ‘$command’” >> /tmp/hooks.log
# 允许非 git 和非 gh 命令
cat << EOF
{
“continue”: true,
“permission”: “allow”
}
EOF
fi

### TypeScript stop 自动化钩子

需要在同一钩子中使用类型化 JSON、持久化文件 I/O  HTTP 调用时,可选择 TypeScript。这个由 Bun 驱动的 `stop` 钩子会在磁盘上记录每个对话的失败次数,将结构化遥测数据转发到内部 API,并可在智能体连续失败两次后自动安排重试。
```json title="hooks.json"
{
  "version": 1,
  "hooks": {
    "stop": [
      {
        "command": "bun run .cursor/hooks/track-stop.ts --stop"
      }
    ]
  }
}

```ts title=”.cursor/hooks/track-stop.ts”
import { mkdir, readFile, writeFile } from ‘node:fs/promises’;
import { stdin } from ‘bun’;

type StopHookInput = {
conversation_id: string;
generation_id: string;
model: string;
model_id?: string;
model_params?: Array<{ id: string; value: string }>;
status: ‘completed’ | ‘aborted’ | ‘error’;
loop_count: number;
};

type StopHookOutput = {
followup_message?: string;
};

type MetricsEntry = {
lastStatus: StopHookInput[‘status’];
errorCount: number;
lastUpdatedIso: string;
};

type MetricsStore = Record;

const STATE_DIR = ‘.cursor/hooks/state’;
const METRICS_PATH = ${STATE_DIR}/agent-metrics.json;
const TELEMETRY_URL = Bun.env.AGENT_TELEMETRY_URL;

async function parseHookInput(): Promise {
const text = await stdin.text();
return JSON.parse(text) as T;
}

async function readMetrics(): Promise {
try {
return JSON.parse(await readFile(METRICS_PATH, ‘utf8’)) as MetricsStore;
} catch {
return {};
}
}

async function writeMetrics(store: MetricsStore) {
await mkdir(STATE_DIR, { recursive: true });
await writeFile(METRICS_PATH, JSON.stringify(store, null, 2), ‘utf8’);
}

async function sendTelemetry(payload: StopHookInput, entry: MetricsEntry) {
if (!TELEMETRY_URL) return;
await fetch(TELEMETRY_URL, {
method: ‘POST’,
headers: { ‘Content-Type’: ‘application/json’ },
body: JSON.stringify({
conversationId: payload.conversation_id,
generationId: payload.generation_id,
model: payload.model,
modelId: payload.model_id,
modelParams: payload.model_params,
status: payload.status,
errorCount: entry.errorCount,
loopCount: payload.loop_count,
timestamp: entry.lastUpdatedIso
})
});
}

async function main() {
const payload = await parseHookInput();
const metrics = await readMetrics();
const entry =
metrics[payload.conversation_id] ?? {
lastStatus: payload.status,
errorCount: 0,
lastUpdatedIso: ‘’
};

entry.lastStatus = payload.status;
entry.lastUpdatedIso = new Date().toISOString();
entry.errorCount = payload.status === ‘error’ ? entry.errorCount + 1 : 0;

metrics[payload.conversation_id] = entry;
await writeMetrics(metrics);
await sendTelemetry(payload, entry);

const response: StopHookOutput = {};
if (entry.errorCount >= 2 && payload.loop_count < 4) {
response.followup_message =
‘Automated retry triggered after two failures. Double-check credentials before running again.’;
}

process.stdout.write(JSON.stringify(response) + ‘\n’);
}

main().catch(error => {
console.error(‘[stop hook] failed’, error);
process.stdout.write(‘{}\n’);
});

 `AGENT_TELEMETRY_URL` 设置为接收运行摘要的内部端点。

### Python 清单保护钩子

需要强大的解析库时,Python 是理想选择。此钩子会在运行 `kubectl apply` 前使用 `pyyaml` 检查 Kubernetes 清单;Bash 难以安全解析多文档 YAML。
```json title="hooks.json"
{
  "version": 1,
  "hooks": {
    "beforeShellExecution": [
      {
        "command": "python3 .cursor/hooks/kube_guard.py"
      }
    ]
  }
}

```python title=”.cursor/hooks/kube_guard.py”

!/usr/bin/env python3

import json
import shlex
import sys
from pathlib import Path

import yaml

SENSITIVE_NAMESPACES = {“prod”, “production”}

def main() -> None:
payload = json.load(sys.stdin)
command = payload.get(“command”, “”)
cwd = Path(payload.get(“cwd”) or “.”)
response = {“continue”: True, “permission”: “allow”}

try:
    args = shlex.split(command)
except ValueError:
    print(json.dumps(response))
    return

if len(args) < 2 or args[0] != "kubectl" or args[1] != "apply" or "-f" not in args:
    print(json.dumps(response))
    return

f_index = args.index("-f")
if f_index + 1 >= len(args):
    print(json.dumps(response))
    return

manifest_arg = args[f_index + 1]
manifest_path = (cwd / manifest_arg).resolve()

if not manifest_path.exists():
    print(json.dumps(response))
    return

cli_namespace = None
for i, arg in enumerate(args):
    if arg in ("-n", "--namespace") and i + 1 < len(args):
        cli_namespace = args[i + 1]
    elif arg.startswith("--namespace="):
        cli_namespace = arg.split("=", 1)[1]
    elif arg.startswith("-n="):
        cli_namespace = arg.split("=", 1)[1]

try:
    documents = list(yaml.safe_load_all(manifest_path.read_text()))
except (OSError, yaml.YAMLError) as exc:
    sys.stderr.write(f"Failed to read/parse {manifest_path}: {exc}\n")
    print(json.dumps(response))
    return

if cli_namespace in SENSITIVE_NAMESPACES or any(
    (doc or {}).get("metadata", {}).get("namespace") in SENSITIVE_NAMESPACES
    for doc in documents
):
    response.update(
        {
            "permission": "ask",
            "user_message": "kubectl apply to prod requires manual approval.",
            "agent_message": f"{manifest_path.name} includes protected namespaces; confirm with your team before continuing.",
        }
    )

print(json.dumps(response))

if name == “main”:
main()

在运行钩子脚本的环境中安装 PyYAML (例如 `pip install pyyaml`) 确保可成功导入解析器

## 合作伙伴集成

我们与已为 Cursor 构建钩子支持的生态合作伙伴合作这些集成涵盖安全扫描治理机密信息管理等

### MCP 治理与可见性

| 合作伙伴                                                                                    | 描述                                            |
| --------------------------------------------------------------------------------------- | --------------------------------------------- |
| [MintMCP](https://www.mintmcp.com/blog/mcp-governance-cursor-hooks)                     | 建立完整的 MCP 服务器清单,监控工具使用模式,并在响应到达 AI 模型前扫描敏感数据。 |
| [Oasis Security](https://www.oasis.security/blog/cursor-oasis-governing-agentic-access) | 对 AI 智能体操作实施最小权限策略,并在企业系统中保留完整的审计追踪记录。        |
| [Runlayer](https://www.runlayer.com/blog/cursor-hooks)                                  | 封装 MCP 工具,并集成其 MCP 代理,以集中管控和监测智能体与工具之间的交互。    |

### 代码安全与最佳实践

| 合作伙伴                                                             | 描述                                         |
| ---------------------------------------------------------------- | ------------------------------------------ |
| [Corridor](https://corridor.dev/blog/corridor-cursor-hooks/)     | 在编写代码的同时,针对代码实现和安全设计决策获得实时反馈。              |
| [Semgrep](https://semgrep.dev/blog/2025/cursor-hooks-mcp-server) | 自动扫描 AI 生成的代码中的漏洞,并提供实时反馈以重新生成代码,直至解决安全问题。 |

### 依赖项安全

| 合作伙伴                                                                                                                | 描述                                  |
| ------------------------------------------------------------------------------------------------------------------- | ----------------------------------- |
| [Endor Labs](https://www.endorlabs.com/learn/bringing-malware-detection-into-ai-coding-workflows-with-cursor-hooks) | 拦截软件包安装并扫描恶意依赖项,在供应链攻击进入您的代码库前加以阻止。 |

### 智能体安全与防护

| 合作伙伴                                                             | 描述                                                 |
| ---------------------------------------------------------------- | -------------------------------------------------- |
| [Snyk](https://snyk.io/blog/evo-agent-guard-cursor-integration/) | 借助 Evo Agent Guard 实时评审智能体操作,检测并防范提示词注入、危险工具调用等问题。 |

### 机密信息管理

| 合作伙伴                                                                    | 描述                                                                              |
| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| [1Password](https://marketplace.1password.com/integration/cursor-hooks) | 在执行 shell 命令前,验证来自 1Password Environments 的环境文件是否已正确挂载,以便在不将凭据写入磁盘的情况下按需访问机密信息。 |

有关钩子合作伙伴的更多信息请参阅博客文章 [面向安全和平台团队的 Hooks](https://cursor.com/blog/hooks-partners)。

## 配置

 `hooks.json` 文件中定义钩子配置可存在于多个层级所有来源中匹配的钩子都会运行如果响应冲突合并时优先采用优先级更高来源的响应
```sh
~/.cursor/
├── hooks.json
└── hooks/
    ├── audit.sh
    └── block-git.sh
  • 企业版 (由 MDM 管理,适用于全系统) :
  • macOS:/Library/Application Support/Cursor/hooks.json
  • Linux/WSL:/etc/cursor/hooks.json
  • Windows:C:\\ProgramData\\Cursor\\hooks.json
  • 团队 (通过云端分发,仅限企业版) :
  • 网页仪表盘中配置,并自动同步给所有团队成员
  • 项目 (项目专用) :
  • <project-root>/.cursor/hooks.json
  • 项目 hooks 会在任何受信任的工作区中运行,并随项目一同提交到版本控制
  • 用户 (用户专用) :
  • ~/.cursor/hooks.json

优先级顺序 (从高到低) :企业版 → 团队 → 项目 → 用户

hooks 对象将 hook 名称映射到 hook 定义数组。每个定义目前支持 command 属性,其值可以是 shell 字符串、绝对路径或相对路径。工作目录取决于 hook 的来源:

  • 项目 hooks (代码仓库中的 .cursor/hooks.json) :从项目根目录运行
  • 用户 hooks (~/.cursor/hooks.json) :从 ~/.cursor/ 运行
  • 企业版 hooks (全系统配置) :从企业版配置目录运行
  • 团队 hooks (通过云端分发) :从受管 hooks 目录运行

对于项目 hooks,请使用 .cursor/hooks/script.sh 这类路径 (相对于项目根目录) ,而不要使用 ./hooks/script.sh (后者会查找 <project>/hooks/script.sh) 。

配置文件

此示例展示用户级钩子文件 (~/.cursor/hooks.json) 。对于项目级钩子,请将 ./hooks/script.sh 之类的路径改为 .cursor/hooks/script.sh

{
  "version": 1,
  "hooks": {
    "sessionStart": [{ "command": "./session-init.sh" }],
    "sessionEnd": [{ "command": "./audit.sh" }],
    "preToolUse": [
      {
        "command": "./hooks/validate-tool.sh",
        "matcher": "Shell|Read|Write"
      }
    ],
    "postToolUse": [{ "command": "./hooks/audit-tool.sh" }],
    "subagentStart": [{ "command": "./hooks/validate-subagent.sh" }],
    "subagentStop": [{ "command": "./hooks/audit-subagent.sh" }],
    "beforeShellExecution": [{ "command": "./script.sh" }],
    "afterShellExecution": [{ "command": "./script.sh" }],
    "afterMCPExecution": [{ "command": "./script.sh" }],
    "afterFileEdit": [{ "command": "./format.sh" }],
    "preCompact": [{ "command": "./audit.sh" }],
    "stop": [{ "command": "./audit.sh", "loop_limit": 10 }],
    "beforeTabFileRead": [{ "command": "./redact-secrets-tab.sh" }],
    "afterTabFileEdit": [{ "command": "./format-tab.sh" }],
    "workspaceOpen": [{ "command": "./register-workspace-plugins.sh" }]
  }
}

智能体钩子 (sessionStartsessionEndpreToolUsepostToolUsepostToolUseFailuresubagentStartsubagentStopbeforeShellExecutionafterShellExecutionbeforeMCPExecutionafterMCPExecutionbeforeReadFileafterFileEditbeforeSubmitPromptpreCompactstopafterAgentResponseafterAgentThought) 适用于 Cmd+K 和 Agent Chat 操作。Tab 钩子 (beforeTabFileReadafterTabFileEdit) 专用于内联 Tab 补全。应用生命周期钩子 (workspaceOpen) 会在工作区打开时以及工作区文件夹发生更改时触发,与任何智能体会话无关。

全局配置选项

选项 类型 默认值 描述
version number 1 配置架构版本

单个脚本的配置选项

选项 类型 默认值 描述
command string 必填 脚本路径或命令
type "command" "prompt" "command" 钩子的执行类型
timeout number 平台默认值 执行超时时间 (秒)
loop_limit number null 5 stop/subagentStop 钩子中单个脚本的循环次数上限。null 表示不设上限。Cursor 钩子的默认值为 5,Claude Code 钩子的默认值为 null
failClosed boolean false 当为 true 时,钩子失败 (崩溃、超时、无效 JSON) 会阻止该操作,而非允许其继续执行。适用于安全性要求较高的钩子。
matcher object - 钩子运行条件的筛选标准

匹配器配置

匹配器可用于筛选钩子的运行时机。匹配器适用于哪个字段取决于钩子类型:

{
  "hooks": {
    "preToolUse": [
      {
        "command": "./validate-shell.sh",
        "matcher": "Shell"
      }
    ],
    "subagentStart": [
      {
        "command": "./validate-explore.sh",
        "matcher": "explore|shell"
      }
    ],
    "beforeShellExecution": [
      {
        "command": "./approve-network.sh",
        "matcher": "curl|wget|nc "
      }
    ]
  }
}
  • subagentStart:匹配器会匹配子智能体类型 (例如 exploreshellgeneralPurpose) 。可用于仅在启动特定类型的子智能体时运行钩子。上述示例仅对 explore 或 shell 子智能体运行 validate-explore.sh
  • beforeShellExecution:匹配器会匹配shell 命令字符串。可用于仅在命令匹配某个模式时运行钩子 (例如网络调用、删除文件) 。上述示例仅当命令包含 curlwgetnc 时运行 approve-network.sh

各钩子可用的匹配器:

  • preToolUse / postToolUse / postToolUseFailure:按工具类型筛选。可选值包括 ShellReadWriteGrepDeleteTask,以及采用 MCP:<tool_name> 格式的 MCP 工具。
  • subagentStart / subagentStop:按子智能体类型筛选 (generalPurposeexploreshell 等) 。
  • beforeShellExecution / afterShellExecution:按 shell 命令文本筛选;匹配器会与完整的命令字符串匹配。
  • beforeReadFile:按工具类型筛选 (TabReadRead 等) 。
  • afterFileEdit:按工具类型筛选 (TabWriteWrite 等) 。
  • beforeSubmitPrompt:匹配值 UserPromptSubmit
  • stop:匹配值 Stop
  • afterAgentResponse:匹配值 AgentResponse
  • afterAgentThought:匹配值 AgentThought

团队分发

可通过项目 hooks (使用版本控制) 、MDM 工具或 Cursor 云分发系统向团队成员分发 hooks。

项目 hooks (版本控制)

项目 hooks 是与团队共享 hooks 最简单的方式。将 hooks.json 文件放在 <project-root>/.cursor/hooks.json 路径下,并提交到代码仓库。团队成员在受信任的工作区中打开项目时,Cursor 会自动加载并运行项目 hooks。

云端代理在云端处理您的代码仓库时,也会加载这些项目 hooks。

项目 hooks:

  • 与代码一同存储在版本控制中
  • 会在受信任的工作区中为所有团队成员自动加载
  • 可以针对特定项目设置 (例如,为特定代码库强制执行格式规范)
  • 只能在受信任的工作区中运行 (出于安全考虑)

通过 MDM 分发

使用移动设备管理 (MDM) 工具在组织内分发钩子。在每台设备的目标目录中放置 hooks.json 文件和钩子脚本。

用户主目录 (按用户分发) :

  • ~/.cursor/hooks.json
  • ~/.cursor/hooks/ (存放钩子脚本)

全局目录 (系统级分发) :

  • macOS:/Library/Application Support/Cursor/hooks.json
  • Linux/WSL:/etc/cursor/hooks.json
  • Windows:C:\\ProgramData\\Cursor\\hooks.json

注意:基于 MDM 的分发完全由您的组织负责管理。Cursor 不会通过您的 MDM 解决方案部署或管理文件。请确保内部 IT 或安全团队按照组织策略处理配置、部署和更新。

云端分发 (仅限企业版)

企业版团队可使用 Cursor 原生的云端分发功能,自动将钩子同步给所有团队成员。在网页仪表盘中配置钩子后,团队成员登录时,Cursor 会自动将已配置的钩子部署到所有客户端设备。

云端分发提供:

  • 每三十分钟自动同步给所有团队成员
  • 可按操作系统为特定平台配置钩子
  • 通过仪表盘集中管理

企业管理员无需访问个人设备,即可通过仪表盘创建、编辑和管理团队钩子。

联系销售,获取企业版云端钩子分发功能。

参考

通用架构

输入 (所有钩子)

除钩子特有的字段外,所有钩子还会接收一组基础字段:

{
  "conversation_id": "string",
  "generation_id": "string",
  "model": "string",
  "model_id": "string",
  "model_params": [{ "id": "string", "value": "string" }],
  "hook_event_name": "string",
  "cursor_version": "string",
  "workspace_roots": ["<path>"],
  "user_email": "string | null",
  "transcript_path": "string | null"
}
字段 类型 描述
conversation_id string 对话在多个轮次中保持不变的稳定 ID
generation_id string 随每条用户消息变化的当前 generation
model string 为触发该钩子的 composer 配置的旧版模型 slug
model_id string (optional) 所选模型的结构化 ID (如有)
model_params array (optional) 所选模型的参数,如思考、上下文或 effort。每项均包含 idvalue
hook_event_name string 正在运行的钩子名称
cursor_version string Cursor 应用版本 (例如 “1.7.2”)
workspace_roots string[] 工作区根文件夹列表 (通常只有一个,但多根工作区可以有多个)
user_email string null 已认证用户的电子邮件地址 (如有)
transcript_path string null 主对话会话记录文件的路径 (禁用会话记录时为 null)

应用生命周期钩子 (workspaceOpen) 在任何智能体会话之外触发,因此请求中不包含 conversation_idgeneration_idmodelsession_idtranscript_path。但仍会收到 hook_event_namecursor_versionworkspace_rootsuser_email

钩子事件

preToolUse

在执行任何工具前调用。这是适用于所有工具类型 (Shell、Read、Write、MCP、Task 等) 的通用钩子。可使用匹配器按特定工具筛选。

// 输入
{
  "tool_name": "Shell",
  "tool_input": { "command": "npm install", "working_directory": "/project" },
  "tool_use_id": "abc123",
  "cwd": "/project",
  "model": "claude-opus-4-7-thinking-max",
  "model_id": "claude-opus-4-7",
  "model_params": [
    { "id": "thinking", "value": "true" },
    { "id": "context", "value": "1m" },
    { "id": "effort", "value": "max" }
  ],
  "agent_message": "Installing dependencies..."
}

// 输出
{
  "permission": "allow" | "deny",
  "user_message": "<message shown in client when denied>",
  "agent_message": "<message sent to agent when denied>",
  "updated_input": { "command": "npm ci" }
}
输出字段 类型 描述
permission string "allow" 表示允许继续,"deny" 表示阻止。架构接受 "ask",但目前不会对 preToolUse 强制执行。
user_message string (可选) 操作被拒绝时向用户显示的消息
agent_message string (可选) 操作被拒绝时反馈给智能体的消息
updated_input object (可选) 要改用的修改后的工具输入

postToolUse

在工具成功执行后触发。可用于审计、使用分析和注入上下文。

// 输入
{
  "tool_name": "Shell",
  "tool_input": { "command": "npm test" },
  "tool_output": "{\"exitCode\":0,\"stdout\":\"All tests passed\"}",
  "tool_use_id": "abc123",
  "cwd": "/project",
  "duration": 5432,
  "model": "claude-opus-4-7-thinking-max",
  "model_id": "claude-opus-4-7",
  "model_params": [
    { "id": "thinking", "value": "true" },
    { "id": "context", "value": "1m" },
    { "id": "effort", "value": "max" }
  ]
}

// 输出
{
  "updated_mcp_tool_output": { "modified": "output" },
  "additional_context": "Test coverage report attached."
}
输入字段 类型 描述
duration number 执行耗时 (毫秒)
tool_output string 工具返回的 JSON 字符串化结果负载 (而非原始终端文本)
输出字段 类型 描述
updated_mcp_tool_output object (optional) 仅适用于 MCP 工具:替换模型所见的工具输出
additional_context string (optional) 工具结果返回后注入对话的额外上下文

postToolUseFailure

在工具执行失败、超时或被拒绝时调用。适用于错误追踪和恢复逻辑。

// 输入
{
  "tool_name": "Shell",
  "tool_input": { "command": "npm test" },
  "tool_use_id": "abc123",
  "cwd": "/project",
  "error_message": "Command timed out after 30s",
  "failure_type": "timeout" | "error" | "permission_denied",
  "duration": 5000,
  "is_interrupt": false
}

// 输出
{
  // 目前不支持任何输出字段
}
输入字段 类型 描述
error_message string 失败原因
failure_type string 失败类型:"error""timeout""permission_denied"
duration number 发生失败前的时间 (毫秒)
is_interrupt boolean 此失败是否由用户中断或取消导致

subagentStart

在启动子智能体 (Task 工具) 之前调用。可允许或拒绝创建子智能体。

// 输入
{
  "subagent_id": "abc-123",
  "subagent_type": "generalPurpose",
  "task": "Explore the authentication flow",
  "parent_conversation_id": "conv-456",
  "tool_call_id": "tc-789",
  "subagent_model": "claude-sonnet-4-20250514",
  "is_parallel_worker": false,
  "git_branch": "feature/auth"
}

// 输出
{
  "permission": "allow" | "deny",
  "user_message": "<message shown when denied>"
}
输入字段 类型 描述
subagent_id string 此子智能体实例的唯一标识符
subagent_type string 子智能体类型:generalPurposeexploreshell
task string 分配给子智能体的任务描述
parent_conversation_id string 父智能体会话的对话 ID
tool_call_id string 触发该子智能体的工具调用 ID
subagent_model string 子智能体将使用的模型
is_parallel_worker boolean 此子智能体是否作为并行工作器运行
git_branch string (optional) 子智能体将操作的 Git 分支 (如适用)
输出字段 类型 描述
permission string "allow" 表示允许继续,"deny" 表示阻止。subagentStart 不支持 "ask",并将其视为 "deny"
user_message string (optional) 子智能体被拒绝时向用户显示的消息

subagentStop

子智能体完成、出错或被中止时调用。可触发后续操作。

// 输入
{
  "subagent_type": "generalPurpose",
  "status": "completed" | "error" | "aborted",
  "task": "Explore the authentication flow",
  "description": "Exploring auth flow",
  "summary": "<subagent output summary>",
  "duration_ms": 45000,
  "message_count": 12,
  "tool_call_count": 8,
  "loop_count": 0,
  "modified_files": ["src/auth.ts"],
  "agent_transcript_path": "/path/to/subagent/transcript.txt"
}

// 输出
{
  "followup_message": "<auto-continue with this message>"
}
输入字段 类型 描述
subagent_type string 子智能体类型:generalPurposeexploreshell
status string "completed""error""aborted"
task string 提供给子智能体的任务描述
description string 子智能体用途的简要说明
summary string 子智能体的输出摘要
duration_ms number 以毫秒为单位的执行时间
message_count number 子智能体会话期间交换的消息数量
tool_call_count number 子智能体发起的工具调用次数
loop_count number 此子智能体已触发的 subagentStop 后续操作次数 (从 0 开始)
modified_files string[] 子智能体修改的文件
agent_transcript_path string null 子智能体自身会话记录文件的路径 (与父对话分开)
输出字段 类型 描述
followup_message string (可选) 使用此消息自动继续。仅当 status"completed" 时使用。

followup_message 字段支持循环式流程:子智能体完成后会触发下一次迭代。后续操作与 stop 钩子使用相同的可配置循环限制 (默认值为 5,可通过 loop_limit 配置) 。

beforeShellExecution / beforeMCPExecution

在执行任何 shell 命令或 MCP 工具前调用。返回权限判定。

默认情况下,钩子执行失败 (崩溃、超时、JSON 无效) 时,操作仍会继续执行 (故障开放) 。可在钩子定义中设置 failClosed: true,使操作在失败时被阻止。对于安全关键型 beforeMCPExecution 钩子,建议这样设置。

// beforeShellExecution 输入
{
  "command": "<full terminal command>",
  "cwd": "<current working directory>",
  "sandbox": false
}

// beforeMCPExecution 输入
{
  "tool_name": "<tool name>",
  "tool_input": "<json params>",
  "mcp_server_name": "<server name from mcp.json>"
}
// 另外必须提供以下之一 (HTTP/SSE 服务器) :
{ "url": "<server url>", "mcp_server_url": "<server url>" }
// 或 (stdio 服务器) :
{ "command": "<launch command and args>" }

// 输出
{
  "permission": "allow" | "deny" | "ask",
  "user_message": "<message shown in client>",
  "agent_message": "<message sent to agent>"
}
字段 类型 描述
tool_name string 即将运行的 MCP 工具名称
tool_input string 将传递给工具的 JSON 参数 string
mcp_server_name string 服务器在其 mcp.json 中的键 (例如 linear) 。使用此项识别特定服务器。
mcp_server_url string 服务器 URL,仅适用于 HTTP/SSE 服务器
url string mcp_server_url 相同;仅适用于 HTTP/SSE 服务器
command string 以空格连接的 stdio 启动命令和参数;仅适用于 stdio 服务器

根据 mcp_server_name (以及 tool_name) 进行匹配,以确定调用是否指向你的服务器。command 是服务器 config 中的启动 string,在不同安装中可能有所不同:相对路径、${CURSOR_PLUGIN_ROOT} 展开,或 HTTP transport (完全没有 command) 。对于未识别内容一律允许通过的钩子,应将缺失或异常的 mcp_server_name 视为拒绝。

afterShellExecution

在 shell 命令执行后触发;可用于审计或从命令输出中收集指标。

// 输入
{
  "command": "<full terminal command>",
  "output": "<full terminal output>",
  "duration": 1234,
  "sandbox": false
}
字段 类型 描述
command string 已执行的完整终端命令
output string 从终端捕获的完整输出
duration number 执行 shell 命令耗时 (毫秒,不包括等待批准的时间)
sandbox boolean 命令是否在沙盒环境中运行

afterMCPExecution

在 MCP 工具执行后触发;包含该工具的输入参数和完整的 JSON 结果。

// 输入
{
  "tool_name": "<tool name>",
  "tool_input": "<json params>",
  "mcp_server_name": "<server name from mcp.json>",
  "result_json": "<tool result json>",
  "duration": 1234
}
字段 类型 描述
tool_name string 已执行的 MCP 工具的名称
tool_input string 传递给工具的 JSON 参数 string
mcp_server_name string 服务器在其 mcp.json 中的键
mcp_server_url string 服务器 URL,仅适用于 HTTP/SSE 服务器
result_json string 工具响应的 JSON string
duration number 执行 MCP 工具所用时长 (单位为毫秒,不包括等待批准的时间)

afterFileEdit

智能体编辑文件后触发;可用于格式化或统计智能体编写的代码。

// 输入
{
  "file_path": "<absolute path>",
  "edits": [{ "old_string": "<search>", "new_string": "<replace>" }]
}

beforeReadFile

在智能体读取文件前调用。可用于访问控制,防止将敏感文件发送给模型。

默认情况下,beforeReadFile 钩子失败 (崩溃、超时、JSON 无效) 时会记录日志,并仍允许读取。请在钩子定义中设置 failClosed: true,以便在失败时阻止读取。

// 输入
{
  "file_path": "<absolute path>",
  "content": "<file contents>",
  "attachments": [
    {
      "type": "file" | "rule",
      "file_path": "<absolute path>"
    }
  ]
}

// 输出
{
  "permission": "allow" | "deny",
  "user_message": "<message shown when denied>"
}
输入字段 类型 描述
file_path string 正在读取的文件的绝对路径
content string 文件的完整内容
attachments array 与提示词关联的上下文附件。每个条目都包含 type ("file""rule") 和 file_path
输出字段 类型 描述
permission string "allow" 表示继续,"deny" 表示阻止
user_message string (optional) 拒绝时向用户显示的消息

beforeTabFileRead

在 Tab (内联补全) 读取文件前调用。可在 Tab 访问文件内容前启用脱敏或访问控制。

beforeReadFile 的主要区别:

  • 仅由 Tab 触发,不会由智能体触发
  • 不包含 attachments 字段 (Tab 不使用提示词附件)
  • 可用于对自主运行的 Tab 操作应用不同策略
// 输入
{
  "file_path": "<absolute path>",
  "content": "<file contents>"
}

// 输出
{
  "permission": "allow" | "deny"
}

afterTabFileEdit

Tab (内联补全) 编辑文件后调用。适用于格式化工具或审计 Tab 写入的代码。

afterFileEdit 的主要区别:

  • 仅由 Tab 触发,不由智能体触发
  • 包含详细的编辑信息:rangeold_linenew_line,可精确跟踪编辑内容
  • 适用于对 Tab 编辑进行细粒度格式化或分析
// 输入
{
  "file_path": "<absolute path>",
  "edits": [
    {
      "old_string": "<search>",
      "new_string": "<replace>",
      "range": {
        "start_line_number": 10,
        "start_column": 5,
        "end_line_number": 10,
        "end_column": 20
      },
      "old_line": "<line before edit>",
      "new_line": "<line after edit>"
    }
  ]
}

// 输出
{
  // 目前不支持输出字段
}

beforeSubmitPrompt

用户点击发送后、发起后端请求前立即调用。可阻止提交。

// 输入
{
  "prompt": "<user prompt text>",
  "attachments": [
    {
      "type": "file" | "rule",
      "file_path": "<absolute path>"
    }
  ]
}

// 输出
{
  "continue": true | false,
  "user_message": "<message shown to user when blocked>"
}
输出字段 类型 描述
continue boolean 是否允许继续提交提示词
user_message string (可选) 提示词被阻止时显示给用户的消息

afterAgentResponse

智能体完成助手消息后调用。

// 输入
{
  "text": "<assistant final text>"
}

afterAgentThought

智能体完成一个思考块后调用。可用于观察智能体的推理过程。

// 输入
{
  "text": "<fully aggregated thinking text>",
  "duration_ms": 5000
}

// 输出
{
  // 目前不支持输出字段
}
字段 类型 描述
text string 已完成块的完整聚合思考文本
duration_ms number (可选) 思考块的持续时间 (毫秒)

stop

智能体循环结束时触发。可选择自动提交后续用户消息,以继续迭代。

// 输入
{
  "status": "completed" | "aborted" | "error",
  "loop_count": 0
}
// 输出
{
  "followup_message": "<message text>"
}
  • 可选的 followup_message 为 string。提供且非空时,Cursor 会自动将其提交为下一条用户消息。这支持循环式流程 (例如,迭代直至达成目标) 。
  • loop_count 字段表示 stop 钩子已为此对话自动触发后续消息的次数 (初始值为 0) 。默认情况下,每个脚本最多可自动发送 5 条后续消息,可通过 loop_limit 选项配置。将 loop_limit 设为 null 可取消此上限。同样的限制也适用于 subagentStop 后续消息。

sessionStart

创建新的 composer 对话时触发。此钩子以即发即弃方式运行;智能体循环不会等待或强制执行阻塞式响应。可用于设置会话专用环境变量或注入额外上下文。

// 输入
{
  "session_id": "<unique session identifier>",
  "is_background_agent": true | false,
  "composer_mode": "agent" | "ask" | "edit"
}
// 输出
{
  "env": { "<key>": "<value>" },
  "additional_context": "<context to add to conversation>"
}
输入字段 类型 描述
session_id string 此会话的唯一标识符 (与 conversation_id 相同)
is_background_agent boolean 此会话是后台智能体会话还是交互式会话
composer_mode string (可选) composer 启动时的模式 (例如 “agent”、”ask”、”edit”)
输出字段 类型 描述
env object (可选) 为此会话设置的环境变量,可供后续所有钩子执行使用
additional_context string (可选) 要添加到对话初始系统上下文中的额外上下文

此架构也接受 continueuser_message 字段,但当前调用方并不强制要求提供这些字段。即使 continuefalse,也不会阻止创建会话。

sessionEnd

composer 对话结束时调用。这是一个即发即弃的钩子,适用于日志记录、使用分析或清理任务。响应会被记录,但不会使用。

// 输入
{
  "session_id": "<唯一会话标识符>",
  "reason": "completed" | "aborted" | "error" | "window_close" | "user_close",
  "duration_ms": 45000,
  "is_background_agent": true | false,
  "final_status": "<状态字符串>",
  "error_message": "<当 reason 为 'error' 时的错误详情>"
}
// 输出
{
  // 无输出字段——触发后不等待结果
}
输入字段 类型 描述
session_id string 即将结束的会话的唯一标识符
reason string 会话结束原因:”completed”、”aborted”、”error”、”window_close” 或 “user_close”
duration_ms number 会话总时长 (毫秒)
is_background_agent boolean 是否为后台智能体会话
final_status string 会话的最终状态
error_message string (optional) 当原因是 “error” 时的错误消息

preCompact

在上下文窗口压缩/摘要之前调用。这是一个仅用于观察的钩子,无法阻止或修改压缩行为。可用于记录压缩发生的时间或通知用户。

// 输入
{
  "trigger": "auto" | "manual",
  "context_usage_percent": 85,
  "context_tokens": 120000,
  "context_window_size": 128000,
  "message_count": 45,
  "messages_to_compact": 30,
  "is_first_compaction": true | false
}
// 输出
{
  "user_message": "<message to show when compaction occurs>"
}
输入字段 类型 描述
trigger string 触发压缩的方式:”auto” 或 “manual”
context_usage_percent number 当前上下文窗口用量百分比 (0-100)
context_tokens number 当前上下文窗口的 token 数
context_window_size number 上下文窗口的最大 token 数
message_count number 对话中的消息数量
messages_to_compact number 将被摘要的消息数量
is_first_compaction boolean 是否为该对话的首次压缩
输出字段 类型 描述
user_message string (可选) 压缩时显示给用户的消息

workspaceOpen

Cursor 打开工作区时触发一次,此后每次工作区文件夹变更时都会再次触发。窗口中没有任何工作区文件夹时不会触发。可在 Cursor 桌面应用和命令行界面中运行。

// 输入
{
  "hook_event_name": "workspaceOpen",
  "cursor_version": "string",
  "workspace_roots": ["<absolute path>"],
  "user_email": "string | null"
}

// 输出
{
  "pluginPaths": ["<absolute path>", "..."]
}
输出字段 类型 描述
pluginPaths string[] (可选) 为当前工作区加载插件的目录绝对路径。

环境变量

钩子脚本执行时会接收环境变量:

变量 描述 始终可用
CURSOR_PROJECT_DIR 工作区根目录
CURSOR_VERSION Cursor 版本字符串
CURSOR_USER_EMAIL 已认证用户的电子邮件 已登录时
CURSOR_TRANSCRIPT_PATH 对话会话记录文件的路径 已启用会话记录时
CURSOR_CODE_REMOTE 在远程工作区中运行时设为字符串 "true" 仅限远程工作区
CLAUDE_PROJECT_DIR 项目目录的别名 (兼容 Claude)

sessionStart 钩子设置的会话级环境变量会传递给该会话中后续执行的所有钩子。

疑难排查

如何确认钩子是否已启用

自定义中,可通过钩子选项卡和钩子输出通道调试已配置和已执行的钩子,并查看错误。

如果钩子未正常工作

  • Cursor 会监视 hooks.json 文件,并在保存时重新加载。如果钩子仍无法加载,请重新启动 Cursor。
  • 检查钩子源文件的相对路径是否正确:
  • 对于项目钩子,路径相对于项目根目录 (例如 .cursor/hooks/script.sh)
  • 对于用户钩子,路径相对于 ~/.cursor/ (例如 ./hooks/script.shhooks/script.sh)

退出码阻止操作

命令钩子返回退出码 2 会阻止该操作 (等同于返回 permission: "deny") 。为保持兼容性,此行为与 Claude Code 一致。

企业版钩子和分发

企业版提供云端分发和团队级钩子管理。

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜