📦 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,546 @@
#!/usr/bin/env bash
# validate-plugin.sh - Comprehensive Claude Code plugin validation
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Counters
ERRORS=0
WARNINGS=0
CHECKS=0
# Options
STRICT_MODE=false
QUIET_MODE=false
FIX_MODE=false
# Print functions
print_error() {
((ERRORS++))
echo -e "${RED}✗ ERROR:${NC} $1"
}
print_warning() {
((WARNINGS++))
echo -e "${YELLOW}⚠ WARNING:${NC} $1"
}
print_info() {
[[ "$QUIET_MODE" == "false" ]] && echo -e "${BLUE} INFO:${NC} $1"
}
print_success() {
[[ "$QUIET_MODE" == "false" ]] && echo -e "${GREEN}✓ PASS:${NC} $1"
}
print_check() {
((CHECKS++))
[[ "$QUIET_MODE" == "false" ]] && echo -e "${CYAN}[CHECK $CHECKS]${NC} $1"
}
# Usage
usage() {
cat << EOF
Usage: $0 [options] <plugin-directory>
Comprehensive validation for Claude Code plugins.
Arguments:
plugin-directory Path to plugin root directory
Options:
-s, --strict Treat warnings as errors
-q, --quiet Only show errors and warnings
-f, --fix Auto-fix issues where possible
-h, --help Show this help
Examples:
# Validate current plugin
$0 .
# Validate specific plugin
$0 /path/to/my-plugin
# Strict validation
$0 --strict .
# Auto-fix common issues
$0 --fix .
Exit Codes:
0 - No errors
1 - Validation errors found
2 - Invalid arguments or plugin not found
EOF
exit 2
}
# Parse arguments
PLUGIN_DIR=""
while [[ $# -gt 0 ]]; do
case $1 in
-s|--strict)
STRICT_MODE=true
shift
;;
-q|--quiet)
QUIET_MODE=true
shift
;;
-f|--fix)
FIX_MODE=true
shift
;;
-h|--help)
usage
;;
-*)
echo -e "${RED}Error: Unknown option $1${NC}"
usage
;;
*)
PLUGIN_DIR="$1"
shift
;;
esac
done
# Validate arguments
if [[ -z "$PLUGIN_DIR" ]]; then
echo -e "${RED}Error: Plugin directory required${NC}"
usage
fi
if [[ ! -d "$PLUGIN_DIR" ]]; then
echo -e "${RED}Error: Directory not found: $PLUGIN_DIR${NC}"
exit 2
fi
# Convert to absolute path
PLUGIN_DIR=$(cd "$PLUGIN_DIR" && pwd)
# Print header
if [[ "$QUIET_MODE" == "false" ]]; then
echo -e "${BLUE}╔════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}║ Claude Code Plugin Validation ║${NC}"
echo -e "${BLUE}╚════════════════════════════════════════════╝${NC}"
echo
echo -e "${CYAN}Plugin Directory:${NC} $PLUGIN_DIR"
echo
fi
# Check 1: plugin.json exists
print_check "Checking for plugin.json"
PLUGIN_JSON="$PLUGIN_DIR/.claude-plugin/plugin.json"
if [[ ! -f "$PLUGIN_JSON" ]]; then
print_error "plugin.json not found at .claude-plugin/plugin.json"
exit 1
else
print_success "plugin.json exists"
fi
# Check 2: plugin.json is valid JSON
print_check "Validating plugin.json syntax"
if ! jq empty "$PLUGIN_JSON" 2>/dev/null; then
print_error "plugin.json contains invalid JSON"
exit 1
else
print_success "plugin.json is valid JSON"
fi
# Check 3: Required fields in plugin.json
print_check "Validating plugin.json required fields"
PLUGIN_NAME=$(jq -r '.name // empty' "$PLUGIN_JSON")
PLUGIN_VERSION=$(jq -r '.version // empty' "$PLUGIN_JSON")
PLUGIN_DESC=$(jq -r '.description // empty' "$PLUGIN_JSON")
if [[ -z "$PLUGIN_NAME" ]]; then
print_error "plugin.json missing required field: name"
else
print_success "Plugin name: $PLUGIN_NAME"
# Validate name format
if [[ ! "$PLUGIN_NAME" =~ ^[a-z][a-z0-9-]*$ ]]; then
print_error "Plugin name must be kebab-case: $PLUGIN_NAME"
fi
fi
if [[ -z "$PLUGIN_VERSION" ]]; then
print_error "plugin.json missing required field: version"
else
print_success "Plugin version: $PLUGIN_VERSION"
# Validate semantic versioning
if [[ ! "$PLUGIN_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
print_warning "Version should follow semantic versioning (e.g., 1.0.0)"
fi
fi
if [[ -z "$PLUGIN_DESC" ]]; then
print_warning "plugin.json missing recommended field: description"
else
print_success "Plugin description present"
# Check description length
DESC_LEN=${#PLUGIN_DESC}
if [[ $DESC_LEN -lt 20 ]]; then
print_warning "Description is very short ($DESC_LEN chars)"
elif [[ $DESC_LEN -gt 200 ]]; then
print_warning "Description is very long ($DESC_LEN chars), consider shortening"
fi
fi
# Check 4: Author info
print_check "Validating author information"
AUTHOR_NAME=$(jq -r '.author.name // empty' "$PLUGIN_JSON")
AUTHOR_EMAIL=$(jq -r '.author.email // empty' "$PLUGIN_JSON")
if [[ -z "$AUTHOR_NAME" ]]; then
print_warning "plugin.json missing recommended field: author.name"
else
print_success "Author: $AUTHOR_NAME"
fi
if [[ -z "$AUTHOR_EMAIL" ]]; then
print_warning "plugin.json missing recommended field: author.email"
elif [[ ! "$AUTHOR_EMAIL" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
print_warning "Author email appears invalid: $AUTHOR_EMAIL"
fi
# Check 5: License
print_check "Checking license"
PLUGIN_LICENSE=$(jq -r '.license // empty' "$PLUGIN_JSON")
if [[ -z "$PLUGIN_LICENSE" ]]; then
print_warning "plugin.json missing recommended field: license"
else
print_success "License: $PLUGIN_LICENSE"
# Check for LICENSE file
if [[ ! -f "$PLUGIN_DIR/LICENSE" ]] && [[ ! -f "$PLUGIN_DIR/LICENSE.md" ]]; then
print_warning "No LICENSE file found in plugin root"
fi
fi
# Check 6: README.md
print_check "Checking README.md"
if [[ ! -f "$PLUGIN_DIR/README.md" ]]; then
print_warning "No README.md found in plugin root"
else
print_success "README.md exists"
# Check README length
README_LINES=$(wc -l < "$PLUGIN_DIR/README.md")
if [[ $README_LINES -lt 10 ]]; then
print_warning "README.md is very short ($README_LINES lines)"
fi
fi
# Check 7: Validate skills
print_check "Validating skills"
SKILLS_DIR="$PLUGIN_DIR/skills"
if [[ -d "$SKILLS_DIR" ]]; then
SKILL_COUNT=$(find "$SKILLS_DIR" -name "SKILL.md" | wc -l | xargs)
if [[ $SKILL_COUNT -eq 0 ]]; then
print_warning "skills/ directory exists but contains no SKILL.md files"
else
print_success "Found $SKILL_COUNT skill(s)"
# Validate each skill
while IFS= read -r skill_file; do
SKILL_NAME=$(dirname "$skill_file" | xargs basename)
print_info "Validating skill: $SKILL_NAME"
# Check for frontmatter
if ! head -n 1 "$skill_file" | grep -q '^---$'; then
print_warning "Skill $SKILL_NAME missing frontmatter"
else
# Validate required frontmatter fields
SKILL_FRONTMATTER=$(awk '/^---$/{flag=!flag; next} flag' "$skill_file" | head -n 20)
if ! echo "$SKILL_FRONTMATTER" | grep -q '^name:'; then
print_error "Skill $SKILL_NAME missing 'name' in frontmatter"
fi
if ! echo "$SKILL_FRONTMATTER" | grep -q '^description:'; then
print_error "Skill $SKILL_NAME missing 'description' in frontmatter"
fi
if ! echo "$SKILL_FRONTMATTER" | grep -q '^version:'; then
print_warning "Skill $SKILL_NAME missing 'version' in frontmatter"
fi
fi
# Check file size (skills should have substantial content)
SKILL_SIZE=$(wc -c < "$skill_file")
if [[ $SKILL_SIZE -lt 500 ]]; then
print_warning "Skill $SKILL_NAME is very small ($SKILL_SIZE bytes)"
fi
done < <(find "$SKILLS_DIR" -name "SKILL.md")
fi
else
print_info "No skills/ directory found"
fi
# Check 8: Validate commands
print_check "Validating slash commands"
COMMANDS_DIR="$PLUGIN_DIR/commands"
if [[ -d "$COMMANDS_DIR" ]]; then
COMMAND_COUNT=$(find "$COMMANDS_DIR" -name "*.md" | wc -l | xargs)
if [[ $COMMAND_COUNT -eq 0 ]]; then
print_warning "commands/ directory exists but contains no .md files"
else
print_success "Found $COMMAND_COUNT command(s)"
# Validate each command
while IFS= read -r cmd_file; do
CMD_NAME=$(basename "$cmd_file" .md)
print_info "Validating command: $CMD_NAME"
# Check filename format
if [[ ! "$CMD_NAME" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]]; then
print_warning "Command name should be kebab-case: $CMD_NAME"
fi
# Check if file is empty
if [[ ! -s "$cmd_file" ]]; then
print_error "Command $CMD_NAME is empty"
continue
fi
# Check for frontmatter
if head -n 1 "$cmd_file" | grep -q '^---$'; then
# Validate frontmatter syntax
FRONTMATTER=$(awk '/^---$/{flag=!flag; next} flag' "$cmd_file" | head -n 20)
# Check for description
if echo "$FRONTMATTER" | grep -q '^description:'; then
DESC=$(echo "$FRONTMATTER" | grep '^description:' | sed 's/^description: *//')
if [[ -z "$DESC" ]]; then
print_warning "Command $CMD_NAME has empty description"
fi
fi
# Check for tabs in frontmatter (YAML doesn't allow tabs)
if echo "$FRONTMATTER" | grep -q $'\t'; then
print_error "Command $CMD_NAME frontmatter contains tabs (use spaces)"
fi
fi
done < <(find "$COMMANDS_DIR" -name "*.md")
fi
else
print_info "No commands/ directory found"
fi
# Check 9: Validate agents
print_check "Validating custom agents"
AGENTS_DIR="$PLUGIN_DIR/agents"
if [[ -d "$AGENTS_DIR" ]]; then
AGENT_COUNT=$(find "$AGENTS_DIR" -name "*.md" | wc -l | xargs)
if [[ $AGENT_COUNT -eq 0 ]]; then
print_warning "agents/ directory exists but contains no .md files"
else
print_success "Found $AGENT_COUNT agent(s)"
# Check if agents are referenced in plugin.json
AGENTS_IN_JSON=$(jq -r '.agents // [] | length' "$PLUGIN_JSON")
if [[ $AGENTS_IN_JSON -eq 0 ]]; then
print_warning "Agents found but not referenced in plugin.json"
fi
# Validate each agent
while IFS= read -r agent_file; do
AGENT_NAME=$(basename "$agent_file" .md)
print_info "Validating agent: $AGENT_NAME"
# Check for frontmatter
if ! head -n 1 "$agent_file" | grep -q '^---$'; then
print_warning "Agent $AGENT_NAME missing frontmatter"
else
AGENT_FRONTMATTER=$(awk '/^---$/{flag=!flag; next} flag' "$agent_file" | head -n 20)
if ! echo "$AGENT_FRONTMATTER" | grep -q '^name:'; then
print_error "Agent $AGENT_NAME missing 'name' in frontmatter"
fi
if ! echo "$AGENT_FRONTMATTER" | grep -q '^description:'; then
print_error "Agent $AGENT_NAME missing 'description' in frontmatter"
fi
fi
done < <(find "$AGENTS_DIR" -name "*.md")
fi
else
print_info "No agents/ directory found"
fi
# Check 10: Validate hooks
print_check "Validating event hooks"
HOOKS_DIR="$PLUGIN_DIR/hooks"
if [[ -d "$HOOKS_DIR" ]]; then
HOOK_COUNT=$(find "$HOOKS_DIR" -type f | wc -l | xargs)
if [[ $HOOK_COUNT -eq 0 ]]; then
print_warning "hooks/ directory exists but contains no files"
else
print_success "Found $HOOK_COUNT hook file(s)"
# Check if hooks are referenced in plugin.json
HOOKS_IN_JSON=$(jq -r '.hooks // {} | length' "$PLUGIN_JSON")
if [[ $HOOKS_IN_JSON -eq 0 ]]; then
print_warning "Hook files found but not configured in plugin.json"
fi
# Validate each hook script
while IFS= read -r hook_file; do
HOOK_NAME=$(basename "$hook_file")
print_info "Validating hook: $HOOK_NAME"
# Check if file is executable
if [[ ! -x "$hook_file" ]]; then
if [[ "$FIX_MODE" == "true" ]]; then
chmod +x "$hook_file"
print_info "Fixed: Made $HOOK_NAME executable"
else
print_warning "Hook $HOOK_NAME is not executable (use chmod +x)"
fi
fi
# Check for shebang
if ! head -n 1 "$hook_file" | grep -q '^#!'; then
print_warning "Hook $HOOK_NAME missing shebang line"
fi
done < <(find "$HOOKS_DIR" -type f)
fi
else
print_info "No hooks/ directory found"
fi
# Check 11: Validate MCP servers
print_check "Validating MCP servers"
SERVERS_DIR="$PLUGIN_DIR/servers"
if [[ -d "$SERVERS_DIR" ]]; then
SERVER_COUNT=$(find "$SERVERS_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l | xargs)
if [[ $SERVER_COUNT -eq 0 ]]; then
print_warning "servers/ directory exists but contains no server directories"
else
print_success "Found $SERVER_COUNT MCP server(s)"
# Check if servers are referenced in plugin.json
SERVERS_IN_JSON=$(jq -r '.mcpServers // {} | length' "$PLUGIN_JSON")
if [[ $SERVERS_IN_JSON -eq 0 ]]; then
print_warning "MCP servers found but not configured in plugin.json"
fi
# Validate each server
while IFS= read -r server_dir; do
SERVER_NAME=$(basename "$server_dir")
print_info "Validating MCP server: $SERVER_NAME"
# Check for server implementation
if [[ -f "$server_dir/server.py" ]]; then
print_success "Found Python server implementation"
# Check for pyproject.toml
if [[ ! -f "$server_dir/pyproject.toml" ]]; then
print_warning "Server $SERVER_NAME missing pyproject.toml"
fi
elif [[ -f "$server_dir/index.js" ]] || [[ -f "$server_dir/index.ts" ]]; then
print_success "Found Node.js server implementation"
# Check for package.json
if [[ ! -f "$server_dir/package.json" ]]; then
print_warning "Server $SERVER_NAME missing package.json"
fi
else
print_warning "Server $SERVER_NAME missing server implementation file"
fi
done < <(find "$SERVERS_DIR" -mindepth 1 -maxdepth 1 -type d)
fi
else
print_info "No servers/ directory found"
fi
# Check 12: Check for common files
print_check "Checking for common files"
if [[ ! -f "$PLUGIN_DIR/.gitignore" ]]; then
print_warning "No .gitignore found"
else
print_success ".gitignore exists"
fi
if [[ ! -f "$PLUGIN_DIR/CHANGELOG.md" ]]; then
print_warning "No CHANGELOG.md found (recommended for versioning)"
else
print_success "CHANGELOG.md exists"
fi
# Check 13: Git repository
print_check "Checking git repository"
if [[ ! -d "$PLUGIN_DIR/.git" ]]; then
print_info "Not a git repository"
else
print_success "Git repository initialized"
# Check for uncommitted changes
if ! git -C "$PLUGIN_DIR" diff-index --quiet HEAD -- 2>/dev/null; then
print_info "Repository has uncommitted changes"
fi
fi
# Summary
echo
echo -e "${BLUE}╔════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}║ Validation Summary ║${NC}"
echo -e "${BLUE}╚════════════════════════════════════════════╝${NC}"
echo
echo -e "${CYAN}Checks Performed:${NC} $CHECKS"
if [[ $ERRORS -gt 0 ]]; then
echo -e "${RED}Errors Found:${NC} $ERRORS"
fi
if [[ $WARNINGS -gt 0 ]]; then
echo -e "${YELLOW}Warnings Found:${NC} $WARNINGS"
fi
echo
# Convert warnings to errors in strict mode
if [[ "$STRICT_MODE" == "true" && $WARNINGS -gt 0 ]]; then
ERRORS=$((ERRORS + WARNINGS))
WARNINGS=0
echo -e "${YELLOW}(Strict mode: warnings treated as errors)${NC}"
echo
fi
# Exit with appropriate code
if [[ $ERRORS -eq 0 && $WARNINGS -eq 0 ]]; then
echo -e "${GREEN}✓ Validation passed! Plugin is ready to use.${NC}"
exit 0
elif [[ $ERRORS -eq 0 ]]; then
echo -e "${YELLOW}⚠ Validation passed with warnings.${NC}"
echo -e "${YELLOW} Consider addressing warnings before distribution.${NC}"
exit 0
else
echo -e "${RED}✗ Validation failed!${NC}"
echo -e "${RED} Please fix errors before using this plugin.${NC}"
exit 1
fi