spacehub

surshar / claude-code

Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.

Built by Anthropic · github.com/anthropics

claude-code / plugins / plugin-dev / skills / agent-development / scripts / validate-agent.sh
225 lines · 6.4 KBRaw
1#!/bin/bash
2# Agent File Validator
3# Validates agent markdown files for correct structure and content
4
5set -euo pipefail
6
7# Usage
8if [ $# -eq 0 ]; then
9 echo "Usage: $0 <path/to/agent.md>"
10 echo ""
11 echo "Validates agent file for:"
12 echo " - YAML frontmatter structure"
13 echo " - Required fields (name, description, model, color)"
14 echo " - Field formats and constraints"
15 echo " - System prompt presence and length"
16 echo " - Example blocks in description"
17 exit 1
18fi
19
20AGENT_FILE="$1"
21
22echo "🔍 Validating agent file: $AGENT_FILE"
23echo ""
24
25# Check 1: File exists
26if [ ! -f "$AGENT_FILE" ]; then
27 echo "❌ File not found: $AGENT_FILE"
28 exit 1
29fi
30echo "✅ File exists"
31
32# Check 2: Starts with ---
33FIRST_LINE=$(head -1 "$AGENT_FILE")
34if [ "$FIRST_LINE" != "---" ]; then
35 echo "❌ File must start with YAML frontmatter (---)"
36 exit 1
37fi
38echo "✅ Starts with frontmatter"
39
40# Check 3: Has closing ---
41if ! tail -n +2 "$AGENT_FILE" | grep -q '^---$'; then
42 echo "❌ Frontmatter not closed (missing second ---)"
43 exit 1
44fi
45echo "✅ Frontmatter properly closed"
46
47# Extract frontmatter and system prompt
48FRONTMATTER=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$AGENT_FILE")
49SYSTEM_PROMPT=$(awk '/^---$/{i++; next} i>=2' "$AGENT_FILE")
50
51# Check 4: Required fields
52echo ""
53echo "Checking required fields..."
54
55error_count=0
56warning_count=0
57
58# Check name field
59# The "|| true" on each field extraction keeps grep's no-match exit status from
60# killing the script under set -e; a missing field is reported below instead.
61NAME=$(echo "$FRONTMATTER" | grep '^name:' | sed 's/name: *//' | sed 's/^"\(.*\)"$/\1/' || true)
62
63if [ -z "$NAME" ]; then
64 echo "❌ Missing required field: name"
65 error_count=$((error_count + 1))
66else
67 echo "✅ name: $NAME"
68
69 # Validate name format
70 if ! [[ "$NAME" =~ ^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$ ]]; then
71 echo "❌ name must start/end with alphanumeric and contain only letters, numbers, hyphens"
72 error_count=$((error_count + 1))
73 fi
74
75 # Validate name length
76 name_length=${#NAME}
77 if [ $name_length -lt 3 ]; then
78 echo "❌ name too short (minimum 3 characters)"
79 error_count=$((error_count + 1))
80 elif [ $name_length -gt 50 ]; then
81 echo "❌ name too long (maximum 50 characters)"
82 error_count=$((error_count + 1))
83 fi
84
85 # Check for generic names
86 if [[ "$NAME" =~ ^(helper|assistant|agent|tool)$ ]]; then
87 echo "⚠️ name is too generic: $NAME"
88 warning_count=$((warning_count + 1))
89 fi
90fi
91
92# Check description field
93# The description may span multiple lines (unquoted prose plus <example> blocks),
94# so capture everything from "description:" until the next top-level agent key.
95DESCRIPTION=$(echo "$FRONTMATTER" | awk '
96 /^description:/ { in_description=1; sub(/^description:[[:space:]]*/, ""); print; next }
97 /^(name|model|color|tools):/ { in_description=0 }
98 in_description { print }
99')
100
101if [ -z "$DESCRIPTION" ]; then
102 echo "❌ Missing required field: description"
103 error_count=$((error_count + 1))
104else
105 desc_length=${#DESCRIPTION}
106 echo "✅ description: ${desc_length} characters"
107
108 if [ $desc_length -lt 10 ]; then
109 echo "⚠️ description too short (minimum 10 characters recommended)"
110 warning_count=$((warning_count + 1))
111 elif [ $desc_length -gt 5000 ]; then
112 echo "⚠️ description very long (over 5000 characters)"
113 warning_count=$((warning_count + 1))
114 fi
115
116 # Check for example blocks
117 if ! echo "$DESCRIPTION" | grep -q '<example>'; then
118 echo "⚠️ description should include <example> blocks for triggering"
119 warning_count=$((warning_count + 1))
120 fi
121
122 # Check for "Use this agent when" pattern
123 if ! echo "$DESCRIPTION" | grep -qi 'use this agent when'; then
124 echo "⚠️ description should start with 'Use this agent when...'"
125 warning_count=$((warning_count + 1))
126 fi
127fi
128
129# Check model field
130MODEL=$(echo "$FRONTMATTER" | grep '^model:' | sed 's/model: *//' || true)
131
132if [ -z "$MODEL" ]; then
133 echo "❌ Missing required field: model"
134 error_count=$((error_count + 1))
135else
136 echo "✅ model: $MODEL"
137
138 case "$MODEL" in
139 inherit|sonnet|opus|haiku)
140 # Valid model
141 ;;
142 *)
143 echo "⚠️ Unknown model: $MODEL (valid: inherit, sonnet, opus, haiku)"
144 warning_count=$((warning_count + 1))
145 ;;
146 esac
147fi
148
149# Check color field
150COLOR=$(echo "$FRONTMATTER" | grep '^color:' | sed 's/color: *//' || true)
151
152if [ -z "$COLOR" ]; then
153 echo "❌ Missing required field: color"
154 error_count=$((error_count + 1))
155else
156 echo "✅ color: $COLOR"
157
158 case "$COLOR" in
159 blue|cyan|green|yellow|magenta|red)
160 # Valid color
161 ;;
162 *)
163 echo "⚠️ Unknown color: $COLOR (valid: blue, cyan, green, yellow, magenta, red)"
164 warning_count=$((warning_count + 1))
165 ;;
166 esac
167fi
168
169# Check tools field (optional)
170TOOLS=$(echo "$FRONTMATTER" | grep '^tools:' | sed 's/tools: *//' || true)
171
172if [ -n "$TOOLS" ]; then
173 echo "✅ tools: $TOOLS"
174else
175 echo "💡 tools: not specified (agent has access to all tools)"
176fi
177
178# Check 5: System prompt
179echo ""
180echo "Checking system prompt..."
181
182if [ -z "$SYSTEM_PROMPT" ]; then
183 echo "❌ System prompt is empty"
184 error_count=$((error_count + 1))
185else
186 prompt_length=${#SYSTEM_PROMPT}
187 echo "✅ System prompt: $prompt_length characters"
188
189 if [ $prompt_length -lt 20 ]; then
190 echo "❌ System prompt too short (minimum 20 characters)"
191 error_count=$((error_count + 1))
192 elif [ $prompt_length -gt 10000 ]; then
193 echo "⚠️ System prompt very long (over 10,000 characters)"
194 warning_count=$((warning_count + 1))
195 fi
196
197 # Check for second person
198 if ! echo "$SYSTEM_PROMPT" | grep -q "You are\|You will\|Your"; then
199 echo "⚠️ System prompt should use second person (You are..., You will...)"
200 warning_count=$((warning_count + 1))
201 fi
202
203 # Check for structure
204 if ! echo "$SYSTEM_PROMPT" | grep -qi "responsibilities\|process\|steps"; then
205 echo "💡 Consider adding clear responsibilities or process steps"
206 fi
207
208 if ! echo "$SYSTEM_PROMPT" | grep -qi "output"; then
209 echo "💡 Consider defining output format expectations"
210 fi
211fi
212
213echo ""
214echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
215
216if [ $error_count -eq 0 ] && [ $warning_count -eq 0 ]; then
217 echo "✅ All checks passed!"
218 exit 0
219elif [ $error_count -eq 0 ]; then
220 echo "⚠️ Validation passed with $warning_count warning(s)"
221 exit 0
222else
223 echo "❌ Validation failed with $error_count error(s) and $warning_count warning(s)"
224 exit 1
225fi