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

The 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 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 hook follows a three-phase execution pattern defined in hooks/ponytail-runtime.js and 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:

// 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:


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

export PONYTAIL_SUBAGENT_MATCHER='explore|planning'

The matching logic in 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.

Practical Usage Examples

Activating Ponytail for All Sub-Agents

// 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


# Shell configuration

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

Test the matcher manually:

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

Output (when matched):

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

Direct Hook Invocation

For debugging or testing:

// 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:

Summary

  • 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.

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 →