DietrichGebert/ponytail:127.5k stars 的 "懒惰资深工程师" 技能,把 AI Agent 从 404 行的日期选择器里拽出来

上周我给自己的一个内部工具加个” 选日期” 的输入框。需求一句话:表单里加个日期选择器,能选年月日就行。

我把这句话丢给 agent,然后去泡了杯咖啡。

回来的时候屏幕上滚过一长串东西。它装了 flatpickr,写了一个 DatePicker.tsx 包装组件,配了一份 datepicker.css,把 locale 抽成了 props,为” 以后可能要支持范围选择” 预留了一个 mode 参数,最后在对话框里问我:时区要按用户本地还是 UTC 存?要不要顺便把 dayjs 也引进来统一处理?

git diff 显示 400 多行新增,一个新依赖,四个新文件。

我盯着看了一会儿,把整个 diff 撤了,手写了一行:

1
<input type="date">

浏览器自带的。有键盘可访问性,有本地化,有原生的日历弹层,移动端还会调起系统滚轮。这个功能在所有主流浏览器上跑了很多年。

那天晚上我在 GitHub Trending 上刷到 DietrichGebert/ponytail,README 第一屏的例子和我白天遇到的事一模一样。它的描述只有一句:让你的 AI agent 像屋里最懒的那个资深工程师一样思考。

127.5k stars,6.8k forks,当日新增 2.8k stars,MIT 协议。我花了两天把它的技能文本、hooks 实现和基准测试报告翻完,写下来的是我看到的东西。


一、它想解决的问题:agent 的” 过度建造” 是结构性的

先说清楚我为什么觉得这个问题值得单独做一个项目。

AI agent 写出臃肿代码,不是模型不聪明。恰恰相反,它太想表现得聪明。

训练语料里的高分回答长什么样?分小标题、给多个方案、考虑扩展性、附带错误处理和类型注解、结尾还有一段” 进一步优化的方向”。这套结构在 Stack Overflow 的高赞回答里是对的,在教程文章里是对的,在面试题解里也是对的。于是模型学会了:详尽 = 优质。

问题是,写业务代码的时候,详尽 = 债务。

多出来的每一个抽象层,都是半年后有人要读懂的东西。那些” 为将来预留” 的参数,从来没人传过,但每次改动都要绕开。新加的依赖带来一条供应链、一次 CVE、一份升级工作。

而 agent 的工作方式让这件事更严重了。人类工程师写多了会累,累了就会偷懒,偷懒往往就写对了。Agent 不累。你让它加个日期选择器,它可以毫不迟疑地把四个文件全部写完,中间不打一个哈欠。产出速度快到你来不及在 review 环节拦住。

ponytail 的作者 DietrichGebert 给这个问题起了个具体的形象:公司里那个扎马尾、戴椭圆眼镜、在职时间比版本控制系统还长的资深工程师。你拿五十行代码去找他,他看一眼,什么都不说,替你换成一行。

README 里那句 tagline 我很喜欢:他什么也不说。他写一行。它能跑。

项目干的事情,就是把这个人塞进你的 agent 里。

三个月,从建库到 12.7 万 star

翻了一下仓库信息:建库时间 2026 年 6 月 12 日,最近一次 push 是 9 月 4 日。三个月不到,127.5k stars、6.8k forks。Trendshift 的日榜、周榜、月榜(JavaScript 分类)三块徽章挂在 README 顶上。

这个速度在”agent 技能” 这个品类里不算孤例,但 ponytail 的位置有点特别。

过去一年 GitHub 上冒出来的 agent 相关项目,大部分在做加法:给 agent 加记忆、加工具、加子 agent 编排、加检索、加多角色讨论。方向都是” 让 agent 能做更多事”。

ponytail 是少数几个在做减法的。它不给 agent 加任何能力,它给 agent 加一道闸门。

