Leading multi-slash prompts expand inconsistently (31/35 only the first, 4/35 all); UserPromptSubmit hooks can't see which were dispatched
Description
When a prompt begins with a slash command and contains additional slash commands, the harness expands them inconsistently: usually only the first one is dispatched, but sometimes all of them are. Same CLI version, same prompt shape.
Because a UserPromptSubmit hook receives only the raw prompt string — never the list of commands the harness actually expanded — a hook that wants to handle multi-command prompts has to guess. When it guesses wrong in the "all were expanded" direction, the model re-invokes the Skill tool for commands already present in context and the full SKILL.md is loaded twice.
Evidence
Measured over a local corpus of 154 session transcripts (~/.claude/projects/<project>/*.jsonl). Method: for each session's opening block (before the first assistant entry), count <command-name> wrapper entries — those are what the harness dispatched — and compare against the slash commands still present as plain text in the ARGUMENTS: payload.
35 sessions had a prompt with 2+ slash commands in leading position:
| Input shape | Sessions | What the harness did |
|---|---|---|
| Prompt starts with /, N slash commands | 31 of 35 | expanded only the first; the rest arrived as plain text |
| Prompt starts with /, N slash commands | 4 of 35 | expanded all of them, each with its own <command-name> |
| Slash command mid-sentence | 7 of 7 | expanded none (this is #77868) |
Hypotheses tested and refuted
I tried to find what distinguishes the 4 cases from the 31:
- CLI version — refuted.
2.1.220in both groups, including one "expanded all" session from Aug 3 and 31 "expanded only first" sessions spanning Aug 1–13. - Position of the trailing slash commands — refuted. In the 31 cases, the non-expanded commands sat at character offset 0–1 of the arguments payload, i.e. immediately adjacent to the leading command, not buried in prose. 28 adjacent slash commands were not expanded.
- Number of commands — refuted. Both groups contain 2-command and 3-command prompts.
So from the prompt alone the outcome is unpredictable.
Consequence, measured
One session invoked three agents in a single prompt. Timeline from the transcript:
10:25:23 <command-name>/A</command-name> + body (49,455 chars)
10:25:23 <command-name>/B</command-name> + body (15,598 chars)
10:25:23 <command-name>/C</command-name> + body (19,932 chars)
10:25:28 Skill(B) -> body re-injected (15,307 chars)
10:25:28 Skill(C) -> body re-injected (19,642 chars)
The harness had already expanded all three at :23. Five seconds later the model loaded two of them again — 34,949 characters, roughly 8,700 tokens of exact duplicate — because a UserPromptSubmit hook had reported B and C as "pending", which is correct in 31 of 35 cases and wrong here.
Worth noting the UI side too: a prompt with three leading slash commands renders as three separate input boxes, each echoing the full argument text. That is easy to mistake for the prompt being submitted three times. It isn't — the transcript shows a single submission — but it does make this class of problem hard for a user to diagnose.
Expected behavior
Either of these would resolve it:
- Make expansion deterministic and document it — expand all leading slash commands, or only the first, but the same way every time.
- Tell hooks what happened. Add the list of harness-expanded commands to the
UserPromptSubmithook payload, e.g.:
{
"prompt": "/a /b do the thing",
"expandedCommands": ["a", "b"]
}
Option 2 is the more generally useful one: any hook that reasons about slash commands is currently blind to the harness's own dispatch decision, so this class of double-work is unavoidable from the hook side. Even without fixing the inconsistency, exposing the list makes it harmless.
Workaround in use
The hook stopped asserting what is pending. It now lists candidates and instructs the model to check whether a <command-name> block for each already exists in context before calling Skill(). This works, but it moves a deterministic decision into model judgment.
Environment
- Claude Code
2.1.220 - Windows 11, PowerShell
- Skills/commands registered under
.claude/commands/and.claude/skills/ UserPromptSubmithook reading the JSON payload from stdin
Related
- #77868 — slash commands highlighted mid-sentence but not dispatched. My 7-of-7 "expanded none" row is independent confirmation of that report; this issue is about the different and inconsistent behavior in leading position.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