What Does ponytail-runtime.js Do in Ponytail? Core Runtime and Mode Management
The ponytail-runtime.js module serves as Ponytail's central runtime helper, managing environment detection, persistent mode tracking, and formatted hook output across VS Code Copilot, Codex, Qoder, and native Claude environments.
The ponytail-runtime.js file is the backbone of the Ponytail project (DietrichGebert/ponytail), enabling seamless integration with multiple AI coding assistants through a unified API. This core runtime module determines the active host environment, persists operational modes between sessions, and standardizes communication protocols for hook events. Understanding how ponytail-runtime.js functions is essential for developers extending Ponytail or debugging its behavior across different IDE plugins.
Detecting the Host Environment
Ponytail supports multiple AI assistant plugins, and ponytail-runtime.js identifies the active host by inspecting specific environment variables. In hooks/ponytail-runtime.js (lines 19-23), the module checks for COPILOT_PLUGIN_DATA, CLAUDE_PLUGIN_ROOT, PLUGIN_DATA, and QODER_SESSION_ID to determine whether it is running inside VS Code Copilot, Codex, Qoder, or native Claude.
const isCopilot = Boolean(process.env.COPILOT_PLUGIN_DATA) ||
isVsCodeCopilotRoot(process.env.CLAUDE_PLUGIN_ROOT);
const isCodex = !isCopilot && Boolean(process.env.PLUGIN_DATA);
const isQoder = !isCopilot && !isCodex && Boolean(process.env.QODER_SESSION_ID);
This detection logic ensures that subsequent operations use the correct file paths and output formats for the specific environment.
Persisting the Current Ponytail Mode
The runtime enables Ponytail to maintain state between process invocations by writing the active mode to a hidden file named .ponytail-active. Located in hooks/ponytail-runtime.js (lines 33-49), the mode management functions handle three critical operations: setMode(), readMode(), and clearMode().
The storage location varies by environment:
- VS Code Copilot:
process.env.COPILOT_PLUGIN_DATAor the default Claude directory - Codex:
process.env.PLUGIN_DATA - Qoder:
~/.qoder
function setMode(mode) {
fs.mkdirSync(path.dirname(statePath), { recursive: true });
fs.writeFileSync(statePath, mode);
}
function clearMode() { try { fs.unlinkSync(statePath); } catch (e) {} }
function readMode() {
try { return fs.readFileSync(statePath, 'utf8').trim() || null; }
catch (e) { return null; }
}
These functions provide a consistent, environment-agnostic API for activating debug or test modes, reading the current state on startup, and clearing the state when Ponytail is disabled.
Emitting Environment-Specific Hook Output
When Ponytail-related events occur—such as SessionStart, SubagentStart, or UserPromptSubmit—the writeHookOutput() function formats JSON payloads tailored to the host plugin's expectations. This logic, found in hooks/ponytail-runtime.js (lines 51-90), ensures that Copilot receives additionalContext objects, while Codex receives systemMessage prefixes with optional hookSpecificOutput blocks.
function writeHookOutput(event, mode, context = '') {
if (isCopilot) {
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 and Native Claude handling omitted for brevity …
}
Qoder and native Claude receive similar but distinct formats, with Claude requiring special handling for SubagentStart events to guarantee the correct JSON shape for sub-agent communication.
Practical Implementation Examples
Developers can import ponytail-runtime.js into custom scripts to interact with Ponytail's state machine directly.
Activating a mode and notifying the host:
// example.js
const { setMode, writeHookOutput } = require('./hooks/ponytail-runtime');
// Turn Ponytail on in “debug” mode
setMode('debug');
// Notify the host that a new session has started, passing extra context
writeHookOutput('SessionStart', 'debug', 'Running debug session for feature X');
Reading the active mode inside another hook:
// some-other-hook.js
const { readMode, isCopilot } = require('./hooks/ponytail-runtime');
const current = readMode();
if (current) {
console.log(`Ponytail is active in ${current} mode`);
if (isCopilot) {
// Copilot‑specific handling …
}
}
Clearing the state when the user disables Ponytail:
const { clearMode } = require('./hooks/ponytail-runtime');
clearMode(); // Removes .ponytail-active, signalling “off”
Integration with the Ponytail Ecosystem
The ponytail-runtime.js module does not operate in isolation. It relies on companion files within the hooks/ directory to provide a complete mode-tracking solution:
hooks/ponytail-config.js: Provides helper functions likegetClaudeDirandgetConfigDirused to locate state files across environments.hooks/ponytail-mode-tracker.js: Listens for IDE events and invokessetMode()orclearMode()accordingly.hooks/ponytail-activate.js: Serves as the entry point that initializes Ponytail and wires the runtime into the host environment.hooks/ponytail-instructions.js: Supplies instruction templates that may be injected viawriteHookOutput().
Together, these components enable Ponytail to maintain a consistent operational state regardless of which AI assistant plugin is hosting the session.
Summary
- Environment Detection:
ponytail-runtime.jsinspects environment variables likeCOPILOT_PLUGIN_DATAandQODER_SESSION_IDto identify the active AI assistant plugin. - Mode Persistence: The module provides
setMode(),readMode(), andclearMode()functions to manage the.ponytail-activestate file across VS Code Copilot, Codex, and Qoder environments. - Standardized Output: The
writeHookOutput()function formats event-specific JSON payloads forSessionStart,SubagentStart, and other hook events according to each plugin's requirements. - Cross-Platform API: Located at
hooks/ponytail-runtime.js, this file offers a unified interface for mode management that abstracts away environment-specific implementation details.
Frequently Asked Questions
What is the primary purpose of ponytail-runtime.js in the Ponytail project?
The ponytail-runtime.js file serves as the central runtime helper that enables Ponytail's mode-tracking and hook output logic across different AI-assistant environments. It abstracts environment detection, state persistence, and communication protocols into a single module that other hooks can import and use consistently.
How does ponytail-runtime.js detect which AI assistant plugin is active?
The module checks for environment-specific variables in the following priority: COPILOT_PLUGIN_DATA or CLAUDE_PLUGIN_ROOT for VS Code Copilot, PLUGIN_DATA for Codex, and QODER_SESSION_ID for Qoder. Boolean checks on these variables determine the active host and configure subsequent file paths and output formats accordingly.
Where is the Ponytail mode state stored on disk?
The active mode is written to a hidden file named .ponytail-active located in environment-specific directories: the Copilot plugin data directory (or default Claude directory) for VS Code Copilot, the PLUGIN_DATA directory for Codex, and ~/.qoder for Qoder. The setMode() function creates these directories recursively if they do not exist.
Can I use ponytail-runtime.js functions in my own custom hooks?
Yes, you can require hooks/ponytail-runtime.js in custom scripts to access setMode(), readMode(), clearMode(), and writeHookOutput(). This allows custom hooks to read the current operational mode, activate specific modes like debug or test, and emit properly formatted JSON output recognized by the host AI assistant plugin.
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 →