Skip to content

fix(hooks): Node.js SessionStart hook for Windows compatibility#475

Open
wombatfish wants to merge 2 commits intoobra:mainfrom
wombatfish:fix/windows-hooks-node-js
Open

fix(hooks): Node.js SessionStart hook for Windows compatibility#475
wombatfish wants to merge 2 commits intoobra:mainfrom
wombatfish:fix/windows-hooks-node-js

Conversation

@wombatfish
Copy link

@wombatfish wombatfish commented Feb 14, 2026

Summary

  • Replace session-start.sh with session-start.js — eliminates bash dependency on Windows
  • Update hooks.json to invoke node instead of relying on Claude Code's .sh auto-detection

Why this works

Claude Code strips backslashes from ${CLAUDE_PLUGIN_ROOT} during template substitution, producing forward-slash paths like C:/Users/dave/.claude/....

This is the root cause behind #420, #414, #417, #354, #440, and #466 — but it's only a problem for bash, which interprets forward-slash paths and unescaped characters differently. Node.js handles forward-slash paths natively on Windows. No upstream Claude Code fix required.

The previous attempt (#421) was closed because it still relied on bash with path normalization. This PR eliminates bash entirely.

Fallback

The script uses path.resolve(__dirname, '..') as fallback when CLAUDE_PLUGIN_ROOT is missing or mangled, so it works even if template substitution is completely broken.

What changed

File Change
hooks/session-start.js New Node.js equivalent of session-start.sh — reads SKILL.md, checks legacy dir, outputs JSON
hooks/hooks.json Changed command from .sh path to node ... .js

session-start.sh is preserved for Linux/macOS users who may reference it directly.

Testing

Tested on Windows 11 (MSYS2/Git Bash environment) with Claude Code 2.1.x. The hook produces valid JSON and the SessionStart context injection works correctly — confirmed by 4 consecutive "startup hook success" messages across session restarts.

Fixes

Closes #420, #414, #417, #354, #440, #466

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Session startup now shows a migration notification for users with skills in legacy locations, including clear steps to move skills and dismiss the notice.
  • Chores

    • Session initialization implementation updated to a Node-based runner while preserving existing startup behavior and output.

…ompatibility

Claude Code strips backslashes from ${CLAUDE_PLUGIN_ROOT} during template
substitution, producing forward-slash paths like C:/Users/dave/.claude/...
This is valid for Node.js but breaks bash (interprets as escape sequences).

By switching from session-start.sh to session-start.js, the hook works on
all platforms without requiring an upstream Claude Code fix. The script uses
path.resolve(__dirname, '..') as fallback if CLAUDE_PLUGIN_ROOT is missing
or mangled.

Fixes obra#420, obra#414, obra#417, obra#354, obra#440, obra#466

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

coderabbitai bot commented Feb 14, 2026

📝 Walkthrough

Walkthrough

Replaces the SessionStart hook shell invocation with a Node.js script that reads SKILL.md, checks for a legacy skills directory, builds an HTML-wrapped additionalContext (including warnings), and writes a JSON hook payload to stdout.

Changes

Cohort / File(s) Summary
Hook Configuration
hooks/hooks.json
Replaced command from ${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh to node '${CLAUDE_PLUGIN_ROOT}/hooks/session-start.js' (same command type and async flag).
SessionStart Hook Implementation
hooks/session-start.js
New Node.js hook: resolves plugin paths, checks legacy skills dir, reads using-superpowers/SKILL.md (with error fallback), composes warning and HTML-wrapped additionalContext, and emits JSON to stdout.

Sequence Diagram(s)

sequenceDiagram
    participant Claude as Claude Host
    participant Node as node (session-start.js)
    participant FS as Filesystem
    Claude->>Node: spawn node session-start.js (hook)
    Node->>FS: stat(...) check legacy skills dir
    FS-->>Node: exists / not found
    Node->>FS: read SKILL.md
    FS-->>Node: file contents / error
    Node->>Claude: stdout JSON {hookEventName, additionalContext, ...}
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Possibly related PRs

Poem

🐰 In plugin meadows, bash would slip,
Paths undone on Windows trip.
I nudge Node in, a nimble hop,
Read skills, warn, and print on top —
JSON hops out, the session's up! 🥕

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: replacing the SessionStart hook with a Node.js version to fix Windows compatibility issues.
Linked Issues check ✅ Passed The PR successfully addresses the primary requirement from issue #420: making the SessionStart hook work on Windows by replacing bash script with Node.js to handle forward-slash paths.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the Windows SessionStart hook issue: updating hooks.json to call Node.js and implementing the new session-start.js script.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

No actionable comments were generated in the recent review. 🎉


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: 2

🤖 Fix all issues with AI agents
In `@hooks/hooks.json`:
- Line 9: The command string "node ${CLAUDE_PLUGIN_ROOT}/hooks/session-start.js"
can break when CLAUDE_PLUGIN_ROOT expands to a path with spaces; update the
command to quote the expanded path so the shell treats it as a single argument,
e.g. change the JSON value to use quotes around the variable like "node
'${CLAUDE_PLUGIN_ROOT}/hooks/session-start.js'"; ensure you update the same
command entry in hooks.json so all similar hook commands use quoted
${CLAUDE_PLUGIN_ROOT}.

In `@hooks/session-start.js`:
- Line 12: The current construction of legacyDir using
path.join(process.env.HOME || process.env.USERPROFILE || '', ...) can produce a
relative path when both env vars are missing; change to first read a home
variable (e.g. const home = process.env.HOME || process.env.USERPROFILE) and
only compute legacyDir (const legacyDir = path.join(home, '.config',
'superpowers', 'skills')) when home is truthy, otherwise leave legacyDir
undefined or skip the legacy stat/lookup; update any subsequent checks (the
statSync/exists check that references legacyDir) to guard for legacyDir being
defined before calling fs.statSync so you never accidentally match a relative
path.

…ames

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

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

1 participant