AO

2026-10-08 · claude-code · hooks · windows · git-bash

On Windows, each program a Claude Code hook starts costs 40 to 200 ms

On Windows, Claude Code runs hooks in Git Bash, where each program a hook starts cost AO 43 to 203 ms, against 1.4 ms on Linux on the same PC. Bash builtins took AO's edit hook from 1.7-9.7 s to a median of 0.09-0.17 s. Measured by AO, 2026-10-08.

A hook on Edit and Write runs before every edit Claude makes, and Claude waits for it. On Windows that hook runs in Git Bash, and in Git Bash starting a program is slow: on AO's PC each one cost 43 to 203 ms, depending on how busy the PC was. The same program on Linux, on the same PC, cost 1.4 ms. Twenty small programs in one bash took 0.9 to 6.6 s on Windows and 28 to 38 ms on Linux, so a hook that feels free when you write it on a Mac or Linux can make every edit slow on Windows.

What it did to AO's own hook. AO's AIsle plugin has a hook on every edit, in every project on the computer, though it only has work to do in folders that joined a room. Version 0.3.4 found its version, its folder and a hash of the path first, with about twenty programs (grep, cut, tr, sed, sha256sum) before it checked whether the folder had joined at all. Measured on 2026-10-08, the way Claude Code starts it:

  • 0.3.4 on Windows: median 3.0 to 5.3 s per edit, up to 9.7 s, at 63 to 87% CPU.
  • The current version on Windows: median 0.09 to 0.17 s.
  • 0.3.4 on Linux in WSL: 60 to 80 ms. The current version: 11 to 21 ms.

The plugin gives the hook a 5 s limit, so on a busy Windows PC the old version could run out of time on an ordinary edit. On Linux nobody would have noticed.

The fix is bash builtins on the common path. The current version reads the hook's JSON with read, finds the fields with [[ =~ ]] and BASH_REMATCH, reads its list of folders with read -d '', and leaves at once when the folder is not on it. Only then does it start any program. These swaps were measured in Git Bash on the same PC:

x=$(date +%s)          277 ms      x=$EPOCHSECONDS       0.8 ms
x=$(basename "$p")     264 ms      x=${p##*/}            1.3 ms
x=$(cat "$f")           65 ms      x=$(<"$f")            1.0 ms
x=$(printf '%s-%s' a b) 34 ms     printf -v x '%s-%s' a b  0.7 ms

A $(...) costs time even with only a builtin inside, because bash starts a copy of itself to run it: 26 to 81 ms in Git Bash, 1 ms on Linux. $(<file) was the exception in bash 5.3: 1 ms.

Shell form starts bash twice. A hook written as "command": "bash script.sh" goes to Git Bash, which then starts a second bash for the script. With "args" set, Claude Code starts the program directly. In our runs that saved a median of about 70 ms per call on Windows. One caution we found: on this PC, Windows' own search of the PATH finds WSL's bash.exe in C:\Windows\System32 before Git's. We did not test which one exec form starts, so give the full path to Git's bash.exe if you use it.

Node.js is not a way out. An empty node -e "" took a median of 77 ms in the first run and 392 ms when the PC was busy, against 53 ms for bash in both.

How to measure your own machine (in Git Bash, then in WSL or on Linux for comparison):

n=50; t0=$EPOCHREALTIME; for ((i=0; i<n; i++)); do /usr/bin/true; done; echo "$(( (${EPOCHREALTIME/./} - ${t0/./}) / n / 1000 )) ms per program"

Not measured. Other Windows PCs and Git for Windows versions, a PC with no other load, and the effect of antivirus scanning on each program start. The busy runs shared the PC with other work, so the higher numbers say how bad it gets, not what it always is.

The numbers

  • Claude Code passes a hook's command string to Git Bash on Windows, or to PowerShell when Git Bash is not installed; with "args" set it starts the program directly, with no shell (Hooks reference, read 2026-10-08)
  • A PreToolUse hook runs before the tool call and can block it, so a hook on Edit and Write adds its time to every edit (Hooks reference, read 2026-10-08)
  • Git Bash on Windows 11 (Git 2.54.0, MSYS 3.6.7, bash 5.3.9, 12 logical CPUs): one external program cost 43 to 51 ms per call in the first runs, and 124 to 203 ms in later runs at 43 to 87% CPU load (measured by AO, 2026-10-08)
  • Same PC: a $(...) with only a builtin inside cost 26 ms, later 59 to 81 ms; a builtin 0.3 to 1.1 ms; starting bash 62 to 178 ms; git --version 69 to 231 ms (measured by AO, 2026-10-08)
  • Same PC, Linux in WSL 2 (Linux 6.6, the same bash 5.3.9): one program 1.4 ms, a $(...) 1.0 ms, starting bash 7.8 ms, git --version 2.0 ms (measured by AO, 2026-10-08)
  • Twenty runs of /usr/bin/true in one bash started from Node.js: median 1.2 s (0.9 to 1.4 s) in the first run and 1.8 s (1.3 to 6.6 s) at 77% CPU; on Linux in WSL 30 ms (28 to 38 ms) (measured by AO, 2026-10-08)
  • Swaps in Git Bash, same PC: $(date +%s) 277 ms against $EPOCHSECONDS 0.8 ms; $(basename "$p") 264 ms against ${p##*/} 1.3 ms; $(cat "$f") 65 ms against $(<"$f") 1.0 ms (measured by AO, 2026-10-08)
  • AO's edit hook as plugin 0.3.4 shipped it, which started about twenty programs before checking anything, run the way Claude Code runs it, in a folder that never joined a room: median 3.0 to 5.3 s, range 1.7 to 9.7 s per edit, at 63 to 87% CPU (measured by AO, 2026-10-08); it was 0.95 s on 2026-10-02 (AO's own log)
  • The plugin gives that hook a 5 s limit; at that load one of 0.3.4's four medians, 5.3 s, was past it, and its slowest runs took 6.6 to 9.7 s (measured by AO, 2026-10-08)
  • The same hook rewritten to use only bash builtins until it knows the folder joined a room: median 160 to 173 ms in shell form and 93 to 94 ms in exec form, range 72 to 546 ms, in the same runs (measured by AO, 2026-10-08)
  • On Linux in WSL the old hook took a median of 60 to 80 ms and the new one 11 to 21 ms (measured by AO, 2026-10-08)
  • Shell form starts bash twice for a command like bash "script.sh"; exec form saved a median of about 70 ms per call on Windows and about 9 ms on Linux (measured by AO, 2026-10-08)
  • Starting Node.js with an empty script (node -e "") took a median of 77 ms in the first run and 392 ms at 70 to 77% CPU, so a hook in Node.js is not cheaper to start than bash (measured by AO, 2026-10-08)
  • On this PC, where.exe bash finds C:\Windows\System32\bash.exe, which is WSL's, before Git's bash; which one Claude Code's exec form starts was not tested (measured by AO, 2026-10-08)
stored on your device — like everything here

← All knowledge · machine version (.md)