# How Ponytail Filters Its Ruleset Based on Intensity Mode

> Discover how Ponytail filters its ruleset by parsing Markdown line-by-line. Learn to retain specific content based on lite, full, or ultra intensity modes for efficient documentation.

- Repository: [DietrichGebert/ponytail](https://github.com/DietrichGebert/ponytail)
- Tags: internals
- Published: 2026-09-08

---

**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`](https://github.com/DietrichGebert/ponytail/blob/main/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`](https://github.com/DietrichGebert/ponytail/blob/main/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`](https://github.com/DietrichGebert/ponytail/blob/main/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`](https://github.com/DietrichGebert/ponytail/blob/main/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:

```javascript
// 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`](https://github.com/DietrichGebert/ponytail/blob/main/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:

```javascript
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:

```javascript
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`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-instructions.js) | Implements `filterSkillBodyForMode` and `getPonytailInstructions`; contains the line-by-line filtering logic |
| [`hooks/ponytail-config.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-config.js) | Defines `DEFAULT_MODE`, `normalizeMode`, and `normalizePersistedMode` for mode resolution |
| [`skills/ponytail/SKILL.md`](https://github.com/DietrichGebert/ponytail/blob/main/skills/ponytail/SKILL.md) | Source of truth containing the complete, unfiltered ruleset with intensity-specific sections |
| [`ponytail-mcp/index.js`](https://github.com/DietrichGebert/ponytail/blob/main/ponytail-mcp/index.js) | Exposes the `/ponytail` command interface and forwards intensity arguments to the instruction builder |
| [`commands/ponytail.toml`](https://github.com/DietrichGebert/ponytail/blob/main/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`](https://github.com/DietrichGebert/ponytail/blob/main/skills/ponytail/SKILL.md) rather than duplicating content across mode-specific files
- The `filterSkillBodyForMode` function in [`hooks/ponytail-instructions.js`](https://github.com/DietrichGebert/ponytail/blob/main/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`](https://github.com/DietrichGebert/ponytail/blob/main/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`](https://github.com/DietrichGebert/ponytail/blob/main/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`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-config.js) to recognize your new mode in `normalizeMode`, then add corresponding labeled sections to [`skills/ponytail/SKILL.md`](https://github.com/DietrichGebert/ponytail/blob/main/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.