Skip to content

fix: Windows compatibility for hook scripts (execFileSync + tmux)#215

Merged
affaan-m merged 2 commits intoaffaan-m:mainfrom
dungan24:fix/windows-hook-compatibility
Feb 13, 2026
Merged

fix: Windows compatibility for hook scripts (execFileSync + tmux)#215
affaan-m merged 2 commits intoaffaan-m:mainfrom
dungan24:fix/windows-hook-compatibility

Conversation

@dungan24
Copy link
Copy Markdown
Contributor

@dungan24 dungan24 commented Feb 13, 2026

Summary

  • post-edit-format.js and post-edit-typecheck.js: Add shell: process.platform === 'win32' to execFileSync options so npx.cmd is resolved via cmd.exe on Windows
  • hooks.json: Skip tmux-dependent hooks on Windows where tmux is unavailable
  • session-end.js: Fix JSONL transcript parsing to match Claude Code's actual format

Problem 1: execFileSync ENOENT on Windows

On Windows (Git Bash / nvm-windows), execFileSync('npx', [...]) without shell: true fails with ENOENT because Node.js's CreateProcessW cannot directly execute .cmd files — only actual .exe binaries. Since npm installs npx.cmd (not npx.exe), the Prettier auto-format and TypeScript type-check hooks silently fail on every Windows installation.

Additionally, the tmux dev-server blocker hook unconditionally exits with code 2 on Windows (blocking npm run dev commands), and the tmux reminder always fires since process.env.TMUX is never set.

Reproduction

// On Windows with nvm-windows:
const { execFileSync } = require('child_process');

execFileSync('npx', ['--version']);
// → Error: spawnSync npx ENOENT

execFileSync('npx', ['--version'], { shell: true });
// → "10.9.4" ✅

Problem 2: Session summary files always blank

extractSessionSummary() in session-end.js expected user messages at entry.content, but Claude Code's actual JSONL transcript format nests them at entry.message.content. This caused all session summary files to remain blank templates — 0 user messages extracted despite 136+ actual user entries in the transcript.

Actual Claude Code v2.1.41 JSONL structure:

{"type":"user","message":{"role":"user","content":"user text here"},...}
{"type":"assistant","message":{"content":[{"type":"tool_use","name":"Edit","input":{"file_path":"..."}},...]},...}
{"type":"progress","data":{...},...}

The parser expected:

  • entry.content (string) — actual location: entry.message.content
  • entry.type === 'tool_use' at top level — actual location: nested in entry.message.content[] blocks within assistant entries

Fix

Minimal, targeted changes:

  1. shell: process.platform === 'win32' — only enables shell on Windows, zero overhead on macOS/Linux
  2. process.platform !== 'win32' guard on tmux hooks — skips tmux logic on Windows
  3. Check entry.message?.content in addition to entry.content for user messages
  4. Extract tool_use blocks from assistant message.content arrays

Test plan

  • Verified execFileSync('npx', [...], { shell: process.platform === 'win32' }) resolves npx.cmd on Windows
  • Verified no behavior change on non-Windows (shell option is false)
  • Verified tmux blocker no longer blocks on Windows
  • Verified tmux reminder no longer fires on Windows
  • Verified session summary extraction works with actual Claude Code v2.1.41 JSONL transcripts (2254 lines, 136 user messages, 223 assistant messages)
  • Existing tests pass (hook logic unchanged on macOS/Linux)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Prevents development hooks from running incorrectly on Windows and enables hooks to work properly across platforms.
    • Ensures tooling (formatting/type-checking) executes on Windows when appropriate.
  • Chores

    • Standardized quote and formatting styles across hook scripts.
  • New Features

    • Improved session transcript parsing to support additional export formats and embedded tool usage.

- post-edit-format.js: add `shell: process.platform === 'win32'` to
  execFileSync options so npx.cmd is resolved via cmd.exe on Windows
- post-edit-typecheck.js: same fix for tsc invocation via npx
- hooks.json: skip tmux-dependent hooks on Windows where tmux is
  unavailable (dev-server blocker and long-running command reminder)

On Windows, execFileSync('npx', ...) without shell:true fails with
ENOENT because Node.js cannot directly execute .cmd files. These
hooks silently fail on all Windows installations.

The tmux hooks unconditionally block dev server commands (exit 2) or
warn about tmux on Windows where tmux is not available.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 13, 2026

📝 Walkthrough

Walkthrough

