Harden SKILL.md parser — error logging, flexible indent, CRLF support, type validation

This commit is contained in:
2026-04-08 01:51:34 +00:00
parent d838fe08cf
commit 2d371ceb86

View File

@@ -67,13 +67,22 @@ except FileNotFoundError:
def parse_skill_md(path):
"""Parse a SKILL.md frontmatter into a tool definition."""
with open(path) as f:
content = f.read()
"""Parse a SKILL.md frontmatter into a tool definition.
Returns tool definition dict or None on failure."""
try:
with open(path) as f:
content = f.read()
except Exception as e:
log(f"Cannot read {path}: {e}")
return None
# Normalize line endings
content = content.replace("\r\n", "\n")
# Extract YAML frontmatter between ---
match = re.match(r"^---\n(.*?)\n---", content, re.DOTALL)
if not match:
log(f"No frontmatter in {path}")
return None
# Simple YAML-like parser (no pyyaml dependency)
@@ -87,21 +96,24 @@ def parse_skill_md(path):
if not stripped or stripped.startswith("#"):
continue
if line.startswith(" ") and current_key == "parameters":
# Inside parameters block
if line.startswith(" ") and current_param:
# Detect indent level (flexible — 2+ spaces counts as nested)
indent = len(line) - len(line.lstrip())
if indent >= 2 and current_key == "parameters":
if indent >= 4 and current_param:
# Parameter property
k, _, v = stripped.partition(":")
k = k.strip()
v = v.strip().strip('"').strip("'")
if k.strip() == "required":
v = v.lower() == "true"
params[current_param][k.strip()] = v
if k == "required":
v = v.lower() in ("true", "yes", "1")
params[current_param][k] = v
elif ":" in stripped:
# New parameter
# New parameter name
param_name = stripped.rstrip(":").strip()
current_param = param_name
params[param_name] = {}
elif ":" in line and not line.startswith(" "):
elif ":" in line and indent == 0:
k, _, v = line.partition(":")
k = k.strip()
v = v.strip().strip('"').strip("'")
@@ -111,14 +123,21 @@ def parse_skill_md(path):
current_param = None
if "name" not in fm:
log(f"No 'name' field in {path}")
return None
if "description" not in fm:
log(f"Warning: no 'description' in {path}")
# Build Ollama tool definition
properties = {}
required = []
for pname, pdata in params.items():
ptype = pdata.get("type", "string")
if ptype not in ("string", "integer", "number", "boolean", "array", "object"):
log(f"Warning: unknown type '{ptype}' for param '{pname}' in {path}")
properties[pname] = {
"type": pdata.get("type", "string"),
"type": ptype,
"description": pdata.get("description", ""),
}
if pdata.get("required", False):