# How Ponytail Handles Configuration Resolution: Priority, Paths, and Code Examples

> Learn how Ponytail handles configuration resolution with its three-layer priority system. Discover environment variables, user config files, and built-in defaults. See code examples.

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

---

**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`](https://github.com/DietrichGebert/ponytail/blob/main/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`](https://github.com/DietrichGebert/ponytail/blob/main/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:

```bash
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`](https://github.com/DietrichGebert/ponytail/blob/main/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`](https://github.com/DietrichGebert/ponytail/blob/main/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:

```javascript
// 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:

```javascript
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:

```javascript
// 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`](https://github.com/DietrichGebert/ponytail/blob/main/pi-extension/index.js)** (lines 65-75): Initializes session defaults and resolves command arguments via `getDefaultMode()`
- **[`hooks/ponytail-instructions.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-instructions.js)** (lines 78-84): Normalizes persisted modes for user-facing status messages using `normalizePersistedMode`
- **[`hooks/ponytail-mode-tracker.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-mode-tracker.js)** (line 36): Handles `/ponytail default <mode>` commands by delegating to `writeDefaultMode()`

No component accesses [`config.json`](https://github.com/DietrichGebert/ponytail/blob/main/config.json) directly—all configuration queries route through [`hooks/ponytail-config.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-config.js).

## Practical Configuration Examples

### Reading the Effective Configuration

```javascript
// 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

```bash
#!/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

```javascript
// 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`](https://github.com/DietrichGebert/ponytail/blob/main/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`](https://github.com/DietrichGebert/ponytail/blob/main/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`](https://github.com/DietrichGebert/ponytail/blob/main/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`](https://github.com/DietrichGebert/ponytail/blob/main/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`](https://github.com/DietrichGebert/ponytail/blob/main/config.json)—the environment variable overrides this if both are present, per the resolution logic in `getQuietStartup()` (lines 5-14).