Skip to content

Latest commit

 

History

History
1693 lines (1255 loc) · 67.8 KB

File metadata and controls

1693 lines (1255 loc) · 67.8 KB

Claude Code Hook Configuration Guide

This document describes the Hook settings included in claude-config.

Authoritative inventory: the Auto-Generated Hook Catalog at the bottom of this document is the single source of truth for the list of hooks shipped under global/hooks/. It is regenerated from the leading comment block of each hook script by scripts/gen-hooks-md.sh and verified in CI by .github/workflows/validate-hooks-doc.yml. The narrative sections that follow provide additional context for the most-used hooks but defer to that catalog for the canonical list.

Overview

Hooks are user-defined commands that automatically execute during specific Claude Code events.

Quick Navigation

I want to... See
Protect sensitive files from being read Sensitive File Protection
Block dangerous shell commands Dangerous Command Blocking
Validate markdown links before commit Markdown Anchor Validation
Auto-format code after edits Auto Formatting
Limit concurrent Agent Teams Team Limit Guard
Log session activity Session Logging
Check for known Claude Code bugs Version Check
Validate commit messages before git commit Commit Message Guard
Prevent git merge/rebase on dirty trees Conflict Guard
Block PRs targeting main from non-develop branches PR Target Guard
Block non-English titles/bodies in gh PR/issue commands PR Language Guard
Block gh pr merge when any check is non-passing Merge Gate Guard
Block AI/Claude attribution in gh PR/issue commands Attribution Guard
Re-inject critical policy after instruction load Instructions Loaded Reinforcer
Restore core principles after context compaction Post-Compact Restore
Validate task descriptions at creation time Task Created Validator
Auto-commit working tree after Task/Agent runs Post Task/Agent Checkpoint
Block Edit/Write on files that weren't Read first Pre-edit Read Guard
Block direct pushes to protected branches Pre-push Protected Branch Guard
Add my own custom hook Adding New Hooks
Set up hooks on Windows Windows Support

Configuration File Locations

File Purpose Scope
global/settings.json Global Hook settings All projects
project/.claude/settings.json Project Hook settings Current project only

Global Hooks (global/settings.json)

1. Sensitive File Protection (PreToolUse)

Prevents accidental exposure of secrets — Claude will never read your .env or credentials, even if asked directly.

Purpose: Block access to sensitive files like .env, .pem, .key

Blocked targets:

  • Extensions: .env, .pem, .key, .p12, .pfx
  • Directories: secrets/, credentials/, passwords/, private/

Behavior:

  • Returns JSON with permissionDecision: "deny" and exits with code 0
  • Claude Code reads the deny reason from the JSON response

2. Dangerous Command Blocking (PreToolUse)

Stops catastrophic mistakes before they happen — no accidental root deletion or unsafe permission changes.

Purpose: Block commands that could have catastrophic system impact

Blocked targets:

  • rm -rf / (root deletion)
  • chmod 777 (dangerous permission change)
  • curl ... | sh (remote script execution)

3. Session Logging (SessionStart/SessionEnd)

Track when and how long Claude Code sessions run for audit and debugging purposes.

Purpose: Record Claude Code session start/end times

Log location: ~/.claude/session.log

Log format:

[Session] Claude Code session started: 2025-12-03 14:30:00
[Session] Claude Code session ended: 2025-12-03 15:45:00

4. Temporary File Cleanup (SessionEnd)

Keeps your temp directory clean without manual intervention.

Purpose: Automatically delete old temporary files on session end

Cleanup targets:

  • /tmp/claude_* (files older than 60 minutes)
  • /tmp/tmp.* (owned by current user, older than 60 minutes)

5. Markdown Anchor Validation (PreToolUse)

Catch broken documentation links before they reach your repository — validates every cross-reference on commit.

Purpose: Validate markdown cross-reference anchors before git commit to prevent broken links

Trigger: git commit commands only (all other commands pass through)

