From 147bcfd621438ae6c12153990832454e3509e39f Mon Sep 17 00:00:00 2001 From: Ben Younes Date: Sun, 14 Jun 2026 17:39:59 +0200 Subject: [PATCH] fix: use PowerShell $env: syntax for Windows hook paths (#26) On Windows the lifecycle hooks run via PowerShell, which does not expand cmd.exe-style %CLAUDE_PLUGIN_ROOT%. The path was passed literally, so the hook launcher could not find the script and both SessionStart and UserPromptSubmit failed with exit code 1 (issue #19). Switch the two commandWindows entries to $env:CLAUDE_PLUGIN_ROOT, keeping the working node + .js invocation. Add a regression test that rejects cmd.exe %VAR% syntax in commandWindows and asserts every hook command points at a script that actually ships in hooks/. Co-authored-by: Claude Opus 4.8 (1M context) --- hooks/hooks.json | 4 ++-- tests/hooks-windows.test.js | 48 +++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 tests/hooks-windows.test.js diff --git a/hooks/hooks.json b/hooks/hooks.json index dc0eece..962b852 100644 --- a/hooks/hooks.json +++ b/hooks/hooks.json @@ -7,7 +7,7 @@ { "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/ponytail-activate.js\"", - "commandWindows": "node \"%CLAUDE_PLUGIN_ROOT%\\hooks\\ponytail-activate.js\"", + "commandWindows": "node \"$env:CLAUDE_PLUGIN_ROOT\\hooks\\ponytail-activate.js\"", "timeout": 5, "statusMessage": "Loading ponytail mode..." } @@ -20,7 +20,7 @@ { "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/ponytail-mode-tracker.js\"", - "commandWindows": "node \"%CLAUDE_PLUGIN_ROOT%\\hooks\\ponytail-mode-tracker.js\"", + "commandWindows": "node \"$env:CLAUDE_PLUGIN_ROOT\\hooks\\ponytail-mode-tracker.js\"", "timeout": 5, "statusMessage": "Tracking ponytail mode..." } diff --git a/tests/hooks-windows.test.js b/tests/hooks-windows.test.js new file mode 100644 index 0000000..f7b5353 --- /dev/null +++ b/tests/hooks-windows.test.js @@ -0,0 +1,48 @@ +#!/usr/bin/env node +// Regression test for issue #19: on Windows the lifecycle hooks run via +// PowerShell, which does NOT expand cmd.exe-style %VAR% — it needs $env:VAR. +// The hook also has to point at a script that actually ships in hooks/. +// This guards both failure modes: the original %CLAUDE_PLUGIN_ROOT% bug, and +// the "switch to a .ps1 that doesn't exist" mistake. + +const test = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('fs'); +const path = require('path'); + +const root = path.join(__dirname, '..'); +const HOOKS_JSON = 'hooks/hooks.json'; +// cmd.exe variable syntax (%FOO%); PowerShell leaves it literal, breaking the path. +const CMD_VAR_SYNTAX = /%[A-Za-z_][A-Za-z0-9_]*%/; +// Pull the hooks/