CodeVetter · Repo Unpack · Data flow
DietrichGebert/ponytail
Data flow is prompt-text injection: config resolution produces a mode, the mode filters the canonical SKILL.md body, and the host-specific hook output protocol (raw stdout, additionalContext JSON, or additional_context JSON) carries the ruleset into the model's context. State is minimal: a one-word flag file per host state dir, a config.json for the persisted default, and a nudge marker file. Inputs are env vars (host detection, mode default, subagent matcher) and stdin JSON (prompt text, agent_type); outputs are stdout writes the host harness parses.
- Ruleset path: skills/ponytail/SKILL.md → getPonytailInstructions(mode) → filterSkillBodyForMode strips frontmatter and drops intensity-table rows (`| **lite** |`) and worked-example bullets (`- lite: "…"`) whose label isn't the active mode → prefixed with 'PONYTAIL MODE ACTIVE — level: <mode>'.
- Mode resolution path: PONYTAIL_DEFAULT_MODE env → ~/.config/ponytail/config.json (or %APPDATA%\ponytail) defaultMode → 'full'; only RUNTIME_MODES (off/lite/full/ultra) are accepted, so 'review' can never become a default (#377).
- Session persistence: ponytail-activate.js writes the resolved mode into <stateDir>/.ponytail-active; ponytail-mode-tracker.js rewrites or deletes it on /ponytail commands; ponytail-subagent.js and the statusline scripts read it.
- Stdout protocol is host-shaped: Copilot gets {additionalContext} only on SessionStart, Codex gets systemMessage + hookSpecificOutput, Qoder gets hookSpecificOutput without systemMessage, Cursor gets {additional_context, continue}, native Claude gets raw stdout except SubagentStart which needs hookSpecificOutput JSON.
- UserPromptSubmit input arrives as piped stdin JSON; the tracker parses data.prompt, lowercases it, matches ^[/@$]ponytail or the exact deactivation phrases, then emits the mode-change header (Cursor also gets the new level's full ruleset since it has no /ponytail command that loads the skill).
- Qoder lacks SessionStart, so its UserPromptSubmit path does double duty: first prompt initializes the flag from getDefaultMode(), and every prompt re-injects the full ruleset, folding the mode-change confirmation into one JSON write.
- Subagent scoping input: when PONYTAIL_SUBAGENT_MATCHER is set, the hook reads agent_type from stdin JSON and skips injection only on a definite mismatch; missing/unparseable payloads, regex errors, and timeouts all fail open to injecting.
- Config writes: only `/ponytail default <mode>` calls writeDefaultMode, which mkdirs the config dir, merges defaultMode into any existing JSON (preserving unrelated keys), and refuses non-runtime modes; plain switches stay session-scoped.
- OpenCode data flow: command.execute.before persists the /ponytail argument to ~/.config/opencode/.ponytail-active; experimental.chat.system.transform reads it each turn and appends the filtered ruleset to the last system message (or pushes one), so a mode switch applies from the next message.
- pi data flow: session_start replays persisted 'ponytail-mode' entries via resolveSessionMode to recover the mode, before_agent_start returns {systemPrompt: base + ruleset}, and the input event listener turns the deactivation phrase into setMode('off').
- Hermes data flow: _pre_llm_call returns {'context': build_injected_context(mode)} each turn; mode lives in the module-global _current_mode set by the /ponytail command handler (process-local, not persisted); review mode injects the ponytail-review SKILL.md body instead of the filtered ladder.
- OpenClaw/ClawHub artifacts are generated: scripts/build-openclaw-skills.js copies each skills/<name>/SKILL.md body verbatim into .openclaw/skills/ with a rewritten single-line <160-char description frontmatter, so the ruleset can't drift from canonical.
- Benchmark data flow: run.py seeds a temp workspace (fixture repo or seed files + git baseline), runs `claude -p` with --plugin-dir for skill arms or --append-system-prompt for prompt arms, then scores the git diff / file stats for correctness+safety and reads cost/tokens from the CLI's JSON output.