仓库的 topics 也把这个定位标得很清楚:agent-skillsai-agentsclaude-code-plugincursor-rulesprompt-engineering,最后一个是 yagni。整个仓库 2.3 MB,核心资产是几份 Markdown。

它爆得快,我猜是因为它戳中的那个痛点,恰好是所有人在同一时间遇到的:2026 年这一年,大量的人从” 偶尔让 AI 补全一段代码” 切换到了” 让 agent 独立跑完一个工单”。工作单元一变大,过度建造这件事就从” 多写两行” 变成了” 多写四个文件”,从审美问题变成了维护成本问题。


二、核心机制:一道七级阶梯

ponytail 的全部核心,是一道在写代码之前必须爬的阶梯。Agent 停在第一个成立的台阶上:

1
2
3
4
5
6
7
1. 这东西需要存在吗?        → 不需要:跳过(YAGNI)
2. 这个代码库里已经有了吗? → 有:复用,别重写
3. 标准库能做吗? → 能:用它
4. 平台原生特性覆盖吗? → 覆盖:用它
5. 已装的依赖能解决吗? → 能:用它
6. 能一行写完吗? → 能:一行
7. 到这一步才轮到:写能跑的最少代码

七个台阶,从” 根本不写” 一路降级到” 写最少”。

我第一眼觉得这就是把 YAGNI 换了个说法。真正让我改主意的是第二级和第四级。

第二级” 代码库里已经有了吗” 是我实际吃亏最多的地方。Agent 有个顽固毛病:它读了任务,扫了几个相关文件,然后开始写一个 formatCurrency。而你的 utils/format.ts 里躺着一个一模一样的,只是名字叫 toMoney。技能文本里对这条的措辞很直接:先看再写,把隔壁几个文件里已经有的东西重新实现一遍,是最常见的那种垃圾。

第四级” 平台原生特性” 是收益最大的一级。前端这十年往浏览器里塞了太多东西,而 agent 的知识分布明显偏向 npm 生态。<dialog> 元素自带焦点陷阱、Escape 关闭、::backdrop 遮罩和默认可访问性,2022 年起全平台可用。但你让 agent 加个确认弹窗,它十有八九给你 npm install @radix-ui/react-dialog

仓库的 examples/ 目录里逐个拆了这些对照。弹窗那个例子:Radix 版本一个依赖三十行,原生 <dialog> 版本零依赖八行。作者在结尾写了一句我认可的判断:那个库在解决一个平台已经解决了的问题。

examples/ 里那些具体的对照

这个目录有十一个文件,每个都是” 没装技能的写法” 对” 装了技能的写法”。挑三个我觉得最有代表性的。

解析 URL 查询串。 没装技能的版本第一步是 npm install query-string,一个 4.5 kB gzip、每周 350 万下载的包:

1
2
import qs from "query-string";
const params = qs.parse(location.search);

装了技能之后:

1
2
3
4
// ponytail: URLSearchParams does this
const params = new URLSearchParams(location.search);
params.get("page"); // "2"
params.getAll("tags"); // ["js", "css"]

URLSearchParams 在每个浏览器里都有,Node.js 从 v10 起也有。编码、重复键、迭代它全处理。那个包是给一个已经全平台落地多年的 API 写的 polyfill。

按 key 给数组分组。 常见写法是装 lodash 用 groupBy,或者手搓一个 reduce。技能给的是:

1
2
// ponytail: Object.groupBy does this
const byStatus = Object.groupBy(orders, order => order.status);

Object.groupBy 在 Chrome 117、Firefox 119、Safari 17.4、Node.js 21 落地。这个例子结尾还有一句我特别认可的话:如果你要支持 IE11 或者老 Node,那个 reduce 一行版才是对的选择,而不是 lodash。它没有把” 用原生” 变成教条,而是让你先看目标运行时。

给搜索框加防抖。 这个例子是从基准测试的原始输出里逐字摘的(Haiku 4.5,temperature 1,benchmarks/output.json),所以能看到无技能那一版的真实形态:116 行。它先给了一个基础版的 debounce 函数加用法,然后给了一个” 增强版,带加载状态”,再往下是错误处理、请求取消、结果渲染。三个逐级递进的版本,全部写在同一个回答里。

用户要的是一个防抖。它给了一份防抖教程。

这就是我在开头说的那个结构性问题的样子:模型不是不会写防抖,它是在按” 高分回答” 的格式交付。

阶梯之上还有一条铁律

技能文本里反复强调的一点,是阶梯运行的时机:

阶梯在你理解问题之后运行,不是代替理解。读任务,读它触及的代码,把真实调用链走一遍,然后再爬。

这句话决定了 ponytail 和” 叫模型少写点” 之间的区别。它对解决方案懒,对读代码不懒。

配套还有一条修 bug 的规则,我觉得比阶梯本身更值钱:

Bug 修复等于修根因,不是修症状。工单描述的是症状。动手改之前,grep 出你要动的那个函数的每一个调用方,在共享函数里修一次。在共享函数里加一个 guard,比在每个调用方各加一个 guard 的 diff 更小;而只修工单点名的那条路径,会留下一堆兄弟调用方仍然是坏的。

把” 最小 diff” 和” 修根因” 统一起来,这个论证我第一次见。通常这两件事被当成矛盾的:修根因意味着动公共代码,风险大改动多。ponytail 的说法是,如果你把所有调用方都算进去,修根因的总 diff 反而更小。

那份规则清单

阶梯之外还有一组具体的禁令,我照着自己的项目对了一遍,条条命中:

  • 不要没人要求的抽象:不要只有一个实现的 interface,不要只生产一种产品的 factory,不要给一个永远不变的值配一个 config。
  • 不要 boilerplate,不要” 为以后” 搭的脚手架。以后可以自己给自己搭。
  • 删优先于加。无聊优先于聪明,聪明是那种半夜三点有人要解码的东西。
  • 文件数越少越好。最短的能跑的 diff 赢,但前提是你已经理解了问题。放错位置的最小改动不是懒,是第二个 bug。
  • 需求复杂?先交懒版本,在同一个回答里质疑它:「做了 X;Y 能覆盖。真需要完整的 X 就说一声。」不要为了一个可以默认掉的答案停下来等。
  • 两个标准库方案体积一样?选边界情况正确的那个。懒是少写代码,不是挑更脆的算法。

第一条那三个例子(单实现 interface、单产品 factory、常量 config)挑得很准。这三样是我见过的 agent 生成代码里出现频率最高的三种空转抽象,而且它们都长着” 良好工程实践” 的脸。

倒数第二条我尤其喜欢。它解决的是”agent 因为不确定而停下来问你” 这个消耗。很多时候 agent 卡住不是因为问题真的有歧义,是因为它想显得谨慎。这条规则给了它一个默认动作:先做简单版本,把疑问附在后面。

输出格式也有规矩

技能里有一节专门管输出:

代码在前。然后最多三行短句:跳过了什么,什么时候再加。不要长篇大论,不要功能巡礼,不要设计笔记。如果解释比代码还长,删掉解释,每一段为简化辩护的文字都是把复杂度用散文的形式偷运回来。

模式是固定的:[代码] → 跳过了:[X],等 [Y] 的时候再加。

“每一段为简化辩护的文字都是把复杂度用散文的形式偷运回来”,这个观察挺锋利。你让 agent 写少一点,它会写同样少的代码,外加三段解释为什么这样就够了。总的阅读负担没降。

同一节里还留了个口子:你明确要求的解释(一份报告、一次讲解、分阶段的说明)不算债务,要给就给全。规则只针对没人要的散文。


三、三档强度和六个命令

技能不是一个开关,分三档:

