Slash-command preprocessor evaluates markdown inline-code-adjacent prose as zsh command substitution
Summary
Claude Code's slash-command preprocessor (the layer that reads \~/.claude/commands/*.md\ when an operator types \/<command>\) appears to mis-parse markdown inline-code spans, treating prose between two adjacent inline-code spans (\ \code1\ \ and \ \code2\ \) as a zsh command-substitution span (\ \...\ \) and eval-ing it through zsh. The eval typically fails on either the upstream \!\ → \\!\ mangling that Claude Code's tool layer applies, OR zsh's NOMATCH glob behavior on \(...)\ patterns, OR both simultaneously.
Reproduction
- Create a markdown command file at \
~/.claude/commands/test-parser-trip.md\with content:
\\\markdownif !\
# Test command
The marker-read heredoc is wrapped with \ to surface import failures loudly (IMP-5). The manifest existence check uses \find_manifest_path\ per CRIT-3.\
\\
- Invoke \
/test-parser-trip\in Claude Code. - Observe stderr:
\\\ to surface import failures loudly (IMP-5). The manifest existence check uses \
Error: Shell command failed for pattern \"!\\": [stderr]\
(eval):1: no matches found: (IMP-5).
\\
Expected behavior
Markdown inline-code spans (\\ \...\ \\) should be treated as opaque text by the slash-command preprocessor, not as zsh command-substitution boundaries. The prose between two adjacent inline-code spans is plain markdown text and should NOT be eval-ed through any shell.
Actual behavior
The preprocessor reads the closing backtick of inline-code #1 and the opening backtick of inline-code #2 as a paired zsh-style \ \...\ \ (command substitution), and eval-s the prose between them. Two documented failure classes can fire:
- \
!\mangling — Claude Code's tool layer escapes \!\to \\!\upstream of the shell, even with \CLAUDE_CODE_SHELL=bash\. zsh \\then errors with \condition expected: !\or \event not found\. - zsh NOMATCH glob — Default zsh behavior treats \
(...)\as KSH-style glob alternation grouping. When no files match, zsh aborts with \(eval):1: no matches found: (...)\.
The combination is high-likelihood when documentation prose contains addendum-marker references like \(IMP-5)\, \(CRIT-3)\, \(SUG-1)\ near inline-code spans containing operators like \!\.
Real-world impact
Discovered during a project's \/compound\ slash command (a /compound-engineering aggregate-knowledge command). One innocuous documentation rewrite added prose like \wrapped with \if !\ to surface import failures loudly (IMP-5)\ to the command file, and \/compound --aggregate\ became completely unusable until the prose was rephrased. The command's actual logic was unchanged.
This is a slash-command authoring footgun: command authors think markdown inline-code spans are safe (they're standard markdown formatting for code references), but Claude Code's preprocessor doesn't fully implement markdown semantics and lets the syntax leak into shell evaluation.
Suggested fix
The preprocessor should treat markdown inline-code spans as terminal tokens — content between matched backtick pairs is opaque, content outside backticks is plain text. Neither should be eval-ed through zsh. If shell evaluation IS required (for variable substitution or similar), it should happen against explicitly-marked template substitution syntax, not implicitly via raw markdown.
Workaround in user-space
The author-side workaround is rephrasing prose to avoid \(...)\ between two inline-code spans (e.g., \(IMP-5)\ → \per IMP-5\). This works but is brittle — operators have to know about the trigger pattern, and any new operator typing slash-command arguments containing parens hits the same parser path.
Versions
- Claude Code CLI: 2.1.126 (visible in jsonl session metadata)
- Shell: zsh on macOS (default behavior with NOMATCH option set)
- \
CLAUDE_CODE_SHELL=bash\does NOT prevent this — escaping is upstream of shell selection
Related context
Companion class — the same upstream parser that escapes \!\ to \\!\ causes a related failure surfaced earlier in the same codebase. Documented at the project level here: \CLAUDE.md\ § \"Shell Conventions for Bash Tool Calls\" → \"Third class\".
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