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.jsonon Linux (XDG compliant)~/.config/ponytail/config.jsonon Linux (fallback)%APPDATA%\ponytail\config.jsonon 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
requestedparameter is empty or resolves to"off", the function callsgetDefaultMode(). - 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, andultraare valid;reviewis excluded from default persistence. - File locations: Configuration resides in
$XDG_CONFIG_HOME/ponytail,~/.config/ponytail, or%APPDATA%\ponytaildepending on the platform. - Server integration:
resolveMode()inponytail-mcp/instructions.jsprovides an additional safety net, ensuring the server never starts in an unservable state. - Implementation core: The resolution logic is centralized in
hooks/ponytail-config.jsviagetDefaultMode(), 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/ponytailor~/.config/ponytail - Windows:
%APPDATA%\ponytail - macOS: Typically
~/.config/ponytailunless 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →