Download ponytail/hooks/ponytail-runtime.js from smlflg/CodingAgent: direct link, hf CLI and curl.
- Browser
- Download file 3.25 kB
-
https://huggingface.co/smlflg/CodingAgent/resolve/main/ponytail/hooks/ponytail-runtime.js
- Command line
-
hf download hf://smlflg/CodingAgent/ponytail/hooks/ponytail-runtime.js
-
curl -L -o ponytail-runtime.js https://huggingface.co/smlflg/CodingAgent/resolve/main/ponytail/hooks/ponytail-runtime.js
3.25 kB
| const fs = require('fs'); | |
| const path = require('path'); | |
| const os = require('os'); | |
| const { getClaudeDir, getConfigDir } = require('./ponytail-config'); | |
| const STATE_FILE = '.ponytail-active'; | |
| // ponytail: VS Code Copilot never sets COPILOT_PLUGIN_DATA — it only injects | |
| // CLAUDE_PLUGIN_ROOT, pointed at an install path under .vscode/agent-plugins/ | |
| // (#528). Without this fallback isCopilot was false, so ponytail assumed | |
| // native Claude Code and emitted the statusline nudge, which VS Code Copilot | |
| // doesn't read. | |
| function isVsCodeCopilotRoot(pluginRoot) { | |
| if (!pluginRoot) return false; | |
| return pluginRoot.split(/[\\/]+/).includes('agent-plugins') && | |
| pluginRoot.toLowerCase().includes('.vscode'); | |
| } | |
| const isCopilot = Boolean(process.env.COPILOT_PLUGIN_DATA) || | |
| isVsCodeCopilotRoot(process.env.CLAUDE_PLUGIN_ROOT); | |
| const isCodex = !isCopilot && Boolean(process.env.PLUGIN_DATA); | |
| const isQoder = !isCopilot && !isCodex && Boolean(process.env.QODER_SESSION_ID); | |
| let stateDir = getClaudeDir(); | |
| if (isCodex) stateDir = process.env.PLUGIN_DATA; | |
| // COPILOT_PLUGIN_DATA is unset under VS Code Copilot, so fall back to | |
| // getClaudeDir() rather than building a path from undefined. | |
| if (isCopilot) stateDir = process.env.COPILOT_PLUGIN_DATA || getClaudeDir(); | |
| if (isQoder) stateDir = path.join(os.homedir(), '.qoder'); | |
| const statePath = path.join(stateDir, STATE_FILE); | |
| function setMode(mode) { | |
| fs.mkdirSync(path.dirname(statePath), { recursive: true }); | |
| fs.writeFileSync(statePath, mode); | |
| } | |
| function clearMode() { | |
| try { fs.unlinkSync(statePath); } catch (e) {} | |
| } | |
| // Live mode written by activate/mode-tracker. Absent flag = ponytail off. | |
| function readMode() { | |
| try { | |
| return fs.readFileSync(statePath, 'utf8').trim() || null; | |
| } catch (e) { | |
| return null; | |
| } | |
| } | |
| function writeHookOutput(event, mode, context = '') { | |
| if (isCopilot) { | |
| // Copilot reads additionalContext on SessionStart; ignores output elsewhere. | |
| process.stdout.write(JSON.stringify( | |
| event === 'SessionStart' && context ? { additionalContext: context } : {})); | |
| return; | |
| } | |
| if (isCodex) { | |
| const output = { systemMessage: `PONYTAIL:${mode.toUpperCase()}` }; | |
| if (context) { | |
| output.hookSpecificOutput = { | |
| hookEventName: event, | |
| additionalContext: context, | |
| }; | |
| } | |
| process.stdout.write(JSON.stringify(output)); | |
| return; | |
| } | |
| if (isQoder) { | |
| // Qoder: hookSpecificOutput JSON, same shape as Codex minus systemMessage. | |
| // UserPromptSubmit additionalContext is injected into the Agent's conversation. | |
| const output = {}; | |
| if (context) { | |
| output.hookSpecificOutput = { | |
| hookEventName: event, | |
| additionalContext: context, | |
| }; | |
| } | |
| process.stdout.write(JSON.stringify(output)); | |
| return; | |
| } | |
| // Native Claude: SessionStart accepts raw stdout, but SubagentStart needs the | |
| // hookSpecificOutput JSON form or the context is dropped. | |
| if (event === 'SubagentStart') { | |
| process.stdout.write(JSON.stringify( | |
| { hookSpecificOutput: { hookEventName: event, additionalContext: context } })); | |
| return; | |
| } | |
| process.stdout.write(context); | |
| } | |
| module.exports = { | |
| clearMode, | |
| isCodex, | |
| isCopilot, | |
| isQoder, | |
| readMode, | |
| setMode, | |
| writeHookOutput, | |
| }; | |