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 byscripts/gen-hooks-md.shand 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.
Hooks are user-defined commands that automatically execute during specific Claude Code events.
| 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 |
| File | Purpose | Scope |
|---|---|---|
global/settings.json |
Global Hook settings | All projects |
project/.claude/settings.json |
Project Hook settings | Current project only |
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
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)
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
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)
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:
- Collects staged markdown files via
git diff --cached --name-only --diff-filter=d -- '*.md' - Pass 1: Builds the primary anchor registry from headings in those staged files (GitHub-style slug algorithm)
- Pass 2: Checks all
](#anchor)(intra-file) and](file.md#anchor)(inter-file) references against the registry - 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.
- 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,-2suffixes
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
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:
- Reads
MAX_TEAMSenvironment variable (default: 3) - Counts directories in
~/.claude/teams/ - 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) andteam-limit-guard.ps1(PowerShell)
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 |
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:
- Gets current Claude Code version via
claude --version - Compares against a hardcoded list of known problematic versions (2.1.69–2.1.81)
- Logs a warning to
~/.claude/session.logif a match is found
Known bugs tracked:
Behavior:
- Lifecycle event hook — no JSON output required
- Always exits 0 (non-blocking)
- Timeout: 10 seconds, async
- Cross-platform:
version-check.sh(bash) andversion-check.ps1(PowerShell)
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 |
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): descriptionortype: 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-msghook for command-substitution messages (-m "$(...") - Timeout: 5 seconds
- Cross-platform:
commit-message-guard.shandcommit-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.
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:
- Existing operation: Denies if
MERGE_HEAD,REBASE_HEAD, orCHERRY_PICK_HEADexists (another operation is in progress) - Uncommitted changes: Denies if
git status --porcelainis 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.shandconflict-guard.ps1
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:
- Scope gate: only process
gh pr createcommands (all others pass through) - Extract
--basevalue (--base main,--base=main,-B main) - If no
--baseflag is present, resolve the repo default branch: usePR_TARGET_GUARD_DEFAULT_BRANCH_OVERRIDEif set (test injection), else querygh api repos/{owner}/{repo} --jq .default_branch(honoring--repo/-Rwhen supplied). If the default branch cannot be resolved, allow (graceful degradation) - If the resolved base is
mainormaster:- Allow if the head branch is
developor matchesrelease/*(legitimate release PR via/release) - Deny otherwise with the branching-policy guidance message
- Allow if the head branch is
- Otherwise (base is neither
mainnormaster): allow
Fail policy: Fail-closed (deny if JSON parsing fails)
Complements:
pre-pushgit hook: blocksgit push origin main/developvalidate-pr-target.ymlGitHub Actions: auto-closes non-develop PRs to main (server-side)
Configuration:
{
"type": "command",
"command": "~/.claude/hooks/pr-target-guard.sh",
"timeout": 5
}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:
- 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. - Skip command-substitution / heredoc bodies (
--body "$(...)") and--body-filereferences — these cannot be parsed at the shell layer, so the hook defers to other safeguards. - Extract
--title/-t,--body/-b, and--notes/-nvalues, supporting both double-quoted and single-quoted forms and--flag value/--flag=valuelayouts. - Dispatch to
validate_content_languageinhooks/lib/validate-language.sh, which selects the active validator fromCLAUDE_CONTENT_LANGUAGE(defaultenglish). - Deny reason includes the first offending substring and references
commit-settings.mdso 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-filecases - Timeout: 5 seconds
- Cross-platform:
pr-language-guard.shandpr-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."
}
}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:
- Scope gate: only process
gh pr mergecommands (all others pass through). - Extract PR number from positional integer, URL form (
https://github.com/owner/repo/pull/N), or anywhere aftergh pr merge. Allow if no PR number is found (interactive mode). - Extract
--repo/-Rvalue if present. - Invoke
gh pr checks <PR> --json bucket,name,state(with-Rif specified). - Parse the JSON array. Allowed buckets:
passandskipping. Anything infail,pending,cancel, or unknown buckets blocks the merge. - 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:
ghCLI is not installedgh pr checksreturns 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.shandmerge-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."
}
}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,--bodygh issue (create|edit|comment)—--title,--bodygh release (create|edit)—--notes(-nshort 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):
- Trailer-style attribution anchored at the start of any line:
Co-Authored-By: Claude,Co-Author: AnthropicGenerated-by: Claude,Generated by: ClaudeCreated-by: Anthropic,Authored-by: ClaudeSigned-Off-By: Claude,Assisted-By: AI-Assisted
- Bot emoji directly adjacent to Claude/Anthropic:
🤖 Claude,🤖 generated by Anthropic
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:
- Scope gate: only process the extended
ghsubcommand set above. - Skip command-substitution / heredoc bodies (
--body "$(...)",--notes "$(...)") and file-based references (--body-file,--notes-file,-F) — these cannot be parsed at the shell layer. - Extract
--title/-t,--body/-b,--notes/-nusing bash native[[ =~ ]]so multi-line values match correctly. - 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.shandattribution-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."
}
}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:
- Compose a fixed four-item digest (attribution ban, content-language pointer, protected-branch rules, Conventional Commits format). No file is read into the payload.
- Emit JSON via
jqif available; otherwise hand-escape and print the JSON literal.
Behavior:
- Returns JSON with
hookSpecificOutput.additionalContextcarrying the reinforcement text - Always exits 0 — the hook never blocks instruction loading; it only augments context
- Cross-platform:
instructions-loaded-reinforcer.shandinstructions-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."
}
}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:
- Parse stdin JSON and exit 0 silently (no output) unless
sourceis"compact"- defense in depth in case the hook is ever wired without a matcher, so startup/resume/clear sessions are never spammed. - Append a restore record (timestamp, session id, working directory) to
~/.claude/logs/compact-snapshots.log- the same log written bypre-compact-snapshot.shso snapshot / restore pairs can be correlated. - 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;
.shand.ps1emit byte-equivalentadditionalContext.
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
sourceother 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.shandpost-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..."
}
}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:
- Description length: trimmed description must be at least 20 characters.
- Acceptance criteria: description must contain at least one
- [ ]markdown checkbox marker.
Logic:
- Read JSON from stdin. If empty, fail open (nothing to validate).
- Extract description from one of
tool_input.description,description, ortask.description— supportsjqfirst, falls back topython3/python. If neither parser is available, fail open. - 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
jqorpython(no JSON parser → fail-open) - Timeout: 5 seconds
- Cross-platform:
task-created-validator.shandtask-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.
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:
- Read JSON from stdin (tool_name, tool_input). Fail-open on malformed input.
- Skip silently if tool_name is not
TaskorAgent. - No-op if not inside a git worktree (prevents errors in non-repo directories).
- No-op if working tree is clean (keeps history free of empty-commit spam).
- 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
/tmpwork) — 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.
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:
PreToolUsematcherEdit|Write— the guard (returnsallow/denyJSON).PostToolUsematcherRead— 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:
- Read JSON from stdin (
tool_name,tool_input.file_path,session_id). - If
tool_name == "Read": resolvefile_pathto absolute, append to tracker if absent. Emit no JSON. Best-effort — any failure is swallowed. - If
tool_name in {"Edit","Write"}: resolvefile_pathto absolute, then:- Tracker file missing →
allow(first-run safety for fresh sessions). Writeon a non-existent file →allow(new files cannot have been Read).- Tracker contains the path →
allow. - Otherwise →
denywith a message naming the exact file to Read first.
- Tracker file missing →
- 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
$TMPDIRand 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
realpathwhere available. Editon 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.
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.
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
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.defaultModeremainsdefault.disableBypassPermissionsModeanddisableAutoModeremaindisable.- No broad
PowerShell(*)rule is allowed. - Direct content reads such as
Get-Content, file mutation cmdlets, remote execution cmdlets, and state-changingghcommands 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.
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) |
The install.ps1 script automatically:
- Copies
settings.windows.jsonas~/.claude/settings.json - Installs all
.ps1hook 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
}| 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 |
| 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 |
- PowerShell 7+ (
pwsh): Recommended for full compatibilitywinget install Microsoft.PowerShell
- Execution Policy: Must allow running local scripts
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser- Verify
pwshis installed:pwsh --version - Verify JSON syntax:
Get-Content ~/.claude/settings.json | ConvertFrom-Json - Check hook files exist:
Get-ChildItem ~/.claude/hooks/*.ps1 - Restart Claude Code
Install PowerShell 7+: winget install Microsoft.PowerShell
To disable a specific hook, remove or comment out the corresponding entry.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"type": "command",
"command": "your-command-here",
"timeout": 30
}
]
}
}| Pattern | Description |
|---|---|
* |
All tools |
Bash |
Bash tool only |
Edit|Write |
Edit or Write tools |
Read |
Read tool only |
- Verify JSON syntax:
cat settings.json | python3 -m json.tool - Check file location:
~/.claude/settings.jsonor.claude/settings.json - Restart Claude Code
Increase the timeout value (unit: seconds, max: 300)
Verify the formatter is installed:
which black
which prettier
which clang-formatPrevent 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 whenevergit pushis 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:
- Git invokes the hook before pushing, passing remote info via stdin
- The hook extracts the target branch from each ref being pushed
- If the target branch matches a protected branch, the push is blocked with an error message
- 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+ |
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.
| 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.
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |