From 6dbff0687d744833d4fe56c268eb344e327f1e8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96zdemir=20=C5=9Earman?= Date: Mon, 27 Jul 2026 17:59:33 +0300 Subject: [PATCH 1/2] fix(plugins): quote ${CLAUDE_PLUGIN_ROOT} in hooks.json commands Hook commands are executed through a shell, so an unquoted ${CLAUDE_PLUGIN_ROOT} word-splits when the plugin is installed under a path containing a space (e.g. a macOS user directory like "/Users/Jane Doe/..."). For hookify this is fatal rather than cosmetic: python3 receives "/Users/Jane" as its script argument and exits non-zero on every PreToolUse event, so the plugin fails on every single tool call. $ export CLAUDE_PLUGIN_ROOT="/tmp/plugin dir with spaces/hookify" $ bash -c "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/pretooluse.py" python3: can't open file '/tmp/plugin': [Errno 2] No such file or directory # after $ bash -c "python3 \"${CLAUDE_PLUGIN_ROOT}/hooks/pretooluse.py\"" <<< '{"tool_name":"Bash"}' {} learning-output-style, explanatory-output-style and ralph-wiggum use the same unquoted pattern and are fixed alongside. security-guidance already quotes the variable; this brings the rest in line with it. Fixes #78490 --- plugins/explanatory-output-style/hooks/hooks.json | 2 +- plugins/hookify/hooks/hooks.json | 8 ++++---- plugins/learning-output-style/hooks/hooks.json | 2 +- plugins/ralph-wiggum/hooks/hooks.json | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/explanatory-output-style/hooks/hooks.json b/plugins/explanatory-output-style/hooks/hooks.json index d1fb8a5734..23f2457f3f 100644 --- a/plugins/explanatory-output-style/hooks/hooks.json +++ b/plugins/explanatory-output-style/hooks/hooks.json @@ -6,7 +6,7 @@ "hooks": [ { "type": "command", - "command": "${CLAUDE_PLUGIN_ROOT}/hooks-handlers/session-start.sh" + "command": "\"${CLAUDE_PLUGIN_ROOT}/hooks-handlers/session-start.sh\"" } ] } diff --git a/plugins/hookify/hooks/hooks.json b/plugins/hookify/hooks/hooks.json index d65daca71b..4aa457a28f 100644 --- a/plugins/hookify/hooks/hooks.json +++ b/plugins/hookify/hooks/hooks.json @@ -6,7 +6,7 @@ "hooks": [ { "type": "command", - "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/pretooluse.py", + "command": "python3 \"${CLAUDE_PLUGIN_ROOT}/hooks/pretooluse.py\"", "timeout": 10 } ] @@ -17,7 +17,7 @@ "hooks": [ { "type": "command", - "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/posttooluse.py", + "command": "python3 \"${CLAUDE_PLUGIN_ROOT}/hooks/posttooluse.py\"", "timeout": 10 } ] @@ -28,7 +28,7 @@ "hooks": [ { "type": "command", - "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/stop.py", + "command": "python3 \"${CLAUDE_PLUGIN_ROOT}/hooks/stop.py\"", "timeout": 10 } ] @@ -39,7 +39,7 @@ "hooks": [ { "type": "command", - "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/userpromptsubmit.py", + "command": "python3 \"${CLAUDE_PLUGIN_ROOT}/hooks/userpromptsubmit.py\"", "timeout": 10 } ] diff --git a/plugins/learning-output-style/hooks/hooks.json b/plugins/learning-output-style/hooks/hooks.json index b3ab7ce9b7..841def8689 100644 --- a/plugins/learning-output-style/hooks/hooks.json +++ b/plugins/learning-output-style/hooks/hooks.json @@ -6,7 +6,7 @@ "hooks": [ { "type": "command", - "command": "${CLAUDE_PLUGIN_ROOT}/hooks-handlers/session-start.sh" + "command": "\"${CLAUDE_PLUGIN_ROOT}/hooks-handlers/session-start.sh\"" } ] } diff --git a/plugins/ralph-wiggum/hooks/hooks.json b/plugins/ralph-wiggum/hooks/hooks.json index 2e5f697934..c193445189 100644 --- a/plugins/ralph-wiggum/hooks/hooks.json +++ b/plugins/ralph-wiggum/hooks/hooks.json @@ -6,7 +6,7 @@ "hooks": [ { "type": "command", - "command": "${CLAUDE_PLUGIN_ROOT}/hooks/stop-hook.sh" + "command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/stop-hook.sh\"" } ] } From 5fa2d440de4cfce901a787c4a3db64eb4c8ac3e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96zdemir=20=C5=9Earman?= Date: Mon, 27 Jul 2026 17:59:33 +0300 Subject: [PATCH 2/2] fix(hookify): name example rules so they actually load when copied load_rules() globs for `.claude/hookify.*.local.md`, but the shipped examples are named `dangerous-rm.local.md`, `console-log-warning.local.md`, etc. Copied into a project as-is - which is what "see examples/" invites - none of them match the glob, so they silently never run. Nothing errors and nothing is logged; the rule just does not exist as far as the plugin is concerned. Renames the four examples to the `hookify.*.local.md` form and states the prefix requirement in the writing-rules skill. Fixes #79143 --- ....local.md => hookify.console-log-warning.local.md} | 0 ...rous-rm.local.md => hookify.dangerous-rm.local.md} | 0 ...p.local.md => hookify.require-tests-stop.local.md} | 0 ...al.md => hookify.sensitive-files-warning.local.md} | 0 plugins/hookify/skills/writing-rules/SKILL.md | 11 +++++++---- 5 files changed, 7 insertions(+), 4 deletions(-) rename plugins/hookify/examples/{console-log-warning.local.md => hookify.console-log-warning.local.md} (100%) rename plugins/hookify/examples/{dangerous-rm.local.md => hookify.dangerous-rm.local.md} (100%) rename plugins/hookify/examples/{require-tests-stop.local.md => hookify.require-tests-stop.local.md} (100%) rename plugins/hookify/examples/{sensitive-files-warning.local.md => hookify.sensitive-files-warning.local.md} (100%) diff --git a/plugins/hookify/examples/console-log-warning.local.md b/plugins/hookify/examples/hookify.console-log-warning.local.md similarity index 100% rename from plugins/hookify/examples/console-log-warning.local.md rename to plugins/hookify/examples/hookify.console-log-warning.local.md diff --git a/plugins/hookify/examples/dangerous-rm.local.md b/plugins/hookify/examples/hookify.dangerous-rm.local.md similarity index 100% rename from plugins/hookify/examples/dangerous-rm.local.md rename to plugins/hookify/examples/hookify.dangerous-rm.local.md diff --git a/plugins/hookify/examples/require-tests-stop.local.md b/plugins/hookify/examples/hookify.require-tests-stop.local.md similarity index 100% rename from plugins/hookify/examples/require-tests-stop.local.md rename to plugins/hookify/examples/hookify.require-tests-stop.local.md diff --git a/plugins/hookify/examples/sensitive-files-warning.local.md b/plugins/hookify/examples/hookify.sensitive-files-warning.local.md similarity index 100% rename from plugins/hookify/examples/sensitive-files-warning.local.md rename to plugins/hookify/examples/hookify.sensitive-files-warning.local.md diff --git a/plugins/hookify/skills/writing-rules/SKILL.md b/plugins/hookify/skills/writing-rules/SKILL.md index 008168a4c9..6e3dfadb0f 100644 --- a/plugins/hookify/skills/writing-rules/SKILL.md +++ b/plugins/hookify/skills/writing-rules/SKILL.md @@ -321,10 +321,13 @@ Better: `rm\s+-rf` ## Examples -See `${CLAUDE_PLUGIN_ROOT}/examples/` for complete examples: -- `dangerous-rm.local.md` - Block dangerous rm commands -- `console-log-warning.local.md` - Warn about console.log -- `sensitive-files-warning.local.md` - Warn about editing .env files +See `${CLAUDE_PLUGIN_ROOT}/examples/` for complete examples. Copy them into your +project's `.claude/` directory as-is - the `hookify.` prefix is required for a +rule file to be picked up: +- `hookify.dangerous-rm.local.md` - Block dangerous rm commands +- `hookify.console-log-warning.local.md` - Warn about console.log +- `hookify.sensitive-files-warning.local.md` - Warn about editing .env files +- `hookify.require-tests-stop.local.md` - Remind about tests before stopping ## Quick Reference