DietrichGebert/ponytail · v4.7.0 · 13 agents · MIT

让 AI agent 拥有
懒人 senior 的判断力

一个 SessionStart + UserPromptSubmit 双 Hook 注入的行为约束层——在模型每次开口写代码之前,逼它先过一遍「这真的需要写吗」的六步决策梯。结果:80-94% 更少代码、3-6× 更快、47-77% 更便宜。

22.3k
Stars
80-94%
代码削减
3-6×
提速
47-77%
降本
13
平台

01 · 核心哲学

「懒」不是缺陷,是方法论

Ponytail 解决的是一个所有大模型都有的系统性偏差:模型倾向于过度构建——你要一个日期选择器,它装 flatpickr、套组件、加样式表、讨论时区。你说「加缓存」,它写一个带 TTL 的类。

「你认识他。长马尾,椭圆眼镜。在公司待得比版本控制还久。你给他看 50 行代码;他看了一眼,什么也不说,用一行替换掉。」

Ponytail 把这个「沉默 senior」的思维模式编码成一套可执行的行为约束——不是系统 prompt,不是 few-shot 示例,而是一套在每次代码生成前都必须走的决策梯,以及每行代码产出后「什么可以跳过、什么时候该加回来」的标注规范。

它的核心洞察是:模型在「想清楚不写」和「写出来」之间的决策成本不对称——写出来容易,停下来难。Ponytail 用 Hook 把「停下来」的门槛变低,把「写之前先想」变成默认行为。

关键设计选择:不做模型微调,不改提示词措辞,用生命周期 Hook 注入强制约束行为——模型每次响应前都重新激活 Ponytail,不会随对话轮次漂移回过构建模式。

02 · 决策梯

Stop at the first rung that holds

每写一行代码之前,先问自己这六个问题,能在第几步停就在第几步停。

这个梯子和 YAGNI / DRY / SOLID 一样是老生常谈,但模型从来不会自发地执行它——它们看到需求就开工,因为「完成」比「判断」更容易被评估。Ponytail 把这个梯子变成每次响应的第一个 token 之前必须经过的强制路径。

①
这真的需要存在吗? YAGNI —— 推测性需求直接跳过,一句话说明原因。
②
标准库能搞定吗? —— Python functools.lru_cache、JS Intl.DateTimeFormat、Go regexp.MustCompile……
③
原生平台特性能覆盖吗? <input type="date"> 替代整个日期选择器库。
→ 跳过依赖
④
已经装好的依赖能解决吗? —— 用已安装包的 API 而非新装一个。
⑤
能写成一行吗? 一行搞定不用两行。
⑥
只有这时候:写能工作的最小代码。
标注规范 ponytail:当故意走捷径时,必须用注释标注。「// ponytail: global lock, per-account locks if throughput matters」——把天花板和升级路径写进注释,让接手的人(或者下一个 agent)知道这个简化是有意的、已知局限的、有方向的。

强度分级对应不同的「梯子严格程度」:

模式行为
lite建完要建的,但告诉用户「其实一行就行」——用户决定是否回退
full梯子强制执行。Stdlib 和原生优先。最短可工作代码。默认。
ultraYAGNI 原教旨主义。先发一行,再质疑其余需求。删除优于添加。
review独立模式。用于 code review 场景——只看别人的代码,标注在哪里可以懒。

03 · 注入机制

SessionStart × UserPromptSubmit:双生命周期钩子

Ponytail 用两个生命周期事件在不同时间点注入约束——SessionStart 保证「启动即激活」,UserPromptSubmit 追踪用户对模式的实时调整。

🚀
SessionStart Hook

匹配 startup | resume | clear | compact 时触发。激活 Ponytail 模式,将 skill 内容注入当前会话上下文。Node.js ponytail-activate.js 负责把 skill body 拼接进系统 prompt。

💬
UserPromptSubmit Hook

每次用户提交 prompt 时触发。ponytail-mode-tracker.js 解析 /ponytail lite|full|ultra|off 命令和「stop ponytail」/「normal mode」关键词,更新持久化状态。

hooks/hooks.json — 声明式 Hook 配置
// 支持 Claude Code · Codex · Copilot,三平台共用同一 hooks.json
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup|resume|clear|compact",
        "hooks": [
          {
            "type": "command",
            "command": "command -v node >/dev/null 2>&1 && node \"${CLAUDE_PLUGIN_ROOT}/hooks/ponytail-activate.js\" || exit 0",
            "commandWindows": "if (Get-Command node ...) { node ... }",
            "timeout": 5,
            "statusMessage": "Loading ponytail mode..."
          }
        ]
      }
    ],
    "UserPromptSubmit": [...]
  }
}

