钩子可让你通过自定义脚本观察、控制和扩展智能体循环。你可以在项目级或用户级的 hooks.json 文件中定义钩子,也可以通过 自定义 中的插件安装钩子。钩子是通过 stdio 使用 JSON 进行双向通信的子进程。它们会在智能体循环中定义的各个阶段之前或之后运行,并可观察、阻止或修改行为。
借助钩子,你可以:
- 在编辑后运行格式化工具
- 为事件添加使用分析
- 扫描 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
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" }]
}
}
智能体钩子 (sessionStart、sessionEnd、preToolUse、postToolUse、postToolUseFailure、subagentStart、subagentStop、beforeShellExecution、afterShellExecution、beforeMCPExecution、afterMCPExecution、beforeReadFile、afterFileEdit、beforeSubmitPrompt、preCompact、stop、afterAgentResponse、afterAgentThought) 适用于 Cmd+K 和 Agent Chat 操作。Tab 钩子 (beforeTabFileRead、afterTabFileEdit) 专用于内联 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:匹配器会匹配子智能体类型 (例如
explore、shell、generalPurpose) 。可用于仅在启动特定类型的子智能体时运行钩子。上述示例仅对 explore 或 shell 子智能体运行validate-explore.sh。 - beforeShellExecution:匹配器会匹配shell 命令字符串。可用于仅在命令匹配某个模式时运行钩子 (例如网络调用、删除文件) 。上述示例仅当命令包含
curl、wget或nc时运行approve-network.sh。
各钩子可用的匹配器:
- preToolUse / postToolUse / postToolUseFailure:按工具类型筛选。可选值包括
Shell、Read、Write、Grep、Delete、Task,以及采用MCP:<tool_name>格式的 MCP 工具。 - subagentStart / subagentStop:按子智能体类型筛选 (
generalPurpose、explore、shell等) 。 - beforeShellExecution / afterShellExecution:按 shell 命令文本筛选;匹配器会与完整的命令字符串匹配。
- beforeReadFile:按工具类型筛选 (
TabRead、Read等) 。 - afterFileEdit:按工具类型筛选 (
TabWrite、Write等) 。 - 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。每项均包含 id 和 value。 |
|
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_id、generation_id、model、session_id 和 transcript_path。但仍会收到 hook_event_name、cursor_version、workspace_roots 和 user_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 | 子智能体类型:generalPurpose、explore、shell 等 |
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 | 子智能体类型:generalPurpose、explore、shell 等 |
|
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 触发,不由智能体触发
- 包含详细的编辑信息:
range、old_line和new_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 (可选) | 要添加到对话初始系统上下文中的额外上下文 |
此架构也接受 continue 和 user_message 字段,但当前调用方并不强制要求提供这些字段。即使 continue 为 false,也不会阻止创建会话。
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.sh或hooks/script.sh)
退出码阻止操作
命令钩子返回退出码 2 会阻止该操作 (等同于返回 permission: "deny") 。为保持兼容性,此行为与 Claude Code 一致。
企业版钩子和分发¶
企业版提供云端分发和团队级钩子管理。