Files
superpowers/docs/windows/polyglot-hooks.md

149 lines
5.8 KiB
Markdown
Raw Normal View History

Add Windows support for plugin hooks (#134) * feat: Add Windows support for session-start hook - Create polyglot session-start.cmd that works in both CMD and bash - Update hooks.json to use the .cmd polyglot launcher - Replace sed/awk with pure bash for JSON escaping (Windows compatibility) The polyglot script uses a heredoc trick: - CMD sees the @echo off block and runs bash.exe with cygpath conversion - Bash sees a heredoc and skips to the Unix section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Add execute permission to session-start.cmd for Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: Add comprehensive polyglot hooks documentation - Add docs/windows/polyglot-hooks.md explaining the cross-platform technique - Add reusable run-hook.cmd wrapper for parameterized hook execution - Document how the polyglot works in CMD vs bash - Include troubleshooting section and related GitHub issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add polyglot hook test script for macOS/Linux Run ./test-polyglot.sh from repo root to verify: - Required files exist with execute permissions - Simple wrapper (session-start.cmd) produces valid JSON - Parameterized wrapper (run-hook.cmd) works - Heredoc correctly skips CMD block on Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Use direct pipe to jq in test to avoid variable escaping issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Use single reusable run-hook.cmd for all hooks - Remove session-start.cmd in favor of run-hook.cmd - Update hooks.json to use: run-hook.cmd session-start.sh - Simplify test script to only test run-hook.cmd This makes it easy to add more hooks - just create the .sh file and add a line to hooks.json pointing to run-hook.cmd with the script name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Simplify run-hook.cmd CMD block Pass path directly to bash instead of using cygpath in a subshell. The complex quoting was causing issues on Windows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: Remove test-polyglot.sh Testing complete - polyglot hooks work on Windows and macOS. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 15:42:12 -08:00
# Cross-Platform Polyglot Hooks for Claude Code
Claude Code plugins need hooks that work on Windows, macOS, and Linux. This document describes the single generic dispatcher pattern used in `hooks/run-hook.cmd`.
> **Authoritative source:** `hooks/run-hook.cmd` is the canonical implementation. When this document and the code diverge, trust the code.
Add Windows support for plugin hooks (#134) * feat: Add Windows support for session-start hook - Create polyglot session-start.cmd that works in both CMD and bash - Update hooks.json to use the .cmd polyglot launcher - Replace sed/awk with pure bash for JSON escaping (Windows compatibility) The polyglot script uses a heredoc trick: - CMD sees the @echo off block and runs bash.exe with cygpath conversion - Bash sees a heredoc and skips to the Unix section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Add execute permission to session-start.cmd for Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: Add comprehensive polyglot hooks documentation - Add docs/windows/polyglot-hooks.md explaining the cross-platform technique - Add reusable run-hook.cmd wrapper for parameterized hook execution - Document how the polyglot works in CMD vs bash - Include troubleshooting section and related GitHub issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add polyglot hook test script for macOS/Linux Run ./test-polyglot.sh from repo root to verify: - Required files exist with execute permissions - Simple wrapper (session-start.cmd) produces valid JSON - Parameterized wrapper (run-hook.cmd) works - Heredoc correctly skips CMD block on Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Use direct pipe to jq in test to avoid variable escaping issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Use single reusable run-hook.cmd for all hooks - Remove session-start.cmd in favor of run-hook.cmd - Update hooks.json to use: run-hook.cmd session-start.sh - Simplify test script to only test run-hook.cmd This makes it easy to add more hooks - just create the .sh file and add a line to hooks.json pointing to run-hook.cmd with the script name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Simplify run-hook.cmd CMD block Pass path directly to bash instead of using cygpath in a subshell. The complex quoting was causing issues on Windows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: Remove test-polyglot.sh Testing complete - polyglot hooks work on Windows and macOS. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 15:42:12 -08:00
## The Problem
Claude Code runs hook commands through the system's default shell:
- **Windows**: CMD.exe
- **macOS/Linux**: bash or sh
This creates several challenges:
1. **Script execution**: Windows CMD can't execute `.sh` files directly
Add Windows support for plugin hooks (#134) * feat: Add Windows support for session-start hook - Create polyglot session-start.cmd that works in both CMD and bash - Update hooks.json to use the .cmd polyglot launcher - Replace sed/awk with pure bash for JSON escaping (Windows compatibility) The polyglot script uses a heredoc trick: - CMD sees the @echo off block and runs bash.exe with cygpath conversion - Bash sees a heredoc and skips to the Unix section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Add execute permission to session-start.cmd for Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: Add comprehensive polyglot hooks documentation - Add docs/windows/polyglot-hooks.md explaining the cross-platform technique - Add reusable run-hook.cmd wrapper for parameterized hook execution - Document how the polyglot works in CMD vs bash - Include troubleshooting section and related GitHub issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add polyglot hook test script for macOS/Linux Run ./test-polyglot.sh from repo root to verify: - Required files exist with execute permissions - Simple wrapper (session-start.cmd) produces valid JSON - Parameterized wrapper (run-hook.cmd) works - Heredoc correctly skips CMD block on Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Use direct pipe to jq in test to avoid variable escaping issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Use single reusable run-hook.cmd for all hooks - Remove session-start.cmd in favor of run-hook.cmd - Update hooks.json to use: run-hook.cmd session-start.sh - Simplify test script to only test run-hook.cmd This makes it easy to add more hooks - just create the .sh file and add a line to hooks.json pointing to run-hook.cmd with the script name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Simplify run-hook.cmd CMD block Pass path directly to bash instead of using cygpath in a subshell. The complex quoting was causing issues on Windows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: Remove test-polyglot.sh Testing complete - polyglot hooks work on Windows and macOS. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 15:42:12 -08:00
2. **Path format**: Windows uses backslashes (`C:\path`), Unix uses forward slashes (`/path`)
3. **Environment variables**: `$VAR` syntax doesn't work in CMD
4. **`.sh` auto-prepend**: Claude Code on Windows automatically prepends `bash` to any command that contains `.sh` in its path — this interferes with the dispatcher if scripts have extensions
Add Windows support for plugin hooks (#134) * feat: Add Windows support for session-start hook - Create polyglot session-start.cmd that works in both CMD and bash - Update hooks.json to use the .cmd polyglot launcher - Replace sed/awk with pure bash for JSON escaping (Windows compatibility) The polyglot script uses a heredoc trick: - CMD sees the @echo off block and runs bash.exe with cygpath conversion - Bash sees a heredoc and skips to the Unix section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Add execute permission to session-start.cmd for Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: Add comprehensive polyglot hooks documentation - Add docs/windows/polyglot-hooks.md explaining the cross-platform technique - Add reusable run-hook.cmd wrapper for parameterized hook execution - Document how the polyglot works in CMD vs bash - Include troubleshooting section and related GitHub issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add polyglot hook test script for macOS/Linux Run ./test-polyglot.sh from repo root to verify: - Required files exist with execute permissions - Simple wrapper (session-start.cmd) produces valid JSON - Parameterized wrapper (run-hook.cmd) works - Heredoc correctly skips CMD block on Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Use direct pipe to jq in test to avoid variable escaping issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Use single reusable run-hook.cmd for all hooks - Remove session-start.cmd in favor of run-hook.cmd - Update hooks.json to use: run-hook.cmd session-start.sh - Simplify test script to only test run-hook.cmd This makes it easy to add more hooks - just create the .sh file and add a line to hooks.json pointing to run-hook.cmd with the script name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Simplify run-hook.cmd CMD block Pass path directly to bash instead of using cygpath in a subshell. The complex quoting was causing issues on Windows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: Remove test-polyglot.sh Testing complete - polyglot hooks work on Windows and macOS. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 15:42:12 -08:00
## The Solution: Extensionless Scripts + Single Generic Dispatcher
Add Windows support for plugin hooks (#134) * feat: Add Windows support for session-start hook - Create polyglot session-start.cmd that works in both CMD and bash - Update hooks.json to use the .cmd polyglot launcher - Replace sed/awk with pure bash for JSON escaping (Windows compatibility) The polyglot script uses a heredoc trick: - CMD sees the @echo off block and runs bash.exe with cygpath conversion - Bash sees a heredoc and skips to the Unix section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Add execute permission to session-start.cmd for Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: Add comprehensive polyglot hooks documentation - Add docs/windows/polyglot-hooks.md explaining the cross-platform technique - Add reusable run-hook.cmd wrapper for parameterized hook execution - Document how the polyglot works in CMD vs bash - Include troubleshooting section and related GitHub issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add polyglot hook test script for macOS/Linux Run ./test-polyglot.sh from repo root to verify: - Required files exist with execute permissions - Simple wrapper (session-start.cmd) produces valid JSON - Parameterized wrapper (run-hook.cmd) works - Heredoc correctly skips CMD block on Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Use direct pipe to jq in test to avoid variable escaping issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Use single reusable run-hook.cmd for all hooks - Remove session-start.cmd in favor of run-hook.cmd - Update hooks.json to use: run-hook.cmd session-start.sh - Simplify test script to only test run-hook.cmd This makes it easy to add more hooks - just create the .sh file and add a line to hooks.json pointing to run-hook.cmd with the script name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Simplify run-hook.cmd CMD block Pass path directly to bash instead of using cygpath in a subshell. The complex quoting was causing issues on Windows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: Remove test-polyglot.sh Testing complete - polyglot hooks work on Windows and macOS. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 15:42:12 -08:00
The repo uses one generic `run-hook.cmd` dispatcher for all hooks. Hook scripts are **extensionless** (`session-start`, not `session-start.sh`). This is deliberate: it prevents Claude Code's Windows auto-detection from prepending `bash` to the dispatcher command and breaking it.
Add Windows support for plugin hooks (#134) * feat: Add Windows support for session-start hook - Create polyglot session-start.cmd that works in both CMD and bash - Update hooks.json to use the .cmd polyglot launcher - Replace sed/awk with pure bash for JSON escaping (Windows compatibility) The polyglot script uses a heredoc trick: - CMD sees the @echo off block and runs bash.exe with cygpath conversion - Bash sees a heredoc and skips to the Unix section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Add execute permission to session-start.cmd for Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: Add comprehensive polyglot hooks documentation - Add docs/windows/polyglot-hooks.md explaining the cross-platform technique - Add reusable run-hook.cmd wrapper for parameterized hook execution - Document how the polyglot works in CMD vs bash - Include troubleshooting section and related GitHub issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add polyglot hook test script for macOS/Linux Run ./test-polyglot.sh from repo root to verify: - Required files exist with execute permissions - Simple wrapper (session-start.cmd) produces valid JSON - Parameterized wrapper (run-hook.cmd) works - Heredoc correctly skips CMD block on Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Use direct pipe to jq in test to avoid variable escaping issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Use single reusable run-hook.cmd for all hooks - Remove session-start.cmd in favor of run-hook.cmd - Update hooks.json to use: run-hook.cmd session-start.sh - Simplify test script to only test run-hook.cmd This makes it easy to add more hooks - just create the .sh file and add a line to hooks.json pointing to run-hook.cmd with the script name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Simplify run-hook.cmd CMD block Pass path directly to bash instead of using cygpath in a subshell. The complex quoting was causing issues on Windows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: Remove test-polyglot.sh Testing complete - polyglot hooks work on Windows and macOS. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 15:42:12 -08:00
### File Structure
Add Windows support for plugin hooks (#134) * feat: Add Windows support for session-start hook - Create polyglot session-start.cmd that works in both CMD and bash - Update hooks.json to use the .cmd polyglot launcher - Replace sed/awk with pure bash for JSON escaping (Windows compatibility) The polyglot script uses a heredoc trick: - CMD sees the @echo off block and runs bash.exe with cygpath conversion - Bash sees a heredoc and skips to the Unix section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Add execute permission to session-start.cmd for Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: Add comprehensive polyglot hooks documentation - Add docs/windows/polyglot-hooks.md explaining the cross-platform technique - Add reusable run-hook.cmd wrapper for parameterized hook execution - Document how the polyglot works in CMD vs bash - Include troubleshooting section and related GitHub issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add polyglot hook test script for macOS/Linux Run ./test-polyglot.sh from repo root to verify: - Required files exist with execute permissions - Simple wrapper (session-start.cmd) produces valid JSON - Parameterized wrapper (run-hook.cmd) works - Heredoc correctly skips CMD block on Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Use direct pipe to jq in test to avoid variable escaping issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Use single reusable run-hook.cmd for all hooks - Remove session-start.cmd in favor of run-hook.cmd - Update hooks.json to use: run-hook.cmd session-start.sh - Simplify test script to only test run-hook.cmd This makes it easy to add more hooks - just create the .sh file and add a line to hooks.json pointing to run-hook.cmd with the script name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Simplify run-hook.cmd CMD block Pass path directly to bash instead of using cygpath in a subshell. The complex quoting was causing issues on Windows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: Remove test-polyglot.sh Testing complete - polyglot hooks work on Windows and macOS. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 15:42:12 -08:00
```
hooks/
├── hooks.json # Points to run-hook.cmd with extensionless script name
├── run-hook.cmd # Cross-platform dispatcher (the polyglot wrapper)
└── session-start # Actual hook logic — extensionless bash script
Add Windows support for plugin hooks (#134) * feat: Add Windows support for session-start hook - Create polyglot session-start.cmd that works in both CMD and bash - Update hooks.json to use the .cmd polyglot launcher - Replace sed/awk with pure bash for JSON escaping (Windows compatibility) The polyglot script uses a heredoc trick: - CMD sees the @echo off block and runs bash.exe with cygpath conversion - Bash sees a heredoc and skips to the Unix section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Add execute permission to session-start.cmd for Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: Add comprehensive polyglot hooks documentation - Add docs/windows/polyglot-hooks.md explaining the cross-platform technique - Add reusable run-hook.cmd wrapper for parameterized hook execution - Document how the polyglot works in CMD vs bash - Include troubleshooting section and related GitHub issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add polyglot hook test script for macOS/Linux Run ./test-polyglot.sh from repo root to verify: - Required files exist with execute permissions - Simple wrapper (session-start.cmd) produces valid JSON - Parameterized wrapper (run-hook.cmd) works - Heredoc correctly skips CMD block on Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Use direct pipe to jq in test to avoid variable escaping issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Use single reusable run-hook.cmd for all hooks - Remove session-start.cmd in favor of run-hook.cmd - Update hooks.json to use: run-hook.cmd session-start.sh - Simplify test script to only test run-hook.cmd This makes it easy to add more hooks - just create the .sh file and add a line to hooks.json pointing to run-hook.cmd with the script name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Simplify run-hook.cmd CMD block Pass path directly to bash instead of using cygpath in a subshell. The complex quoting was causing issues on Windows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: Remove test-polyglot.sh Testing complete - polyglot hooks work on Windows and macOS. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 15:42:12 -08:00
```
### hooks.json
```json
{
"hooks": {
"SessionStart": [
{
"matcher": "startup|clear|compact",
Add Windows support for plugin hooks (#134) * feat: Add Windows support for session-start hook - Create polyglot session-start.cmd that works in both CMD and bash - Update hooks.json to use the .cmd polyglot launcher - Replace sed/awk with pure bash for JSON escaping (Windows compatibility) The polyglot script uses a heredoc trick: - CMD sees the @echo off block and runs bash.exe with cygpath conversion - Bash sees a heredoc and skips to the Unix section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Add execute permission to session-start.cmd for Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: Add comprehensive polyglot hooks documentation - Add docs/windows/polyglot-hooks.md explaining the cross-platform technique - Add reusable run-hook.cmd wrapper for parameterized hook execution - Document how the polyglot works in CMD vs bash - Include troubleshooting section and related GitHub issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add polyglot hook test script for macOS/Linux Run ./test-polyglot.sh from repo root to verify: - Required files exist with execute permissions - Simple wrapper (session-start.cmd) produces valid JSON - Parameterized wrapper (run-hook.cmd) works - Heredoc correctly skips CMD block on Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Use direct pipe to jq in test to avoid variable escaping issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Use single reusable run-hook.cmd for all hooks - Remove session-start.cmd in favor of run-hook.cmd - Update hooks.json to use: run-hook.cmd session-start.sh - Simplify test script to only test run-hook.cmd This makes it easy to add more hooks - just create the .sh file and add a line to hooks.json pointing to run-hook.cmd with the script name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Simplify run-hook.cmd CMD block Pass path directly to bash instead of using cygpath in a subshell. The complex quoting was causing issues on Windows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: Remove test-polyglot.sh Testing complete - polyglot hooks work on Windows and macOS. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 15:42:12 -08:00
"hooks": [
{
"type": "command",
"command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cmd\" session-start",
"async": false
Add Windows support for plugin hooks (#134) * feat: Add Windows support for session-start hook - Create polyglot session-start.cmd that works in both CMD and bash - Update hooks.json to use the .cmd polyglot launcher - Replace sed/awk with pure bash for JSON escaping (Windows compatibility) The polyglot script uses a heredoc trick: - CMD sees the @echo off block and runs bash.exe with cygpath conversion - Bash sees a heredoc and skips to the Unix section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Add execute permission to session-start.cmd for Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: Add comprehensive polyglot hooks documentation - Add docs/windows/polyglot-hooks.md explaining the cross-platform technique - Add reusable run-hook.cmd wrapper for parameterized hook execution - Document how the polyglot works in CMD vs bash - Include troubleshooting section and related GitHub issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add polyglot hook test script for macOS/Linux Run ./test-polyglot.sh from repo root to verify: - Required files exist with execute permissions - Simple wrapper (session-start.cmd) produces valid JSON - Parameterized wrapper (run-hook.cmd) works - Heredoc correctly skips CMD block on Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Use direct pipe to jq in test to avoid variable escaping issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Use single reusable run-hook.cmd for all hooks - Remove session-start.cmd in favor of run-hook.cmd - Update hooks.json to use: run-hook.cmd session-start.sh - Simplify test script to only test run-hook.cmd This makes it easy to add more hooks - just create the .sh file and add a line to hooks.json pointing to run-hook.cmd with the script name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Simplify run-hook.cmd CMD block Pass path directly to bash instead of using cygpath in a subshell. The complex quoting was causing issues on Windows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: Remove test-polyglot.sh Testing complete - polyglot hooks work on Windows and macOS. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 15:42:12 -08:00
}
]
}
]
}
}
```
The path is quoted because `${CLAUDE_PLUGIN_ROOT}` may contain spaces.
## How `run-hook.cmd` Works at a High Level
`run-hook.cmd` is a polyglot script: Windows treats the first block as batch
commands, while Unix shells treat that block as a no-op heredoc and continue
after it.
Do not copy an implementation from this document. Read `hooks/run-hook.cmd`
directly when changing the dispatcher, and run `tests/hooks/test-session-start.sh`
afterward.
### How it works on Windows (CMD.exe)
Add Windows support for plugin hooks (#134) * feat: Add Windows support for session-start hook - Create polyglot session-start.cmd that works in both CMD and bash - Update hooks.json to use the .cmd polyglot launcher - Replace sed/awk with pure bash for JSON escaping (Windows compatibility) The polyglot script uses a heredoc trick: - CMD sees the @echo off block and runs bash.exe with cygpath conversion - Bash sees a heredoc and skips to the Unix section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Add execute permission to session-start.cmd for Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: Add comprehensive polyglot hooks documentation - Add docs/windows/polyglot-hooks.md explaining the cross-platform technique - Add reusable run-hook.cmd wrapper for parameterized hook execution - Document how the polyglot works in CMD vs bash - Include troubleshooting section and related GitHub issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add polyglot hook test script for macOS/Linux Run ./test-polyglot.sh from repo root to verify: - Required files exist with execute permissions - Simple wrapper (session-start.cmd) produces valid JSON - Parameterized wrapper (run-hook.cmd) works - Heredoc correctly skips CMD block on Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Use direct pipe to jq in test to avoid variable escaping issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Use single reusable run-hook.cmd for all hooks - Remove session-start.cmd in favor of run-hook.cmd - Update hooks.json to use: run-hook.cmd session-start.sh - Simplify test script to only test run-hook.cmd This makes it easy to add more hooks - just create the .sh file and add a line to hooks.json pointing to run-hook.cmd with the script name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Simplify run-hook.cmd CMD block Pass path directly to bash instead of using cygpath in a subshell. The complex quoting was causing issues on Windows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: Remove test-polyglot.sh Testing complete - polyglot hooks work on Windows and macOS. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 15:42:12 -08:00
1. The batch section validates the script name and resolves the hook directory
from the dispatcher's own location.
2. It tries bash in three places:
- `C:\Program Files\Git\bin\bash.exe`
- `C:\Program Files (x86)\Git\bin\bash.exe`
- `bash` on `PATH` (MSYS2, Cygwin, or a non-default Git install)
3. If bash is found, it runs the named extensionless hook script from the hooks
directory.
4. If no bash is found, the dispatcher exits `0` silently — the plugin
continues working, it just skips the hook.
5. `exit /b` stops CMD before it reaches the Unix section.
Add Windows support for plugin hooks (#134) * feat: Add Windows support for session-start hook - Create polyglot session-start.cmd that works in both CMD and bash - Update hooks.json to use the .cmd polyglot launcher - Replace sed/awk with pure bash for JSON escaping (Windows compatibility) The polyglot script uses a heredoc trick: - CMD sees the @echo off block and runs bash.exe with cygpath conversion - Bash sees a heredoc and skips to the Unix section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Add execute permission to session-start.cmd for Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: Add comprehensive polyglot hooks documentation - Add docs/windows/polyglot-hooks.md explaining the cross-platform technique - Add reusable run-hook.cmd wrapper for parameterized hook execution - Document how the polyglot works in CMD vs bash - Include troubleshooting section and related GitHub issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add polyglot hook test script for macOS/Linux Run ./test-polyglot.sh from repo root to verify: - Required files exist with execute permissions - Simple wrapper (session-start.cmd) produces valid JSON - Parameterized wrapper (run-hook.cmd) works - Heredoc correctly skips CMD block on Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Use direct pipe to jq in test to avoid variable escaping issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Use single reusable run-hook.cmd for all hooks - Remove session-start.cmd in favor of run-hook.cmd - Update hooks.json to use: run-hook.cmd session-start.sh - Simplify test script to only test run-hook.cmd This makes it easy to add more hooks - just create the .sh file and add a line to hooks.json pointing to run-hook.cmd with the script name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Simplify run-hook.cmd CMD block Pass path directly to bash instead of using cygpath in a subshell. The complex quoting was causing issues on Windows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: Remove test-polyglot.sh Testing complete - polyglot hooks work on Windows and macOS. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 15:42:12 -08:00
### How it works on Unix (bash/sh)
Add Windows support for plugin hooks (#134) * feat: Add Windows support for session-start hook - Create polyglot session-start.cmd that works in both CMD and bash - Update hooks.json to use the .cmd polyglot launcher - Replace sed/awk with pure bash for JSON escaping (Windows compatibility) The polyglot script uses a heredoc trick: - CMD sees the @echo off block and runs bash.exe with cygpath conversion - Bash sees a heredoc and skips to the Unix section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Add execute permission to session-start.cmd for Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: Add comprehensive polyglot hooks documentation - Add docs/windows/polyglot-hooks.md explaining the cross-platform technique - Add reusable run-hook.cmd wrapper for parameterized hook execution - Document how the polyglot works in CMD vs bash - Include troubleshooting section and related GitHub issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add polyglot hook test script for macOS/Linux Run ./test-polyglot.sh from repo root to verify: - Required files exist with execute permissions - Simple wrapper (session-start.cmd) produces valid JSON - Parameterized wrapper (run-hook.cmd) works - Heredoc correctly skips CMD block on Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Use direct pipe to jq in test to avoid variable escaping issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Use single reusable run-hook.cmd for all hooks - Remove session-start.cmd in favor of run-hook.cmd - Update hooks.json to use: run-hook.cmd session-start.sh - Simplify test script to only test run-hook.cmd This makes it easy to add more hooks - just create the .sh file and add a line to hooks.json pointing to run-hook.cmd with the script name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Simplify run-hook.cmd CMD block Pass path directly to bash instead of using cygpath in a subshell. The complex quoting was causing issues on Windows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: Remove test-polyglot.sh Testing complete - polyglot hooks work on Windows and macOS. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 15:42:12 -08:00
1. `: << 'CMDBLOCK'` opens a heredoc on a no-op command.
2. The entire CMD batch block is consumed by the heredoc and ignored.
3. After `CMDBLOCK`, bash resolves the script directory and `exec`s the named
extensionless script directly.
### Key design decisions
| Decision | Why |
|----------|-----|
| Extensionless scripts | Prevents Claude Code's Windows `.sh`-auto-prepend from interfering with the dispatcher command |
| No `-l` (login shell) | Not needed; hook scripts should be self-contained and not depend on login-shell PATH setup |
| No `cygpath` | Bash receives the Windows path directly and handles it correctly; `cygpath` was needed by the old `-c "..."` invocation pattern, not by direct exec |
| Silent exit on no-bash | Avoids breaking the plugin for users who don't have Git for Windows; hook context injection is skipped gracefully |
Add Windows support for plugin hooks (#134) * feat: Add Windows support for session-start hook - Create polyglot session-start.cmd that works in both CMD and bash - Update hooks.json to use the .cmd polyglot launcher - Replace sed/awk with pure bash for JSON escaping (Windows compatibility) The polyglot script uses a heredoc trick: - CMD sees the @echo off block and runs bash.exe with cygpath conversion - Bash sees a heredoc and skips to the Unix section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Add execute permission to session-start.cmd for Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: Add comprehensive polyglot hooks documentation - Add docs/windows/polyglot-hooks.md explaining the cross-platform technique - Add reusable run-hook.cmd wrapper for parameterized hook execution - Document how the polyglot works in CMD vs bash - Include troubleshooting section and related GitHub issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add polyglot hook test script for macOS/Linux Run ./test-polyglot.sh from repo root to verify: - Required files exist with execute permissions - Simple wrapper (session-start.cmd) produces valid JSON - Parameterized wrapper (run-hook.cmd) works - Heredoc correctly skips CMD block on Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Use direct pipe to jq in test to avoid variable escaping issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Use single reusable run-hook.cmd for all hooks - Remove session-start.cmd in favor of run-hook.cmd - Update hooks.json to use: run-hook.cmd session-start.sh - Simplify test script to only test run-hook.cmd This makes it easy to add more hooks - just create the .sh file and add a line to hooks.json pointing to run-hook.cmd with the script name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Simplify run-hook.cmd CMD block Pass path directly to bash instead of using cygpath in a subshell. The complex quoting was causing issues on Windows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: Remove test-polyglot.sh Testing complete - polyglot hooks work on Windows and macOS. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 15:42:12 -08:00
## Writing Cross-Platform Hook Scripts
Your hook logic goes in the extensionless script file. A few portable patterns:
Add Windows support for plugin hooks (#134) * feat: Add Windows support for session-start hook - Create polyglot session-start.cmd that works in both CMD and bash - Update hooks.json to use the .cmd polyglot launcher - Replace sed/awk with pure bash for JSON escaping (Windows compatibility) The polyglot script uses a heredoc trick: - CMD sees the @echo off block and runs bash.exe with cygpath conversion - Bash sees a heredoc and skips to the Unix section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Add execute permission to session-start.cmd for Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: Add comprehensive polyglot hooks documentation - Add docs/windows/polyglot-hooks.md explaining the cross-platform technique - Add reusable run-hook.cmd wrapper for parameterized hook execution - Document how the polyglot works in CMD vs bash - Include troubleshooting section and related GitHub issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add polyglot hook test script for macOS/Linux Run ./test-polyglot.sh from repo root to verify: - Required files exist with execute permissions - Simple wrapper (session-start.cmd) produces valid JSON - Parameterized wrapper (run-hook.cmd) works - Heredoc correctly skips CMD block on Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Use direct pipe to jq in test to avoid variable escaping issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Use single reusable run-hook.cmd for all hooks - Remove session-start.cmd in favor of run-hook.cmd - Update hooks.json to use: run-hook.cmd session-start.sh - Simplify test script to only test run-hook.cmd This makes it easy to add more hooks - just create the .sh file and add a line to hooks.json pointing to run-hook.cmd with the script name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Simplify run-hook.cmd CMD block Pass path directly to bash instead of using cygpath in a subshell. The complex quoting was causing issues on Windows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: Remove test-polyglot.sh Testing complete - polyglot hooks work on Windows and macOS. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 15:42:12 -08:00
### Do
Add Windows support for plugin hooks (#134) * feat: Add Windows support for session-start hook - Create polyglot session-start.cmd that works in both CMD and bash - Update hooks.json to use the .cmd polyglot launcher - Replace sed/awk with pure bash for JSON escaping (Windows compatibility) The polyglot script uses a heredoc trick: - CMD sees the @echo off block and runs bash.exe with cygpath conversion - Bash sees a heredoc and skips to the Unix section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Add execute permission to session-start.cmd for Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: Add comprehensive polyglot hooks documentation - Add docs/windows/polyglot-hooks.md explaining the cross-platform technique - Add reusable run-hook.cmd wrapper for parameterized hook execution - Document how the polyglot works in CMD vs bash - Include troubleshooting section and related GitHub issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add polyglot hook test script for macOS/Linux Run ./test-polyglot.sh from repo root to verify: - Required files exist with execute permissions - Simple wrapper (session-start.cmd) produces valid JSON - Parameterized wrapper (run-hook.cmd) works - Heredoc correctly skips CMD block on Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Use direct pipe to jq in test to avoid variable escaping issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Use single reusable run-hook.cmd for all hooks - Remove session-start.cmd in favor of run-hook.cmd - Update hooks.json to use: run-hook.cmd session-start.sh - Simplify test script to only test run-hook.cmd This makes it easy to add more hooks - just create the .sh file and add a line to hooks.json pointing to run-hook.cmd with the script name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Simplify run-hook.cmd CMD block Pass path directly to bash instead of using cygpath in a subshell. The complex quoting was causing issues on Windows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: Remove test-polyglot.sh Testing complete - polyglot hooks work on Windows and macOS. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 15:42:12 -08:00
- Use pure bash builtins when possible
- Use `$(command)` instead of backticks
- Quote all variable expansions: `"$VAR"`
### Avoid
- Relying on PATH-dependent tools without fallbacks (the hook runs without `-l`, so login-shell PATH is not set)
- Giving scripts a `.sh` extension — this triggers Claude Code's Windows auto-prepend
Add Windows support for plugin hooks (#134) * feat: Add Windows support for session-start hook - Create polyglot session-start.cmd that works in both CMD and bash - Update hooks.json to use the .cmd polyglot launcher - Replace sed/awk with pure bash for JSON escaping (Windows compatibility) The polyglot script uses a heredoc trick: - CMD sees the @echo off block and runs bash.exe with cygpath conversion - Bash sees a heredoc and skips to the Unix section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Add execute permission to session-start.cmd for Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: Add comprehensive polyglot hooks documentation - Add docs/windows/polyglot-hooks.md explaining the cross-platform technique - Add reusable run-hook.cmd wrapper for parameterized hook execution - Document how the polyglot works in CMD vs bash - Include troubleshooting section and related GitHub issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add polyglot hook test script for macOS/Linux Run ./test-polyglot.sh from repo root to verify: - Required files exist with execute permissions - Simple wrapper (session-start.cmd) produces valid JSON - Parameterized wrapper (run-hook.cmd) works - Heredoc correctly skips CMD block on Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Use direct pipe to jq in test to avoid variable escaping issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Use single reusable run-hook.cmd for all hooks - Remove session-start.cmd in favor of run-hook.cmd - Update hooks.json to use: run-hook.cmd session-start.sh - Simplify test script to only test run-hook.cmd This makes it easy to add more hooks - just create the .sh file and add a line to hooks.json pointing to run-hook.cmd with the script name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Simplify run-hook.cmd CMD block Pass path directly to bash instead of using cygpath in a subshell. The complex quoting was causing issues on Windows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: Remove test-polyglot.sh Testing complete - polyglot hooks work on Windows and macOS. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 15:42:12 -08:00
### Example: JSON escaping without external tools
Add Windows support for plugin hooks (#134) * feat: Add Windows support for session-start hook - Create polyglot session-start.cmd that works in both CMD and bash - Update hooks.json to use the .cmd polyglot launcher - Replace sed/awk with pure bash for JSON escaping (Windows compatibility) The polyglot script uses a heredoc trick: - CMD sees the @echo off block and runs bash.exe with cygpath conversion - Bash sees a heredoc and skips to the Unix section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Add execute permission to session-start.cmd for Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: Add comprehensive polyglot hooks documentation - Add docs/windows/polyglot-hooks.md explaining the cross-platform technique - Add reusable run-hook.cmd wrapper for parameterized hook execution - Document how the polyglot works in CMD vs bash - Include troubleshooting section and related GitHub issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add polyglot hook test script for macOS/Linux Run ./test-polyglot.sh from repo root to verify: - Required files exist with execute permissions - Simple wrapper (session-start.cmd) produces valid JSON - Parameterized wrapper (run-hook.cmd) works - Heredoc correctly skips CMD block on Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Use direct pipe to jq in test to avoid variable escaping issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Use single reusable run-hook.cmd for all hooks - Remove session-start.cmd in favor of run-hook.cmd - Update hooks.json to use: run-hook.cmd session-start.sh - Simplify test script to only test run-hook.cmd This makes it easy to add more hooks - just create the .sh file and add a line to hooks.json pointing to run-hook.cmd with the script name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Simplify run-hook.cmd CMD block Pass path directly to bash instead of using cygpath in a subshell. The complex quoting was causing issues on Windows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: Remove test-polyglot.sh Testing complete - polyglot hooks work on Windows and macOS. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 15:42:12 -08:00
```bash
escape_for_json() {
local input="$1"
local output=""
local i char
for (( i=0; i<${#input}; i++ )); do
char="${input:$i:1}"
case "$char" in
$'\\') output+='\\' ;;
'"') output+='\"' ;;
$'\n') output+='\n' ;;
$'\r') output+='\r' ;;
$'\t') output+='\t' ;;
*) output+="$char" ;;
esac
done
printf '%s' "$output"
}
```
## Troubleshooting
### "bash is not recognized"
CMD couldn't find bash in any of the three locations the dispatcher tries. The dispatcher exits silently (0) rather than erroring, so the hook is skipped. Install Git for Windows at the standard path or ensure `bash` is on `PATH`.
Add Windows support for plugin hooks (#134) * feat: Add Windows support for session-start hook - Create polyglot session-start.cmd that works in both CMD and bash - Update hooks.json to use the .cmd polyglot launcher - Replace sed/awk with pure bash for JSON escaping (Windows compatibility) The polyglot script uses a heredoc trick: - CMD sees the @echo off block and runs bash.exe with cygpath conversion - Bash sees a heredoc and skips to the Unix section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Add execute permission to session-start.cmd for Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: Add comprehensive polyglot hooks documentation - Add docs/windows/polyglot-hooks.md explaining the cross-platform technique - Add reusable run-hook.cmd wrapper for parameterized hook execution - Document how the polyglot works in CMD vs bash - Include troubleshooting section and related GitHub issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add polyglot hook test script for macOS/Linux Run ./test-polyglot.sh from repo root to verify: - Required files exist with execute permissions - Simple wrapper (session-start.cmd) produces valid JSON - Parameterized wrapper (run-hook.cmd) works - Heredoc correctly skips CMD block on Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Use direct pipe to jq in test to avoid variable escaping issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Use single reusable run-hook.cmd for all hooks - Remove session-start.cmd in favor of run-hook.cmd - Update hooks.json to use: run-hook.cmd session-start.sh - Simplify test script to only test run-hook.cmd This makes it easy to add more hooks - just create the .sh file and add a line to hooks.json pointing to run-hook.cmd with the script name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Simplify run-hook.cmd CMD block Pass path directly to bash instead of using cygpath in a subshell. The complex quoting was causing issues on Windows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: Remove test-polyglot.sh Testing complete - polyglot hooks work on Windows and macOS. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 15:42:12 -08:00
### Hook runs on Unix but does nothing on Windows
Add Windows support for plugin hooks (#134) * feat: Add Windows support for session-start hook - Create polyglot session-start.cmd that works in both CMD and bash - Update hooks.json to use the .cmd polyglot launcher - Replace sed/awk with pure bash for JSON escaping (Windows compatibility) The polyglot script uses a heredoc trick: - CMD sees the @echo off block and runs bash.exe with cygpath conversion - Bash sees a heredoc and skips to the Unix section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Add execute permission to session-start.cmd for Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: Add comprehensive polyglot hooks documentation - Add docs/windows/polyglot-hooks.md explaining the cross-platform technique - Add reusable run-hook.cmd wrapper for parameterized hook execution - Document how the polyglot works in CMD vs bash - Include troubleshooting section and related GitHub issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add polyglot hook test script for macOS/Linux Run ./test-polyglot.sh from repo root to verify: - Required files exist with execute permissions - Simple wrapper (session-start.cmd) produces valid JSON - Parameterized wrapper (run-hook.cmd) works - Heredoc correctly skips CMD block on Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Use direct pipe to jq in test to avoid variable escaping issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Use single reusable run-hook.cmd for all hooks - Remove session-start.cmd in favor of run-hook.cmd - Update hooks.json to use: run-hook.cmd session-start.sh - Simplify test script to only test run-hook.cmd This makes it easy to add more hooks - just create the .sh file and add a line to hooks.json pointing to run-hook.cmd with the script name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Simplify run-hook.cmd CMD block Pass path directly to bash instead of using cygpath in a subshell. The complex quoting was causing issues on Windows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: Remove test-polyglot.sh Testing complete - polyglot hooks work on Windows and macOS. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 15:42:12 -08:00
Check that the script filename is **extensionless** in `hooks.json`. A command like `run-hook.cmd session-start.sh` can trigger Claude Code's `.sh` auto-detection and bypass the intended CMD dispatcher path, or just try to run a non-existent `session-start.sh` script.
Add Windows support for plugin hooks (#134) * feat: Add Windows support for session-start hook - Create polyglot session-start.cmd that works in both CMD and bash - Update hooks.json to use the .cmd polyglot launcher - Replace sed/awk with pure bash for JSON escaping (Windows compatibility) The polyglot script uses a heredoc trick: - CMD sees the @echo off block and runs bash.exe with cygpath conversion - Bash sees a heredoc and skips to the Unix section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Add execute permission to session-start.cmd for Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: Add comprehensive polyglot hooks documentation - Add docs/windows/polyglot-hooks.md explaining the cross-platform technique - Add reusable run-hook.cmd wrapper for parameterized hook execution - Document how the polyglot works in CMD vs bash - Include troubleshooting section and related GitHub issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add polyglot hook test script for macOS/Linux Run ./test-polyglot.sh from repo root to verify: - Required files exist with execute permissions - Simple wrapper (session-start.cmd) produces valid JSON - Parameterized wrapper (run-hook.cmd) works - Heredoc correctly skips CMD block on Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Use direct pipe to jq in test to avoid variable escaping issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Use single reusable run-hook.cmd for all hooks - Remove session-start.cmd in favor of run-hook.cmd - Update hooks.json to use: run-hook.cmd session-start.sh - Simplify test script to only test run-hook.cmd This makes it easy to add more hooks - just create the .sh file and add a line to hooks.json pointing to run-hook.cmd with the script name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Simplify run-hook.cmd CMD block Pass path directly to bash instead of using cygpath in a subshell. The complex quoting was causing issues on Windows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: Remove test-polyglot.sh Testing complete - polyglot hooks work on Windows and macOS. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 15:42:12 -08:00
### Hook doesn't fire at all
Verify the `matcher` in `hooks.json` matches the event type your harness emits. Claude Code uses `startup|clear|compact`; Codex uses `startup|resume|clear`. Check `hooks-codex.json` for the Codex variant.
Add Windows support for plugin hooks (#134) * feat: Add Windows support for session-start hook - Create polyglot session-start.cmd that works in both CMD and bash - Update hooks.json to use the .cmd polyglot launcher - Replace sed/awk with pure bash for JSON escaping (Windows compatibility) The polyglot script uses a heredoc trick: - CMD sees the @echo off block and runs bash.exe with cygpath conversion - Bash sees a heredoc and skips to the Unix section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Add execute permission to session-start.cmd for Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: Add comprehensive polyglot hooks documentation - Add docs/windows/polyglot-hooks.md explaining the cross-platform technique - Add reusable run-hook.cmd wrapper for parameterized hook execution - Document how the polyglot works in CMD vs bash - Include troubleshooting section and related GitHub issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add polyglot hook test script for macOS/Linux Run ./test-polyglot.sh from repo root to verify: - Required files exist with execute permissions - Simple wrapper (session-start.cmd) produces valid JSON - Parameterized wrapper (run-hook.cmd) works - Heredoc correctly skips CMD block on Unix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Use direct pipe to jq in test to avoid variable escaping issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Use single reusable run-hook.cmd for all hooks - Remove session-start.cmd in favor of run-hook.cmd - Update hooks.json to use: run-hook.cmd session-start.sh - Simplify test script to only test run-hook.cmd This makes it easy to add more hooks - just create the .sh file and add a line to hooks.json pointing to run-hook.cmd with the script name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Simplify run-hook.cmd CMD block Pass path directly to bash instead of using cygpath in a subshell. The complex quoting was causing issues on Windows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: Remove test-polyglot.sh Testing complete - polyglot hooks work on Windows and macOS. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 15:42:12 -08:00
## Related Issues
- [anthropics/claude-code#9758](https://github.com/anthropics/claude-code/issues/9758) — `.sh` scripts open in editor on Windows
- [anthropics/claude-code#3417](https://github.com/anthropics/claude-code/issues/3417) — Hooks don't work on Windows