How Ponytail Filters Its Ruleset Based on Intensity Mode

Ponytail filters its ruleset by parsing the Markdown source line-by-line and retaining only table rows and worked-example bullets that match the requested intensity mode (lite, full, or ultra), while preserving all other content unchanged.

In the DietrichGebert/ponytail repository, the complete skill ruleset lives in a single Markdown file (skills/ponytail/SKILL.md). When you request rules via the intensity mode parameter, Ponytail dynamically tailors the output by selecting specific sections rather than maintaining separate files for each mode.

The Filtering Pipeline

When you invoke the /ponytail command with an intensity argument, the system executes a three-stage pipeline:

  1. Mode Resolution – The requested mode is normalized against valid options (lite, full, ultra) using utilities from hooks/ponytail-config.js
  2. Content Parsing – The raw Markdown is stripped of frontmatter and split into individual lines
  3. Selective Filtering – Lines containing intensity-specific markers are conditionally retained based on the effective mode

Core Filtering Logic in hooks/ponytail-instructions.js

The heart of the system is the filterSkillBodyForMode function. This utility reads the skill body, determines the effective intensity, and applies pattern-based filtering.

Normalizing the Intensity Mode

Before filtering begins, Ponytail resolves the requested mode through normalizeMode (or normalizePersistedMode for stored preferences) defined in hooks/ponytail-config.js. If the user provides an invalid or empty mode, the system falls back to DEFAULT_MODE (typically "full").

Parsing and Filtering Markdown Content

The filterSkillBodyForMode function processes the Markdown using regex patterns to identify intensity-specific content:

// hooks/ponytail-instructions.js – filterSkillBodyForMode
function filterSkillBodyForMode(body, mode) {
  const effectiveMode = normalizeMode(mode) || DEFAULT_MODE;
  const withoutFrontmatter = String(body || '').replace(/^---[\s\S]*?---\s*/, '');

  return withoutFrontmatter
    .split(/\r?\n/)
    .filter((line) => {
      // ── Intensity table rows ──
      const tableLabel = line.match(/^\|\s*\*\*(.+?)\*\*\s*\|/);
      if (tableLabel) {
        const labelMode = normalizeMode(tableLabel[1].trim());
        if (labelMode) return labelMode === effectiveMode;
      }

      // ── Worked‑example bullets ──
      const exampleLabel = line.match(/^-\s*([^:]+):\s*"/);
      if (exampleLabel) {
        const labelMode = normalizeMode(exampleLabel[1].trim());
        if (labelMode) return labelMode === effectiveMode;
      }

      // Keep everything else unchanged
      return true;
    })
    .join('\n');
}

Table row filtering: Lines matching the pattern | **mode** | ... are parsed to extract the bolded intensity label. Only rows where the label matches the effective mode are retained. For example, | **lite** | Description... appears only when the effective mode is lite.

Worked-example filtering: Bullet points following the pattern - mode: "example text" are similarly evaluated. A line like - ultra: "Complex reasoning..." is kept only when running in ultra mode.

Preserved content: All standard Markdown elements—headings, regular bullet points, prose paragraphs, and code blocks—pass through unmodified regardless of the selected intensity.

Finally, getPonytailInstructions (also in hooks/ponytail-instructions.js) assembles the filtered body into the final instruction string returned to the user. If the skill file read fails, it generates a fallback message.

Practical Usage Examples

You can programmatically access the filtering system through the exported functions:

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

// Request "lite" mode for minimal guidance
console.log(getPonytailInstructions('lite'));
// Returns ruleset containing only "lite" table rows and examples

// Request "ultra" mode for comprehensive guidance
console.log(getPonytailInstructions('ultra'));
// Returns same base ruleset with only "ultra" specific content

// Empty or invalid input falls back to default ("full")
console.log(getPonytailInstructions(''));

For direct manipulation of skill files without the full instruction wrapper:

const fs = require('fs');
const { filterSkillBodyForMode } = require('./hooks/ponytail-instructions.js');

const skillBody = fs.readFileSync('skills/ponytail/SKILL.md', 'utf8');
const liteOnly = filterSkillBodyForMode(skillBody, 'lite');
console.log(liteOnly);   // Markdown filtered to lite-specific content only

Key Architecture Components

File Purpose
hooks/ponytail-instructions.js Implements filterSkillBodyForMode and getPonytailInstructions; contains the line-by-line filtering logic
hooks/ponytail-config.js Defines DEFAULT_MODE, normalizeMode, and normalizePersistedMode for mode resolution
skills/ponytail/SKILL.md Source of truth containing the complete, unfiltered ruleset with intensity-specific sections
ponytail-mcp/index.js Exposes the /ponytail command interface and forwards intensity arguments to the instruction builder
commands/ponytail.toml Declares the CLI command signature for switching between intensity levels

Summary

  • Ponytail maintains a single source of truth in skills/ponytail/SKILL.md rather than duplicating content across mode-specific files
  • The filterSkillBodyForMode function in hooks/ponytail-instructions.js performs line-level filtering using regex patterns to identify table rows and worked examples
  • Intensity modes (lite, full, ultra) are normalized via hooks/ponytail-config.js, with "full" serving as the typical default
  • All standard Markdown content flows through unchanged; only explicitly labeled sections receive mode-based filtering
  • The architecture separates resolution (config), filtering (instructions), and exposition (MCP interface) concerns

Frequently Asked Questions

What are the valid intensity modes in Ponytail?

Ponytail recognizes three intensity levels: lite (minimal guidance), full (standard guidance), and ultra (comprehensive, detailed guidance). These values are normalized through normalizeMode in hooks/ponytail-config.js to ensure consistent handling regardless of user input casing or whitespace.

What happens if I request an invalid or empty intensity mode?

If the requested mode is invalid, undefined, or an empty string, Ponytail falls back to the DEFAULT_MODE constant—typically set to "full". This fallback is enforced inside filterSkillBodyForMode by the expression normalizeMode(mode) || DEFAULT_MODE, ensuring the system never operates without a defined intensity baseline.

How does Ponytail identify which content belongs to which mode?

The system uses two regex patterns. For table rows, it matches | **mode** | where the bold text contains the intensity label. For worked examples, it matches lines starting with - mode: ". In both cases, the extracted label is normalized and compared against the effective mode. Matching lines are retained; non-matching labeled lines are discarded.

Can I extend Ponytail to support custom intensity modes?

Yes, though it requires modifying the configuration and source Markdown. You would need to update hooks/ponytail-config.js to recognize your new mode in normalizeMode, then add corresponding labeled sections to skills/ponytail/SKILL.md using the established patterns (| **custom** | or - custom: "example"). The filtering logic itself is agnostic to the specific mode values as long as they pass through the normalization function.

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 →