档位 行为
lite 按你要求的建,但用一行点出更懒的那个方案,你来选
full 阶梯强制执行。标准库和原生优先,最短 diff,最短解释。默认档
ultra YAGNI 极端派。删优先于加。先甩出一行版本,同一口气里质疑剩下的需求

README 用同一个需求演示了三档的差别。需求是” 给这些 API 响应加个缓存”:

  • lite:「加好了。顺带一提,functools.lru_cache 一行就能覆盖,如果你不想自己维护一个缓存类的话。」
  • full:「在 fetch 函数上加了 @lru_cache(maxsize=1000)。跳过了自定义缓存类,等 lru_cache 明显不够用了再加。」
  • ultra:「在 profiler 说话之前不加缓存。真要加的时候:@lru_cache。手写的 TTL 缓存类是一个带命中率的 bug 农场。」

三档的差别不在措辞强度,在于谁做决定。lite 把选择权交回给你,full 替你选懒的那个并告诉你跳过了什么,ultra 连需求本身一起质疑。

README 里对 ultra 的说明是:当这个代码库让你个人感到被冒犯的时候,用它。

命令有六个:

命令 作用
/ponytail [lite|full|ultra|off] 切档位,或关掉。不带参数就报告当前档位
/ponytail-review 审当前 diff 的过度设计,交回一份删除清单
/ponytail-audit 审整个仓库,不只是 diff
/ponytail-debt 把你欠下的 ponytail: 快捷方式收成一份账本
/ponytail-gain 显示基准测试的收益记分板
/ponytail-help 命令速查

/ponytail-review 是我用得最多的。它的输出不是一份改进建议,是一份删除清单,这个定位差别很大。普通的 code review 技能会告诉你” 这里可以加个错误处理”,它告诉你” 这三个文件可以删,功能不受影响”。

/ponytail-debt 对应的是技能里一条不起眼但很实在的约定。当 agent 有意抄了一条有已知天花板的近路(全局锁、O (n²) 扫描、拍脑袋的启发式),它要留一个 ponytail: 注释,写清天花板在哪、怎么升级:

1
# ponytail: 全局锁,吞吐量成为瓶颈时改成按账户分锁

然后 /ponytail-debt 把仓库里所有这类注释收集成一份账本。这条设计解决的是” 以后再说” 变成” 永远不说” 的老毛病。TODO 注释谁都会写,但没人有那份把它们捞回来的清单。


四、基准测试:这是它和一句提示词的分水岭

看到这里你大概会想:这不就是一段提示词吗,我自己在 CLAUDE.md 里写一句” 遵循 YAGNI,优先一行方案”,效果能差多少。

这个问题不是我提的,是 issue #126 里 Colin Eberhardt 提的。作者的回应方式,是把整个基准测试推翻重做了一遍。

先说旧基准错在哪

ponytail 最早那版数据宣称” 代码量减少 80% 到 94%”。Colin 的批评有四条,每条都成立:

  1. 单次补全不是 coding agent 的真实用法。真实工作是 agent 在真实代码库上跑很多轮。
  2. 基线是一个裸的、话痨的模型。它输出散文、免责声明、多个备选方案,所以” 回答的行数” 数的是评论不是代码。这会把基线撑大,反过来抬高技能的成绩。
  3. “偏好一行方案” 可能拿安全性换行数。如果纪律是” 少写”,它会不会把输入校验和错误处理一起砍了?
  4. 一句短提示词说不定就能干同样的活。

作者在报告里直接写:这四条都合理,这次的基准是照着能推翻 ponytail 的目标搭的,不是照着讨好它。

新基准怎么搭

旧(单次) 新(agentic)
工作单元 一个 prompt 一次补全 一次真实的 headless Claude Code 会话
基线 裸 API 模型 同一个 Claude Code agent,不加技能
任务 “给我写个 X” 真实仓库上的真实工单
计代码量 整个回答含评论 落地文件的 git diff 新增行
对照组 ponytail vs 裸模型 基线・ponytail・caveman・Colin 自己那句提示词
安全性 没测 测了:产出的代码拿对抗性输入实际跑

