Skip to content

Commit 7fe9694

Browse files
committed
fix(plugin-dev): support plugin wrapper format and non-tool hooks in validate-hook-schema.sh
1 parent 92bb685 commit 7fe9694

1 file changed

Lines changed: 28 additions & 14 deletions

File tree

‎plugins/plugin-dev/skills/hook-development/scripts/validate-hook-schema.sh‎

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,24 @@ if ! jq empty "$HOOKS_FILE" 2>/dev/null; then
3535
fi
3636
echo "✅ 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
3943
echo ""
4044
echo "Checking root structure..."
4145
VALID_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..."
6274
error_count=0
6375
warning_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

Comments
 (0)