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:
- Detect the active mode — calls
readMode()to check.ponytail-activestate file - Build instruction bundle — invokes
getPonytailInstructions(mode)to filter skills by mode - 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:
hooks/ponytail-subagent.js— Entry point implementing the SubagentStart hook protocol with matcher logic and timeout-based stdin readinghooks/ponytail-runtime.js— ProvidesreadMode(),writeHookOutput(), and platform-specific formatting (lines 42-88)hooks/ponytail-instructions.js— ContainsgetPonytailInstructions(mode)for skill document filtering (lines 77-88)hooks/ponytail-config.js— SuppliesDEFAULT_MODEand path constants used across the hook system
Summary
ponytail-subagent.jsis a Claude-Code SubagentStart hook that propagates Ponytail personas to sub-agents- Mode detection reads
.ponytail-activeviareadMode()from the runtime module - Scoping via
PONYTAIL_SUBAGENT_MATCHERenables 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →