How Ponytail Integrates with Agents That Support Node.js Lifecycle Hooks

Ponytail injects mode-specific instructions into Claude-style agents by hooking into Node.js lifecycle events like session_start, before_agent_start, and SubagentStart, formatting output differently for Copilot, Codex, Qoder, and native Claude environments.

Ponytail is a lightweight "mode" engine from the DietrichGebert/ponytail repository that augments AI agents by intercepting standard lifecycle events in Node.js environments. When an agent supports hooks such as session_start or before_agent_start, Ponytail writes JSON payloads or additional context fields to ensure the agent receives the complete instruction set at precisely the right execution moment.

Core Integration Components

Ponytail integrates with Node.js lifecycle hooks through four specialized components that detect the host environment and format instructions accordingly.

ponytail-runtime.js

The ponytail-runtime.js module serves as the environment detector and output formatter. Located at [hooks/ponytail-runtime.js](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-runtime.js), this script identifies whether the host is Copilot, Codex, Qoder, or native Claude. It manages the mode state via the .ponytail-active flag and exposes the writeHookOutput(event, mode, context) function that all hook scripts call to emit properly formatted JSON.

ponytail-subagent.js

For task-spawned sub-agents, ponytail-subagent.js implements the SubagentStart hook. According to the source at [hooks/ponytail-subagent.js](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-subagent.js), this script reads the current mode and optionally filters injection via the PONYTAIL_SUBAGENT_MATCHER environment variable to limit instructions to specific agent_type values.

ponytail-instructions.js

The instruction builder resides in ponytail-instructions.js at [hooks/ponytail-instructions.js](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-instructions.js). This module constructs the final instruction text from skills/ponytail/SKILL.md (or a fallback) based on the active mode—lite, full, or ultra. Both hook scripts and the Pi extension call getPonytailInstructions(mode) to retrieve the payload.

pi-extension/index.js

