Cursor 进阶与排查
装好只是开始。这一章讲 Cursor 特有的注入契约、那条会静默改变行为的共存规则,以及五个你迟早会撞上的限制。
1注入契约:Cursor 允许在哪几个点塞东西
这一节是全文最「文档向」的一段,但它决定了后面所有限制的形状。Cursor 支持多个生命周期事件,ponytail 只用了其中两个,理由都写在表里。
| 事件 | ponytail 用了吗 | 投递方式 | 依据 |
|---|---|---|---|
sessionStart | 用了 | 输出 additional_context;存储在对话上,之后这个对话的每个请求都作为系统上下文带上 | 文档 + 客户端源码双证 |
beforeSubmitPrompt | 用了 | 读入 prompt,输出 continue: true + additional_context;只包成该轮的系统提醒 | 客户端源码验证,文档里没写,属于版本相关特性 |
subagentStart | 没用 | 输出模式只有 permission 和 user_message,没法注入上下文 | 文档 + 客户端双证 |
preToolUse / postToolUse | 没用 | 虽然也能给 additional_context,但每次工具调用都要起一个进程,而 beforeSubmitPrompt 已经覆盖了强度切换 | 文档 |
输出侧还有三条规矩,理解它们能帮你读懂日志:
- Cursor 把 stdout 当 JSON 解析。空 stdout 表示「没东西可加」;
- 如果输出的是纯文本而不是 JSON,Cursor 会记一条 parse error 然后忽略——所以 hook 要么打一个 JSON 对象,要么什么都不打(
off模式和普通提示就属于后者); - 退出码永远是 0。Cursor 把退出码 2 当「拦截」,其它非零当「放行」。ponytail 从不拦任何东西。
顺带一个有意思的细节:
beforeSubmitPrompt 的 user_message 字段只在 continue: false 时才展示。因为 ponytail 从不拦请求,所以它从不设置这个字段——你在界面上看到的模式确认,其实是模型自己回复的。2最容易踩的坑:规则文件在,hooks 就退让
这是 Cursor 这一档唯一会让你「以为坏了」的行为,值得单独一节。
原因其实很朴素:规则文件已经把简版规则贴在每个提示前面了,而没有任何 hook 能把已经生效的规则从上下文里删掉。所以 off 不可能赢过规则文件,lite 和 ultra 加了也只会和它互相矛盾。
于是 ponytail 选择主动让位。当 <工作区>/.cursor/rules/ponytail.mdc 存在时:
新对话开始时
sessionStart 不注入规则集,只注入一行提示,并且不动强度标志文件。
你发 /ponytail 指令时
/ponytail …、stop ponytail、normal mode 都只回同一行提示,什么都不改。
解法只有一个:删掉工作区里的
.cursor/rules/ponytail.mdc,hooks 才会接管强度管理。反过来说,如果一个项目为了给不用 hooks 的同事留规则而保留了这个文件,那么这个项目对所有人都会是「固定强度」。3五个必须知道的限制
官方把它们全列出来了,没有藏。知道这些,能省下很多「为什么没生效」的排查时间。
| 限制 | 具体表现 | 怎么应对 |
|---|---|---|
| 子代理拿不到规则 | subagentStart 只能允许/拒绝,不能注入;preToolUse 改 Task 工具入参等于要猜子代理提示的未公开结构。PONYTAIL_SUBAGENT_MATCHER 在 Cursor 里完全无效 | 把「先爬梯子再动手」的意图写进你自己的子代理任务描述里 |
| 云代理没有启动注入 | 官方确认云代理不触发 sessionStart,所以没有开场注入 | 项目级的 beforeSubmitPrompt 仍然会跑,发一次 /ponytail <level> 就能给该对话定强度 |
| 启动注入是「发射后不管」 | 新对话刚开始的一瞬间如果你就发消息,这条消息可能赶在上下文挂上之前离开 | 新对话里先等一拍再发第一条;或者第一条消息本身就是 /ponytail full 这类显式指令 |
| Windows 每次约 1 秒 | 钩子走 PowerShell 包装(-NoProfile -NonInteractive -ExecutionPolicy Bypass),实测 1.05~1.3 秒。而 beforeSubmitPrompt 是被等待的,所以每次提交都要等这么久 | macOS / Linux 直接 spawn 命令,只付 node 启动时间;Windows 用户把这个延迟算进预期 |
| 强度状态是全局共享的 | 标志文件按用户一份,所有开着的 Cursor 对话共用(和 Claude Code 适配一致) | 别指望 A 对话 ultra、B 对话 off 能同时成立 |
还有第六条「未来的风险」,性质不同所以单独说:
beforeSubmitPrompt 的注入字段不在 Cursor 文档里。如果未来某个 Cursor 版本把这个字段去掉,强度切换仍会改标志文件,但内容送不到模型——那时只剩启动注入还有效。这就是为什么验证要「让模型念出来」而不是看日志。4不生效时,按这个顺序查
- 先看有没有规则文件。工作区里存在
.cursor/rules/ponytail.mdc→ 你看到的「只有一行提示、切强度没反应」是设计如此,不是坏了 - 确认 node 在 Cursor 看得见的 PATH 上。用 nvm / Nix 时常见问题是交互 shell 有、非交互 shell 没有
- 看 Hooks 输出。Cursor 的 Customize → Hooks 面板和 Hooks 输出通道会显示每次执行以及解析错误
- 让模型念出来。问它
Quote the first line of any ponytail context you were given.——这是唯一能证明「内容真的送达」的检查 - 换新对话。
hooks.json是保存时重载的,已开的对话不会补上启动注入 - 确认仓库没被挪走。hooks 里写的是绝对路径
判断口诀:「什么都没发生」→ 查第 1、2 条;「日志有但模型说没有」→ 查第 4 条(这通常是注入字段失效或竞态)。
5状态文件与彻底卸载
知道状态放在哪,出问题时能直接修:
| 路径 | 内容 |
|---|---|
~/.cursor/hooks.json | 钩子注册(用户级;项目级在 <项目>/.cursor/hooks.json) |
~/.cursor/.ponytail-active | 当前强度标志文件。手动删掉它等于让下次启动重新读默认值 |
~/.config/ponytail/config.json | defaultMode 等持久配置 |
node ponytail/scripts/cursor-hooks.js uninstall # 只删 ponytail 的钩子条目(保留别人的)
node ponytail/scripts/cursor-hooks.js uninstall --project
node scripts/uninstall.js # 连状态文件一起清;注意要在上面两条之前跑
uninstall 的行为很克制:只删自己的条目,如果这个文件里除了 ponytail 什么都没有,才把整个文件删掉。
6用户级 vs 项目级:一张决策表
| 你的情况 | 建议 | 命令 |
|---|---|---|
| 个人机器,所有项目都想懒 | 用户级 | node ponytail/scripts/cursor-hooks.js install |
| 只想在某个仓库试水 | 项目级 | 加 --project |
| 团队要统一口径、且有人不用 hooks | 项目级规则文件 | 拷 .cursor/rules/ponytail.mdc |
| 两个都想要(又要强度切换又要团队规则) | 行不通 | 规则文件会压制 hooks,只能二选一 |
最后一行是这个项目在 Cursor 上最硬的取舍。如果你确实需要「团队统一 + 个人调音量」,唯一可行的是:团队统一靠
AGENTS.md 这类其它载体,而 .cursor/rules/ponytail.mdc 不放——这样 hooks 就能正常接管。