Summary
The vendored PreToolUse hooks match paths with POSIX-only regexes, so on Windows the .env protection and the database module's migration protection never fire. Claude Code on Windows hands the hook tool_input.file_path with backslashes, the regex does not match, the hook exits 0 and the write goes through.
Where
packages/cli/templates/claude/hooks/protect-files.mjs:29: /(^|\/)\.env(\.[\w.]+)?$/.test(filePath)
packages/cli/modules/database/hooks/protect-migrations.fragment.mjs:27: /(^|\/)(supabase\/|db\/|prisma\/)?migrations\/[^/]+\.(sql|js|ts|rb|py)$/
- same pattern in
.github/facility/doctor/resolve.mjs SENSITIVE_PATHS if that ever runs on a Windows runner.
Repro (Windows 11, Node 24, facility main)
printf '{"tool_name":"Write","tool_input":{"file_path":"/repo/.env"}}' | node protect-files.mjs ; echo $? # 2 (blocked)
printf '{"tool_name":"Write","tool_input":{"file_path":"C:\repo\.env"}}' | node protect-files.mjs ; echo $? # 0 (allowed)
Same with C:\repo\supabase\migrations\0001_init.sql → allowed; the forward-slash form → blocked.
Why it matters
The hooks are the local half of the "protect files / protect branch" story that init prints and STANDARD.md relies on; on Windows they are installed, listed as ✓ by doctor, and inert. Nothing tells the user. Runners in CI are Linux so this only bites developers running the agents locally, but that is exactly where .env files with real secrets live.
Suggested fix
Normalise once at the top of each hook: const filePath = String(raw).replace(/\/g, "/"), and add a hook test with a backslash path. Related in spirit to #173 (Windows paths in guards), different files.
Summary
The vendored PreToolUse hooks match paths with POSIX-only regexes, so on Windows the
.envprotection and the database module's migration protection never fire. Claude Code on Windows hands the hooktool_input.file_pathwith backslashes, the regex does not match, the hook exits 0 and the write goes through.Where
packages/cli/templates/claude/hooks/protect-files.mjs:29:/(^|\/)\.env(\.[\w.]+)?$/.test(filePath)packages/cli/modules/database/hooks/protect-migrations.fragment.mjs:27:/(^|\/)(supabase\/|db\/|prisma\/)?migrations\/[^/]+\.(sql|js|ts|rb|py)$/.github/facility/doctor/resolve.mjsSENSITIVE_PATHSif that ever runs on a Windows runner.Repro (Windows 11, Node 24, facility
main)Same with
C:\repo\supabase\migrations\0001_init.sql→ allowed; the forward-slash form → blocked.Why it matters
The hooks are the local half of the "protect files / protect branch" story that
initprints andSTANDARD.mdrelies on; on Windows they are installed, listed as ✓ bydoctor, and inert. Nothing tells the user. Runners in CI are Linux so this only bites developers running the agents locally, but that is exactly where.envfiles with real secrets live.Suggested fix
Normalise once at the top of each hook:
const filePath = String(raw).replace(/\/g, "/"), and add a hook test with a backslash path. Related in spirit to #173 (Windows paths in guards), different files.