# How Ponytail Activates on Different Platforms: VS Code Copilot, Codex, and Native Claude Support

> Learn how Ponytail activates on VS Code Copilot, Codex, and native Claude. Discover its SessionStart hook and platform-specific flag file system for seamless integration.

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

---

**Ponytail activates via a SessionStart hook that detects the runtime environment, writes a platform-specific flag file, and emits tailored instruction sets for VS Code Copilot, Claude Codex, Qoder, or native Claude.**

The DietrichGebert/ponytail repository implements a unified activation system that automatically adapts to whichever Claude-based client is running. When a session begins, the [`hooks/ponytail-activate.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-activate.js) hook executes a three-phase pipeline: platform detection, state persistence, and context emission. This ensures consistent behavior across macOS, Linux, and Windows while respecting each client's unique integration constraints.

## Platform Detection in ponytail-runtime.js

All platform identification logic resides in [`hooks/ponytail-runtime.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-runtime.js). The hook distinguishes between four execution environments by inspecting specific environment variables and path patterns.

### VS Code Copilot Detection

The hook identifies VS Code Copilot by checking if `process.env.COPILOT_PLUGIN_DATA` is set **or** if `CLAUDE_PLUGIN_ROOT` contains both `agent-plugins` and `.vscode` substrings. This logic is encapsulated in the `isCopilot` boolean flag defined at lines 19-21 of [`hooks/ponytail-runtime.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-runtime.js).

### Claude Codex Detection

Codex sessions are detected when `process.env.PLUGIN_DATA` exists **and** the Copilot condition is false. This `isCodex` check at lines 21-22 ensures the hook does not misclassify Codex instances running within plugin-capable environments.

### Qoder Detection

The `isQoder` flag (lines 22-23) triggers when `process.env.QODER_SESSION_ID` is present and neither Copilot nor Codex conditions are met. This provides dedicated support for the Qoder IDE integration.

### Native Claude Fallback

When none of the above environment variables are present, the hook defaults to native Claude mode. This fallback branch handles standard Claude Desktop or CLI installations without plugin-specific modifications.

## Writing the Activation Flag and State Management

Once the platform is identified, `setMode(mode)` in [`hooks/ponytail-activate.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-activate.js) creates a `.ponytail-active` flag file in a platform-specific state directory.

The state directory resolution follows this precedence (as implemented in [`hooks/ponytail-runtime.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-runtime.js)):

- **Default**: `~/.claude` (via `getClaudeDir()` in [`hooks/ponytail-config.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-config.js))
- **Codex**: `process.env.PLUGIN_DATA`
- **Copilot**: `process.env.COPILOT_PLUGIN_DATA` or falls back to `~/.claude`
- **Qoder**: `~/.qoder`

The `setMode()` function (lines 33-36) recursively creates the target directory if missing and writes the mode value (`full`, `lite`, `ultra`, or `off`) to `.ponytail-active`. If the resolved mode is `off`, the hook calls `clearMode()` (lines 26-31) to delete the flag file and exit early without emitting instructions.

## Platform-Specific Output Routing

The `writeHookOutput` function routes activation instructions differently for each client to maximize compatibility:

- **Copilot**: Emits a JSON object containing only `additionalContext` for SessionStart events (lines 51-58)
- **Codex**: Sends a system message formatted as `PONYTAIL:<MODE>` followed by a `hookSpecificOutput` JSON payload (lines 59-67)
- **Qoder**: Uses the same JSON payload as Codex but omits the system message prefix (lines 69-78)
- **Native Claude**: Outputs raw text for SessionStart, but uses the JSON wrapper for SubagentStart to prevent context loss (lines 82-89)

This differentiation ensures that VS Code Copilot receives properly scoped context while Codex gets the explicit system message required for its architecture.

## Status-Line Setup Nudges

When activation occurs, the hook checks for the presence of a `statusLine` configuration in Claude settings. If absent and the platform is not Copilot, Ponytail emits a one-time setup nudge pointing to platform-specific helper scripts.

