diff --git a/agent/agent.py b/agent/agent.py index dd8a5ed..9f9140a 100644 --- a/agent/agent.py +++ b/agent/agent.py @@ -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):