How Ponytail Handles Mode Switching and Deactivation Commands
Ponytail processes mode switches and deactivation commands through two core files: hooks/ponytail-mode-tracker.js parses /ponytail commands and natural language deactivation phrases, while hooks/ponytail-config.js manages default mode persistence and command detection utilities.
The DietrichGebert/ponytail repository implements a hook-based architecture that intercepts user prompts to dynamically adjust runtime behavior. Understanding how Ponytail mode switching and deactivation commands function requires examining the interaction between the mode tracker hook and configuration utilities.
Understanding Ponytail's Mode Switching Architecture
The Core Hook System (ponytail-mode-tracker.js)
The primary logic for Ponytail mode switching resides in hooks/ponytail-mode-tracker.js. This file handles incoming JSON prompts, parses command syntax, and manages state transitions between lite, full, ultra, and off states. When a prompt arrives, the hook lower-cases the input string and checks for command prefixes at lines 23-54.
Configuration Utilities (ponytail-config.js)
Supporting the mode tracker, hooks/ponytail-config.js provides essential helper functions including writeDefaultMode() for persisting user preferences and isDeactivationCommand() for detecting natural language shutdown requests. These utilities abstract file I/O operations and string normalization logic away from the main hook flow.
How Mode Switching Works in Ponytail
Command Syntax and Runtime Modes
Ponytail recognizes explicit command syntax when prompts begin with /ponytail. The parser supports several variants:
/ponytail lite|full|ultra– Immediately switches the current session to the requested runtime mode by callingsetMode(mode)at lines 65-66/ponytail off– Deactivates Ponytail for the current session usingclearMode()at lines 77-81/ponytail(no arguments) – Returns a status report without modifying the active mode- Any other text after
/ponytail– Falls back to the configured default mode
When setMode() executes successfully, the hook emits a "mode changed" confirmation message via writeHookOutput().
Persisting Default Modes Across Sessions
Users can permanently store their preferred mode using the default subcommand. When the hook detects /ponytail default <mode> at lines 38-44, it invokes writeDefaultMode() to persist the selection (valid options: off, lite, full, ultra) to configuration storage. Future sessions automatically initialize to this default unless overridden by an explicit runtime command.
Special Handling for Qoder Client
The repository includes client-specific logic for "Qoder" at lines 70-76. Rather than emitting separate status messages, mode change confirmations are folded directly into the ruleset output for this client. This ensures the Qoder interface receives consolidated feedback without additional hook writes cluttering the response stream.
Deactivation Commands and Flow
Explicit Off Commands (/ponytail off)
The most direct method for disabling Ponytail involves sending /ponytail off. This command triggers clearMode() and emits the standardized "PONYTAIL MODE OFF" message at lines 77-81. This immediate deactivation takes precedence over other operations and signals the system to skip subsequent rule injections.
Natural Language Deactivation Phrases
Beyond explicit commands, Ponytail supports conversational deactivation through isDeactivationCommand() at lines 40-43 of ponytail-config.js. This helper recognizes two specific phrases:
- "stop ponytail"
- "normal mode"
The detection is case-insensitive, automatically trimmed, and ignores punctuation. When the mode tracker identifies these phrases at lines 84-89—and no mode switch has already occurred—it executes clearMode() and emits the same "off" confirmation used for explicit commands.
Deactivation Impact on Rule Injection
Post-deactivation behavior varies by client. For standard clients, the off state simply prevents mode-specific processing. However, for the Qoder client, deactivation carries specific consequences at lines 95-96: the hook explicitly skips automatic ruleset injection following a shutdown command, ensuring clean deactivation without residual rule output.
Implementation Flow and Code Examples
The complete processing flow follows this sequence:
- Incoming prompt JSON is parsed and the
promptfield is lower-cased - If the text begins with
/ponytail, parse the command and potentially persist defaults viawriteDefaultMode() - For runtime modes (
lite,full,ultra), invokesetMode()and emit confirmation - For
off, invokeclearMode()and emit "PONYTAIL MODE OFF" - If no command prefix exists but
isDeactivationCommand()returns true, clear mode and emit off message - For Qoder clients, inject the full ruleset unless deactivation just occurred (lines 95-96)
// Switch to full mode for the current session
{ "prompt": "/ponytail full" }
// Persist ultra as the default for all future sessions
{ "prompt": "/ponytail default ultra" }
// Turn Ponytail off via explicit command
{ "prompt": "/ponytail off" }
// Natural language deactivation phrase
{ "prompt": "stop ponytail" }
// Alternative deactivation phrase (punctuation ignored)
{ "prompt": "Normal mode!" }
Summary
- Dual-file architecture: Mode switching logic splits between
hooks/ponytail-mode-tracker.js(orchestration) andhooks/ponytail-config.js(utilities). - Command flexibility: Supports both explicit
/ponytail <mode>syntax and conversational phrases like "stop ponytail". - Persistent defaults: The
defaultsubcommand writes preferences viawriteDefaultMode()for automatic future session initialization. - Clean deactivation: Both
/ponytail offand natural language triggers executeclearMode()and suppress subsequent rule injection for Qoder clients.
Frequently Asked Questions
How does Ponytail detect deactivation commands in natural language?
Ponytail uses the isDeactivationCommand() helper in hooks/ponytail-config.js (lines 40-43) to identify the exact strings "stop ponytail" or "normal mode". The function normalizes input by converting to lowercase, trimming whitespace, and stripping punctuation before comparison, allowing flexible phrasing like "Stop Ponytail!" or "normal mode..." to trigger deactivation.
What happens when I set a default mode versus a runtime mode?
Setting a default mode via /ponytail default ultra calls writeDefaultMode() to permanently store the preference for future sessions, while runtime commands like /ponytail ultra call setMode() to temporarily change the active session without persisting the selection. The default only applies when no explicit mode command is issued.
Why does the Qoder client handle mode changes differently?
According to the source at lines 70-76 and 95-96 of ponytail-mode-tracker.js, the Qoder client receives mode confirmations embedded within ruleset output rather than separate hook messages. Additionally, Qoder skips automatic ruleset injection entirely after deactivation to prevent conflicting instructions, ensuring the client receives clean "off" state signaling.
Can I deactivate Ponytail without using the /ponytail prefix?
Yes. The hook checks for deactivation phrases independently of command prefixes at lines 84-89. If your prompt contains "stop ponytail" or "normal mode" (case-insensitive) and no mode switch has already occurred, clearMode() executes and emits "PONYTAIL MODE OFF" regardless of whether you used the /ponytail syntax.
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 →