* fix(hooks): detect Claude Code via stdin session_id, not filtered env (#2520)
The #2344 fix assumed `CLAUDECODE` would propagate to hook subprocesses.
On Claude Code v2.1.116 it doesn't — Claude Code applies a separate env
filter to PreToolUse hook commands that drops bare CLAUDECODE and
CLAUDE_SESSION_ID, keeping only CLAUDE_CODE_*-prefixed vars plus
CLAUDE_PROJECT_DIR. As a result every Edit/Write on an existing file
produced a redundant READ-BEFORE-EDIT advisory inside Claude Code.
Use `data.session_id` from the hook's stdin JSON as the primary Claude
Code signal (it's part of Claude Code's documented PreToolUse hook-input
schema). Keep CLAUDE_CODE_ENTRYPOINT / CLAUDE_CODE_SSE_PORT env checks
as propagation-verified fallbacks, and keep the legacy
CLAUDE_SESSION_ID / CLAUDECODE checks for back-compat and
future-proofing.
Add tests/bug-2520-read-guard-hook-subprocess-env.test.cjs, which spawns
the hook with an env mirroring the actual Claude Code hook-subprocess
filter. Extend the legacy test harnesses to also strip the
propagation-verified CLAUDE_CODE_* vars so positive-path tests keep
passing when the suite itself runs inside a Claude Code session (same
class of leak as #2370 / PR #2375, now covering the new detection
signals).
Non-Claude-host behavior (OpenCode / MiniMax) is unchanged: with no
`session_id` on stdin and no CLAUDE_CODE_* env var, the advisory still
fires.
Closes#2520
* test(2520): isolate session_id signal from env fallbacks in regression test
Per reviewer feedback (Copilot + CodeRabbit on #2521): the session_id
isolation test used the helper's default CLAUDE_CODE_ENTRYPOINT /
CLAUDE_CODE_SSE_PORT values, so the env fallback would rescue the skip
even if the primary `data.session_id` check regressed. Pass an explicit
env override that clears those fallbacks, so only the stdin `session_id`
signal can trigger the skip.
Other cases (env-only fallback, negative / non-Claude host) already
override env appropriately.
---------
Co-authored-by: forfrossen <forfrossensvart@gmail.com>
* fix(hooks): skip read-guard advisory on Claude Code runtime (#1984)
Claude Code natively enforces read-before-edit at the runtime level,
so the gsd-read-guard.js advisory is redundant — it wastes ~80 tokens
per Write/Edit call and clutters tool flow with system-reminder noise.
Add early exit when CLAUDE_SESSION_ID is set (standard Claude Code
session env var). Non-Claude runtimes (OpenCode, Gemini, etc.) that
lack native read-before-edit enforcement continue to receive the
advisory as before.
Closes#1984
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(hooks): sanitize runHook env to prevent test failures in Claude Code
The runHook() test helper now blanks CLAUDE_SESSION_ID so positive-path
tests pass even when the test suite runs inside a Claude Code session.
The new skip test passes the env var explicitly via envOverrides.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix(hooks): add read-before-edit guidance for non-Claude runtimes
When models that don't natively enforce read-before-edit hit the guard,
the error message now includes explicit instruction to Read first.
This prevents infinite retry loops that burn through usage.
Closes#1628
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(build): register gsd-read-guard.js in HOOKS_TO_COPY and harden tests
The hook was missing from scripts/build-hooks.js, so global installs
would never receive the hook file in hooks/dist/. Also adds tests for
build registration, install uninstall list, and non-string file_path.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>