@@ -35,12 +35,24 @@ if ! jq empty "$HOOKS_FILE" 2>/dev/null; then
3535fi
3636echo " ✅ Valid JSON"
3737
38+ # Determine hook root expression (support both plugin wrapper format {"hooks": {...}} and settings direct format)
39+ HOOK_ROOT_JQ=' if (type == "object" and has("hooks") and (.hooks | type == "object")) then .hooks else . end'
40+ HAS_HOOKS_WRAPPER=$( jq -r ' if (type == "object" and has("hooks") and (.hooks | type == "object")) then "true" else "false" end' " $HOOKS_FILE " )
41+
3842# Check 2: Root structure
3943echo " "
4044echo " Checking root structure..."
4145VALID_EVENTS=(" PreToolUse" " PostToolUse" " UserPromptSubmit" " Stop" " SubagentStop" " SessionStart" " SessionEnd" " PreCompact" " Notification" )
4246
43- for event in $( jq -r ' keys[]' " $HOOKS_FILE " ) ; do
47+ if [ " $HAS_HOOKS_WRAPPER " = " true" ]; then
48+ for key in $( jq -r ' keys[]' " $HOOKS_FILE " | tr -d ' \r' ) ; do
49+ if [ " $key " != " hooks" ] && [ " $key " != " description" ]; then
50+ echo " ⚠️ Unknown top-level field in plugin format: $key "
51+ fi
52+ done
53+ fi
54+
55+ for event in $( jq -r " ($HOOK_ROOT_JQ ) | keys[]" " $HOOKS_FILE " | tr -d ' \r' ) ; do
4456 found=false
4557 for valid_event in " ${VALID_EVENTS[@]} " ; do
4658 if [ " $event " = " $valid_event " ]; then
@@ -62,31 +74,33 @@ echo "Validating individual hooks..."
6274error_count=0
6375warning_count=0
6476
65- for event in $( jq -r ' keys[]' " $HOOKS_FILE " ) ; do
66- hook_count=$( jq -r " . \" $event \" | length" " $HOOKS_FILE " )
77+ for event in $( jq -r " ( $HOOK_ROOT_JQ ) | keys[]" " $HOOKS_FILE " | tr -d ' \r ' ) ; do
78+ hook_count=$( jq -r " ( $HOOK_ROOT_JQ ) | .[ \" $event \" ] | length" " $HOOKS_FILE " | tr -d ' \r ' )
6779
6880 for (( i= 0 ; i< hook_count; i++ )) ; do
69- # Check matcher exists
70- matcher=$( jq -r " . \" $event \" [$i ].matcher // empty" " $HOOKS_FILE " )
81+ # Check matcher (only required for tool-level events: PreToolUse, PostToolUse)
82+ matcher=$( jq -r " ( $HOOK_ROOT_JQ ) | .[ \" $event \" ] [$i ].matcher // empty" " $HOOKS_FILE " )
7183 if [ -z " $matcher " ]; then
72- echo " ❌ $event [$i ]: Missing 'matcher' field"
73- (( error_count++ ))
74- continue
84+ if [ " $event " = " PreToolUse" ] || [ " $event " = " PostToolUse" ]; then
85+ echo " ❌ $event [$i ]: Missing 'matcher' field"
86+ (( error_count++ ))
87+ continue
88+ fi
7589 fi
7690
7791 # Check hooks array exists
78- hooks=$( jq -r " . \" $event \" [$i ].hooks // empty" " $HOOKS_FILE " )
92+ hooks=$( jq -r " ( $HOOK_ROOT_JQ ) | .[ \" $event \" ] [$i ].hooks // empty" " $HOOKS_FILE " )
7993 if [ -z " $hooks " ] || [ " $hooks " = " null" ]; then
8094 echo " ❌ $event [$i ]: Missing 'hooks' array"
8195 (( error_count++ ))
8296 continue
8397 fi
8498
8599 # Validate each hook in the array
86- hook_array_count=$( jq -r " . \" $event \" [$i ].hooks | length" " $HOOKS_FILE " )
100+ hook_array_count=$( jq -r " ( $HOOK_ROOT_JQ ) | .[ \" $event \" ] [$i ].hooks | length" " $HOOKS_FILE " )
87101
88102 for (( j= 0 ; j< hook_array_count; j++ )) ; do
89- hook_type=$( jq -r " . \" $event \" [$i ].hooks[$j ].type // empty" " $HOOKS_FILE " )
103+ hook_type=$( jq -r " ( $HOOK_ROOT_JQ ) | .[ \" $event \" ] [$i ].hooks[$j ].type // empty" " $HOOKS_FILE " )
90104
91105 if [ -z " $hook_type " ]; then
92106 echo " ❌ $event [$i ].hooks[$j ]: Missing 'type' field"
@@ -102,7 +116,7 @@ for event in $(jq -r 'keys[]' "$HOOKS_FILE"); do
102116
103117 # Check type-specific fields
104118 if [ " $hook_type " = " command" ]; then
105- command=$( jq -r " . \" $event \" [$i ].hooks[$j ].command // empty" " $HOOKS_FILE " )
119+ command=$( jq -r " ( $HOOK_ROOT_JQ ) | .[ \" $event \" ] [$i ].hooks[$j ].command // empty" " $HOOKS_FILE " )
106120 if [ -z " $command " ]; then
107121 echo " ❌ $event [$i ].hooks[$j ]: Command hooks must have 'command' field"
108122 (( error_count++ ))
@@ -114,7 +128,7 @@ for event in $(jq -r 'keys[]' "$HOOKS_FILE"); do
114128 fi
115129 fi
116130 elif [ " $hook_type " = " prompt" ]; then
117- prompt=$( jq -r " . \" $event \" [$i ].hooks[$j ].prompt // empty" " $HOOKS_FILE " )
131+ prompt=$( jq -r " ( $HOOK_ROOT_JQ ) | .[ \" $event \" ] [$i ].hooks[$j ].prompt // empty" " $HOOKS_FILE " )
118132 if [ -z " $prompt " ]; then
119133 echo " ❌ $event [$i ].hooks[$j ]: Prompt hooks must have 'prompt' field"
120134 (( error_count++ ))
@@ -128,7 +142,7 @@ for event in $(jq -r 'keys[]' "$HOOKS_FILE"); do
128142 fi
129143
130144 # Check timeout
131- timeout=$( jq -r " . \" $event \" [$i ].hooks[$j ].timeout // empty" " $HOOKS_FILE " )
145+ timeout=$( jq -r " ( $HOOK_ROOT_JQ ) | .[ \" $event \" ] [$i ].hooks[$j ].timeout // empty" " $HOOKS_FILE " )
132146 if [ -n " $timeout " ] && [ " $timeout " != " null" ]; then
133147 if ! [[ " $timeout " =~ ^[0-9]+$ ]]; then
134148 echo " ❌ $event [$i ].hooks[$j ]: Timeout must be a number"
0 commit comments