How Ponytail Determines Its Default Mode: Environment, Config, and Fallback Priority

Ponytail determines its default mode at startup through a three-tier priority system defined in hooks/ponytail-config.js: it first checks the PONYTAIL_DEFAULT_MODE environment variable, then falls back to the defaultMode field in the user's config.json, and finally defaults to the built-in constant "full".

The DietrichGebert/ponytail repository implements this deterministic resolution strategy to establish runtime intensity before the MCP server begins serving instructions. When initializing, the system relies on the getDefaultMode() function to decide which instruction set—lite, full, or ultra—should be active, ensuring consistent behavior across sessions while respecting user preferences.

The Three-Tier Resolution Priority

The resolution logic in hooks/ponytail-config.js follows a strict hierarchy, processing each layer only if the previous one yields no valid result.

1. Environment Variable PONYTAIL_DEFAULT_MODE

The highest priority source is the environment variable PONYTAIL_DEFAULT_MODE. At lines 76‑84, the code checks if this variable is set, converts the value to lowercase, and validates it against the RUNTIME_MODES array.

Only the runtime levels off, lite, full, and ultra are accepted. The review mode is deliberately excluded from this check because it is designed as a session-only state and cannot be persisted as a default.

2. User Configuration File config.json

If the environment variable is unset or invalid, Ponytail searches for a configuration file in the platform-specific config directory. The helper functions getConfigDir() and getConfigPath() (lines 54‑69) resolve the correct path:

  • $XDG_CONFIG_HOME/ponytail/config.json on Linux (XDG compliant)
  • ~/.config/ponytail/config.json on Linux (fallback)
  • %APPDATA%\ponytail\config.json on Windows

If the file exists and contains a defaultMode field matching a valid runtime level (validated at lines 91‑93), that value is used.

3. Built-in Fallback to "full"

If neither the environment variable nor the configuration file provides a valid mode, the system falls back to the constant DEFAULT_MODE, defined as the string "full" at lines 99‑100 in ponytail-config.js. This ensures the MCP server always starts with a functional instruction set rather than failing silently.

Validation and Mode Restrictions

Both the environment variable and configuration file inputs are validated against the RUNTIME_MODES array: ['off','lite','full','ultra']. This validation guard (implemented at lines 78‑84 and 91‑93) serves a critical purpose: it prevents the review mode from being established as a default.

The review mode is intended for temporary, single-session analysis tasks. By excluding it from the default resolution pipeline, Ponytail ensures users cannot accidentally persist a diagnostic state that disables normal operation.

MCP Server Integration and resolveMode()

Once getDefaultMode() resolves the initial setting, the MCP server consumes this value through resolveMode() in ponytail-mcp/instructions.js (lines 16‑22). This function handles runtime mode negotiation:

  • If the requested parameter is empty or resolves to "off", the function calls getDefaultMode().
  • If getDefaultMode() also returns "off", the server finally defaults to "full" to guarantee service availability.
  • Explicit valid requests (lite, full, ultra) bypass the default resolution entirely.

This dual-layer fallback ensures the server never launches in a state that cannot serve instructions.

Practical Configuration Examples

Setting the Default via Environment Variable

export PONYTAIL_DEFAULT_MODE=lite
ponytail

# → Server starts with the "lite" instruction set

Persisting a Default in config.json

{
  "defaultMode": "ultra"
}

Place this file at ~/.config/ponytail/config.json (Linux) or %APPDATA%\ponytail\config.json (Windows). The environment variable, if set, will override this value on the next startup.

Querying the Default Programmatically

const { getDefaultMode } = require('./hooks/ponytail-config');

console.log('Resolved default:', getDefaultMode());
// → "lite", "full", "ultra", or the fallback "full"

Resolving Modes in the MCP Server

const { resolveMode } = require('./ponytail-mcp/instructions');

console.log(resolveMode(''));      // → Uses config/default → "lite" or "full"
console.log(resolveMode('off'));    // → Falls back to config/default
console.log(resolveMode('ultra'));  // → "ultra" (explicit request honored)

Summary

  • Priority order: Environment variable → config.json → built-in "full" constant.
  • Validation: Only off, lite, full, and ultra are valid; review is excluded from default persistence.
  • File locations: Configuration resides in $XDG_CONFIG_HOME/ponytail, ~/.config/ponytail, or %APPDATA%\ponytail depending on the platform.
  • Server integration: resolveMode() in ponytail-mcp/instructions.js provides an additional safety net, ensuring the server never starts in an unservable state.
  • Implementation core: The resolution logic is centralized in hooks/ponytail-config.js via getDefaultMode(), with validation guards at lines 78‑84 and 91‑93.

Frequently Asked Questions

What happens if I set PONYTAIL_DEFAULT_MODE to an invalid value?

If the environment variable contains a value not in ['off','lite','full','ultra'], the validation logic at lines 78‑84 rejects it and proceeds to check the config.json file. If that also fails, the system falls back to "full".

Why can't I set "review" as the default mode for Ponytail?

The review mode is intentionally excluded from the RUNTIME_MODES validation array used by getDefaultMode(). According to the source code comments, this mode is session-only and designed for temporary diagnostic analysis; allowing it as a persistent default could leave the system in a restricted state unintentionally.

Where is the config.json file located on my operating system?

The exact path depends on your platform. The getConfigDir() function resolves:

  • Linux: $XDG_CONFIG_HOME/ponytail or ~/.config/ponytail
  • Windows: %APPDATA%\ponytail
  • macOS: Typically ~/.config/ponytail unless overridden by XDG variables

How does the MCP server prevent starting in "off" mode?

While getDefaultMode() can legally return "off" (if explicitly configured), the resolveMode() function in ponytail-mcp/instructions.js contains a secondary guard at lines 16‑22. If the resolved default is "off", it overrides to "full", ensuring the server always maintains at least the full instruction capability.

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 →