Ponytail 新手教程

Cursor 进阶与排查

装好只是开始。这一章讲 Cursor 特有的注入契约、那条会静默改变行为的共存规则,以及五个你迟早会撞上的限制。

5 个已知限制 注入契约 issue #817 原始讨论

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不生效时,按这个顺序查

  1. 先看有没有规则文件。工作区里存在 .cursor/rules/ponytail.mdc → 你看到的「只有一行提示、切强度没反应」是设计如此,不是坏了
  2. 确认 node 在 Cursor 看得见的 PATH 上。用 nvm / Nix 时常见问题是交互 shell 有、非交互 shell 没有
  3. 看 Hooks 输出。Cursor 的 Customize → Hooks 面板和 Hooks 输出通道会显示每次执行以及解析错误
  4. 让模型念出来。问它 Quote the first line of any ponytail context you were given.——这是唯一能证明「内容真的送达」的检查
  5. 换新对话。hooks.json 是保存时重载的,已开的对话不会补上启动注入
  6. 确认仓库没被挪走。hooks 里写的是绝对路径
判断口诀:「什么都没发生」→ 查第 1、2 条;「日志有但模型说没有」→ 查第 4 条(这通常是注入字段失效或竞态)。

5状态文件与彻底卸载

知道状态放在哪,出问题时能直接修:

路径内容
~/.cursor/hooks.json钩子注册(用户级;项目级在 <项目>/.cursor/hooks.json)
~/.cursor/.ponytail-active当前强度标志文件。手动删掉它等于让下次启动重新读默认值
~/.config/ponytail/config.jsondefaultMode 等持久配置
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 就能正常接管。