# How to Use PONYTAIL_SUBAGENT_MATCHER to Scope Ruleset Injection in Ponytail

> Scope Ponytail ruleset injection using PONYTAIL_SUBAGENT_MATCHER. Limit injection to sub-agents matching your regex pattern for precise control.

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

---

**Set the `PONYTAIL_SUBAGENT_MATCHER` environment variable to a case‑insensitive regular expression to limit Ponytail’s ruleset injection to sub‑agents whose `agent_type` matches the pattern.**

The Ponytail plugin injects instruction rulesets into sub‑agents spawned by AI hosts like Claude, Codex, and Qoder. By default, this injection applies to every sub‑agent unconditionally, but the `PONYTAIL_SUBAGENT_MATCHER` environment variable lets you restrict injection to specific agent types. This guide covers the matching implementation in [`hooks/ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-subagent.js) and practical configuration examples for supported hosts.

## How PONYTAIL_SUBAGENT_MATCHER Works

### Regex Compilation and Validation

At startup, the hook reads `process.env.PONYTAIL_SUBAGENT_MATCHER` and compiles it with the **case‑insensitive** flag (`i`). In [`hooks/ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-subagent.js) (lines 33‑38), the code attempts to instantiate a `RegExp` from the variable. If the pattern is malformed, the error is caught silently and the hook falls back to the default behavior: injecting into every sub‑agent.

### Runtime Matching Logic

When the host spawns a sub‑agent, the hook receives a JSON payload via `stdin` containing the `agent_type` field (lines 60‑66). The logic follows these steps:

- **Parse the payload.** If JSON parsing fails or the `agent_type` key is missing, the hook proceeds with injection (fail‑open) to avoid blocking the sub‑agent.
- **Test the regex.** If `agent_type` exists and does **not** satisfy the compiled regex, the hook exits silently with `process.exit(0)` (lines 66‑70).
- **Inject the ruleset.** If the regex matches, the `inject()` function is called, sending the `SubagentStart` payload containing Ponytail’s instructions.

### Default Behavior When Unset

When `PONYTAIL_SUBAGENT_MATCHER` is unset or contains an invalid pattern, the hook skips the `stdin` read entirely and calls `inject()` synchronously (lines 45‑48). This preserves backward compatibility with Windows hosts and ensures the ruleset is always injected when no matcher is configured.

## Configuration Examples

### Bash Wrapper for Claude or Codex

Export the matcher before launching your AI host to restrict injection to agents with types containing "general" or "plan":

```bash
export PONYTAIL_SUBAGENT_MATCHER='general|plan'

# Launch the host—Ponytail will only inject into matching sub-agents

claude

```

Use anchors for exact matches. The pattern `^general$` matches only the agent type `general`, while `general` (unanchored) matches `general‑purpose`, `general‑coding`, etc.

### Node.js Test Script

Validate your regex programmatically by simulating the hook’s stdin payload:

```javascript
const { spawnSync } = require('child_process');
const path = require('path');

const env = {
  ...process.env,
  PONYTAIL_SUBAGENT_MATCHER: '^general$'  // Exact match only
};

const result = spawnSync(
  process.execPath,
  [path.join(__dirname, 'hooks', 'ponytail-subagent.js')],
  {
    env,
    input: JSON.stringify({ agent_type: 'general' }), // Matching payload
    encoding: 'utf8',
  }
);

console.log(result.stdout);   // → Contains SubagentStart JSON

```

Change `agent_type` to `plan` in the input to verify the hook exits silently for non‑matching types.

### Qoder Hook Configuration

Add the environment variable to Qoder’s hook configuration in [`hooks/qoder-hooks.json`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/qoder-hooks.json):

```json
{
  "UserPromptSubmit": {
    "command": "node $HOME/.qoder/plugins/ponytail/hooks/ponytail-subagent.js",
    "env": {
      "PONYTAIL_SUBAGENT_MATCHER": "explore|general"
    }
  }
}

```

With this configuration, Qoder spawns sub‑agents that receive Ponytail’s ruleset **only** if their `agent_type` contains "explore" or "general".

## Regex Pattern Guidelines

The matcher is **unanchored** and **case‑insensitive** by design:

- `explore|general` matches any `agent_type` that contains either substring regardless of case.
- `^general$` forces an exact match, excluding variants like `general‑coding`.
- Invalid regex patterns (e.g., unclosed parentheses) cause the hook to ignore the variable and inject everywhere, preventing accidental lockouts.

Because the implementation is fail‑open, missing `agent_type` fields in the platform payload result in unconditional injection. This ensures Ponytail’s instructions remain visible even if the host platform changes its message format.

## Summary

- **Set `PONYTAIL_SUBAGENT_MATCHER`** to a JavaScript‑compatible regular expression to filter which sub‑agents receive Ponytail’s ruleset.
- **Case‑insensitive matching** is enforced automatically; anchoring (`^` and `$`) is optional for exact matches.
- **Fail‑open behavior** protects against misconfiguration: invalid regexes or missing `agent_type` fields result in unconditional injection.
- **Source location:** The core logic resides in [`hooks/ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-subagent.js) (lines 33‑70), with validation tests in [`tests/hooks.test.js`](https://github.com/DietrichGebert/ponytail/blob/main/tests/hooks.test.js).

## Frequently Asked Questions

### What happens if PONYTAIL_SUBAGENT_MATCHER is unset?

When the environment variable is undefined, the hook skips regex compilation and stdin processing entirely, injecting the ruleset into every sub‑agent synchronously. This matches the original Windows‑compatible behavior.

### How do I match exact agent types only?

Use start‑of‑string (`^`) and end‑of‑string (`$`) anchors in your pattern. For example, `^general$` matches only the exact string `general`, whereas `general` (unanchored) also matches `general‑purpose` or `my‑general‑agent`.

### What happens if the regex is malformed?

If `PONYTAIL_SUBAGENT_MATCHER` contains invalid syntax (e.g., `[a-z`), the hook catches the `SyntaxError` during `RegExp` construction and falls back to injecting into every sub‑agent. This prevents a bad regex from accidentally blocking all sub‑agent activity.

### Does this work with all AI hosts?

Yes. The matcher operates inside [`hooks/ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-subagent.js), which is invoked by the host’s sub‑agent spawn hook. As long as the host conforms to the standard payload format (JSON with `agent_type` on stdin), the filtering works identically for Claude, Codex, Qoder, and other supported platforms.