How Ponytail Detects and Suggests Statusline Configuration in Claude Code

Ponytail detects missing statusline configuration by parsing Claude's settings.json during session initialization and suggests a shell-safe command snippet that displays the active mode badge directly in the terminal prompt.

The DietrichGebert/ponytail plugin enhances Claude Code sessions by displaying the current operational mode (such as [PONYTAIL] or [PONYTAIL:ULTRA]) in the terminal statusline. To ensure users benefit from this visual indicator, the plugin automatically detects whether statusline configuration is present and suggests the appropriate setup command if missing.

The Session Initialization Hook

When a Claude Code session begins, the ponytail-activate.js hook executes automatically. This script performs three critical operations: writing the active mode flag to ~/.claude/.ponytail-active, emitting mode-specific instructions, and scanning for existing statusline configuration in lines 45-87.

Reading Claude's settings.json

The detection logic begins by locating and parsing Claude's configuration file. At lines 47-52, the hook constructs the path to ~/.claude/settings.json and attempts to read it:

const fs = require('fs');
const path = require('path');
const os = require('os');

const claudeDir = path.join(os.homedir(), '.claude');
const settingsPath = path.join(claudeDir, 'settings.json');

if (fs.existsSync(settingsPath)) {
  const content = fs.readFileSync(settingsPath, 'utf8').replace(/^\uFEFF/, '');
  const settings = JSON.parse(content);
  // Detection logic continues...
}

The code strips the UTF-8 BOM (Byte Order Mark) using .replace(/^\uFEFF/, '') before parsing to handle files created on Windows systems.

Checking for Existing Configuration

After parsing, the hook checks for a statusLine property at lines 51-53:

let hasStatusline = false;
if (settings.statusLine) {
  hasStatusline = true;
}

If settings.statusLine exists, the plugin assumes the user has already configured a custom statusline and skips the suggestion logic entirely.

Preventing Repeated Prompts

To avoid annoying users with persistent notifications, Ponytail implements a nudge-once guard. Before displaying any suggestion, the hook checks for a hidden flag file at .ponytail-statusline-nudged within the Claude configuration directory (lines 59-62):

const nudgeFlagPath = path.join(claudeDir, '.ponytail-statusline-nudged');

if (!hasStatusline && !fs.existsSync(nudgeFlagPath)) {
  // Show suggestion and create flag file
  fs.writeFileSync(nudgeFlagPath, '');
}

This ensures the setup prompt appears only during the first session where a missing configuration is detected, preventing repeated interruptions on subsequent launches.

Validating Path Safety with isShellSafe

Before embedding the statusline script path into a command string, Ponytail validates shell safety using the isShellSafe function defined in hooks/ponytail-config.js (lines 50-52):

function isShellSafe(str) {
  return /^[A-Za-z0-9 _.\-:/\\~]+$/.test(str);
}

This whitelist regex permits only alphanumeric characters, spaces, underscores, dots, hyphens, colons, forward slashes, backslashes, and tildes. If the installation path contains special characters like $, backticks, or quotes, the function returns false, triggering manual configuration instructions instead of automatic embedding to prevent command injection.

Generating the Statusline Suggestion

Based on the safety check and operating system, the hook constructs the appropriate command at lines 70-85. For Unix systems with safe paths, it generates a command pointing to ponytail-statusline.sh:

const scriptPath = path.join(__dirname, 'ponytail-statusline.sh');
const command = `bash "${scriptPath}"`;
const suggestion = {
  statusLine: {
    type: "command",
    command: command
  }
};

If the path fails the safety check, the hook outputs a warning directing users to manually add the statusline configuration to ~/.claude/settings.json, explaining that automatic embedding is unsafe due to special characters in the installation path.

The Statusline Script Implementation

The ponytail-statusline.sh script (referenced in the generated command) reads the active mode flag and outputs a colored badge:

#!/usr/bin/env bash
flag="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/.ponytail-active"
[ -f "$flag" ] || exit 0

mode=$(head -n1 "$flag" | tr -d '[:space:]')
color=108               # green for normal modes

[ "$mode" = "ultra" ] && color=173   # amber for ultra mode

