Repository navigation
Releases
Releases · dooly6276/claude-bulkhead
v0.5.1
Fixed
- basics
rm-rootblockscmd /c "rmdir /s /q \"D:\path\""run from PowerShell or cmd. Neither cmd.exe nor PowerShell treats\"as an escaped quote, so rd gets a path that starts at\, the root of the current drive; the same line from Antigravity wiped drive D (a r/google_antigravity report).rd /sanddel /stargets that start with\"(or with\D:once PowerShell has ended its string at\") are blocked, and so isdel /sof\. The message suggestsRemove-Item -LiteralPath '<path>' -Recurseorcmd /c rmdir /s /q "<path>"without backslash-escaped quotes. From bash (Git Bash) the escapes are removed before cmd reads the line, socmd //c "rmdir /s /q \"D:\work\out\""gets the whole path and passes; a single quotedcmd /cargument from bash or PowerShell is now read as that shell's value, so such a path is checked like any otherrd /starget.
0.5.0: xargs-split and link-following deletes, home app data, PowerShell $home, Pi and four more agents
0.5.0 adds guards for four more ways agents deleted real data this autumn, closes the holes a reviewer found in 0.4.0's root check, and adds Pi, Qwen Code, Kimi Code, Grok Build and Antigravity.
New blocks
rm-xargs-split:xargswithout-0/-drunningrm -r, orxargs dirname/basenamefeeding anrm -rlater in the pipeline. From a r/ClaudeCode report wherexargs -n1 dirnamesplitApplication Supportat the space, returned~/Library, andrm -rfdeleted it. Usefind ... -print0 | xargs -0 rm -rf --orfind ... -delete.rm-rootnow also covers whole app-data folders one level into home (~/Library/Application Support,~/Library/Containers,~/AppData/Roaming,~/AppData/Local,~/.local/share), another user's home, andC:\Users.- PowerShell:
Remove-Item -Recurse(any spelling, with or without-Force) of$home,$env:USERPROFILE,(Split-Path $home)or a drive root, also insidepowershell -Commandandpwsh -c(anomalyco/opencode#62825, where$homeis PowerShell's automatic variable for the user profile). rm-through-link:git worktree remove --forceon a folder holding a junction or symlink to an existing folder outside it, and on Windows a PowerShellRemove-Item -Recurseof such a folder. From a r/ClaudeAI report where removing two old worktrees on Windows deleted about 230 GB of models and a.venvthey linked to. The message givescmd /c rmdir "<link>"orunlink <link>to remove the link first.
Fixed
rm-rootfills in variables set on the same line, soD=/; rm -rf "$D/"is blocked, and a variable set fromcygpath,wslpath,realpath,readlink -fordirnameof a root counts as that root, so"${D:?}/"no longer passes for it."${D:?}/sub"with a literal subfolder still passes. Reported by foma-agent in anthropics/claude-code#95426.- The
rm-empty-varhint forrm -rf "$BUILD_DIR"/*kept the glob inside the quotes. It now saysrm -rf "${BUILD_DIR:?}"/*.
More agents
- Pi:
pi install npm:claude-bulkhead(the npm package is now a Pi package), orbulkhead setup pi. bulkhead setup qwen|kimi|grok|antigravityfor Qwen Code, Kimi Code, Grok Build and Antigravity.
Check any command without installing: npx -y claude-bulkhead explain -- '<command>'. Upgrade in Claude Code with /plugin marketplace update bulkhead, or npm i -g claude-bulkhead@0.5.0. Full notes in CHANGELOG.md.
0.4.0: deletes through an empty variable, .git deletes, BSD pkill
Added
- Bulkhead recognizes Aider CLI sessions in status and session ownership checks.
- basics
rm-empty-var:rm -rf $D/,rm -rf "$D"/*,rm -rf $(cygpath -u "$X")/andrsync --delete src/ $D/are blocked when the variable is not set to something non-empty earlier in the same shell, because an empty or unset variable (or a command that prints nothing) turns the path into/(anthropics/claude-code#109735). Deleting the whole output of a path command, such asrm -rf "$(cygpath -u 'C:\')", which prints/c/, is blocked too (anthropics/claude-code#95426 lost 600 GB)."${D:?}/","$TMPDIR/foo"andD=$(mktemp -d) && rm -rf "$D"/pass. AlsoRemove-Item -Recurse -Force $env:X\,rd /s /q %X%\, and the same commands insidebash -candwsl -- bash -lc. Withwsl -- bash -lc '...'an assignment inside the string does not count, because wsl hands the line to a Linux shell that expands the variable first. Applies with a single session. Turn it off withbasics.rmEmptyVar. - basics
rm-unquoted-var:rm -rf $DwithDunquoted is blocked when the value, set earlier on the line or in the environment, has a space or a wildcard, since bash splits it into several paths and deletes each (anthropics/claude-code#107271). Turn it off withbasics.rmUnquotedVar. - basics
rm-git-dir:rm -rf .git,rm -rf path/.gitandfind . -name .git -exec rm -rf {} +are blocked when that.gitis an existing folder, since the repository's history goes with it (anthropics/claude-code#107274). A.gitthe same line creates, such as aftergit clone, passes. Turn it off withbasics.rmGitDir. - kill-guard
bsd-pkill: on macOS and the BSDs,pkillwith a long option (--signal,--full) or with more than one pattern is blocked, also with a single session. BSDpkillhas no long options and reads everything after the first pattern as another pattern, sopkill -f "gcloud.py compute" -u me --signal 0, meant as an existence check, also matched0and sent SIGTERM to about 66 processes, every other Claude Code session among them (anthropics/claude-code#109627). The message suggestspgrep -f '<pattern>'for an existence check. Turn it off withkillGuard.bsdPkill.
Changed
bulkhead scan --sharelists what people fear first, in plain words: kills by name that would have hit other sessions' processes, recursive deletes,git add -Aof another session's edits, branch switches in a shared tree, port clashes. The rest folds into one line and the foreground waits come last. No rule names appear in the share text any more (before, a rule without a description showed asbasics self-allow).
Fixed
- kill-guard reads
pkillarguments the way the platform'spkilldoes. On Linux, options written after the pattern (pkill -f x --signal 0,pkill -f x -P $$) are options, as procps reads them, so a signal-0 check or a-Pscope there passes. On macOS, every extra operand is a pattern, and each gets the same checks as the first.
0.3.7
Changed
- The bulkhead skill now fits every agent that loads it, not only Claude Code. It says that orphan-reaper's cleanup at session end is Claude Code only and that in Copilot CLI and the other agents you stop your own servers before you finish, and it explains how to run
bulkheadandbulkhead-slotwithnodefrom the plugin folder when a plugin-only install (Copilot CLI) does not put them onPATH.
Fixed
- Codex and Copilot CLI sessions are found when
CODEX_HOMEorCOPILOT_HOMEmoves their config folder. Before, a machine with only a moved folder skipped the process scan for that agent, so its servers and folders were not protected from other sessions.
0.3.6
Changed
- Releases are published to npm by GitHub Actions with npm trusted publishing (
.github/workflows/publish.yml), so each version on npm carries a provenance statement linking it to this repository and the commit it was built from. The same workflow attaches the Gemini CLI extension archive to the release. The guards are unchanged from 0.3.5.
0.3.5
Added
- kill-guard
broad-pattern:pkill -f <pattern>is blocked when the pattern matches a desktop app (a process inside an.appbundle) or 8 or more processes outside the session's own tree, also when only one session runs.pkill -f cat, meant for one stuckcat, matches every app under/Applications; on macOS, BSDpkillreads-n,-uand-Pwritten after the pattern as more patterns, so they narrow nothing (anthropics/claude-code#100684, #107867). Processes the session started do not count. Turn it off withkillGuard.broadPattern.
Fixed
- The Gemini extension archive test no longer fails on Windows runners where the checkout and the temp folder are on different drives.
0.3.4: Gemini CLI extension and Codex plugin
Added
- Gemini CLI extension:
gemini extensions install https://github.com/dooly6276/claude-bulkheadinstalls the guards as an extension (gemini-extension.json, aBeforeToolhook onrun_shell_command). The extension is the release assetbulkhead-gemini-extension.tar.gz, built byscripts/build-gemini-extension.js. - Codex plugin:
codex plugin marketplace add dooly6276/claude-bulkhead, thencodex plugin add bulkhead@bulkheadinstalls the guards as a Codex plugin (.codex-plugin/plugin.json, aPreToolUsehook onBash). The marketplace entry installs the npm package, which now ships.codex-plugin/andcodex/.
Fixed
- Windows:
bulkhead worktree pruneandlistnow match a session or the current folder to its worktree when one of the two paths uses an 8.3 short name (C:\Users\RUNNER~1\...) and git prints the long one. Before, prune could remove a worktree a live session was working in, or the one it was run from. The Windows CI jobs pass again: the other failures were tests that assumed/bin/sh, paths without a drive letter, or the Linux time limit for the 1 MB fuzz case.
v0.3.3: guard errors are logged, CI fuzz fix
Changed
- A guard that throws is still skipped so the other guards run, but the error is now written to the event log with rule
guard-errorand toguards.log, sobulkhead logandbulkhead topshow it instead of it looking like a clean pass.
Fixed
- port-guard reads a very long pipeline (hundreds of thousands of segments) in a fraction of the time: dev-server detection looks a program up by name instead of testing every known tool against every segment. This also keeps the 1 MB fuzz case inside its time limit on slower CI runners.
v0.3.2: plugins for Copilot CLI, Cursor and opencode
New
- Plugin bundles for Copilot CLI, Cursor and opencode. The repository is now also an Agent Plugins 1.0.0 plugin (
plugin.jsonat the root, hooks incom.github.copilot/hooks/hooks.json) that Copilot CLI installs withcopilot plugin marketplace add dooly6276/claude-bulkheadandcopilot plugin install bulkhead@bulkhead; a Cursor plugin (.cursor-plugin/plugin.jsonwith its own.cursor-plugin/hooks.json); and an opencode plugin from npm ("plugin": ["claude-bulkhead"], entryopencode/plugin.mjs). Each callsbulkhead hook --agent <name>from the plugin's own copy, so the guards are the same as withbulkhead setup <agent>and nothing is written to the agent's config. The Claude Code plugin andhooks/hooks.jsonare unchanged.
v0.3.1
Changed
- A project file can only make bulkhead stricter. A repository's
.claude/bulkhead.jsoncomes with whatever you clone, so it can no longer take names off kill-guard's list, ignore ports, lengthen the wait limit, lower the RAM floor, turn a guard or rule off, turn on auto-queue or add programs for orphan-reaper to stop. Those settings are read from the global file only; in a project file they are ignored with a warning inbulkhead config,bulkhead doctorand the startup notice. A project can still add shared names, protected branches and secret patterns (added to the global lists, no longer replacing them), tighten limits and turn guards on. A project preset applies only where it is stricter. Block messages now point to the global file. - The skill no longer runs
bulkhead remote runon its own. It shows the user the remote, the commit and every uncommitted change it would send, any--filesit would add, and the command, and runs it only after the user says yes to that run, as/bulkhead:remotealready did. memory-guard's block message says the same.
Fixed
bulkhead worktree prunecounts a branch as merged when every commit on it was cherry-picked or rebased onto the base, not only when it was merged or squash-merged.