Skip to content

Update SessionStart hook command to handle CLAUDE_PLUGIN_ROOT template substitution path across Linux, Windows with WSL bash.exe and Windows with Git Bash#421

Closed
pedropaulovc wants to merge 3 commits intoobra:mainfrom
pedropaulovc:hooks-polyglot
Closed

Conversation

@pedropaulovc
Copy link

@pedropaulovc pedropaulovc commented Feb 5, 2026

Claude Code injects CLAUDE_PLUGIN_ROOT via template substitution BEFORE the shell runs therefore. Furthermore, command lines that match regex \.sh(\s|$|") are automatically handed over to bash for execution, even on Windows. The choice of which bash instance will be used on Windows depends on the user's + system's PATH sort order and if they have WSL installed with a distro or not. Finally, Windows paths contain backslashes and they conflict with Bash's string escaping.

This command must work across three bash environments:

  1. Git Bash on Windows: OSTYPE=msys, OS=Windows_NT, paths like /c/Users/...
  2. WSL bash: OSTYPE=linux-gnu, OS unset, paths like /mnt/c/Users/...
  3. Native Linux /bin/bash: OSTYPE=linux-gnu, no drive letter conversion needed

The command was augmented to parse the OS environment variable to differentiate between WSL and Git Bash and parse the plugin path to detect if it's running on Linux. The final script does not require any dependency such as node or python to work.

Motivation and Context

The SessionStart hook is important to ensure that Claude Code will use its new skills consistently from the beginning.

How Has This Been Tested?

  1. WSL Bash: Keep C:\Windows\System32\bash.exe as first option. Run claude.exe --debug and inspect the logs for the hook output.
  2. Git Bash on Windows: Move C:\Program Files\Git\bin to top of system's PATH. Run claude.exe --debug and inspect the logs for the hook output.
  3. Native Linux: Run claude --debug on Ubuntu 24.04.3 LTS

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Fixes #420, #417, #414, #413

Summary by CodeRabbit

  • Improvements
    • Strengthened cross-platform plugin execution with enhanced path normalization that works seamlessly across Windows, macOS, and Linux environments
    • Added diagnostic logging capabilities to provide detailed visibility into plugin initialization processes and path resolution steps
    • Improved reliability of script invocation through more robust environment-aware path handling

Copilot AI review requested due to automatic review settings February 5, 2026 06:15
@coderabbitai
Copy link

coderabbitai bot commented Feb 5, 2026

📝 Walkthrough

Walkthrough

Replaces the SessionStart hook command in hooks/hooks.json with a bash -c inline script that normalizes ${CLAUDE_PLUGIN_ROOT} across environments (Windows, WSL, Linux), adds _comment documenting the approach, and adds _debug_echo to emit runtime diagnostics.

Changes

Cohort / File(s) Summary
SessionStart Hook Configuration
hooks/hooks.json
Replaced direct session-start.sh invocation with a bash -c inline resolver that: assigns RAW, normalizes slashes to PLUGIN_UNIX_PATH, extracts DRIVE and REST, computes RESOLVED based on OS/path shape, then executes the resolved session-start.sh. Added _comment and _debug_echo fields; kept async: true.

Sequence Diagram(s)

(Skipped — change is a single-file hook command replacement without multi-component sequential interactions requiring diagramming.)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Possibly related PRs

Poem

🐰
I hopped through slashes, backslashes and more,
Tuned RAW to PLUGIN_UNIX, tracked DRIVE to the core.
With debug echoes bright and RESOLVED paths set,
Hooks wake and run—no more path upset. ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: updating the SessionStart hook command to handle path normalization across three bash environments (Git Bash, WSL bash, native Linux).
Linked Issues check ✅ Passed The PR successfully addresses issue #420 by implementing runtime path resolution that detects bash environment (Git Bash vs WSL vs Linux) and converts CLAUDE_PLUGIN_ROOT to the correct path format for each environment, resolving the backslash-stripping bug without external dependencies.
Out of Scope Changes check ✅ Passed All changes are scoped to the SessionStart hook in hooks.json and directly address the cross-platform path handling issue. The additions (_comment, _debug_echo, updated command) are all necessary to implement the environment-aware path resolution solution.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@hooks/hooks.json`:
- Line 32: The command string uses RAW="${CLAUDE_PLUGIN_ROOT}" which leaves the
substituted path open to further bash expansion; change the RAW assignment to
use single quotes so the substituted value is single-quoted (e.g.,
RAW='${CLAUDE_PLUGIN_ROOT}') and adjust the outer quoting/escaping in the
"command" value so the shell-level single-quote wrapping remains valid; update
the same quoted occurrences that consume RAW (PLUGIN_UNIX_PATH, DRIVE, REST,
RESOLVED and the final call to hooks/session-start.sh) to continue to work with
the single-quoted RAW value and ensure no additional bash interpolation occurs.
🧹 Nitpick comments (1)
hooks/hooks.json (1)

30-30: Debug field is defined but not invoked.

The _debug_echo field stores a useful debug snippet, but it isn't executed by the command. Consider adding a brief note in the _comment explaining how to use it (e.g., "paste into command after RAW=...; to debug"), or alternatively, you could provide a separate command_debug variant that includes it inline.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the SessionStart hook to handle CLAUDE_PLUGIN_ROOT template substitution and path conversion across three bash environments: Git Bash on Windows, WSL bash, and native Linux bash. The change addresses Windows path handling issues (#420), HCS sandbox blocking (#417), and improves reliability of bash execution on Windows.

Changes:

  • Replaced direct script invocation with inline bash command that performs path conversion
  • Added logic to detect platform (Git Bash vs WSL vs Linux) and convert Windows paths to appropriate Unix-style formats (/c/... for Git Bash, /mnt/c/... for WSL)
  • Avoided .sh extension in command to bypass Claude Code 2.1.x's automatic bash routing through HCS sandbox
  • Included comprehensive documentation in comments explaining the template substitution and path conversion logic

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@obra obra added windows hooks Hook system (SessionStart, Stop, etc.) bug Something isn't working claude-code Claude Code (Anthropic CLI) issues labels Feb 5, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@hooks/hooks.json`:
- Line 32: The REST variable can lack a leading slash when backslashes are
stripped; inside the command where RAW, PLUGIN_UNIX_PATH, DRIVE, REST and
RESOLVED are computed, normalize REST to ensure exactly one leading slash (e.g.,
set REST="/${REST#/}" after computing REST and before using it to build
RESOLVED) so paths like C:Users... become /c/Users... and existing /Users/...
stay unchanged.

@obra
Copy link
Owner

obra commented Feb 5, 2026

Thank you for this — the path normalization logic (detecting drive letters, converting for Git Bash vs WSL vs native Linux) is well thought out.

Unfortunately the core issue is that Claude Code strips backslashes from ${CLAUDE_PLUGIN_ROOT} during template substitution before the shell runs, so by the time the bash -c command executes, the path is already mangled. We're tracking that as an upstream CC bug at #420.

We've addressed the fixable parts with async: true (prevents freeze) and a faster escape_for_json.

@obra obra closed this Feb 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working claude-code Claude Code (Anthropic CLI) issues hooks Hook system (SessionStart, Stop, etc.) windows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows: SessionStart hook fails due to Claude Code path handling bug

2 participants