docs: describe the :; label-line wrapper mechanism (#571)

This commit is contained in:
Drew Ritter
2026-08-05 15:16:30 -07:00
parent d80fc1808a
commit f2bbe9ff92
2 changed files with 15 additions and 9 deletions

View File

@@ -745,8 +745,10 @@ single file that's valid as both a Windows batch script and a Unix shell script.
On Windows, `cmd.exe` runs the batch portion, which locates `bash` (Git for On Windows, `cmd.exe` runs the batch portion, which locates `bash` (Git for
Windows, then `bash` on PATH) and runs the named hook script; if no bash is Windows, then `bash` on PATH) and runs the named hook script; if no bash is
found it exits cleanly so the harness still works, just without injection. On found it exits cleanly so the harness still works, just without injection. On
Unix, the leading `:` makes the batch block a no-op and the shell runs the Unix, the shell executes the leading `:;` lines and `exec`s the hook script
script directly. before reaching the batch block (cmd.exe skips those lines as labels).
Heredocs are banned throughout hooks/ — see issue #571 and the fence test
`tests/hooks/test-no-heredocs-in-hooks.sh`.
Two rules this enforces, which you must respect: Two rules this enforces, which you must respect:

View File

@@ -65,9 +65,9 @@ The path is quoted because `${CLAUDE_PLUGIN_ROOT}` may contain spaces.
## How `run-hook.cmd` Works at a High Level ## How `run-hook.cmd` Works at a High Level
`run-hook.cmd` is a polyglot script: Windows treats the first block as batch `run-hook.cmd` is a polyglot script: its first lines start with `:;`, which
commands, while Unix shells treat that block as a no-op heredoc and continue cmd.exe skips as labels and Unix shells execute — the shell execs the hook
after it. before ever reaching the batch block that Windows runs.
Do not copy an implementation from this document. Read `hooks/run-hook.cmd` Do not copy an implementation from this document. Read `hooks/run-hook.cmd`
directly when changing the dispatcher, and run `tests/hooks/test-session-start.sh` directly when changing the dispatcher, and run `tests/hooks/test-session-start.sh`
@@ -89,10 +89,14 @@ afterward.
### How it works on Unix (bash/sh) ### How it works on Unix (bash/sh)
1. `: << 'CMDBLOCK'` opens a heredoc on a no-op command. 1. Each leading `:;` line is a no-op label to cmd.exe but real commands to a
2. The entire CMD batch block is consumed by the heredoc and ignored. POSIX shell.
3. After `CMDBLOCK`, bash resolves the script directory and `exec`s the named 2. The shell checks bash exists (silent exit 0 if not), resolves the script
extensionless script directly. directory CDPATH-proof, and `exec`s the named extensionless script — it
never reads the batch block at all.
3. Heredocs are banned in this file and all hooks/ executables (issue #571:
bash >= 5.1 pre-fork pipe writes deadlock on macOS under pipe pressure);
`tests/hooks/test-no-heredocs-in-hooks.sh` enforces the ban.
### Key design decisions ### Key design decisions