How to Extend Ponytail's Functionality: 3 Proven Methods for OpenCode Plugin Development
You can extend Ponytail by adding new slash commands in the command/ directory, creating custom skill markdown files in skills/ponytail/, or modifying the instruction builder in hooks/ponytail-instructions.js to inject project-specific logic.
Ponytail is an OpenCode plugin that injects "lazy senior-dev" guidelines into AI-assisted coding sessions by persisting modes (off, lite, full, ultra, review) and appending instructions to the system prompt. Because the codebase isolates command registration, skill management, and instruction generation into distinct modules, you can safely extend Ponytail's functionality without disrupting the core workflow. All extension points reside in predictable file paths like hooks/ponytail-instructions.js and the command/ directory.
Understanding Ponytail's Extension Architecture
Before extending, understand that Ponytail uses three isolated components that minimize coupling:
- Command registration: The
ponytail.mjsentry point scanscommand/*.mdfiles (lines 59-64) to auto-register slash commands. - Instruction generation: The
getPonytailInstructionsfunction inhooks/ponytail-instructions.jsreadsSKILL.mdand filters content by mode. - Configuration persistence:
hooks/ponytail-config.jshandles mode state in~/.config/opencode/.ponytail-active.
This separation lets you extend functionality at any layer without touching the others.
Method 1: Add Custom Slash Commands
Declaring New Commands
Create a markdown file in the command/ directory following the front-matter schema:
---
name: ponytail-debug
description: Show the current Ponytail mode and config paths for troubleshooting.
argument-hint: ""
---
The plugin automatically discovers this because ponytail.mjs scans the command directory at startup (lines 59-64).
Implementing Command Handlers
Add execution logic in ponytail.mjs within the command.execute.before hook:
'command.execute.before': async (input) => {
if (input.command === 'ponytail-debug') {
const mode = readMode();
const cfg = getConfigPath();
client && client.app && client.app.log({
body: { service: 'ponytail', level: 'info',
message: `mode=${mode}, config=${cfg}` }
});
}
// existing /ponytail handling …
},
Now /ponytail-debug is available and reports internal state without leaving the chat.
Method 2: Create Custom Skill Files
Ponytail's instruction builder reads markdown from skills/ponytail/ and filters content based on mode labels. Create skills/ponytail/custom-rules.md:
---
name: custom-rules
description: Extra guidelines that apply only in "ultra" mode.
---
# Custom Rules (ultra only)
- **ultra**: "Never write more than 5 lines of code for any function."
- **ultra**: "Prefer `Array.at(-1)` over manual indexing."
Because ponytail.mjs adds the skills directory to the runtime's search paths (lines 68-70), this file loads automatically. The filterSkillBodyForMode function (lines 22-27 in hooks/ponytail-instructions.js) includes these bullets only when the active mode is ultra.
Method 3: Hook Into the Instruction Builder
To programmatically modify generated instructions, edit hooks/ponytail-instructions.js. The getPonytailInstructions function builds the final prompt text. You can prepend project-specific headers:
function getPonytailInstructions(mode) {
const configuredMode = normalizePersistedMode(mode) || DEFAULT_MODE;
const effectiveMode = normalizeMode(configuredMode) || DEFAULT_MODE;
const base = (() => {
try {
return filterSkillBodyForMode(
fs.readFileSync(SKILL_PATH, 'utf8'), effectiveMode
);
} catch (e) {
return getFallbackInstructions(effectiveMode);
}
})();
// Custom extension for full mode
if (effectiveMode === 'full') {
const header = '🚀 Project "MyApp" – full-mode guidelines\n\n';
return 'PONYTAIL MODE ACTIVE — level: full\n\n' + header + base;
}
return 'PONYTAIL MODE ACTIVE — level: ' + effectiveMode + '\n\n' + base;
}
This ensures every system prompt in full mode includes your custom banner before the standard guidelines.
Summary
- Add slash commands by creating
.mdfiles incommand/and handling them inponytail.mjsvia thecommand.execute.beforehook. - Create custom skills by adding markdown files to
skills/ponytail/with mode-specific bullets;filterSkillBodyForModeautomatically filters by bolded mode labels. - Modify instruction generation by extending
getPonytailInstructionsinhooks/ponytail-instructions.jsto inject custom text based on the active mode.
Frequently Asked Questions
Where does Ponytail store the active mode configuration?
Ponytail persists the active mode in a per-user state file located at ~/.config/opencode/.ponytail-active, managed by hooks/ponytail-config.js through the writeDefaultMode and readMode utilities.
Can I add commands without modifying ponytail.mjs?
No, while command declarations are auto-scanned from command/*.md files, you must still add execution handlers in ponytail.mjs within the command.execute.before hook to define what happens when users invoke the command.
How does Ponytail filter which skill rules appear in each mode?
The filterSkillBodyForMode function in hooks/ponytail-instructions.js scans for bolded mode labels (e.g., **ultra**) at the start of list items and includes only the rules matching the current active mode.
What happens if the SKILL.md file is missing?
If SKILL.md cannot be read, getPonytailInstructions returns a static fallback via getFallbackInstructions(effectiveMode), ensuring the plugin continues functioning even when skill files are unavailable.
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 →