CodeVetter · Repo Unpack · Extension points
DietrichGebert/ponytail
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'.
- 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').
- 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.
- 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.
- 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.
- 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.
- 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.
- 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).
- 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.
- 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.
- 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.