The Pi extension at [pi-extension/index.js](https://github.com/DietrichGebert/ponytail/blob/main/pi-extension/index.js) registers Ponytail commands (/ponytail …) and listens to Pi-specific lifecycle events. It handles session_start, agent_start, agent_end, and before_agent_start events, prepending generated instructions to the systemPrompt during before_agent_start to ensure the base agent receives full context.

The Lifecycle Hook Execution Flow

Ponytail integrates with agents through a five-stage lifecycle process that ensures consistent instruction delivery across different entry points.

  1. Mode activation – Users run /ponytail <mode> via the Pi extension or set the PONYTAIL_MODE environment variable. The setMode function in ponytail-runtime.js writes the mode to .ponytail-active.

  2. Session initialization – When the agent session begins, the session_start event fires. The Pi extension reads the stored mode and updates the UI status bar.

  3. Pre-agent injection – The before_agent_start listener builds instruction text via getPonytailInstructions and returns a modified systemPrompt. This represents the primary injection path for agents exposing the before_agent_start hook.

  4. Sub-agent handling – When tasks spawn new Claude sub-agents, the ponytail-subagent.js script executes as the SubagentStart hook. It checks PONYTAIL_SUBAGENT_MATCHER if defined, then calls writeHookOutput('SubagentStart', …) to append instructions.

  5. Host formatting – The writeHookOutput function detects the host environment and emits the appropriate JSON structure, ensuring Copilot receives additionalContext, Codex receives systemMessage, and native Claude receives hookSpecificOutput.

Host-Specific Output Formats

The writeHookOutput function in ponytail-runtime.js adapts its JSON output based on the detected host environment:

  • VS Code Copilot – Writes { additionalContext: … } during SessionStart events to populate the agent's additional context field.

  • Codex – Outputs { systemMessage: "PONYTAIL:…", hookSpecificOutput: { … } } to ensure the mode is visible in the system message while preserving hook metadata.

  • Qoder – Emits only the hookSpecificOutput object containing the event name and context.

  • Native Claude – For SubagentStart events, writes a plain JSON object with hookSpecificOutput containing the instruction payload.

Because every path uses the same ponytail-instructions.js builder, the agent receives identical rules regardless of whether it starts via lifecycle event or sub-agent spawn.

Practical Implementation Examples

Activating Ponytail from a Pi Session

Use the Pi extension command interface to enable a specific mode:

// In a Pi extension or console:
await pi.runCommand('/ponytail full');   // enables "full" mode

Injecting Instructions via before_agent_start

Hook into the Pi SDK's before_agent_start event to prepend instructions to the system prompt:

pi.on('before_agent_start', async (event) => {
  // Skip if Ponytail is off
  if (!currentMode || currentMode === 'off') return;
  const base = event.systemPrompt ? `${event.systemPrompt}\n\n` : '';
  return { systemPrompt: `${base}${getPonytailInstructions(currentMode)}` };
});

Scoping Sub-Agent Injection

Limit Ponytail instructions to specific agent types using regex matching:


# Export a regex that only matches "General-purpose" agents

export PONYTAIL_SUBAGENT_MATCHER='^General-purpose$'

# Run a task that spawns a sub-agent – only matching agents receive instructions

Runtime Output Handling

The core output logic detects the host and formats JSON accordingly:

function writeHookOutput(event, mode, context = '') {
  if (isCopilot) {
    process.stdout.write(JSON.stringify(
      event === 'SessionStart' && context ? { additionalContext: context } : {}
    ));
  } else if (isCodex) {
    const out = { systemMessage: `PONYTAIL:${mode.toUpperCase()}` };
    if (context) out.hookSpecificOutput = { hookEventName: event, additionalContext: context };
    process.stdout.write(JSON.stringify(out));
  } else if (event === 'SubagentStart') {
    process.stdout.write(JSON.stringify(
      { hookSpecificOutput: { hookEventName: event, additionalContext: context } }
    ));
  } else {
    process.stdout.write(context);
  }
}

Summary

  • Ponytail augments Claude-style agents by hooking into Node.js lifecycle events including session_start, before_agent_start, and SubagentStart.
  • The integration relies on four core files: ponytail-runtime.js for host detection, ponytail-subagent.js for task-spawned agents, ponytail-instructions.js for payload generation, and pi-extension/index.js for Pi SDK event handling.
  • Host-specific formatting ensures Copilot receives additionalContext, Codex receives systemMessage, and other agents receive appropriate hookSpecificOutput JSON.
  • Mode management occurs via the .ponytail-active flag, supporting lite, full, and ultra modes that determine the instruction payload complexity.
  • Sub-agent scoping via PONYTAIL_SUBAGENT_MATCHER allows precise control over which agent types receive Ponytail instructions during task execution.

Frequently Asked Questions

Which AI agents support Ponytail's Node.js lifecycle hooks?

Ponytail supports Claude-style agents including Claude Code, Codex, Qoder, and VS Code Copilot. According to the source code in ponytail-runtime.js, each host receives specially formatted output—Copilot uses additionalContext, Codex uses systemMessage, and native Claude uses hookSpecificOutput. The [AGENTS.md](https://github.com/DietrichGebert/ponytail/blob/main/AGENTS.md) file in the repository documents the specific capabilities and hook support for each agent type.

How does Ponytail handle different execution modes?

Ponytail operates in three modes—lite, full, and ultra—defined in ponytail-instructions.js. When a user activates a mode via /ponytail <mode> or the PONYTAIL_MODE environment variable, the system stores the value in .ponytail-active. The getPonytailInstructions(mode) function then loads the appropriate instruction set from skills/ponytail/SKILL.md (or a fallback), ensuring agents receive context calibrated to the selected complexity level.

Can I prevent Ponytail from injecting instructions into certain sub-agents?

Yes. Set the PONYTAIL_SUBAGENT_MATCHER environment variable to a regex pattern that matches only the agent_type values you want to target. The ponytail-subagent.js script evaluates this regex during the SubagentStart hook and only calls writeHookOutput for matching agents. If the variable is unset, Ponytail injects instructions into all sub-agents spawned during task execution.

What is the difference between the Pi extension and the hook scripts?

The Pi extension (pi-extension/index.js) provides an interactive command interface (/ponytail …) and listens to Pi SDK lifecycle events like before_agent_start to modify the systemPrompt directly. The hook scripts (ponytail-runtime.js, ponytail-subagent.js) run as standalone Node.js processes triggered by agent-specific hooks like SubagentStart, writing JSON to stdout via writeHookOutput. Both paths use ponytail-instructions.js to generate identical instruction payloads, ensuring consistency across injection methods.

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 →