From 9490f8c7e7c2a79f12e6fd77ef56821f64fda7ab Mon Sep 17 00:00:00 2001 From: Jon Langevin Date: Mon, 22 Jun 2026 22:22:05 -0400 Subject: [PATCH] fix(hooks): avoid shell globs in root fallback --- hooks/hooks.json | 22 +++++++-------- tests/hook-contracts.sh | 59 ++++++++++++++++++++++------------------- 2 files changed, 43 insertions(+), 38 deletions(-) diff --git a/hooks/hooks.json b/hooks/hooks.json index 50d7235..8f34906 100644 --- a/hooks/hooks.json +++ b/hooks/hooks.json @@ -6,7 +6,7 @@ "hooks": [ { "type": "command", - "command": "AUTODEV_HOOK=session-start; AUTODEV_ROOT=\"${AUTODEV_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-}}}}\"; for AUTODEV_ROOT_CAND in \"$AUTODEV_ROOT\" . \"$HOME/.codex/plugins/cache/autodev-marketplace/autodev\"/* \"$HOME/.claude/plugins/marketplace/autodev\"/*; do [ -n \"$AUTODEV_ROOT_CAND\" ] || continue; if [ -x \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" ]; then exec \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" \"$AUTODEV_HOOK\"; fi; done; printf '{}\\n'", + "command": "AUTODEV_HOOK=session-start; AUTODEV_ROOT=\"${AUTODEV_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}}}\"; run_autodev_hook() { AUTODEV_ROOT_CAND=\"$1\"; [ -n \"$AUTODEV_ROOT_CAND\" ] || return 1; if [ -x \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" ]; then exec \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" \"$AUTODEV_HOOK\"; fi; return 1; }; run_autodev_hook \"$AUTODEV_ROOT\"; run_autodev_hook .; AUTODEV_BASE=\"$HOME/.codex/plugins/cache/autodev-marketplace/autodev\"; if [ -d \"$AUTODEV_BASE\" ]; then AUTODEV_ROOT_CAND=$(find \"$AUTODEV_BASE\" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort | tail -1); run_autodev_hook \"$AUTODEV_ROOT_CAND\"; fi; printf '{}\\n'", "async": false } ] @@ -18,7 +18,7 @@ "hooks": [ { "type": "command", - "command": "AUTODEV_HOOK=pre-tool-scope-guard; AUTODEV_ROOT=\"${AUTODEV_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-}}}}\"; for AUTODEV_ROOT_CAND in \"$AUTODEV_ROOT\" . \"$HOME/.codex/plugins/cache/autodev-marketplace/autodev\"/* \"$HOME/.claude/plugins/marketplace/autodev\"/*; do [ -n \"$AUTODEV_ROOT_CAND\" ] || continue; if [ -x \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" ]; then exec \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" \"$AUTODEV_HOOK\"; fi; done; printf '{}\\n'", + "command": "AUTODEV_HOOK=pre-tool-scope-guard; AUTODEV_ROOT=\"${AUTODEV_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}}}\"; run_autodev_hook() { AUTODEV_ROOT_CAND=\"$1\"; [ -n \"$AUTODEV_ROOT_CAND\" ] || return 1; if [ -x \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" ]; then exec \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" \"$AUTODEV_HOOK\"; fi; return 1; }; run_autodev_hook \"$AUTODEV_ROOT\"; run_autodev_hook .; AUTODEV_BASE=\"$HOME/.codex/plugins/cache/autodev-marketplace/autodev\"; if [ -d \"$AUTODEV_BASE\" ]; then AUTODEV_ROOT_CAND=$(find \"$AUTODEV_BASE\" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort | tail -1); run_autodev_hook \"$AUTODEV_ROOT_CAND\"; fi; printf '{}\\n'", "timeout": 15 } ] @@ -28,7 +28,7 @@ "hooks": [ { "type": "command", - "command": "AUTODEV_HOOK=pretool-pr-review-reminder; AUTODEV_ROOT=\"${AUTODEV_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-}}}}\"; for AUTODEV_ROOT_CAND in \"$AUTODEV_ROOT\" . \"$HOME/.codex/plugins/cache/autodev-marketplace/autodev\"/* \"$HOME/.claude/plugins/marketplace/autodev\"/*; do [ -n \"$AUTODEV_ROOT_CAND\" ] || continue; if [ -x \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" ]; then exec \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" \"$AUTODEV_HOOK\"; fi; done; printf '{}\\n'", + "command": "AUTODEV_HOOK=pretool-pr-review-reminder; AUTODEV_ROOT=\"${AUTODEV_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}}}\"; run_autodev_hook() { AUTODEV_ROOT_CAND=\"$1\"; [ -n \"$AUTODEV_ROOT_CAND\" ] || return 1; if [ -x \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" ]; then exec \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" \"$AUTODEV_HOOK\"; fi; return 1; }; run_autodev_hook \"$AUTODEV_ROOT\"; run_autodev_hook .; AUTODEV_BASE=\"$HOME/.codex/plugins/cache/autodev-marketplace/autodev\"; if [ -d \"$AUTODEV_BASE\" ]; then AUTODEV_ROOT_CAND=$(find \"$AUTODEV_BASE\" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort | tail -1); run_autodev_hook \"$AUTODEV_ROOT_CAND\"; fi; printf '{}\\n'", "timeout": 10 } ] @@ -38,7 +38,7 @@ "hooks": [ { "type": "command", - "command": "AUTODEV_HOOK=pretool-demo-fidelity-guard; AUTODEV_ROOT=\"${AUTODEV_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-}}}}\"; for AUTODEV_ROOT_CAND in \"$AUTODEV_ROOT\" . \"$HOME/.codex/plugins/cache/autodev-marketplace/autodev\"/* \"$HOME/.claude/plugins/marketplace/autodev\"/*; do [ -n \"$AUTODEV_ROOT_CAND\" ] || continue; if [ -x \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" ]; then exec \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" \"$AUTODEV_HOOK\"; fi; done; printf '{}\\n'", + "command": "AUTODEV_HOOK=pretool-demo-fidelity-guard; AUTODEV_ROOT=\"${AUTODEV_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}}}\"; run_autodev_hook() { AUTODEV_ROOT_CAND=\"$1\"; [ -n \"$AUTODEV_ROOT_CAND\" ] || return 1; if [ -x \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" ]; then exec \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" \"$AUTODEV_HOOK\"; fi; return 1; }; run_autodev_hook \"$AUTODEV_ROOT\"; run_autodev_hook .; AUTODEV_BASE=\"$HOME/.codex/plugins/cache/autodev-marketplace/autodev\"; if [ -d \"$AUTODEV_BASE\" ]; then AUTODEV_ROOT_CAND=$(find \"$AUTODEV_BASE\" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort | tail -1); run_autodev_hook \"$AUTODEV_ROOT_CAND\"; fi; printf '{}\\n'", "timeout": 10 } ] @@ -50,7 +50,7 @@ "hooks": [ { "type": "command", - "command": "AUTODEV_HOOK=record-activity; AUTODEV_ROOT=\"${AUTODEV_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-}}}}\"; for AUTODEV_ROOT_CAND in \"$AUTODEV_ROOT\" . \"$HOME/.codex/plugins/cache/autodev-marketplace/autodev\"/* \"$HOME/.claude/plugins/marketplace/autodev\"/*; do [ -n \"$AUTODEV_ROOT_CAND\" ] || continue; if [ -x \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" ]; then exec \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" \"$AUTODEV_HOOK\"; fi; done; printf '{}\\n'", + "command": "AUTODEV_HOOK=record-activity; AUTODEV_ROOT=\"${AUTODEV_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}}}\"; run_autodev_hook() { AUTODEV_ROOT_CAND=\"$1\"; [ -n \"$AUTODEV_ROOT_CAND\" ] || return 1; if [ -x \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" ]; then exec \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" \"$AUTODEV_HOOK\"; fi; return 1; }; run_autodev_hook \"$AUTODEV_ROOT\"; run_autodev_hook .; AUTODEV_BASE=\"$HOME/.codex/plugins/cache/autodev-marketplace/autodev\"; if [ -d \"$AUTODEV_BASE\" ]; then AUTODEV_ROOT_CAND=$(find \"$AUTODEV_BASE\" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort | tail -1); run_autodev_hook \"$AUTODEV_ROOT_CAND\"; fi; printf '{}\\n'", "timeout": 10 } ] @@ -60,7 +60,7 @@ "hooks": [ { "type": "command", - "command": "AUTODEV_HOOK=posttool-pr-created; AUTODEV_ROOT=\"${AUTODEV_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-}}}}\"; for AUTODEV_ROOT_CAND in \"$AUTODEV_ROOT\" . \"$HOME/.codex/plugins/cache/autodev-marketplace/autodev\"/* \"$HOME/.claude/plugins/marketplace/autodev\"/*; do [ -n \"$AUTODEV_ROOT_CAND\" ] || continue; if [ -x \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" ]; then exec \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" \"$AUTODEV_HOOK\"; fi; done; printf '{}\\n'", + "command": "AUTODEV_HOOK=posttool-pr-created; AUTODEV_ROOT=\"${AUTODEV_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}}}\"; run_autodev_hook() { AUTODEV_ROOT_CAND=\"$1\"; [ -n \"$AUTODEV_ROOT_CAND\" ] || return 1; if [ -x \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" ]; then exec \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" \"$AUTODEV_HOOK\"; fi; return 1; }; run_autodev_hook \"$AUTODEV_ROOT\"; run_autodev_hook .; AUTODEV_BASE=\"$HOME/.codex/plugins/cache/autodev-marketplace/autodev\"; if [ -d \"$AUTODEV_BASE\" ]; then AUTODEV_ROOT_CAND=$(find \"$AUTODEV_BASE\" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort | tail -1); run_autodev_hook \"$AUTODEV_ROOT_CAND\"; fi; printf '{}\\n'", "timeout": 10 } ] @@ -72,7 +72,7 @@ "hooks": [ { "type": "command", - "command": "AUTODEV_HOOK=prompt-strict-interpretation; AUTODEV_ROOT=\"${AUTODEV_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-}}}}\"; for AUTODEV_ROOT_CAND in \"$AUTODEV_ROOT\" . \"$HOME/.codex/plugins/cache/autodev-marketplace/autodev\"/* \"$HOME/.claude/plugins/marketplace/autodev\"/*; do [ -n \"$AUTODEV_ROOT_CAND\" ] || continue; if [ -x \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" ]; then exec \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" \"$AUTODEV_HOOK\"; fi; done; printf '{}\\n'", + "command": "AUTODEV_HOOK=prompt-strict-interpretation; AUTODEV_ROOT=\"${AUTODEV_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}}}\"; run_autodev_hook() { AUTODEV_ROOT_CAND=\"$1\"; [ -n \"$AUTODEV_ROOT_CAND\" ] || return 1; if [ -x \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" ]; then exec \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" \"$AUTODEV_HOOK\"; fi; return 1; }; run_autodev_hook \"$AUTODEV_ROOT\"; run_autodev_hook .; AUTODEV_BASE=\"$HOME/.codex/plugins/cache/autodev-marketplace/autodev\"; if [ -d \"$AUTODEV_BASE\" ]; then AUTODEV_ROOT_CAND=$(find \"$AUTODEV_BASE\" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort | tail -1); run_autodev_hook \"$AUTODEV_ROOT_CAND\"; fi; printf '{}\\n'", "timeout": 10 } ] @@ -82,7 +82,7 @@ "hooks": [ { "type": "command", - "command": "AUTODEV_HOOK=portfolio-inventory-reminder; AUTODEV_ROOT=\"${AUTODEV_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-}}}}\"; for AUTODEV_ROOT_CAND in \"$AUTODEV_ROOT\" . \"$HOME/.codex/plugins/cache/autodev-marketplace/autodev\"/* \"$HOME/.claude/plugins/marketplace/autodev\"/*; do [ -n \"$AUTODEV_ROOT_CAND\" ] || continue; if [ -x \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" ]; then exec \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" \"$AUTODEV_HOOK\"; fi; done; printf '{}\\n'", + "command": "AUTODEV_HOOK=portfolio-inventory-reminder; AUTODEV_ROOT=\"${AUTODEV_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}}}\"; run_autodev_hook() { AUTODEV_ROOT_CAND=\"$1\"; [ -n \"$AUTODEV_ROOT_CAND\" ] || return 1; if [ -x \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" ]; then exec \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" \"$AUTODEV_HOOK\"; fi; return 1; }; run_autodev_hook \"$AUTODEV_ROOT\"; run_autodev_hook .; AUTODEV_BASE=\"$HOME/.codex/plugins/cache/autodev-marketplace/autodev\"; if [ -d \"$AUTODEV_BASE\" ]; then AUTODEV_ROOT_CAND=$(find \"$AUTODEV_BASE\" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort | tail -1); run_autodev_hook \"$AUTODEV_ROOT_CAND\"; fi; printf '{}\\n'", "timeout": 10 } ] @@ -94,7 +94,7 @@ "hooks": [ { "type": "command", - "command": "AUTODEV_HOOK=completion-claim-guard; AUTODEV_ROOT=\"${AUTODEV_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-}}}}\"; for AUTODEV_ROOT_CAND in \"$AUTODEV_ROOT\" . \"$HOME/.codex/plugins/cache/autodev-marketplace/autodev\"/* \"$HOME/.claude/plugins/marketplace/autodev\"/*; do [ -n \"$AUTODEV_ROOT_CAND\" ] || continue; if [ -x \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" ]; then exec \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" \"$AUTODEV_HOOK\"; fi; done; printf '{}\\n'", + "command": "AUTODEV_HOOK=completion-claim-guard; AUTODEV_ROOT=\"${AUTODEV_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}}}\"; run_autodev_hook() { AUTODEV_ROOT_CAND=\"$1\"; [ -n \"$AUTODEV_ROOT_CAND\" ] || return 1; if [ -x \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" ]; then exec \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" \"$AUTODEV_HOOK\"; fi; return 1; }; run_autodev_hook \"$AUTODEV_ROOT\"; run_autodev_hook .; AUTODEV_BASE=\"$HOME/.codex/plugins/cache/autodev-marketplace/autodev\"; if [ -d \"$AUTODEV_BASE\" ]; then AUTODEV_ROOT_CAND=$(find \"$AUTODEV_BASE\" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort | tail -1); run_autodev_hook \"$AUTODEV_ROOT_CAND\"; fi; printf '{}\\n'", "timeout": 30 } ] @@ -106,7 +106,7 @@ "hooks": [ { "type": "command", - "command": "AUTODEV_HOOK=subagent-scope-guard; AUTODEV_ROOT=\"${AUTODEV_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-}}}}\"; for AUTODEV_ROOT_CAND in \"$AUTODEV_ROOT\" . \"$HOME/.codex/plugins/cache/autodev-marketplace/autodev\"/* \"$HOME/.claude/plugins/marketplace/autodev\"/*; do [ -n \"$AUTODEV_ROOT_CAND\" ] || continue; if [ -x \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" ]; then exec \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" \"$AUTODEV_HOOK\"; fi; done; printf '{}\\n'", + "command": "AUTODEV_HOOK=subagent-scope-guard; AUTODEV_ROOT=\"${AUTODEV_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}}}\"; run_autodev_hook() { AUTODEV_ROOT_CAND=\"$1\"; [ -n \"$AUTODEV_ROOT_CAND\" ] || return 1; if [ -x \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" ]; then exec \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" \"$AUTODEV_HOOK\"; fi; return 1; }; run_autodev_hook \"$AUTODEV_ROOT\"; run_autodev_hook .; AUTODEV_BASE=\"$HOME/.codex/plugins/cache/autodev-marketplace/autodev\"; if [ -d \"$AUTODEV_BASE\" ]; then AUTODEV_ROOT_CAND=$(find \"$AUTODEV_BASE\" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort | tail -1); run_autodev_hook \"$AUTODEV_ROOT_CAND\"; fi; printf '{}\\n'", "timeout": 15 } ] @@ -118,7 +118,7 @@ "hooks": [ { "type": "command", - "command": "AUTODEV_HOOK=pre-compact-snapshot; AUTODEV_ROOT=\"${AUTODEV_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-}}}}\"; for AUTODEV_ROOT_CAND in \"$AUTODEV_ROOT\" . \"$HOME/.codex/plugins/cache/autodev-marketplace/autodev\"/* \"$HOME/.claude/plugins/marketplace/autodev\"/*; do [ -n \"$AUTODEV_ROOT_CAND\" ] || continue; if [ -x \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" ]; then exec \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" \"$AUTODEV_HOOK\"; fi; done; printf '{}\\n'", + "command": "AUTODEV_HOOK=pre-compact-snapshot; AUTODEV_ROOT=\"${AUTODEV_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}}}\"; run_autodev_hook() { AUTODEV_ROOT_CAND=\"$1\"; [ -n \"$AUTODEV_ROOT_CAND\" ] || return 1; if [ -x \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" ]; then exec \"$AUTODEV_ROOT_CAND/hooks/run-hook.cmd\" \"$AUTODEV_HOOK\"; fi; return 1; }; run_autodev_hook \"$AUTODEV_ROOT\"; run_autodev_hook .; AUTODEV_BASE=\"$HOME/.codex/plugins/cache/autodev-marketplace/autodev\"; if [ -d \"$AUTODEV_BASE\" ]; then AUTODEV_ROOT_CAND=$(find \"$AUTODEV_BASE\" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort | tail -1); run_autodev_hook \"$AUTODEV_ROOT_CAND\"; fi; printf '{}\\n'", "timeout": 10 } ] diff --git a/tests/hook-contracts.sh b/tests/hook-contracts.sh index 52a44d1..f020231 100755 --- a/tests/hook-contracts.sh +++ b/tests/hook-contracts.sh @@ -42,8 +42,8 @@ run_hook_wrapper() { hooks/run-hook.cmd "$hook" >"$stdout_file" 2>"$stderr_file" <<<"$payload" } -test_hooks_manifest_commands_do_not_require_claude_plugin_root() { - local tmp payload cmd count stdout_file stderr_file stdout_text status +test_hooks_manifest_commands_do_not_require_root_env_vars() { + local tmp payload cmd count stdout_file stderr_file stdout_text status shell_name shell_bin tmp="$(mktemp -d)" trap 'rm -rf "$tmp"' RETURN payload='{"source":"startup","tool_name":"Bash","tool_input":{"command":"echo hi"},"prompt":"continue","cwd":"'"$tmp"'","transcript_path":"'"$tmp"'/session.jsonl"}' @@ -52,36 +52,41 @@ test_hooks_manifest_commands_do_not_require_claude_plugin_root() { count=0 while IFS= read -r cmd; do count=$((count + 1)) - stdout_file="$tmp/stdout-${count}.json" - stderr_file="$tmp/stderr-${count}.txt" - set +e - ( - cd "$tmp" - printf '%s' "$payload" | env -u AUTODEV_PLUGIN_ROOT -u CLAUDE_PLUGIN_ROOT -u CODEX_PLUGIN_ROOT -u CURSOR_PLUGIN_ROOT \ - LC_ALL=C LANG=C LC_CTYPE=C sh -c "$cmd" - ) >"$stdout_file" 2>"$stderr_file" - status=$? - set -e - - if [ "$status" = "127" ]; then - fail "hooks.json command ${count}: must not require CLAUDE_PLUGIN_ROOT; stderr: $(cat "$stderr_file")" - continue - fi - if [ "$status" != "0" ]; then - fail "hooks.json command ${count}: expected exit 0, got ${status}; stderr: $(cat "$stderr_file")" - continue - fi - stdout_text="$(cat "$stdout_file")" - if ! printf '%s' "$stdout_text" | jq -e . >/dev/null 2>&1; then - fail "hooks.json command ${count}: expected JSON stdout, got: ${stdout_text}; stderr: $(cat "$stderr_file")" - fi + for shell_name in sh zsh; do + if ! shell_bin="$(command -v "$shell_name" 2>/dev/null)"; then + continue + fi + stdout_file="$tmp/stdout-${count}-${shell_name}.json" + stderr_file="$tmp/stderr-${count}-${shell_name}.txt" + set +e + ( + cd "$tmp" + printf '%s' "$payload" | env -u AUTODEV_PLUGIN_ROOT -u CLAUDE_PLUGIN_ROOT -u CODEX_PLUGIN_ROOT -u CURSOR_PLUGIN_ROOT \ + LC_ALL=C LANG=C LC_CTYPE=C "$shell_bin" -c "$cmd" + ) >"$stdout_file" 2>"$stderr_file" + status=$? + set -e + + if [ "$status" = "127" ]; then + fail "hooks.json command ${count} (${shell_name}): must not require plugin-root env vars; stderr: $(cat "$stderr_file")" + continue + fi + if [ "$status" != "0" ]; then + fail "hooks.json command ${count} (${shell_name}): expected exit 0, got ${status}; stderr: $(cat "$stderr_file")" + continue + fi + stdout_text="$(cat "$stdout_file")" + if ! printf '%s' "$stdout_text" | jq -e . >/dev/null 2>&1; then + fail "hooks.json command ${count} (${shell_name}): expected JSON stdout, got: ${stdout_text}; stderr: $(cat "$stderr_file")" + fi + done done < <(jq -r '.. | objects | select(.type? == "command") | .command' hooks/hooks.json) if [ "$count" = "0" ]; then fail "hooks.json command discovery: expected at least one command hook" return fi - pass "hooks.json commands: run without CLAUDE_PLUGIN_ROOT and emit JSON" + pass "hooks.json commands: run without plugin-root env vars and emit JSON under sh/zsh" } # emit_locked_fixture @@ -1859,7 +1864,7 @@ test_demo_fidelity_fail_open_when_state_unwritable() { } require_jq -test_hooks_manifest_commands_do_not_require_claude_plugin_root +test_hooks_manifest_commands_do_not_require_root_env_vars test_session_start_json test_session_start_time_dedup_suppresses_rapid_refires test_session_start_resume_target_checkpoint