How Ponytail Detects AI Host Environments: Copilot, Codex, and Qoder Implementation
Ponytail detects AI host environments by inspecting specific environment variables injected by each platform, prioritizing COPILOT_PLUGIN_DATA for Copilot, PLUGIN_DATA for Codex, and QODER_SESSION_ID for Qoder within hooks/ponytail-runtime.js.
The DietrichGebert/ponytail repository implements a robust AI host environment detection system that allows the plugin to adapt its behavior across multiple AI coding platforms. By analyzing environment variables set by GitHub Copilot, OpenAI Codex, Qoder, and native Claude instances, Ponytail dynamically selects state directories and adjusts hook activation without hardcoding platform-specific logic.
Environment Variable Detection Architecture
The detection mechanism relies on process.env variables that each AI host injects when loading the plugin. In hooks/ponytail-runtime.js, Ponytail evaluates these variables in a strict priority order to determine the active host environment and exports boolean flags for downstream consumption.
GitHub Copilot Detection
Ponytail identifies GitHub Copilot primarily through the COPILOT_PLUGIN_DATA environment variable. When this variable is absent—common in VS Code Copilot installations—the system falls back to checking CLAUDE_PLUGIN_ROOT to infer Copilot presence via the isVsCodeCopilotRoot() helper.
// From hooks/ponytail-runtime.js
const isCopilot = Boolean(process.env.COPILOT_PLUGIN_DATA) ||
isVsCodeCopilotRoot(process.env.CLAUDE_PLUGIN_ROOT);
OpenAI Codex Identification
For Codex environments, Ponytail checks for the PLUGIN_DATA environment variable, but only after confirming the environment is not Copilot. This sequential checking prevents false positives when multiple variables might be present in hybrid environments.
const isCodex = !isCopilot && Boolean(process.env.PLUGIN_DATA);
Qoder Session Detection
Qoder environments are identified by the presence of QODER_SESSION_ID. This check occurs only when both Copilot and Codex detection have failed, ensuring Qoder detection does not interfere with the primary platforms.
const isQoder = !isCopilot && !isCodex && Boolean(process.env.QODER_SESSION_ID);
Native Claude Fallback
If none of the host-specific variables are detected, Ponytail assumes a native Claude environment and utilizes CLAUDE_PLUGIN_ROOT to locate the Claude data directory for state persistence.
Runtime Implementation in ponytail-runtime.js
The core detection logic resides in hooks/ponytail-runtime.js. The runtime evaluates environment variables at initialization and exposes boolean flags (isCopilot, isCodex, isQoder) that other hooks import to modify behavior dynamically.
// Complete detection sequence in hooks/ponytail-runtime.js
const { isCopilot, isCodex, isQoder } = (() => {
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);
return { isCopilot, isCodex, isQoder };
})();
// Export for use by other hooks
module.exports = { isCopilot, isCodex, isQoder };
State Directory and Hook Adaptation
Once detected, the host environment flags drive adaptive behaviors throughout the codebase:
- State Directory Selection – Copilot uses
process.env.COPILOT_PLUGIN_DATA(or falls back to the Claude directory) to persist mode state, while Codex usesPLUGIN_DATAand Qoder uses/tmp/qoder-${process.env.QODER_SESSION_ID}. - Hook Activation Control – In
hooks/ponytail-activate.js, output suppression occurs for Copilot and Codex environments:const hookOutput = (isCodex || isCopilot) ? '' : 'OK'; - UI Nudge Disabling – Certain status-line hooks are disabled for Copilot because it never reads the Claude-specific status line, preventing redundant UI updates.
// Example: Conditional state directory selection
let stateDir;
if (isCopilot) {
stateDir = process.env.COPILOT_PLUGIN_DATA || getClaudeDir();
} else if (isCodex) {
stateDir = process.env.PLUGIN_DATA;
} else if (isQoder) {
stateDir = `/tmp/qoder-${process.env.QODER_SESSION_ID}`;
} else {
stateDir = getClaudeDir(); // native Claude
}
Summary
- Ponytail detects AI host environments by checking
process.envvariables inhooks/ponytail-runtime.js, specificallyCOPILOT_PLUGIN_DATA,PLUGIN_DATA, andQODER_SESSION_ID. - The detection follows a priority order: Copilot is checked first, then Codex, then Qoder, with native Claude serving as the default fallback.
- Boolean flags (
isCopilot,isCodex,isQoder) exported from the runtime module enable conditional logic in activation hooks and state management. - The system adapts storage locations and UI outputs based on the detected host, ensuring compatibility across VS Code Copilot, OpenAI Codex, Qoder, and native Claude installations.
Frequently Asked Questions
How does Ponytail distinguish between Copilot and native Claude?
Ponytail checks for COPILOT_PLUGIN_DATA first; if absent, it examines CLAUDE_PLUGIN_ROOT via the isVsCodeCopilotRoot() helper to detect VS Code Copilot specifically. If neither Copilot-specific indicator is present but CLAUDE_PLUGIN_ROOT exists, Ponytail assumes native Claude. This layered approach prevents native Claude from being misidentified as Copilot while allowing VS Code Copilot to use Claude's directory structure when needed.
What environment variable indicates a Codex environment?
OpenAI Codex sets the PLUGIN_DATA environment variable. Ponytail detects Codex by evaluating Boolean(process.env.PLUGIN_DATA), but only after confirming isCopilot is false. This sequential validation ensures that environments with overlapping variables are correctly attributed to Copilot rather than Codex.
Why does Ponytail check for Copilot before Codex?
The priority order prevents misidentification in environments where multiple AI tools might leave residual environment variables. Since COPILOT_PLUGIN_DATA is unique to GitHub's implementation and PLUGIN_DATA could theoretically appear in other contexts, checking Copilot first ensures the most specific identifier takes precedence. This ordering is hardcoded in hooks/ponytail-runtime.js to maintain consistent behavior across installations.
Where is the host detection logic implemented?
The primary detection logic resides in hooks/ponytail-runtime.js, which exports the boolean flags isCopilot, isCodex, and isQoder. Consumer hooks like hooks/ponytail-activate.js import these flags to adjust activation output, while configuration files in hooks/copilot-hooks.json and hooks/claude-codex-hooks.json provide host-specific wiring that works in conjunction with these runtime detections.
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 →