# How Ponytail Detects AI Host Environments: Copilot, Codex, and Qoder Implementation

> Discover how Ponytail detects AI environments like Copilot, Codex, and Qoder by examining unique environment variables. Learn about its implementation in ponytail-runtime.js.

- Repository: [DietrichGebert/ponytail](https://github.com/DietrichGebert/ponytail)
- Tags: how-to-guide
- Published: 2026-09-04

---

**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`](https://github.com/DietrichGebert/ponytail/blob/main/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`](https://github.com/DietrichGebert/ponytail/blob/main/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.

```javascript
// 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.

```javascript
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.

```javascript
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`](https://github.com/DietrichGebert/ponytail/blob/main/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.

```javascript
// 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 uses `PLUGIN_DATA` and Qoder uses `/tmp/qoder-${process.env.QODER_SESSION_ID}`.
- **Hook Activation Control** – In [`hooks/ponytail-activate.js`](https://github.com/DietrichGebert/ponytail/blob/main/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.

```javascript
// 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.env` variables in [`hooks/ponytail-runtime.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-runtime.js), specifically `COPILOT_PLUGIN_DATA`, `PLUGIN_DATA`, and `QODER_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`](https://github.com/DietrichGebert/ponytail/blob/main/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`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-runtime.js), which exports the boolean flags `isCopilot`, `isCodex`, and `isQoder`. Consumer hooks like [`hooks/ponytail-activate.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-activate.js) import these flags to adjust activation output, while configuration files in [`hooks/copilot-hooks.json`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/copilot-hooks.json) and [`hooks/claude-codex-hooks.json`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/claude-codex-hooks.json) provide host-specific wiring that works in conjunction with these runtime detections.