跑的仓库是 tiangolo/full-stack-fastapi-template,一个真实的 FastAPI + React 项目,锁在 cd83fc1 这个 commit 上,MIT,任何人都能复现。模型是 Haiku 4.5,每个(任务,对照组)跑 4 次,每次一份全新的仓库副本和全新的 agent 上下文。

四个对照组里,caveman 是个很聪明的设计:它是一个只让模型说话简短、不改变建造行为的技能。如果 ponytail 的效果只是” 话少了”,caveman 应该能追平它。

作者自曝的那个污染 bug

报告里有一段我认为比数据本身更有价值。

早期的 agentic 跑分显示 ponytail 和基线只差 4%,作者差点就这么发出去了。后来发现结果是错的:ponytail 和 caveman 都是 Claude Code 插件,会触发 SessionStart hook,而那个 hook 在每一个对照组上都触发了,包括基线。基线组在偷偷跑 ponytail。

修法是给每个对照组做隔离:用 --setting-sources project,local 排掉用户的全局插件,再用 --plugin-dir 精确加载唯一一个插件。

作者写这段的理由是:这正是那种会让基准测试说谎的错误,而找到它,就是相信后面那些数字的理由。

我很少看到有人在自己项目的 README 里写这种东西。它比任何一行数据都更能说明这份报告的可信度。

数据

12 个功能任务,相对于无技能基线(基线绝对值:每任务 191 行、349k tokens、$0.097、69 秒):

对照组 代码量 tokens 成本 耗时
caveman(话少对照) −20% +7% +3% +2%
ponytail −54% −22% −20% −27%
yagni-oneliner(那句提示词) −33% −14% −21% −30%

拆到单个任务,前端这边的差距集中在几个特定位置:

工单 基线 caveman ponytail 那句提示词
日期选择器 404 202 23 162
颜色选择器 287 188 23 25
文件拖拽区 251 226 95 175
多步向导 571 492 312 406
星级评分 103 95 70 101
命令面板 268 260 233 285

后端六个 CRUD 工单(归档、搜索、导出 CSV、批量删除、复制、计数),四个对照组的数字几乎贴在一起,差距在个位数行。

三个我认为重要的读法:

大幅收益全部来自” 平台原生特性替代自建组件”。 日期选择器 −94%,颜色选择器 −92%,拖拽区 −62%。基线手搓组件,ponytail 伸手去拿 <input type="date"><input type="color"><input type="file">。而且这次的基线是真实的 Claude Code,不是话痨的裸模型,所以这个差距不再是” 基线在写散文” 的假象。

在不可压缩的代码上,各组收敛。 后端 CRUD 和命令面板,四组数字接近。ponytail 稍微修一点,从不膨胀,但它也没有在没水分的地方凭空造出节省。一份诚实的基准必须把这部分展示出来,这份报告展示了。

那句七个词的提示词,表现是飘的。 颜色选择器上它很漂亮(25 行),但日期选择器上 162 行,多步向导 406 行,命令面板 285 行高于基线的 268 行。这就是对第四条批评的回答:短提示词有时候管用有时候不管用,技能每次都管用。

顺带一提成本:日期选择器那个任务,ponytail 花了约 $0.06 / 49 秒,基线约 $0.15 / 88 秒。行数少了,token 就少了。


五、懒和敷衍的分界线在哪

第三条批评(少写会不会砍掉安全防护)单独占了一个测试轴,这是我最关心的部分。

六个” 手术式” 任务,每个任务给一个起手文件,要求实现一个函数。安全要求是隐含的,就像真实工单那样写。评分器随后拿对抗性输入实际执行产出的函数:路径穿越、SQL 注入、伪造 token、畸形 CSV 行、耗尽配额的客户端。每个任务的” 坏” 参照物,是那种 happy path 正确、对抗路径不安全的偷懒版本。

