# Extension points — DietrichGebert/ponytail

> Canonical page: https://codevetter.com/unpack/ponytail/extension-points

# Extension points — DietrichGebert/ponytail

Scanned commit e3ba2aa6f1e6f0bc4d69eb09c9f0d0a93af56156 (2026-09-14). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

The repo is built for adapter growth: a new host means a manifest plus (ideally) a reuse of the shared hooks/ scripts, and the test suite auto-discovers some surfaces. Adding a skill requires touching five places (skills/, SKILL_COMMANDS in __init__.py, pi registerCommand, commands/*.toml, .opencode/command/*.md, DESCRIPTIONS in build-openclaw-skills.js) — commands.test.js catches the file gaps. Adding a mode means extending RUNTIME_MODES/VALID_MODES and the SKILL.md intensity table. Adding a host version manifest requires updating VERSION_FILES in check-versions.js.
- New host adapters should reuse the shared builder: every existing adapter (OpenCode, pi, MCP, hooks, Hermes) funnels through getPonytailInstructions()/getDefaultMode() so a new host needs only host detection + output shaping, per the documented 'Adapter Rule: keep adapters thin'. — [docs/agent-portability.md#L35-39](https://github.com/DietrichGebert/ponytail/blob/e3ba2aa6f1e6f0bc4d69eb09c9f0d0a93af56156/docs/agent-portability.md#L35-L39) [hooks/ponytail-instructions.js#L77-92](https://github.com/DietrichGebert/ponytail/blob/e3ba2aa6f1e6f0bc4d69eb09c9f0d0a93af56156/hooks/ponytail-instructions.js#L77-L92) [ponytail-mcp/instructions.js#L4-8](https://github.com/DietrichGebert/ponytail/blob/e3ba2aa6f1e6f0bc4d69eb09c9f0d0a93af56156/ponytail-mcp/instructions.js#L4-L8)
- New version-bearing manifests must be registered in the VERSION_FILES array in scripts/check-versions.js ('Add new host manifests here so a future ecosystem can't drift unnoticed'). — [scripts/check-versions.js#L19-30](https://github.com/DietrichGebert/ponytail/blob/e3ba2aa6f1e6f0bc4d69eb09c9f0d0a93af56156/scripts/check-versions.js#L19-L30)
- New rule-file copies must be added to the copies array in scripts/check-rule-copies.js with a normalizer (stripFrontmatter or trim), or they won't be drift-checked against AGENTS.md. — [scripts/check-rule-copies.js#L19-37](https://github.com/DietrichGebert/ponytail/blob/e3ba2aa6f1e6f0bc4d69eb09c9f0d0a93af56156/scripts/check-rule-copies.js#L19-L37)
- New skills plug in at skills/<name>/SKILL.md with frontmatter (name/description/argument-hint); the OpenCode plugin auto-discovers the skills/ dir, and Hermes's register() iterates skills/ children — but SKILL_COMMANDS in __init__.py, pi registerCommand calls, commands/*.toml, .opencode/command/*.md, and DESCRIPTIONS in build-openclaw-skills.js must be added by hand. — [__init__.py#L14-20](https://github.com/DietrichGebert/ponytail/blob/e3ba2aa6f1e6f0bc4d69eb09c9f0d0a93af56156/__init__.py#L14-L20) [__init__.py#L195-201](https://github.com/DietrichGebert/ponytail/blob/e3ba2aa6f1e6f0bc4d69eb09c9f0d0a93af56156/__init__.py#L195-L201) [pi-extension/index.js#L114-172](https://github.com/DietrichGebert/ponytail/blob/e3ba2aa6f1e6f0bc4d69eb09c9f0d0a93af56156/pi-extension/index.js#L114-L172) [scripts/build-openclaw-skills.js#L19-26](https://github.com/DietrichGebert/ponytail/blob/e3ba2aa6f1e6f0bc4d69eb09c9f0d0a93af56156/scripts/build-openclaw-skills.js#L19-L26) [.opencode/plugins/ponytail.mjs#L55-71](https://github.com/DietrichGebert/ponytail/blob/e3ba2aa6f1e6f0bc4d69eb09c9f0d0a93af56156/.opencode/plugins/ponytail.mjs#L55-L71)
- tests/commands.test.js enforces the command-file contract automatically: it regexes registerCommand names out of pi-extension/index.js and requires matching commands/<name>.toml and .opencode/command/<name>.md files — a registered command with no adapter files fails CI. — [tests/commands.test.js#L15-39](https://github.com/DietrichGebert/ponytail/blob/e3ba2aa6f1e6f0bc4d69eb09c9f0d0a93af56156/tests/commands.test.js#L15-L39)
- New intensity modes extend RUNTIME_MODES (runtime levels) vs VALID_MODES (adds 'review' for config) in ponytail-config.js, the SKILL.md intensity table/example bullets (auto-filtered by label), and the pi status icon map — Hermes mirrors RUNTIME_MODES/CONFIG_MODES in __init__.py. — [hooks/ponytail-config.js#L16-34](https://github.com/DietrichGebert/ponytail/blob/e3ba2aa6f1e6f0bc4d69eb09c9f0d0a93af56156/hooks/ponytail-config.js#L16-L34) [skills/ponytail/SKILL.md#L79-88](https://github.com/DietrichGebert/ponytail/blob/e3ba2aa6f1e6f0bc4d69eb09c9f0d0a93af56156/skills/ponytail/SKILL.md#L79-L88) [pi-extension/index.js#L84](https://github.com/DietrichGebert/ponytail/blob/e3ba2aa6f1e6f0bc4d69eb09c9f0d0a93af56156/pi-extension/index.js#L84) [__init__.py#L11-13](https://github.com/DietrichGebert/ponytail/blob/e3ba2aa6f1e6f0bc4d69eb09c9f0d0a93af56156/__init__.py#L11-L13)
- INDEPENDENT_MODES in ponytail-instructions.js is the extension seam for modes that don't share the ladder ruleset (currently 'review', which emits only a pointer to its skill); adding a mode there bypasses SKILL.md filtering. — [hooks/ponytail-instructions.js#L8](https://github.com/DietrichGebert/ponytail/blob/e3ba2aa6f1e6f0bc4d69eb09c9f0d0a93af56156/hooks/ponytail-instructions.js#L8) [hooks/ponytail-instructions.js#L80-82](https://github.com/DietrichGebert/ponytail/blob/e3ba2aa6f1e6f0bc4d69eb09c9f0d0a93af56156/hooks/ponytail-instructions.js#L80-L82)
- Hook output protocol for a new host is one branch in writeHookOutput() plus detection flags at the top of ponytail-runtime.js; Cursor's branch was added exactly this way ({additional_context, continue} shape). — [hooks/ponytail-runtime.js#L19-37](https://github.com/DietrichGebert/ponytail/blob/e3ba2aa6f1e6f0bc4d69eb09c9f0d0a93af56156/hooks/ponytail-runtime.js#L19-L37) [hooks/ponytail-runtime.js#L111-122](https://github.com/DietrichGebert/ponytail/blob/e3ba2aa6f1e6f0bc4d69eb09c9f0d0a93af56156/hooks/ponytail-runtime.js#L111-L122)
- Config surface extension: getQuietStartup and getHideStatus are the pattern for new user toggles — env var (PONYTAIL_*) beats config.json field, both BOM-tolerant, both exported for the pi extension. — [hooks/ponytail-config.js#L102-134](https://github.com/DietrichGebert/ponytail/blob/e3ba2aa6f1e6f0bc4d69eb09c9f0d0a93af56156/hooks/ponytail-config.js#L102-L134) [pi-extension/index.js#L8-9](https://github.com/DietrichGebert/ponytail/blob/e3ba2aa6f1e6f0bc4d69eb09c9f0d0a93af56156/pi-extension/index.js#L8-L9)
- Benchmark arms are a registry: the ARMS dict in run.py maps arm name → skill loader (file read or literal prompt); PLUGIN_ARMS selects which arms activate via --plugin-dir vs --append-system-prompt, and MODELS maps friendly names to model IDs. — [benchmarks/agentic/run.py#L35-61](https://github.com/DietrichGebert/ponytail/blob/e3ba2aa6f1e6f0bc4d69eb09c9f0d0a93af56156/benchmarks/agentic/run.py#L35-L61)
- OpenClaw packaging is generated, not hand-maintained: extend DESCRIPTIONS in build-openclaw-skills.js and rerun to emit .openclaw/skills/<name>/SKILL.md; tests/openclaw-skills.test.js fails on stale committed copies. — [scripts/build-openclaw-skills.js#L19-44](https://github.com/DietrichGebert/ponytail/blob/e3ba2aa6f1e6f0bc4d69eb09c9f0d0a93af56156/scripts/build-openclaw-skills.js#L19-L44) [tests/openclaw-skills.test.js](https://github.com/DietrichGebert/ponytail/blob/e3ba2aa6f1e6f0bc4d69eb09c9f0d0a93af56156/tests/openclaw-skills.test.js)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
