#!/usr/bin/env bash # PreToolUse(Bash) hook: block bash commands that WRITE files under /tmp on # Windows (Git Bash / MSYS). # # Why: MSYS bash and the harness's other tools (Write, Python via py launcher) # resolve /tmp to DIFFERENT real directories on Windows, so a file curl/tee # writes to /tmp cannot be read back by the next tool call — the single most # repeated tmp-path friction in errorlog.md (ref=feedback_tmp_path_windows, # 6+ hits; also breaks run_in_background shells where TMPDIR is unset). # # Windows-only: exits 0 immediately on macOS/Linux where /tmp is one real dir. # Only WRITE patterns are blocked (redirects, tee, -o/--output, cp/mv/mktemp # targets). Reads (ls/cat/rm /tmp/...) pass. # # Dual-driver like block-backslash-winpath.sh: handles Claude (tool_input) and # Grok (toolInput) event shapes; emits Grok decision JSON when denying there. case "$(uname -s 2>/dev/null)" in MINGW*|MSYS*|CYGWIN*) ;; # Windows Git-bash: the mismatch exists — check *) exit 0 ;; # real /tmp elsewhere: nothing to protect esac input=$(cat) cmd=$(echo "$input" | jq -r '(.toolInput // .tool_input // {}) | .command // ""' 2>/dev/null || python -c " import sys, json try: d = json.load(sys.stdin) ti = d.get('toolInput') or d.get('tool_input') or {} print(ti.get('command', '')) except: print('') " 2>/dev/null || echo '') is_grok=$(echo "$input" | jq -r 'if has("hookEventName") or has("toolInput") then "1" else "0" end' 2>/dev/null || echo '0') # Strip quoted substrings so a /tmp mention inside a string (commit message, # grep pattern) does not false-trigger; real write targets sit outside quotes. bare=$(printf '%s' "$cmd" | sed -E "s/'[^']*'//g; s/\"[^\"]*\"//g") if printf '%s' "$bare" | grep -qE '(>>?[[:space:]]*/tmp/|[[:space:]]tee[[:space:]]+(-a[[:space:]]+)?/tmp/|(^|[[:space:]])-o[[:space:]]*/tmp/|--output(=|[[:space:]]+)/tmp/|(^|[[:space:]]|;|\|)(cp|mv)[[:space:]][^|;&>]*[[:space:]]/tmp/|mktemp[[:space:]]+(-d[[:space:]]+)?/tmp/)'; then reason="Blocked write to /tmp in bash on Windows: MSYS /tmp and the Write/Python tools' /tmp are DIFFERENT directories — the file cannot be read back by the next tool call." echo "BLOCKED: do not write files under /tmp on Windows (Git Bash)." echo "" echo "MSYS bash and the harness's other tools resolve /tmp to different real" echo "directories, so the next tool call cannot read what you just wrote" echo "(ref: feedback_tmp_path_windows). Use one of these instead:" echo " - repo-relative scratch: curl -o ./.x.json ... (gitignored .tmp-* also works)" echo " - the session scratchpad dir (absolute path, shared by all tools)" echo " - pipe directly: curl ... | jq ... (no intermediate file)" if [ "$is_grok" = "1" ]; then printf '{"decision":"deny","reason":"%s"}\n' "$reason" fi exit 2 fi exit 0