# How Ponytail Injects Rulesets into Subagents Using ponytail-subagent.js

> Learn how Ponytail injects rulesets into subagents using ponytail-subagent.js. Discover how it bridges contexts, reads active modes, and injects instructions via the SubagentStart hook.

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

---

**Ponytail bridges parent and subagent contexts by reading the active mode from state, optionally filtering by agent type, and injecting instruction text via the `SubagentStart` hook.**

When spawning subagents in Claude, Codex, or compatible AI environments, parent processes typically lose the active ruleset context. The [`ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/ponytail-subagent.js) hook in the DietrichGebert/ponytail repository solves this by intercepting subagent creation events and automatically injecting the current Ponytail instructions, ensuring consistent behavior across agent hierarchies.

## Overview of the Injection Mechanism

The injection process centers on [`hooks/ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-subagent.js), which acts as a middleware between the parent process and the subagent initialization. Unlike manual context passing, this hook operates automatically by reading stdin for the subagent's metadata, checking the current Ponytail mode, and conditionally writing ruleset instructions to the appropriate output channel.

The architecture relies on three core components:

- **`readMode()`** from [`ponytail-runtime.js`](https://github.com/DietrichGebert/ponytail/blob/main/ponytail-runtime.js) detects the active mode (lite, full, ultra, or off)
- **`getPonytailInstructions(mode)`** from [`ponytail-instructions.js`](https://github.com/DietrichGebert/ponytail/blob/main/ponytail-instructions.js) assembles the instruction payload
- **`writeHookOutput()`** from [`ponytail-runtime.js`](https://github.com/DietrichGebert/ponytail/blob/main/ponytail-runtime.js) formats the output for Claude, Codex, Copilot, or Qoder runtimes

## Step-by-Step Injection Process

### Detecting Ponytail Mode

The hook first determines whether injection should occur by calling `readMode()` at lines 42-49 of [`hooks/ponytail-runtime.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-runtime.js). This function reads the current state from the filesystem. If the mode is unset or explicitly `"off"`, the hook exits immediately without modifying the subagent context, as shown at lines 18-21 of [`hooks/ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-subagent.js).

```javascript
const { readMode } = require('./ponytail-runtime');

const mode = readMode();
if (!mode || mode === 'off') {
  process.exit(0);
}

```

This early exit prevents unnecessary processing and ensures dormant configurations do not interfere with standard subagent operations.

### Building the Ruleset Instructions

When a valid mode is detected, the hook calls `getPonytailInstructions(mode)` from [`hooks/ponytail-instructions.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-instructions.js) (lines 77-92). This function aggregates the appropriate ruleset text based on the selected mode—lite, full, or ultra—returning a complete instruction string ready for injection.

The specific content varies by mode, but the delivery mechanism remains consistent: the assembled text becomes the `additionalContext` payload attached to the subagent's initialization event.

### Optional Agent Type Filtering

Before injection, the hook optionally validates the subagent type against the `PONYTAIL_SUBAGENT_MATCHER` environment variable. At lines 33-36 of [`hooks/ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-subagent.js), this variable is compiled into a case-insensitive RegExp.

The hook then reads the JSON payload from stdin (lines 53-67) to extract the `agent_type` field:

```javascript
// Simplified logic from ponytail-subagent.js lines 53-67
const payload = JSON.parse(stdinData);
const agentType = payload.agent_type;

if (matcher && !matcher.test(agentType)) {
  process.exit(0); // Silently skip non-matching agents
}

```

**Fail-open behavior** ensures reliability: if stdin parsing fails, the payload is missing, or the read times out, the hook defaults to injecting the ruleset rather than withholding it (lines 60-66). This prevents accidental loss of critical instructions due to malformed input.

### Writing Output to the Subagent Context

The `inject()` helper function (lines 24-26) calls `writeHookOutput` from [`hooks/ponytail-runtime.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-runtime.js). This utility handles runtime-specific formatting:

- **Claude**: Emits JSON with `hookSpecificOutput` and `additionalContext` for the `SubagentStart` event (lines 84-88 of [`ponytail-runtime.js`](https://github.com/DietrichGebert/ponytail/blob/main/ponytail-runtime.js))
- **Codex, Copilot, Qoder**: Uses platform-specific output formats (lines 51-80 of [`ponytail-runtime.js`](https://github.com/DietrichGebert/ponytail/blob/main/ponytail-runtime.js))

```javascript
const { writeHookOutput } = require('./ponytail-runtime');

function inject() {
  writeHookOutput('SubagentStart', mode, getPonytailInstructions(mode));
}

```

## Configuration and Environment Variables

Control the injection behavior through environment variables before launching the parent process:

- **`PONYTAIL_MODE`**: Sets the operating mode (lite, full, ultra, off). When off or unset, injection is skipped.
- **`PONYTAIL_SUBAGENT_MATCHER`**: Regex pattern to filter which subagent types receive the ruleset. If omitted, all subagents get the injection.

The matcher supports standard JavaScript regular expression syntax without delimiters. For example, setting it to `explore|general` limits injection to agents with those specific types.

## Practical Examples

Enable ruleset injection for all subagents:

```bash
export PONYTAIL_MODE=full

# No matcher defined → every subagent receives the full ruleset

```

Restrict injection to specific agent types:

```bash
export PONYTAIL_MODE=ultra
export PONYTAIL_SUBAGENT_MATCHER="explore|general"

# Only "explore" or "general" type subagents get the instructions

```

Simulate a subagent launch where the matcher blocks injection:

```bash
echo '{"agent_type":"code"}' | node hooks/ponytail-subagent.js

# Matcher does not match "code" → exits without output

```

Simulate a successful injection:

```bash
echo '{"agent_type":"explore"}' | node hooks/ponytail-subagent.js

# Outputs the Ponytail instruction JSON to stdout

```

## Summary

- **[`ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/ponytail-subagent.js)** automatically propagates Ponytail rulesets from parent to subagent contexts
- **Mode detection** via `readMode()` prevents injection when Ponytail is disabled
- **Optional filtering** using `PONYTAIL_SUBAGENT_MATCHER` allows selective injection based on `agent_type`
- **Fail-open design** ensures parsing errors or timeouts result in injection rather than silent omission
- **Non-blocking execution** uses a 1-second timeout and error listeners to prevent hanging the parent process
- **Multi-platform support** through `writeHookOutput()` handles Claude, Codex, Copilot, and Qoder output formats

## Frequently Asked Questions

### What happens if Ponytail mode is set to "off"?

When `readMode()` returns `"off"` or an undefined value, [`ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/ponytail-subagent.js) exits immediately at lines 18-21 without reading stdin or producing output. The subagent launches normally without any Ponytail instructions injected into its context.

### How does the PONYTAIL_SUBAGENT_MATCHER work?

The environment variable is converted to a case-insensitive regular expression at lines 33-36. The hook parses the subagent's `agent_type` from the JSON payload on stdin and tests it against this regex. If the match fails, the process exits silently without injection. If the variable is unset, the hook bypasses this check and injects all subagents.

### What platforms are supported by ponytail-subagent.js?

The hook supports Claude (native), Codex, GitHub Copilot, and Qoder runtimes. The `writeHookOutput` function in [`hooks/ponytail-runtime.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-runtime.js) (lines 51-88) detects the target platform and formats the JSON payload accordingly, ensuring compatibility across different AI coding environments.

### Is the injection process blocking or non-blocking?

The implementation is explicitly non-blocking. At lines 73-78, [`ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/ponytail-subagent.js) sets a 1-second timeout on stdin reading and attaches error listeners. This guarantees the hook exits promptly even if the parent process provides no payload, preventing delays in subagent initialization.