Repository navigation
[Bug] /ponytail never sets a mode in Claude Code: the UserPromptSubmit hook receives the SKILL.md body, not the command text #584
Description
[Bug] /ponytail never sets a mode in Claude Code: the UserPromptSubmit hook receives the SKILL.md body, not the command text
Summary
In Claude Code, /ponytail is dispatched as a skill, not as a commands/*.md slash command. The harness therefore hands UserPromptSubmit a data.prompt that begins with <command-message> / <command-name> tags and then contains the entire body of skills/ponytail/SKILL.md — not the literal string /ponytail:ponytail.
hooks/ponytail-mode-tracker.js:23 tests:
if (/^[/@$]ponytail/.test(prompt)) {That anchor never matches, so setMode() is never called, ~/.claude/.ponytail-active is never written, and everything that reads it (ponytail-instructions.js, ponytail-subagent.js, the statusline) sees no active mode.
This is the same root cause as the (now fixed) #161, seen from the other side. Before #162 landed, the old deactivation regex
if (/\b(stop ponytail|normal mode)\b/i.test(prompt)) {matched ponytail's own SKILL.md, which says on line 29:
unsure. Off only: "stop ponytail" / "normal mode". Default: **full**.
So invoking /ponytail:ponytail printed PONYTAIL MODE OFF. The skill disabled itself by reading itself. #162's isDeactivationCommand fixed that half; the command-detection half is still open.
Environment
- ponytail 4.8.4 (marketplace
DietrichGebert/ponytail, user scope) - Claude Code CLI 2.1.206, Linux
- Hook wired via
hooks/claude-codex-hooks.json→UserPromptSubmit→ponytail-mode-tracker.js
Reproduction
- Install ponytail 4.8.4 in Claude Code, start a session.
- Invoke
/ponytail:ponytail(or/ponytail full) from the slash menu. - Observe: no
PONYTAIL MODE CHANGEDoutput from the hook. ls ~/.claude/.ponytail-active→ No such file or directory.
On 4.2.0, step 3 instead prints PONYTAIL MODE OFF, every single time.
Typing ponytail ultra as ordinary prose still works, because then data.prompt really is the literal text. Only the slash/skill path is affected — which is the documented way to use it.
Why this wasn't caught
#269 reported the commands/*.toml vs commands/*.md mismatch and was closed unmerged, with:
the
UserPromptSubmithook (ponytail-mode-tracker.js) parses the prompt text, so/ponytail ultrapersists the mode whether it came from a skill or was typed raw. It even handles the/ponytail:ponytailform.
The code does handle that form. The harness just never delivers it: when the command comes from a skill, the prompt is the skill body. The skills do show up in the / menu with their arg hints, as you observed — that part is orthogonal, and it's what makes the failure invisible. The menu entry works; the hook behind it doesn't.
Suggested fix
Prefer the <command-name> tag when present, and fall back to the raw prompt otherwise. Claude Code emits a <command-args> tag for at least some commands (empty when there is no argument); the snippet below tolerates its absence:
const raw = data.prompt || '';
const name = raw.match(/<command-name>\s*([^<]+?)\s*<\/command-name>/)?.[1];
const args = raw.match(/<command-args>\s*([^<]*?)\s*<\/command-args>/)?.[1];
const prompt = (name ? `${name} ${args ?? ''}` : raw).trim().toLowerCase();Everything downstream (^[/@$]ponytail, the parts[0] / parts[1] split, isDeactivationCommand) then works unchanged, and isDeactivationCommand keeps protecting the raw-prose path.
Worth a regression test that feeds the tracker a data.prompt built from the real skill body: it fails on both counts today (no activation; and on any build predating #162, a spurious deactivation).
Activity
Merge pull request #1 from JustasMonkev/claude/performance-gold-sugge…
DietrichGebert commentedon Oct 3, 2026
I could not reproduce this on current Claude Code. I logged the raw
UserPromptSubmitpayload on 2.1.186 and on 2.1.288 (print mode): for/ponytail ultraand/ponytail:ponytail litethe hook gets the literal command text, and the mode flag switches correctly. The<command-message>tags plus the SKILL.md body are what the model sees after dispatch, not what the hook gets. Closing; if it still happens for you on a current version, please reopen with the version and whether you picked the command from the slash menu or typed it. Thanks @Zoz92 for the detailed report.