# How to Scope Ponytail Subagent Injection to Specific Agent Types

> Learn to scope Ponytail subagent injection using the PONYTAIL_SUBAGENT_MATCHER environment variable and regular expressions to target specific agent types.

- Repository: [DietrichGebert/ponytail](https://github.com/DietrichGebert/ponytail)
- Tags: how-to-guide
- Published: 2026-08-30

---

**Set the `PONYTAIL_SUBAGENT_MATCHER` environment variable to a case-insensitive regular expression matching the target `agent_type` to selectively inject the Ponytail ruleset into specific sub-agents.**

The DietrichGebert/ponytail repository enables precise control over which sub-agents receive injected rulesets through regex-based filtering. While Ponytail traditionally injects its configuration into every sub-agent, issue #506 introduced the ability to scope the Ponytail subagent injection to specific agent types using the `PONYTAIL_SUBAGENT_MATCHER` environment variable.

## How the Scoping Mechanism Works

### Environment Variable Configuration

The injection behavior is governed by the `PONYTAIL_SUBAGENT_MATCHER` environment variable. According to the source code comments in [`hooks/ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-subagent.js) (lines 8-12), when this variable is unset or empty, the hook preserves backward compatibility and injects the ruleset into **all** sub-agents. This ensures existing deployments continue to function without configuration changes.

### Regex Pattern Construction

When `PONYTAIL_SUBAGENT_MATCHER` is defined, the hook attempts to build a case-insensitive `RegExp` from the provided string. As implemented at lines 31-38 of [`hooks/ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-subagent.js), the system wraps the pattern construction in a try-catch block. If you provide a malformed regular expression (such as an unclosed bracket), the hook catches the error and falls back to "no matcher" mode, effectively injecting into all sub-agents rather than crashing.

### Agent Type Matching and Fail-Open Logic

For valid regex patterns, the hook enters an inspection phase to determine whether injection should occur:

1. **Reading stdin**: The hook reads the JSON payload that the parent process sends on **stdin** (lines 50-70)
2. **Extracting `agent_type`**: It parses the JSON, extracts the `agent_type` field, and trims whitespace
3. **Testing the pattern**: It compares the trimmed `agent_type` against the compiled RegExp
4. **Decision logic**: 
   - If the type **matches**, injection proceeds via `inject()`
   - If the type **does not match**, the hook exits without injecting
   - If the `agent_type` is missing, JSON parsing fails, or the payload is malformed, injection proceeds (fail-open)

This fail-open design ensures that scoping misconfigurations never accidentally disable Ponytail functionality entirely.

### Cross-Platform Compatibility and Timeouts

The implementation maintains a synchronous injection path for Windows environments where stdin handling can be fragile. At lines 45-48, if the regex cannot be constructed, the hook immediately calls `inject()` and exits without attempting to read from stdin. Additionally, the hook implements a 1-second timeout (lines 73-78) to prevent session blocking; if stdin errors occur or the timeout expires, the system falls back to injection.

## Configuration Examples

### Inject into All Sub-Agents (Default Behavior)

When `PONYTAIL_SUBAGENT_MATCHER` is unset, every sub-agent receives the ruleset:

```bash
export PONYTAIL_MODE=on
ponytail run your-script.py

```

### Inject Only into "General" Agents

Use an anchored regex for exact matching:

```bash
export PONYTAIL_MODE=on
export PONYTAIL_SUBAGENT_MATCHER="^general$"
ponytail run your-script.py

```

### Inject into Multiple Agent Types

Match agents containing "explore" or "general" substrings:

```bash
export PONYTAIL_MODE=on
export PONYTAIL_SUBAGENT_MATCHER="explore|general"
ponytail run your-script.py

```

### Programmatic Configuration in Node.js

Set the matcher programmatically when spawning Ponytail processes:

```javascript
process.env.PONYTAIL_SUBAGENT_MATCHER = "code-review|doc-gen";
require('child_process').spawnSync('ponytail', ['run', 'my_task'], { 
  stdio: 'inherit' 
});

```

### Handling Malformed Regex Patterns

Invalid patterns safely fall back to universal injection rather than crashing:

```bash
export PONYTAIL_MODE=on
export PONYTAIL_SUBAGENT_MATCHER="*invalid["  # Illegal regex syntax

ponytail run my_task  # Injects into all sub-agents despite the error

```

## Core Implementation Files

The scoping functionality is distributed across several key files in the repository:

- **[`hooks/ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-subagent.js)**: Implements the injection hook, regex compilation, stdin reading, and scoping logic (lines 8-78)
- **[`hooks/ponytail-instructions.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-instructions.js)**: Provides the actual ruleset content that gets injected into matched sub-agents
- **[`hooks/ponytail-runtime.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-runtime.js)**: Reads the current Ponytail mode and manages hook output streams
- **[`tests/hooks.test.js`](https://github.com/DietrichGebert/ponytail/blob/main/tests/hooks.test.js)**: Contains the test suite verifying matcher behavior, timeout handling, and fail-open scenarios

## Summary

- Set `PONYTAIL_SUBAGENT_MATCHER` to a JavaScript-compatible regex to filter sub-agents by their `agent_type` field
- Patterns are case-insensitive; malformed regexes fall back to injecting all sub-agents rather than causing errors
- The system uses fail-open behavior: injection proceeds if the regex matches, the type is missing, JSON parsing fails, or timeouts occur
- A 1-second timeout prevents blocking, while a synchronous path maintains Windows compatibility
- The primary implementation resides in [`hooks/ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-subagent.js) in the DietrichGebert/ponytail repository

## Frequently Asked Questions

### What happens if I don't set the PONYTAIL_SUBAGENT_MATCHER variable?

If the environment variable is unset or empty, Ponytail maintains full backward compatibility and injects the ruleset into every sub-agent. This preserves the original behavior from versions prior to issue #506, ensuring existing scripts continue to work without modification.

### Can I use complex regular expressions with anchors and alternations?

Yes, the hook accepts any valid JavaScript regular expression syntax. You can use anchors like `^` and `$` for exact matching (e.g., `^general$`), alternation pipes for multiple types (e.g., `explore|general|review`), or character classes. The pattern is always compiled as case-insensitive.

### Why does Ponytail still inject when the agent_type is missing or the JSON is malformed?

This fail-open design prevents accidental loss of Ponytail functionality due to configuration errors, parsing issues, or unexpected stdin formats. If the hook cannot definitively determine that a sub-agent should be excluded, it defaults to injection to maintain system safety and avoid silently disabling the ruleset.

### Does this scoping feature work on Windows?

Yes, the implementation specifically accounts for Windows environments where stdin handling can be unreliable. If `PONYTAIL_SUBAGENT_MATCHER` is unset or contains a malformed regex, the hook uses a synchronous code path that calls `inject()` immediately without reading from stdin, ensuring cross-platform compatibility.