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:

  1. Environment variables — immediate, ephemeral overrides (highest priority)
  2. User configuration file — persistent per-user settings in config.json
  3. 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 notifications
  • PONYTAIL_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:

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, ultra may be stored as defaults
  • Session-safe: The review mode 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:

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 →