How Ponytail Handles Configuration Resolution: Priority, Paths, and Code Examples
Ponytail resolves configuration through a three-layer priority system: environment variables override user config files, which override built-in defaults, all centralized in hooks/ponytail-config.js.
Ponytail's configuration resolution system provides a predictable, deterministic way to manage runtime settings across platforms. Understanding how Ponytail handles configuration resolution helps you customize behavior for CI pipelines, user preferences, or deployment environments without source code changes.
How Configuration Priority Works in Ponytail
The configuration resolver implements a strict precedence chain that guarantees consistent behavior. When Ponytail needs a configuration value, it checks sources in this exact order:
- Environment variables — immediate, ephemeral overrides (highest priority)
- User configuration file — persistent per-user settings in
config.json - Built-in defaults — hard-coded fallbacks that ship with the application
Only values recognized as valid runtime modes pass through. The review mode, being session-only, is deliberately excluded from persistence—only off, lite, full, and ultra may be stored as defaults.
Configuration Sources and File Locations
Environment Variable Overrides
Ponytail recognizes three environment variables for immediate configuration:
PONYTAIL_DEFAULT_MODE— sets the default intensity (off,lite,full,ultra)PONYTAIL_QUIET_STARTUP— suppresses startup toast notificationsPONYTAIL_HIDE_STATUS— hides status-bar indicators
These take precedence over any file-based configuration. Set them before launching Ponytail:
export PONYTAIL_DEFAULT_MODE=lite
export PONYTAIL_QUIET_STARTUP=true
User Configuration File Paths
The getConfigDir() function (lines 54-64 in hooks/ponytail-config.js) implements cross-platform path resolution:
| Platform | Config Directory |
|---|---|
| Any (XDG-compliant) | $XDG_CONFIG_HOME/ponytail/ |
| macOS/Linux fallback | ~/.config/ponytail/ |
| Windows | %APPDATA%\ponytail\ |
The getConfigPath() function (lines 66-69) appends config.json to the resolved directory.
Core Resolution Functions in ponytail-config.js
getDefaultMode()
This function implements the full resolution chain for the default intensity mode:
// From hooks/ponytail-config.js, lines 76-94 pattern
const { getDefaultMode } = require('./hooks/ponytail-config');
// Checks in order:
// 1. process.env.PONYTAIL_DEFAULT_MODE
// 2. config.json value
// 3. DEFAULT_MODE constant ("full")
const mode = getDefaultMode();
console.log(mode); // "full" | "lite" | "ultra" | "off"
writeDefaultMode(mode)
Persists validated configuration to disk with defensive file operations:
const { writeDefaultMode } = require('./hooks/ponytail-config');
// Normalizes input, creates directories if needed, merges with existing config
writeDefaultMode('ultra');
// Result: config.json contains {"defaultMode":"ultra"}
The function normalizes through normalizeMode() (lines 38-40), ensures directory existence (lines 41-43), and performs atomic JSON merging (lines 44-50).
Boolean Flag Resolution
getQuietStartup() and getHideStatus() follow the same pattern—environment variables checked first, then config file values. The implementation at lines 5-14 demonstrates this consistent approach:
// Environment check (lines 5-10)
const quiet = process.env.PONYTAIL_QUIET_STARTUP === 'true';
// Config file fallback (lines 11-14)
return config?.quietStartup ?? false;
How Other Ponytail Components Use Configuration Resolution
The centralized resolver ensures single-source-of-truth semantics across the codebase:
pi-extension/index.js(lines 65-75): Initializes session defaults and resolves command arguments viagetDefaultMode()hooks/ponytail-instructions.js(lines 78-84): Normalizes persisted modes for user-facing status messages usingnormalizePersistedModehooks/ponytail-mode-tracker.js(line 36): Handles/ponytail default <mode>commands by delegating towriteDefaultMode()
No component accesses config.json directly—all configuration queries route through hooks/ponytail-config.js.
Practical Configuration Examples
Reading the Effective Configuration
// Example: Complete configuration inspection
const {
getDefaultMode,
getQuietStartup,
getHideStatus,
getConfigPath
} = require('./hooks/ponytail-config');
console.log({
configFile: getConfigPath(),
defaultMode: getDefaultMode(),
quietStartup: getQuietStartup(),
hideStatus: getHideStatus()
});
Environment-Driven CI Configuration
#!/bin/bash
# ci-pipeline.sh — ephemeral configuration without file I/O
export PONYTAIL_DEFAULT_MODE=off
export PONYTAIL_QUIET_STARTUP=true
export PONYTAIL_HIDE_STATUS=true
node ponytail-job.js
Persistent User Preference
// setup-user-profile.js — one-time configuration
const { writeDefaultMode } = require('./hooks/ponytail-config');
// Valid: persisted to config.json
writeDefaultMode('lite');
// Invalid: throws or rejects — 'review' cannot be persisted
writeDefaultMode('review'); // Error: review is session-only
Summary
- Three-layer precedence: Environment variables →
config.json→ hard-coded defaults - Cross-platform paths: XDG-compliant with macOS/Linux and Windows fallbacks
- Centralized resolver: All modules import from
hooks/ponytail-config.js - Validation at persistence: Only
off,lite,full,ultramay be stored as defaults - Session-safe: The
reviewmode operates transiently without config file pollution
Frequently Asked Questions
What takes precedence: PONYTAIL_DEFAULT_MODE or the config.json file?
Environment variables always win. If PONYTAIL_DEFAULT_MODE is set, Ponytail ignores any value in config.json and uses the environment variable directly. This enables temporary overrides without modifying persistent user settings.
Where is the Ponytail configuration file stored on different operating systems?
Platform-specific paths determined by getConfigDir(): Linux and macOS use $XDG_CONFIG_HOME/ponytail/config.json or ~/.config/ponytail/config.json; Windows uses %APPDATA%\ponytail\config.json. The getConfigPath() function in hooks/ponytail-config.js (lines 66-69) computes the final absolute path.
Why can't I set 'review' as the default mode in Ponytail?
The review mode is intentionally session-only. The validation in writeDefaultMode() and getDefaultMode() explicitly excludes review from persistence because it represents temporary diagnostic state rather than ongoing operational preference. Use off, lite, full, or ultra for defaults; activate review per-session via command or API.
How do I completely disable Ponytail's startup notifications?
Set PONYTAIL_QUIET_STARTUP=true. This environment variable immediately suppresses all startup toasts. For permanent quiet startup, add "quietStartup": true to your config.json—the environment variable overrides this if both are present, per the resolution logic in getQuietStartup() (lines 5-14).
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 →