Summary
On Windows (git-bash / msys2), every hook event silently fails because peon.sh passes ~30 KB of Python source to python3 -c "...". Windows' CreateProcess caps the full command line at ~32,767 characters, so the exec is rejected before Python ever runs. The failure is invisible because stderr on that line is redirected to /dev/null — peon debug status keeps reporting log files: 0, and no sounds play on Stop / Notification / PermissionRequest / etc.
Manual sub-commands (peon preview, packs list, volume) work fine because they take shorter code paths that don't go through the consolidated block.
Environment
- peon-ping: 2.17.3
- OS: Windows 11 Pro (build 26200)
- Shell: Git Bash / MSYS2
- Python: 3.12.x
- Installed via
install.sh | bash to ~/.claude/hooks/peon-ping
Reproduction
With debug: true in config.json:
echo '{"hook_event_name":"Stop","session_id":"test","cwd":"/f/Git-Projects"}' \
| bash ~/.claude/hooks/peon-ping/peon.sh
Expected: an entry in logs/peon-ping-<date>.log, a sound played via the msys2 backend.
Observed: silent exit, no log entries, no sound.
Temporarily removing the 2>/dev/null from line 4280 reveals the underlying error:
peon.sh: line 4280: .../python3: Argument list too long
peon debug status still shows log files: 0 no matter how many hook events fire.
Root cause
peon.sh lines ~3341–4280:
_PEON_PYOUT=$(python3 -c "
<~30 KB of inline Python, with bash-interpolated $vars
and the injected ${_PEON_STATE_PY_HELPERS} block>
" <<< "$INPUT" 2>/dev/null)
python3 -c "<code>" passes <code> as argv[2], which goes through Windows' CreateProcess command-line length limit. The consolidated block (added for the "~120–200 ms faster hook response" optimization) plus the interpolated state-helpers easily exceeds 32 KB.
Linux execve and macOS limits are ~2 MB, so this never surfaces there.
Local workaround (confirmed fix)
Write the Python to a temp file and invoke it by path; $INPUT still arrives via stdin:
_PEON_PY_TMP=$(mktemp -t peon-ping-XXXXXX.py 2>/dev/null || echo "/tmp/peon-ping-$$.py")
cat > "$_PEON_PY_TMP" <<__PEON_PY_EOF__
<same Python block — unquoted heredoc preserves the same $/`/\ expansion
that the current double-quoted `python3 -c "..."` already does,
so the bash-interpolated variables still work unchanged>
__PEON_PY_EOF__
_PEON_PYOUT=$(python3 "$_PEON_PY_TMP" <<< "$INPUT" 2>/dev/null)
rm -f "$_PEON_PY_TMP"
After applying locally against v2.17.3, Stop / Notification / PermissionRequest events start logging and playing sounds immediately. Tested on Windows 11 + git-bash + Python 3.12.
Suggested upstream fix
Two reasonable approaches:
- Temp file — what the workaround above uses. Small diff, preserves the existing bash-interpolation behavior the code already relies on, no Python rewrite needed.
python3 - — needs redesigning how $INPUT gets delivered (currently on stdin), so less clean.
Option 1 is the smaller change and matches the existing architecture. Happy to send a PR if helpful.
Secondary suggestion
Line 4280 unconditionally swallows stderr (2>/dev/null). Even a one-line message when debug: true or $PEON_DEBUG=1 would have made this obvious instead of an untraceable silent failure. Something like:
if [ "${PEON_DEBUG:-0}" = "1" ] || [ "$(jq -r .debug "$CONFIG")" = "true" ]; then
_PEON_PYOUT=$(python3 ... <<< "$INPUT")
else
_PEON_PYOUT=$(python3 ... <<< "$INPUT" 2>/dev/null)
fi
Worth considering as a general observability improvement — right now, any Python-side crash in that block on any platform will look identical to this Windows issue.
Summary
On Windows (git-bash / msys2), every hook event silently fails because
peon.shpasses ~30 KB of Python source topython3 -c "...". Windows'CreateProcesscaps the full command line at ~32,767 characters, so the exec is rejected before Python ever runs. The failure is invisible because stderr on that line is redirected to/dev/null—peon debug statuskeeps reportinglog files: 0, and no sounds play onStop/Notification/PermissionRequest/ etc.Manual sub-commands (
peon preview,packs list,volume) work fine because they take shorter code paths that don't go through the consolidated block.Environment
install.sh | bashto~/.claude/hooks/peon-pingReproduction
With
debug: trueinconfig.json:Expected: an entry in
logs/peon-ping-<date>.log, a sound played via the msys2 backend.Observed: silent exit, no log entries, no sound.
Temporarily removing the
2>/dev/nullfrom line 4280 reveals the underlying error:peon debug statusstill showslog files: 0no matter how many hook events fire.Root cause
peon.shlines ~3341–4280:python3 -c "<code>"passes<code>asargv[2], which goes through Windows'CreateProcesscommand-line length limit. The consolidated block (added for the "~120–200 ms faster hook response" optimization) plus the interpolated state-helpers easily exceeds 32 KB.Linux
execveand macOS limits are ~2 MB, so this never surfaces there.Local workaround (confirmed fix)
Write the Python to a temp file and invoke it by path;
$INPUTstill arrives via stdin:After applying locally against v2.17.3,
Stop/Notification/PermissionRequestevents start logging and playing sounds immediately. Tested on Windows 11 + git-bash + Python 3.12.Suggested upstream fix
Two reasonable approaches:
python3 -— needs redesigning how$INPUTgets delivered (currently on stdin), so less clean.Option 1 is the smaller change and matches the existing architecture. Happy to send a PR if helpful.
Secondary suggestion
Line 4280 unconditionally swallows stderr (
2>/dev/null). Even a one-line message whendebug: trueor$PEON_DEBUG=1would have made this obvious instead of an untraceable silent failure. Something like:Worth considering as a general observability improvement — right now, any Python-side crash in that block on any platform will look identical to this Windows issue.