agentleFS
sergeeeysergeeey/Claude-cod-top-2026/hooks/CLAUDE.md

Claude-cod-top-2026 / hooks

CLAUDE.md9 starsChanged 6 months agoMITView on GitHub

hooks/utils.py still exists as a backward-compatible facade re-exporting every symbol, so old from utils import X call sites keep working — but it is a facade, not the source. New code should import directly from hooks/lib/{runtime,state, discovery,security}.py, not from utils. Never duplicate these symbols elsewhere. Missing this = infinite loop when Claude Code invokes subagents. Claude Code SDK uses a different signal per hook type (full detail: hooks/lib/runtime.py's module docstring): - PreToolUse — call emit_permission_decision() from hooks/lib/runtime.py (JSON hookSpecificOutput to…

The file

We split this CLAUDE.md into the rules it gives an agent

5 rules. About 0 are common, 4 are distinctive, and 1 is very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈4

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    If a hook runs via async_wrapper.py, stdout goes to DEVNULL → emit_hook_result silently fails.

    line 18
  2. Distinctive:

    Every hook that calls Claude or reads memory must check:

    line 22
  3. Distinctive:

    Never raise unhandled exceptions — hook dies silently

    line 55
  4. Distinctive:

    Add recursion guard if hook reads memory or calls Claude

    line 59
  5. Distinctive:

    Add entry to README hook table

    line 61

≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
CLAUDE.md

Hooks — Local Rules

Architecture

Shared code lives in hooks/lib/{runtime,state,discovery,security}.py, grouped by responsibility (hook I/O protocol, state files/locking, project discovery, sanitization/secrets) — split out 2026-08-22 from a single utils.py that had grown to 36 symbols and fan-in 74 (HS-01, artifacts/architecture-coupling/hotspots.json).

hooks/utils.py still exists as a backward-compatible facade re-exporting every symbol, so old from utils import X call sites keep working — but it is a facade, not the source. New code should import directly from hooks/lib/{runtime,state, discovery,security}.py, not from utils. Never duplicate these symbols elsewhere.

Critical Patterns

stdout is the hook protocol

emit_hook_result() writes JSON to stdout → Claude Code reads it. If a hook runs via async_wrapper.py, stdout goes to DEVNULL → emit_hook_result silently fails. Never wrap a hook in async_wrapper if it needs to inject context.

Recursion guard

Every hook that calls Claude or reads memory must check:

if os.environ.get("CLAUDE_INVOKED_BY"):
    sys.exit(0)

Missing this = infinite loop when Claude Code invokes subagents.

Blocking protocol — two different mechanisms, do not mix

Claude Code SDK uses a different signal per hook type (full detail: hooks/lib/runtime.py's module docstring):

  • PreToolUse — call emit_permission_decision() from hooks/lib/runtime.py (JSON hookSpecificOutput to stdout). A bare sys.exit(N) does NOT block a PreToolUse call the way this repo does it — only exit code 2 blocks per Claude Code's own protocol, and this repo standardizes on the JSON path instead. Correct examples: pre_commit_guard.py, security_verify.py, input_guard.py, scripts/redact.py.
  • PostToolUse — the tool has already run; no exit code blocks it. sys.exit(2) is the strongest available signal (surfaces stderr to Claude prominently) — current convention, see skeptic_auto_trigger.py, goal_stub_detector.py, validation_theater_guard.py (migrated 2026-08-29). sys.exit(1) is a legacy pattern with no special surfacing semantics — avoid it for new PostToolUse hooks.

Exit codes

  • sys.exit(0) — success or skip (no action needed)
  • sys.exit(2) — PreToolUse: blocks, per Claude Code protocol (this repo prefers emit_permission_decision() instead of a bare exit code). PostToolUse: strongest available signal shown to Claude — NOT a true block, the tool already ran.
  • sys.exit(1) — non-blocking everywhere. Legacy PostToolUse pattern with no special surfacing semantics, being replaced by sys.exit(2).
  • Never raise unhandled exceptions — hook dies silently

Adding a New Hook

  1. Import from lib.runtime import emit_hook_result, hook_main, parse_stdin
  2. Add recursion guard if hook reads memory or calls Claude
  3. Register in settings.json with __PYTHON_CMD__ __CLAUDE_HOME__/hooks/<name>.py
  4. Add entry to README hook table

Known Anti-Patterns

  • async_wrapper + emit_hook_result = silent failure (learned 2026-04-19)
  • Missing CLAUDE_INVOKED_BY guard = Agent SDK infinite loop
  • datetime.utcnow() mixed with timezone-aware datetimes = TypeError at runtime

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

Reviews can't be read right now.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.