What Are Node.js Lifecycle Hooks in Ponytail? A Complete Technical Guide

Node.js lifecycle hooks in Ponytail are event-driven scripts that execute at specific points in an LLM session—SessionStart, SubagentStart, and UserPromptSubmit—to persist mode configurations, inject rule sets into sub-agents, and communicate status updates to the host interface.

Ponytail is an open-source plugin for the Claude AI ecosystem that utilizes Node.js lifecycle hooks to maintain contextual awareness across conversation boundaries. According to the DietrichGebert/ponytail repository, these hooks enable cross-platform mode persistence and dynamic rule injection without introducing external dependencies. The hook system allows Ponytail to function seamlessly across Claude, Codex, Copilot, and Qoder environments by responding to standardized lifecycle events defined in the manifest.

The Three Node.js Lifecycle Hooks Defined by Ponytail

The plugin architecture registers three distinct hooks in the hooks/ directory, each triggered by a specific event in the LLM session lifecycle.

SessionStart Hook

The SessionStart hook executes when a new Claude session begins or resumes after a clear/compact operation. It runs hooks/ponytail-activate.js to load the current Ponytail mode from the filesystem and emit an initial status message. This ensures the UI immediately displays whether Ponytail is active and which mode (off, lite, full, or ultra) is currently selected.

SubagentStart Hook

When a sub-agent spawns—such as a tool-driven agent invoked for specific tasks—the SubagentStart hook fires. It executes hooks/ponytail-subagent.js to inject the active mode's rule set into the sub-agent's environment. This inheritance mechanism guarantees that child agents respect the parent's contextual constraints and coding standards throughout the conversation tree.

UserPromptSubmit Hook

The UserPromptSubmit hook runs on every user prompt submission via hooks/ponytail-mode-tracker.js. This script parses the incoming prompt for /ponytail commands (e.g., /ponytail full), updates the .ponytail-active flag file to persist the new mode, and returns a JSON payload that displays the mode change in the host's status line.

Hook Registration and Manifest Configuration

All three Node.js lifecycle hooks are declared in the central manifest file hooks/claude-codex-hooks.json. This JSON configuration maps lifecycle events to executable Node.js commands using environment-variable interpolation (${CLAUDE_PLUGIN_ROOT}) to ensure cross-platform path resolution.

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup|resume|clear|compact",
        "hooks": [
          {
            "type": "command",
            "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/ponytail-activate.js\"",
            "timeout": 5,
            "statusMessage": "Loading ponytail mode..."
          }
        ]
      }
    ],
    "SubagentStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/ponytail-subagent.js\"",
            "timeout": 5,
            "statusMessage": "Loading ponytail mode..."
          }
        ]
      }
    ],
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/ponytail-mode-tracker.js\"",
            "timeout": 5,
            "statusMessage": "Tracking ponytail mode..."
          }
        ]
      }
    ]
  }
}

The manifest supports timeout constraints (set to 5 seconds) and status messages to prevent blocking the LLM session if a hook fails to complete.

Cross-Platform Output Handling with ponytail-runtime.js

The hooks/ponytail-runtime.js module provides the writeHookOutput() utility that abstracts host-specific output formatting. This function detects whether the plugin runs under Copilot, Codex, Qoder, or native Claude, then emits the appropriate JSON structure:

  • Copilot: Emits additionalContext objects only during SessionStart
  • Codex: Uses systemMessage fields with optional hookSpecificOutput payloads
  • Qoder: Structures output specifically for Qoder's interface expectations
  • Native Claude: Falls back to raw text output when JSON structures aren't supported

This abstraction layer ensures that Node.js lifecycle hooks produce compatible output across all supported AI agents without requiring host-specific logic in individual hook scripts.

Practical Implementation Examples

Parsing Mode Commands in UserPromptSubmit

The hooks/ponytail-mode-tracker.js file implements the command parsing logic that detects and processes /ponytail instructions:

if (/^[/@$]ponytail/.test(prompt)) {
  const parts = prompt.split(/\s+/);
  const cmd = parts[0].replace(/^[@$]/, '/');
  const arg = parts[1] || '';

  if (cmd === '/ponytail') {
    // handle /ponytail off|lite|full|ultra etc.
    setMode(mode);               // writes .ponytail-active
    writeHookOutput('UserPromptSubmit', mode,
      `PONYTAIL MODE CHANGED — level: ${mode}`);
  }
}

When a user submits /ponytail full, this code updates the mode flag and emits a structured response that the host renders as a status line update.

Output Routing Logic

The writeHookOutput() function in hooks/ponytail-runtime.js handles the multi-host compatibility:

function writeHookOutput(event, mode, context = '') {
  if (isCopilot) {
    // Copilot only cares about additionalContext on SessionStart
    process.stdout.write(JSON.stringify(
      event === 'SessionStart' && context ? { additionalContext: context } : {}));
    return;
  }
  if (isCodex) {
    const output = { systemMessage: `PONYTAIL:${mode.toUpperCase()}` };
    if (context) output.hookSpecificOutput = { hookEventName: event,
                                                additionalContext: context };
    process.stdout.write(JSON.stringify(output));
    return;
  }
  // Qoder & native Claude handling omitted for brevity
}

This utility ensures that Node.js lifecycle hooks communicate effectively regardless of which AI agent hosts the plugin.

Summary

Node.js lifecycle hooks enable Ponytail to maintain stateful mode management across serverless LLM environments:

Frequently Asked Questions

What triggers the Node.js lifecycle hooks in Ponytail?

The hooks trigger in response to specific events in the LLM session lifecycle managed by the host agent. SessionStart fires when a new conversation begins or resumes; SubagentStart activates when spawning tool-driven child agents; UserPromptSubmit executes on every prompt submission. These events are defined in the claude-codex-hooks.json manifest file.

How does Ponytail persist mode state across sessions?

Ponytail writes the current mode to a flag file named .ponytail-active in the project directory. The SessionStart hook (ponytail-activate.js) reads this file during initialization to restore the previous mode, while the UserPromptSubmit hook (ponytail-mode-tracker.js) updates it whenever users execute /ponytail commands.

Are Ponytail's lifecycle hooks compatible with all AI agents?

Yes, the hooks are designed for cross-platform compatibility. The ponytail-runtime.js module detects the host environment—whether Claude, Codex, Copilot, or Qoder—and emits the appropriately formatted JSON output. The plain Node.js scripts require no external dependencies and run on both POSIX and Windows systems.

Where are the hook scripts located in the repository?

All lifecycle hook scripts reside in the hooks/ directory at the repository root. Key files include claude-codex-hooks.json (the manifest), ponytail-runtime.js (shared utilities), ponytail-mode-tracker.js (UserPromptSubmit logic), ponytail-activate.js (SessionStart logic), and ponytail-subagent.js (SubagentStart logic).

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 →