5 个安全任务 × 4 次 = 每组 20 次运行:

对照组 安全率 关键位置的行数
基线 100%(20/20) -
caveman 100%(20/20) -
ponytail 100%(20/20) safe-path 9.5 行
yagni-oneliner 95%(19/20) safe-path 6 行

整个论点浓缩在一个任务里。safe-path 要求把一个不可信的文件名拼到基准目录上:

  • 那句提示词写出了最少的行数(6 行),四次里有一次不安全,一个 ../../ 文件名逃出了目录。
  • ponytail 写了约 9.5 行,四次全安全。

ponytail 多留的那三行,就是路径穿越校验。

“少写” 这条指令,在没有判断力的时候,砍掉的是防护。ponytail 的规则(永远不要把信任边界上的输入校验简化掉)把它留住了。

技能文本里那份” 不许懒” 的清单写得很具体:

不许懒的地方:理解问题(读全、走通真实流程,再挑台阶。一个你不理解的小 diff 只是伪装成效率的懒惰)、信任边界上的输入校验、防止数据丢失的错误处理、安全、可访问性、真实硬件需要的标定、以及任何你明确要求的东西。

最后那条硬件标定我没想到会出现在这里:

硬件永远不是纸面上的理想值。真实的时钟会漂,真实的传感器会偏,PCA9685 会快跑几个百分点。留下那个校准旋钮,而不只是更少的代码,物理世界需要的调节是一个极简模型看不见的。

作者八成是被真实的舵机坑过。

还有一条我准备直接抄进自己项目的:

没有配套检查的懒代码是未完成的。非平凡逻辑(一个分支、一个循环、一个解析器、一条涉及金额或安全的路径)要留下一个可运行的检查,即那个逻辑坏掉时最先失败的最小东西:一个基于 assert 的自检,或者一个小测试文件。不要框架,不要 fixture。平凡的一行代码不需要测试,YAGNI 对测试同样适用。

一个检查,不是一套测试。这个量级把”agent 顺手生成 200 行永远没人看的测试” 这个问题堵住了。


六、怎么跨 20 个宿主落地

技术上 ponytail 没有什么复杂的东西,但它的分发做得很扎实。

核心资产是纯文本:skills/ 下六个 SKILL.md,加一份仓库根目录的 AGENTS.md。规则本身不到 40 行。

难的是让这段文本在 20 个 agent 宿主里都能持续生效。仓库根目录那一长串点开头的目录就是干这个的:

1
2
3
4
.claude-plugin/   .codex-plugin/   .devin-plugin/   .grok-plugin/
.cursor/ .windsurf/ .clinerules/ .kiro/
.qoder/ .openclaw/ .opencode/ .agents/
AGENTS.md gemini-extension.json

每个生态一套自己的规则文件格式,作者一个个适配过去。scripts/check-rule-copies.js 负责在 CI 里检查这些副本有没有互相跑偏,改了规则文本忘了同步,测试直接失败。

分发上分成两个层级:

指令层(instruction-only):Cursor、Windsurf、Cline、Copilot Chat、Aider、Kiro、Zed 这些,复制一个规则文件进去就行。拿到的是常驻规则,拿不到档位切换和 hooks。

插件层(plugin):Claude Code、Codex、Copilot CLI、OpenCode、Gemini CLI、Pi、Hermes、Grok Build、Devin CLI、OpenClaw、Swival、Qoder 这些,装插件,多拿两个 Node.js 生命周期 hook。

hooks 干的事情很小:

  • ponytail-activate.js:会话开始时激活默认档位
  • ponytail-instructions.js:每一轮 prompt 都把规则文本重新注入一次
  • ponytail-mode-tracker.js:跟踪当前档位
  • ponytail-subagent.js:往子 agent 里注入规则
  • ponytail-statusline.sh / .ps1:状态栏显示当前档位