How it works:

  1. Collects staged markdown files via git diff --cached --name-only --diff-filter=d -- '*.md'
  2. Pass 1: Builds the primary anchor registry from headings in those staged files (GitHub-style slug algorithm)
  3. Pass 2: Checks all ](#anchor) (intra-file) and ](file.md#anchor) (inter-file) references against the registry
  4. Cross-file fallback: when an inter-file reference's target is not in the primary registry (i.e. the target file is on disk but not staged), the validator lazy-parses the target file and caches its anchors. The reference passes if and only if the target heading exists. See issue #646 for the rationale — single-file edit PRs that legitimately reference unstaged sibling files must not produce false positives.
  5. Blocks commit if broken anchors are found

Anchor generation algorithm (matches GitHub):

  • Strip inline formatting (bold, italic, code, links)
  • Lowercase → remove non-alphanumeric/space/hyphen (Unicode letters preserved) → spaces to hyphens
  • Duplicate headings get -1, -2 suffixes

Features:

  • Skips code blocks (``` and ~~~ delimiters)
  • Handles Korean/CJK characters in anchors
  • Validates both intra-file and inter-file references
  • Excludes external URLs (detects : in path)
  • Lazy cross-file anchor resolution with per-file caching (no double-parse)

Behavior:

  • Returns JSON with permissionDecision: "deny" listing broken anchors
  • Timeout: 30 seconds

6. Team Limit Guard (PreToolUse)

Prevent resource exhaustion by capping the number of concurrent Agent Teams across sessions.

Purpose: Enforce a maximum number of concurrent Agent Teams across sessions

Trigger: TeamCreate tool invocation

How it works:

  1. Reads MAX_TEAMS environment variable (default: 3)
  2. Counts directories in ~/.claude/teams/
  3. Blocks team creation if the count meets or exceeds the limit

Behavior:

  • Returns JSON with permissionDecision: "deny" when limit is reached
  • Timeout: 5 seconds
  • Cross-platform: team-limit-guard.sh (bash) and team-limit-guard.ps1 (PowerShell)

7. TeammateIdle (TeammateIdle)

React when teammates finish work — enforce quality gates or trigger follow-up actions.

Purpose: Fires when a teammate finishes its turn and is about to go idle. Use this to enforce quality gates or log teammate activity.

Hook input (JSON via stdin):

{
  "session_id": "abc123",
  "hook_event_name": "TeammateIdle",
  "teammate_name": "researcher",
  "team_name": "my-project",
  "cwd": "/path/to/project",
  "permission_mode": "default"
}

Decision control: Uses exit code only (not JSON permissionDecision):

Exit Code Effect
0 Allow teammate to go idle
2 Block idle — stderr message sent as feedback to teammate

8. Version Check (SessionStart)

Get warned early if your Claude Code version has known performance bugs.

Purpose: Warn when running Claude Code versions with known cache efficiency bugs

Trigger: Every session start (async, non-blocking)

How it works:

  1. Gets current Claude Code version via claude --version
  2. Compares against a hardcoded list of known problematic versions (2.1.69–2.1.81)
  3. Logs a warning to ~/.claude/session.log if a match is found

Known bugs tracked:

  • Resume cache regression (#34629)
  • Sentinel replacement (#40524)

Behavior:

  • Lifecycle event hook — no JSON output required
  • Always exits 0 (non-blocking)
  • Timeout: 10 seconds, async
  • Cross-platform: version-check.sh (bash) and version-check.ps1 (PowerShell)

9. TaskCompleted (TaskCompleted)

Enforce quality gates before accepting task completion from teammates.

Purpose: Fires when a teammate completes a task from the shared task list. Use this to enforce quality gates before accepting task completion.

Hook input (JSON via stdin):

{
  "session_id": "abc123",
  "hook_event_name": "TaskCompleted",
  "task_id": "task-456",
  "task_subject": "Implement user validation",
  "teammate_name": "backend",
  "team_name": "my-project",
  "cwd": "/path/to/project"
}

Decision control: Uses exit code only (not JSON permissionDecision):

Exit Code Effect
0 Accept task completion
2 Block completion — stderr message sent as feedback to teammate

10. Commit Message Guard (PreToolUse)

Blocks non-conventional commit messages at Claude's Bash tool boundary — deterministic, same input always yields same decision.

Purpose: Validate git commit messages against Conventional Commits rules before Claude invokes git commit.

Trigger: Bash commands matching git commit ... -m ... only.

Rules enforced:

  • Conventional Commits format: type(scope): description or type: description
  • Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, security
  • Description starts with lowercase, no trailing period
  • No AI/Claude attribution
  • No emojis

Behavior:

  • Returns JSON with permissionDecision: "deny" listing the failed rule
  • Defers to the git commit-msg hook for command-substitution messages (-m "$(...")
  • Timeout: 5 seconds
  • Cross-platform: commit-message-guard.sh and commit-message-guard.ps1

Shared validation library: Both this PreToolUse hook and the git commit-msg hook (installed by hooks/install-hooks.sh) source the same validator at hooks/lib/validate-commit-message.sh, ensuring rule consistency across enforcement layers.

11. Conflict Guard (PreToolUse)

Prevents git merge/rebase/cherry-pick/pull when the working tree is dirty or another operation is already in progress.

Purpose: Block conflict-prone git operations when conditions would cause data loss or nested conflicts.

Trigger: Bash commands matching git merge, git rebase, git cherry-pick, or git pull.

Checks performed:

  1. Existing operation: Denies if MERGE_HEAD, REBASE_HEAD, or CHERRY_PICK_HEAD exists (another operation is in progress)
  2. Uncommitted changes: Denies if git status --porcelain is non-empty (dirty working tree)

Behavior:

  • Returns JSON with permissionDecision: "deny" describing the blocking condition
  • Fail-open: if parsing fails or git is not available, the command is allowed
  • Advisory only (conflict prevention), not security-critical
  • Cross-platform: conflict-guard.sh and conflict-guard.ps1

12. PR Target Guard (PreToolUse)

Enforces branching policy: only develop or a release/* branch may merge into the repo default branch (main/master).

Purpose: Intercepts gh pr create commands and blocks those targeting the default branch (main/master) unless the source branch is develop or release/* (a legitimate release PR).

Trigger: Bash tool calls containing gh pr create

Files: global/hooks/pr-target-guard.sh, global/hooks/pr-target-guard.ps1

Logic:

  1. Scope gate: only process gh pr create commands (all others pass through)
  2. Extract --base value (--base main, --base=main, -B main)
  3. If no --base flag is present, resolve the repo default branch: use PR_TARGET_GUARD_DEFAULT_BRANCH_OVERRIDE if set (test injection), else query gh api repos/{owner}/{repo} --jq .default_branch (honoring --repo/-R when supplied). If the default branch cannot be resolved, allow (graceful degradation)
  4. If the resolved base is main or master:
    • Allow if the head branch is develop or matches release/* (legitimate release PR via /release)
    • Deny otherwise with the branching-policy guidance message
  5. Otherwise (base is neither main nor master): allow

Fail policy: Fail-closed (deny if JSON parsing fails)

Complements:

  • pre-push git hook: blocks git push origin main/develop
  • validate-pr-target.yml GitHub Actions: auto-closes non-develop PRs to main (server-side)

Configuration:

{
  "type": "command",
  "command": "~/.claude/hooks/pr-target-guard.sh",
  "timeout": 5
}

13. PR Language Guard (PreToolUse)

Hard-blocks gh pr and gh issue titles/bodies that violate the active CLAUDE_CONTENT_LANGUAGE policy — eliminates the rule drift that lets off-policy content slip through in long-running batch workflows.

Purpose: Enforces the per-policy artifact rule from commit-settings.md (default policy english; other values: korean_plus_english, exclusive_bilingual, any) at the Bash tool boundary. Mirrors the commit-message-guard enforcement model that proved effective for commit messages.

Trigger: Bash tool calls matching gh (pr|issue) (create|edit|comment).

Files: global/hooks/pr-language-guard.sh, global/hooks/pr-language-guard.ps1

Shared validation library: hooks/lib/validate-language.sh (single source of truth — same pattern as validate-commit-message.sh).

Logic:

  1. Scope gate: only process gh (pr|issue) (create|edit|comment) commands (all others pass through). Six combinations are guarded — gh pr create, gh pr edit, gh pr comment, gh issue create, gh issue edit, gh issue comment.
  2. Skip command-substitution / heredoc bodies (--body "$(...)") and --body-file references — these cannot be parsed at the shell layer, so the hook defers to other safeguards.
  3. Extract --title / -t, --body / -b, and --notes / -n values, supporting both double-quoted and single-quoted forms and --flag value / --flag=value layouts.
  4. Dispatch to validate_content_language in hooks/lib/validate-language.sh, which selects the active validator from CLAUDE_CONTENT_LANGUAGE (default english).
  5. Deny reason includes the first offending substring and references commit-settings.md so Claude can self-correct under whichever policy is active.

Allowed characters (depends on CLAUDE_CONTENT_LANGUAGE):

  • english (default): ASCII printable + whitespace + allowlisted English typographic punctuation (em-dash, en-dash, curly quotes, ellipsis, NBSP).
  • korean_plus_english: ASCII + Hangul Syllables / Jamo / Compat Jamo, mixed inline.
  • exclusive_bilingual: per-artifact — English-only or Korean-only (Korean side allows fenced code, inline code, URLs, and the 한국어(English) translation form as ASCII containers).
  • any: validation skipped.

Not covered: gh pr review --body is intentionally not guarded (review comments may have different tone/content needs and would warrant a separate policy decision).

Fail policy: Fail-open. If stdin parsing fails or the command is unrecognized, the hook returns allow. Server-side review and commit-msg hooks remain as additional layers.

Behavior:

  • Returns JSON with permissionDecision: "deny" listing the first non-ASCII grapheme found
  • Defers to other layers for command-substitution and --body-file cases
  • Timeout: 5 seconds
  • Cross-platform: pr-language-guard.sh and pr-language-guard.ps1

Configuration:

{
  "type": "command",
  "command": "~/.claude/hooks/pr-language-guard.sh",
  "timeout": 5
}

Example deny response:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "PR/issue --body rejected: Text contains Korean (Hangul) characters (first run: '한국어'). Active CLAUDE_CONTENT_LANGUAGE='english' rejects this artifact. To allow Korean, set CLAUDE_CONTENT_LANGUAGE=exclusive_bilingual or korean_plus_english. See commit-settings.md."
  }
}

14. Merge Gate Guard (PreToolUse)

Hard-blocks gh pr merge when any PR check is failing, pending, or cancelled — eliminates the rule drift that lets failing CI rationalizations slip through in long-running batch workflows.

Purpose: Enforces the "ABSOLUTE CI GATE" rule from global/CLAUDE.md at the Bash tool boundary. Mirrors the commit-message-guard and pr-language-guard enforcement model: a deterministic hook gate that catches drift where the model occasionally rationalizes failing checks as "unrelated", "infrastructure", or "pre-existing".

Trigger: Bash tool calls matching gh pr merge.

Files: global/hooks/merge-gate-guard.sh, global/hooks/merge-gate-guard.ps1

Logic:

  1. Scope gate: only process gh pr merge commands (all others pass through).
  2. Extract PR number from positional integer, URL form (https://github.com/owner/repo/pull/N), or anywhere after gh pr merge. Allow if no PR number is found (interactive mode).
  3. Extract --repo / -R value if present.
  4. Invoke gh pr checks <PR> --json bucket,name,state (with -R if specified).
  5. Parse the JSON array. Allowed buckets: pass and skipping. Anything in fail, pending, cancel, or unknown buckets blocks the merge.
  6. Deny reason includes every non-passing check name with its bucket and state, plus a reminder not to rationalize failures.

Allow policy: A check qualifies as passing if its bucket is pass or skipping. The skipping bucket covers checks intentionally skipped (e.g. paths-ignore matches) and is treated as neutral, the same way GitHub itself does for branch protection rules.

Fail policy: Fail-OPEN on gh CLI errors. Unlike most other guards, this hook allows the merge when:

  • gh CLI is not installed
  • gh pr checks returns a non-zero exit code (e.g. transient network error, auth failure, unresolvable PR)
  • The JSON response cannot be parsed
  • The PR has no checks configured at all

A diagnostic is written to stderr in each fail-open case so the user can see why the gate did not run. The rationale is that this hook is a "best-effort gate", not a "hard fail on tool unavailability" — server-side branch protection rules remain as the authoritative gate, so a transient failure here should not permanently block user work.

Behavior:

  • Returns JSON with permissionDecision: "deny" listing every non-passing check
  • Fail-open on any gh CLI error; diagnostics written to stderr
  • Timeout: 30 seconds (longer than other guards because it makes an external API call)
  • Cross-platform: merge-gate-guard.sh and merge-gate-guard.ps1

Configuration:

{
  "type": "command",
  "command": "~/.claude/hooks/merge-gate-guard.sh",
  "timeout": 30
}

Example deny response:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Merge blocked by ABSOLUTE CI GATE: PR #100 has non-passing checks: Build Linux [fail/FAILURE], Build Windows [pending/IN_PROGRESS]. Wait for all checks to pass before merging — never rationalize a failure as unrelated, infrastructure, or pre-existing."
  }
}

15. Attribution Guard (PreToolUse)

Hard-blocks AI/Claude attribution markers (Co-Authored-By: Claude, 🤖 Claude, "Generated with Claude", etc.) in PR/issue/release artifacts created via the gh CLI — closes the channels the commit-message guard cannot reach.

Purpose: Enforces the "No AI/Claude attribution in commits, issues, or PRs" rule from commit-settings.md. The existing commit-message-guard only inspects git commit -m messages; PR titles/bodies, issue titles/bodies, PR review bodies, and release notes created via gh previously bypassed it. This hook closes that gap by gating the same Bash boundary that pr-language-guard uses.

Trigger: Bash tool calls matching one of:

  • gh pr (create|edit|comment|review)--title, --body
  • gh issue (create|edit|comment)--title, --body
  • gh release (create|edit)--notes (-n short flag also supported)

Issue #480 extended scope from gh (pr|issue) (create|edit|comment) to include pr review and release create|edit. The --notes channel was the largest remaining attribution leak, since release notes are highly visible artifacts.

Files: global/hooks/attribution-guard.sh, global/hooks/attribution-guard.ps1

Shared validation library: hooks/lib/validate-commit-message.sh exposes CMV_ATTRIBUTION_TRAILER_REGEX, CMV_ATTRIBUTION_EMOJI_REGEX, CMV_ATTRIBUTION_PROSE_REGEX, and the unified validate_no_attribution() function. Both bash hooks source this single source of truth so attribution rules stay in lockstep across enforcement layers. The PowerShell variant inlines equivalent patterns; update both when changing the design.

Three-pattern design (issue #480 — replaces the earlier broad case-insensitive prose match that produced false positives on legitimate technical writing):

  1. Trailer-style attribution anchored at the start of any line:
    • Co-Authored-By: Claude, Co-Author: Anthropic
    • Generated-by: Claude, Generated by: Claude
    • Created-by: Anthropic, Authored-by: Claude
    • Signed-Off-By: Claude, Assisted-By: AI-Assisted
  2. Bot emoji directly adjacent to Claude/Anthropic:
    • 🤖 Claude, 🤖 generated by Anthropic
  3. Generated|Created|Authored|Written {with|by|using} {Claude|Anthropic|AI-Assistant} prose:
    • "Generated with Claude", "Created by Anthropic", "Written using AI-Assistant"

Allowed (deliberately) — false-positive prevention:

  • Casual technical mentions: "feat: add Claude API integration", "fix: anthropic SDK fallback", "Compatible with Claude 4.7 models"
  • These describe what the code interacts with, not who authored the change. Blocking them was the most-reported friction in the prior broad-regex design.

Logic:

  1. Scope gate: only process the extended gh subcommand set above.
  2. Skip command-substitution / heredoc bodies (--body "$(...)", --notes "$(...)") and file-based references (--body-file, --notes-file, -F) — these cannot be parsed at the shell layer.
  3. Extract --title/-t, --body/-b, --notes/-n using bash native [[ =~ ]] so multi-line values match correctly.
  4. Pass each value through validate_no_attribution(). Reject on match from any of the three patterns; the deny reason names which pattern fired.

Fail policy: Fail-open. If stdin parsing fails or the command is unrecognized, the hook returns allow. The commit-msg git hook and commit-message-guard PreToolUse hook remain as additional layers for committed content.

Behavior:

  • Returns JSON with permissionDecision: "deny" when attribution is detected
  • Defers to other layers for command-substitution and file-based cases
  • Timeout: 5 seconds
  • Cross-platform: attribution-guard.sh and attribution-guard.ps1

Configuration:

{
  "type": "command",
  "command": "~/.claude/hooks/attribution-guard.sh",
  "timeout": 5
}

Example deny response:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "release --notes rejected: Text contains AI/Claude attribution trailer (Co-Authored-By: / Generated-by: / Authored-by: Claude or Anthropic). Remove the trailer before submitting."
  }
}

16. Instructions Loaded Reinforcer (InstructionsLoaded)

Re-asserts critical policy (commit-settings, branching, conventional commits) immediately after CLAUDE.md and .claude/rules/*.md are loaded — closes the gap where long sessions drift away from policy that lives only in the system prompt.

Purpose: Inject a short, fixed policy digest right after Claude finishes loading instruction files. The digest restates the AI-attribution prohibition, the CLAUDE_CONTENT_LANGUAGE policy pointer (full rules stay in commit-settings.md), protected-branch policy, and Conventional Commits format so they remain in active context even when the original instruction files scroll out. The full commit-settings.md text already reaches context via the CLAUDE.md @import chain, so the hook never re-injects it verbatim (issue #716); the payload is capped at ~10 lines / ~500 bytes.

Trigger: InstructionsLoaded event — fires once per session, after CLAUDE.md / .claude/rules/*.md have been ingested.

Files: global/hooks/instructions-loaded-reinforcer.sh, global/hooks/instructions-loaded-reinforcer.ps1

Logic:

  1. Compose a fixed four-item digest (attribution ban, content-language pointer, protected-branch rules, Conventional Commits format). No file is read into the payload.
  2. Emit JSON via jq if available; otherwise hand-escape and print the JSON literal.

Behavior:

  • Returns JSON with hookSpecificOutput.additionalContext carrying the reinforcement text
  • Always exits 0 — the hook never blocks instruction loading; it only augments context
  • Cross-platform: instructions-loaded-reinforcer.sh and instructions-loaded-reinforcer.ps1

Configuration:

{
  "type": "command",
  "command": "~/.claude/hooks/instructions-loaded-reinforcer.sh",
  "timeout": 5
}

Sample input (JSON via stdin):

{
  "session_id": "abc123",
  "hook_event_name": "InstructionsLoaded",
  "cwd": "/path/to/project"
}

Sample output:

{
  "hookSpecificOutput": {
    "hookEventName": "InstructionsLoaded",
    "additionalContext": "## Critical Policy Reinforcement (digest)\n\n- No AI/Claude attribution in commits, issues, or PRs.\n- Issue/PR/commit prose: follow the CLAUDE_CONTENT_LANGUAGE policy (see commit-settings.md).\n- Branches: work branches from develop; never push directly to main or develop; squash merge only.\n- Commits: Conventional Commits `type(scope): description`; lowercase first char, no trailing period."
  }
}

17. Post-Compact Restore (SessionStart)

Re-asserts the four core principles immediately after Claude Code automatically compacts the conversation - pairs with pre-compact-snapshot to keep the principles in context across long sessions.

Purpose: When automatic context compaction discards the original CLAUDE.md and rule files, this hook re-asserts the four core principles (Think, Minimize, Surgical, Verify) plus the senior-engineer self-check so the model does not regress to pre-policy defaults after compaction.

Trigger: SessionStart event with matcher compact - fires once when the harness starts the fresh post-compaction context. The PostCompact event does NOT support hookSpecificOutput (Claude Code rejects such output with "Hook JSON output validation failed"); SessionStart with source == "compact" is the official channel for injecting context after compaction (issue #720). Pairs with the existing pre-compact-snapshot hook (PreCompact event) which captures pre-compact state.

Files: global/hooks/post-compact-restore.sh, global/hooks/post-compact-restore.ps1

Logic:

  1. Parse stdin JSON and exit 0 silently (no output) unless source is "compact" - defense in depth in case the hook is ever wired without a matcher, so startup/resume/clear sessions are never spammed.
  2. Append a restore record (timestamp, session id, working directory) to ~/.claude/logs/compact-snapshots.log - the same log written by pre-compact-snapshot.sh so snapshot / restore pairs can be correlated.
  3. Emit a fixed short digest (~500 bytes) of the four core principles plus the self-check line. The hook never reads rule files into the payload; .sh and .ps1 emit byte-equivalent additionalContext.

Behavior:

  • Returns JSON with hookSpecificOutput.{hookEventName: "SessionStart", additionalContext} carrying the principles digest
  • Always exits 0 - the hook never blocks session start; it only augments the post-compact context
  • Silent no-op (no output) for any source other than "compact", for empty stdin, and when no JSON parser is available
  • Writes to ~/.claude/logs/compact-snapshots.log (created on demand)
  • Cross-platform: post-compact-restore.sh and post-compact-restore.ps1

Configuration:

{
  "matcher": "compact",
  "hooks": [
    {
      "type": "command",
      "command": "~/.claude/hooks/post-compact-restore.sh",
      "timeout": 5
    }
  ]
}

Sample input (JSON via stdin):

{
  "session_id": "abc123",
  "hook_event_name": "SessionStart",
  "source": "compact",
  "cwd": "/path/to/project"
}

Sample output:

{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "## Post-Compaction Restore (digest)\n\nContext was just compacted. Re-asserting the four core principles:\n\n1. Think Before Acting - state assumptions explicitly; if uncertain, ask.\n..."
  }
}

18. Task Created Validator (TaskCreated)

Hard-blocks low-quality task descriptions at the TaskCreate boundary — enforces a minimum length and at least one acceptance-criteria checkbox so that downstream teammates and reviewers never receive vague work items.

Purpose: Validate that every task created via TaskCreate carries enough scope and acceptance criteria to be actionable. Mirrors the commit-message-guard enforcement model: a deterministic gate that catches the drift where short, ambiguous task descriptions leak into multi-agent batch workflows.

Trigger: TaskCreated event — fires synchronously when any agent (lead or teammate) calls TaskCreate. Blocking: a non-zero exit halts task creation and surfaces the rejection reason to the calling model.

Files: global/hooks/task-created-validator.sh, global/hooks/task-created-validator.ps1

Rules enforced:

  1. Description length: trimmed description must be at least 20 characters.
  2. Acceptance criteria: description must contain at least one - [ ] markdown checkbox marker.

Logic:

  1. Read JSON from stdin. If empty, fail open (nothing to validate).
  2. Extract description from one of tool_input.description, description, or task.description — supports jq first, falls back to python3 / python. If neither parser is available, fail open.
  3. If no description field is present, fail open. If the field is present but fails either rule, exit 2 with a guidance message on stderr.

Decision control: Uses exit code only (not JSON permissionDecision):

Exit Code Effect
0 Approve task creation
2 Block creation — stderr message sent as feedback to the model

Fail policy: Fail-open on missing field, missing JSON parser, or unparseable input. Fail-closed only when the description is present and demonstrably violates a rule. The rationale matches merge-gate-guard: a tooling gap should not permanently block legitimate work.

Behavior:

  • Returns exit code 0 on approval, exit code 2 on rejection with a stderr message naming the failed rule
  • Reads JSON from stdin via jq or python (no JSON parser → fail-open)
  • Timeout: 5 seconds
  • Cross-platform: task-created-validator.sh and task-created-validator.ps1

Configuration:

{
  "type": "command",
  "command": "~/.claude/hooks/task-created-validator.sh",
  "timeout": 5
}

Sample input (JSON via stdin):

{
  "session_id": "abc123",
  "hook_event_name": "TaskCreated",
  "tool_input": {
    "subject": "Implement validation",
    "description": "Add input validation to the user form.\n\nAcceptance:\n- [ ] Empty fields rejected\n- [ ] Email format validated"
  }
}

Sample rejection (stderr, exit 2):

TaskCreated rejected: description must be at least 20 characters (got 12). Add scope, context, and acceptance criteria.
TaskCreated rejected: description must contain at least one '- [ ]' checkbox marker for acceptance criteria.

19. Post Task/Agent Checkpoint (PostToolUse)

Snapshots the working tree into a WIP commit after every Task or Agent call — prevents a later sub-agent from silently overwriting a prior agent's output in multi-agent workflows.

Purpose: Close the write-race window in multi-agent skills (issue-work team mode, harness fan-out, fleet-orchestrator) where a second agent can clobber uncommitted output from a first agent. The hook checkpoints after each Task/Agent completes so the previous agent's changes survive in git history even if the working tree is overwritten.

Trigger: PostToolUse event, matcher Task|Agent. Non-matching tools pass through silently.

Files: global/hooks/post-task-checkpoint.sh, global/hooks/post-task-checkpoint.ps1

Behavior:

  1. Read JSON from stdin (tool_name, tool_input). Fail-open on malformed input.
  2. Skip silently if tool_name is not Task or Agent.
  3. No-op if not inside a git worktree (prevents errors in non-repo directories).
  4. No-op if working tree is clean (keeps history free of empty-commit spam).
  5. Otherwise: git add -A && git commit -m "wip(agent): $AGENT_NAME checkpoint $TS" --no-verify --allow-empty.

Commit message format: wip(agent): <sanitized-agent-name> checkpoint YYYY-MM-DD HH:MM:SS. Agent name is extracted from tool_input.subagent_type (preferred) or tool_input.name (fallback); only [A-Za-z0-9_-] characters survive sanitization, clipped to 64 chars.

Why --no-verify: wip(agent): is not in the Conventional Commits type list that commit-msg accepts, so the validator would reject it. Checkpoint commits are throwaway and expected to be squashed at PR merge.

Why --allow-empty: Defensive — satisfies the acceptance criterion that "hook succeeds on empty tree" even if the no-op check is skipped for some reason (e.g., staged/unstaged boundary edge cases).

Decision control: Always exits 0 — the hook never blocks a workflow. Any git, jq, or JSON failure is swallowed silently. The failure mode is "checkpoint didn't happen," not "workflow stopped."

Configuration:

{
  "type": "command",
  "command": "~/.claude/hooks/post-task-checkpoint.sh",
  "timeout": 15,
  "async": true
}

Limitations:

  • WIP checkpoints pollute history before squash merge. Acceptable tradeoff for recoverability. Release-time squash cleans them up.
  • Async: the hook does not block the model's next turn. A rapid-fire agent could start before its predecessor's checkpoint lands, though the wall-clock gap in practice is < 100 ms.
  • Does not run in non-git directories (e.g., ad-hoc /tmp work) — nothing to checkpoint there.

Opt-out: Remove the PostToolUse matcher block from global/settings.json and re-run scripts/sync.sh. Individual sessions can skip by running outside a git worktree.

Test fixture: tests/hooks/test-post-task-checkpoint.sh exercises dirty/clean/non-repo paths, agent-name sanitization, malformed-JSON fail-open, and the two-agent overwrite scenario.

20. Pre-edit Read Guard (PreToolUse/PostToolUse)

Converts "file was not read" tool-contract violations into an actionable Read-first deny message on the first attempt — no more silent Edit retries.

Purpose: Enforce the Claude Code contract that Edit and Write on an existing file require a prior Read in the same session. Without this guard the tool simply errors out and the model retries in the dark. With it, the hook denies the edit up-front with a reason that tells Claude exactly which file to Read.

Trigger: A single script is registered under two hook entries:

  • PreToolUse matcher Edit|Write — the guard (returns allow/deny JSON).
  • PostToolUse matcher Read — the tracker (records the Read path, no JSON).

Files: global/hooks/pre-edit-read-guard.sh, global/hooks/pre-edit-read-guard.ps1

Tracker: $TMPDIR/claude-read-set-<session-id> on Unix, %TEMP%\claude-read-set-<session-id> on Windows. One absolute path per line, deduplicated. Cleared naturally when the temp directory rotates between sessions.

Behavior:

  1. Read JSON from stdin (tool_name, tool_input.file_path, session_id).
  2. If tool_name == "Read": resolve file_path to absolute, append to tracker if absent. Emit no JSON. Best-effort — any failure is swallowed.
  3. If tool_name in {"Edit","Write"}: resolve file_path to absolute, then:
    • Tracker file missing → allow (first-run safety for fresh sessions).
    • Write on a non-existent file → allow (new files cannot have been Read).
    • Tracker contains the path → allow.
    • Otherwise → deny with a message naming the exact file to Read first.
  4. Any other tool_name: allow (prevents interference with other matchers).

Deny reason format:

Cannot Edit '/abs/path/to/file' without reading it first in this session. Call Read on '/abs/path/to/file' and retry. (Session <id>, tracker <path>.)

Decision control: Always exits 0. Fail-open on empty stdin or missing jq — the hook never blocks the workflow when it can't do its job correctly.

Configuration (global/settings.json):

{
  "PreToolUse": [
    {
      "matcher": "Edit|Write",
      "hooks": [
        { "type": "command", "command": "~/.claude/hooks/pre-edit-read-guard.sh", "timeout": 5 }
      ]
    }
  ],
  "PostToolUse": [
    {
      "matcher": "Read",
      "hooks": [
        { "type": "command", "command": "~/.claude/hooks/pre-edit-read-guard.sh", "timeout": 5, "async": true }
      ]
    }
  ]
}

Limitations:

  • Session-scoped. Across restarts the tracker rotates with $TMPDIR and Claude must Read again.
  • Case-sensitive file match on case-insensitive filesystems (macOS default HFS+/APFS) can produce rare false negatives if the same path is Read with different casing — normalize via realpath where available.
  • Edit on a file that the user manually modified outside the session still passes once it is in the tracker. The hook verifies Read, not freshness.

Opt-out: Remove the two matcher blocks from global/settings.json and global/settings.windows.json, then re-run scripts/sync.sh.

Test fixture: tests/hooks/test-pre-edit-read-guard.sh exercises the deny/allow paths, first-run tracker-missing safety, Write on non-existent files, and Read-then-Edit unlocking.

Hook Response Format

All PreToolUse hooks must output JSON to stdout and exit with code 0:

Allow response:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow"
  }
}

Deny response:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Reason for blocking"
  }
}

Input: Hooks receive tool input as JSON via stdin. Use jq to extract fields:

INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

Exit codes: Always exit 0 when returning JSON. The decision (allow/deny) is conveyed through the permissionDecision field, not through the exit code.

Project Hooks (project/.claude/settings.json)

1. Auto Formatting (PostToolUse)

Never worry about code style — every edit is automatically formatted in your language's standard style.

Purpose: Automatically run language-specific formatters after file modifications

Supported languages and tools:

Extension Formatter Installation
.py black + isort pip install black isort
.ts, .tsx, .js, .jsx, .json, .md prettier npm install prettier
.cpp, .cc, .h, .hpp clang-format brew install clang-format
.kt, .kts ktlint brew install ktlint
.go gofmt Included with Go installation
.rs rustfmt Included with Rust installation

Behavior:

  • Skips if tool is not installed (no error)
  • Timeout: 30 seconds

Permission Settings

Deny rules defined in global/settings.json and global/settings.windows.json block direct tool access to sensitive files:

{
  "permissions": {
    "deny": [
      "Read(.env)",
      "Read(.env.*)",
      "Read(**/.env)",
      "Read(**/secrets/**)",
      "Read(**/credentials/**)",
      "Read(**/*.pem)",
      "Read(**/*.key)",
      ...
    ]
  }
}

The Windows profile also carries a narrow permissions.allow list for native PowerShell(...) read-only discovery commands. It covers common inspection verbs such as Get-ChildItem, Test-Path, Select-Object, Select-String, and Write-Output, plus read-only git and gh commands. The allowlist is intended to reduce repeated confirmation prompts for routine local inspection; it is not a general bypass profile.

The Windows profile intentionally keeps these guardrails:

  • permissions.defaultMode remains default.
  • disableBypassPermissionsMode and disableAutoMode remain disable.
  • No broad PowerShell(*) rule is allowed.
  • Direct content reads such as Get-Content, file mutation cmdlets, remote execution cmdlets, and state-changing gh commands are not allowlisted.
  • Sensitive-file deny rules stay in place.

skipDangerousModePermissionPrompt only suppresses the extra dangerous-mode launch warning. It does not widen permissions.allow, and it does not override the installed profile's bypass/auto-mode disable settings.

Windows Support (PowerShell)

Overview

All hooks have PowerShell (.ps1) equivalents for native Windows support without Git Bash.

Configuration File
macOS/Linux global/settings.json (runs .sh hooks via bash)
Windows global/settings.windows.json (runs .ps1 hooks via pwsh)

How It Works

The install.ps1 script automatically:

  1. Copies settings.windows.json as ~/.claude/settings.json
  2. Installs all .ps1 hook scripts to ~/.claude/hooks/

Hook commands use pwsh -NoProfile -File for fast, profile-independent execution:

{
  "type": "command",
  "command": "pwsh -NoProfile -File ~/.claude/hooks/sensitive-file-guard.ps1",
  "timeout": 5
}

PowerShell Hook Scripts

Hook File Description
Sensitive File Guard sensitive-file-guard.ps1 Blocks .env, .pem, .key access
Dangerous Command Guard dangerous-command-guard.ps1 Blocks rm -rf /, chmod 777, pipe execution
Session Logger session-logger.ps1 Logs session start/end/stop events
Cleanup cleanup.ps1 Removes old temp files from $env:TEMP
Prompt Validator prompt-validator.ps1 Warns on dangerous operation requests
GitHub API Preflight github-api-preflight.ps1 Tests GitHub API connectivity
Tool Failure Logger tool-failure-logger.ps1 Logs tool execution failures
Subagent Logger subagent-logger.ps1 Logs subagent start/stop events
Pre-Compact Snapshot pre-compact-snapshot.ps1 Captures state before compaction
Worktree Create worktree-create.ps1 Creates isolated worktree directory
Worktree Remove worktree-remove.ps1 Logs worktree removal events
Task Completed Logger task-completed-logger.ps1 Logs task completion events
Config Change Logger config-change-logger.ps1 Logs configuration changes
Markdown Anchor Validator markdown-anchor-validator.ps1 Validates markdown cross-reference anchors before commit
Team Limit Guard team-limit-guard.ps1 Enforces MAX_TEAMS concurrent team limit
Version Check version-check.ps1 Warns about known cache bug versions on session start

Key Differences from Bash Hooks

Feature Bash (.sh) PowerShell (.ps1)
JSON parsing jq (external dependency) ConvertFrom-Json (built-in)
Temp file cleanup find /tmp -mmin +60 Get-ChildItem $env:TEMP
Pattern matching grep -qE -match operator
HTTP requests curl Invoke-WebRequest
Timestamps date +"%Y-%m-%d" Get-Date -Format

Prerequisites

  • PowerShell 7+ (pwsh): Recommended for full compatibility
    winget install Microsoft.PowerShell
  • Execution Policy: Must allow running local scripts
    Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

Troubleshooting (Windows)

"File cannot be loaded because running scripts is disabled"

Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

Hooks not executing

  1. Verify pwsh is installed: pwsh --version
  2. Verify JSON syntax: Get-Content ~/.claude/settings.json | ConvertFrom-Json
  3. Check hook files exist: Get-ChildItem ~/.claude/hooks/*.ps1
  4. Restart Claude Code

"pwsh is not recognized"

Install PowerShell 7+: winget install Microsoft.PowerShell


Customization

Disabling Hooks

To disable a specific hook, remove or comment out the corresponding entry.

Adding New Hooks

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "type": "command",
        "command": "your-command-here",
        "timeout": 30
      }
    ]
  }
}

Matcher Patterns

Pattern Description
* All tools
Bash Bash tool only
Edit|Write Edit or Write tools
Read Read tool only

Troubleshooting

Hook is not executing

  1. Verify JSON syntax: cat settings.json | python3 -m json.tool
  2. Check file location: ~/.claude/settings.json or .claude/settings.json
  3. Restart Claude Code

Timeout occurring

Increase the timeout value (unit: seconds, max: 300)

Formatter not working

Verify the formatter is installed:

which black
which prettier
which clang-format

Git Hooks: Pre-push Protected Branch Guard

Prevent accidental pushes to main or develop — requires pull request workflow for protected branches.

Note: This is a standard git hook (.git/hooks/pre-push), not a Claude Code event hook. It runs whenever git push is executed, regardless of whether Claude Code is active.

Purpose: Block direct pushes to protected branches (main, develop)

Install: ./hooks/install-hooks.sh (or .\hooks\install-hooks.ps1 on Windows)

Protected branches: main, develop

How it works:

  1. Git invokes the hook before pushing, passing remote info via stdin
  2. The hook extracts the target branch from each ref being pushed
  3. If the target branch matches a protected branch, the push is blocked with an error message
  4. The error message guides the user to use the feature-branch + pull request workflow

Behavior:

  • Exits with code 1 to block the push when targeting a protected branch
  • Exits with code 0 to allow the push for non-protected branches
  • Bypass: git push --no-verify (forbidden by project policy)

Cross-platform:

File Runtime
hooks/pre-push bash
hooks/pre-push.ps1 PowerShell 7+

References

Auto-Generated Hook Catalog

The catalog below is built from the leading comment block of each hook script under global/hooks/. It is the authoritative listing — the hand-written sections elsewhere in this document provide narrative context but defer to this catalog for the canonical hook inventory.

Index

Hook Hook Type PowerShell
attribution-guard.sh PreToolUse (Bash) yes
bash-sensitive-read-guard.sh PreToolUse (Bash) yes
bash-write-guard.sh PreToolUse (Bash) yes
cleanup.sh SessionEnd yes
commit-message-guard.sh PreToolUse (Bash) yes
config-change-logger.sh ConfigChange yes
conflict-guard.sh PreToolUse (Bash) yes
cwd-change-logger.sh CwdChanged yes
dangerous-command-guard.sh PreToolUse (Bash) yes
gh-write-verb-guard.sh PreToolUse (Bash) yes
github-api-preflight.sh PreToolUse (Bash) yes
instructions-loaded-reinforcer.sh InstructionsLoaded (sync) yes
markdown-anchor-validator.sh PreToolUse (Bash) yes
memory-access-logger.sh PostToolUse (Read) yes
memory-integrity-check.sh SessionStart (sync) yes
memory-write-guard.sh PreToolUse (Edit Write)
merge-gate-guard.sh PreToolUse (Bash) yes
permission-denial-logger.sh PermissionDenied yes
post-compact-restore.sh SessionStart (matcher: compact, sync) yes
post-task-checkpoint.sh PostToolUse yes
pr-language-guard.sh PreToolUse (Bash) yes
pr-target-guard.sh PreToolUse (Bash) yes
pre-compact-snapshot.sh PreCompact (async) yes
pre-edit-read-guard.sh PreToolUse (Edit Write) + PostToolUse (Read)
prompt-validator.sh UserPromptSubmit yes
sensitive-file-guard.sh PreToolUse (Edit Write
session-logger.sh SessionStart, SessionEnd, Stop, TeammateIdle yes
subagent-logger.sh SubagentStart, SubagentStop yes
task-completed-logger.sh TaskCompleted yes
task-created-validator.sh TaskCreated (sync, blocking) yes
team-limit-guard.sh PreToolUse (TeamCreate) yes
tool-failure-logger.sh PostToolUseFailure yes
traceability-guard.sh PreToolUse (Bash) yes
version-check.sh SessionStart yes
worktree-create.sh WorktreeCreate (synchronous, type: command only) yes
worktree-remove.sh WorktreeRemove (async, type: command only) yes

Total: 36 bash hooks, 36 with PowerShell counterparts.

Hook Details

attribution-guard

File: attribution-guard.sh

Anchor: #attribution-guard

Blocks gh pr/issue/release commands whose user-facing text fields contain AI/Claude attribution markers. Scope (Issue #480 extended): pr create|edit|comment|review, issue create|edit|comment, release create|edit.

Field Value
Hook Type PreToolUse (Bash)
Trigger / Matcher Bash
Exit codes 0 (always — decision is in JSON)
Response format hookSpecificOutput with hookEventName
PowerShell counterpart present (attribution-guard.ps1)
Source global/hooks/attribution-guard.sh

bash-sensitive-read-guard

File: bash-sensitive-read-guard.sh

Anchor: #bash-sensitive-read-guard

Blocks reading sensitive files via the Bash tool channel.

Field Value
Hook Type PreToolUse (Bash)
Trigger / Matcher Bash
Exit codes 0 (always — decision is in JSON)
Response format hookSpecificOutput with hookEventName
PowerShell counterpart present (bash-sensitive-read-guard.ps1)
Source global/hooks/bash-sensitive-read-guard.sh

bash-write-guard

File: bash-write-guard.sh

Anchor: #bash-write-guard

Enforces the "Read before Edit/Write" invariant on the Bash tool channel.

Field Value
Hook Type PreToolUse (Bash)
Trigger / Matcher Bash
Exit codes 0 (always — decision is in JSON)
Response format
PowerShell counterpart present (bash-write-guard.ps1)
Source global/hooks/bash-write-guard.sh

cleanup

File: cleanup.sh

Anchor: #cleanup

Cleans up temporary files created during session

Field Value
Hook Type SessionEnd
Trigger / Matcher
Exit codes 0=success
Response format none (lifecycle event, no JSON output needed)
PowerShell counterpart present (cleanup.ps1)
Source global/hooks/cleanup.sh

commit-message-guard

File: commit-message-guard.sh

Anchor: #commit-message-guard

Deterministic git commit message validator

Field Value
Hook Type PreToolUse (Bash)
Trigger / Matcher Bash
Exit codes 0 (always — decision is in JSON)
Response format hookSpecificOutput with hookEventName
PowerShell counterpart present (commit-message-guard.ps1)
Source global/hooks/commit-message-guard.sh

config-change-logger

File: config-change-logger.sh

Anchor: #config-change-logger

Logs configuration file changes during session

Field Value
Hook Type ConfigChange
Trigger / Matcher
Exit codes
Response format none (lifecycle event, no JSON output needed)
PowerShell counterpart present (config-change-logger.ps1)
Source global/hooks/config-change-logger.sh

conflict-guard

File: conflict-guard.sh

Anchor: #conflict-guard

Guards against git operations that could cause conflicts

Field Value
Hook Type PreToolUse (Bash)
Trigger / Matcher Bash
Exit codes 0 (always — decision is in JSON)
Response format hookSpecificOutput with hookEventName
PowerShell counterpart present (conflict-guard.ps1)
Source global/hooks/conflict-guard.sh

cwd-change-logger

File: cwd-change-logger.sh

Anchor: #cwd-change-logger

Logs working-directory changes during a session for audit trails.

Field Value
Hook Type CwdChanged
Trigger / Matcher
Exit codes
Response format none (observation-only event; CwdChanged cannot block, exit 2 only shows stderr)
PowerShell counterpart present (cwd-change-logger.ps1)
Source global/hooks/cwd-change-logger.sh

dangerous-command-guard

File: dangerous-command-guard.sh

Anchor: #dangerous-command-guard

Blocks dangerous bash commands and records every decision.

Field Value
Hook Type PreToolUse (Bash)
Trigger / Matcher Bash
Exit codes 0 (always — decision is in JSON)
Response format hookSpecificOutput with hookEventName
PowerShell counterpart present (dangerous-command-guard.ps1)
Source global/hooks/dangerous-command-guard.sh

gh-write-verb-guard

File: gh-write-verb-guard.sh

Anchor: #gh-write-verb-guard

Narrows the gh CLI surface on the Bash channel:

Field Value
Hook Type PreToolUse (Bash)
Trigger / Matcher Bash
Exit codes 0 (always — decision is in JSON)
Response format
PowerShell counterpart present (gh-write-verb-guard.ps1)
Source global/hooks/gh-write-verb-guard.sh

github-api-preflight

File: github-api-preflight.sh

Anchor: #github-api-preflight

Checks GitHub API connectivity before executing GitHub-related commands

Field Value
Hook Type PreToolUse (Bash)
Trigger / Matcher Bash
Exit codes 0 (always — decision is in JSON, warning only)
Response format hookSpecificOutput with hookEventName
PowerShell counterpart present (github-api-preflight.ps1)
Source global/hooks/github-api-preflight.sh

instructions-loaded-reinforcer

File: instructions-loaded-reinforcer.sh

Anchor: #instructions-loaded-reinforcer

Re-asserts critical policy after CLAUDE.md / .claude/rules/*.md loads.

Field Value
Hook Type InstructionsLoaded (sync)
Trigger / Matcher sync
Exit codes 0 (always — context is delivered via JSON)
Response format hookSpecificOutput.additionalContext
PowerShell counterpart present (instructions-loaded-reinforcer.ps1)
Source global/hooks/instructions-loaded-reinforcer.sh

markdown-anchor-validator

File: markdown-anchor-validator.sh

Anchor: #markdown-anchor-validator

Validates markdown anchor references before git commit

Field Value
Hook Type PreToolUse (Bash)
Trigger / Matcher Bash
Exit codes 0 (always — decision is in JSON)
Response format hookSpecificOutput with hookEventName
PowerShell counterpart present (markdown-anchor-validator.ps1)
Source global/hooks/markdown-anchor-validator.sh

memory-access-logger

File: memory-access-logger.sh

Anchor: #memory-access-logger

Logs Claude Code Read tool calls targeting memory files (path only).

Field Value
Hook Type PostToolUse (Read)
Trigger / Matcher Read
Exit codes 0 (always — this is a passive logger; failure must NOT affect tool flow)
Response format empty stdout (PostToolUse cannot influence the past tool call)
PowerShell counterpart present (memory-access-logger.ps1)
Source global/hooks/memory-access-logger.sh

memory-integrity-check

File: memory-integrity-check.sh

Anchor: #memory-integrity-check

Prints a brief memory health summary at SessionStart. Reads ~/.claude/memory-shared/ metadata only -- no network, no validators.

Field Value
Hook Type SessionStart (sync)
Trigger / Matcher sync
Exit codes 0 always (SessionStart must never block the session)
Response format
PowerShell counterpart present (memory-integrity-check.ps1)
Source global/hooks/memory-integrity-check.sh

memory-write-guard

File: memory-write-guard.sh

Anchor: #memory-write-guard

Validates Claude Code Edit/Write tool calls targeting memory files BEFORE disk write.

Field Value
Hook Type PreToolUse (Edit
Trigger / Matcher Edit
Exit codes 0 (always — decision is encoded in JSON response)
Response format hookSpecificOutput with hookEventName "PreToolUse"
PowerShell counterpart present (memory-write-guard.ps1)
Source global/hooks/memory-write-guard.sh

merge-gate-guard

File: merge-gate-guard.sh

Anchor: #merge-gate-guard

Blocks gh pr merge commands when any PR check is not passing.

Field Value
Hook Type PreToolUse (Bash)
Trigger / Matcher Bash
Exit codes 0 (always — decision is in JSON)
Response format hookSpecificOutput with hookEventName
Fail policy FAIL-OPEN on gh CLI errors. If gh is missing, unauthenticated,
PowerShell counterpart present (merge-gate-guard.ps1)
Source global/hooks/merge-gate-guard.sh

permission-denial-logger

File: permission-denial-logger.sh

Anchor: #permission-denial-logger

Appends a redacted JSONL audit record for every denied tool call.

Field Value
Hook Type PermissionDenied
Trigger / Matcher
Exit codes 0 (always — passive logger, never alters the permission decision)
Response format none (observation-only event; no JSON emitted, never blocks)
Fail policy best-effort; logging failures are swallowed and never surface
PowerShell counterpart present (permission-denial-logger.ps1)
Source global/hooks/permission-denial-logger.sh

post-compact-restore

File: post-compact-restore.sh

Anchor: #post-compact-restore

Re-asserts the four core principles after automatic context compaction. Pairs with pre-compact-snapshot.sh (PreCompact event).

Field Value
Hook Type SessionStart (matcher: compact, sync)
Trigger / Matcher matcher: compact, sync
Exit codes 0 (always - silent no-op unless stdin source is "compact")
Response format hookSpecificOutput.additionalContext
Fail policy fails quiet - no JSON parser or non-compact source means no output
PowerShell counterpart present (post-compact-restore.ps1)
Source global/hooks/post-compact-restore.sh

post-task-checkpoint

File: post-task-checkpoint.sh

Anchor: #post-task-checkpoint

Auto-commits working-tree changes after a Task or Agent tool call completes, preventing a later sub-agent from silently overwriting a prior agent's output.

Field Value
Hook Type PostToolUse
Trigger / Matcher Task
Exit codes
Response format
PowerShell counterpart present (post-task-checkpoint.ps1)
Source global/hooks/post-task-checkpoint.sh

pr-language-guard

File: pr-language-guard.sh

Anchor: #pr-language-guard

Blocks gh commands that publish artifacts (PRs, issues, comments, reviews, releases) when their text content violates the resolved CLAUDE_CONTENT_LANGUAGE policy.

Field Value
Hook Type PreToolUse (Bash)
Trigger / Matcher Bash
Exit codes 0 (always — decision is in JSON)
Response format hookSpecificOutput with hookEventName
PowerShell counterpart present (pr-language-guard.ps1)
Source global/hooks/pr-language-guard.sh

pr-target-guard

File: pr-target-guard.sh

Anchor: #pr-target-guard

Blocks PRs targeting 'main' from non-develop branches

Field Value
Hook Type PreToolUse (Bash)
Trigger / Matcher Bash
Exit codes 0 (always — decision is in JSON)
Response format hookSpecificOutput with hookEventName
PowerShell counterpart present (pr-target-guard.ps1)
Source global/hooks/pr-target-guard.sh

pre-compact-snapshot

File: pre-compact-snapshot.sh

Anchor: #pre-compact-snapshot

Captures working state before automatic context compaction

Field Value
Hook Type PreCompact (async)
Trigger / Matcher async
Exit codes
Response format
PowerShell counterpart present (pre-compact-snapshot.ps1)
Source global/hooks/pre-compact-snapshot.sh

pre-edit-read-guard

File: pre-edit-read-guard.sh

Anchor: #pre-edit-read-guard

Enforces the "Read before Edit/Write" tool contract.

Field Value
Hook Type PreToolUse (Edit
Trigger / Matcher Read
Exit codes 0 (always — decision is in JSON for PreToolUse, no JSON for PostToolUse)
Response format hookSpecificOutput with hookEventName (PreToolUse only)
PowerShell counterpart present (pre-edit-read-guard.ps1)
Source global/hooks/pre-edit-read-guard.sh

prompt-validator

File: prompt-validator.sh

Anchor: #prompt-validator

Validates user prompts for dangerous operations

Field Value
Hook Type UserPromptSubmit
Trigger / Matcher
Exit codes 0=allow (with optional warning)
Response format hookSpecificOutput with additionalContext (UserPromptSubmit)
PowerShell counterpart present (prompt-validator.ps1)
Source global/hooks/prompt-validator.sh

sensitive-file-guard

File: sensitive-file-guard.sh

Anchor: #sensitive-file-guard

Blocks access to sensitive files

Field Value
Hook Type PreToolUse (Edit
Trigger / Matcher Edit
Exit codes 0 (always — decision is in JSON)
Response format hookSpecificOutput with hookEventName
PowerShell counterpart present (sensitive-file-guard.ps1)
Source global/hooks/sensitive-file-guard.sh

session-logger

File: session-logger.sh

Anchor: #session-logger

Logs session start/end events

Field Value
Hook Type SessionStart, SessionEnd, Stop, TeammateIdle
Trigger / Matcher
Exit codes
Response format none (lifecycle event, no JSON output needed)
PowerShell counterpart present (session-logger.ps1)
Source global/hooks/session-logger.sh

subagent-logger

File: subagent-logger.sh

Anchor: #subagent-logger

Logs subagent start/stop events for monitoring.

Field Value
Hook Type SubagentStart, SubagentStop
Trigger / Matcher
Exit codes 0 (always — lifecycle event)
Response format none (lifecycle event, no JSON output needed)
PowerShell counterpart present (subagent-logger.ps1)
Source global/hooks/subagent-logger.sh

task-completed-logger

File: task-completed-logger.sh

Anchor: #task-completed-logger

Logs task completion events for audit trail

Field Value
Hook Type TaskCompleted
Trigger / Matcher
Exit codes
Response format
PowerShell counterpart present (task-completed-logger.ps1)
Source global/hooks/task-completed-logger.sh

task-created-validator

File: task-created-validator.sh

Anchor: #task-created-validator

Validates task quality at creation time.

Field Value
Hook Type TaskCreated (sync, blocking)
Trigger / Matcher sync, blocking
Exit codes 0 = approve, 2 = block (stderr message shown to model)
Response format
PowerShell counterpart present (task-created-validator.ps1)
Source global/hooks/task-created-validator.sh

team-limit-guard

File: team-limit-guard.sh

Anchor: #team-limit-guard

Limits the maximum number of concurrent Agent Teams

Field Value
Hook Type PreToolUse (TeamCreate)
Trigger / Matcher TeamCreate
Exit codes 0 (always — decision is in JSON)
Response format hookSpecificOutput with hookEventName
PowerShell counterpart present (team-limit-guard.ps1)
Source global/hooks/team-limit-guard.sh

tool-failure-logger

File: tool-failure-logger.sh

Anchor: #tool-failure-logger

Logs tool execution failures for debugging and analysis.

Field Value
Hook Type PostToolUseFailure
Trigger / Matcher
Exit codes 0 (always — lifecycle event)
Response format none (lifecycle event, no JSON output needed)
PowerShell counterpart present (tool-failure-logger.ps1)
Source global/hooks/tool-failure-logger.sh

traceability-guard

File: traceability-guard.sh

Anchor: #traceability-guard

Deterministic traceability cascade validator.

Field Value
Hook Type PreToolUse (Bash)
Trigger / Matcher Bash
Exit codes 0 (always — decision is in JSON)
Response format hookSpecificOutput with hookEventName
PowerShell counterpart present (traceability-guard.ps1)
Source global/hooks/traceability-guard.sh

version-check

File: version-check.sh

Anchor: #version-check

Checks Claude Code version against known problematic versions

Field Value
Hook Type SessionStart
Trigger / Matcher
Exit codes
Response format none (lifecycle event, no JSON output needed)
PowerShell counterpart present (version-check.ps1)
Source global/hooks/version-check.sh

worktree-create

File: worktree-create.sh

Anchor: #worktree-create

Creates an isolated worktree directory for non-git environments

Field Value
Hook Type WorktreeCreate (synchronous, type: command only)
Trigger / Matcher synchronous, type: command only
Exit codes
Response format
PowerShell counterpart present (worktree-create.ps1)
Source global/hooks/worktree-create.sh

worktree-remove

File: worktree-remove.sh

Anchor: #worktree-remove

Cleans up and logs worktree removal events

Field Value
Hook Type WorktreeRemove (async, type: command only)
Trigger / Matcher async, type: command only
Exit codes
Response format
PowerShell counterpart present (worktree-remove.ps1)
Source global/hooks/worktree-remove.sh