How Ponytail Detects Copilot, Codex, and Qoder Host Environments
Ponytail detects its host environment by inspecting environment variables injected at session startup, using unique markers like COPILOT_PLUGIN_DATA, PLUGIN_DATA, and QODER_SESSION_ID to distinguish between VS Code Copilot, OpenAI Codex, and Qoder.
Ponytail, an open-source runtime hook system for Claude-based workflows, must adapt its behavior depending on which AI coding assistant is hosting it. The detection logic in hooks/ponytail-runtime.js identifies the host through a cascading check of well-known environment variables, each exclusive to a specific platform.
Environment Variable Detection Strategy
Ponytail's host detection relies on three boolean flags evaluated at module load time. The order of checks matters: Copilot is tested first due to its dual detection paths, followed by Codex and Qoder as mutually exclusive fallbacks.
VS Code Copilot Detection
Copilot presents two possible signatures. The primary marker is COPILOT_PLUGIN_DATA, set by the Copilot plugin directly. When this is absent—such as during VS Code execution—Ponytail falls back to CLAUDE_PLUGIN_ROOT and validates it through the isVsCodeCopilotRoot helper:
const isCopilot = Boolean(process.env.COPILOT_PLUGIN_DATA) || isVsCodeCopilotRoot(process.env.CLAUDE_PLUGIN_ROOT);
The helper function, located at lines 13-17 in hooks/ponytail-runtime.js, verifies that the path contains agent-plugins and the case-insensitive substring .vscode, matching Copilot's characteristic plugin directory layout.
OpenAI Codex Detection
Codex exposes a single unique marker: PLUGIN_DATA. Ponytail checks this only after confirming Copilot is not present, preventing false overlap:
const isCodex = !isCopilot && Boolean(process.env.PLUGIN_DATA);
This check appears at line 21 of hooks/ponytail-runtime.js.
Qoder Detection
Qoder supplies QODER_SESSION_ID, tested as the final branch when neither Copilot nor Codex conditions are met:
const isQoder = !isCopilot && !isCodex && Boolean(process.env.QODER_SESSION_ID);
Found at line 22, this ensures Qoder is identified only when its exclusive session identifier is present.
Why the Detection Order Matters
The cascading structure prevents misidentification where multiple environment variables might coexist.
- Copilot's VS Code variant lacks
COPILOT_PLUGIN_DATAbut reveals itself through directory structure, requiring theCLAUDE_PLUGIN_ROOTfallback - Codex and Qoder use non-overlapping variables, but Ponytail explicitly excludes prior matches to guarantee unambiguous detection
- Native Claude is assumed when no host-specific markers exist, triggering fallback to
getClaudeDir()for state directory resolution
Practical Usage in Runtime Code
The detected flags drive all subsequent behavior. Below, Ponytail's runtime helpers adapt output formatting to the host environment:
// Example: import the runtime helpers
const {
isCopilot,
isCodex,
isQoder,
writeHookOutput,
} = require('./hooks/ponytail-runtime');
// Show which host has been detected
if (isCopilot) {
console.log('Running inside VS Code Copilot');
} else if (isCodex) {
console.log('Running inside OpenAI Codex');
} else if (isQoder) {
console.log('Running inside Qoder');
} else {
console.log('Running under native Claude');
}
// Emit a hook-specific payload – Ponytail automatically picks the format
writeHookOutput('SessionStart', 'on', 'Welcome to Ponytail!');
The writeHookOutput function serializes payloads appropriately: JSON for Copilot, Codex, and Qoder; plain text for native Claude.
Key Source Files
| File | Responsibility |
|---|---|
hooks/ponytail-runtime.js |
Core host detection, state-directory handling, and host-specific hook output formatting |
hooks/ponytail-activate.js |
Consumes detection flags to conditionally emit hook output during activation |
hooks/ponytail-config.js |
Provides getClaudeDir() and getConfigDir() for native Claude fallback scenarios |
Summary
- Copilot detection uses
COPILOT_PLUGIN_DATAorCLAUDE_PLUGIN_ROOTwith path validation for.vscode/agent-plugins/ - Codex requires
PLUGIN_DATAand is checked only after Copilot exclusion - Qoder identifies via
QODER_SESSION_IDas the final fallback - All detection happens once at module load in
hooks/ponytail-runtime.js, with resulting booleans driving runtime behavior - Unsupported environments default to native Claude configuration
Frequently Asked Questions
What happens if multiple environment variables are set?
Ponytail's ordered checks prioritize Copilot first, then Codex, then Qoder. The first matching condition wins, preventing ambiguous states. This design assumes hosts are mutually exclusive in practice.
Can the detection be overridden manually?
The source code in hooks/ponytail-runtime.js does not expose manual override mechanisms. Detection is strictly environment-driven based on process.env inspection at require-time.
Why does Copilot need two different detection methods?
The Copilot plugin sets COPILOT_PLUGIN_DATA directly, but VS Code's internal Claude integration uses CLAUDE_PLUGIN_ROOT instead. The isVsCodeCopilotRoot helper captures this alternate path pattern without breaking standard detection.
How does Ponytail handle unknown or future hosts?
Any environment lacking COPILOT_PLUGIN_DATA, CLAUDE_PLUGIN_ROOT, PLUGIN_DATA, or QODER_SESSION_ID falls through to native Claude mode, using getClaudeDir() from hooks/ponytail-config.js for state management.
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 →