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