第二个 hook 是关键。每一轮都重新注入,对抗的是长会话里的规则漂移。技能文本开头那句”ACTIVE EVERY RESPONSE. No drift back to over-building”,靠的就是这个机制而不是模型的自觉。跑过长会话的都知道,二十轮之后系统提示词里的约束会变得很淡。

子 agent 注入那条也想得比较细。默认往每一个通过 Agent 工具派生的子 agent 里注规则,但可以用 PONYTAIL_SUBAGENT_MATCHER 环境变量传一个正则来限定范围。比如你不想让只读的搜索型子 agent 也背上这套规则,设成 ^general$ 就只注入通用型的。正则不锚定、大小写不敏感,写错了或者宿主没报告子 agent 类型,都退回到” 全部注入”。

默认档位有两种设法:PONYTAIL_DEFAULT_MODE 环境变量,或者 ~/.config/ponytail/config.json 里的 defaultMode 字段。Windows 上路径是 %APPDATA%\ponytail\config.json。不设就是 full

卸载这块作者也考虑到了。插件目录之外,ponytail 会写三处状态:档位标记、~/.config/ponytail/config.json、以及(如果你接受了那个安装提示)~/.claude/settings.json 里的一个 statusLine 条目。node scripts/uninstall.js 负责清掉这些。README 特意标了一句:先跑这个脚本,再执行宿主的卸载命令。因为脚本本身就是插件文件之一,先卸插件就把脚本一起删了。

这个细节挺能说明作者的状态:他真的自己走过一遍完整的卸载流程。


七、快速上手

Claude Code 装法是两条命令,README 特意注明要分两次发送

1
/plugin marketplace add DietrichGebert/ponytail
1
/plugin install ponytail@ponytail

Codex:

1
2
codex plugin marketplace add DietrichGebert/ponytail
codex plugin add ponytail@ponytail

装完跑 codex,进 /hooks 审查并信任那两个生命周期 hook,然后开一个新线程。

GitHub Copilot CLI:

1
2
copilot plugin marketplace add DietrichGebert/ponytail
copilot plugin install ponytail@ponytail

Copilot CLI 会按插件名给命令加命名空间,所以调用长这样:/ponytail:ponytail ultra/ponytail:ponytail-review

OpenCode 改 opencode.json

1
{ "plugin": ["@dietrichgebert/ponytail"] }

Gemini CLI:

1
gemini extensions install https://github.com/DietrichGebert/ponytail

Google 正在把 Gemini CLI 改名成 Antigravity CLI(agy 二进制),同一个扩展也能装过去,复用的是仓库里那份 gemini-extension.json

Cursor / Windsurf / Cline / Kiro 这些,从仓库里复制对应的规则文件到你的项目:

1
2
3
4
# Cursor
cp .cursor/rules/ponytail.mdc ~/projects/yourapp/.cursor/rules/
# Kiro 全局
cp .kiro/steering/ponytail.md ~/.kiro/steering/

还有一条零配置路径:很多 agent(Codex 的 VS Code 扩展、Amp、Jules、CodeWhale、Swival、Qoder)会自动读项目根目录的 AGENTS.md。这个仓库根目录就有一份,直接复制到你的项目根目录,什么都不用装。想全局生效就放 ~/.codex/AGENTS.md

一个前置条件:Claude Code 和 Codex 的插件要跑两个 Node.js hook,node 得在 PATH 上。用 Nix 或 nvm 的注意,得在非交互 shell 的 PATH 上。不在也不会炸,只是常驻激活那部分静默失效,技能本身还能用。

装完验证一下:

1
/ponytail

不带参数会报告当前档位。然后随便找个最近的 diff 跑:

1
/ponytail-review

我在自己一个跑了三个月的小项目上跑了 /ponytail-audit,它给我列出来的第一条是一个只有一个实现类的 interface,第二条是一个包了三层但每层都只是转发的 service。两条都对。


