How Ponytail Handles Subagent Scoping: Regex-Based Filtering in hooks/ponytail-subagent.js

Ponytail filters which child processes receive injected rule-sets by comparing the PONYTAIL_AGENT_TYPE environment variable against a configurable regex matcher defined in hooks/ponytail-subagent.js.

The DietrichGebert/ponytail repository implements a hierarchical agent architecture where parent agents spawn sub-agents as child processes. To prevent blanket application of constraints across every subprocess, Ponytail provides a granular scoping mechanism that inspects environment variables at spawn time to determine rule-set inheritance.

The Scoping Implementation in hooks/ponytail-subagent.js

All subagent scoping logic resides in the hook script hooks/ponytail-subagent.js. This script executes for every subprocess created while Ponytail mode is active, implementing a conditional injection strategy that respects explicit agent type declarations.

Execution Flow and Environment Inspection

The hook operates through a six-step validation process:

  1. Hook Activation: The script triggers for each new subprocess spawned under active Ponytail supervision.
  2. Type Extraction: It reads the PONYTAIL_AGENT_TYPE environment variable from the child process context.
  3. Regex Evaluation: The type value is tested against the PONYTAIL_SUBAGENT_MATCHER regular expression.
  4. Match Verification: Only sub-agents whose type matches the regex receive the injected parent rule-set.
  5. Rule Injection: Upon match confirmation, the parent’s configuration is written into the child’s environment variables.
  6. Fallback Handling: If the matcher is unset, the hook bypasses filtering and injects rules into every sub-agent.

Default Matcher Behavior

The default configuration in hooks/ponytail-subagent.js uses the regex ^general$, restricting rule injection to agents that explicitly declare PONYTAIL_AGENT_TYPE=general. This prevents unintended constraint inheritance in specialized sub-agents that do not identify as general-purpose workers.

Configuring Subagent Scoping Patterns

You control subagent scoping through two environment variables: PONYTAIL_SUBAGENT_MATCHER for the parent’s filtering criteria, and PONYTAIL_AGENT_TYPE for the child’s self-identification.

Targeted Injection by Agent Type

To limit rule propagation to specific agent families, export a targeted regex before invoking the parent agent:

export PONYTAIL_SUBAGENT_MATCHER='^node$'

ponytail run my-script.js

When the child process sets PONYTAIL_AGENT_TYPE=node, the hook in hooks/ponytail-subagent.js detects the match and injects the parent’s rule-set. Non-matching types receive no injection.

Universal Injection for Backward Compatibility

For workflows requiring legacy behavior or simple scripts without type declarations, unset the matcher to restore universal injection:

unset PONYTAIL_SUBAGENT_MATCHER

ponytail run complex-workflow.sh

This configuration forces hooks/ponytail-subagent.js to inject the rule-set into every spawned sub-agent regardless of PONYTAIL_AGENT_TYPE values.

Validation and Testing

The scoping mechanism is validated by the test suite in tests/hooks.test.js. These tests verify that PONYTAIL_SUBAGENT_MATCHER correctly prevents rule-set leakage to non-matching agent types while confirming that the fallback "inject-into-all" behavior functions when the matcher is undefined. The test file specifically covers edge cases including empty matchers, partial regex matches, and multiple nested subagent spawning scenarios.

Summary

  • hooks/ponytail-subagent.js implements the core scoping logic that filters rule-set injection based on environment variables.
  • PONYTAIL_SUBAGENT_MATCHER accepts a regular expression that determines which sub-agent types receive the parent configuration.
  • Default regex ^general$ restricts injection to agents explicitly typed as "general" unless configured otherwise.
  • Unset matcher fallback triggers historic behavior, injecting rules into every sub-agent for backward compatibility.
  • tests/hooks.test.js provides comprehensive validation of matcher behavior and injection boundaries.

Frequently Asked Questions

What file contains the subagent scoping logic in Ponytail?

The scoping implementation resides in hooks/ponytail-subagent.js within the DietrichGebert/ponytail repository. This hook script executes for every subprocess spawned under Ponytail supervision and evaluates PONYTAIL_AGENT_TYPE against the configured regex matcher to determine injection eligibility.

What is the default regex pattern for subagent matching?

The default PONYTAIL_SUBAGENT_MATCHER value is ^general$, meaning only sub-agents that explicitly set PONYTAIL_AGENT_TYPE=general receive the injected rule-set. This conservative default prevents accidental constraint application to specialized or untyped child processes.

How do I force Ponytail to inject rules into every sub-agent?

Unset or empty the PONYTAIL_SUBAGENT_MATCHER environment variable before running the parent agent. When this variable is missing or empty, hooks/ponytail-subagent.js falls back to the historic behavior and injects the parent rule-set into every spawned sub-agent regardless of their declared type.

Can a sub-agent opt out of receiving the parent's rule-set?

Yes. A sub-agent opts out by setting a PONYTAIL_AGENT_TYPE value that does not match the parent’s PONYTAIL_SUBAGENT_MATCHER regex. For example, if the matcher is ^python$, a sub-agent setting PONYTAIL_AGENT_TYPE=shell will not receive the injected constraints.

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 →