| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- #!/bin/bash
- # Agent File Validator
- # Validates agent markdown files for correct structure and content
- set -euo pipefail
- # Usage
- if [ $# -eq 0 ]; then
- echo "Usage: $0 <path/to/agent.md>"
- echo ""
- echo "Validates agent file for:"
- echo " - YAML frontmatter structure"
- echo " - Required fields (name, description, model, color)"
- echo " - Field formats and constraints"
- echo " - System prompt presence and length"
- echo " - Example blocks in description"
- exit 1
- fi
- AGENT_FILE="$1"
- echo "🔍 Validating agent file: $AGENT_FILE"
- echo ""
- # Check 1: File exists
- if [ ! -f "$AGENT_FILE" ]; then
- echo "❌ File not found: $AGENT_FILE"
- exit 1
- fi
- echo "✅ File exists"
- # Check 2: Starts with ---
- FIRST_LINE=$(head -1 "$AGENT_FILE")
- if [ "$FIRST_LINE" != "---" ]; then
- echo "❌ File must start with YAML frontmatter (---)"
- exit 1
- fi
- echo "✅ Starts with frontmatter"
- # Check 3: Has closing ---
- if ! tail -n +2 "$AGENT_FILE" | grep -q '^---$'; then
- echo "❌ Frontmatter not closed (missing second ---)"
- exit 1
- fi
- echo "✅ Frontmatter properly closed"
- # Extract frontmatter and system prompt
- FRONTMATTER=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$AGENT_FILE")
- SYSTEM_PROMPT=$(awk '/^---$/{i++; next} i>=2' "$AGENT_FILE")
- # Check 4: Required fields
- echo ""
- echo "Checking required fields..."
- error_count=0
- warning_count=0
- # Check name field
- NAME=$(echo "$FRONTMATTER" | grep '^name:' | sed 's/name: *//' | sed 's/^"\(.*\)"$/\1/')
- if [ -z "$NAME" ]; then
- echo "❌ Missing required field: name"
- ((error_count++))
- else
- echo "✅ name: $NAME"
- # Validate name format
- if ! [[ "$NAME" =~ ^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$ ]]; then
- echo "❌ name must start/end with alphanumeric and contain only letters, numbers, hyphens"
- ((error_count++))
- fi
- # Validate name length
- name_length=${#NAME}
- if [ $name_length -lt 3 ]; then
- echo "❌ name too short (minimum 3 characters)"
- ((error_count++))
- elif [ $name_length -gt 50 ]; then
- echo "❌ name too long (maximum 50 characters)"
- ((error_count++))
- fi
- # Check for generic names
- if [[ "$NAME" =~ ^(helper|assistant|agent|tool)$ ]]; then
- echo "⚠️ name is too generic: $NAME"
- ((warning_count++))
- fi
- fi
- # Check description field
- DESCRIPTION=$(echo "$FRONTMATTER" | grep '^description:' | sed 's/description: *//')
- if [ -z "$DESCRIPTION" ]; then
- echo "❌ Missing required field: description"
- ((error_count++))
- else
- desc_length=${#DESCRIPTION}
- echo "✅ description: ${desc_length} characters"
- if [ $desc_length -lt 10 ]; then
- echo "⚠️ description too short (minimum 10 characters recommended)"
- ((warning_count++))
- elif [ $desc_length -gt 5000 ]; then
- echo "⚠️ description very long (over 5000 characters)"
- ((warning_count++))
- fi
- # Check for example blocks
- if ! echo "$DESCRIPTION" | grep -q '<example>'; then
- echo "⚠️ description should include <example> blocks for triggering"
- ((warning_count++))
- fi
- # Check for "Use this agent when" pattern
- if ! echo "$DESCRIPTION" | grep -qi 'use this agent when'; then
- echo "⚠️ description should start with 'Use this agent when...'"
- ((warning_count++))
- fi
- fi
- # Check model field
- MODEL=$(echo "$FRONTMATTER" | grep '^model:' | sed 's/model: *//')
- if [ -z "$MODEL" ]; then
- echo "❌ Missing required field: model"
- ((error_count++))
- else
- echo "✅ model: $MODEL"
- case "$MODEL" in
- inherit|sonnet|opus|haiku)
- # Valid model
- ;;
- *)
- echo "⚠️ Unknown model: $MODEL (valid: inherit, sonnet, opus, haiku)"
- ((warning_count++))
- ;;
- esac
- fi
- # Check color field
- COLOR=$(echo "$FRONTMATTER" | grep '^color:' | sed 's/color: *//')
- if [ -z "$COLOR" ]; then
- echo "❌ Missing required field: color"
- ((error_count++))
- else
- echo "✅ color: $COLOR"
- case "$COLOR" in
- blue|cyan|green|yellow|magenta|red)
- # Valid color
- ;;
- *)
- echo "⚠️ Unknown color: $COLOR (valid: blue, cyan, green, yellow, magenta, red)"
- ((warning_count++))
- ;;
- esac
- fi
- # Check tools field (optional)
- TOOLS=$(echo "$FRONTMATTER" | grep '^tools:' | sed 's/tools: *//')
- if [ -n "$TOOLS" ]; then
- echo "✅ tools: $TOOLS"
- else
- echo "💡 tools: not specified (agent has access to all tools)"
- fi
- # Check 5: System prompt
- echo ""
- echo "Checking system prompt..."
- if [ -z "$SYSTEM_PROMPT" ]; then
- echo "❌ System prompt is empty"
- ((error_count++))
- else
- prompt_length=${#SYSTEM_PROMPT}
- echo "✅ System prompt: $prompt_length characters"
- if [ $prompt_length -lt 20 ]; then
- echo "❌ System prompt too short (minimum 20 characters)"
- ((error_count++))
- elif [ $prompt_length -gt 10000 ]; then
- echo "⚠️ System prompt very long (over 10,000 characters)"
- ((warning_count++))
- fi
- # Check for second person
- if ! echo "$SYSTEM_PROMPT" | grep -q "You are\|You will\|Your"; then
- echo "⚠️ System prompt should use second person (You are..., You will...)"
- ((warning_count++))
- fi
- # Check for structure
- if ! echo "$SYSTEM_PROMPT" | grep -qi "responsibilities\|process\|steps"; then
- echo "💡 Consider adding clear responsibilities or process steps"
- fi
- if ! echo "$SYSTEM_PROMPT" | grep -qi "output"; then
- echo "💡 Consider defining output format expectations"
- fi
- fi
- echo ""
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
- if [ $error_count -eq 0 ] && [ $warning_count -eq 0 ]; then
- echo "✅ All checks passed!"
- exit 0
- elif [ $error_count -eq 0 ]; then
- echo "⚠️ Validation passed with $warning_count warning(s)"
- exit 0
- else
- echo "❌ Validation failed with $error_count error(s) and $warning_count warning(s)"
- exit 1
- fi
|