The script selection logic (lines 62-69 in [`hooks/ponytail-activate.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-activate.js)) uses `process.platform === 'win32'` to determine the appropriate helper:

- **Windows**: `ponytail-statusline.ps1` (PowerShell)
- **Unix-like**: [`ponytail-statusline.sh`](https://github.com/DietrichGebert/ponytail/blob/main/ponytail-statusline.sh) (Bash)

The hook embeds the selected script path in a JSON snippet suitable for pasting into [`settings.json`](https://github.com/DietrichGebert/ponytail/blob/main/settings.json). If the installation path contains unsafe shell characters (detected via `isShellSafe()`), it falls back to plain text instructions instead of embedded commands (lines 70-85). To avoid repeated notifications, the hook creates a `.ponytail-statusline-nudged` flag file after the first display (lines 56-60).

## Default Mode Resolution

Before writing activation state, `getDefaultMode()` in [`hooks/ponytail-config.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-config.js) (lines 76-84) resolves the initial operating mode through the following precedence:

1. `PONYTAIL_DEFAULT_MODE` environment variable
2. [`config.json`](https://github.com/DietrichGebert/ponytail/blob/main/config.json) in the XDG or Windows configuration directory
3. Hardcoded fallback to `'full'`

The function validates that only runtime-level modes (`off`, `lite`, `full`, `ultra`) are permitted; the session-only `review` mode is explicitly rejected to prevent invalid default configurations.

## Summary

- **Platform detection** relies on environment variables (`COPILOT_PLUGIN_DATA`, `PLUGIN_DATA`, `QODER_SESSION_ID`) defined in [`hooks/ponytail-runtime.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-runtime.js)
- **State persistence** uses `.ponytail-active` flag files written to platform-specific directories via `setMode()` and `clearMode()`
- **Output routing** adapts instruction format per client: JSON context for Copilot, system messages for Codex, raw text for native Claude
- **Status-line helpers** are selected by `process.platform` checks, providing `.ps1` scripts for Windows and `.sh` scripts for Unix systems
- **Mode resolution** follows a strict hierarchy from environment variables to config files to the `'full'` default

## Frequently Asked Questions

### How can I manually set Ponytail to operate in lite mode across all sessions?

Set the `PONYTAIL_DEFAULT_MODE` environment variable to `'lite'` before starting your Claude client. Alternatively, modify the [`config.json`](https://github.com/DietrichGebert/ponytail/blob/main/config.json) file in your platform's configuration directory (managed by `getClaudeDir()` in [`hooks/ponytail-config.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-config.js)) to include `"defaultMode": "lite"`.

### Why does Ponytail use different output formats for VS Code Copilot versus Claude Codex?

VS Code Copilot requires wrapped JSON context via `additionalContext` to integrate with its chat interface, while Claude Codex expects a system message prefix (`PONYTAIL:<MODE>`) to properly register the plugin state. The `writeHookOutput` function in [`hooks/ponytail-runtime.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-runtime.js) handles these distinctions automatically based on the `isCopilot` and `isCodex` flags.

### Where does Ponytail store its activation flag on Windows versus Linux?

On Windows, the flag writes to the directory specified by `COPILOT_PLUGIN_DATA` (for Copilot) or `%USERPROFILE%\.claude` (for native Claude). On Linux and macOS, it uses `$PLUGIN_DATA` (Codex), `~/.qoder` (Qoder), or `~/.claude` (native). All paths are computed dynamically in [`hooks/ponytail-runtime.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-runtime.js) before calling `setMode()`.

### How do I prevent Ponytail from showing the status-line setup nudge repeatedly?

The hook automatically creates a `.ponytail-statusline-nudged` file in your state directory after displaying the notification once. If you accidentally dismiss it or need to restore it, delete this hidden flag file and restart your Claude session to trigger [`hooks/ponytail-activate.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-activate.js) again.