八、总结

ponytail 的形态是一段不到 40 行的规则文本,这个体量让人容易低估它。我看完之后觉得它有三个地方做对了:

它约束的是建造行为,不是说话方式。 FAQ 里作者主动划清了和 caveman 的边界:caveman 压缩 agent 说的话,ponytail 压缩 agent 建的东西。两者不重叠,caveman 让代码一个字节不变,ponytail 不碰散文。基准测试里 caveman 那一列证实了这个划分:话少下来了,代码只降 20%,token 反而涨了 7%。

它把” 懒” 和” 敷衍” 的边界写死了。 七级阶梯负责压缩解决方案,那份” 不许懒” 的清单负责守住底线:理解问题、信任边界校验、防数据丢失的错误处理、安全、可访问性、硬件标定。安全轴的数据说明这条边界是有效的,而少了它的那个对照组,四次里掉了一次。

它有一份能推翻自己的基准测试。 换掉话痨基线,加上” 只让说话简短” 的对照组,把批评者自己的提示词也拉进来当一列,自曝一个差点让结果反转的污染 bug,最后在 Limitations 里承认只跑了 Haiku 一个模型、n=4、安全测试只是地板不是证明。这份报告的可信度,来自作者试图证伪自己的那部分。

适合谁

每天用 agent 写业务代码的人。 收益最直接。你不需要在每次对话里手打” 别过度设计”,也不用眼睁睁看着 agent 在长会话第二十轮忘了这回事。

做前端的人。 收益上限最高。基准里的大幅下降全部集中在平台原生特性能替代自建组件的场景,而这类场景在前端最密集。

维护老项目的人。 /ponytail-audit/ponytail-review 可以当独立工具用,不必开常驻模式。

带团队的人。 那份 AGENTS.md 可以直接进你的仓库,配合 /ponytail-review 当 PR 前的自查步骤。

不适合谁

在写框架或者公共库的人。 你的抽象是产品的一部分,” 只有一个实现的 interface” 在库设计里经常是对的。ultra 档会跟你打架,建议用 lite 或者干脆关掉。

需求本身就要求可扩展性的场景。 技能文本里写了” 任何你明确要求的东西” 不在简化范围内,所以你只要说清楚就行。但如果你的项目大部分需求都长这样,常驻这个技能的收益会被反复的解释成本吃掉。

推理型模型的用户要注意成本方向。 README 里作者点了一句:降低成本和延迟只是” 跟着阶梯走” 的模型上的副作用,一个花思考 token 去逐级斟酌台阶的简洁推理模型可能反着来,在 GPT-5.5 上就是这样。

局限

基准测试只跑了 Haiku 4.5 一个模型。作者自己在 Limitations 里承认,更大的模型可能会收窄这个差距(它们需要的手把手更少),也可能拉大。harness 支持 Sonnet 和 Opus,作者出于成本停在了 Haiku。

安全测试是地板不是证明。六个手术式任务、确定性检查,说明的是” 某个对照组会不会掉一个已知的防护”,不是” 代码是安全的”。

n=4,前端代码量的方差不小(手搓一个组件在 300 到 570 行之间浮动),均值稳定但不紧。

还有 219 个 open issues。项目今年六月才建库,九月初还在密集提交,生态适配面铺得很开(20 个宿主),这个 issue 数量在我看来是正常的活跃度,不是失修的信号。但如果你用的是那些冷门宿主,做好偶尔踩坑的准备。


最后回到我那个日期选择器。

装上 ponytail 之后我重新问了一遍同样的问题。这次它给我的是:

1
2
<!-- ponytail: browser has one -->
<input type="date">

外加两行说明:跳过了 flatpickr 和包装组件;需要日期范围选择的时候再加。

咖啡还没凉。

项目地址:https://github.com/DietrichGebert/ponytail