Environment
moshi-hooks 1.0.2 (published to npm)
- Bun 1.3.12
- Ubuntu 24.04.4 LTS (kernel 6.17), x86_64
- Claude Code (global
~/.claude/settings.json)
- Invocation:
bunx moshi-hooks setup (no dir argument → global scope)
Repro
bunx moshi-hooks setup
grep -o '"command": *"[^"]*"' ~/.claude/settings.json | sort -u
Observed command string written to all six hook entries (SessionStart, Stop, SubagentStop, Notification, PreToolUse, PostToolUse):
bun /tmp/bunx-1000-moshi-hooks@latest/node_modules/moshi-hooks/src/index.ts # moshi-hooks
Problem
/tmp/bunx-<uid>-<pkg>@<version>/ is Bun's per-invocation bunx cache directory. On most Linux distributions /tmp is cleared on reboot (tmpfs, or systemd-tmpfiles cleanup). Ubuntu 24.04 with default systemd-tmpfiles-clean.timer will also age out files in /tmp after ~10 days even without a reboot.
Once that directory is gone, every hook invocation fails:
error: Module not found "/tmp/bunx-1000-moshi-hooks@latest/node_modules/moshi-hooks/src/index.ts"
Because the hooks are registered with "async": true, the Claude Code session does not surface the failure — the user sees no push notifications and no indication why. This makes the bug particularly painful: setup appears to succeed, works for a while, then silently degrades.
Impact
Any Linux user running bunx moshi-hooks setup will eventually lose all hook delivery without warning. macOS users are also exposed if /tmp is pruned.
Workaround I used
bun add -g moshi-hooks # persistent install at ~/.bun/bin/moshi-hooks
sed -i 's#bun /tmp/bunx-[^ ]*/node_modules/moshi-hooks/src/index.ts#'"$HOME"'/.bun/bin/moshi-hooks#g' \
~/.claude/settings.json
After this, the six hook entries look like:
"command": "/home/<user>/.bun/bin/moshi-hooks # moshi-hooks"
which survives reboots and tmp cleanup.
Suggested fix
setup should either:
- Detect how it was invoked and emit the right persistent path. If running under
bunx, prefer a globally-installed moshi-hooks binary on PATH, falling back to installing one (bun add -g moshi-hooks) before writing settings. Fail loudly if no persistent location exists.
- Or emit a re-resolving command, e.g.
bunx --bun moshi-hooks or npx --yes moshi-hooks@latest, and accept the per-invocation cost. This avoids a stale path but adds latency on every hook fire.
Option 1 is the cleaner default. A README note recommending bun add -g moshi-hooks before setup would also help users who want to do this manually.
Notes
- Setup does correctly write to
~/.claude/settings.json for global scope.
- The written hook entries themselves are structurally valid Claude Code hooks.
- This is purely an issue with the
command string pointing at a volatile path.
Environment
moshi-hooks1.0.2 (published to npm)~/.claude/settings.json)bunx moshi-hooks setup(no dir argument → global scope)Repro
Observed command string written to all six hook entries (
SessionStart,Stop,SubagentStop,Notification,PreToolUse,PostToolUse):Problem
/tmp/bunx-<uid>-<pkg>@<version>/is Bun's per-invocationbunxcache directory. On most Linux distributions/tmpis cleared on reboot (tmpfs, orsystemd-tmpfilescleanup). Ubuntu 24.04 with defaultsystemd-tmpfiles-clean.timerwill also age out files in/tmpafter ~10 days even without a reboot.Once that directory is gone, every hook invocation fails:
Because the hooks are registered with
"async": true, the Claude Code session does not surface the failure — the user sees no push notifications and no indication why. This makes the bug particularly painful: setup appears to succeed, works for a while, then silently degrades.Impact
Any Linux user running
bunx moshi-hooks setupwill eventually lose all hook delivery without warning. macOS users are also exposed if/tmpis pruned.Workaround I used
After this, the six hook entries look like:
which survives reboots and tmp cleanup.
Suggested fix
setupshould either:bunx, prefer a globally-installedmoshi-hooksbinary onPATH, falling back to installing one (bun add -g moshi-hooks) before writing settings. Fail loudly if no persistent location exists.bunx --bun moshi-hooksornpx --yes moshi-hooks@latest, and accept the per-invocation cost. This avoids a stale path but adds latency on every hook fire.Option 1 is the cleaner default. A README note recommending
bun add -g moshi-hooksbeforesetupwould also help users who want to do this manually.Notes
~/.claude/settings.jsonfor global scope.commandstring pointing at a volatile path.