claude-bulkhead 0.0.0-stage → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/CHANGELOG.md +141 -0
  2. package/LICENSE +21 -0
  3. package/README.es.md +590 -0
  4. package/README.ja.md +590 -0
  5. package/README.ko.md +590 -0
  6. package/README.md +589 -2
  7. package/README.pt-BR.md +590 -0
  8. package/README.zh-CN.md +590 -0
  9. package/bin/bulkhead +3 -0
  10. package/bin/bulkhead-slot +3 -0
  11. package/bin/bulkhead-statusline +3 -0
  12. package/hooks/hooks.json +99 -0
  13. package/package.json +55 -4
  14. package/src/agent-hook.js +162 -0
  15. package/src/agent-setup.js +256 -0
  16. package/src/agents.js +89 -0
  17. package/src/allow-cli.js +65 -0
  18. package/src/allow-hint.js +62 -0
  19. package/src/allow-once.js +152 -0
  20. package/src/auto-queue.js +93 -0
  21. package/src/bash-guards.js +36 -0
  22. package/src/basics.js +255 -0
  23. package/src/checkout-guard.js +564 -0
  24. package/src/cli-util.js +91 -0
  25. package/src/cli.js +77 -0
  26. package/src/config-check.js +46 -0
  27. package/src/config-cli.js +116 -0
  28. package/src/config.js +333 -0
  29. package/src/dev-servers.js +523 -0
  30. package/src/disk-guard.js +200 -0
  31. package/src/doctor-enforcement.js +273 -0
  32. package/src/doctor.js +265 -0
  33. package/src/edit-guard.js +196 -0
  34. package/src/events.js +133 -0
  35. package/src/explain.js +251 -0
  36. package/src/git-info.js +55 -0
  37. package/src/gitargs.js +82 -0
  38. package/src/hook.js +126 -0
  39. package/src/kill-extras.js +219 -0
  40. package/src/kill-guard.js +586 -0
  41. package/src/kill-owners.js +116 -0
  42. package/src/log-cli.js +151 -0
  43. package/src/mcp.js +131 -0
  44. package/src/memory-guard.js +247 -0
  45. package/src/memory.js +95 -0
  46. package/src/notices.js +95 -0
  47. package/src/orphan-reaper.js +574 -0
  48. package/src/paths.js +31 -0
  49. package/src/port-cli.js +134 -0
  50. package/src/port-guard.js +154 -0
  51. package/src/port-kills.js +198 -0
  52. package/src/pressure.js +75 -0
  53. package/src/processes.js +213 -0
  54. package/src/reap-cli.js +56 -0
  55. package/src/remote-sh.js +478 -0
  56. package/src/remote.js +1034 -0
  57. package/src/remotes.js +126 -0
  58. package/src/scan-report.js +166 -0
  59. package/src/scan.js +673 -0
  60. package/src/secrets-guard.js +301 -0
  61. package/src/self-allow.js +73 -0
  62. package/src/session-info.js +121 -0
  63. package/src/session-state.js +176 -0
  64. package/src/sessions.js +236 -0
  65. package/src/shell.js +857 -0
  66. package/src/slot-cli.js +221 -0
  67. package/src/slots.js +216 -0
  68. package/src/stable-port.js +61 -0
  69. package/src/status.js +149 -0
  70. package/src/statusline.js +98 -0
  71. package/src/subagent-guard.js +24 -0
  72. package/src/top-worker.js +14 -0
  73. package/src/top.js +330 -0
  74. package/src/wait-guard.js +327 -0
  75. package/src/worktree-cli.js +454 -0
  76. package/src/worktree-copy.js +82 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,141 @@