Adds platform guards to hook configuration to avoid running certain hooks on Windows, enables shell execution on Windows for format/typecheck hook scripts, normalizes string quote styles, and broadens session-end transcript parsing to handle Claude Code JSONL formats and nested content.

Changes

Cohort / File(s) Summary
Hook Configuration
hooks/hooks.json
Adds process.platform !== 'win32' guards to dev-server and install/test hooks to avoid triggering those hooks on Windows.
Format & Typecheck Hooks
scripts/hooks/post-edit-format.js, scripts/hooks/post-edit-typecheck.js
Adds shell: process.platform === 'win32' to execFileSync options for Windows shell support; normalizes quote style to double quotes and minor trailing-comma/formatting tweaks.
Session End Parsing
scripts/hooks/session-end.js
Extends transcript parsing to support Claude Code JSONL structures: extracts messages from nested entry.message.content or entry.content, handles string/array content, and collects tool usages from assistant content blocks as well as direct entries.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I nibble logs and patch each line,
I tuck in quotes and make them shine,
I teach the hooks to mind the ground,
And listen when new messages sound,
Hopping cross-platform, tidy and fine! 🥕

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main changes: adding Windows compatibility for hook scripts through execFileSync shell option and tmux platform guards.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

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

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

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
scripts/hooks/session-end.js (1)

72-85: Minor: tool_input fallback omitted for assistant content blocks.

Line 66 checks both entry.tool_input?.file_path || entry.input?.file_path, but line 79 only checks block.input?.file_path. This is likely intentional since the Anthropic API tool_use content blocks use input, but if the direct-entry path needs the tool_input fallback for some transcript variants, the same may apply here.

Not a bug — just flagging the asymmetry for awareness.

Defensive alignment (optional)
-            const filePath = block.input?.file_path || '';
+            const filePath = block.tool_input?.file_path || block.input?.file_path || '';

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.

The session-end hook expected user messages at entry.content, but
Claude Code's actual JSONL format nests them at entry.message.content.
This caused all session files to be blank templates (0 user messages
despite 136+ actual entries).

- Check entry.message?.content in addition to entry.content
- Extract tool_use blocks from assistant message.content arrays

Verified with Claude Code v2.1.41 JSONL transcripts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@affaan-m
Copy link
Copy Markdown
Owner

[openclaw-bot:pr-review]

Automated Review - CI checks are passing! ✅

Hi @dungan24, thanks for this contribution! All CI checks have passed successfully.

A maintainer will review this PR shortly. In the meantime, please ensure:

  • The PR description explains the changes
  • Tests cover the new/modified functionality
  • No breaking changes (or they're documented)

This is an automated review from OpenClaw.

@affaan-m affaan-m merged commit 4843a06 into affaan-m:main Feb 13, 2026
2 checks passed
affaan-m added a commit that referenced this pull request Feb 13, 2026
…ocks

Tests the new transcript parsing from PR #215:
- entry.message.content format (string and array content)
- tool_use blocks nested in assistant message content arrays
- Verifies file paths and tool names extracted from both formats
bus1029 pushed a commit to bus1029/everything-claude-code that referenced this pull request Feb 24, 2026
…faan-m#215)

* fix: Windows compatibility for hook scripts

- post-edit-format.js: add `shell: process.platform === 'win32'` to
  execFileSync options so npx.cmd is resolved via cmd.exe on Windows
- post-edit-typecheck.js: same fix for tsc invocation via npx
- hooks.json: skip tmux-dependent hooks on Windows where tmux is
  unavailable (dev-server blocker and long-running command reminder)

On Windows, execFileSync('npx', ...) without shell:true fails with
ENOENT because Node.js cannot directly execute .cmd files. These
hooks silently fail on all Windows installations.

The tmux hooks unconditionally block dev server commands (exit 2) or
warn about tmux on Windows where tmux is not available.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: parse Claude Code JSONL transcript format correctly

The session-end hook expected user messages at entry.content, but
Claude Code's actual JSONL format nests them at entry.message.content.
This caused all session files to be blank templates (0 user messages
despite 136+ actual entries).

- Check entry.message?.content in addition to entry.content
- Extract tool_use blocks from assistant message.content arrays

Verified with Claude Code v2.1.41 JSONL transcripts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: ddungan <sckim@mococo.co.kr>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
bus1029 pushed a commit to bus1029/everything-claude-code that referenced this pull request Feb 24, 2026
…ocks

Tests the new transcript parsing from PR affaan-m#215:
- entry.message.content format (string and array content)
- tool_use blocks nested in assistant message content arrays
- Verifies file paths and tool names extracted from both formats
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants