
Hooks Eval
- 92 installs
- 325 repo stars
- Updated August 2, 2026
- athola/claude-night-market
hooks-eval is an agent skill that scores Claude Code hooks on a 100-point security-first rubric with explicit quality gates.
About
hooks-eval is a checker skill from the Claude Night Market skills-eval line that gives solo builders a repeatable way to grade Claude Code hooks before they go live. Instead of eyeballing a PreToolUse or PostToolUse script in chat, you run it through a 100-point Multi-Criteria Decision Analysis rubric where security carries the largest share—30 points of deductions for critical injection and eval issues down to low-severity leakage patterns. Performance is scored as its own 25-point pillar so slow or chatty hooks do not slip through on aesthetics alone. The readme ties scoring to normalized metrics and penalty-based aggregation so results stay comparable across repos. Use it when you are iterating agent-tooling under build, during ship-phase review or security hardening, or when you re-check hooks after config changes in operate. It is procedural knowledge packaged as evaluation criteria, not a hosted scanner—your agent applies the tables and gates locally against the hook source you provide.
- 100-point MCDA scoring rubric with documented vector normalization and stakeholder-weight methodology
- Security analysis block (30 points) with Critical −15, High −8, Medium −4, and Low −1 per finding
- Performance analysis block (25 points) as a dedicated weighted criterion alongside security
- Security checklist rows for dynamic eval with user input, command injection, unvalidated paths, and embedded secrets
- Aligns with the night-market skills-eval multi-metric evaluation methodology and sensitivity analysis guidance
Hooks Eval by the numbers
- 92 all-time installs (skills.sh)
- Ranked #465 of 1,352 Code Review & Quality skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/athola/claude-night-market --skill hooks-evalAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 92 |
|---|---|
| repo stars | ★ 325 |
| Security audit | 3 / 3 scanners passed |
| Last updated | August 2, 2026 |
| Repository | athola/claude-night-market ↗ |
What it does
Score Claude Code hook scripts against a security-first 100-point rubric before you enable them in your agent loop.
Who is it for?
Best when you add Claude Code hooks and want MCDA-weighted security and performance review before enabling automation.
Skip if: Skip if you have no Claude Code hook lifecycle, or anyone and only needs generic app pen-testing unrelated to agent hook scripts.
When should I use this skill?
Use when evaluating Claude Code hook scripts for security vulnerabilities, performance risk, and overall quality gates before enabling or merging them.
What you get
You get a normalized score, categorized security deductions, and a clear pass/fail against quality gates so you can fix hooks or ship them with justified confidence.
- Weighted hook score out of 100 with security and performance breakdown
- Checklist-mapped findings with severity and point deductions
- Pass/fail recommendation against documented quality gates
By the numbers
- 100-point total scoring system
- 30-point security analysis weight with per-severity deductions
- 25-point performance analysis weight
Files
Table of Contents
- Overview
- Key Capabilities
- Core Components
- Quick Reference
- Hook Event Types
- Hook Callback Signature
- Return Values
- Quality Scoring (100 points))
- Detailed Resources
- Basic Evaluation Workflow
- Integration with Other Tools
- Related Skills
Hooks Evaluation Framework
Overview
This skill provides a detailed framework for evaluating, auditing, and implementing Claude Code hooks across all scopes (plugin, project, global) and both JSON-based and programmatic (Python SDK) hooks.
Key Capabilities
- Security Analysis: Vulnerability scanning, dangerous pattern detection, injection prevention
- Performance Analysis: Execution time benchmarking, resource usage, optimization
- Compliance Checking: Structure validation, documentation requirements, best practices
- SDK Integration: Python SDK hook types, callbacks, matchers, and patterns
Core Components
| Component | Purpose |
|---|---|
| Hook Types Reference | Complete SDK hook event types and signatures |
| Evaluation Criteria | Scoring system and quality gates |
| Security Patterns | Common vulnerabilities and mitigations |
| Performance Benchmarks | Thresholds and optimization guidance |
Quick Reference
Hook Event Types
HookEvent = Literal[
"PreToolUse", # Before tool execution
"PostToolUse", # After tool execution
"UserPromptSubmit", # When user submits prompt
"Stop", # When stopping execution
"SubagentStop", # When a subagent stops
"TeammateIdle", # When teammate agent becomes idle (2.1.33+)
"TaskCompleted", # When a task finishes execution (2.1.33+)
"PreCompact" # Before message compaction
]Verification: Run the command with --help flag to verify availability.
Note: Python SDK does not support SessionStart, SessionEnd, or Notification hooks due to setup limitations. However, plugins can define SessionStart hooks via hooks.json using shell commands (e.g., leyline's detect-git-platform.sh).
Plugin-Level hooks.json
Plugins can declare hooks via "hooks": "./hooks/hooks.json" in plugin.json. The evaluator validates:
- Referenced hooks.json exists and is valid JSON
- Shell commands referenced in hooks exist and are executable
- Hook matchers use valid event types
Hook Callback Signature
async def my_hook(
input_data: dict[str, Any], # Hook-specific input
tool_use_id: str | None, # Tool ID (for tool hooks)
context: HookContext # Additional context
) -> dict[str, Any]: # Return decision/messages
...Verification: Run the command with --help flag to verify availability.
Return Values
return {
"hookSpecificOutput": {
"hookEventName": "PreToolUse", # Match hook type
"permissionDecision": "deny", # Optional: block action
"permissionDecisionReason": "...", # Reason for denial
"additionalContext": "...", # Optional: context added
}
}Verification: Run the command with --help flag to verify availability.
Quality Scoring (100 points)
| Category | Points | Focus |
|---|---|---|
| Security | 30 | Vulnerabilities, injection, validation |
| Performance | 25 | Execution time, memory, I/O |
| Compliance | 20 | Structure, documentation, error handling |
| Reliability | 15 | Timeouts, idempotency, degradation |
| Maintainability | 10 | Code structure, modularity |
Detailed Resources
- SDK Hook Types: See
modules/sdk-hook-types.mdfor complete Python SDK type definitions, patterns, and examples - Evaluation Criteria: See
modules/evaluation-criteria.mdfor detailed scoring rubric and quality gates - Security Patterns: See
modules/sdk-hook-types.mdfor vulnerability detection and mitigation - Performance Guide: See
modules/evaluation-criteria.mdfor benchmarking and optimization
Basic Evaluation Workflow
# 1. Run detailed evaluation
/hooks-eval --detailed
# 2. Focus on security issues
/hooks-eval --security-only --format sarif
# 3. Benchmark performance
/hooks-eval --performance-baseline
# 4. Check compliance
/hooks-eval --compliance-reportVerification: Run the command with --help flag to verify availability.
Integration with Other Tools
# Complete plugin evaluation pipeline
/hooks-eval --detailed # Evaluate all hooks
/analyze-hook hooks/specific.py # Deep-dive on one hook
/validate-plugin . # Validate overall structureVerification: Run the command with --help flag to verify availability.
Related Skills
abstract:hook-scope-guide- Decide where to place hooks (plugin/project/global)abstract:hook-authoring- Write hook rules and patternsabstract:validate-plugin- Validate complete plugin structure
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 Evaluation Criteria
Detailed scoring rubric and quality gates for hook evaluation.
Mathematical Foundation
This evaluation framework follows Multi-Criteria Decision Analysis (MCDA) best practices:
- Normalization: Vector normalization for scale invariance (full methodology)
- Weighting: Security-first weights with stakeholder validation
- Aggregation: Weighted sum with penalty-based security scoring
- Validation: Sensitivity analysis on non-security weights
Documentation: See Multi-Metric Evaluation Methodology for complete mathematical foundation.
Scoring System (100 points total)
Security Analysis (30 points)
Vulnerability Detection:
- Critical vulnerabilities: -15 points each
- High-risk issues: -8 points each
- Medium-risk issues: -4 points each
- Low-risk issues: -1 point each
Security Checklist:
| Check | Severity | Points Lost |
|---|---|---|
| Dynamic code evaluation with user input | Critical | -15 |
| Command injection vulnerability | Critical | -15 |
| Unvalidated file path access | High | -8 |
| Secrets/credentials in code | High | -8 |
| Missing input validation | Medium | -4 |
| Overly permissive patterns | Medium | -4 |
| No rate limiting | Low | -1 |
| Verbose error messages exposing internals | Low | -1 |
Performance Analysis (25 points)
| Metric | Max Points | Criteria |
|---|---|---|
| Execution time efficiency | 10 | PreToolUse <100ms, PostToolUse <200ms |
| Memory usage optimization | 8 | <50MB for simple hooks, <100MB for complex |
| I/O operation efficiency | 4 | Minimal file/network operations |
| Resource cleanup | 3 | Proper cleanup of handles, connections |
Performance Thresholds:
pre_tool_use:
excellent: <50ms
good: <100ms
acceptable: <200ms
poor: >200ms
post_tool_use:
excellent: <100ms
good: <200ms
acceptable: <500ms
poor: >500ms
memory:
excellent: <25MB
good: <50MB
acceptable: <100MB
poor: >100MBCompliance Analysis (20 points)
| Aspect | Max Points | Requirements |
|---|---|---|
| Structure compliance | 8 | Valid JSON/Python, correct schema |
| Documentation completeness | 6 | Purpose, parameters, return values documented |
| Error handling | 4 | All exceptions caught, meaningful messages |
| Best practices | 2 | Follows hook authoring guidelines |
Structure Requirements:
- JSON hooks: Valid JSON schema with required fields
- Python hooks: Type hints, async/await patterns
- Matcher patterns: Valid regex, appropriate scope
Reliability Analysis (15 points)
| Aspect | Max Points | Requirements |
|---|---|---|
| Error handling robustness | 6 | Graceful handling of all error conditions |
| Timeout management | 4 | Appropriate timeouts configured |
| Idempotency | 3 | Safe to retry without side effects |
| Graceful degradation | 2 | Falls back safely on failure |
Reliability Checklist:
- [ ] Hook returns valid response on all code paths
- [ ] Exceptions are caught and handled
- [ ] Timeout is configured appropriately
- [ ] Hook can be called multiple times safely
- [ ] Failure doesn't break agent operation
Maintainability (10 points)
| Aspect | Max Points | Requirements |
|---|---|---|
| Code structure | 4 | Clear, modular, single responsibility |
| Documentation clarity | 3 | Purpose and behavior well explained |
| Modularity | 2 | Reusable components, no duplication |
| Test coverage | 1 | Tests exist for key functionality |
Quality Levels
| Score | Level | Description |
|---|---|---|
| 91-100 | Excellent | Production-ready, follows all best practices |
| 76-90 | Good | Minor improvements suggested |
| 51-75 | Acceptable | Some issues requiring attention |
| 26-50 | Poor | Significant issues need addressing |
| 0-25 | Critical | Major security or reliability issues |
Quality Gates
Default thresholds for CI/CD integration:
quality_gates:
security_score: ">= 80"
performance_score: ">= 70"
compliance_score: ">= 85"
reliability_score: ">= 85"
overall_score: ">= 75"
max_critical_issues: 0
max_high_issues: 2Sensitivity Analysis Requirements
Security weights are non-negotiable, but other weights should be validated:
sensitivity_analysis:
# Security weights are fixed (non-negotiable)
fixed_weights: ["security_analysis"]
# Other weights tested for sensitivity
test_weights: ["performance", "compliance", "reliability", "maintainability"]
variation: 0.20 # ±20% weight variation
requirements:
stable_rankings: true # Rankings shouldn't change (except security)
critical_weights_identified: true # Document sensitive weightsSee Sensitivity Analysis for implementation details.
Gate Behaviors
| Gate | Failure Action |
|---|---|
security_score | Block deployment, require review |
performance_score | Warn, suggest optimization |
compliance_score | Block until documentation complete |
reliability_score | Block deployment |
max_critical_issues | Immediate block |
Issue Classification
Critical Issues (Immediate Action Required)
- Dynamic code evaluation with untrusted input
- Command injection vulnerabilities
- Credential exposure
- Unhandled exceptions that break agent
High Issues (Address Before Release)
- Missing input validation
- Performance exceeds thresholds
- Missing error handling
- Insecure file operations
Medium Issues (Address Soon)
- Missing documentation
- Suboptimal patterns
- Minor performance concerns
- Code style violations
Low Issues (Nice to Fix)
- Minor documentation gaps
- Formatting inconsistencies
- Optimization opportunities
- Enhanced logging suggestions
Evaluation Report Format
Summary Format
=== Hooks Evaluation Report ===
Plugin: {name} (v{version})
Scope: {scope}
Total hooks: {count} ({json_count} JSON, {python_count} Python)
=== Scores ===
Security: {score}/100 ({level})
Performance: {score}/100 ({level})
Compliance: {score}/100 ({level})
Reliability: {score}/100 ({level})
Maintainability: {score}/100 ({level})
────────────────────────────────
Overall: {score}/100 ({level})
=== Issues ===
Critical: {count}
High: {count}
Medium: {count}
Low: {count}Detailed Format
Includes per-hook breakdown:
=== Hook: {hook_path} ===
Type: {json|python}
Event: {PreToolUse|PostToolUse|...}
Matcher: {pattern|universal}
Security Issues:
[{severity}] Line {n}: {description}
Performance:
Estimated time: {ms}ms (threshold: {threshold}ms)
Memory usage: {mb}MB (threshold: {threshold}MB)
Recommendations:
1. {recommendation}
2. {recommendation}Customization
Per-Plugin Configuration
Create .hooks-eval.yaml in plugin root:
hooks_eval:
# Override security thresholds
security_thresholds:
critical_score: 80
high_score: 70
# Override performance thresholds
performance_thresholds:
pre_tool_use_max_ms: 100
post_tool_use_max_ms: 200
max_memory_mb: 50
# Compliance requirements
compliance_requirements:
require_documentation: true
require_error_handling: true
require_timeout_config: true
# Custom rules
custom_rules:
- name: "no-hardcoded-secrets"
pattern: "password|secret|token"
severity: "high"
- name: "require-shebang"
pattern: "^#!"
file_types: [".sh", ".py"]
severity: "medium"
# Excluded paths
exclude_paths:
- "hooks/experimental/*"
- "hooks/deprecated/*"Severity Overrides
Override default severity for specific patterns:
severity_overrides:
- pattern: "subprocess.run"
default_severity: "high"
override_severity: "medium"
reason: "Safe usage verified in review"Python SDK Hook Types
Complete reference for Claude Agent SDK hook types, callbacks, and matchers.
Hook Events
HookEvent
Supported hook event types in the Python SDK.
from typing import Literal
HookEvent = Literal[
"Setup", # Called when plugin installed/enabled
"SessionStart", # Called when session begins
"SessionEnd", # Called when session ends normally
"UserPromptSubmit", # Called when user submits a prompt
"PreToolUse", # Called before tool execution
"PostToolUse", # Called after tool execution
"PostToolUseFailure",# Called when tool execution fails (2.1.20+)
"PermissionRequest", # Called when permission dialog would appear
"Notification", # Called on system notification (2.1.20+)
"SubagentStart", # Called when subagent spawns (2.1.20+)
"SubagentStop", # Called when a subagent stops
"Stop", # Called when stopping execution
"TeammateIdle", # Called when teammate agent becomes idle (2.1.33+)
"TaskCompleted", # Called when a task finishes execution (2.1.33+)
"ConfigChange", # Called when config is modified (2.1.49+)
"InstructionsLoaded",# Called when instructions are loaded (2.1.33+)
"PreCompact", # Called before message compaction
"PostCompact", # Called after compaction (2.1.76+)
"WorktreeCreate", # Called when git worktree is created (2.1.50+)
"WorktreeRemove", # Called when git worktree is removed (2.1.50+)
"StopFailure", # Called on error (2.1.78+)
"TaskCreated", # Called when task created (2.1.84+)
"CwdChanged", # Called on working dir change (2.1.83+)
"FileChanged", # Called on file change (2.1.83+)
"Elicitation", # MCP elicitation request (2.1.76+)
"ElicitationResult", # MCP elicitation response (2.1.76+)
]SDK vs CLI availability: Most events work in both JSON hooks (CLI) and Python SDK hooks. PermissionRequest is CLI-only. Setup, SessionStart, SessionEnd, and Notification are CLI-only (JSON hooks). WorktreeCreate and WorktreeRemove are command-only hooks (no Python SDK callback). They do not support matchers.
Event Summary
| Event | Trigger | Blockable | Matcher |
|---|---|---|---|
Setup | Plugin installed/enabled | No | No |
SessionStart | Session begins | No | No |
SessionEnd | Session ends normally | No | No |
UserPromptSubmit | User submits input | No | No |
PreToolUse | Before any tool runs | Yes | Tool name |
PostToolUse | After tool completes | No | Tool name |
PostToolUseFailure | Tool execution fails | No | Tool name |
PermissionRequest | Permission dialog | Yes | Tool name |
SubagentStart | Subagent spawns | No | No |
SubagentStop | Subagent completes | No | No |
Stop | Agent stops | No | No |
TeammateIdle | Teammate idle | No | No |
TaskCompleted | Task finishes | No | No |
ConfigChange | Config modified | No | No |
InstructionsLoaded | Instructions loaded | No | No |
PreCompact | Before compaction | No | No |
PostCompact | After compaction | No | No |
WorktreeCreate | Worktree created | No | No |
WorktreeRemove | Worktree removed | No | No |
StopFailure | Error occurs | No | Error type |
TaskCreated | Task created | Yes | No |
CwdChanged | Directory changed | No | No |
FileChanged | File changed | No | Filename |
Elicitation | MCP elicitation | Yes | MCP server |
ElicitationResult | Elicitation response | Yes | MCP server |
Notable Version Changes
All hook events include agent_id and agent_type as of 2.1.69+.
| Version | Change |
|---|---|
| 2.1.69 | TeammateIdle/TaskCompleted support {"continue": false} for graceful shutdown |
| 2.1.69 | Plugin WorktreeCreate/WorktreeRemove hooks fire correctly (were silently ignored) |
| 2.1.71 | New tools: CronCreate, CronList, CronDelete appear in PreToolUse/PostToolUse |
| 2.1.72 | ExitWorktree tool added; lsof/pgrep/tput/ss/fd/fdfind auto-approved |
| 2.1.72 | Skill hook double-fire fixed; transcript_path correct for resumed sessions |
| 2.1.72 | Failed Read/WebFetch/Glob no longer cancel sibling tool calls (only Bash cascades) |
| 2.1.73 | SessionStart no longer double-fires on --resume/--continue |
| 2.1.73 | JSON-output hooks no longer inject spurious system-reminder messages |
| 2.1.74 | SessionEnd hooks timeout now configurable via CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS |
| 2.1.75 | Hook source displayed in permission prompts; async hook messages suppressed by default |
| 2.1.76 | Elicitation and ElicitationResult events for MCP servers |
| 2.1.76 | PostCompact event fires after context compaction |
| 2.1.77 | PreToolUse "allow" no longer bypasses deny rules (security fix) |
| 2.1.83 | CwdChanged and FileChanged events added |
| 2.1.84 | TaskCreated event (blockable); HTTP hooks can return worktree path |
| 2.1.85 | if field for conditional hook execution; PreToolUse can match AskUserQuestion |
Type Definitions
HookCallback
from typing import Any, Awaitable, Callable
HookCallback = Callable[
[dict[str, Any], str | None, HookContext],
Awaitable[dict[str, Any]]
]| Parameter | Type | Description |
|---|---|---|
input_data | dict[str, Any] | Hook-specific input data (varies by event) |
tool_use_id | `str \ | None` |
context | HookContext | Additional context information |
Returns: dict[str, Any] with optional fields: decision ("block"), systemMessage (str), hookSpecificOutput (dict).
HookMatcher
@dataclass
class HookMatcher:
matcher: str | None = None
hooks: list[HookCallback] = field(default_factory=list)
timeout: float | None = None # Default: 60s| Pattern | Matches |
|---|---|
"Bash" | Only Bash tool |
| `"Write\ | Edit"` |
None | All tools (universal matcher) |
Complete Usage Example
from claude_agent_sdk import query, ClaudeAgentOptions, HookMatcher, HookContext
from typing import Any
async def validate_bash_command(
input_data: dict[str, Any],
tool_use_id: str | None,
context: HookContext
) -> dict[str, Any]:
"""Block dangerous bash commands."""
if input_data['tool_name'] == 'Bash':
command = input_data['tool_input'].get('command', '')
if 'rm -rf /' in command:
return {
'hookSpecificOutput': {
'hookEventName': 'PreToolUse',
'permissionDecision': 'deny',
'permissionDecisionReason': 'Dangerous command blocked'
}
}
return {}
async def log_tool_use(
input_data: dict[str, Any],
tool_use_id: str | None,
context: HookContext
) -> dict[str, Any]:
"""Log all tool usage for auditing."""
print(f"Tool used: {input_data.get('tool_name')}")
return {}
options = ClaudeAgentOptions(
hooks={
'PreToolUse': [
HookMatcher(matcher='Bash', hooks=[validate_bash_command], timeout=120),
HookMatcher(hooks=[log_tool_use])
],
'PostToolUse': [
HookMatcher(hooks=[log_tool_use])
]
}
)
async for message in query(prompt="Analyze this codebase", options=options):
print(message)Input Data by Event Type
PreToolUse / PostToolUse
# PreToolUse
{"tool_name": "Bash", "tool_input": {"command": "ls -la"}}
# PostToolUse (adds result)
{"tool_name": "Bash", "tool_input": {"command": "ls -la"},
"tool_result": "file1.txt\nfile2.txt", "error": None}PermissionRequest (CLI only)
# Input
{"session_id": "abc123", "tool_name": "Bash",
"tool_input": {"command": "npm install"},
"permission_mode": "default", "cwd": "/path/to/project"}
# Output: allow
{"hookSpecificOutput": {"hookEventName": "PermissionRequest",
"decision": {"behavior": "allow"}}}
# Output: deny
{"hookSpecificOutput": {"hookEventName": "PermissionRequest",
"decision": {"behavior": "deny", "message": "Reason"}}}Other Events
| Event | Key Fields |
|---|---|
UserPromptSubmit | prompt, conversation_id |
TeammateIdle | agent_id, session_id |
TaskCompleted | task_id, result, duration_ms, token_count |
Stop / SubagentStop | reason, final_message |
PreCompact | messages, token_count |
PostCompact | trigger ("manual"/"auto"), compact_summary |
WorktreeCreate | name (must print worktree path to stdout) |
WorktreeRemove | worktree_path (cannot block removal) |
Hook Return Patterns
# Allow (default)
return {}
# Block action
return {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Explanation"
}
}
# Add system message
return {"systemMessage": "Important context added to conversation"}Best Practices
| Area | Guidance |
|---|---|
| Performance | Keep hooks fast (<100ms PreToolUse, <200ms PostToolUse) |
| Performance | Use appropriate timeouts; cache expensive computations |
| Security | Validate all input; never use dynamic code eval with hook input |
| Security | Use allowlists over blocklists; sanitize log data |
| Reliability | Always return a dict (even empty {}); handle exceptions |
| Reliability | Design hooks to be idempotent; include meaningful block reasons |
| Testing | Test with various input patterns; verify timeout behavior |
Related skills
How it compares
Use a structured hook rubric instead of one-off chat reviews that skip weighted security penalties and repeatable scoring.
FAQ
Who is hooks-eval for?
It is for developers and small teams shipping Claude Code agent hooks who need a documented scoring rubric—not a popularity list entry—before hooks touch real tool-use traffic.
When should I use hooks-eval?
Use it in Build—agent-tooling while authoring hooks, in Ship—security before merging hook changes, in Ship—review on PRs, and in Operate—iterate after you change paths, dependencies, or hook events; run it whenever a hook reads user input or runs shell commands.
Is hooks-eval safe to install?
Treat it as evaluation criteria your agent follows locally; review the Security Audits panel on this Prism page for the ingested package risk signals before enabling it in automated workflows.