Compare commits

...
Author SHA1 Message Date
EmerikoandClaude Opus 4.8 426ffd0b36 docs: sweep em dashes out of the active published surface
Em dashes crept back into examples, docs/platform-native.md, several READMEs,
the ponytail-debt skill, and a command file since 88431de. Replaced with plain
punctuation (commas, matching the house convention), .openclaw mirror
regenerated. Follows 88431de's scope: leaves untouched the vendored caveman
SKILL.md and the dated benchmarks/results/ writeups (historical records).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 02:35:48 +02:00
DietrichGebertandClaude Opus 4.8 bd6176a9b3 fix: drop em dashes from ponytail-audit/review scope wording (#163) (#179)
#163 clarified the audit/review Boundaries scope but reintroduced em dashes,
which this repo deliberately purged (commit 88431de "replace em dashes with
plain punctuation across prose"). Keeps the clearer wording, swaps the em dash
for a period. .openclaw mirrors regenerated; suite green.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 02:20:55 +02:00
DietrichGebertandClaude Opus 4.8 cf9cbd531e Revert #82 (Modern Web Guidance rung-3) and re-sync mirrors (#178)
#82 added a "Web tasks: rung 3 lookup" section to the always-on ponytail
SKILL.md, about an external `modern-web` CLI most users won't have installed.
It's optional bloat in the always-on ruleset, and it broke CI by leaving the
.openclaw mirror stale.

Reverts the section from skills/ponytail/SKILL.md, the README callout, and
examples/web-platform-lookup.md, then regenerates the .openclaw mirror and
removes the Spanish callout that #174 had mirrored. Suite green (56/56).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 02:09:30 +02:00
DietrichGebertandClaude Opus 4.8 10a375b836 fix: regenerate .openclaw ponytail skill mirror (fixes red CI) (#177)
#82 added the "Web tasks: rung 3 lookup" section to skills/ponytail/SKILL.md
but did not run scripts/build-openclaw-skills.js, so the committed
.openclaw/skills/ponytail/SKILL.md mirror drifted from its source. The two
generator-sync tests in tests/openclaw-skills.test.js have failed on main
since that merge. Regenerated the mirror; suite is green again.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 02:01:16 +02:00
DietrichGebertandClaude Opus 4.8 f316f14d96 chore(ponytail-mcp): mark private, drop dead bin (#176)
ponytail-mcp reuses the repo's hooks/ via createRequire("../hooks/..."), which
reaches outside the package dir, so it can only run from a checkout (as its
README says), never as a published npm package — a publish tarball wouldn't
include ../hooks/ and would crash. The `bin` field and missing `private` made
it look publishable. Mark it private so an accidental `npm publish` can't ship
a broken package, and drop the dead bin (you point the host at ponytail-mcp/index.js).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 01:57:02 +02:00
DietrichGebert 48cdf05a25 Revert "benchmarks: add system prompt to baseline arm so it doesn't ramble (closes #126) (#128)" (#175)
This reverts commit 37f46b8f02.
2026-06-19 01:44:13 +02:00
DietrichGebertandClaude Opus 4.8 a4e5e479d6 docs: re-sync README.es.md to current English (agentic numbers, CodeWhale) (#174)
The Spanish README merged (#110) carrying stale content: the old flat
"80-94% menos código" single-shot headline (the exact claim #126 corrected),
no CodeWhale section, badge stuck at 13 agents, and missing the Modern Web
Guidance callout (#82) and the Claude Code desktop-install paragraph.

Re-translates the hero + Números section to the corrected agentic numbers
(~54%, up to 94%, 100% safe) with the old figures demoted to the same
<details> block English uses, adds CodeWhale, fixes the badge, and adds a
"community translation, English is the reference" note. Also adds a minimal
Español discoverability link to the English README so readers can find it.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 00:44:08 +02:00
27 changed files with 98 additions and 143 deletions
+1 -1
View File
@@ -32,6 +32,6 @@ End with `net: -<N> lines, -<M> deps possible.` Nothing to cut: `Lean already. S
## Boundaries
Scope: over-engineering and complexity only. Correctness bugs, security holes,
and performance are explicitly out of scope — route them to a normal review
and performance are explicitly out of scope. Route them to a normal review
pass. Lists findings, applies nothing. One-shot.
"stop ponytail-audit" or "normal mode" to revert.
+1 -1
View File
@@ -23,7 +23,7 @@ the convention out of the ledger.
One row per marker, grouped by file:
`<file>:<line> <what was simplified>. ceiling: <the limit named>. upgrade: <the trigger to revisit>.`
`<file>:<line>, <what was simplified>. ceiling: <the limit named>. upgrade: <the trigger to revisit>.`
The convention is `ponytail: <ceiling>, <upgrade path>`, so pull the ceiling
and the trigger straight from the comment. Want an owner per row too? add
+1 -1
View File
@@ -45,7 +45,7 @@ If there is nothing to cut, say `Lean already. Ship.` and stop.
## Boundaries
Scope: over-engineering and complexity only. Correctness bugs, security holes,
and performance are explicitly out of scope — route them to a normal review
and performance are explicitly out of scope. Route them to a normal review
pass, not this one. A single smoke test or `assert`-based
self-check is the ponytail minimum, not bloat, never flag it for deletion.
Does not apply the fixes, only lists them.
+1 -1
View File
@@ -2,4 +2,4 @@
description: "Harvest ponytail: comments into a tracked debt ledger"
---
Harvest every `ponytail:` comment in this repository into a debt ledger so deferrals do not rot into 'later means never'. Grep the whole tree for comment markers (grep -rnE '(#|//) ?ponytail:' ., skipping node_modules/.git/build output). One row per marker, grouped by file: <file>:<line> <what was simplified>. ceiling: <the limit named in the comment>. upgrade: <the trigger to revisit>. Tag any marker that names no upgrade path or trigger as no-trigger, those rot silently. End with the count of markers and how many lack a trigger. If none: 'No ponytail: debt. Clean ledger.' Report only, change nothing.
Harvest every `ponytail:` comment in this repository into a debt ledger so deferrals do not rot into 'later means never'. Grep the whole tree for comment markers (grep -rnE '(#|//) ?ponytail:' ., skipping node_modules/.git/build output). One row per marker, grouped by file: <file>:<line>, <what was simplified>. ceiling: <the limit named in the comment>. upgrade: <the trigger to revisit>. Tag any marker that names no upgrade path or trigger as no-trigger, those rot silently. End with the count of markers and how many lack a trigger. If none: 'No ponytail: debt. Clean ledger.' Report only, change nothing.
+39 -8
View File
@@ -14,13 +14,17 @@
<p align="center">
<img src="https://img.shields.io/github/stars/DietrichGebert/ponytail?style=flat-square&color=111111&label=stars" alt="Stars">
<img src="https://img.shields.io/github/v/release/DietrichGebert/ponytail?style=flat-square&color=111111&label=release" alt="Release">
<img src="https://img.shields.io/badge/funciona%20con-13%20agentes-111111?style=flat-square" alt="Works with 13 agents">
<img src="https://img.shields.io/badge/funciona%20con-14%20agentes-111111?style=flat-square" alt="Works with 14 agents">
<img src="https://img.shields.io/badge/licencia-MIT-111111?style=flat-square" alt="MIT license">
</p>
<p align="center">
<strong>80-94% menos código &middot; 3-6&times; más rápido &middot; 47-77% más barato</strong><br>
<sub>Mediana de 10 ejecuciones con Haiku, Sonnet y Opus. <a href="benchmarks/">Reprodúcelo tú mismo.</a></sub>
<strong>~54% menos código (hasta 94%) &middot; ~20% más barato &middot; ~27% más rápido &middot; 100% seguro</strong><br>
<sub>Medido en sesiones reales de Claude Code editando un repo open-source real (FastAPI + React), contra el mismo agente sin skill. ~54% es el promedio de 12 tareas de feature (Haiku 4.5, n=4); llega al 94% cuando un agente sobre-construye (un selector de fechas) y es casi cero cuando el código ya es mínimo. ponytail mantiene cada guarda de seguridad, mientras que un prompt pelado de "escribe one-liners" se salta una. (El benchmark anterior de un solo disparo reportaba 80-94% como cifra plana; contra un baseline agéntico justo, ese es el techo por tarea, no el promedio.) <a href="benchmarks/results/2026-06-18-agentic.md">Reporte completo</a> &middot; <a href="benchmarks/">reprodúcelo</a>.</sub>
</p>
<p align="center">
<sub>Traducción de la comunidad. La versión de referencia y más reciente es el <a href="README.md">README en inglés</a>.</sub>
</p>
---
@@ -44,13 +48,34 @@ Más sobrevivientes en [examples/](examples/).
## Números
Cinco tareas del día a día (validador de email, debounce, suma de CSV, temporizador, rate limiter), tres modelos, tres variantes: sin skill, el skill [caveman](https://github.com/JuliusBrussee/caveman), y ponytail. Diez ejecuciones por celda, mediana reportada.
La medición honesta es un agente real haciendo trabajo real: una sesión headless de Claude Code editando [el template full-stack-fastapi de tiangolo](https://github.com/fastapi/full-stack-fastapi-template) (un repo real de FastAPI + React), evaluada sobre el `git diff` que deja. Doce tickets de feature, el mismo agente con y sin el skill, n=4, Haiku 4.5.
<p align="center">
<img src="assets/benchmark-3model.svg" width="860" alt="Mediana de líneas de código por variante en Haiku, Sonnet y Opus; ponytail escribe 80-94% menos código que el baseline sin skill">
<img src="assets/benchmark-agentic.svg" width="860" alt="Cada variante como porcentaje del baseline sin skill en LOC, tokens, costo y tiempo (Haiku 4.5). ponytail es el más bajo en cada métrica (LOC 46%, tokens 78%, costo 80%, tiempo 73%); caveman sube por encima del 100% en tokens, costo y tiempo; yagni-oneliner LOC 67%. Seguridad, tier adversarial aparte: baseline, caveman y ponytail 100%, yagni-oneliner 95%.">
</p>
**80-94% menos código, 47-77% menos costo, y 3-6× más rápido que un agente sin skill, en todos los modelos.** Cada atajo que toma ponytail queda marcado en el código con un comentario `ponytail:` que nombra la ruta de actualización. Reprodúcelo: `npx promptfoo eval -c benchmarks/promptfooconfig.yaml`. Método y números completos: [benchmarks/](benchmarks/). Tareas de nivel producción, donde un agente sin restricciones se infla mucho más, están documentadas en [benchmarks/results/](benchmarks/results/).
| vs baseline sin skill | LOC | tokens | costo | tiempo | seguro |
|---|--:|--:|--:|--:|--:|
| **ponytail** | **-54%** | **-22%** | **-20%** | **-27%** | **100%** |
| caveman (control de prosa concisa) | -20% | +7% | +3% | +2% | 100% |
| prompt "YAGNI + one-liners" | -33% | -14% | -21% | -30% | 95% |
ponytail es la única variante que recorta cada métrica, y la única que se mantiene totalmente segura al hacerlo. El recorte es mayor donde hay una trampa real de sobre-construcción (selector de fechas de 404 a 23 líneas, selector de color de 287 a 23, porque usa un `<input>` nativo en vez de un componente) y casi cero en código que ya es mínimo. Método completo, tablas por tarea y limitaciones: [benchmarks/results/2026-06-18-agentic.md](benchmarks/results/2026-06-18-agentic.md).
<details>
<summary><strong>Números anteriores de un solo disparo (generación aislada)</strong></summary>
Cinco tareas del día a día, tres modelos, tres variantes (sin skill, [caveman](https://github.com/JuliusBrussee/caveman), ponytail), diez ejecuciones, mediana reportada. Un prompt, una completación, contando las líneas de la respuesta:
<p align="center">
<img src="assets/benchmark-3model.svg" width="860" alt="Mediana de líneas de código por variante en Haiku, Sonnet y Opus">
</p>
Esto mostraba **80-94% menos código**. [#126](https://github.com/DietrichGebert/ponytail/issues/126) señaló con razón que el baseline del modelo pelado infla su respuesta con prosa y opciones, así que esa diferencia es en parte un artefacto del baseline conversacional. Los números agénticos de arriba son la versión corregida y defendible. Reproduce la corrida de un solo disparo con `npx promptfoo eval -c benchmarks/promptfooconfig.yaml`.
</details>
**La regla nunca fue "menos tokens."** Es: escribe solo lo que la tarea necesita, y nunca recortes validación, manejo de errores, seguridad ni accesibilidad. El código termina pequeño porque es necesario, no por golf. El menor costo y latencia son un efecto secundario en los modelos que siguen la escalera; un modelo de razonamiento conciso que gasta tokens de pensamiento deliberando los peldaños puede ir al revés (en GPT-5.5 lo hace).
## Cómo funciona
@@ -71,7 +96,7 @@ Flojo, no negligente: la validación en límites de confianza, el manejo de pér
El mayor esfuerzo que ponytail te va a pedir:
Los plugins de Claude Code y Codex ejecutan dos pequeños lifecycle hooks de Node.js, así que `node` debe estar en tu PATH (nota para usuarios de Nix/nvm: debe estar en el PATH del shell no-interactivo). Si no lo está, los skills igualmente funcionan la activación automática simplemente queda en silencio en vez de lanzar un error en cada prompt.
Los plugins de Claude Code y Codex ejecutan dos pequeños lifecycle hooks de Node.js, así que `node` debe estar en tu PATH (nota para usuarios de Nix/nvm: debe estar en el PATH del shell no-interactivo). Si no lo está, los skills igualmente funcionan, la activación automática simplemente queda en silencio en vez de lanzar un error en cada prompt.
### Claude Code
@@ -80,6 +105,8 @@ Los plugins de Claude Code y Codex ejecutan dos pequeños lifecycle hooks de Nod
/plugin install ponytail@ponytail
```
La app de escritorio no tiene el comando `/plugin`. Instálala desde la interfaz: Customize, el + junto a los plugins personales, Create plugin and add marketplace, Add from repository, y luego ingresa la URL del repo (gracias @NiklasDHahn, #98).
### Codex
```bash
@@ -148,6 +175,10 @@ agy plugin install https://github.com/DietrichGebert/ponytail
Reutiliza el `gemini-extension.json` de este repo. Una diferencia: Antigravity convierte los comandos `/ponytail` en skills, así que los escribes en el chat (por ejemplo `/ponytail-review` como mensaje) en vez de seleccionarlos de un menú slash. Hasta que la migración se complete (alrededor del 18 de junio de 2026), `gemini extensions install` también funciona. Para usarlo como regla permanente, coloca el ruleset en `.agents/rules/`.
### CodeWhale
Lee `AGENTS.md` desde la raíz del proyecto, sin configuración. Copia [`AGENTS.md`](AGENTS.md) a tu proyecto, o ejecuta `codewhale` desde un checkout de este repo. Eso es todo.
### OpenClaw
```bash
@@ -193,7 +224,7 @@ node scripts/check-rule-copies.js
npm test
```
El paquete de skills de OpenClaw (`.openclaw/skills/`) se genera desde `skills/`; ejecuta `node scripts/build-openclaw-skills.js` después de cambiar un skill la suite de tests falla si está desactualizado.
El paquete de skills de OpenClaw (`.openclaw/skills/`) se genera desde `skills/`; ejecuta `node scripts/build-openclaw-skills.js` después de cambiar un skill, la suite de tests falla si está desactualizado.
El benchmark de correctness lanza Python para las verificaciones de email y CSV; se prueba `python3` antes que `python`. Las verificaciones de CSV requieren `pandas` instalado localmente.
+5 -3
View File
@@ -23,6 +23,10 @@
<sub>Measured on real Claude Code sessions editing a real open-source repo (FastAPI + React), against the same agent with no skill. ~54% is the mean across 12 feature tasks (Haiku 4.5, n=4); it reaches 94% where an agent over-builds (a date picker) and is near zero where the code is already minimal. ponytail keeps every safety guard while a bare "write one-liners" prompt drops one. (The earlier single-shot benchmark reported 80-94% as a flat figure; against a fair agentic baseline that is the per-task ceiling, not the average.) <a href="benchmarks/results/2026-06-18-agentic.md">Full writeup</a> &middot; <a href="benchmarks/">reproduce it</a>.</sub>
</p>
<p align="center">
<sub><a href="README.es.md">Español</a></sub>
</p>
---
You know him. Long ponytail. Oval glasses. Has been at the company longer than the version control. You show him fifty lines; he looks at them, says nothing, and replaces them with one.
@@ -42,8 +46,6 @@ With ponytail:
More survivors in [examples/](examples/).
> **Pairs well with** [Modern Web Guidance](https://github.com/GoogleChrome/modern-web-guidance) for web work: ponytail decides *whether* to lean on the platform, MWG is how the agent looks up *which* native feature does the job. See [examples/web-platform-lookup.md](examples/web-platform-lookup.md).
## Numbers
The honest measurement is a real agent doing real work: a headless Claude Code session editing [tiangolo's full-stack-fastapi-template](https://github.com/fastapi/full-stack-fastapi-template) (a real FastAPI + React repo), scored on the `git diff` it leaves behind. Twelve feature tickets, the same agent with and without the skill, n=4, Haiku 4.5.
@@ -179,7 +181,7 @@ It reuses this repo's `gemini-extension.json`. One difference: Antigravity conve
### CodeWhale
Reads `AGENTS.md` from the project root zero setup. Copy [`AGENTS.md`](AGENTS.md) to your project, or run `codewhale` from a checkout of this repo. That's it.
Reads `AGENTS.md` from the project root, zero setup. Copy [`AGENTS.md`](AGENTS.md) to your project, or run `codewhale` from a checkout of this repo. That's it.
### OpenClaw
+1 -1
View File
@@ -6,7 +6,7 @@ Three arms (no skill, [caveman](https://github.com/JuliusBrussee/caveman), ponyt
### Claude (Haiku / Sonnet / Opus)
Requires an Anthropic API key and **Node.js ≥ 22.22.0** (promptfoo's engine constraint
Requires an Anthropic API key and **Node.js ≥ 22.22.0** (promptfoo's engine constraint,
check with `node --version` and upgrade if needed):
```bash
+2 -2
View File
@@ -92,11 +92,11 @@ python judge.py --run runs/<stamp> # score every workspace's source
Fewer lines only counts as a win if the code still does the job. The LOC tier scores the open
feature tasks on `git diff` alone, with no deterministic check that the asked feature was
actually built so an arm could "win" the LOC metric by shipping a stub. This pass closes that
actually built, so an arm could "win" the LOC metric by shipping a stub. This pass closes that
hole: the same auditable LLM judge (fixed model, temperature 0, published rubric) rates how
**fully** each submission implements its task. Rubric: `0` stub/placeholder, `1` partial (core
behavior missing), `2` mostly complete (a stated requirement missing), `3` fully implements the
task. Read it **alongside** the LOC table a low-LOC arm whose completeness also drops is doing
task. Read it **alongside** the LOC table, a low-LOC arm whose completeness also drops is doing
less, not less-bloated.
Validated like the over-engineering judge: `--selftest` requires the judge to rank a complete
+2 -6
View File
@@ -1,6 +1,2 @@
// Baseline arm: no skill, with a one-line system prompt so the model doesn't ramble.
const system = 'Provide just one example for any given task, and no commentary or usage examples.';
module.exports = ({ vars }) => [
{ role: 'system', content: system },
{ role: 'user', content: vars.task },
];
// Baseline arm: no skill, just the task.
module.exports = ({ vars }) => [{ role: 'user', content: vars.task }];
+8 -8
View File
@@ -60,7 +60,7 @@ Libraries people install that the runtime already ships.
| `query-string` / `qs` | `new URLSearchParams(location.search)` |
| `lodash.clonedeep` | `structuredClone(obj)` |
| `lodash.groupby` | `Object.groupBy(arr, fn)` |
| `lodash.debounce` | see debounce one-liner below |
| `lodash.debounce` | see debounce one-liner below |
| `numeral` / `accounting` | `new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" })` |
| `date-fns` format | `new Intl.DateTimeFormat("en-US", { dateStyle: "long" }).format(date)` |
| `date-fns` relative time | `new Intl.RelativeTimeFormat("en", { numeric: "auto" }).format(-3, "day")` |
@@ -97,7 +97,7 @@ Packages that wrap Node built-ins.
| `make-dir` | `fs.mkdirSync(path, { recursive: true })` |
| `slash` (win paths) | `path.posix` or `path.normalize()` |
| `uuid` (v4) | `crypto.randomUUID()` |
| `ms` (parse duration strings) | keep `ms`, it's genuinely useful and tiny |
| `ms` (parse duration strings) | keep `ms`, it's genuinely useful and tiny |
| `is-stream` | `val instanceof stream.Readable` |
| `object-assign` | `Object.assign()` / spread |
| `array-uniq` | `[...new Set(arr)]` |
@@ -119,12 +119,12 @@ Packages that wrap what Python already ships.
| `python-dateutil` (basic parsing) | `datetime.fromisoformat()` (Python 3.7+) |
| `pytz` | `zoneinfo.ZoneInfo("America/New_York")` (Python 3.9+) |
| `attrs` (simple data classes) | `@dataclass` |
| `six` | drop it, Python 2 is gone |
| `six` | drop it, Python 2 is gone |
| `pathlib2` | `pathlib.Path` (built-in since Python 3.4) |
| `enum34` | `enum.Enum` (built-in since Python 3.4) |
| `typing_extensions` (common types) | `from __future__ import annotations` + built-in generics |
| `simplejson` (basic use) | `json` (stdlib) |
| `requests` (simple GET) | `urllib.request.urlopen(url)` `requests` for anything real |
| `requests` (simple GET) | `urllib.request.urlopen(url)`, `requests` for anything real |
| `click` (single command) | `argparse` (stdlib) |
| `mergedeep` | `dict \| other_dict` (Python 3.9+) |
| `more-itertools` (basic) | `itertools` (stdlib): `chain`, `islice`, `groupby`, `product` |
@@ -150,9 +150,9 @@ Things the application layer implements that the database already does.
| JSON storage + query | `jsonb` (Postgres) / `JSON_EXTRACT` (SQLite/MySQL) |
| UUID generation | `gen_random_uuid()` (Postgres) / `UUID()` (MySQL) |
| Timestamps on insert/update | `DEFAULT now()` + trigger or `ON UPDATE CURRENT_TIMESTAMP` |
| Enforce uniqueness | `UNIQUE` constraint not application-level checks |
| Enforce referential integrity | `FOREIGN KEY` not application-level checks |
| Enforce value ranges | `CHECK (price > 0)` not application-level validation |
| Enforce uniqueness | `UNIQUE` constraint, not application-level checks |
| Enforce referential integrity | `FOREIGN KEY`, not application-level checks |
| Enforce value ranges | `CHECK (price > 0)`, not application-level validation |
---
@@ -170,4 +170,4 @@ You debug the wrapper.
Skip the wrapper. The platform ships with your app for free.
When the native solution is genuinely insufficient (old browser support, edge cases it doesn't handle, ergonomics that matter at scale), the library earns its place. Install it then not before.
When the native solution is genuinely insufficient (old browser support, edge cases it doesn't handle, ergonomics that matter at scale), the library earns its place. Install it then, not before.
+1 -1
View File
@@ -1,6 +1,6 @@
# Examples
Real model output, verbatim from benchmark runs the same task answered by the same model
Real model output, verbatim from benchmark runs, the same task answered by the same model
with no skill (`## Without Ponytail`) and with ponytail (`## With Ponytail`), so you can
compare side by side. Model: Claude Haiku 4.5, temperature 1, source `benchmarks/output.json`.
+5 -5
View File
@@ -2,9 +2,9 @@
**Task:** "Write Python code that reads sales.csv and sums the 'amount' column."
Verbatim model output from a benchmark run Claude Haiku 4.5, no-skill arm vs ponytail arm, temperature 1, source `benchmarks/output.json`. Reproduce: `npx promptfoo@latest eval -c benchmarks/promptfooconfig.yaml`.
Verbatim model output from a benchmark run, Claude Haiku 4.5, no-skill arm vs ponytail arm, temperature 1, source `benchmarks/output.json`. Reproduce: `npx promptfoo@latest eval -c benchmarks/promptfooconfig.yaml`.
## Without Ponytail 20 lines of code
## Without Ponytail, 20 lines of code
# Python code to read sales.csv and sum the 'amount' column
@@ -57,7 +57,7 @@ except KeyError:
- Provides built-in error handling
- Efficient for large files
## With Ponytail 3 lines of code
## With Ponytail, 3 lines of code
```python
import csv
@@ -66,6 +66,6 @@ total = sum(float(row['amount']) for row in csv.DictReader(open('sales.csv')))
print(total)
```
Skipped: pandas, error handling, file closing add when the CSV is large, malformed, or you need more analysis.
Skipped: pandas, error handling, file closing, add when the CSV is large, malformed, or you need more analysis.
**20 → 3 lines of code** same model, same prompt.
**20 → 3 lines of code**, same model, same prompt.
+5 -5
View File
@@ -2,9 +2,9 @@
**Task:** "Add debounce to a search input in vanilla JavaScript. It currently fires an API call on every keystroke."
Verbatim model output from a benchmark run Claude Haiku 4.5, no-skill arm vs ponytail arm, temperature 1, source `benchmarks/output.json`. Reproduce: `npx promptfoo@latest eval -c benchmarks/promptfooconfig.yaml`.
Verbatim model output from a benchmark run, Claude Haiku 4.5, no-skill arm vs ponytail arm, temperature 1, source `benchmarks/output.json`. Reproduce: `npx promptfoo@latest eval -c benchmarks/promptfooconfig.yaml`.
## Without Ponytail 116 lines of code
## Without Ponytail, 116 lines of code
# Debounce Search Input
@@ -190,7 +190,7 @@ searchInput.addEventListener('blur', () => {
**Typical keystroke reduction:** 10 characters = 10 API calls → 1-2 API calls with debounce ✅
## With Ponytail 10 lines of code
## With Ponytail, 10 lines of code
```javascript
const searchInput = document.querySelector('input[type="search"]');
@@ -206,6 +206,6 @@ searchInput.addEventListener('input', (e) => {
});
```
Skipped: debounce utility function, class wrapper, config object `setTimeout` + `clearTimeout` is the debounce. Add a utility when you need it on 3+ inputs.
Skipped: debounce utility function, class wrapper, config object, `setTimeout` + `clearTimeout` is the debounce. Add a utility when you need it on 3+ inputs.
**116 → 10 lines of code** same model, same prompt.
**116 → 10 lines of code**, same model, same prompt.
+1 -1
View File
@@ -28,4 +28,4 @@ const copy = JSON.parse(JSON.stringify(original));
const copy = structuredClone(original);
```
**1 dependency (or a fragile hack) → 1 built-in.** `structuredClone` handles `Date`, `Map`, `Set`, `ArrayBuffer`, `RegExp`, circular references, and more everything `JSON.parse/stringify` silently drops. Available in every browser since 2022 and Node.js since v17. Pull lodash in when you need the rest of it, not for one function.
**1 dependency (or a fragile hack) → 1 built-in.** `structuredClone` handles `Date`, `Map`, `Set`, `ArrayBuffer`, `RegExp`, circular references, and more, everything `JSON.parse/stringify` silently drops. Available in every browser since 2022 and Node.js since v17. Pull lodash in when you need the rest of it, not for one function.
+5 -5
View File
@@ -2,9 +2,9 @@
**Task:** "Write me a Python function that validates email addresses."
Verbatim model output from a benchmark run Claude Haiku 4.5, no-skill arm vs ponytail arm, temperature 1, source `benchmarks/output.json`. Reproduce: `npx promptfoo@latest eval -c benchmarks/promptfooconfig.yaml`.
Verbatim model output from a benchmark run, Claude Haiku 4.5, no-skill arm vs ponytail arm, temperature 1, source `benchmarks/output.json`. Reproduce: `npx promptfoo@latest eval -c benchmarks/promptfooconfig.yaml`.
## Without Ponytail 75 lines of code
## Without Ponytail, 75 lines of code
# Email Validation Function
@@ -142,7 +142,7 @@ if __name__ == "__main__":
**Recommendation**: Use the `email-validator` library for production applications, as it properly handles RFC 5321/5322 standards. Use the simple regex version for quick scripts or when you want to avoid dependencies.
## With Ponytail 3 lines of code
## With Ponytail, 3 lines of code
```python
import re
@@ -151,6 +151,6 @@ def is_valid_email(email: str) -> bool:
return bool(re.match(r'^[^@]+@[^@]+\.[^@]+$', email))
```
Skipped: RFC 5322 parser, DNS MX lookup, confirmation email. Add when you actually need to reject `user+tag@sub.domain.co.uk` or catch typos until then, this catches 99% of "oops I fat-fingered it" cases.
Skipped: RFC 5322 parser, DNS MX lookup, confirmation email. Add when you actually need to reject `user+tag@sub.domain.co.uk` or catch typos, until then, this catches 99% of "oops I fat-fingered it" cases.
**75 → 3 lines of code** same model, same prompt.
**75 → 3 lines of code**, same model, same prompt.
+1 -1
View File
@@ -32,4 +32,4 @@ const byStatus = Object.groupBy(orders, order => order.status);
// → { pending: [...], shipped: [...], delivered: [...] }
```
**1 dependency (or a reduce) → 1 built-in.** `Object.groupBy` shipped in Chrome 117, Firefox 119, Safari 17.4, Node.js 21. If you need a `Map` instead of a plain object: `Map.groupBy(orders, o => o.status)`. Check your target runtime; if you need IE11 or old Node, the `reduce` one-liner is still the right call not lodash.
**1 dependency (or a reduce) → 1 built-in.** `Object.groupBy` shipped in Chrome 117, Firefox 119, Safari 17.4, Node.js 21. If you need a `Map` instead of a plain object: `Map.groupBy(orders, o => o.status)`. Check your target runtime; if you need IE11 or old Node, the `reduce` one-liner is still the right call, not lodash.
+1 -1
View File
@@ -55,4 +55,4 @@ export function Feed({ items, fetchMore, hasMore }) {
}
```
**1 dependency → 0 dependencies.** `IntersectionObserver` fires only when the sentinel enters the viewport no scroll event, no throttling, no jank. Ships in every browser. The library wraps exactly this API.
**1 dependency → 0 dependencies.** `IntersectionObserver` fires only when the sentinel enters the viewport, no scroll event, no throttling, no jank. Ships in every browser. The library wraps exactly this API.
+1 -1
View File
@@ -37,7 +37,7 @@ export function DeleteModal({ onConfirm, onCancel }) {
}
```
A dependency, a portal, an overlay, a root, a trigger, a content wrapper to show a box with two buttons.
A dependency, a portal, an overlay, a root, a trigger, a content wrapper, to show a box with two buttons.
## With Ponytail
+6 -6
View File
@@ -2,9 +2,9 @@
**Task:** "Add rate limiting to my FastAPI endpoint so users can't spam it."
Verbatim model output from a benchmark run Claude Haiku 4.5, no-skill arm vs ponytail arm, temperature 1, source `benchmarks/output.json`. Reproduce: `npx promptfoo@latest eval -c benchmarks/promptfooconfig.yaml`.
Verbatim model output from a benchmark run, Claude Haiku 4.5, no-skill arm vs ponytail arm, temperature 1, source `benchmarks/output.json`. Reproduce: `npx promptfoo@latest eval -c benchmarks/promptfooconfig.yaml`.
## Without Ponytail 128 lines of code
## Without Ponytail, 128 lines of code
# Rate Limiting in FastAPI
@@ -248,9 +248,9 @@ async def test_rate_limit():
# First 5 should be 200, 6th should be 429
```
**Recommendation:** Use `slowapi` for most casesit's the most popular and easiest to implement! 🚀
**Recommendation:** Use `slowapi` for most cases, it's the most popular and easiest to implement! 🚀
## With Ponytail 10 lines of code
## With Ponytail, 10 lines of code
```python
from fastapi import FastAPI, HTTPException
@@ -267,6 +267,6 @@ async def my_endpoint(request):
return {"status": "ok"}
```
Skipped: custom rate limit logic, Redis, sliding windows `slowapi` handles it. Add when: you need distributed rate limiting across multiple servers (swap `Limiter` for Redis backend) or per-user limits (add `key_func=lambda r: r.headers.get("authorization")`).
Skipped: custom rate limit logic, Redis, sliding windows, `slowapi` handles it. Add when: you need distributed rate limiting across multiple servers (swap `Limiter` for Redis backend) or per-user limits (add `key_func=lambda r: r.headers.get("authorization")`).
**128 → 10 lines of code** same model, same prompt.
**128 → 10 lines of code**, same model, same prompt.
+5 -5
View File
@@ -2,9 +2,9 @@
**Task:** "Build me a countdown timer component in React that counts down from a given number of seconds."
Verbatim model output from a benchmark run Claude Haiku 4.5, no-skill arm vs ponytail arm, temperature 1, source `benchmarks/output.json`. Reproduce: `npx promptfoo@latest eval -c benchmarks/promptfooconfig.yaml`.
Verbatim model output from a benchmark run, Claude Haiku 4.5, no-skill arm vs ponytail arm, temperature 1, source `benchmarks/output.json`. Reproduce: `npx promptfoo@latest eval -c benchmarks/promptfooconfig.yaml`.
## Without Ponytail 267 lines of code
## Without Ponytail, 267 lines of code
# React Countdown Timer Component
@@ -369,7 +369,7 @@ export function StyledCountdownTimer({ initialSeconds = 60 }) {
Choose the version that best fits your needs!
## With Ponytail 9 lines of code
## With Ponytail, 9 lines of code
```jsx
export function CountdownTimer({ seconds }) {
@@ -385,6 +385,6 @@ export function CountdownTimer({ seconds }) {
}
```
Skipped: pause/resume, formatted display (mm:ss), sound on zero, styling add when needed.
Skipped: pause/resume, formatted display (mm:ss), sound on zero, styling, add when needed.
**267 → 9 lines of code** same model, same prompt.
**267 → 9 lines of code**, same model, same prompt.
-64
View File
@@ -1,64 +0,0 @@
# Web Platform Lookup
**Task:** "Add a modal dialog that closes when you click the backdrop."
Rung 3 of the ladder is "native platform feature covers it?" On web work the
trap is that the agent forgets what the platform already does and reaches for a
library. When ponytail has [Modern Web Guidance](https://github.com/GoogleChrome/modern-web-guidance)
on hand, rung 3 gets a lookup: `modern-web search "modal dialog light dismiss"`.
## Without Ponytail
```bash
npm install @radix-ui/react-dialog
```
```jsx
import * as Dialog from "@radix-ui/react-dialog";
export default function Modal({ open, onOpenChange, children }) {
return (
<Dialog.Root open={open} onOpenChange={onOpenChange}>
<Dialog.Portal>
<Dialog.Overlay className="overlay" />
<Dialog.Content className="content">
{children}
<Dialog.Close className="close">×</Dialog.Close>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
}
```
A dependency, a portal, an overlay node, and controlled open state, to put a
box on top with a backdrop.
## With Ponytail
`modern-web search "modal dialog light dismiss"`
`modern-web retrieve light-dismiss-a-dialog`. The platform has it:
```html
<!-- ponytail: <dialog> + closedby, browser does the backdrop, focus trap, and Esc -->
<dialog closedby="any">
<p>...</p>
</dialog>
```
```js
document.querySelector("dialog").showModal();
```
**1 dependency + portal/overlay machinery → 0 dependencies + a `<dialog>`.**
The `::backdrop` is free, focus is trapped and restored for you, `Esc` closes
it, and `closedby="any"` adds click-outside dismissal. The browser team did the
work.
## The point
MWG suggests the cutting edge, ponytail keeps only the rung that holds. The
lookup found `light-dismiss-a-dialog`; the ladder took it because it deletes a
dependency. The same search would have offered scroll-driven animations and
view transitions for other tasks, and the ladder would have skipped them when
the task didn't need them. Lookup, not license.
+2 -2
View File
@@ -12,9 +12,9 @@ prompt menu, or that pull context through tools. See issue #70.
## What it exposes
- Prompt `ponytail` returns the ruleset as a user message. Optional `mode`
- Prompt `ponytail`, returns the ruleset as a user message. Optional `mode`
argument: `lite`, `full`, or `ultra`. Omit it to use the configured default.
- Tool `ponytail_instructions` same text, plus `structuredContent`
- Tool `ponytail_instructions`, same text, plus `structuredContent`
(`{ mode, instructions }`), for hosts that pull context via tools or code
execution. Read-only.
+1 -1
View File
@@ -2,9 +2,9 @@
"name": "ponytail-mcp",
"version": "0.1.0",
"description": "MCP server that serves Ponytail's lazy-senior-dev instructions as a prompt and a tool.",
"private": true,
"type": "module",
"license": "MIT",
"bin": { "ponytail-mcp": "./index.js" },
"scripts": { "test": "node --test ./test/*.test.js" },
"dependencies": {
"@modelcontextprotocol/sdk": "^1.19.0",
+1 -1
View File
@@ -36,6 +36,6 @@ End with `net: -<N> lines, -<M> deps possible.` Nothing to cut: `Lean already. S
## Boundaries
Scope: over-engineering and complexity only. Correctness bugs, security holes,
and performance are explicitly out of scope — route them to a normal review
and performance are explicitly out of scope. Route them to a normal review
pass. Lists findings, applies nothing. One-shot.
"stop ponytail-audit" or "normal mode" to revert.
+1 -1
View File
@@ -26,7 +26,7 @@ the convention out of the ledger.
One row per marker, grouped by file:
`<file>:<line> <what was simplified>. ceiling: <the limit named>. upgrade: <the trigger to revisit>.`
`<file>:<line>, <what was simplified>. ceiling: <the limit named>. upgrade: <the trigger to revisit>.`
The convention is `ponytail: <ceiling>, <upgrade path>`, so pull the ceiling
and the trigger straight from the comment. Want an owner per row too? add
+1 -1
View File
@@ -50,7 +50,7 @@ If there is nothing to cut, say `Lean already. Ship.` and stop.
## Boundaries
Scope: over-engineering and complexity only. Correctness bugs, security holes,
and performance are explicitly out of scope — route them to a normal review
and performance are explicitly out of scope. Route them to a normal review
pass, not this one. A single smoke test or `assert`-based
self-check is the ponytail minimum, not bloat, never flag it for deletion.
Does not apply the fixes, only lists them.
-10
View File
@@ -40,16 +40,6 @@ Stop at the first rung that holds:
The ladder is a reflex, not a research project. Two rungs work → take the
higher one and move on. The first lazy solution that works is the right one.
## Web tasks: rung 3 lookup
On web work, rung 3 is where the laziest win hides: a native element or CSS
behavior the agent forgot exists. If a web task turns on whether the platform
covers it (a date input, dialog, popover, view transition, container query),
and the `modern-web` CLI is available, look it up: `modern-web search "<task>"`,
then `modern-web retrieve <id>`. It is a lookup, not a license, the answer
still goes through the ladder. MWG suggests the cutting edge; you keep only the
rung that holds. Not installed? Skip it, the ladder runs fine without it.
## Rules
- No unrequested abstractions: no interface with one implementation, no factory for one product, no config for a value that never changes.