三平台兼容的关键是同一个 hooks.json——Ponytail 在 .claude-plugin/、.codex-plugin/、.opencode/ 各有一份副本,平台启动时加载自己的。跨平台时行为完全一致,不存在「在 Claude Code 里用得好在 Codex 里降级」的问题。

Windows 支持同样精心打磨——Shell 命令用 POSIX command -v 检测 Node 存在性,Windows 端用 Get-Command node -ErrorAction SilentlyContinue 做等效检测,逻辑完全等价。


04 · 状态持久化

三平台同一状态文件:跨厂商的 Mode 追踪

ponytail-runtime.js 是 Ponytail 的状态引擎——在 Claude Code、Codex、Copilot 三套完全不同的插件 API 之间,它用同一份 ~/.claude/.ponytail-active 状态文件实现模式跨会话持久化。

hooks/ponytail-runtime.js — 跨平台状态传递
const STATE_FILE = '.ponytail-active';
const isCopilot = Boolean(process.env.COPILOT_PLUGIN_DATA);
const isCodex     = Boolean(process.env.PLUGIN_DATA);

// Claude Code → ~/.claude/  Codex → $PLUGIN_DATA  Copilot → $COPILOT_PLUGIN_DATA
let stateDir = getClaudeDir();
if (isCodex)     stateDir = process.env.PLUGIN_DATA;
if (isCopilot) stateDir = process.env.COPILOT_PLUGIN_DATA;

function writeHookOutput(event, mode, context) {
  if (isCopilot) {
    // Copilot: additionalContext on SessionStart
    process.stdout.write(JSON.stringify({ additionalContext: context }));
    return;
  }
  if (isCodex) {
    // Codex: systemMessage prefix + hookSpecificOutput
    process.stdout.write(JSON.stringify({
      systemMessage: `PONYTAIL:${mode.toUpperCase()}`,
      hookSpecificOutput: { hookEventName: event, additionalContext: context }
    }));
    return;
  }
  // Claude Code: plain text output
  process.stdout.write(context);
}

这是一个被严重低估的设计细节——三个插件 API 的 Hook 输出格式完全不同:

  • Claude Code:直接写 stdout 文本,追加到上下文中
  • Codex:JSON systemMessage 前缀 + hookSpecificOutput 结构化字段
  • Copilot:只读 additionalContext,其他字段全部忽略

同一套代码通过 process.env 探测平台,自动切换输出形状——这让用户换平台时零迁移成本,mode 状态完整携带。

hooks/ponytail-config.js — 模式解析优先级链
// 优先级:环境变量 → 配置文件 → 默认值
function getDefaultMode() {
  // 1. PONYTAIL_DEFAULT_MODE 环境变量
  if (envMode && VALID_MODES.includes(envMode)) return envMode;
  // 2. XDG_CONFIG_HOME/ponytail/config.json 或平台默认值
  const config = JSON.parse(fs.readFileSync(configPath));
  // 3. 'full'
  return DEFAULT_MODE;  // = 'full'
}

05 · 强度体系

off · lite · full · ultra · review

Ponytail 不是非开即关的二值开关——它有五个模式,对应不同的「懒度」,用户可以随时切换,模式名本身就是命令。

off — Ponytail 关闭,模型正常响应
lite — 建完要的,提示用户有更懒方案
full — 强制梯子执行,默认
ultra — YAGNI 原教旨,删除优于添加
review — 代码评审模式,标注别人的过度工程

模式切换通过两种途径:

  • 用户命令:/ponytail lite、/ponytail full、/ponytail ultra、/ponytail off、/ponytail-review
  • 关键词检测:「stop ponytail」或「normal mode」自动关闭
  • 环境变量:PONYTAIL_DEFAULT_MODE=ultra 覆盖配置文件

切换后状态写入 .ponytail-active 文件,下次 SessionStart 时恢复该模式——不会因会话重启而丢失。

Skill body 模式过滤:ponytail-instructions.js 的 filterSkillBodyForMode() 在注入前按模式过滤 SKILL.md 内容——lite/full/ultra 在同一个文件里通过表格行和示例标签做分支,同一个文件支持三个强度的内容,按需注入不同文本。review 模式是独立 skill(/ponytail-review),因为它的行为与其他三个完全不同。

06 · 技能矩阵

不只是主技能:五个配套 Skill

Ponytail 不只是一个行为规范——它是一个以「懒」为核心的技能矩阵,五个 Skill 覆盖开发全生命周期。

🎯
ponytail

主技能——六步决策梯 + 三级强度。激活即生效,/ponytail lite|full|ultra 切换。

🔍
ponytail-help

「这能懒吗?」——给定一个需求,告诉用户用平台特性 / stdlib / 一行替代的方案。

📋
ponytail-debt

标记代码中的「懒债」——哪些 ponytail: 注释存在但还没补升级路径。

✅
ponytail-audit

