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_DATA but reveals itself through directory structure, requiring the CLAUDE_PLUGIN_ROOT fallback
  • 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_DATA or CLAUDE_PLUGIN_ROOT with path validation for .vscode/agent-plugins/
  • Codex requires PLUGIN_DATA and is checked only after Copilot exclusion
  • Qoder identifies via QODER_SESSION_ID as 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:

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 →