🎀 ponytail
适用于 14 种 agent · MIT 许可
Ponytail

Ponytail

他什么都不说。他只写一行。它能跑。

一个常驻在 AI agent 里的精简 skill,让它在动手写代码之前先停在最小可用之处。信任边界的校验、错误处理、安全与无障碍 —— 永远不砍。

⭐ 7.4k stars v2.6.0 14 agents MIT
−54%
代码量
−20%
成本
−27%
耗时
100%
安全性
404 行 → 23 行· 287 行 → 23 行· Haiku 4.5· Sonnet· Opus· n=4· tiangolo/full-stack-fastapi-template·
404 行 → 23 行· 287 行 → 23 行· Haiku 4.5· Sonnet· Opus· n=4· tiangolo/full-stack-fastapi-template·
// 你认识他

长马尾,椭圆眼镜。在公司待的时间比版本控制系统还久。 你给他看五十行代码;他看一眼,什么都不说,删到只剩一行。

Ponytail 把他请进了你的 AI agent。代码量减少约 54%,最高处可达 94%——这正是真正存在过度构建陷阱的地方(日期选择器、拾色器)。在代码本身已经极简的任务上则几乎为零。

规则从来不是"越少 token 越好"。规则是:只写任务真正需要的代码,绝不砍掉校验、错误处理、安全性和无障碍。让代码变小的原因是"够用",而不是"打高尔夫球"。

// 用前 / 用后

一个日期选择器的故事

无 ponytail · DatePicker.tsx
404 行
// "我先装个 flatpickr 吧"
import flatpickr from "flatpickr";
import "flatpickr/dist/flatpickr.min.css";

export function DatePicker(props) {
  const [date, setDate] = useState(props.value);
  const ref = useRef<HTMLInputElement>(null);
  const fp = useRef<flatpickr.Instance>(null);

  useEffect(() => {
    if (!ref.current) return;
    fp.current = flatpickr(ref.current, {
      mode: "single",
      enableTime: props.withTime,
      time_24hr: props.time24 ?? true,
      minDate: props.min,
      maxDate: props.max,
      locale: "zh",
      onChange: (sel) => setDate(sel[0]),
    });
    return () => fp.current?.destroy();
  }, [props.min, props.max, props.withTime, props.time24]);

  return (
    <div className="datepicker-wrapper">
      <input ref={ref} defaultValue={date?.toISOString()} />
    </div>
  );
}

// 样式、时区讨论、Disabled 状态、...
// 另外 387 行...
ponytail · index.html
23 行
<!-- ponytail: 浏览器自带一个 -->
<input type="date" />

{/* 没了。零依赖。
    无障碍:原生控件,自带键盘 + 屏幕阅读器。
    安全:浏览器接管格式化,避开手写解析。
    时区:用户本地时区,永远不会出现 UTC 偏移。 */}

// 同样的原则也适用于拾色器:
<input type="color" />

// 287 行 → 23 行。

更多"幸存者"见 examples/

// 数据

最诚实的衡量

让一个真实的 agent 做真实的工作:一个无头的 Claude Code 会话,去改 tiangolo 的 full-stack-fastapi-template (一个真实的 FastAPI + React 仓库),按它留下的 git diff 评分。

每条分支相对于无 skill 基线的百分比
vs 无 skill 基线 LOC tokens cost time safe
ponytail −54% −22% −20% −27% 100%
caveman(精简措辞对照组) −20% +7% +3% +2% 100%
"YAGNI + 一行流"提示 −33% −14% −21% −30% 95%

ponytail 是唯一在所有指标上都下降的那一臂,也是唯一同时保持满分安全性的那一臂。 完整方法与每个任务的明细表 →

// 工作原理

阶梯

动手写代码之前,agent 停在第一档能撑住的地方。

01
YAGNI
这东西真的需要存在吗?

不需要 → 跳过。别为"将来可能用到"的东西埋桩。

02
标准库
标准库能做?

用它。永远比引入依赖更轻、更稳、更可移植。

03
平台原生
平台原生功能能做?

用它。<input type="date"><input type="color"><dialog>,浏览器已经替你写好了。

04
已安装依赖
已安装的依赖能做?

用它。先翻翻 package.json,别急着 npm i

05
一行能搞定?
一行能搞定?

一行。可读性比行数重要 —— 但当二者重合时,行数赢。

06
都不行?
再写最小可用的实现

只有走到这里才写代码。校验、错误处理、安全、无障碍,永远不在砍掉之列。

懒,但不是疏忽。信任边界的校验、丢数据的处理、安全、无障碍,这些永远不在砍掉之列。

// 安装

ponytail 让你付出的最大努力

也就这样。一次命令,针对你的 agent。

在 Claude Code 里跑两条命令即可。桌面端应用没有 /plugin 命令,改从界面里装:Customize → 个人插件旁的 + → Create plugin and add marketplace → Add from repository。

终端
$ /plugin marketplace add DietrichGebert/ponytail
$ /plugin install ponytail@ponytail

Claude Code 与 Codex 的插件会跑两个极小的 Node.js 生命周期钩子,所以 node 需要在你的 PATH 上(Nix / nvm 用户注意:必须在非交互式 shell 的 PATH 里)。如果没有,skills 仍能工作,常驻激活只是保持静默。

// Commands

一小撮命令

每个会话常驻启用,并附带下面这一小撮斜杠命令。

Command 做什么
/ponytail [lite | full | ultra | off] 设置强度,或关掉。不带参数则报告当前等级。
/ponytail-review 审查当前 diff 中的过度构建,返还一份待删清单。
/ponytail-audit 审计整个仓库的过度构建,不只是 diff。
/ponytail-debt 把之前 deferred 的 ponytail: 捷径收进账本,让"稍后"不会变成"永不"。
/ponytail-gain 展示来自基准的实测影响榜(更少代码、更低成本、更高速度)。
/ponytail-help 上面这些命令的速查。

命令需要支持 skill 的宿主(Claude Code、Codex、OpenCode、Gemini、pi)。在 Codex 中它们就是 skills,用 @ 调用(@ponytail-review)。

// FAQ

常见问题

需要配置文件吗? +
不需要。可以放一个可选的 ~/.config/ponytail/config.json,或设 PONYTAIL_DEFAULT_MODE 环境变量来设定默认等级(lite / full / ultra / off),但都不是必填的。默认为 full
我真就要那个 120 行的缓存类呢? +
不需要。你非要的话,他也会写。慢慢地。准确地。一边看着你。
它能扩展吗? +
你没写出来的代码可以无限扩展。零 bug、零 CVE、100% uptime,亘古不变。
为什么叫 "ponytail"? +
你懂的。
怎么衡量"过度构建"?它会变懒吗? +
基准里有个独立的安全对照组,ponytail 在那里仍拿到 100%。规则从来不是"越少 token 越好" —— 规则是"只写任务真正需要的代码"。让代码变小的原因是"够用",而不是"打高尔夫球"。
🎀

他会感到欣慰。

但他不会说。

启动和切换模式的提示文本会显示当前模式。装一个,他就在你每次会话里悄悄地把多余的代码删掉。