scripts/quick_validate.py
scripts/quick_validate.pyBrowse 3 files
1,093 tokens
5,077 bytes
Token encoding: o200k_base
Snapshot 229ffef
← Back to SKILL.md
1#!/usr/bin/env python32"""Quick validation script for skills - minimal version.3 4For deepagents CLI, skills are located at:5$DEEPAGENTS_HOME/<agent>/skills/<skill-name>/6 7Example:8```python9python quick_validate.py "${DEEPAGENTS_HOME:-$HOME/.deepagents}/agent/skills/my-skill"10```11"""12 13import re14import sys15from pathlib import Path16 17import yaml18 19 20def validate_skill(skill_path):21 """Basic validation of a skill.22 23 Returns:24 Tuple of (is_valid, message) where is_valid is bool and message25 describes result.26 """27 skill_path = Path(skill_path)28 29 # Check SKILL.md exists30 skill_md = skill_path / "SKILL.md"31 if not skill_md.exists():32 return False, "SKILL.md not found"33 34 # Read and validate frontmatter35 content = skill_md.read_text()36 if not content.startswith("---"):37 return False, "No YAML frontmatter found"38 39 # Extract frontmatter40 match = re.match(r"^---\n(.*?)\n---", content, re.DOTALL)41 if not match:42 return False, "Invalid frontmatter format"43 44 frontmatter_text = match.group(1)45 46 # Parse YAML frontmatter47 try:48 frontmatter = yaml.safe_load(frontmatter_text)49 if not isinstance(frontmatter, dict):50 return False, "Frontmatter must be a YAML dictionary"51 except yaml.YAMLError as e:52 return False, f"Invalid YAML in frontmatter: {e}"53 54 # Define allowed properties55 ALLOWED_PROPERTIES = {56 "name",57 "description",58 "license",59 "compatibility",60 "allowed-tools",61 "metadata",62 }63 64 # Check for unexpected properties (excluding nested keys under metadata)65 unexpected_keys = {str(key) for key in frontmatter if key not in ALLOWED_PROPERTIES}66 if unexpected_keys:67 unexpected_str = ", ".join(sorted(unexpected_keys))68 allowed_str = ", ".join(sorted(ALLOWED_PROPERTIES))69 return False, (70 f"Unexpected key(s) in SKILL.md frontmatter: {unexpected_str}. "71 f"Allowed properties are: {allowed_str}"72 )73 74 # Check required fields75 if "name" not in frontmatter:76 return False, "Missing 'name' in frontmatter"77 if "description" not in frontmatter:78 return False, "Missing 'description' in frontmatter"79 80 # Extract name for validation81 name = frontmatter.get("name", "")82 if not isinstance(name, str):83 return False, f"Name must be a string, got {type(name).__name__}"84 name = name.strip()85 if name:86 # Structural hyphen checks87 if name.startswith("-") or name.endswith("-") or "--" in name:88 return (89 False,90 (91 f"Name '{name}' cannot start/end with hyphen "92 "or contain consecutive hyphens"93 ),94 )95 # Character-by-character check matching SDK's _validate_skill_name:96 # Unicode lowercase alphanumeric and hyphens only97 for c in name:98 if c == "-":99 continue100 if (c.isalpha() and c.islower()) or c.isdigit():101 continue102 return (103 False,104 (105 f"Name '{name}' should be hyphen-case "106 "(lowercase letters, digits, and hyphens only)"107 ),108 )109 # Check name length (max 64 characters per spec)110 if len(name) > 64:111 return (112 False,113 f"Name is too long ({len(name)} characters). Maximum is 64 characters.",114 )115 116 # Extract and validate description117 description = frontmatter.get("description", "")118 if not isinstance(description, str):119 return False, f"Description must be a string, got {type(description).__name__}"120 description = description.strip()121 if description:122 # Check for angle brackets123 if "<" in description or ">" in description:124 return False, "Description cannot contain angle brackets (< or >)"125 # Check description length (max 1024 characters per spec)126 if len(description) > 1024:127 return (128 False,129 (130 f"Description is too long ({len(description)} characters). "131 "Maximum is 1024 characters."132 ),133 )134 135 # Extract and validate compatibility (max 500 characters per spec)136 compatibility = frontmatter.get("compatibility", "")137 if isinstance(compatibility, str):138 compatibility = compatibility.strip()139 if len(compatibility) > 500:140 return (141 False,142 (143 f"Compatibility is too long ({len(compatibility)} characters). "144 "Maximum is 500 characters."145 ),146 )147 148 return True, "Skill is valid!"149 150 151if __name__ == "__main__":152 if len(sys.argv) != 2:153 print("Usage: python quick_validate.py <skill_directory>")154 sys.exit(1)155 156 valid, message = validate_skill(sys.argv[1])157 print(message)158 sys.exit(0 if valid else 1)159