aboutsummaryrefslogtreecommitdiffstats
path: root/.agents/skills/hook-development/scripts
diff options
context:
space:
mode:
authorsillylaird <sillyfanboy@gmail.com>2026-09-03 00:33:59 +0000
committersillylaird <sillyfanboy@gmail.com>2026-09-03 00:33:59 +0000
commit898b52edcb47bcb3e9d6106e74ca73e74ea01e70 (patch)
tree85c6ee5ad58b860144551184d4cf86b560c62b91 /.agents/skills/hook-development/scripts
downloadwww-main.tar.gz
www-main.zip
import live www.sillylaird.ca webrootHEADmain
Diffstat (limited to '')
-rw-r--r--.agents/skills/hook-development/scripts/README.md164
-rw-r--r--.agents/skills/hook-development/scripts/hook-linter.sh153
-rw-r--r--.agents/skills/hook-development/scripts/test-hook.sh252
-rw-r--r--.agents/skills/hook-development/scripts/validate-hook-schema.sh159
4 files changed, 728 insertions, 0 deletions
diff --git a/.agents/skills/hook-development/scripts/README.md b/.agents/skills/hook-development/scripts/README.md
new file mode 100644
index 0000000..02a556f
--- /dev/null
+++ b/.agents/skills/hook-development/scripts/README.md
@@ -0,0 +1,164 @@
+# Hook Development Utility Scripts
+
+These scripts help validate, test, and lint hook implementations before deployment.
+
+## validate-hook-schema.sh
+
+Validates `hooks.json` configuration files for correct structure and common issues.
+
+**Usage:**
+```bash
+./validate-hook-schema.sh path/to/hooks.json
+```
+
+**Checks:**
+- Valid JSON syntax
+- Required fields present
+- Valid hook event names
+- Proper hook types (command/prompt)
+- Timeout values in valid ranges
+- Hardcoded path detection
+- Prompt hook event compatibility
+
+**Example:**
+```bash
+cd my-plugin
+./validate-hook-schema.sh hooks/hooks.json
+```
+
+## test-hook.sh
+
+Tests individual hook scripts with sample input before deploying to Claude Code.
+
+**Usage:**
+```bash
+./test-hook.sh [options] <hook-script> <test-input.json>
+```
+
+**Options:**
+- `-v, --verbose` - Show detailed execution information
+- `-t, --timeout N` - Set timeout in seconds (default: 60)
+- `--create-sample <event-type>` - Generate sample test input
+
+**Example:**
+```bash
+# Create sample test input
+./test-hook.sh --create-sample PreToolUse > test-input.json
+
+# Test a hook script
+./test-hook.sh my-hook.sh test-input.json
+
+# Test with verbose output and custom timeout
+./test-hook.sh -v -t 30 my-hook.sh test-input.json
+```
+
+**Features:**
+- Sets up proper environment variables (CLAUDE_PROJECT_DIR, CLAUDE_PLUGIN_ROOT)
+- Measures execution time
+- Validates output JSON
+- Shows exit codes and their meanings
+- Captures environment file output
+
+## hook-linter.sh
+
+Checks hook scripts for common issues and best practices violations.
+
+**Usage:**
+```bash
+./hook-linter.sh <hook-script.sh> [hook-script2.sh ...]
+```
+
+**Checks:**
+- Shebang presence
+- `set -euo pipefail` usage
+- Stdin input reading
+- Proper error handling
+- Variable quoting (injection prevention)
+- Exit code usage
+- Hardcoded paths
+- Long-running code detection
+- Error output to stderr
+- Input validation
+
+**Example:**
+```bash
+# Lint single script
+./hook-linter.sh ../examples/validate-write.sh
+
+# Lint multiple scripts
+./hook-linter.sh ../examples/*.sh
+```
+
+## Typical Workflow
+
+1. **Write your hook script**
+ ```bash
+ vim my-plugin/scripts/my-hook.sh
+ ```
+
+2. **Lint the script**
+ ```bash
+ ./hook-linter.sh my-plugin/scripts/my-hook.sh
+ ```
+
+3. **Create test input**
+ ```bash
+ ./test-hook.sh --create-sample PreToolUse > test-input.json
+ # Edit test-input.json as needed
+ ```
+
+4. **Test the hook**
+ ```bash
+ ./test-hook.sh -v my-plugin/scripts/my-hook.sh test-input.json
+ ```
+
+5. **Add to hooks.json**
+ ```bash
+ # Edit my-plugin/hooks/hooks.json
+ ```
+
+6. **Validate configuration**
+ ```bash
+ ./validate-hook-schema.sh my-plugin/hooks/hooks.json
+ ```
+
+7. **Test in Claude Code**
+ ```bash
+ claude --debug
+ ```
+
+## Tips
+
+- Always test hooks before deploying to avoid breaking user workflows
+- Use verbose mode (`-v`) to debug hook behavior
+- Check the linter output for security and best practice issues
+- Validate hooks.json after any changes
+- Create different test inputs for various scenarios (safe operations, dangerous operations, edge cases)
+
+## Common Issues
+
+### Hook doesn't execute
+
+Check:
+- Script has shebang (`#!/bin/bash`)
+- Script is executable (`chmod +x`)
+- Path in hooks.json is correct (use `${CLAUDE_PLUGIN_ROOT}`)
+
+### Hook times out
+
+- Reduce timeout in hooks.json
+- Optimize hook script performance
+- Remove long-running operations
+
+### Hook fails silently
+
+- Check exit codes (should be 0 or 2)
+- Ensure errors go to stderr (`>&2`)
+- Validate JSON output structure
+
+### Injection vulnerabilities
+
+- Always quote variables: `"$variable"`
+- Use `set -euo pipefail`
+- Validate all input fields
+- Run the linter to catch issues
diff --git a/.agents/skills/hook-development/scripts/hook-linter.sh b/.agents/skills/hook-development/scripts/hook-linter.sh
new file mode 100644
index 0000000..64f6041
--- /dev/null
+++ b/.agents/skills/hook-development/scripts/hook-linter.sh
@@ -0,0 +1,153 @@
+#!/bin/bash
+# Hook Linter
+# Checks hook scripts for common issues and best practices
+
+set -euo pipefail
+
+# Usage
+if [ $# -eq 0 ]; then
+ echo "Usage: $0 <hook-script.sh> [hook-script2.sh ...]"
+ echo ""
+ echo "Checks hook scripts for:"
+ echo " - Shebang presence"
+ echo " - set -euo pipefail usage"
+ echo " - Input reading from stdin"
+ echo " - Proper error handling"
+ echo " - Variable quoting"
+ echo " - Exit code usage"
+ echo " - Hardcoded paths"
+ echo " - Timeout considerations"
+ exit 1
+fi
+
+check_script() {
+ local script="$1"
+ local warnings=0
+ local errors=0
+
+ echo "๐Ÿ” Linting: $script"
+ echo ""
+
+ if [ ! -f "$script" ]; then
+ echo "โŒ Error: File not found"
+ return 1
+ fi
+
+ # Check 1: Executable
+ if [ ! -x "$script" ]; then
+ echo "โš ๏ธ Not executable (chmod +x $script)"
+ ((warnings++))
+ fi
+
+ # Check 2: Shebang
+ first_line=$(head -1 "$script")
+ if [[ ! "$first_line" =~ ^#!/ ]]; then
+ echo "โŒ Missing shebang (#!/bin/bash)"
+ ((errors++))
+ fi
+
+ # Check 3: set -euo pipefail
+ if ! grep -q "set -euo pipefail" "$script"; then
+ echo "โš ๏ธ Missing 'set -euo pipefail' (recommended for safety)"
+ ((warnings++))
+ fi
+
+ # Check 4: Reads from stdin
+ if ! grep -q "cat\|read" "$script"; then
+ echo "โš ๏ธ Doesn't appear to read input from stdin"
+ ((warnings++))
+ fi
+
+ # Check 5: Uses jq for JSON parsing
+ if grep -q "tool_input\|tool_name" "$script" && ! grep -q "jq" "$script"; then
+ echo "โš ๏ธ Parses hook input but doesn't use jq"
+ ((warnings++))
+ fi
+
+ # Check 6: Unquoted variables
+ if grep -E '\$[A-Za-z_][A-Za-z0-9_]*[^"]' "$script" | grep -v '#' | grep -q .; then
+ echo "โš ๏ธ Potentially unquoted variables detected (injection risk)"
+ echo " Always use double quotes: \"\$variable\" not \$variable"
+ ((warnings++))
+ fi
+
+ # Check 7: Hardcoded paths
+ if grep -E '^[^#]*/home/|^[^#]*/usr/|^[^#]*/opt/' "$script" | grep -q .; then
+ echo "โš ๏ธ Hardcoded absolute paths detected"
+ echo " Use \$CLAUDE_PROJECT_DIR or \$CLAUDE_PLUGIN_ROOT"
+ ((warnings++))
+ fi
+
+ # Check 8: Uses CLAUDE_PLUGIN_ROOT
+ if ! grep -q "CLAUDE_PLUGIN_ROOT\|CLAUDE_PROJECT_DIR" "$script"; then
+ echo "๐Ÿ’ก Tip: Use \$CLAUDE_PLUGIN_ROOT for plugin-relative paths"
+ fi
+
+ # Check 9: Exit codes
+ if ! grep -q "exit 0\|exit 2" "$script"; then
+ echo "โš ๏ธ No explicit exit codes (should exit 0 or 2)"
+ ((warnings++))
+ fi
+
+ # Check 10: JSON output for decision hooks
+ if grep -q "PreToolUse\|Stop" "$script"; then
+ if ! grep -q "permissionDecision\|decision" "$script"; then
+ echo "๐Ÿ’ก Tip: PreToolUse/Stop hooks should output decision JSON"
+ fi
+ fi
+
+ # Check 11: Long-running commands
+ if grep -E 'sleep [0-9]{3,}|while true' "$script" | grep -v '#' | grep -q .; then
+ echo "โš ๏ธ Potentially long-running code detected"
+ echo " Hooks should complete quickly (< 60s)"
+ ((warnings++))
+ fi
+
+ # Check 12: Error messages to stderr
+ if grep -q 'echo.*".*error\|Error\|denied\|Denied' "$script"; then
+ if ! grep -q '>&2' "$script"; then
+ echo "โš ๏ธ Error messages should be written to stderr (>&2)"
+ ((warnings++))
+ fi
+ fi
+
+ # Check 13: Input validation
+ if ! grep -q "if.*empty\|if.*null\|if.*-z" "$script"; then
+ echo "๐Ÿ’ก Tip: Consider validating input fields aren't empty"
+ fi
+
+ echo ""
+ echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”"
+
+ if [ $errors -eq 0 ] && [ $warnings -eq 0 ]; then
+ echo "โœ… No issues found"
+ return 0
+ elif [ $errors -eq 0 ]; then
+ echo "โš ๏ธ Found $warnings warning(s)"
+ return 0
+ else
+ echo "โŒ Found $errors error(s) and $warnings warning(s)"
+ return 1
+ fi
+}
+
+echo "๐Ÿ”Ž Hook Script Linter"
+echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”"
+echo ""
+
+total_errors=0
+
+for script in "$@"; do
+ if ! check_script "$script"; then
+ ((total_errors++))
+ fi
+ echo ""
+done
+
+if [ $total_errors -eq 0 ]; then
+ echo "โœ… All scripts passed linting"
+ exit 0
+else
+ echo "โŒ $total_errors script(s) had errors"
+ exit 1
+fi
diff --git a/.agents/skills/hook-development/scripts/test-hook.sh b/.agents/skills/hook-development/scripts/test-hook.sh
new file mode 100644
index 0000000..527b119
--- /dev/null
+++ b/.agents/skills/hook-development/scripts/test-hook.sh
@@ -0,0 +1,252 @@
+#!/bin/bash
+# Hook Testing Helper
+# Tests a hook with sample input and shows output
+
+set -euo pipefail
+
+# Usage
+show_usage() {
+ echo "Usage: $0 [options] <hook-script> <test-input.json>"
+ echo ""
+ echo "Options:"
+ echo " -h, --help Show this help message"
+ echo " -v, --verbose Show detailed execution information"
+ echo " -t, --timeout N Set timeout in seconds (default: 60)"
+ echo ""
+ echo "Examples:"
+ echo " $0 validate-bash.sh test-input.json"
+ echo " $0 -v -t 30 validate-write.sh write-input.json"
+ echo ""
+ echo "Creates sample test input with:"
+ echo " $0 --create-sample <event-type>"
+ exit 0
+}
+
+# Create sample input
+create_sample() {
+ event_type="$1"
+
+ case "$event_type" in
+ PreToolUse)
+ cat <<'EOF'
+{
+ "session_id": "test-session",
+ "transcript_path": "/tmp/transcript.txt",
+ "cwd": "/tmp/test-project",
+ "permission_mode": "ask",
+ "hook_event_name": "PreToolUse",
+ "tool_name": "Write",
+ "tool_input": {
+ "file_path": "/tmp/test.txt",
+ "content": "Test content"
+ }
+}
+EOF
+ ;;
+ PostToolUse)
+ cat <<'EOF'
+{
+ "session_id": "test-session",
+ "transcript_path": "/tmp/transcript.txt",
+ "cwd": "/tmp/test-project",
+ "permission_mode": "ask",
+ "hook_event_name": "PostToolUse",
+ "tool_name": "Bash",
+ "tool_result": "Command executed successfully"
+}
+EOF
+ ;;
+ Stop|SubagentStop)
+ cat <<'EOF'
+{
+ "session_id": "test-session",
+ "transcript_path": "/tmp/transcript.txt",
+ "cwd": "/tmp/test-project",
+ "permission_mode": "ask",
+ "hook_event_name": "Stop",
+ "reason": "Task appears complete"
+}
+EOF
+ ;;
+ UserPromptSubmit)
+ cat <<'EOF'
+{
+ "session_id": "test-session",
+ "transcript_path": "/tmp/transcript.txt",
+ "cwd": "/tmp/test-project",
+ "permission_mode": "ask",
+ "hook_event_name": "UserPromptSubmit",
+ "user_prompt": "Test user prompt"
+}
+EOF
+ ;;
+ SessionStart|SessionEnd)
+ cat <<'EOF'
+{
+ "session_id": "test-session",
+ "transcript_path": "/tmp/transcript.txt",
+ "cwd": "/tmp/test-project",
+ "permission_mode": "ask",
+ "hook_event_name": "SessionStart"
+}
+EOF
+ ;;
+ *)
+ echo "Unknown event type: $event_type"
+ echo "Valid types: PreToolUse, PostToolUse, Stop, SubagentStop, UserPromptSubmit, SessionStart, SessionEnd"
+ exit 1
+ ;;
+ esac
+}
+
+# Parse arguments
+VERBOSE=false
+TIMEOUT=60
+
+while [ $# -gt 0 ]; do
+ case "$1" in
+ -h|--help)
+ show_usage
+ ;;
+ -v|--verbose)
+ VERBOSE=true
+ shift
+ ;;
+ -t|--timeout)
+ TIMEOUT="$2"
+ shift 2
+ ;;
+ --create-sample)
+ create_sample "$2"
+ exit 0
+ ;;
+ *)
+ break
+ ;;
+ esac
+done
+
+if [ $# -ne 2 ]; then
+ echo "Error: Missing required arguments"
+ echo ""
+ show_usage
+fi
+
+HOOK_SCRIPT="$1"
+TEST_INPUT="$2"
+
+# Validate inputs
+if [ ! -f "$HOOK_SCRIPT" ]; then
+ echo "โŒ Error: Hook script not found: $HOOK_SCRIPT"
+ exit 1
+fi
+
+if [ ! -x "$HOOK_SCRIPT" ]; then
+ echo "โš ๏ธ Warning: Hook script is not executable. Attempting to run with bash..."
+ HOOK_SCRIPT="bash $HOOK_SCRIPT"
+fi
+
+if [ ! -f "$TEST_INPUT" ]; then
+ echo "โŒ Error: Test input not found: $TEST_INPUT"
+ exit 1
+fi
+
+# Validate test input JSON
+if ! jq empty "$TEST_INPUT" 2>/dev/null; then
+ echo "โŒ Error: Test input is not valid JSON"
+ exit 1
+fi
+
+echo "๐Ÿงช Testing hook: $HOOK_SCRIPT"
+echo "๐Ÿ“ฅ Input: $TEST_INPUT"
+echo ""
+
+if [ "$VERBOSE" = true ]; then
+ echo "Input JSON:"
+ jq . "$TEST_INPUT"
+ echo ""
+fi
+
+# Set up environment
+export CLAUDE_PROJECT_DIR="${CLAUDE_PROJECT_DIR:-/tmp/test-project}"
+export CLAUDE_PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(pwd)}"
+export CLAUDE_ENV_FILE="${CLAUDE_ENV_FILE:-/tmp/test-env-$$}"
+
+if [ "$VERBOSE" = true ]; then
+ echo "Environment:"
+ echo " CLAUDE_PROJECT_DIR=$CLAUDE_PROJECT_DIR"
+ echo " CLAUDE_PLUGIN_ROOT=$CLAUDE_PLUGIN_ROOT"
+ echo " CLAUDE_ENV_FILE=$CLAUDE_ENV_FILE"
+ echo ""
+fi
+
+# Run the hook
+echo "โ–ถ๏ธ Running hook (timeout: ${TIMEOUT}s)..."
+echo ""
+
+start_time=$(date +%s)
+
+set +e
+output=$(timeout "$TIMEOUT" bash -c "cat '$TEST_INPUT' | $HOOK_SCRIPT" 2>&1)
+exit_code=$?
+set -e
+
+end_time=$(date +%s)
+duration=$((end_time - start_time))
+
+# Analyze results
+echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”"
+echo "Results:"
+echo ""
+echo "Exit Code: $exit_code"
+echo "Duration: ${duration}s"
+echo ""
+
+case $exit_code in
+ 0)
+ echo "โœ… Hook approved/succeeded"
+ ;;
+ 2)
+ echo "๐Ÿšซ Hook blocked/denied"
+ ;;
+ 124)
+ echo "โฑ๏ธ Hook timed out after ${TIMEOUT}s"
+ ;;
+ *)
+ echo "โš ๏ธ Hook returned unexpected exit code: $exit_code"
+ ;;
+esac
+
+echo ""
+echo "Output:"
+if [ -n "$output" ]; then
+ echo "$output"
+ echo ""
+
+ # Try to parse as JSON
+ if echo "$output" | jq empty 2>/dev/null; then
+ echo "Parsed JSON output:"
+ echo "$output" | jq .
+ fi
+else
+ echo "(no output)"
+fi
+
+# Check for environment file
+if [ -f "$CLAUDE_ENV_FILE" ]; then
+ echo ""
+ echo "Environment file created:"
+ cat "$CLAUDE_ENV_FILE"
+ rm -f "$CLAUDE_ENV_FILE"
+fi
+
+echo ""
+echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”"
+
+if [ $exit_code -eq 0 ] || [ $exit_code -eq 2 ]; then
+ echo "โœ… Test completed successfully"
+ exit 0
+else
+ echo "โŒ Test failed"
+ exit 1
+fi
diff --git a/.agents/skills/hook-development/scripts/validate-hook-schema.sh b/.agents/skills/hook-development/scripts/validate-hook-schema.sh
new file mode 100644
index 0000000..fed0a1f
--- /dev/null
+++ b/.agents/skills/hook-development/scripts/validate-hook-schema.sh
@@ -0,0 +1,159 @@
+#!/bin/bash
+# Hook Schema Validator
+# Validates hooks.json structure and checks for common issues
+
+set -euo pipefail
+
+# Usage
+if [ $# -eq 0 ]; then
+ echo "Usage: $0 <path/to/hooks.json>"
+ echo ""
+ echo "Validates hook configuration file for:"
+ echo " - Valid JSON syntax"
+ echo " - Required fields"
+ echo " - Hook type validity"
+ echo " - Matcher patterns"
+ echo " - Timeout ranges"
+ exit 1
+fi
+
+HOOKS_FILE="$1"
+
+if [ ! -f "$HOOKS_FILE" ]; then
+ echo "โŒ Error: File not found: $HOOKS_FILE"
+ exit 1
+fi
+
+echo "๐Ÿ” Validating hooks configuration: $HOOKS_FILE"
+echo ""
+
+# Check 1: Valid JSON
+echo "Checking JSON syntax..."
+if ! jq empty "$HOOKS_FILE" 2>/dev/null; then
+ echo "โŒ Invalid JSON syntax"
+ exit 1
+fi
+echo "โœ… Valid JSON"
+
+# Check 2: Root structure
+echo ""
+echo "Checking root structure..."
+VALID_EVENTS=("PreToolUse" "PostToolUse" "UserPromptSubmit" "Stop" "SubagentStop" "SessionStart" "SessionEnd" "PreCompact" "Notification")
+
+for event in $(jq -r 'keys[]' "$HOOKS_FILE"); do
+ found=false
+ for valid_event in "${VALID_EVENTS[@]}"; do
+ if [ "$event" = "$valid_event" ]; then
+ found=true
+ break
+ fi
+ done
+
+ if [ "$found" = false ]; then
+ echo "โš ๏ธ Unknown event type: $event"
+ fi
+done
+echo "โœ… Root structure valid"
+
+# Check 3: Validate each hook
+echo ""
+echo "Validating individual hooks..."
+
+error_count=0
+warning_count=0
+
+for event in $(jq -r 'keys[]' "$HOOKS_FILE"); do
+ hook_count=$(jq -r ".\"$event\" | length" "$HOOKS_FILE")
+
+ for ((i=0; i<hook_count; i++)); do
+ # Check matcher exists
+ matcher=$(jq -r ".\"$event\"[$i].matcher // empty" "$HOOKS_FILE")
+ if [ -z "$matcher" ]; then
+ echo "โŒ $event[$i]: Missing 'matcher' field"
+ ((error_count++))
+ continue
+ fi
+
+ # Check hooks array exists
+ hooks=$(jq -r ".\"$event\"[$i].hooks // empty" "$HOOKS_FILE")
+ if [ -z "$hooks" ] || [ "$hooks" = "null" ]; then
+ echo "โŒ $event[$i]: Missing 'hooks' array"
+ ((error_count++))
+ continue
+ fi
+
+ # Validate each hook in the array
+ hook_array_count=$(jq -r ".\"$event\"[$i].hooks | length" "$HOOKS_FILE")
+
+ for ((j=0; j<hook_array_count; j++)); do
+ hook_type=$(jq -r ".\"$event\"[$i].hooks[$j].type // empty" "$HOOKS_FILE")
+
+ if [ -z "$hook_type" ]; then
+ echo "โŒ $event[$i].hooks[$j]: Missing 'type' field"
+ ((error_count++))
+ continue
+ fi
+
+ if [ "$hook_type" != "command" ] && [ "$hook_type" != "prompt" ]; then
+ echo "โŒ $event[$i].hooks[$j]: Invalid type '$hook_type' (must be 'command' or 'prompt')"
+ ((error_count++))
+ continue
+ fi
+
+ # Check type-specific fields
+ if [ "$hook_type" = "command" ]; then
+ command=$(jq -r ".\"$event\"[$i].hooks[$j].command // empty" "$HOOKS_FILE")
+ if [ -z "$command" ]; then
+ echo "โŒ $event[$i].hooks[$j]: Command hooks must have 'command' field"
+ ((error_count++))
+ else
+ # Check for hardcoded paths
+ if [[ "$command" == /* ]] && [[ "$command" != *'${CLAUDE_PLUGIN_ROOT}'* ]]; then
+ echo "โš ๏ธ $event[$i].hooks[$j]: Hardcoded absolute path detected. Consider using \${CLAUDE_PLUGIN_ROOT}"
+ ((warning_count++))
+ fi
+ fi
+ elif [ "$hook_type" = "prompt" ]; then
+ prompt=$(jq -r ".\"$event\"[$i].hooks[$j].prompt // empty" "$HOOKS_FILE")
+ if [ -z "$prompt" ]; then
+ echo "โŒ $event[$i].hooks[$j]: Prompt hooks must have 'prompt' field"
+ ((error_count++))
+ fi
+
+ # Check if prompt-based hooks are used on supported events
+ if [ "$event" != "Stop" ] && [ "$event" != "SubagentStop" ] && [ "$event" != "UserPromptSubmit" ] && [ "$event" != "PreToolUse" ]; then
+ echo "โš ๏ธ $event[$i].hooks[$j]: Prompt hooks may not be fully supported on $event (best on Stop, SubagentStop, UserPromptSubmit, PreToolUse)"
+ ((warning_count++))
+ fi
+ fi
+
+ # Check timeout
+ timeout=$(jq -r ".\"$event\"[$i].hooks[$j].timeout // empty" "$HOOKS_FILE")
+ if [ -n "$timeout" ] && [ "$timeout" != "null" ]; then
+ if ! [[ "$timeout" =~ ^[0-9]+$ ]]; then
+ echo "โŒ $event[$i].hooks[$j]: Timeout must be a number"
+ ((error_count++))
+ elif [ "$timeout" -gt 600 ]; then
+ echo "โš ๏ธ $event[$i].hooks[$j]: Timeout $timeout seconds is very high (max 600s)"
+ ((warning_count++))
+ elif [ "$timeout" -lt 5 ]; then
+ echo "โš ๏ธ $event[$i].hooks[$j]: Timeout $timeout seconds is very low"
+ ((warning_count++))
+ fi
+ fi
+ done
+ done
+done
+
+echo ""
+echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”"
+if [ $error_count -eq 0 ] && [ $warning_count -eq 0 ]; then
+ echo "โœ… All checks passed!"
+ exit 0
+elif [ $error_count -eq 0 ]; then
+ echo "โš ๏ธ Validation passed with $warning_count warning(s)"
+ exit 0
+else
+ echo "โŒ Validation failed with $error_count error(s) and $warning_count warning(s)"
+ exit 1
+fi