This commit is contained in:
Zied
2026-03-02 09:29:59 +01:00
parent 107d03b0f8
commit c8e1c0ef19
948 changed files with 7020 additions and 2745 deletions

View File

@@ -2,6 +2,29 @@ import os
import re
import argparse
import sys
import io
def configure_utf8_output() -> None:
"""Best-effort UTF-8 stdout/stderr on Windows without dropping diagnostics."""
if sys.platform != "win32":
return
for stream_name in ("stdout", "stderr"):
stream = getattr(sys, stream_name)
try:
stream.reconfigure(encoding="utf-8", errors="backslashreplace")
continue
except Exception:
pass
buffer = getattr(stream, "buffer", None)
if buffer is not None:
setattr(
sys,
stream_name,
io.TextIOWrapper(buffer, encoding="utf-8", errors="backslashreplace"),
)
WHEN_TO_USE_PATTERNS = [
re.compile(r"^##\s+When\s+to\s+Use", re.MULTILINE | re.IGNORECASE),
@@ -12,24 +35,37 @@ WHEN_TO_USE_PATTERNS = [
def has_when_to_use_section(content):
return any(pattern.search(content) for pattern in WHEN_TO_USE_PATTERNS)
def parse_frontmatter(content):
import yaml
def parse_frontmatter(content, rel_path=None):
"""
Simple frontmatter parser using regex to avoid external dependencies.
Returns a dict of key-values.
Parse frontmatter using PyYAML for robustness.
Returns a dict of key-values and a list of error messages.
"""
fm_match = re.search(r'^---\s*\n(.*?)\n---', content, re.DOTALL)
if not fm_match:
return None
return None, ["Missing or malformed YAML frontmatter"]
fm_text = fm_match.group(1)
metadata = {}
for line in fm_text.split('\n'):
if ':' in line:
key, val = line.split(':', 1)
metadata[key.strip()] = val.strip().strip('"').strip("'")
return metadata
fm_errors = []
try:
metadata = yaml.safe_load(fm_text) or {}
# Identification of the specific regression issue for better reporting
if "description" in metadata:
desc = metadata["description"]
if not desc or (isinstance(desc, str) and not desc.strip()):
fm_errors.append("description field is empty or whitespace only.")
elif desc == "|":
fm_errors.append("description contains only the YAML block indicator '|', likely due to a parsing regression.")
return metadata, fm_errors
except yaml.YAMLError as e:
return None, [f"YAML Syntax Error: {e}"]
def validate_skills(skills_dir, strict_mode=False):
configure_utf8_output()
print(f"🔍 Validating skills in: {skills_dir}")
print(f"⚙️ Mode: {'STRICT (CI)' if strict_mode else 'Standard (Dev)'}")
@@ -60,10 +96,14 @@ def validate_skills(skills_dir, strict_mode=False):
continue
# 1. Frontmatter Check
metadata = parse_frontmatter(content)
metadata, fm_errors = parse_frontmatter(content, rel_path)
if not metadata:
errors.append(f"{rel_path}: Missing or malformed YAML frontmatter")
continue # Cannot proceed without metadata
if fm_errors:
for fe in fm_errors:
errors.append(f"{rel_path}: YAML Structure Error - {fe}")
# 2. Metadata Schema Checks
if "name" not in metadata:
@@ -71,12 +111,15 @@ def validate_skills(skills_dir, strict_mode=False):
elif metadata["name"] != os.path.basename(root):
errors.append(f"{rel_path}: Name '{metadata['name']}' does not match folder name '{os.path.basename(root)}'")
if "description" not in metadata:
if "description" not in metadata or metadata["description"] is None:
errors.append(f"{rel_path}: Missing 'description' in frontmatter")
else:
# agentskills-ref checks for short descriptions
if len(metadata["description"]) > 200:
errors.append(f"{rel_path}: Description is oversized ({len(metadata['description'])} chars). Must be concise.")
desc = metadata["description"]
if not isinstance(desc, str):
errors.append(f"{rel_path}: 'description' must be a string, got {type(desc).__name__}")
elif len(desc) > 300: # increased limit for multi-line support
errors.append(f"{rel_path}: Description is oversized ({len(desc)} chars). Must be concise.")
# Risk Validation (Quality Bar)
if "risk" not in metadata: