Download ponytail/.opencode/plugins/ponytail.mjs from smlflg/CodingAgent: direct link, hf CLI and curl.
- Browser
- Download file 3.77 kB
-
https://huggingface.co/smlflg/CodingAgent/resolve/main/ponytail/.opencode/plugins/ponytail.mjs
- Command line
-
hf download hf://smlflg/CodingAgent/ponytail/.opencode/plugins/ponytail.mjs
-
curl -L -o ponytail.mjs https://huggingface.co/smlflg/CodingAgent/resolve/main/ponytail/.opencode/plugins/ponytail.mjs
3.77 kB
| // ponytail — OpenCode plugin. | |
| // | |
| // Injects the ponytail ruleset into every chat's system prompt at the active | |
| // intensity, persists /ponytail mode switches, and registers slash commands so | |
| // they work when the package is installed from npm. Reuses the shared | |
| // instruction builder so Claude Code, Codex, pi, and OpenCode all read one | |
| // source of truth. | |
| // | |
| // OpenCode loads this as a server plugin — add it to your opencode.json: | |
| // { "plugin": ["@dietrichgebert/ponytail"] } | |
| import { createRequire } from 'module'; | |
| import fs from 'fs'; | |
| import os from 'os'; | |
| import path from 'path'; | |
| import { fileURLToPath } from 'url'; | |
| const __dirname = path.dirname(fileURLToPath(import.meta.url)); | |
| // The shared instruction builder is CommonJS; bridge to it from this ES module. | |
| const require = createRequire(import.meta.url); | |
| const { getPonytailInstructions } = require('../../hooks/ponytail-instructions'); | |
| const { getDefaultMode, normalizePersistedMode } = require('../../hooks/ponytail-config'); | |
| const { parseCommandFile } = require('./ponytail-frontmatter.cjs'); | |
| // OpenCode has no flag-file convention of its own; keep mode beside its config. | |
| const statePath = path.join( | |
| process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config'), | |
| 'opencode', | |
| '.ponytail-active', | |
| ); | |
| function readMode() { | |
| try { | |
| return normalizePersistedMode(fs.readFileSync(statePath, 'utf8').trim()) || getDefaultMode(); | |
| } catch (e) { | |
| return getDefaultMode(); | |
| } | |
| } | |
| function writeMode(mode) { | |
| fs.mkdirSync(path.dirname(statePath), { recursive: true }); | |
| fs.writeFileSync(statePath, mode); | |
| } | |
| export default async ({ client } = {}) => { | |
| const log = (level, message) => { | |
| try { client && client.app && client.app.log({ body: { service: 'ponytail', level, message } }); } catch (e) {} | |
| }; | |
| const ponytailSkillsDir = path.resolve(__dirname, '../../skills'); | |
| return { | |
| // Register slash commands + skills directory. | |
| config: async (config) => { | |
| if (!config.command) config.command = {}; | |
| const commandDir = path.join(__dirname, '..', 'command'); | |
| try { | |
| for (const file of fs.readdirSync(commandDir).filter((f) => f.endsWith('.md'))) { | |
| const name = path.basename(file, '.md'); | |
| const parsed = parseCommandFile(path.join(commandDir, file)); | |
| if (parsed) config.command[name] = parsed; | |
| } | |
| } catch (e) {} | |
| config.skills = config.skills || {}; | |
| config.skills.paths = config.skills.paths || []; | |
| if (!config.skills.paths.includes(ponytailSkillsDir)) { | |
| config.skills.paths.push(ponytailSkillsDir); | |
| } | |
| }, | |
| // Append the ruleset to the system prompt every turn. | |
| 'experimental.chat.system.transform': async (_input, output) => { | |
| const mode = readMode(); | |
| if (mode === 'off') return; | |
| const instructions = getPonytailInstructions(mode); | |
| if (output.system.length > 0) { | |
| output.system[output.system.length - 1] += '\n\n' + instructions; | |
| } else { | |
| output.system.push(instructions); | |
| } | |
| }, | |
| // Persist `/ponytail <level>` so the next turn's injection follows it. | |
| // ponytail: mode applies from the next message, not the current one — the | |
| // transform reads the flag the command writes. Good enough; switch to a | |
| // synchronous store if same-turn switching ever matters. | |
| 'command.execute.before': async (input) => { | |
| if (!input || input.command !== 'ponytail') return; | |
| // `off` is persisted like any mode; the transform reads it and stays silent. | |
| const args = String(input.arguments || '').trim(); | |
| const mode = args ? normalizePersistedMode(args) : getDefaultMode(); | |
| if (!mode) return; | |
| writeMode(mode); | |
| log('info', 'ponytail ' + mode); | |
| }, | |
| }; | |
| }; | |