# What Is the Role of the `ponytail-subagent.js` Hook in Claude-Code?

> Discover the role of the ponytail-subagent.js hook in Claude-Code. This hook automatically applies the Ponytail lazy-senior-dev persona to all sub-agents, enhancing task execution.

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

---

**The [`ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/ponytail-subagent.js) hook is a Claude-Code SubagentStart hook that automatically propagates the Ponytail "lazy-senior-dev" persona to every sub-agent spawned during a task.**

This hook ensures behavioral consistency across complex multi-agent workflows in the [DietrichGebert/ponytail](https://github.com/DietrichGebert/ponytail) repository. When an active Ponytail mode is detected, the hook intercepts sub-agent creation and injects the appropriate instruction set before any code execution begins.

## How the Hook Operates

The [`ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/ponytail-subagent.js) hook follows a three-phase execution pattern defined in [`hooks/ponytail-runtime.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-runtime.js) and [`hooks/ponytail-instructions.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-instructions.js):

1. **Detect the active mode** — calls `readMode()` to check `.ponytail-active` state file
2. **Build instruction bundle** — invokes `getPonytailInstructions(mode)` to filter skills by mode
3. **Emit formatted output** — uses `writeHookOutput('SubagentStart', …)` for cross-platform compatibility

The hook supports Claude platforms including Copilot, Codex, Qoder, and native Claude through platform-aware output formatting.

## Scoping Sub-Agents with PONYTAIL_SUBAGENT_MATCHER

You can restrict which sub-agents receive Ponytail instructions using the `PONYTAIL_SUBAGENT_MATCHER` environment variable.

### Unconditional Injection (Default)

When `PONYTAIL_SUBAGENT_MATCHER` is unset or contains an invalid regex, the hook injects instructions into **all** sub-agents:

```javascript
// hooks/ponytail-subagent.js L31-48
// Falls through to immediate injection without stdin reading

```

### Pattern-Based Filtering

With a valid regex, the hook reads `agent_type` from stdin and matches case-insensitively:

```bash

# Only 'explore' and 'planning' sub-agents get Ponytail instructions

export PONYTAIL_SUBAGENT_MATCHER='explore|planning'

```

The matching logic in [`hooks/ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-subagent.js) handles JSON parsing and regex evaluation at lines 50-71.

## Non-Blocking Architecture

The hook guarantees **zero impact on parent process execution** through timeout-based stdin handling:

| Scenario | Behavior |
|----------|----------|
| No matcher configured | Synchronous inject-and-exit path |
| Matcher configured | 1-second timeout with error handler |

This design prevents the Windows sub-agent JSON-swallowing issue from stalling task execution. The timeout and error handling are implemented at lines 73-78 of [`hooks/ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-subagent.js).

## Practical Usage Examples

### Activating Ponytail for All Sub-Agents

```javascript
// Activate full mode in your workflow entry point
const { setMode } = require('./hooks/ponytail-runtime');

setMode('full'); // Writes to .ponytail-active

```

All subsequent sub-agents automatically receive full-mode instructions.

### Restricting to Specific Agent Types

```bash

# Shell configuration

export PONYTAIL_SUBAGENT_MATCHER='^(explore|general)$'

```

Test the matcher manually:

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

```

Output (when matched):

```json
{
  "hookSpecificOutput": {
    "hookEventName": "SubagentStart",
    "additionalContext": "PONYTAIL MODE ACTIVE — level: full\n\n[skill instructions]"
  }
}

```

### Direct Hook Invocation

For debugging or testing:

```javascript
// Simulate sub-agent startup with explicit type
const mockStdin = JSON.stringify({ agent_type: "review" });
// With PONYTAIL_SUBAGENT_MATCHER='explore', this exits silently
// With PONYTAIL_SUBAGENT_MATCHER='review', instructions are emitted

```

## Key Source Files

Understanding the hook requires familiarity with these modules:

- **[`hooks/ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-subagent.js)** — Entry point implementing the SubagentStart hook protocol with matcher logic and timeout-based stdin reading
- **[`hooks/ponytail-runtime.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-runtime.js)** — Provides `readMode()`, `writeHookOutput()`, and platform-specific formatting (lines 42-88)
- **[`hooks/ponytail-instructions.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-instructions.js)** — Contains `getPonytailInstructions(mode)` for skill document filtering (lines 77-88)
- **[`hooks/ponytail-config.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-config.js)** — Supplies `DEFAULT_MODE` and path constants used across the hook system

## Summary

- **[`ponytail-subagent.js`](https://github.com/DietrichGebert/ponytail/blob/main/ponytail-subagent.js)** is a Claude-Code SubagentStart hook that propagates Ponytail personas to sub-agents
- **Mode detection** reads `.ponytail-active` via `readMode()` from the runtime module
- **Scoping** via `PONYTAIL_SUBAGENT_MATCHER` enables regex-based filtering of target agents
- **Non-blocking design** uses 1-second timeouts to prevent execution stalls on Windows
- **Cross-platform output** formatting supports Copilot, Codex, Qoder, and native Claude through `writeHookOutput()`

## Frequently Asked Questions

### What happens if no Ponytail mode is active?

The hook exits silently without emitting instructions. Since `readMode()` returns `null` when `.ponytail-active` does not exist, the hook produces no output and the sub-agent starts with default behavior.

### How does the regex matching work for sub-agent filtering?

The hook reads JSON from stdin containing `agent_type`, then applies the `PONYTAIL_SUBAGENT_MATCHER` regex case-insensitively. If the match succeeds, instructions are injected; otherwise the hook exits without output. Invalid regex patterns trigger unconditional injection as a safe fallback.

### Why does the hook use a 1-second timeout?

Sub-agents on Windows can consume piped JSON without proper stream handling, causing indefinite blocking. The timeout guarantees the hook always exits, with error handlers ensuring instruction injection occurs even if stdin reading fails. This prevents parent task execution delays.

### Can I use this hook outside Claude-Code?

The hook relies on Claude-Code's SubagentStart hook protocol and `stdin`/`stdout` conventions. While you could invoke it manually for testing, integration with other agent systems would require mimicking Claude's hook calling convention and JSON payload format.