审计模式——扫描项目中所有 ponytail: 注释,生成「技术债 → 升级路径」清单。

🧐
ponytail-review

Code review 技能——review 别人的 PR 或代码片段,标注在哪里可以更懒,提供升级建议。

sources/ponytail/skills/ ├── ponytail/ SKILL.md — 主技能,模式过滤表 ├── ponytail-help/ 「这能懒吗」 ├── ponytail-debt/ 懒债扫描 ├── ponytail-audit/ 全项目审计 └── ponytail-review/ 评审模式

这五个技能组合覆盖了一个完整生命周期:

开发时 ponytail 强制约束 → ponytail-help 实时查替代方案 → Review 时 ponytail-review 挑过度工程 → 合并后 ponytail-audit 建懒债清单 → ponytail-debt 跟踪偿还进度。


07 · Benchmark

有数字的懒:Promptfoo 实证

不是营销语言,是可复现的 Promptfoo benchmark。三个模型(Haiku / Sonnet / Opus),五个日常任务,十次中位数。

指标ArmHaikuSonnetOpus
代码行数baseline518693256
caveman11612067
ponytail394451
API 成本(5 任务)baseline$0.032$0.141$0.135
ponytail$0.010$0.032$0.071
延迟(秒)baseline37.7124.158.7
ponytail9.920.118.0

↑ ponytail vs baseline 代码削减幅度

Haiku
−92%
Sonnet
−94%
Opus
−80%
正确性门控:benchmark 的 correctness.js 提取 fenced code block 并执行真实检查(email/debounce/CSV 用 Node/Python 跑,React/FastAPI 用结构化正则)。一个得分好看但跑不通的代码会被标记为 FAIL——「少代码」不等于「错代码」。

任务:email validator、JS debounce、CSV sum、React countdown timer、FastAPI rate limiter。都是典型「模型会过度工程化」的场景。

结果在所有模型、所有任务上稳定胜出——不是一个巧合,是「强制决策梯」这一简单约束在概率上的必然胜利。


08 · 生态覆盖

13 个 Agent,一个 Ponytail

不绑定任何一家厂商——通过插件化架构,Ponytail 同时覆盖 Claude Code、Codex、Copilot、OpenClaw、OpenCode、Pi、Windsurf 等 13 个平台。

Claude Code
Codex
Copilot CLI
OpenClaw
OpenCode
Pi Agent
Windsurf
Cody
Gemini (ext)
Kiro
Cursor
Continue
...more

支持方式分三类:

  • 官方插件目录:Claude Code /plugin marketplace add、Codex codex plugin marketplace add、Copilot copilot plugin marketplace add
  • OpenClaw / OpenCode / Pi:通过 .openclaw/、.opencode/、pi-extension/ 适配层集成
  • 其他:Cody / Windsurf / Continue / Gemini extension 通过通用 skill 格式或 API 挂载
ponytail/ ├── .claude-plugin/ Claude Code 官方插件格式 ├── .codex-plugin/ Codex 插件格式 ├── .opencode/ OpenCode 适配 ├── .openclaw/ OpenClaw 适配 ├── pi-extension/ Pi Agent 扩展 ├── .windsurf/ Windsurf 集成 └── .cursor/ Cursor 集成

09 · 生态意义

在「能力」和「克制」之间搭桥

Ponytail 的出现回答了一个大模型应用里被忽视的问题:模型的能力上限不是问题,模型的过度建设惯性才是。

2025-2026 年,所有主流 coding agent 都经历了同样的轨迹:能力越来越强,输出越来越长,过度工程化越来越严重。用户想要一个「过滤重复字符串」,模型装了 lodash。用户要「当前时间」,模型写了 moment.js 初始化。这不是模型笨——这是强化学习训练信号里「完成任务」和「写得多」高度相关的历史包袱。

Ponytail 的解法优雅在于:它不修改模型,不微调,不改 API,只改输入时的约束。通过 Hook 在「模型准备开口」和「模型真的开口」之间插入一个 gate——六步决策梯。这个 gate 成本极低(几毫秒的 Node.js),效果可测量(promptfoo benchmark),适用范围极广(13 个平台)。

它还带来了一个意外的工程价值:ponytail: 注释本身就是一份「已知技术债 → 升级路径」的元数据。传统技术债的问题是「未来某天会有人想起来修」,ponytail-debt 把这个问题变成了「升级路径已经在代码里」,reviewer 可以直接看到哪些简化是有意的、哪些是历史遗留。

对 coding agent 生态来说,Ponytail 是一个行为规范层面的基础设施——不是又一个 code agent,而是一种让所有 code agent 变得更高效的通用约束协议。

「写之前先问:需要写吗?标准库能搞定吗?平台特性覆盖了吗?已装依赖能解决吗?一行够吗?只有最后才写。」