Add skill testing workflow and clarify Nix integration
- Add scripts/test-skill.sh for testing skills without deploying - Update AGENTS.md with accurate Nix integration details - Explain agent config nuance (embedded vs symlinked) - Fix quick_validate.py missing skill_md variable - Update README.md to match documentation changes
This commit is contained in:
@@ -9,18 +9,20 @@ import re
|
||||
import yaml
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def validate_skill(skill_path):
|
||||
"""Basic validation of a skill"""
|
||||
skill_path = Path(skill_path)
|
||||
skill_md = skill_path / "SKILL.md"
|
||||
|
||||
if not skill_md.exists():
|
||||
return False, "SKILL.md not found"
|
||||
|
||||
content = skill_md.read_text()
|
||||
if not content.startswith('---'):
|
||||
if not content.startswith("---"):
|
||||
return False, "No YAML frontmatter found"
|
||||
|
||||
match = re.match(r'^---\n(.*?)\n---', content, re.DOTALL)
|
||||
match = re.match(r"^---\n(.*?)\n---", content, re.DOTALL)
|
||||
if not match:
|
||||
return False, "Invalid frontmatter format"
|
||||
|
||||
@@ -33,7 +35,14 @@ def validate_skill(skill_path):
|
||||
except yaml.YAMLError as e:
|
||||
return False, f"Invalid YAML in frontmatter: {e}"
|
||||
|
||||
ALLOWED_PROPERTIES = {'name', 'description', 'license', 'allowed-tools', 'metadata', 'compatibility'}
|
||||
ALLOWED_PROPERTIES = {
|
||||
"name",
|
||||
"description",
|
||||
"license",
|
||||
"allowed-tools",
|
||||
"metadata",
|
||||
"compatibility",
|
||||
}
|
||||
|
||||
unexpected_keys = set(frontmatter.keys()) - ALLOWED_PROPERTIES
|
||||
if unexpected_keys:
|
||||
@@ -42,40 +51,53 @@ def validate_skill(skill_path):
|
||||
f"Allowed properties are: {', '.join(sorted(ALLOWED_PROPERTIES))}"
|
||||
)
|
||||
|
||||
if 'name' not in frontmatter:
|
||||
if "name" not in frontmatter:
|
||||
return False, "Missing 'name' in frontmatter"
|
||||
if 'description' not in frontmatter:
|
||||
if "description" not in frontmatter:
|
||||
return False, "Missing 'description' in frontmatter"
|
||||
|
||||
name = frontmatter.get('name', '')
|
||||
name = frontmatter.get("name", "")
|
||||
if not isinstance(name, str):
|
||||
return False, f"Name must be a string, got {type(name).__name__}"
|
||||
name = name.strip()
|
||||
if name:
|
||||
if not re.match(r'^[a-z0-9-]+$', name):
|
||||
return False, f"Name '{name}' should be hyphen-case (lowercase letters, digits, and hyphens only)"
|
||||
if name.startswith('-') or name.endswith('-') or '--' in name:
|
||||
return False, f"Name '{name}' cannot start/end with hyphen or contain consecutive hyphens"
|
||||
if not re.match(r"^[a-z0-9-]+$", name):
|
||||
return (
|
||||
False,
|
||||
f"Name '{name}' should be hyphen-case (lowercase letters, digits, and hyphens only)",
|
||||
)
|
||||
if name.startswith("-") or name.endswith("-") or "--" in name:
|
||||
return (
|
||||
False,
|
||||
f"Name '{name}' cannot start/end with hyphen or contain consecutive hyphens",
|
||||
)
|
||||
if len(name) > 64:
|
||||
return False, f"Name is too long ({len(name)} characters). Maximum is 64 characters."
|
||||
return (
|
||||
False,
|
||||
f"Name is too long ({len(name)} characters). Maximum is 64 characters.",
|
||||
)
|
||||
|
||||
description = frontmatter.get('description', '')
|
||||
description = frontmatter.get("description", "")
|
||||
if not isinstance(description, str):
|
||||
return False, f"Description must be a string, got {type(description).__name__}"
|
||||
description = description.strip()
|
||||
if description:
|
||||
if '<' in description or '>' in description:
|
||||
if "<" in description or ">" in description:
|
||||
return False, "Description cannot contain angle brackets (< or >)"
|
||||
if len(description) > 1024:
|
||||
return False, f"Description is too long ({len(description)} characters). Maximum is 1024 characters."
|
||||
return (
|
||||
False,
|
||||
f"Description is too long ({len(description)} characters). Maximum is 1024 characters.",
|
||||
)
|
||||
|
||||
return True, "Skill is valid!"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: python quick_validate.py <skill_directory>")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
valid, message = validate_skill(sys.argv[1])
|
||||
print(message)
|
||||
sys.exit(0 if valid else 1)
|
||||
|
||||
Reference in New Issue
Block a user