[BUG] Hooks in a mid-session-created `.claude/settings.json` never arm until restart (settings watcher scans once at startup, skips a not-yet-existent `.claude/`)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
If a Claude Code session starts in a directory that has no .claude/ directory, then creating .claude/settings.json (with hooks) later in that same session never arms those hooks — they don't take effect until the session is restarted. This is surprising because in-session edits to an already-existing settings file normally do hot-reload.
The twist: Claude Code itself creates the project .claude/ directory mid-session (as part of its .cc-writes atomic-write staging), so a directory that started without .claude/ ends up with one — but the watcher still never picks it up.
The tell: pre-create an empty .claude/ directory before starting the session, and the hooks fire immediately with no restart. So the trigger is specifically ".claude/ absent at session start."
What Should Happen?
Hooks written into .claude/settings.json during a session should take effect in that session — as in-session edits to an already-existing settings file already do — especially since Claude Code creates the project .claude/ directory itself mid-session. At minimum, a project .claude/ that Claude Code creates during the session should become watched.
Error Messages/Logs
No error is produced — the failure is silent. The hook simply never fires and nothing is logged.
Steps to Reproduce
mkdir /tmp/repro && cd /tmp/repro— ensure no.claude/exists.- Start
claude. - In-session, create
.claude/settings.json:
``json``
{"hooks":{"PreToolUse":[{"matcher":"Bash","hooks":[{"type":"command","command":"echo FIRED >> /tmp/repro/HOOKFIRED.log"}]}]}}
- Trigger a
Bashtool call. - Observed:
/tmp/repro/HOOKFIRED.logis never created — the hook never fires. /exit, thenclaude --continue, and trigger aBashcall → now it fires (restart-only).
Contrast (non-triggering): pre-create an empty .claude/ before step 2 → the hook fires at step 4 with no restart.
Environment: Claude Code v2.1.208, macOS (arm64). Reproduces in both interactive and -p (headless) sessions.
Likely Root Cause
The settings-file watcher appears to scan the filesystem once at startup and never re-scan. Its per-settings-directory gate skips a directory that doesn't exist at that instant — an empty-but-present .claude/ passes and is watched, but a missing one is excluded, and nothing watches the project root for a .claude/ being created later. Meanwhile Claude Code lazily creates the project .claude/ itself (as part of its .cc-writes atomic-write staging) after that one-time scan — so the just-created .claude/, and any settings.json/hooks inside it, are never watched.
Suggested Fix
When a settings directory is absent at scan time, watch its parent directory for the directory being created (then promote to a full settings-dir watch); or re-run the watch-target scan when the atomic-write staging path first creates a project .claude/.