Windows: Plugin hook \${CLAUDE_PLUGIN_ROOT} expansion produces broken bash paths

Resolved 💬 3 comments Opened Feb 16, 2026 by Ahmed-aleryani Closed Feb 20, 2026

Bug

On Windows, when Claude Code expands ${CLAUDE_PLUGIN_ROOT} in plugin hook commands (hooks.json), the resulting path uses Windows backslashes (C:\Users\...). This path is then passed directly to /bin/bash which interprets the backslashes as escape characters, stripping them entirely.

Expected

${CLAUDE_PLUGIN_ROOT} should be converted to POSIX format (/c/Users/...) or properly escaped before passing to bash on Windows.

Actual

Stop hook error: Failed with non-blocking status code: /bin/bash: C:Users<user>.claudepluginscacheclaude-plugins-officialralph-loop<hash>/hooks/stop-hook.sh: No such file or directory

The path C:\Users\<user>\.claude\plugins\cache\... becomes C:Users<user>.claudepluginscache... after bash strips the backslash escapes.

Reproduction

  1. Windows 11 with Git Bash (MSYS2)
  2. Enable any plugin that uses hooks with ${CLAUDE_PLUGIN_ROOT} in the command (e.g., ralph-loop)
  3. End a Claude Code session
  4. Observe the stop hook error

Affected plugin

ralph-loop@claude-plugins-official — its hooks/hooks.json contains:

"command": "${CLAUDE_PLUGIN_ROOT}/hooks/stop-hook.sh"

Suggested fix

In the hook runner, convert CLAUDE_PLUGIN_ROOT to POSIX format before template expansion when the shell is bash/sh on Windows:

// Before expanding ${CLAUDE_PLUGIN_ROOT} in hook commands
if (process.platform === 'win32' && shellIsBash) {
    pluginRoot = pluginRoot.replace(/\/g, '/').replace(/^([A-Z]):/, '/$1');
}

Environment

  • OS: Windows 11 Pro
  • Shell: bash (Git Bash / MSYS2)
  • Claude Code: latest
  • Affected plugin: ralph-loop@claude-plugins-official

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