Compare commits

..
Author SHA1 Message Date
DietrichGebert 16f6cbf4b8 docs(benchmarks): add Independent benchmarks section (#460)
Curated, plugin-installed third-party runs (KuldeepB19, RicardoCostaGit) with
method, headline, and date, clearly framed as independent corroboration not
official figures. Gives the recurring 'did anyone independent check this?' ask
a canonical home.

Closes #121
Closes #236
2026-06-30 02:04:48 +02:00
nanaubusinessandAdmin 85cc1d9935 fix: copilot-plugin test checks all 6 command files (#393)
Previously only checked 4 of 6. ponytail-gain.toml and
ponytail-help.toml are now validated.

Closes #381

Co-authored-by: Admin <admin@Admins-MacBook-Pro.local>
2026-06-30 00:21:09 +02:00
DietrichGebert bc9ee949d5 Merge pull request #448 from DietrichGebert/release-4.8.4
release: 4.8.4
2026-06-29 07:23:36 +02:00
Emeriko c99757a14a release: bump plugin.yaml (Hermes manifest) to 4.8.4 2026-06-29 07:19:46 +02:00
Emeriko 4b7a012626 release: bump .devin-plugin to 4.8.4 2026-06-29 07:15:31 +02:00
Emeriko 801d976c69 release: 4.8.4 2026-06-29 07:12:58 +02:00
DietrichGebert adad1e451a Merge pull request #447 from DietrichGebert/fix-skill-trigger-recall
Trigger ponytail skill on any coding task, not just keywords
2026-06-29 07:08:50 +02:00
Emeriko 0e3fd0cfee Trigger ponytail skill on any coding task, not just keywords
The skill description was written keyword-first ("use when the user
says lazy / complains about boilerplate"), so a model-invoked host
only loaded it on prompts that hit those words. Plain coding tasks
(add a date picker, write a dedupe function, build a cache, fix a bug)
were skipped, exactly the over-build cases ponytail is best at.

Tested via a skill-router proxy (3 runs, 12 labeled prompts): recall
on coding tasks was 2/6 before, 6/6 after, with precision unchanged at
6/6 (no false fires on non-coding prompts). Fix is one clause naming
coding tasks explicitly plus a negative clause to keep precision.

Trigger descriptions live in two source-of-truth spots, both updated:
skills/ponytail/SKILL.md and the DESCRIPTIONS map in
scripts/build-openclaw-skills.js (.openclaw regenerated). The store
blurbs in the plugin manifests are listing metadata, not triggers, so
they are left alone.
2026-06-29 07:06:59 +02:00
DietrichGebert c4d1925ae9 Merge pull request #323 from DietrichGebert/hermes-plugin
feat: add Hermes Agent plugin (rebuilt from #78)
2026-06-26 03:28:08 +02:00
Emeriko 6eb2b7829c Merge branch 'main' into salaamdev/main
# Conflicts:
#	README.md
2026-06-26 03:24:37 +02:00
Emeriko ac75159e5e Merge branch 'main' into salaamdev/main
# Conflicts:
#	README.md
2026-06-26 03:08:24 +02:00
Lakshya Sharma 8d154e6c2a fix(benchmarks): strip block comments before counting LOC (#232)
loc.js filtered comments line by line, so /* ... */ block comments whose
continuation lines are not *-aligned had their inner lines counted as code.
A plain indented block comment scored higher than the same code written
JSDoc-style. Strip /* ... */ before the line count so both are equal, and
add loc.test.js to lock the behavior.

Fixes #231
2026-06-26 03:02:55 +02:00
systemBlueandClaude Opus 4.8 e353a1a5d3 docs: add Swift/SwiftUI section to platform-native.md (#313)
Closes #299. The "does the platform already do this?" rung is especially
rich on Apple platforms, but the doc had no Swift coverage. Adds a
Swift/SwiftUI section in the existing two-table format: SwiftUI controls
and the Swift/framework layer, with library carve-outs kept inline to
match the `ms` / `requests` style already in the doc.


Claude-Session: https://claude.ai/code/session_01MtGgxEbiXPQjqMyXWbnixn

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 02:38:34 +02:00
DietrichGebert 7147937ae8 Sync ES and Korean READMEs with the Devin CLI addition (#322)
Follow-up to #318, which updated only the English README. Bumps the agent badge 14->15, adds a Devin CLI install section before OpenClaw, and lists Devin CLI in the skill-capable host line. Also adds Swival to both host lines, which the translations were already missing.
2026-06-26 02:33:56 +02:00
Hamza Ali Shahjahan 33a4977a83 fix(benchmark): reject --ollama-url without a host (#315)
Follow-up to #274: the merged scheme validation accepts host-less URLs (http://, https://), which then fail opaquely inside urlopen(). Add a minimal netloc check after the existing scheme check, so a missing host is rejected up front with a clear argparse error. Additive; keeps the existing scheme message intact.
2026-06-26 02:28:51 +02:00
salaamdev 731d3193e7 Merge branch 'main' into main 2026-06-19 05:58:49 +03:00
salaamdev b4f725f7ce Merge branch 'main' into main 2026-06-18 10:45:26 +03:00
salaamdev ce55fd460a Update author in plugin.yaml to Salaamdev 2026-06-15 23:13:09 +03:00
Abdisalam Hassan 4198fc30ac add Hermes plugin for Ponytail with command support and documentation 2026-06-15 19:57:45 +00:00
22 changed files with 593 additions and 17 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "ponytail",
"version": "4.8.3",
"version": "4.8.4",
"description": "Lazy senior dev mode. Forces the simplest, shortest solution that actually works: YAGNI, stdlib first, no unrequested abstractions.",
"author": {
"name": "Dietrich Gebert",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "ponytail",
"version": "4.8.3",
"version": "4.8.4",
"description": "Lazy senior dev mode. Forces the simplest, shortest solution that actually works: YAGNI, stdlib first, no unrequested abstractions.",
"author": {
"name": "Dietrich Gebert",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "ponytail",
"version": "4.8.3",
"version": "4.8.4",
"description": "Lazy senior dev mode. Forces the simplest, shortest solution that actually works: YAGNI, stdlib first, no unrequested abstractions.",
"author": {
"name": "Dietrich Gebert",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "ponytail",
"description": "Lazy senior dev mode. Forces the simplest, shortest solution that actually works: YAGNI, stdlib first, no unrequested abstractions.",
"version": "4.8.3",
"version": "4.8.4",
"author": {
"name": "Dietrich Gebert",
"url": "https://github.com/DietrichGebert"
+1 -1
View File
@@ -1,6 +1,6 @@
---
name: ponytail
description: "Lazy senior dev mode. Forces the simplest, shortest solution that works: YAGNI, stdlib first, no unrequested abstractions."
description: "Lazy senior dev mode for any coding task (write, refactor, fix, review): YAGNI, stdlib first, no unrequested abstractions. Not for non-coding requests."
homepage: https://github.com/DietrichGebert/ponytail
license: MIT
---
+10 -2
View File
@@ -15,7 +15,7 @@
<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/npm/v/@dietrichgebert/ponytail?style=flat-square&color=111111&label=npm" alt="npm">
<img src="https://img.shields.io/badge/works%20with-15%20agents-111111?style=flat-square" alt="Works with 15 agents">
<img src="https://img.shields.io/badge/works%20with-16%20agents-111111?style=flat-square" alt="Works with 16 agents">
<img src="https://img.shields.io/badge/license-MIT-111111?style=flat-square" alt="MIT license">
</p>
@@ -199,6 +199,14 @@ agy plugin install https://github.com/DietrichGebert/ponytail
It reuses this repo's `gemini-extension.json`. One difference: Antigravity converts the `/ponytail` commands into skills, so you type them into the chat (e.g. `/ponytail-review` as a message) instead of picking them from a slash menu. Until the migration completes (around June 18, 2026), `gemini extensions install` still works too. To run it as an always-on rule instead, drop the ruleset into `.agents/rules/`.
### Hermes Agent
```bash
hermes plugins install DietrichGebert/ponytail --enable
```
Restart Hermes after installing. The plugin injects the active Ponytail mode before each LLM turn, registers the bundled skills as `ponytail:<skill>`, and adds `/ponytail`, `/ponytail-review`, `/ponytail-audit`, `/ponytail-debt`, `/ponytail-gain`, and `/ponytail-help`. In shared gateways, restrict `/ponytail` to trusted users with Hermes slash-command access controls; runtime mode is process-local.
### 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.
@@ -272,7 +280,7 @@ These remove the plugin's own files. They leave behind a small amount of state p
| `/ponytail-gain` | Show the measured impact scoreboard (less code, less cost, more speed) from the benchmark. |
| `/ponytail-help` | Quick reference for the commands above. |
Commands need a skill-capable host (Claude Code, Codex, Devin CLI, OpenCode, Gemini, pi, Swival). In Codex they're skills, invoke with `@` (`@ponytail-review`). The instruction-only adapters (Cursor, Windsurf, Cline, Copilot, Kiro, Antigravity) load the always-on ruleset without the commands.
Commands need a skill-capable host (Claude Code, Codex, Devin CLI, OpenCode, Gemini, pi, Swival, Hermes Agent). In Codex they're skills, invoke with `@` (`@ponytail-review`). The instruction-only adapters (Cursor, Windsurf, Cline, Copilot, Kiro, Antigravity) load the always-on ruleset without the commands.
## Development
+217
View File
@@ -0,0 +1,217 @@
"""Hermes plugin for Ponytail."""
from __future__ import annotations
import json
import os
import re
from pathlib import Path
from typing import Any, Callable
DEFAULT_MODE = "full"
RUNTIME_MODES = {"off", "lite", "full", "ultra"}
CONFIG_MODES = RUNTIME_MODES | {"review"}
SKILL_COMMANDS = {
"ponytail-review": "Review the current diff or provided target for over-engineering.",
"ponytail-audit": "Audit the repo for over-engineering and deletion opportunities.",
"ponytail-debt": "List every deliberate `ponytail:` shortcut and its upgrade path.",
"ponytail-gain": "Show the measured-impact scoreboard (less code, less cost, more speed).",
"ponytail-help": "Show the Ponytail command reference.",
}
ROOT = Path(__file__).resolve().parent
SKILLS_DIR = ROOT / "skills"
PONYTAIL_SKILL = SKILLS_DIR / "ponytail" / "SKILL.md"
REVIEW_SKILL = SKILLS_DIR / "ponytail-review" / "SKILL.md"
_current_mode = None
def _normalize_runtime_mode(mode: str | None) -> str | None:
if not isinstance(mode, str):
return None
mode = mode.strip().lower()
return mode if mode in RUNTIME_MODES else None
def _normalize_config_mode(mode: str | None) -> str | None:
if not isinstance(mode, str):
return None
mode = mode.strip().lower()
return mode if mode in CONFIG_MODES else None
def _config_dir() -> Path:
if os.environ.get("XDG_CONFIG_HOME"):
return Path(os.environ["XDG_CONFIG_HOME"]) / "ponytail"
if os.name == "nt":
return Path(os.environ.get("APPDATA", Path.home() / "AppData" / "Roaming")) / "ponytail"
return Path.home() / ".config" / "ponytail"
def _default_mode() -> str:
env_mode = _normalize_config_mode(os.environ.get("PONYTAIL_DEFAULT_MODE"))
if env_mode:
return env_mode
try:
data = json.loads((_config_dir() / "config.json").read_text(encoding="utf-8"))
file_mode = _normalize_config_mode(data.get("defaultMode"))
if file_mode:
return file_mode
except Exception:
pass
return DEFAULT_MODE
def _strip_frontmatter(text: str) -> str:
return re.sub(r"^---[\s\S]*?---\s*", "", text or "", count=1)
def _filter_skill_body_for_mode(body: str, mode: str) -> str:
effective = _normalize_runtime_mode(mode) or DEFAULT_MODE
lines = []
for line in _strip_frontmatter(body).splitlines():
table_label = re.match(r"^\|\s*\*\*(.+?)\*\*\s*\|", line)
if table_label:
label_mode = _normalize_runtime_mode(table_label.group(1))
if label_mode and label_mode != effective:
continue
example_label = re.match(r"^-\s*([^:]+):\s*", line)
if example_label:
label_mode = _normalize_runtime_mode(example_label.group(1))
if label_mode and label_mode != effective:
continue
lines.append(line)
return "\n".join(lines)
def _fallback_instructions(mode: str) -> str:
return (
f"PONYTAIL MODE ACTIVE — level: {mode}\n\n"
"You are a lazy senior developer. Lazy means efficient, not careless. "
"The best code is the code never written.\n\n"
"Before any code, stop at the first rung that holds: YAGNI, stdlib, "
"native platform, installed dependency, one line, then minimum code. "
"No unrequested abstractions, avoidable dependencies, boilerplate, or "
"speculative scaffolding. Deletion over addition. Boring over clever. "
"Do not simplify away trust-boundary validation, data-loss handling, "
"security, accessibility, explicitly requested behavior, or one small "
"runnable check for non-trivial logic."
)
def build_injected_context(mode: str | None = None) -> str:
"""Return the mode-filtered Ponytail context injected before LLM turns."""
configured = _normalize_config_mode(mode) or _default_mode()
if configured == "off":
return ""
if configured == "review":
try:
body = REVIEW_SKILL.read_text(encoding="utf-8")
return f"PONYTAIL MODE ACTIVE — level: review\n\n{_strip_frontmatter(body)}"
except OSError:
return "PONYTAIL MODE ACTIVE — level: review. Review diffs for unnecessary complexity."
effective = _normalize_runtime_mode(configured) or DEFAULT_MODE
try:
body = PONYTAIL_SKILL.read_text(encoding="utf-8")
return f"PONYTAIL MODE ACTIVE — level: {effective}\n\n{_filter_skill_body_for_mode(body, effective)}"
except OSError:
return _fallback_instructions(effective)
def _pre_llm_call(session_id: str = "", **_: Any) -> dict[str, str] | None:
mode = _current_mode or _default_mode()
context = build_injected_context(mode)
return {"context": context} if context else None
def _skill_prompt(command: str, args: str = "") -> str:
tail = args.strip()
target = f"\n\nUser arguments: {tail}" if tail else ""
return (
f"Load and follow the Hermes plugin skill `ponytail:{command}`. "
f"{SKILL_COMMANDS[command]}{target}"
)
def _slash_access_denied(event: Any, gateway: Any, command: str) -> bool:
if gateway is None or event is None:
return False
checker = getattr(gateway, "_check_slash_access", None)
source = getattr(event, "source", None)
if checker is None or source is None:
return False
try:
return checker(source, command) is not None
except Exception:
return True
def rewrite_gateway_command(event: Any = None, gateway: Any = None, **_: Any) -> dict[str, str] | None:
"""Rewrite authorized gateway /ponytail-* commands into normal agent prompts."""
text = str(getattr(event, "text", "") or "").strip()
if not text.startswith("/"):
return None
head, _, rest = text[1:].partition(" ")
command = head.replace("_", "-").lower()
if command not in SKILL_COMMANDS:
return None
if _slash_access_denied(event, gateway, command):
return None
return {"action": "rewrite", "text": _skill_prompt(command, rest)}
def _handle_mode_command(raw_args: str) -> str:
global _current_mode
arg = (raw_args or "").strip().lower()
if not arg:
mode = _current_mode or _default_mode()
return f"Ponytail mode: {mode}. Use `/ponytail lite|full|ultra|off`."
mode = _normalize_runtime_mode(arg)
if not mode:
return "Usage: /ponytail [lite|full|ultra|off]"
_current_mode = mode
return f"Ponytail mode set to {mode}."
def _make_skill_command_handler(ctx: Any, command: str) -> Callable[[str], str]:
def handler(raw_args: str) -> str:
prompt = _skill_prompt(command, raw_args or "")
injected = False
try:
injected = bool(ctx.inject_message(prompt))
except Exception:
injected = False
if injected:
return f"Queued `{command}` for the agent."
return prompt
return handler
def register(ctx: Any) -> None:
"""Register Ponytail hooks, skills, and slash commands with Hermes."""
for child in sorted(SKILLS_DIR.iterdir() if SKILLS_DIR.exists() else []):
skill_md = child / "SKILL.md"
if child.is_dir() and skill_md.exists():
ctx.register_skill(child.name, skill_md)
ctx.register_hook("pre_llm_call", _pre_llm_call)
ctx.register_hook("pre_gateway_dispatch", rewrite_gateway_command)
ctx.register_command(
"ponytail",
_handle_mode_command,
description="Set Ponytail lazy senior dev mode: lite, full, ultra, or off.",
args_hint="[lite|full|ultra|off]",
)
for command, description in SKILL_COMMANDS.items():
ctx.register_command(
command,
_make_skill_command_handler(ctx, command),
description=description,
args_hint="[target or notes]",
)
+22
View File
@@ -0,0 +1,22 @@
# Ponytail for Hermes installed
Enable it if you did not install with `--enable`:
```bash
hermes plugins enable ponytail
```
Restart Hermes or the gateway after enabling.
In shared gateways, restrict `/ponytail` to trusted users with Hermes slash-command access controls; runtime mode is process-local.
Commands:
- `/ponytail [lite|full|ultra|off]`
- `/ponytail-review [target]`
- `/ponytail-audit [target]`
- `/ponytail-debt`
- `/ponytail-gain`
- `/ponytail-help`
Bundled skills are available as `ponytail:ponytail`, `ponytail:ponytail-review`, `ponytail:ponytail-audit`, `ponytail:ponytail-debt`, `ponytail:ponytail-gain`, and `ponytail:ponytail-help`.
+16
View File
@@ -70,6 +70,22 @@ Versus baseline, ponytail writes **80-94% less code**, costs **42-75% less**, an
> while the bare "one-liner" prompt drops a guard. That is the honest, defensible number. See
> [results/2026-06-18-agentic.md](results/2026-06-18-agentic.md).
## Independent benchmarks
Run by other people, not by us, on their own harnesses and machines. Linked for
transparency: the numbers are theirs, may shift between runs, and are corroboration
rather than official figures. Only plugin-installed runs are listed, since pasting
`SKILL.md` into a prompt is a rough approximation of `full` and skews the result.
| Source | Method | Headline | Date |
|---|---|---|---|
| [KuldeepB19](https://kuldeepb19.github.io/ponytail-benchmark/) | Installed plugin, 24 tasks, no-skill vs Lite/Full/Ultra, 5 runs each (480 builds), Opus 4.8, graded by executing the code | ~44% less code (53% fewer statements), no correctness or security regression; trims everyday bad-input handling on 5/24 tasks | 2026-06-24 |
| [RicardoCostaGit](https://github.com/RicardoCostaGit/ponytail-benchmark-from-cursor) | Multi-turn agentic runs via the Cursor SDK, isolated git worktrees, rule file toggled per run | Leaner output but higher process cost (more tool calls/tokens) on large completion-forced tasks; savings land on blocked/snowball-prone tasks | 2026-06-16 |
Both land on the same split as the honesty note above: ponytail reliably writes less
code, and whether that *saves money* depends on the workload (big win on
over-build and blocked tasks, can cost more on large completion-forced agentic runs).
## Metrics
| File | Metric | Behavior |
+2
View File
@@ -154,6 +154,8 @@ def main():
parsed_url = urllib.parse.urlparse(args.ollama_url)
if parsed_url.scheme not in ("http", "https"):
parser.error(f"Invalid --ollama-url scheme: '{parsed_url.scheme}'. Only 'http' and 'https' are supported.")
if not parsed_url.netloc:
parser.error(f"--ollama-url must include a host, e.g. http://localhost:11434 (got '{args.ollama_url}').")
run(args.model, args.repeat, args.ollama_url)
+3 -1
View File
@@ -4,7 +4,9 @@
module.exports = (output) => {
const text = String(output || '');
const blocks = [...text.matchAll(/```[a-zA-Z0-9_+-]*\n([\s\S]*?)```/g)].map((m) => m[1]);
const code = blocks.length ? blocks.join('\n') : text;
// Drop /* ... */ block comments before counting; the line filter below only
// caught `*`-aligned JSDoc, so plain block comments were miscounted as code.
const code = (blocks.length ? blocks.join('\n') : text).replace(/\/\*[\s\S]*?\*\//g, '');
const loc = code
.split('\n')
.map((l) => l.trim())
+22
View File
@@ -0,0 +1,22 @@
// Regression guard for loc.js comment handling. Run: node loc.test.js
const assert = require('assert');
const loc = require('./loc.js');
const score = (src) => loc(src).score;
let pass = 0;
const cases = [
// /* ... */ block comments must not count as code, whether or not the
// continuation lines are *-aligned (the old filter only caught JSDoc style).
['plain block comment not counted', score('```js\nfunction f() {\n /* explain\n the rest */\n return 1;\n}\n```'), 3],
['jsdoc block comment not counted', score('```js\nfunction g() {\n /*\n * explain\n */\n return 2;\n}\n```'), 3],
['inline block comment keeps its code line', score('```js\nconst x = 1; /* note */\nconst y = 2;\n```'), 2],
['line comments still stripped', score('```js\n// header\nconst x = 1;\n```'), 1],
['plain code unchanged', score('```js\nconst a = 1;\nconst b = 2;\n```'), 2],
];
for (const [name, got, want] of cases) {
assert.strictEqual(got, want, `FAILED: ${name} (got ${got}, want ${want})`);
console.log(`ok - ${name}`);
pass++;
}
console.log(`\n${pass}/${cases.length} passed`);
+1
View File
@@ -12,6 +12,7 @@ to load in a given agent.
| Codex | `.codex-plugin/plugin.json`, `hooks/claude-codex-hooks.json`, `hooks/`, `skills/` | Plugin install with the same skills plus lifecycle hooks for activation and mode tracking. |
| OpenCode | `.opencode/plugins/ponytail.mjs`, `.opencode/command/`, `hooks/`, `skills/` | Server plugin injects the ruleset each turn via `experimental.chat.system.transform` and persists `/ponytail` switches; reuses the shared instruction builder. |
| pi | `pi-extension/`, `skills/`, `hooks/` | Package extension: injects the ruleset each turn through the shared instruction builder and registers the `/ponytail` commands. |
| Hermes Agent | `plugin.yaml`, `__init__.py`, `skills/` | Native Hermes plugin: injects active mode through `pre_llm_call`, rewrites gateway `/ponytail-*` skill commands into agent prompts, registers `/ponytail` mode switching, and exposes bundled skills as `ponytail:<skill>`. |
| Gemini CLI | `gemini-extension.json`, `AGENTS.md`, `commands/`, `skills/` | Extension manifest points `contextFileName` at `AGENTS.md` for always-on rules, and reuses the existing `commands/*.toml` and `skills/`, which Gemini CLI auto-discovers. The Claude/Codex hook map is not placed at Gemini's auto-discovered `hooks/hooks.json` path. |
| Cursor | `.cursor/rules/ponytail.mdc` | Always-on project rule. |
| Windsurf | `.windsurf/rules/ponytail.md` | Project rule. |
+38
View File
@@ -86,6 +86,44 @@ const debounce = (fn, ms) => (...args) => { clearTimeout(t); t = setTimeout(() =
---
## Swift / SwiftUI
UI components people reach for a library or a custom view for.
| You think you need | What the platform has |
|---|---|
| Date/time picker library | `DatePicker` |
| Color picker library | `ColorPicker` |
| Search bar + filtering | `.searchable(text:)` |
| Pull-to-refresh library | `.refreshable { }` |
| Swipe-to-delete / row actions | `.swipeActions { }` |
| Async image loading + cache | `AsyncImage` |
| Charting library | Swift Charts (`import Charts`) |
| Markdown rendering | `Text(...)` markdown / `AttributedString(markdown:)` |
| Share sheet wrapper | `ShareLink` |
| Loading spinner | `ProgressView()` |
| Photo picker | `PhotosPicker` |
| Map SDK (basic) | `Map` (MapKit for SwiftUI) |
| Grid layout library | `Grid` / `LazyVGrid` |
Frameworks and stdlib that wrappers wrap.
| You think you need | What the platform has |
|---|---|
| JSON library (SwiftyJSON) | `Codable` + `JSONDecoder` / `JSONEncoder` |
| HTTP client (Alamofire, simple use) | `URLSession` async/await; Alamofire earns it for complex retry/multipart at scale |
| Date/number/currency formatting | `.formatted()` / `FormatStyle` |
| Regex library | Swift regex literals + `Regex` |
| Crypto library (CryptoSwift) | `CryptoKit` |
| Keychain wrapper | Security `SecItem`; a few lines, not a dependency |
| Persistence / ORM | `SwiftData`, or `@AppStorage` for small key-values |
| Logging library | `Logger` (`os.log`) |
| UUID / Base64 helpers | `UUID()`, `Data(...).base64EncodedString()` |
| Image downsampling | ImageIO `CGImageSourceCreateThumbnailAtIndex` |
| Combine wrappers for async | async/await + `AsyncSequence` |
---
## Node.js Standard Library
Packages that wrap Node built-ins.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "ponytail",
"version": "4.8.3",
"version": "4.8.4",
"description": "Lazy senior dev mode. Forces the simplest, shortest solution that actually works: YAGNI, stdlib first, no unrequested abstractions.",
"contextFileName": "AGENTS.md"
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@dietrichgebert/ponytail",
"version": "4.8.3",
"version": "4.8.4",
"description": "Lazy senior dev mode for AI agents. The best code is the code you never wrote.",
"keywords": ["opencode-plugin", "opencode", "ponytail", "pi-package", "pi", "skills"],
"license": "MIT",
+21
View File
@@ -0,0 +1,21 @@
name: ponytail
version: 4.8.4
description: Lazy senior dev mode for Hermes Agent, always-on context, bundled skills, and slash commands.
author: Salaamdev
provides_hooks:
- pre_llm_call
- pre_gateway_dispatch
provides_commands:
- ponytail
- ponytail-review
- ponytail-audit
- ponytail-debt
- ponytail-gain
- ponytail-help
provides_skills:
- ponytail
- ponytail-review
- ponytail-audit
- ponytail-debt
- ponytail-gain
- ponytail-help
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "ponytail-mcp",
"version": "4.8.3",
"version": "4.8.4",
"description": "MCP server that serves Ponytail's lazy-senior-dev instructions as a prompt and a tool.",
"private": true,
"type": "module",
+1 -1
View File
@@ -17,7 +17,7 @@ const ROOT = path.join(__dirname, '..');
const HOMEPAGE = 'https://github.com/DietrichGebert/ponytail';
const DESCRIPTIONS = {
'ponytail': 'Lazy senior dev mode. Forces the simplest, shortest solution that works: YAGNI, stdlib first, no unrequested abstractions.',
'ponytail': 'Lazy senior dev mode for any coding task (write, refactor, fix, review): YAGNI, stdlib first, no unrequested abstractions. Not for non-coding requests.',
'ponytail-review': 'Review a diff for over-engineering. Finds what to delete: reinvented stdlib, needless deps, speculative abstractions. One line per finding.',
'ponytail-audit': 'Audit the whole repo for over-engineering. A ranked list of what to delete, simplify, or replace with stdlib or native features.',
'ponytail-debt': 'Harvest every ponytail: shortcut comment into one debt ledger, so deferrals get tracked instead of forgotten. One-shot report.',
+8 -5
View File
@@ -5,11 +5,14 @@ description: >
minimal. Channels a senior dev who has seen everything: question whether the
task needs to exist at all (YAGNI), reach for the standard library before
custom code, native platform features before dependencies, one line before
fifty. Supports intensity levels: lite, full (default), ultra. Use whenever
the user says "ponytail", "be lazy", "lazy mode", "simplest solution",
"minimal solution", "yagni", "do less", or "shortest path", and whenever
they complain about over-engineering, bloat, boilerplate, or unnecessary
dependencies.
fifty. Supports intensity levels: lite, full (default), ultra. Use on ANY
coding task: writing, adding, refactoring, fixing, reviewing, or designing
code, and choosing libraries or dependencies. Also use whenever the user
says "ponytail", "be lazy", "lazy mode", "simplest solution", "minimal
solution", "yagni", "do less", or "shortest path", or complains about
over-engineering, bloat, boilerplate, or unnecessary dependencies. Do NOT
use for non-coding requests (general knowledge, prose, translation,
summaries, recipes).
argument-hint: "[lite|full|ultra]"
license: MIT
---
+2
View File
@@ -13,6 +13,8 @@ const REQUIRED_COMMAND_FILES = [
'ponytail-review.toml',
'ponytail-audit.toml',
'ponytail-debt.toml',
'ponytail-gain.toml',
'ponytail-help.toml',
];
function readJSON(relPath) {
+222
View File
@@ -0,0 +1,222 @@
#!/usr/bin/env node
// Hermes support is a real plugin, not just copied rules: the repo root must be
// installable with `hermes plugins install owner/repo`, register bundled skills,
// inject active mode context, and expose slash commands.
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const os = require('os');
const path = require('path');
const { spawnSync } = require('child_process');
const commands = ['ponytail', 'ponytail-review', 'ponytail-audit', 'ponytail-debt', 'ponytail-gain', 'ponytail-help'];
const skillCommands = commands.filter((name) => name !== 'ponytail');
const root = path.join(__dirname, '..');
function python(script, env = {}) {
const result = spawnSync('python3', ['-c', script], {
cwd: root,
env: { ...process.env, ...env },
encoding: 'utf8',
});
if (result.status !== 0) {
throw new Error(`python failed\nSTDOUT:\n${result.stdout}\nSTDERR:\n${result.stderr}`);
}
return result.stdout.trim();
}
test('Hermes plugin manifest matches runtime skills, hooks, commands, and package version', () => {
const manifestPath = path.join(root, 'plugin.yaml');
assert.ok(fs.existsSync(manifestPath), 'missing root plugin.yaml');
const manifest = fs.readFileSync(manifestPath, 'utf8');
const packageJson = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
const skillDirs = fs.readdirSync(path.join(root, 'skills'))
.filter((name) => fs.existsSync(path.join(root, 'skills', name, 'SKILL.md')))
.sort();
assert.match(manifest, /^name:\s*ponytail$/m);
assert.match(manifest, new RegExp(`^version:\\s*${packageJson.version}$`, 'm'));
assert.deepEqual(commands.filter((name) => manifest.includes(` - ${name}`)), commands);
assert.deepEqual(skillDirs.filter((name) => manifest.includes(` - ${name}`)), skillDirs);
assert.match(manifest, /pre_llm_call/);
assert.match(manifest, /pre_gateway_dispatch/);
});
test('Hermes plugin registers every shipped skill under the ponytail namespace', () => {
const output = python(String.raw`
import importlib.util, json, pathlib
spec = importlib.util.spec_from_file_location('ponytail_hermes_plugin', '__init__.py')
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
class Ctx:
def __init__(self):
self.skills = []
self.hooks = []
self.commands = []
def register_skill(self, name, path):
self.skills.append((name, pathlib.Path(path).as_posix()))
def register_hook(self, name, handler):
self.hooks.append(name)
def register_command(self, name, handler, description='', args_hint=''):
self.commands.append(name)
ctx = Ctx()
mod.register(ctx)
print(json.dumps({'skills': ctx.skills, 'hooks': ctx.hooks, 'commands': ctx.commands}, sort_keys=True))
`);
const data = JSON.parse(output);
assert.deepEqual(data.skills.map(([name]) => name).sort(), [
'ponytail',
'ponytail-audit',
'ponytail-debt',
'ponytail-gain',
'ponytail-help',
'ponytail-review',
]);
assert.ok(data.skills.every(([, skillPath]) => skillPath.endsWith('/SKILL.md')));
assert.ok(data.hooks.includes('pre_llm_call'));
assert.ok(data.commands.includes('ponytail'));
assert.ok(data.commands.includes('ponytail-review'));
});
test('Hermes plugin builds mode-aware injected context from the canonical skill', () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ponytail-config-'));
const output = python(String.raw`
import importlib.util, json
spec = importlib.util.spec_from_file_location('ponytail_hermes_plugin', '__init__.py')
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
ctx = mod.build_injected_context('ultra')
print(json.dumps({'ctx': ctx}))
`, { XDG_CONFIG_HOME: tmp });
const { ctx } = JSON.parse(output);
assert.match(ctx, /PONYTAIL MODE ACTIVE — level: ultra/);
assert.match(ctx, /The best\s+code is the code never written/);
assert.match(ctx, /ultra/i);
assert.doesNotMatch(ctx, /^---/);
assert.doesNotMatch(ctx, /\|\s*\*\*Lite\*\*/i);
});
test('Hermes mode config respects env, config file, off, and invalid command behavior', () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ponytail-config-'));
fs.mkdirSync(path.join(tmp, 'ponytail'), { recursive: true });
fs.writeFileSync(path.join(tmp, 'ponytail', 'config.json'), JSON.stringify({ defaultMode: 'lite' }));
const output = python(String.raw`
import importlib.util, json
spec = importlib.util.spec_from_file_location('ponytail_hermes_plugin', '__init__.py')
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
class Ctx:
def __init__(self): self.commands = {}
def register_skill(self, name, path): pass
def register_hook(self, name, handler): pass
def register_command(self, name, handler, description='', args_hint=''):
self.commands[name] = handler
ctx = Ctx()
mod.register(ctx)
status_before = ctx.commands['ponytail']('')
invalid = ctx.commands['ponytail']('maximum')
status_after = ctx.commands['ponytail']('')
print(json.dumps({
'default': mod.build_injected_context(None),
'off': mod.build_injected_context('off'),
'status_before': status_before,
'invalid': invalid,
'status_after': status_after,
}))
`, { XDG_CONFIG_HOME: tmp, PONYTAIL_DEFAULT_MODE: 'ultra' });
const data = JSON.parse(output);
assert.match(data.default, /level: ultra/);
assert.equal(data.off, '');
assert.match(data.status_before, /Ponytail mode: ultra/);
assert.match(data.invalid, /Usage:/);
assert.match(data.status_after, /Ponytail mode: ultra/);
});
test('Hermes plugin review mode injects the real review skill body', () => {
const output = python(String.raw`
import importlib.util, json
spec = importlib.util.spec_from_file_location('ponytail_hermes_plugin', '__init__.py')
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
ctx = mod.build_injected_context('review')
print(json.dumps({'ctx': ctx}))
`);
const { ctx } = JSON.parse(output);
assert.match(ctx, /PONYTAIL MODE ACTIVE — level: review/);
assert.match(ctx, /Review diffs for unnecessary complexity/);
assert.match(ctx, /net: -<N> lines possible/);
assert.doesNotMatch(ctx, /^---/);
});
test('Hermes /ponytail command changes mode and pre_llm_call injects current context', () => {
const output = python(String.raw`
import importlib.util, json
spec = importlib.util.spec_from_file_location('ponytail_hermes_plugin', '__init__.py')
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
class Ctx:
def __init__(self):
self.hooks = {}
self.commands = {}
def register_skill(self, name, path): pass
def register_hook(self, name, handler): self.hooks[name] = handler
def register_command(self, name, handler, description='', args_hint=''):
self.commands[name] = handler
ctx = Ctx()
mod.register(ctx)
message = ctx.commands['ponytail']('ultra')
injected = ctx.hooks['pre_llm_call'](session_id='s1', user_message='build it', conversation_history=[], is_first_turn=False, model='m', platform='cli')
print(json.dumps({'message': message, 'context': injected['context']}))
`);
const data = JSON.parse(output);
assert.match(data.message, /ultra/);
assert.match(data.context, /PONYTAIL MODE ACTIVE — level: ultra/);
});
test('Hermes gateway rewrite respects slash access denial', () => {
const output = python(String.raw`
import importlib.util, json
spec = importlib.util.spec_from_file_location('ponytail_hermes_plugin', '__init__.py')
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
class Source:
platform = None
chat_id = 'c1'
user_id = 'u1'
class Event:
text = '/ponytail-review src/app.js'
source = Source()
class Gateway:
def _check_slash_access(self, source, command):
return 'denied'
result = mod.rewrite_gateway_command(event=Event(), gateway=Gateway())
print(json.dumps(result))
`);
assert.equal(output, 'null');
});
test('Hermes gateway rewrite preserves every skill command and ignores unrelated text', () => {
const output = python(String.raw`
import importlib.util, json
spec = importlib.util.spec_from_file_location('ponytail_hermes_plugin', '__init__.py')
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
class Event:
def __init__(self, text): self.text = text
cases = {}
for text in ['/ponytail-review x', '/ponytail_audit repo', '/ponytail-debt', '/ponytail-help', '/status', 'hello']:
cases[text] = mod.rewrite_gateway_command(event=Event(text))
print(json.dumps(cases, sort_keys=True))
`);
const data = JSON.parse(output);
assert.match(data['/ponytail-review x'].text, /ponytail-review/);
assert.match(data['/ponytail_audit repo'].text, /ponytail-audit/);
assert.match(data['/ponytail_audit repo'].text, /repo/);
assert.match(data['/ponytail-debt'].text, /ponytail-debt/);
assert.match(data['/ponytail-help'].text, /ponytail-help/);
assert.equal(data['/status'], null);
assert.equal(data.hello, null);
});