if [ -z "$mode" ] || [ "$mode" = "full" ]; then
    printf '\033[38;5;%sm[PONYTAIL]\033[0m' "$color"
else
    printf '\033[38;5;%sm[PONYTAIL:%s]\033[0m' "$color" "$(printf '%s' "$mode" | tr '[:lower:]' '[:upper:]')"
fi

This script outputs ANSI-colored text displaying [PONYTAIL] for standard modes or [PONYTAIL:ULTRA] when ultra mode is active, using green (color 108) or amber (color 173) respectively.

Complete Detection Logic Example

Here is a runnable Node.js script that simulates Ponytail's detection and suggestion logic:

const fs = require('fs');
const path = require('path');
const os = require('os');

// Simulated isShellSafe from ponytail-config.js
function isShellSafe(str) {
  return /^[A-Za-z0-9 _.\-:/\\~]+$/.test(str);
}

function suggestStatusline(claudeDir) {
  const settingsPath = path.join(claudeDir, 'settings.json');
  if (!fs.existsSync(settingsPath)) return null;

  const raw = fs.readFileSync(settingsPath, 'utf8').replace(/^\uFEFF/, '');
  const settings = JSON.parse(raw);
  if (settings.statusLine) return null; // already configured

  const scriptName = process.platform === 'win32'
    ? 'ponytail-statusline.ps1' : 'ponytail-statusline.sh';
  const scriptPath = path.join(__dirname, scriptName);

  if (isShellSafe(scriptPath)) {
    const cmd = process.platform === 'win32'
      ? `powershell -ExecutionPolicy Bypass -File "${scriptPath}"`
      : `bash "${scriptPath}"`;
    return {
      statusLine: { type: 'command', command: cmd },
      message: 'Add the above statusLine entry to your settings.json'
    };
  }
  return { message: 'Install path unsafe – configure statusLine manually.' };
}

// Usage example
const claudeDir = path.join(os.homedir(), '.claude');
console.log(suggestStatusline(claudeDir));

Summary

  • Detection Entry Point: The ponytail-activate.js hook runs at every Claude session start, reading ~/.claude/settings.json to check for existing statusLine configuration.
  • BOM Handling: The parser strips UTF-8 Byte Order Marks before JSON parsing to ensure cross-platform compatibility with Windows-created files.
  • Nudge Prevention: A hidden .ponytail-statusline-nudged flag file ensures users see the setup suggestion only once, preventing repetitive prompts.
  • Security Validation: The isShellSafe regex whitelist /^[A-Za-z0-9 _.\-:/\\~]+$/ prevents command injection by validating script paths before embedding them in JSON configuration strings.
  • Platform Support: The system generates appropriate commands for both Unix (ponytail-statusline.sh) and Windows (ponytail-statusline.ps1) environments based on process.platform.

Frequently Asked Questions

What file does Ponytail check to detect statusline configuration?

Ponytail checks ~/.claude/settings.json (or %USERPROFILE%\.claude\settings.json on Windows) for a statusLine property. According to the source code in hooks/ponytail-activate.js, if settings.statusLine exists, the plugin assumes the user has already configured a statusline and does not display setup suggestions.

How does Ponytail prevent showing the same suggestion multiple times?

The plugin creates a hidden flag file named .ponytail-statusline-nudged in the Claude configuration directory the first time it suggests statusline setup. On subsequent sessions, the hook checks for this file at line 59 and skips the suggestion logic if the file exists, ensuring the nudge appears only once.

What makes a file path "unsafe" for automatic configuration embedding?

Paths containing characters outside the whitelist regex /^[A-Za-z0-9 _.\-:/\\~]+$/ are considered unsafe. This includes shell metacharacters like $, backticks, quotes, semicolons, or ampersands that could enable command injection if embedded directly into JSON configuration strings, as implemented in hooks/ponytail-config.js.

Does Ponytail automatically modify my settings.json file?

No, Ponytail only detects the missing configuration and suggests the appropriate JSON snippet. It outputs a ready-to-paste configuration for safe paths or manual instructions for unsafe paths, but never writes to settings.json automatically. The separate scripts/uninstall.js script only removes Ponytail-specific entries during uninstallation, leaving user-added configurations intact.

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 →