Node.js Lifecycle Hooks Used by Ponytail Plugins: The Complete Developer Guide

Ponytail plugins implement three specific Node.js lifecycle hooks—SessionStart, UserPromptSubmit, and SubagentStart—to intercept and modify AI agent behavior at critical execution points.

Ponytail is an open-source framework by DietrichGebert that extends Claude, Codex, Qoder, and Copilot through plugin hooks written in Node.js. Understanding the Node.js lifecycle hooks used by Ponytail plugins enables developers to initialize modes, parse user commands, and inject contextual rulesets across different host agents.

The Three Core Lifecycle Hooks

Ponytail recognizes exactly three lifecycle events. Each corresponds to a specific phase in the host agent's execution cycle and receives different input payloads via stdin or environment variables.

SessionStart Hook

The SessionStart hook fires at the beginning of a native Claude session—the first time the agent starts. It receives no input payload via stdin, but can access environment variables like PONYTAIL_DEFAULT_MODE. Developers use this hook to initialize the default Ponytail mode, emit status-line nudges, or write the active mode flag file.

UserPromptSubmit Hook

The UserPromptSubmit hook fires every time the user submits a prompt, including the first interaction. It receives a JSON object { prompt: string, ... } from stdin. This hook parses @ponytail ... commands to switch modes dynamically and inject the generated ruleset as additional context for the current prompt.

SubagentStart Hook

The SubagentStart hook fires when sub-agents launch—whether tool-use agents, planners, or Qoder sub-agents. It receives an optional JSON payload that may contain an agent_type field from stdin. The hook injects the current Ponytail ruleset into the sub-agent context, optionally filtered by the PONYTAIL_SUBAGENT_MATCHER environment variable.

How the Hook Runtime Works

The runtime logic resides in hooks/ponytail-runtime.js. This module exports three critical functions: readMode(), setMode(), and writeHookOutput(event, mode, context).

Hook scripts follow a consistent pattern:

  • Detect the host platform (isCopilot, isCodex, isQoder) via environment variables
  • Read the current mode from the .ponytail-active flag file using readMode()
  • Produce output through writeHookOutput() to ensure host-compatible formatting

The runtime automatically formats output as plain text or JSON (with systemMessage or hookSpecificOutput fields) based on the detected host platform.

Implementing Each Lifecycle Hook

SessionStart Hook Implementation

The hooks/ponytail-activate.js script handles initialization by reading the default mode and persisting it:

// hooks/ponytail-activate.js (SessionStart hook)
const { getDefaultMode, writeHookOutput } = require('./ponytail-config');
const { setMode } = require('./ponytail-runtime');

const mode = getDefaultMode();          // e.g. "ultra" from env or config
setMode(mode);                           // write the flag file
writeHookOutput('SessionStart', mode, `PONYTAIL MODE ACTIVE — level: ${mode}`);

UserPromptSubmit Hook Implementation

The hooks/ponytail-mode-tracker.js parses @ponytail commands from user input:

// hooks/ponytail-mode-tracker.js (UserPromptSubmit hook)
const { readMode, setMode, clearMode } = require('./ponytail-runtime');

const { prompt } = JSON.parse(fs.readFileSync(0, 'utf8'));

if (/^@ponytail\s+(\w+)/i.test(prompt)) {
  const newMode = RegExp.$1.toLowerCase();
  setMode(newMode);
  writeHookOutput('UserPromptSubmit', newMode,
    `PONYTAIL MODE CHANGED — level: ${newMode}`);
} else if (/^stop\s+ponytail$/i.test(prompt)) {
  clearMode();
  writeHookOutput('UserPromptSubmit', 'off', 'PONYTAIL MODE OFF');
}

SubagentStart Hook Implementation

The hooks/ponytail-subagent.js handles context injection with optional type matching:

// hooks/ponytail-subagent.js (SubagentStart hook)
const { readMode } = require('./ponytail-runtime');

const mode = readMode();
if (!mode) process.exit(0);   // ponytail disabled → stay silent

const envMatcher = process.env.PONYTAIL_SUBAGENT_MATCHER;
if (envMatcher) {
  const { agent_type } = JSON.parse(fs.readFileSync(0, 'utf8') || '{}');
  try {
    const re = new RegExp(envMatcher, 'i');
    if (agent_type && !re.test(agent_type)) process.exit(0);
  } catch (_) { /* invalid regex → fall back to inject everywhere */ }
}

writeHookOutput('SubagentStart', mode,
  `PONYTAIL MODE ACTIVE — level: ${mode}`);

Summary

  • Ponytail implements exactly three Node.js lifecycle hooks: SessionStart, UserPromptSubmit, and SubagentStart
  • Hook scripts reside in the hooks/ directory and leverage utilities from hooks/ponytail-runtime.js
  • SessionStart initializes modes using environment variables like PONYTAIL_DEFAULT_MODE
  • UserPromptSubmit parses JSON input from stdin to detect @ponytail commands
  • SubagentStart supports scoped injection via the PONYTAIL_SUBAGENT_MATCHER environment variable

Frequently Asked Questions

What triggers the SessionStart hook in Ponytail?

The SessionStart hook fires at the beginning of a native Claude session when the agent is first started. It receives no JSON payload via stdin, relying instead on environment variables like PONYTAIL_DEFAULT_MODE to determine the initial configuration state.

How do I parse user commands in the UserPromptSubmit hook?

Read the JSON payload from stdin using fs.readFileSync(0, 'utf8'), then access the prompt property. Use regex patterns like /^@ponytail\s+(\w+)/i to detect mode-switching commands, then call setMode() from hooks/ponytail-runtime.js to persist changes to the .ponytail-active flag file.

Can I scope Ponytail rules to specific sub-agent types?

Yes. The SubagentStart hook checks the PONYTAIL_SUBAGENT_MATCHER environment variable against the agent_type field from the JSON stdin payload. If the regex doesn't match the agent type, the script exits silently without injecting the ruleset into that specific sub-agent.

Where are the hook scripts located in the repository?

All hook implementations reside in the hooks/ directory of the DietrichGebert/ponytail repository. Key files include ponytail-runtime.js (core utilities), ponytail-activate.js (SessionStart), ponytail-mode-tracker.js (UserPromptSubmit), and ponytail-subagent.js (SubagentStart).

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 →