How to Scope Ponytail Subagent Injection to Specific Agent Types
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 (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, 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:
- Reading stdin: The hook reads the JSON payload that the parent process sends on stdin (lines 50-70)
- Extracting
agent_type: It parses the JSON, extracts theagent_typefield, and trims whitespace - Testing the pattern: It compares the trimmed
agent_typeagainst the compiled RegExp - Decision logic:
- If the type matches, injection proceeds via
inject() - If the type does not match, the hook exits without injecting
- If the
agent_typeis missing, JSON parsing fails, or the payload is malformed, injection proceeds (fail-open)
- If the type matches, injection proceeds via
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:
export PONYTAIL_MODE=on
ponytail run your-script.py
Inject Only into "General" Agents
Use an anchored regex for exact matching:
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:
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:
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:
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: Implements the injection hook, regex compilation, stdin reading, and scoping logic (lines 8-78)hooks/ponytail-instructions.js: Provides the actual ruleset content that gets injected into matched sub-agentshooks/ponytail-runtime.js: Reads the current Ponytail mode and manages hook output streamstests/hooks.test.js: Contains the test suite verifying matcher behavior, timeout handling, and fail-open scenarios
Summary
- Set
PONYTAIL_SUBAGENT_MATCHERto a JavaScript-compatible regex to filter sub-agents by theiragent_typefield - 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.jsin 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →