--- type: Changed pr: 4236 --- Secret-file read protection moved from installer-written permission deny rules to a managed hook. The Claude Code installer no longer writes `Read(.env)` / `Read(.env.*)` / `Read(.secrets)` into `permissions.deny`, and removes those three strings (byte-equal only) from existing installs on install and uninstall — on Claude Code >= 2.1.259 any `Read()` deny rule made every `cd DIR && grep …` compound prompt for approval, even in `auto` mode. The same protection now ships as the always-on `gsd-secret-read-guard.js` PreToolUse hook (matcher `Read|Grep|Bash`; Kimi `ReadFile|Grep|Shell`; OpenCode/Kilo plugin dispatch), which denies reads of `.env`, `.env.` and `.secrets` — matched case-insensitively — via Read, Grep (explicit path or a selecting glob, judged per brace alternative) and Bash (operands, input redirects, `$( )`/backtick/`<( )` bodies, `git show :`). A shell interpreter (`bash`/`sh`/`zsh`/`dash`/`ksh`) has its script scanned however it arrives — `-c '…'`, a `<( )` file operand, a heredoc / here-string, or a pipe from a knowable `echo`/`printf` source — plus `eval`'s joined operands, a `source`/`.` process-substitution operand, and `find … | xargs cat` pipelines (upstream literal names become the sub-command's read operands). `.env.example` / `.env.sample` / `.env.template` / `.env.dist` stay readable, and existence checks (`[ -f .env ]`, `ls .env*`) pass. Documented gaps: `$VAR` indirection, shell globs, interpreter one-liners, a piped script from a non-`echo`/`printf` source (`cat gen.sh | bash`, `curl … | sh`), reads inside executed scripts, and a Grep `glob: '*'` reaching a non-gitignored `.env`. Breaking: a hand-written deny rule identical to one of the three strings is removed too; re-add it if you want both layers. Cursor, Windsurf, Cline, Copilot, Codex and ZCode have no per-tool hook matcher and are not covered (they never had the deny rules either).