1
+ # Changelog
2
+
3
+ ## 0.3.0 - 2026-11-12
4
+
5
+ ### New
6
+
7
+ - **One hook process for every Bash guard.** `src/bash-guards.js` runs the guards in a row, text-only checks first and the ones that read git, memory, processes or ports last, and stops at the first block. A Bash call used to start seven Node processes. With 10 sessions making a tool call at the same moment, each now waits about 89 ms instead of 318 ms (611 ms to 109 ms with 20). `node scripts/bench-hooks.js 9 --sessions 10` measures it on your machine.
8
+ - **`bulkhead explain -- <command>`** shows what every guard would say about a command, with the evidence (which session owns the process or port, how much RAM is free) and how to allow it, without running anything.
9
+ - **Allow once.** Every block message ends with a code: the user runs `bulkhead allow <code>` in their own terminal and the same command passes once within 10 minutes. The new basics rule `self-allow` blocks the agent from running `bulkhead allow` or writing the allow files itself, in 25 spellings.
10
+ - **Presets.** `bulkhead config --preset quiet|normal|strict [--project]`. `quiet` keeps only what protects one session from another and the rules that cannot be undone; `strict` tightens the limits, scaled to the machine's RAM, and turns secrets-guard on.
11
+ - **`bulkhead worktree add|list|prune`.** `add` makes `../<repo>-<name>` on a new branch, copies your `.env` files (or what `.worktreeinclude` lists), installs dependencies with the lockfile's package manager and writes a stable `PORT` to `.env.local`. `list` shows branch, age, changes, ahead and behind, live session and port. `prune` removes worktrees that are merged (squash and rebase merges included), clean and idle, never one a live session works in, and is a dry run until `--yes`.
12
+ - **`bulkhead port --stable`** gives a worktree the same port every time, and port-guard suggests it first.
13
+ - **Auto-queue.** With `{"memoryGuard":{"autoQueue":true}}`, a heavy command is not refused: the hook rewrites it to run through `bulkhead-slot` and it starts when there is room. Waiting jobs are served first in, first out, poll once a second, and print their place in line every 30 seconds.
14
+ - **subagent-guard** (opt-in, `memoryGuard.subagents`) stops new subagents while memory pressure is critical.
15
+ - **edit-guard** tells the agent, before it edits a file, that another live session edited the same file in the last 30 minutes, in this tree or another worktree of the repository. Advisory by default; `editGuard.mode` can be `ask` or `block`.
16
+ - **secrets-guard** (off by default, on in `strict`) blocks showing `.env` files, private keys, cloud and registry credentials, and the whole environment to the model, through the Read and Grep tools or a shell command. `.env.example`, `ls ~/.ssh`, `.pub` files and appending to `.env` pass.
17
+ - **Codex, Gemini CLI, Copilot CLI, Cursor, Windsurf, Droid, Devin, Cline, opencode and Amp.** `bulkhead setup <agent> --write` registers the same guards in that agent's hook config, with a backup of the original. Their CLI sessions count as sessions for port-guard, checkout-guard and `bulkhead status`.
18
+ - **`bulkhead status` and `top`** show a `STATE` column (working, waiting for you, waiting for a permission) and a `BRANCH` column, the session and project of each queued job, and duplicate MCP servers that several sessions start. A `notify.command` runs when a session starts waiting for you.
19
+ - **`bulkhead doctor`** has an Enforcement section: it runs a harmless command through each guard and shows the block and how long it took, and finds settings that switch the hooks off, duplicate installs and a `node` missing from the hooks' `PATH`.
20
+ - **`bulkhead scan --share`** prints a short summary with counts only, for a post or a chat. `--explain N` shows one hit in full with the part that matched.
21
+ - orphan-reaper keeps processes started with `BULKHEAD_KEEP=1`, SSH tunnels, `cloudflared` and databases, and anything matching `orphanReaper.keepPatterns`. The next session start tells you what it stopped.
22
+
23
+ ### Changed
24
+
25
+ - On macOS, free RAM comes from `kern.memorystatus_level` and the memory pressure level instead of a `vm_stat` estimate, which counted far too little as free on a Mac with a lot of file cache. memory-guard blocks heavy commands at pressure level 2 or higher. When neither can be read, heavy commands are let through instead of blocked.
26
+ - More heavy commands are recognised: `xcodebuild`, `swift build` and `swift test`, `fastlane`, `bundle exec`, `go build` and `go test` with `-race` or `./...`, and `nx run|build|test|e2e`.
27
+ - Block messages name the other session by project and branch, and end with the exact config line that would allow the rule.
28
+ - `bulkhead --version`, `bulkhead help <command>` and `<command> --help`; unknown commands and options that need a number are rejected with a clear message.
29
+ - Inside a hook, reading the process table gives up after 4 seconds (8 on Windows) and lets the call through, instead of waiting up to 15 (60).
30
+ - `guards.log` is rotated at 1 MB like `events.jsonl`, and two hooks rotating at the same moment no longer lose the older log.
31
+
32
+ - port-guard knows many more servers, each on its default port: `serve`, `live-server`, `json-server`, `puma`, `gunicorn`, `hypercorn`, `fastapi dev`, Django `runserver`, Streamlit, Jupyter, `trunk`, Metro and React Native, VuePress, VitePress, Phoenix, Parcel, Ember, MkDocs, Hugo, Hexo, Zola, mdBook, Netlify and Vercel dev, Rspack, Rsbuild, Spring Boot and more. It reads the port from `php -S`, `dotnet run --urls`, `--server.port`, `docker run -p` and the `ports:` of a compose file, through `pnpm`, `yarn`, `npm exec`, `bunx`, `uv run`, workspaces and wrappers such as `timeout`, `caffeinate`, `cross-env` and `(cd web && npm run dev &)`. The free-port suggestion uses each tool's own flag.
33
+ - port-guard catches more ways to kill by port: `lsof ... | awk ... | xargs kill` without `-t`, `PID=$(lsof -ti :3000); kill $PID`, loops over `lsof` output, `fkill :3000`, and `netstat`, `ss` and `fuser -n tcp -k` pipelines.
34
+
35
+ - checkout-guard sees more ways to change a shared tree: `git --no-pager add -A` and other global options in front of the subcommand, `git ls-files -m | xargs git add`, `git add $(git ls-files -m)`, `git add "$PWD"` and `*`, `checkout -f`, `stash -a`, `stash drop` and `clear`, `rm -r` of a folder, `restore` or `checkout --` of a folder with uncommitted changes, `cherry-pick`, `revert`, `am`, `apply`, `reset --merge` and `--keep`, and a mixed `reset` over staged files. `rm -rf ./*`, `rm -rf .git` and `find . -delete` count as removing a folder another session works in. Each `cd` in the command is followed, so `cd /tmp/x && rm -rf ./*` passes.
36
+ - New basics rule **`protected-delete`** blocks deleting a protected branch: `git push origin :main`, `git push --delete origin main`, `git branch -D main`. `git branch -d` passes, because git itself refuses to lose unmerged work.
37
+ - checkout-guard compares a session file's start time with the process's, so a reused PID no longer counts as a live session.
38
+
39
+ - kill-guard sees through more indirection: `echo pkill node | sh`, `source <(...)`, `bash <(...)`, `eval "$(...)"`, `xargs pkill`, `busybox`, shell functions and variables (`cmd=pkill; $cmd node`), `tmux`, `screen`, `script`, `parallel`, shell commands that python, node, perl, ruby or php run from a string, and `ssh localhost`. Dev-server and tool names (`next-server`, `uvicorn`, `gunicorn`, `rails`, `expo`, `metro`, `tsc`, `playwright`, Google Chrome and its helpers) are on the shared list, versions included (`node22`, `python3.11`), and so are other coding agents (`codex`, `gemini`, `copilot`, `cursor-agent`).
40
+ - While another Claude session is alive, kill-guard also blocks: `kill <pid>`, `taskkill /PID` and `Stop-Process -Id` of a process in another session's tree; `pkill -f <path>` that matches another session's command line; `pkill -u`, `kill -1` and `kill 0`; `docker ... $(docker ps -q)`, `docker system prune`, `pm2 kill` and `docker compose down` in another session's project; `adb kill-server`, `adb emu kill` without a serial and `simctl shutdown all`; quitting shared apps (Slack, Finder, Dock) and stopping shared services (`brew services stop`, `launchctl bootout`, `systemctl stop`). Each rule has its own switch: `killGuard.massKill`, `dockerMass`, `deviceKill`, `appQuit`, `serviceStop` and `ownerCheck`. The process table is read only for these commands.
41
+ - kill-guard messages are two lines: who owns what you were about to stop, and the command that stops only yours.
42
+
43
+ ### Fixed
44
+
45
+ - checkout-guard lets these through: `git add -A src` and other stage-all forms with a path, `git add -n`, `git stash push -- <paths>`, `--help` of any git command, and single-file `restore`. In a sample of 20,000 real commands in a shared, dirty tree, it blocked 1,432 instead of 1,962; the 39 newly blocked ones were all tree-changing commands.
46
+ - basics advice for `git clean` no longer suggests `git stash push -u`, which checkout-guard blocks in a shared tree.
47
+ - port-guard no longer reads quoted text as a command: `grep -E "next dev|vite"` and `ps | grep -E 'next start|playwright'` pass, as do `vite --version`, `vite build` and any tool run with `--help`. Heredoc bodies fed to python, node or cat are data, not commands. In 160,288 real commands from transcripts, it blocked 12 instead of 28 under the same conditions, and every one of the 12 was a real conflict.
48
+ - A heredoc is shell code only when a shell reads it (`bash <<EOF`, `cat <<EOF | sh`, `ssh localhost bash`). Bodies fed to `while read`, `git`, `psql` or a script are data; bodies fed to python or node are read in that language. In 161,188 real commands, kill-guard blocked 4 instead of 14 when no other session ran, and wait-guard blocked 1,009 instead of 1,123 at a 10-minute timeout, with no new blocks.
49
+ - wait-guard reads python `time.sleep` and node `sleep`/`setTimeout` waits inside heredocs in their own units, ignores them inside strings and comments, and no longer multiplies a script's waits by an earlier shell loop.
50
+ - `pkill -f "ssh -N -L 9333"` and other specific `-f` patterns pass; `pkill -f server` and `pkill -f "next dev"` still block.
51
+ - Claude Code sessions started by the macOS desktop app were not seen as sessions.
52
+ - `bulkhead doctor` reported the plugin as installed when run through `npx` from an unrelated copy.
53
+
54
+ ## 0.2.2 - 2026-11-11
55
+
56
+ ### Fixed
57
+
58
+ - `bulkhead scan` no longer crashes with `RangeError: Maximum call stack size exceeded` on a long history (hundreds of thousands of commands). The span is found with a loop, and a list append in the event log reader no longer spreads a whole file into one call.
59
+ - `bulkhead scan 30d` now means `--since 30d`. It used to ignore the argument and scan everything. Every subcommand now rejects an option or argument it does not know, with its usage text, instead of ignoring it.
60
+ - The status line counted session files whose pid was alive, so a reused pid counted as a session. It now counts Claude Code processes the way `bulkhead status` does, from one `ps` listing (Windows keeps the pid check).
61
+ - `git pull` in a tree another session works in gets its own advice (wait for that session to commit or stash, or pull from a clone nobody uses) instead of the worktree advice.
62
+
63
+ ### Added
64
+
65
+ - `bulkhead scan` reads about twice as fast and prints a progress line on stderr when it runs longer than 3 seconds in a terminal.
66
+ - A block message from kill-guard, wait-guard, disk-guard, checkout-guard and basics ends with one line telling the agent to ask the user when the command was meant, and that the user can allow it in `.claude/bulkhead.json`.
67
+ - checkout-guard `remove-session-dir` resolves `$PWD`, `${PWD}`, `$(pwd)` and `` `pwd` `` to the current folder, so `rm -rf "$(pwd)"` is caught like `rm -rf .`.
68
+ - port-guard checks a `PORT=3000` set in the command for `npm start`, `npm run dev` and similar scripts, and for `node server.js`, `tsx`, `nodemon`, `bun` and `deno` running a file.
69
+ - kill-guard also blocks killing by name Electron, Simulator, Xcode, postgres, Docker Desktop, Code Helper and Cursor.
70
+ - The package can be run without the plugin: `npx claude-bulkhead status`, `top`, `scan`, `doctor`. It installs a `claude-bulkhead` command next to `bulkhead`. With `npm install -g claude-bulkhead`, the status line command no longer contains the plugin version.
71
+ - The README opens with a real Claude Code recording of kill-guard blocking `pkill node` (`docs/demo.gif`, re-recorded with `docs/demo.tape`), and the install steps come first.
72
+
73
+ ## 0.2.1 - 2026-11-11
74
+
75
+ - `/bulkhead:remote` now shows what a run will upload (the current commit and every uncommitted change, deletions included) and the remote it goes to, and waits for your yes before anything is sent.
76
+ - The README says that a project's `.claude/bulkhead.json` can relax the guards for that repository, so read it in a repository you clone, and that `bulkhead config` shows where each setting came from.
77
+
78
+ ## 0.2.0 - 2026-11-08
79
+
80
+ ### New
81
+
82
+ - **`bulkhead scan`** replays the shell commands in your Claude Code transcripts (`~/.claude/projects`, subagents included) against the guards and prints what they would have stopped: hits per guard and rule, in how many sessions, how many a hook or you already stopped, the newest examples with secrets masked, and the most sessions that ran at once. checkout-guard is rebuilt from which sessions were active in the same git tree at that minute. Read-only, about 200 MB of transcripts a second. `--since`, `--project`, `--path`, `-n`, `--json`, and `/bulkhead:scan`.
83
+ - **disk-guard** blocks scans that walk the whole drive or home folder (`du -sh ~`, `du -sh ~/*`, `ncdu /`, `Get-ChildItem C:\ -Recurse`, `dir /s C:\`), `find /` and `find ~` without `-maxdepth`, and disk scans run in parallel (`xargs -P`, GNU `parallel`, `&` in a loop, `ForEach-Object -Parallel`). The disk is shared by every session, and these stall all of them for minutes.
84
+ - **checkout-guard `stage-all`** blocks `git add -A`, `git add .`, `git add -u` and `git commit -a` in a tree with changes when another live session works in it: the other session's half-finished edits would go into your commit. Named files pass.
85
+ - **checkout-guard `remove-session-dir`** blocks `rm -rf`, `Remove-Item -Recurse -Force`, `rd /s` and `git worktree remove` of the folder another live session works in, or a folder above it.
86
+ - **checkout-guard `worktree-force-links`** (Windows) blocks `git worktree remove --force` on a worktree with junctions or symlinks in its first two levels, which git has followed into the main checkout's `node_modules`.
87
+ - **basics `agent-author`** blocks commits authored as Claude or Anthropic: `--author`, `git config user.name/user.email`, `git -c user.name=`, `GIT_AUTHOR_*` and `GIT_COMMITTER_*`. A `Co-Authored-By` trailer passes.
88
+ - **basics `rm-root`** also covers a folder directly in the home directory (`~/Documents`, `~/Desktop`; caches such as `~/.cache`, `~/.npm` and `~/tmp` pass) and top-level folders of the drive (`/srv`, `/usr`).
89
+ - New config keys `diskGuard.enabled`, `checkoutGuard.stageAll`, `checkoutGuard.removeSessionDir` and `basics.agentAuthor`, and `BULKHEAD_DISABLE` names `disk-guard`, `stage-all`, `remove-session-dir` and `agent-author`. `bulkhead doctor` lists rules turned off in any guard.
90
+ - The skill covers committing by file name, not deleting another session's folder, the git identity, and the disk.
91
+
92
+ ### Changed
93
+
94
+ - The repository moved to `dooly6276/claude-bulkhead`; the old address redirects. Install with `/plugin marketplace add dooly6276/claude-bulkhead`.
95
+ - New README hero and `bulkhead scan` animations, light and dark.
96
+
97
+ ## 0.1.1 - 2026-10-13
98
+
99
+ - `/bulkhead:remote` printed nothing when no remote was configured, and `/bulkhead:doctor` printed nothing when a check failed: Claude Code drops a slash command whose shell step exits non-zero. Both now show the output.
100
+ - Each slash command pre-approves only the one `bulkhead` command it runs instead of any `node` command.
101
+ - The repository no longer ships `package-lock.json`. Claude Code installs a plugin's packages when it finds a lockfile in the plugin root, so the lint tools could be installed with the plugin. The runtime still has no dependencies.
102
+ - `PRIVACY.md` lists what bulkhead reads, stores and sends, and `plugin.json` carries the listing links.
103
+
104
+ ## 0.1.0 - 2026-10-13
105
+
106
+ First public release.
107
+
108
+ ### Guards
109
+
110
+ - **kill-guard** blocks kills by program name that would hit other sessions' processes: `pkill`, `killall`, `taskkill /IM` and `/FI`, `Stop-Process -Name`, `Get-Process ... | Stop-Process`, `wmic ... delete`, name lookups fed to `kill` through `$(...)`, `xargs`, loops or variables, wildcards and several names at once. Kills scoped to a PID, a command line, a path or a script pass.
111
+ - **wait-guard** blocks foreground waits that can hold a session longer than 180 seconds: sleep loops, inline `python -c` or `node -e` sleeps, `timeout /t`, `ping -n`, `[Threading.Thread]::Sleep`, and following a log with `tail -f`, `docker logs -f`, `journalctl -f` or `Get-Content -Wait`. Sleeps in a background job pass.
112
+ - **memory-guard** blocks heavy commands (test suites, builds, browser automation, container builds) when free RAM is below 3 GB or 15% of RAM, and points to `bulkhead-slot` or, when one is configured, `bulkhead remote`.
113
+ - **port-guard** blocks starting a dev server on a port another live session is listening on and suggests a free one. It also blocks kills by port aimed at another session's server (`npx kill-port`, `fuser -k`, `lsof -t -i:N | xargs kill`, `Get-NetTCPConnection` with `Stop-Process`, `netstat` with `taskkill`).
114
+ - **checkout-guard** blocks `git checkout`, `switch`, `stash`, `reset --hard`, `clean -f`, `rebase`, `merge` and a dirty `pull` when another live session works in the same working tree, and suggests `git worktree add`.
115
+ - **basics** blocks `rm -rf` at roots, the home directory and folders above the repository, force-pushes to protected branches, and `git reset --hard` or `git clean -fd` that would lose uncommitted work. Each rule can be turned off.
116
+ - **orphan-reaper** stops what a finished session left running: dev servers, test runners and their workers (Vitest, Jest, Mocha, AVA, `node --test`, Playwright, pytest and pytest-xdist, `cargo test`, `go test`, `dotnet test`), watchers, builders and automation browsers. It runs within seconds of a session exiting (a detached waiter started by the `SessionEnd` hook) and again at session start. Only processes started from that session's Claude tool shells are touched; editors, terminals, shared build daemons and machine services are never stopped, nor anything under them. Windows, macOS and Linux.
117
+
118
+ All guards read commands through one shared parser for Bash, PowerShell and cmd that opens wrappers (`bash -c`, `powershell -Command`, `-EncodedCommand`, `cmd /c`, `eval`, `sudo`, `env`, `timeout`, `xargs` and more) up to three levels deep. Commit, tag, pull request and release text never triggers a guard. Commands sent to another machine or container are left to that machine.
119
+
120
+ ### Commands
121
+
122
+ - `bulkhead status` and `bulkhead top`: sessions with their project, memory, child processes and ports, slot jobs and the queue, free RAM, and the latest blocks. `top` is a live full-screen view.
123
+ - `bulkhead-slot run --gb=N -- <command>` (also `bulkhead slot`): queues heavy commands against a RAM budget shared by every session on the machine.
124
+ - `bulkhead remote` (optional): runs a heavy command on another machine over SSH. It sends the commit as an incremental git bundle plus uncommitted changes, runs it in one of up to 8 copies of the repository with its own port in `BULKHEAD_PORT`, returns the exit code, and stops everything the command started when it ends or the connection drops. `--tail=N` keeps the full output in a file and prints only the end. Also `fetch`, `status`, `list`, `add` and `remove`. The remote needs only an SSH server, bash, git and tar. See `docs/remote.md`.
125
+ - `bulkhead reap [--dry-run]`: runs orphan-reaper by hand; the dry run lists what it would stop and why.
126
+ - `bulkhead log`: what the guards blocked and what orphan-reaper stopped, with `--since`, `--guard`, `--summary` and `--json`.
127
+ - `bulkhead doctor`: checks the install, the settings, each guard, the process reader, RAM limits, slots, the event log, git and configured remotes.
128
+ - `bulkhead config`: every setting, its effective value and where it came from.
129
+ - `bulkhead port [--near N]`: a port nothing listens on.
130
+ - `bulkhead-statusline`: live sessions, held slots and free RAM for the Claude Code status line.
131
+ - Slash commands `/bulkhead:status`, `/bulkhead:log`, `/bulkhead:doctor`, `/bulkhead:remote` and `/bulkhead:reap`, and a `bulkhead` skill that tells Claude how to work on a machine shared with other sessions.
132
+
133
+ ### Configuration
134
+
135
+ - `~/.claude/bulkhead/config.json` and a per-project `.claude/bulkhead.json`, merged in that order, with environment variables winning. Every guard can be turned off, and the common false positives have their own keys. A broken file is ignored and reported once at session start. Remotes can be set in the global file only. See `config.example.json` and `docs/configuration.md`.
136
+ - Every block and every stopped process is appended to `events.jsonl` in the state directory; `BULKHEAD_NO_LOG=1` turns it off.
137
+
138
+ ### Quality
139
+
140
+ - More than 2,400 tests on Linux, macOS and Windows with Node 18, 22 and 24, including a corpus of more than 900 real command lines run through the actual hooks, a fuzz test that checks every guard stays linear on 1 MB inputs, and an end-to-end `bulkhead remote` run against a real SSH server.
141
+ - No dependencies. Each guard adds about 40 to 50 ms to a tool call.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Myeongseok-Kang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.