Source profileQuality 96/100Review permissions

athola/claude-night-market/plugins/abstract/skills/hook-authoring/SKILL.md

hook-authoring

Guide creating Claude Code hooks with security-first design. Use for validation and enforcement.

Source repository stars
330
Declared platforms
1
Static risk flags
2
Last source update
2026-08-24
Source checked
2026-08-25

Decision brief

What it does: where it fits

Guide creating Claude Code hooks with security-first design. Use for validation and enforcement.

Best for

    Not for

    • Hook not firing Verify hook pattern matches the event. Check hook logs for errors
    • Syntax errors Validate JSON/Python syntax before deployment

    Compatibility matrix

    Platform support, with evidence labels

    PlatformStatusEvidenceWhat to check
    CodexNot declaredNo explicit evidencePortability before use
    Claude CodeDeclaredSource recordInstall path and trigger
    CursorNot declaredNo explicit evidencePortability before use
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    Installation

    Inspect first. Install second.

    The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

    Source-detected install commandSource
    npx skills add https://github.com/athola/claude-night-market --skill "plugins/abstract/skills/hook-authoring"
    Safe inspection promptEditorial

    Inspect the Agent Skill "hook-authoring" from https://github.com/athola/claude-night-market/blob/90037391d2db6536f67a7ccc8dee7c6819f170b7/plugins/abstract/skills/hook-authoring/SKILL.md at commit 90037391d2db6536f67a7ccc8dee7c6819f170b7. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

    Workflow

    What the source asks the agent to do

    1. 01

      Quick Start

      Create a simple logging hook in .claude/settings.json:

      Create a simple logging hook in .claude/settings.json:Note: Use string matchers ("Bash") not object matchers ({"toolName": "Bash"}).Verification: Run the command with --help flag to verify availability.
    2. 02

      When NOT To Use

      Auditing a hook that already exists (use abstract:hooks-eval)

      Auditing a hook that already exists (use abstract:hooks-eval)Choosing where a hook should live (use abstract:hook-scope-guide)Authoring a skill rather than a hook (use abstract:skill-authoring)
    3. 03

      Hook Authoring Guide

      Hooks are event interceptors that allow you to extend Claude Code and Claude Agent SDK behavior by executing custom logic at specific points in the agent lifecycle. They enable validation before tool use, logging after actions, context injection, workflow automation, and securit…

      PreToolUse: Validate, filter, or transform tool inputs before execution; inject context (2.1.9+)PostToolUse: Log, analyze, or modify tool outputs after executionUserPromptSubmit: Inject context or filter user messages before processing
    4. 04

      Key Capabilities

      New in 2.1.9: PreToolUse hooks can now return additionalContext to inject information before a tool executes. This enables patterns like cache hints, security warnings, or relevant context injection.

      PreToolUse: Validate, filter, or transform tool inputs before execution; inject context (2.1.9+)PostToolUse: Log, analyze, or modify tool outputs after executionUserPromptSubmit: Inject context or filter user messages before processing
    5. 05

      Your First Hook (JSON - Claude Code)

      Create a simple logging hook in .claude/settings.json:

      Create a simple logging hook in .claude/settings.json:Note: Use string matchers ("Bash") not object matchers ({"toolName": "Bash"}).Verification: Run the command with --help flag to verify availability.

    Permission review

    Static risk signals and limitations

    Runs scripts

    medium · line 82

    The documentation asks the agent to run terminal commands or scripts.

    *Verification:** Run the command with `--help` flag to verify availability.

    Runs scripts

    medium · line 105

    The documentation asks the agent to run terminal commands or scripts.

    *Verification:** Run the command with `--help` flag to verify availability.

    Network access

    medium · line 280

    The documentation includes network, browsing, or remote request actions.

    "url": "https://my-service.example.com/hooks/validate-bash"

    Evidence record

    Why each signal appears

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score96/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars330SourceRepository attention, not individual Skill quality
    Compatibility1 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

    Pinned source

    Provenance and original SKILL.md

    Repository
    athola/claude-night-market
    Skill path
    plugins/abstract/skills/hook-authoring/SKILL.md
    Commit
    90037391d2db6536f67a7ccc8dee7c6819f170b7
    License
    MIT
    Collected
    2026-08-25
    Default branch
    master
    View the original SKILL.md

    When NOT To Use

    • Auditing a hook that already exists (use abstract:hooks-eval)
    • Choosing where a hook should live (use abstract:hook-scope-guide)
    • Authoring a skill rather than a hook (use abstract:skill-authoring)

    Table of Contents

    Hook Authoring Guide

    Overview

    Hooks are event interceptors that allow you to extend Claude Code and Claude Agent SDK behavior by executing custom logic at specific points in the agent lifecycle. They enable validation before tool use, logging after actions, context injection, workflow automation, and security enforcement.

    This skill teaches you how to write effective, secure, and performant hooks for both declarative JSON (Claude Code) and programmatic Python (Claude Agent SDK) use cases.

    Key Capabilities

    • PreToolUse: Validate, filter, or transform tool inputs before execution; inject context (2.1.9+)
    • PostToolUse: Log, analyze, or modify tool outputs after execution
    • UserPromptSubmit: Inject context or filter user messages before processing
    • Stop/SubagentStop: Cleanup, final reporting, or result aggregation
    • TeammateIdle/TaskCompleted: Multi-agent coordination and orchestration (2.1.33+)
    • PreCompact: State preservation before context window compaction

    New in 2.1.9: PreToolUse hooks can now return additionalContext to inject information before a tool executes. This enables patterns like cache hints, security warnings, or relevant context injection.

    Quick Start

    Your First Hook (JSON - Claude Code)

    Create a simple logging hook in .claude/settings.json:

    {
      "PostToolUse": [
        {
          "matcher": "Bash",
          "hooks": [{
            "type": "command",
            "command": "echo \"$(date): Executed $(jq -r '.tool_name')\" >> ~/.claude/audit.log"
          }]
        }
      ]
    }
    

    Note: Use string matchers ("Bash") not object matchers ({"toolName": "Bash"}).

    Verification: Run the command with --help flag to verify availability.

    This logs every Bash command execution with a timestamp.

    Your First Hook (Python - Claude Agent SDK)

    Create a validation hook using the SDK:

    from claude_agent_sdk import AgentHooks
    
    
    class ValidationHooks(AgentHooks):
        async def on_pre_tool_use(self, tool_name: str, tool_input: dict) -> dict | None:
            """Validate tool inputs before execution."""
            if tool_name == "Bash":
                command = tool_input.get("command", "")
                if "rm -rf /" in command:
                    raise ValueError("Dangerous command blocked by hook")
    
            # Return None to proceed unchanged, or modified dict to transform
            return None
    

    Verification: Run the command with --help flag to verify availability.

    Hook Event Types

    Quick reference for all supported hook events:

    EventTrigger PointParametersCommon Use Cases
    PreToolUseBefore tool executiontool_name, tool_inputValidation, filtering, input transformation
    PostToolUseAfter tool executiontool_name, tool_input, tool_outputLogging, metrics, output transformation
    UserPromptSubmitUser sends messagemessageContext injection, content filtering
    PermissionRequestPermission dialog showntool_name, tool_inputAuto-approve/deny with custom logic
    NotificationClaude Code sends notificationmessageCustom notification handling
    StopAgent completesreason, resultFinal cleanup, summary reports
    SubagentStopSubagent completessubagent_id, resultResult processing, aggregation
    TeammateIdleTeammate agent becomes idleagent_id, session_idWork assignment, load balancing (2.1.33+)
    TaskCompletedTask finishes executiontask_id, resultCoordination, chaining, reporting (2.1.33+)
    PreCompactBefore context compactcontext_sizeState preservation, checkpointing
    SessionStartSession starts/resumessession_id, source, agent_typeInitialization, context loading
    SessionEndSession terminatessession_idCleanup, final logging
    WorktreeCreateAgent worktree createdworktree_path, session_idCustom VCS setup, symlink .venv, pre-populate caches (2.1.50+)
    WorktreeRemoveAgent worktree removedworktree_path, session_idCleanup temp files, teardown worktree-scoped resources (2.1.50+)

    SessionStart Input Schema (Claude Code 2.1.2+)

    The SessionStart hook receives JSON input via stdin with these fields:

    {
      "session_id": "abc123",
      "source": "startup",
      "agent_type": "my-agent"
    }
    

    Fields: source is one of "startup", "resume", "clear", or "compact". agent_type is populated when the --agent flag is used.

    agent_type field: When Claude Code is launched with --agent my-agent, this field contains the agent name, enabling agent-specific initialization:

    # Python example: Agent-aware SessionStart hook
    input_data = json.loads(sys.stdin.read())
    agent_type = input_data.get("agent_type", "")
    
    if agent_type in ["code-reviewer", "quick-query"]:
        # Skip heavy context injection for lightweight agents
        print(json.dumps({"hookSpecificOutput": {"additionalContext": "Minimal context"}}))
    else:
        # Full initialization for implementation agents
        print(json.dumps({"hookSpecificOutput": {"additionalContext": full_context}}))
    
    # Bash example: Agent-aware SessionStart hook
    HOOK_INPUT=$(cat)
    AGENT_TYPE=$(echo "$HOOK_INPUT" | jq -r '.agent_type // empty')
    
    case "$AGENT_TYPE" in
        code-reviewer|quick-query)
            echo '{"hookSpecificOutput": {"additionalContext": "Minimal context"}}'
            ;;
        *)
            echo '{"hookSpecificOutput": {"additionalContext": "Full context"}}'
            ;;
    esac
    

    Hooks in Frontmatter (Claude Code 2.1.0+)

    New in 2.1.0: Define hooks directly in skill, command, or agent frontmatter. These hooks are scoped to the component's lifecycle.

    Skill/Command/Agent Frontmatter Hooks

    ---
    name: validated-skill
    description: Skill with lifecycle hooks
    hooks:
      PreToolUse:
        - matcher: "Bash"
          command: "./validate-command.sh"
          once: true  # NEW: Run only once per session
        - matcher: "Write|Edit"
          command: "./pre-edit-check.sh"
      PostToolUse:
        - matcher: "Write|Edit"
          command: "./format-on-save.sh"
      Stop:
        - command: "./cleanup-and-report.sh"
    ---
    

    The once: true Configuration

    New in 2.1.0: Use once: true to execute a hook only once per session, ideal for:

    • One-time setup/initialization
    • Resource allocation that shouldn't repeat
    • Session-level configuration
    hooks:
      PreToolUse:
        - matcher: "Bash"
          command: "./setup-environment.sh"
          once: true  # Runs only on first Bash call
      SessionStart:
        - command: "./initialize-session.sh"
          once: true  # Runs only once at session start
    

    Frontmatter vs Settings Hooks

    AspectFrontmatter HooksSettings Hooks
    ScopeComponent lifecycleGlobal/project
    LocationIn skill/agent/commandsettings.json
    PersistenceActive only when component runsAlways active
    Use caseComponent-specific validationCross-cutting concerns

    PreToolUse updatedInput (2.1.0 Fix)

    PreToolUse hooks can now return updatedInput when returning ask permission decision, enabling hooks to act as middleware while still requesting user consent:

    {
      "decision": "ask",
      "updatedInput": {
        "command": "modified-command --safe-flag"
      }
    }
    

    Claude Code vs SDK

    JSON Hooks (Claude Code)

    Declarative configuration in .claude/settings.json, project .claude/settings.json, or plugin hooks/hooks.json:

    {
      "PreToolUse": [
        {
          "matcher": "Edit",
          "hooks": [{
            "type": "command",
            "command": "echo 'WARNING: Editing production file' >&2"
          }]
        }
      ]
    }
    

    Important: Use string matchers (regex patterns), not object matchers. The object format {"toolName": "Edit"} is deprecated.

    Matcher patterns:

    • "Edit" - Match single tool
    • "Read|Write|Edit" - Match multiple tools (regex OR)
    • ".*" - Match all tools

    Verification: Run the command with --help flag to verify availability.

    Pros: Simple, no code required, easy to version control Cons: Limited logic capabilities, shell command only

    HTTP Hooks (Claude Code 2.1.63+)

    New in 2.1.63: Hooks can POST JSON to a URL and receive JSON responses instead of running shell commands. Use "type": "http" with a "url" field:

    {
      "PreToolUse": [
        {
          "matcher": "Bash",
          "hooks": [{
            "type": "http",
            "url": "https://my-service.example.com/hooks/validate-bash"
          }]
        }
      ]
    }
    

    The hook POSTs the standard hook input as JSON and expects a standard hook response JSON body.

    When to use HTTP hooks over command hooks:

    • Enterprise environments where shell execution is restricted
    • Centralized hook logic shared across teams via a web service
    • Sandboxed or containerized setups without local script access
    • Integration with external validation/logging services

    Pros: No local scripts needed, centralized logic, works in sandboxed environments Cons: Network latency, requires running HTTP service, external dependency

    Python SDK Hooks

    Programmatic callbacks using AgentHooks base class:

    from claude_agent_sdk import AgentHooks
    
    
    class MyHooks(AgentHooks):
        async def on_pre_tool_use(self, tool_name: str, tool_input: dict) -> dict | None:
            # Complex validation logic
            if self._is_dangerous(tool_input):
                raise ValueError("Operation blocked")
            return None  # or return modified input
    

    Verification: Run the command with --help flag to verify availability.

    Pros: Full Python capabilities, complex logic, state management Cons: Requires Python, more complex setup

    Bash Permission Matching Notes

    Environment Variable Wrappers (2.1.38+)

    Permission rules now correctly match commands prefixed with environment variable assignments. Before 2.1.38, NODE_ENV=production npm test would not match a rule for Bash(npm *).

    # These now all match `Bash(npm *)`:
    npm test
    NODE_ENV=production npm test
    FORCE_COLOR=1 CI=true npm test
    

    When writing PreToolUse hooks that inspect bash commands, be aware that the permission system strips env var prefixes for matching, but your hook receives the full command string including prefixes.

    Heredoc Delimiter Security (2.1.38+)

    Claude Code now validates heredoc delimiters to prevent command smuggling. The recommended pattern <<'EOF' (single-quoted) remains the safest approach. Always use single-quoted delimiters in heredoc patterns to prevent variable expansion.

    Security Essentials

    Critical Security Rules

    1. Input Validation: Always validate tool inputs before processing
    2. No Secret Logging: Never log API keys, tokens, passwords, or credentials
    3. Sandbox Awareness: Respect sandbox boundaries, don't escape. Note: .claude/skills/ is read-only in sandbox mode (2.1.38+)
    4. Fail-Safe Defaults: Return None on error instead of blocking the agent
    5. Rate Limiting: Prevent hook abuse from malicious or buggy code
    6. Injection Prevention: Sanitize all logged content to prevent log injection

    Example: Secure Logging Hook

    import re
    from claude_agent_sdk import AgentHooks
    
    
    class SecureLoggingHooks(AgentHooks):
        # Patterns that might contain secrets
        SECRET_PATTERNS = [
            r"api[_-]?key",
            r"password",
            r"token",
            r"secret",
            r"credential",
            r"auth",
        ]
    
        def _sanitize_output(self, text: str) -> str:
            """Remove potential secrets from log output."""
            for pattern in self.SECRET_PATTERNS:
                text = re.sub(
                    rf'({pattern}["\s:=]+)([^\s,}}]+)',
                    r"\1***REDACTED***",
                    text,
                    flags=re.IGNORECASE,
                )
            return text
    
        async def on_post_tool_use(
            self, tool_name: str, tool_input: dict, tool_output: str
        ) -> str | None:
            """Log tool use with sanitization."""
            safe_output = self._sanitize_output(tool_output)
            # Log safe_output...
            return None  # Don't modify output
    

    Verification: Run the command with --help flag to verify availability.

    See modules/testing-hooks.md for detailed security guidance.

    Performance Guidelines

    Performance Best Practices

    1. Non-Blocking: Use async/await properly, don't block the event loop
    2. Timeout Handling: Hook timeout is 10 minutes (increased from 60s in 2.1.3). For most hooks, aim for < 30s; use extended time only for CI/CD integration, complex validation, or external API calls
    3. Efficient Logging: Batch writes, use async I/O
    4. Memory Management: Don't accumulate unbounded state
    5. Fail Fast: Quick validation, early returns, avoid expensive operations

    Example: Efficient Hook

    import asyncio
    from claude_agent_sdk import AgentHooks
    
    
    class EfficientHooks(AgentHooks):
        def __init__(self):
            self._log_queue = asyncio.Queue()
            self._log_task = None
    
        async def on_pre_tool_use(self, tool_name: str, tool_input: dict) -> dict | None:
            # Quick validation only
            if not self._is_valid_input(tool_input):
                raise ValueError("Invalid input")
            return None
    
        async def on_post_tool_use(
            self, tool_name: str, tool_input: dict, tool_output: str
        ) -> str | None:
            # Queue log entry without blocking
            await self._log_queue.put({"tool": tool_name, "timestamp": time.time()})
            return None
    
        def _is_valid_input(self, tool_input: dict) -> bool:
            """Fast validation check."""
            # Simple checks only, < 10ms
            return len(str(tool_input)) < 1_000_000
    

    Verification: Run the command with --help flag to verify availability.

    See modules/performance-guidelines.md for detailed optimization techniques.

    Scope Selection

    Choose the right location for your hooks based on audience and purpose.

    Important: Auto-Loading Behavior

    hooks/hooks.json is automatically loaded when a plugin is enabled. Do NOT add "hooks": "./hooks/hooks.json" to plugin.json - this causes duplicate load errors. Only use the hooks field for additional hook files beyond the standard location.

    Decision Framework

    **Verification:** Run the command with `--help` flag to verify availability.
    Is this hook part of a plugin's core functionality?
    ├─ YES → Plugin hooks (hooks/hooks.json in plugin)
    └─ NO ↓
    
    Should all team members on this project have this hook?
    ├─ YES → Project hooks (.claude/settings.json)
    └─ NO ↓
    
    Should this hook apply to all my Claude sessions?
    ├─ YES → Global hooks (~/.claude/settings.json)
    └─ NO → Reconsider if you need a hook at all
    

    Verification: Run the command with --help flag to verify availability.

    Scope Comparison

    ScopeLocationAudienceCommitted?Example Use Case
    Pluginhooks/hooks.jsonPlugin usersYes (with plugin)YAML validation in YAML plugin
    Project.claude/settings.jsonTeam membersYes (in repo)Block production config edits
    Global~/.claude/settings.jsonOnly youNeverPersonal audit logging

    See modules/scope-selection.md for detailed scope decision guidance.

    Common Patterns

    Validation Hook

    Block dangerous operations before execution:

    async def on_pre_tool_use(self, tool_name: str, tool_input: dict) -> dict | None:
        if tool_name == "Bash":
            command = tool_input.get("command", "")
    
            # Block dangerous patterns
            if any(pattern in command for pattern in ["rm -rf /", ":(){ :|:& };:"]):
                raise ValueError(f"Dangerous command blocked: {command}")
    
            # Block production access
            if "production" in command and not self._has_approval():
                raise ValueError("Production access requires approval")
    
        return None
    

    Verification: Run the command with --help flag to verify availability.

    Logging Hook

    Audit all tool operations:

    async def on_post_tool_use(
        self, tool_name: str, tool_input: dict, tool_output: str
    ) -> str | None:
        await self._log_entry(
            {
                "timestamp": datetime.now().isoformat(),
                "tool": tool_name,
                "input_size": len(str(tool_input)),
                "output_size": len(tool_output),
                "success": True,
            }
        )
        return None
    

    Verification: Run the command with --help flag to verify availability.

    Context Injection Hook

    Add relevant context before user prompts:

    async def on_user_prompt_submit(self, message: str) -> str | None:
        # Inject project-specific context
        context = await self._load_project_context()
        enhanced_message = f"{context}\n\n{message}"
        return enhanced_message
    

    PreToolUse Context Injection (Claude Code 2.1.9+)

    Inject context before a tool executes using additionalContext:

    #!/usr/bin/env python3
    """PreToolUse hook that injects context before WebFetch."""
    
    import json
    import sys
    
    
    def main():
        payload = json.load(sys.stdin)
        tool_name = payload.get("tool_name", "")
    
        if tool_name == "WebFetch":
            url = payload.get("tool_input", {}).get("url", "")
            # Check cache or knowledge base
            cached = lookup_knowledge_base(url)
            if cached:
                print(
                    json.dumps(
                        {
                            "hookSpecificOutput": {
                                "hookEventName": "PreToolUse",
                                "additionalContext": f"Relevant cached info: {cached}",
                            }
                        }
                    )
                )
        sys.exit(0)
    
    
    if __name__ == "__main__":
        main()
    

    This pattern is useful for: cache hints before web requests, security warnings before risky operations, and injecting relevant project context before file operations.

    Testing Hooks

    Unit Testing

    import pytest
    from my_hooks import ValidationHooks
    
    
    @pytest.mark.asyncio
    async def test_dangerous_command_blocked():
        hooks = ValidationHooks()
    
        with pytest.raises(ValueError, match="Dangerous command"):
            await hooks.on_pre_tool_use("Bash", {"command": "rm -rf /"})
    
    
    @pytest.mark.asyncio
    async def test_safe_command_allowed():
        hooks = ValidationHooks()
        result = await hooks.on_pre_tool_use("Bash", {"command": "ls -la"})
        assert result is None  # Allows execution
    

    Verification: Run pytest -v from to verify.

    See modules/testing-hooks.md for detailed testing strategies.

    Module References

    For detailed guidance on specific topics:

    • Hook Types: modules/hook-types.md - Detailed event signatures and parameters
    • SDK Callbacks: modules/sdk-callbacks.md - Python SDK implementation patterns
    • Security Patterns: modules/testing-hooks.md - detailed security guidance
    • Performance Guidelines: modules/performance-guidelines.md - Optimization techniques
    • Scope Selection: modules/scope-selection.md - Choosing plugin/project/global
    • Testing Hooks: modules/testing-hooks.md - Testing strategies and fixtures
    • Observability Warnings: modules/observability-warnings.md - Copy-pasteable resolution pattern for binary-actionable drift hooks

    Tools

    • hook_validator.py: Validate hook structure and syntax (in scripts/)

    Related Skills

    • hook-scope-guide: Decision framework for hook placement (existing)
    • modular-skills: Design patterns for skill architecture
    • skills-eval: Quality assessment and improvement framework

    Next Steps

    1. Choose your hook type (JSON vs SDK) based on complexity needs
    2. Select the appropriate scope (plugin/project/global)
    3. Implement following security and performance best practices
    4. Test thoroughly with unit and integration tests
    5. Validate using hook_validator.py before deployment

    Environment Variables (Claude Code 2.1.2+)

    FORCE_AUTOUPDATE_PLUGINS

    Forces plugin auto-update even when the main Claude Code auto-updater is disabled.

    Use cases:

    • CI/CD pipelines that need latest plugin versions
    • Development environments testing plugin updates
    • Controlled update rollouts in enterprise settings
    # Enable forced plugin updates
    export FORCE_AUTOUPDATE_PLUGINS=1
    claude
    
    # Or inline
    FORCE_AUTOUPDATE_PLUGINS=1 claude --agent my-agent
    

    Note: This only affects plugin updates, not Claude Code core updates.

    References

    Hook Exit Codes

    Hooks communicate decisions to Claude Code via exit codes:

    Exit CodeMeaningstdoutstderr
    0Success/allowShown to Claude as system contextIgnored
    2Block/denyIgnoredShown to user as explanation (2.1.39+ fix)
    OtherErrorIgnoredShown to user as error message

    Blocking with Exit Code 2 (2.1.39+)

    Use exit code 2 to block an action and display a message to the user:

    #!/bin/bash
    # Example: Block force pushes with user-facing message
    command=$(echo "$1" | jq -r '.tool_input.command // empty')
    if echo "$command" | grep -q 'push.*--force'; then
      echo "Force push blocked: use --force-with-lease instead" >&2
      exit 2
    fi
    exit 0
    

    Important: Before Claude Code 2.1.39, stderr from exit code 2 was silently swallowed (#10964). Users would see a generic "hook error" instead of the custom message. This is now fixed: stderr is properly displayed to the user.

    Plugin hooks: Before 2.1.39, plugin-installed hooks had a separate code path that also failed to show stderr for exit code 2 (#10412). Both plugin and project hooks now work correctly.

    Troubleshooting

    Common Issues

    Hook not firing Verify hook pattern matches the event. Check hook logs for errors

    Syntax errors Validate JSON/Python syntax before deployment

    Permission denied Check hook file permissions and ownership

    Hook blocking message not shown (pre-2.1.39) If using exit code 2 to block with a user-facing message and the message isn't appearing, upgrade to Claude Code 2.1.39+. In older versions, use exit 0 with stdout as a workaround.

    Exit Criteria

    • The authored hook file exists at a valid scope location (hooks/hooks.json, .claude/settings.json, or ~/.claude/settings.json) with correct JSON or Python syntax.
    • The hook fires on the target event: a test invocation of the matching tool call triggers the hook command or callback without error.
    • The hook contains no secret logging: no field names matching api[_-]?key, password, token, secret, or credential appear in log output paths.
    • Blocking hooks exit with code 2 and write the user-facing explanation to stderr (not stdout).
    • If abstract:validate-hook is available, it exits 0 on the authored hook file.

    Frequently asked questions

    What to verify before installation and use

    What does the hook-authoring source document cover?

    Guide creating Claude Code hooks with security-first design. Use for validation and enforcement.

    How do I install hook-authoring?

    The source record exposes this install command: npx skills add https://github.com/athola/claude-night-market --skill "plugins/abstract/skills/hook-authoring". Inspect the command and pinned source before running it.

    Which Agent platforms does the source record declare?

    The pinned source record declares support for: claude code.

    Which permission-related actions were detected?

    Static rules flagged exec-script, network in the source; the page lists the matching lines and excerpts.

    Alternatives

    Compare before choosing

    Computed 9324,921

    alirezarezvani/claude-skills

    chaos-engineering

    Use when planning, running, or learning from chaos engineering experiments. Triggers on "chaos experiment", "fault injection", "gameday", "resilience test", "blast radius", "steady state", "abort criteria", "Chaos Toolkit", "Chaos Mesh", "Litmus", "Gremlin", "AWS FIS", or any deliberate failure-injection question. Ships experiment designer, blast-radius calculator, and postmortem generator (all stdlib Python), 4 references on chaos principles + experiment design + attack taxonomy + tooling lands

    Computed 9024,921

    alirezarezvani/claude-skills

    kubernetes-operator

    Use when building a Kubernetes Operator — custom controllers that reconcile CRD state. Triggers on "build an operator", "CRD design", "reconcile loop", "controller-runtime", "kubebuilder", "operator-sdk", "metacontroller", "KOPF", "operator capability levels", or "custom resource". Ships CRD validator, reconcile-loop linter, and OperatorHub capability auditor (all stdlib Python), 4 references on the operator pattern + CRD design + reconcile patterns + tooling landscape, and a /operator-audit sla

    Computed 9764

    Jamie-BitFlight/claude_skills

    standards-for-python-development

    Shared Python 3.11+ development standards covering type safety (ty, native generics, Protocol, TypeIs), layered architecture, error handling, performance, identifier naming, UI/CLI patterns (Rich/Typer), testing requirements (pytest, 80% coverage, TDD), and quality gates. Activates when any Python skill or agent needs to apply shared standards for implementation, code review, refactoring, or test authoring.

    Computed 9618,905

    teng-lin/notebooklm-py

    notebooklm

    Complete API for Google NotebookLM - full programmatic access including features not in the web UI. Create notebooks, add sources, generate all artifact types, download in multiple formats. Activates on explicit /notebooklm or intent like "create a podcast about X"