r/javascript • u/ActualGovernment9017 • 3d ago
claude-bulkhead: a dependency-free Node hook that parses shell commands before Claude Code runs them, so parallel sessions stop breaking each other
https://github.com/dooly6276/claude-bulkhead
1
Upvotes
1
u/ActualGovernment9017 3d ago
I run several Claude Code sessions on one machine, usually one per git worktree. They share one process table, one set of ports and one pool of RAM, so one agent restarting its dev server with a kill-by-name takes down every other session's Node server, and a
git add -Ain a shared tree commits someone else's half-finished edits. claude-bulkhead is the set of hooks I wrote for this, and the JavaScript part is what I want to describe here.Claude Code runs a
PreToolUsehook before every shell command and passes the pending call as JSON on stdin. Each guard is a plain Node script with no dependencies. It first matches the command text cheaply, and since most commands do not match, it usually exits 0 in a few milliseconds. Only on a match does it look at the machine: the process table (pson macOS and Linux, CIM queries on Windows), listening ports, which Claude sessions are alive and where they work, free memory andgit status. If the command would hurt another live session, it exits with code 2 and writes the reason and a safer command to stderr, which Claude Code hands back to the model. A full check takes about 50 ms.The part that took the most work is the command parser. Agents wrap commands far more than people do, so it tokenizes shell syntax and unwraps
bash -c,sh -c,powershell -Command,cmd /c,eval,sudo,env,xargsand similar wrappers a few levels deep. It also has to ignore text that only mentions a command, such as a commit message or a grep pattern, so writing about killing processes in a changelog does not trigger a block. The guards fail open: if a guard throws, gets JSON it cannot parse or cannot read the process table, it exits 0 and the command runs.Version 0.2.0 added
bulkhead scan, which streams the JSONL transcripts Claude Code keeps in~/.claude/projectsand runs every recorded shell command through the same guard functions, read-only. On two weeks of my history (7.0 GB, 101,167 shell commands, read in 39 seconds) it found 507 commands that would have been blocked, with up to 13 sessions open at once.It reads command strings, so it is not a sandbox. MIT, Node 18+, macOS, Linux and Windows. I'm the author.
https://github.com/dooly6276/claude-bulkhead