What Is the Role of hooks/ponytail-instructions.js in Building Instruction Blocks in Ponytail

The hooks/ponytail-instructions.js module serves as the central builder that constructs mode-specific textual instruction blocks (system prompts) for Ponytail-enabled Claude hooks and the Pi extension by filtering markdown skills and providing fallback templates.

In the DietrichGebert/ponytail repository, this JavaScript file functions as the primary engine for generating dynamic system prompts that guide LLM behavior. It transforms static skill documentation into tailored instruction sets, ensuring the model follows "lazy senior developer" guidelines that vary based on the selected operational intensity.

Core Responsibilities of the Instruction Builder

The module operates through a five-stage pipeline that bridges configuration and runtime execution.

Determining the Effective Ponytail Mode

First, the builder establishes which mode should govern the current session. It reads the persisted mode from environment variables, configuration files, or defaults, then normalizes that value via normalizePersistedMode imported from hooks/ponytail-config.js【/cache/repos/github.com/DietrichGebert/ponytail/main/hooks/ponytail-config.js#L20-L34】. This normalization ensures consistent handling of mode aliases and case variations before processing continues.

Selecting the Instruction Source

Depending on the resolved mode, the module chooses between two distinct content strategies:

  • Independent review mode — Returns a short static line that points to the dedicated skill (e.g., /ponytail-review) without loading external files【/cache/repos/github.com/DietrichGebert/ponytail/main/hooks/ponytail-instructions.js#L78-L83】.
  • Runtime modes (lite, full, ultra) — Loads the full skill markdown file located at skills/ponytail/SKILL.md and prepares it for mode-aware filtering.

Filtering Content by Mode

The helper function filterSkillBodyForMode executes the core transformation logic. It first strips YAML front-matter from the skill body, then iterates through each line to conditionally retain content:

  • Table rows whose header matches the current mode are preserved.
  • Worked-example bullets explicitly tagged with mode labels (e.g., - lite: "...") are kept for the specified mode only【/cache/repos/github.com/DietrichGebert/ponytail/main/hooks/ponytail-instructions.js#L11-L38】.
  • Generic rules remain visible regardless of mode, ensuring baseline guidelines persist across all configurations.

This filtering mechanism allows a single master skill file to serve multiple intensity levels without manual duplication.

Providing Graceful Fallbacks

When the primary skill file cannot be read—whether due to missing files, corruption, or path errors—the module invokes getFallbackInstructions. This function assembles a concise instructional template that still conveys the essential Ponytail ladder, rules, and mode-switch syntax【/cache/repos/github.com/DietrichGebert/ponytail/main/hooks/ponytail-instructions.js#L43-L74】. This fallback ensures the system remains functional even when the skills/ponytail/SKILL.md asset is unavailable.

Public API and Module Exports

The module exposes three utilities through its public interface【/cache/repos/github.com/DietrichGebert/ponytail/main/hooks/ponytail-instructions.js#L94-L98】:

  • filterSkillBodyForMode — Reusable line-filter logic for custom processing pipelines.
  • getFallbackInstructions — The default block generator for error scenarios.
  • getPonytailInstructions — The primary entry point used by hooks/ponytail-runtime.js and other consumers to retrieve the complete instruction string.

Practical Implementation Examples

To obtain the instruction block for the current session and pass it to an LLM client:

const { getPonytailInstructions } = require('./hooks/ponytail-instructions');

// Suppose the user has set PONYTAIL_DEFAULT_MODE=lite
const instructions = getPonytailInstructions(process.env.PONYTAIL_DEFAULT_MODE);

// The string can now be passed as the system prompt to Claude or Pi:
await claudeClient.chat({
  system: instructions,
  messages: [...],
});

For manual debugging or custom tooling, you can filter a skill file for a specific mode without invoking the full builder:

const { filterSkillBodyForMode } = require('./hooks/ponytail-instructions');
const fs = require('fs');
const SKILL_PATH = '../skills/ponytail/SKILL.md';

const skillBody = fs.readFileSync(SKILL_PATH, 'utf8');
const liteOnly = filterSkillBodyForMode(skillBody, 'lite');
console.log(liteOnly);   // shows only the lite-specific intensity table rows & examples

Integration with the Ponytail Ecosystem

The instruction builder collaborates with several components to generate the final system prompts:

  • hooks/ponytail-config.js — Resolves the default or persisted mode and provides normalization helpers used before filtering begins.
  • skills/ponytail/SKILL.md — The master markdown containing the full set of Ponytail rules, intensity tables, and worked examples that get pruned per-mode.
  • hooks/ponytail-runtime.js — The runtime glue that reads the instruction block via getPonytailInstructions and injects it into live LLM calls.

Together, these components enable dynamic, mode-specific system prompts that adapt automatically to the selected development intensity.

Summary

  • hooks/ponytail-instructions.js acts as the central builder for Ponytail system prompts.
  • It filters skills/ponytail/SKILL.md based on the active mode (lite, full, ultra, or review), retaining only relevant table rows and worked examples.
  • The module exports three utilities: filterSkillBodyForMode, getFallbackInstructions, and the primary getPonytailInstructions function.
  • Graceful degradation is handled through getFallbackInstructions, which supplies a concise template when the primary skill file is unavailable.
  • This architecture enables the "lazy senior developer" persona to shift intensity levels dynamically without maintaining separate prompt files for each mode.

Frequently Asked Questions

What does getPonytailInstructions do?

getPonytailInstructions is the public entry point that returns the complete instruction block string for a given mode. It orchestrates mode detection, file loading, and content filtering, returning a string ready for injection into LLM system prompts according to the DietrichGebert/ponytail source code.

How does the module handle missing skill files?

When skills/ponytail/SKILL.md cannot be read, the module calls getFallbackInstructions to assemble a concise instructional template. This fallback contains the Ponytail ladder, core rules, and mode-switch syntax, ensuring the system remains operational even when the primary skill asset is missing or corrupted.

What is the difference between review mode and runtime modes?

Review mode triggers a lightweight response returning a static line pointing to /ponytail-review skills, suitable for session-only analysis. Runtime modes (lite, full, ultra) trigger full markdown parsing where filterSkillBodyForMode processes the entire skill body to extract mode-specific tables and examples while preserving generic guidelines.

How does filterSkillBodyForMode determine which content to keep?

The function removes YAML front-matter first, then preserves table rows whose headers match the current mode and worked-example bullets explicitly tagged with mode labels (e.g., - lite: "..."). All generic rules outside these scoped sections are retained verbatim, ensuring baseline instructions remain visible across all operational modes.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →