📦 deps(thirdparty): update snapshots

This commit is contained in:
ci[bot]
2026-05-29 08:33:53 +00:00
parent fdb52f1e96
commit 06e0d13d57
1615 changed files with 232858 additions and 0 deletions
@@ -0,0 +1,363 @@
#!/usr/bin/env bash
# validate-hook.sh - Validate Claude Code hook configuration and scripts
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Counters
ERRORS=0
WARNINGS=0
# Help text
show_help() {
cat << EOF
Usage: $(basename "$0") <config-file> [options]
Validate Claude Code hook configuration and referenced scripts.
Arguments:
config-file Path to settings.json file with hooks configuration
Options:
-s, --strict Strict mode (warnings become errors)
-q, --quiet Only show errors and warnings
--check-scripts Verify all referenced scripts exist and are executable
-h, --help Show this help
Examples:
# Validate project hooks
$(basename "$0") .claude/settings.json
# Validate personal hooks
$(basename "$0") ~/.claude/settings.json
# Strict validation with script checking
$(basename "$0") .claude/settings.json --strict --check-scripts
Exit Codes:
0 - Validation passed
1 - Validation failed with errors
EOF
}
# Error reporting
error() {
((ERRORS++))
echo -e "${RED}✗ Error:${NC} $1"
}
warning() {
((WARNINGS++))
echo -e "${YELLOW}⚠ Warning:${NC} $1"
}
info() {
if [[ "$QUIET" == "false" ]]; then
echo -e "${BLUE} Info:${NC} $1"
fi
}
success() {
if [[ "$QUIET" == "false" ]]; then
echo -e "${GREEN}$1${NC}"
fi
}
# Parse arguments
CONFIG_FILE=""
STRICT=false
QUIET=false
CHECK_SCRIPTS=false
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
show_help
exit 0
;;
-s|--strict)
STRICT=true
shift
;;
-q|--quiet)
QUIET=true
shift
;;
--check-scripts)
CHECK_SCRIPTS=true
shift
;;
-*)
echo -e "${RED}Error: Unknown option $1${NC}"
show_help
exit 1
;;
*)
CONFIG_FILE="$1"
shift
;;
esac
done
# Validate file argument
if [[ -z "$CONFIG_FILE" ]]; then
echo -e "${RED}Error: Config file required${NC}"
show_help
exit 1
fi
if [[ ! -f "$CONFIG_FILE" ]]; then
error "File not found: $CONFIG_FILE"
exit 1
fi
# Check if jq is installed
if ! command -v jq &>/dev/null; then
error "jq is not installed (required for JSON validation)"
exit 1
fi
# Start validation
if [[ "$QUIET" == "false" ]]; then
echo -e "${BLUE}Validating: $CONFIG_FILE${NC}"
echo
fi
# 1. Validate JSON syntax
if ! jq empty "$CONFIG_FILE" 2>/dev/null; then
error "Invalid JSON syntax"
exit 1
fi
success "JSON syntax valid"
# 2. Check if hooks exist in config
if ! jq -e '.hooks' "$CONFIG_FILE" >/dev/null 2>&1; then
info "No hooks configuration found (file is valid but has no hooks)"
exit 0
fi
success "Hooks configuration found"
# 3. Validate hook structure
VALID_EVENTS=(PreToolUse PostToolUse UserPromptSubmit Notification Stop SubagentStop PreCompact SessionStart SessionEnd)
# Get all event names
EVENTS=$(jq -r '.hooks | keys[]' "$CONFIG_FILE" 2>/dev/null || echo "")
if [[ -z "$EVENTS" ]]; then
warning "No events defined in hooks configuration"
fi
TOTAL_HOOKS=0
while IFS= read -r event; do
[[ -z "$event" ]] && continue
# Check if valid event
if [[ ! " ${VALID_EVENTS[*]} " =~ " ${event} " ]]; then
error "Invalid event type: $event (valid: ${VALID_EVENTS[*]})"
continue
fi
success "Event: $event"
# Validate event structure
EVENT_ARRAY=$(jq -c ".hooks.\"$event\"" "$CONFIG_FILE")
# Check if array
if ! echo "$EVENT_ARRAY" | jq -e 'type == "array"' >/dev/null 2>&1; then
error "Event '$event' must be an array"
continue
fi
# Get array length
ARRAY_LENGTH=$(echo "$EVENT_ARRAY" | jq 'length')
info " Found $ARRAY_LENGTH matcher(s) for $event"
# Validate each matcher entry
for ((i=0; i<ARRAY_LENGTH; i++)); do
MATCHER_ENTRY=$(echo "$EVENT_ARRAY" | jq -c ".[$i]")
# Check for matcher field
if ! echo "$MATCHER_ENTRY" | jq -e '.matcher' >/dev/null 2>&1; then
error " Entry $i in $event: missing 'matcher' field"
continue
fi
MATCHER=$(echo "$MATCHER_ENTRY" | jq -r '.matcher')
info " Matcher: $MATCHER"
# Validate matcher pattern
if [[ -z "$MATCHER" ]]; then
warning " Empty matcher pattern"
fi
# Check for hooks array
if ! echo "$MATCHER_ENTRY" | jq -e '.hooks' >/dev/null 2>&1; then
error " Entry $i in $event: missing 'hooks' array"
continue
fi
if ! echo "$MATCHER_ENTRY" | jq -e '.hooks | type == "array"' >/dev/null 2>&1; then
error " Entry $i in $event: 'hooks' must be an array"
continue
fi
# Validate each hook in the array
HOOKS_LENGTH=$(echo "$MATCHER_ENTRY" | jq '.hooks | length')
((TOTAL_HOOKS += HOOKS_LENGTH))
for ((j=0; j<HOOKS_LENGTH; j++)); do
HOOK=$(echo "$MATCHER_ENTRY" | jq -c ".hooks[$j]")
# Check type field
if ! echo "$HOOK" | jq -e '.type' >/dev/null 2>&1; then
error " Hook $j: missing 'type' field"
continue
fi
HOOK_TYPE=$(echo "$HOOK" | jq -r '.type')
if [[ "$HOOK_TYPE" != "command" ]]; then
error " Hook $j: invalid type '$HOOK_TYPE' (must be 'command')"
fi
# Check command field
if ! echo "$HOOK" | jq -e '.command' >/dev/null 2>&1; then
error " Hook $j: missing 'command' field"
continue
fi
COMMAND=$(echo "$HOOK" | jq -r '.command')
info " Command: $COMMAND"
# Check if command is empty
if [[ -z "$COMMAND" ]]; then
error " Hook $j: empty command"
continue
fi
# Check timeout
if echo "$HOOK" | jq -e '.timeout' >/dev/null 2>&1; then
TIMEOUT=$(echo "$HOOK" | jq -r '.timeout')
if ! [[ "$TIMEOUT" =~ ^[0-9]+$ ]]; then
error " Hook $j: timeout must be a number"
elif [[ "$TIMEOUT" -lt 1 ]]; then
error " Hook $j: timeout must be at least 1 second"
elif [[ "$TIMEOUT" -gt 300 ]]; then
warning " Hook $j: timeout is very long ($TIMEOUT seconds)"
fi
else
info " Using default timeout (30 seconds)"
fi
# Check script if enabled
if [[ "$CHECK_SCRIPTS" == "true" ]]; then
# Extract script path (handle variables)
SCRIPT_PATH="$COMMAND"
# Replace common variables
SCRIPT_PATH="${SCRIPT_PATH//\$CLAUDE_PROJECT_DIR/$(dirname "$CONFIG_FILE")}"
SCRIPT_PATH="${SCRIPT_PATH//\$\{CLAUDE_PROJECT_DIR\}/$(dirname "$CONFIG_FILE")}"
SCRIPT_PATH="${SCRIPT_PATH//\~/~}"
# Extract first argument (script path)
SCRIPT_PATH=$(echo "$SCRIPT_PATH" | awk '{print $1}')
# Remove quotes
SCRIPT_PATH="${SCRIPT_PATH//\"/}"
SCRIPT_PATH="${SCRIPT_PATH//\'/}"
# Check if script exists
if [[ -f "$SCRIPT_PATH" ]]; then
success " Script exists: $SCRIPT_PATH"
# Check if executable
if [[ ! -x "$SCRIPT_PATH" ]]; then
warning " Script not executable: $SCRIPT_PATH (run: chmod +x $SCRIPT_PATH)"
else
success " Script is executable"
fi
# Check shebang
FIRST_LINE=$(head -n 1 "$SCRIPT_PATH")
if [[ ! "$FIRST_LINE" =~ ^#! ]]; then
warning " Script missing shebang line"
fi
elif [[ "$SCRIPT_PATH" =~ ^\$ ]] || [[ "$SCRIPT_PATH" =~ ^(echo|printf|cat|true|false|test|:)$ ]]; then
info " Inline/built-in command, skipping script check"
else
warning " Script not found: $SCRIPT_PATH"
fi
fi
done
done
echo
done <<< "$EVENTS"
# Summary
info "Total hooks validated: $TOTAL_HOOKS"
echo
# 4. Check for common issues
# Duplicate matchers
DUPLICATE_MATCHERS=$(jq -r '.hooks | to_entries[] | .key as $event | .value[] | .matcher | "\($event):\(.)"' "$CONFIG_FILE" | sort | uniq -c | awk '$1 > 1 {print $0}')
if [[ -n "$DUPLICATE_MATCHERS" ]]; then
warning "Duplicate matchers found (may cause hooks to run multiple times):"
echo "$DUPLICATE_MATCHERS" | while read -r line; do
warning " $line"
done
fi
# Very long commands
LONG_COMMANDS=$(jq -r '.hooks | to_entries[] | .value[] | .hooks[] | .command | select(length > 200)' "$CONFIG_FILE" 2>/dev/null || echo "")
if [[ -n "$LONG_COMMANDS" ]]; then
warning "Very long commands found (consider using script files):"
echo "$LONG_COMMANDS" | while IFS= read -r cmd; do
warning " ${cmd:0:100}..."
done
fi
# Hooks without timeout
HOOKS_WITHOUT_TIMEOUT=$(jq '[.hooks | to_entries[] | .value[] | .hooks[] | select(.timeout == null)] | length' "$CONFIG_FILE")
if [[ "$HOOKS_WITHOUT_TIMEOUT" -gt 0 ]]; then
info "$HOOKS_WITHOUT_TIMEOUT hook(s) using default timeout"
fi
# PreToolUse hooks with long timeouts
SLOW_PRE_HOOKS=$(jq -r '.hooks.PreToolUse[]? | .hooks[] | select(.timeout > 10) | .command' "$CONFIG_FILE" 2>/dev/null || echo "")
if [[ -n "$SLOW_PRE_HOOKS" ]]; then
warning "PreToolUse hooks with timeout >10s (may slow down operations):"
echo "$SLOW_PRE_HOOKS" | while IFS= read -r cmd; do
warning " $cmd"
done
fi
# Convert warnings to errors in strict mode
if [[ "$STRICT" == "true" && $WARNINGS -gt 0 ]]; then
ERRORS=$((ERRORS + WARNINGS))
WARNINGS=0
fi
# Final summary
echo
if [[ $ERRORS -eq 0 && $WARNINGS -eq 0 ]]; then
echo -e "${GREEN}✓ Validation passed!${NC}"
exit 0
elif [[ $ERRORS -eq 0 ]]; then
echo -e "${YELLOW}⚠ Validation passed with $WARNINGS warning(s)${NC}"
exit 0
else
echo -e "${RED}✗ Validation failed with $ERRORS error(s)${NC}"
if [[ $WARNINGS -gt 0 ]]; then
echo -e "${YELLOW} and $WARNINGS warning(s)${NC}"
fi
exit 1
fi