mirror of
https://github.com/anthropics/claude-code.git
synced 2026-02-19 04:27:33 -08:00
Compare commits
1 Commits
1b50583382
...
claude/sla
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
411381bf3e |
@@ -3,6 +3,7 @@
|
|||||||
"hooks": {
|
"hooks": {
|
||||||
"SessionStart": [
|
"SessionStart": [
|
||||||
{
|
{
|
||||||
|
"matcher": "*",
|
||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"hooks": {
|
"hooks": {
|
||||||
"PreToolUse": [
|
"PreToolUse": [
|
||||||
{
|
{
|
||||||
|
"matcher": "*",
|
||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
@@ -14,6 +15,7 @@
|
|||||||
],
|
],
|
||||||
"PostToolUse": [
|
"PostToolUse": [
|
||||||
{
|
{
|
||||||
|
"matcher": "*",
|
||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
@@ -25,6 +27,7 @@
|
|||||||
],
|
],
|
||||||
"Stop": [
|
"Stop": [
|
||||||
{
|
{
|
||||||
|
"matcher": "*",
|
||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
@@ -36,6 +39,7 @@
|
|||||||
],
|
],
|
||||||
"UserPromptSubmit": [
|
"UserPromptSubmit": [
|
||||||
{
|
{
|
||||||
|
"matcher": "*",
|
||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"hooks": {
|
"hooks": {
|
||||||
"SessionStart": [
|
"SessionStart": [
|
||||||
{
|
{
|
||||||
|
"matcher": "*",
|
||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
|
|||||||
@@ -40,7 +40,27 @@ echo ""
|
|||||||
echo "Checking root structure..."
|
echo "Checking root structure..."
|
||||||
VALID_EVENTS=("PreToolUse" "PostToolUse" "UserPromptSubmit" "Stop" "SubagentStop" "SessionStart" "SessionEnd" "PreCompact" "Notification")
|
VALID_EVENTS=("PreToolUse" "PostToolUse" "UserPromptSubmit" "Stop" "SubagentStop" "SessionStart" "SessionEnd" "PreCompact" "Notification")
|
||||||
|
|
||||||
for event in $(jq -r 'keys[]' "$HOOKS_FILE"); do
|
# Detect format: plugin format has { description?, hooks: {...} } wrapper
|
||||||
|
# Settings format has events directly at root level
|
||||||
|
is_plugin_format=false
|
||||||
|
if jq -e '.hooks' "$HOOKS_FILE" >/dev/null 2>&1; then
|
||||||
|
is_plugin_format=true
|
||||||
|
HOOKS_PATH=".hooks"
|
||||||
|
echo "Detected plugin format (with 'hooks' wrapper)"
|
||||||
|
|
||||||
|
# Validate allowed root keys for plugin format
|
||||||
|
for key in $(jq -r 'keys[]' "$HOOKS_FILE"); do
|
||||||
|
if [ "$key" != "hooks" ] && [ "$key" != "description" ]; then
|
||||||
|
echo "⚠️ Unknown root key in plugin format: $key (expected: 'hooks', 'description')"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
HOOKS_PATH="."
|
||||||
|
echo "Detected settings format (events at root)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate event types
|
||||||
|
for event in $(jq -r "$HOOKS_PATH | keys[]" "$HOOKS_FILE"); do
|
||||||
found=false
|
found=false
|
||||||
for valid_event in "${VALID_EVENTS[@]}"; do
|
for valid_event in "${VALID_EVENTS[@]}"; do
|
||||||
if [ "$event" = "$valid_event" ]; then
|
if [ "$event" = "$valid_event" ]; then
|
||||||
@@ -62,12 +82,12 @@ echo "Validating individual hooks..."
|
|||||||
error_count=0
|
error_count=0
|
||||||
warning_count=0
|
warning_count=0
|
||||||
|
|
||||||
for event in $(jq -r 'keys[]' "$HOOKS_FILE"); do
|
for event in $(jq -r "$HOOKS_PATH | keys[]" "$HOOKS_FILE"); do
|
||||||
hook_count=$(jq -r ".\"$event\" | length" "$HOOKS_FILE")
|
hook_count=$(jq -r "$HOOKS_PATH.\"$event\" | length" "$HOOKS_FILE")
|
||||||
|
|
||||||
for ((i=0; i<hook_count; i++)); do
|
for ((i=0; i<hook_count; i++)); do
|
||||||
# Check matcher exists
|
# Check matcher exists
|
||||||
matcher=$(jq -r ".\"$event\"[$i].matcher // empty" "$HOOKS_FILE")
|
matcher=$(jq -r "$HOOKS_PATH.\"$event\"[$i].matcher // empty" "$HOOKS_FILE")
|
||||||
if [ -z "$matcher" ]; then
|
if [ -z "$matcher" ]; then
|
||||||
echo "❌ $event[$i]: Missing 'matcher' field"
|
echo "❌ $event[$i]: Missing 'matcher' field"
|
||||||
((error_count++))
|
((error_count++))
|
||||||
@@ -75,7 +95,7 @@ for event in $(jq -r 'keys[]' "$HOOKS_FILE"); do
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Check hooks array exists
|
# Check hooks array exists
|
||||||
hooks=$(jq -r ".\"$event\"[$i].hooks // empty" "$HOOKS_FILE")
|
hooks=$(jq -r "$HOOKS_PATH.\"$event\"[$i].hooks // empty" "$HOOKS_FILE")
|
||||||
if [ -z "$hooks" ] || [ "$hooks" = "null" ]; then
|
if [ -z "$hooks" ] || [ "$hooks" = "null" ]; then
|
||||||
echo "❌ $event[$i]: Missing 'hooks' array"
|
echo "❌ $event[$i]: Missing 'hooks' array"
|
||||||
((error_count++))
|
((error_count++))
|
||||||
@@ -83,10 +103,10 @@ for event in $(jq -r 'keys[]' "$HOOKS_FILE"); do
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Validate each hook in the array
|
# Validate each hook in the array
|
||||||
hook_array_count=$(jq -r ".\"$event\"[$i].hooks | length" "$HOOKS_FILE")
|
hook_array_count=$(jq -r "$HOOKS_PATH.\"$event\"[$i].hooks | length" "$HOOKS_FILE")
|
||||||
|
|
||||||
for ((j=0; j<hook_array_count; j++)); do
|
for ((j=0; j<hook_array_count; j++)); do
|
||||||
hook_type=$(jq -r ".\"$event\"[$i].hooks[$j].type // empty" "$HOOKS_FILE")
|
hook_type=$(jq -r "$HOOKS_PATH.\"$event\"[$i].hooks[$j].type // empty" "$HOOKS_FILE")
|
||||||
|
|
||||||
if [ -z "$hook_type" ]; then
|
if [ -z "$hook_type" ]; then
|
||||||
echo "❌ $event[$i].hooks[$j]: Missing 'type' field"
|
echo "❌ $event[$i].hooks[$j]: Missing 'type' field"
|
||||||
@@ -102,7 +122,7 @@ for event in $(jq -r 'keys[]' "$HOOKS_FILE"); do
|
|||||||
|
|
||||||
# Check type-specific fields
|
# Check type-specific fields
|
||||||
if [ "$hook_type" = "command" ]; then
|
if [ "$hook_type" = "command" ]; then
|
||||||
command=$(jq -r ".\"$event\"[$i].hooks[$j].command // empty" "$HOOKS_FILE")
|
command=$(jq -r "$HOOKS_PATH.\"$event\"[$i].hooks[$j].command // empty" "$HOOKS_FILE")
|
||||||
if [ -z "$command" ]; then
|
if [ -z "$command" ]; then
|
||||||
echo "❌ $event[$i].hooks[$j]: Command hooks must have 'command' field"
|
echo "❌ $event[$i].hooks[$j]: Command hooks must have 'command' field"
|
||||||
((error_count++))
|
((error_count++))
|
||||||
@@ -114,7 +134,7 @@ for event in $(jq -r 'keys[]' "$HOOKS_FILE"); do
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
elif [ "$hook_type" = "prompt" ]; then
|
elif [ "$hook_type" = "prompt" ]; then
|
||||||
prompt=$(jq -r ".\"$event\"[$i].hooks[$j].prompt // empty" "$HOOKS_FILE")
|
prompt=$(jq -r "$HOOKS_PATH.\"$event\"[$i].hooks[$j].prompt // empty" "$HOOKS_FILE")
|
||||||
if [ -z "$prompt" ]; then
|
if [ -z "$prompt" ]; then
|
||||||
echo "❌ $event[$i].hooks[$j]: Prompt hooks must have 'prompt' field"
|
echo "❌ $event[$i].hooks[$j]: Prompt hooks must have 'prompt' field"
|
||||||
((error_count++))
|
((error_count++))
|
||||||
@@ -128,7 +148,7 @@ for event in $(jq -r 'keys[]' "$HOOKS_FILE"); do
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Check timeout
|
# Check timeout
|
||||||
timeout=$(jq -r ".\"$event\"[$i].hooks[$j].timeout // empty" "$HOOKS_FILE")
|
timeout=$(jq -r "$HOOKS_PATH.\"$event\"[$i].hooks[$j].timeout // empty" "$HOOKS_FILE")
|
||||||
if [ -n "$timeout" ] && [ "$timeout" != "null" ]; then
|
if [ -n "$timeout" ] && [ "$timeout" != "null" ]; then
|
||||||
if ! [[ "$timeout" =~ ^[0-9]+$ ]]; then
|
if ! [[ "$timeout" =~ ^[0-9]+$ ]]; then
|
||||||
echo "❌ $event[$i].hooks[$j]: Timeout must be a number"
|
echo "❌ $event[$i].hooks[$j]: Timeout must be a number"
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"hooks": {
|
"hooks": {
|
||||||
"Stop": [
|
"Stop": [
|
||||||
{
|
{
|
||||||
|
"matcher": "*",
|
||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
|
|||||||
49
scripts/validate-all-hooks.sh
Executable file
49
scripts/validate-all-hooks.sh
Executable file
@@ -0,0 +1,49 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Validate all hooks.json files in the repository
|
||||||
|
# This script can be run in CI to ensure all plugins have valid hook configurations
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||||
|
VALIDATOR="$REPO_ROOT/plugins/plugin-dev/skills/hook-development/scripts/validate-hook-schema.sh"
|
||||||
|
|
||||||
|
echo "🔍 Validating all hooks.json files in the repository..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Find all hooks.json files
|
||||||
|
mapfile -t HOOKS_FILES < <(find "$REPO_ROOT/plugins" -name "hooks.json" -type f 2>/dev/null)
|
||||||
|
|
||||||
|
if [ ${#HOOKS_FILES[@]} -eq 0 ]; then
|
||||||
|
echo "No hooks.json files found"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Found ${#HOOKS_FILES[@]} hooks.json file(s)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
errors=0
|
||||||
|
for hooks_file in "${HOOKS_FILES[@]}"; do
|
||||||
|
relative_path="${hooks_file#$REPO_ROOT/}"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "📄 $relative_path"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if bash "$VALIDATOR" "$hooks_file"; then
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
((errors++))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ $errors -eq 0 ]; then
|
||||||
|
echo "✅ All ${#HOOKS_FILES[@]} hooks.json file(s) are valid!"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "❌ $errors hooks.json file(s) have validation errors"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user