Class: AgentsSkillVault::SkillValidator
- Inherits:
-
Object
- Object
- AgentsSkillVault::SkillValidator
- Defined in:
- lib/agents_skill_vault/skill_validator.rb
Overview
Validates SKILL.md files against the Agent Skills specification.
Checks for required fields, field formats, and constraints.
Constant Summary collapse
- REQUIRED_FIELDS =
%w[name description].freeze
- NAME_MAX_LENGTH =
64- NAME_MIN_LENGTH =
1- DESCRIPTION_MAX_LENGTH =
1024- DESCRIPTION_MIN_LENGTH =
1- COMPATIBILITY_MAX_LENGTH =
500- NAME_PATTERN =
/\A[a-z0-9]+(?:-[a-z0-9]+)*\z/
Class Method Summary collapse
-
.build_skill_data(data) ⇒ Hash
Builds skill data hash from parsed YAML.
-
.extract_frontmatter(content) ⇒ Array<String, String>
Extracts YAML frontmatter from content.
-
.parse_yaml(yaml_string, errors) ⇒ Hash?
Parses YAML string.
-
.validate(skill_file_path) ⇒ Hash
Validates a SKILL.md file.
-
.validate_description(data, errors) ⇒ Object
Validates the description field.
-
.validate_fields(data, errors) ⇒ Object
Validates all fields for the parsed data.
-
.validate_name(data, errors) ⇒ Object
Validates the name field.
-
.validate_optional_fields(data, errors) ⇒ Object
Validates optional fields if present.
-
.validate_required_fields(data, errors) ⇒ Object
Validates that all required fields are present.
Class Method Details
.build_skill_data(data) ⇒ Hash
Builds skill data hash from parsed YAML.
199 200 201 202 203 204 205 206 207 208 |
# File 'lib/agents_skill_vault/skill_validator.rb', line 199 def self.build_skill_data(data) { name: data["name"], description: data["description"], license: data["license"], compatibility: data["compatibility"], metadata: data["metadata"], allowed_tools: data["allowed-tools"] } end |
.extract_frontmatter(content) ⇒ Array<String, String>
Extracts YAML frontmatter from content.
75 76 77 78 79 80 81 82 |
# File 'lib/agents_skill_vault/skill_validator.rb', line 75 def self.extract_frontmatter(content) return [nil, nil] unless content.start_with?("---") parts = content.split(/^---\s*$/) return [nil, nil] unless parts.length >= 2 [parts[1].strip, parts[2..]&.join("---")] end |
.parse_yaml(yaml_string, errors) ⇒ Hash?
Parses YAML string.
90 91 92 93 94 95 |
# File 'lib/agents_skill_vault/skill_validator.rb', line 90 def self.parse_yaml(yaml_string, errors) YAML.safe_load(yaml_string) rescue Psych::SyntaxError => e errors << "Invalid YAML syntax: #{e.message}" nil end |
.validate(skill_file_path) ⇒ Hash
Validates a SKILL.md file.
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/agents_skill_vault/skill_validator.rb', line 37 def self.validate(skill_file_path) raise ArgumentError, "skill_file_path cannot be nil" if skill_file_path.nil? raise ArgumentError, "skill_file_path cannot be empty" if skill_file_path.empty? errors = [] # Check file exists and is readable unless File.exist?(skill_file_path) return { valid: false, errors: ["File does not exist: #{skill_file_path}"], skill_data: {} } end unless File.readable?(skill_file_path) return { valid: false, errors: ["File is not readable: #{skill_file_path}"], skill_data: {} } end content = File.read(skill_file_path) # Parse YAML frontmatter frontmatter, = extract_frontmatter(content) if frontmatter.nil? errors << "No YAML frontmatter found (missing '---' delimiters)" return { valid: false, errors:, skill_data: {} } end # Parse YAML parsed_data = parse_yaml(frontmatter, errors) return { valid: false, errors:, skill_data: {} } if parsed_data.nil? validate_fields(parsed_data, errors) { valid: errors.empty?, errors:, skill_data: build_skill_data(parsed_data) } end |
.validate_description(data, errors) ⇒ Object
Validates the description field.
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/agents_skill_vault/skill_validator.rb', line 136 def self.validate_description(data, errors) description = data["description"] if description.empty? errors << "Field 'description' cannot be empty" return end if description.length > DESCRIPTION_MAX_LENGTH errors << "Field 'description' exceeds maximum length of #{DESCRIPTION_MAX_LENGTH} characters" end return unless description.length < DESCRIPTION_MIN_LENGTH errors << "Field 'description' must be at least #{DESCRIPTION_MIN_LENGTH} character" end |
.validate_fields(data, errors) ⇒ Object
Validates all fields for the parsed data.
187 188 189 190 191 192 |
# File 'lib/agents_skill_vault/skill_validator.rb', line 187 def self.validate_fields(data, errors) validate_required_fields(data, errors) validate_name(data, errors) if data["name"] validate_description(data, errors) if data["description"] validate_optional_fields(data, errors) end |
.validate_name(data, errors) ⇒ Object
Validates the name field.
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/agents_skill_vault/skill_validator.rb', line 113 def self.validate_name(data, errors) name = data["name"] if name.empty? errors << "Field 'name' cannot be empty" return end errors << "Field 'name' exceeds maximum length of #{NAME_MAX_LENGTH} characters" if name.length > NAME_MAX_LENGTH errors << "Field 'name' must be at least #{NAME_MIN_LENGTH} character" if name.length < NAME_MIN_LENGTH return if name.match?(NAME_PATTERN) errors << "Field 'name' must contain only lowercase letters, numbers, and hyphens " \ "(no consecutive or leading/trailing hyphens)" end |
.validate_optional_fields(data, errors) ⇒ Object
Validates optional fields if present.
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/agents_skill_vault/skill_validator.rb', line 158 def self.validate_optional_fields(data, errors) # Validate compatibility field if present if data["compatibility"] compatibility = data["compatibility"] if compatibility.length > COMPATIBILITY_MAX_LENGTH errors << "Field 'compatibility' exceeds maximum length of #{COMPATIBILITY_MAX_LENGTH} characters" end end # Validate metadata field if present if data["metadata"] = data["metadata"] errors << "Field 'metadata' must be a hash/object" unless .is_a?(Hash) end # Validate allowed-tools field if present return unless data["allowed-tools"] allowed_tools = data["allowed-tools"] return if allowed_tools.is_a?(String) errors << "Field 'allowed-tools' must be a string" end |
.validate_required_fields(data, errors) ⇒ Object
Validates that all required fields are present.
102 103 104 105 106 |
# File 'lib/agents_skill_vault/skill_validator.rb', line 102 def self.validate_required_fields(data, errors) REQUIRED_FIELDS.each do |field| errors << "Required field '#{field}' is missing" unless data[field] end end |