Load single skill from SKILL.md file Args: skill_path: SKILL.md file path Returns: Skill object, or None if loading fails
(self, skill_path: Path)
| 58 | self.loaded_skills: Dict[str, Skill] = {} |
| 59 | |
| 60 | def load_skill(self, skill_path: Path) -> Optional[Skill]: |
| 61 | """ |
| 62 | Load single skill from SKILL.md file |
| 63 | |
| 64 | Args: |
| 65 | skill_path: SKILL.md file path |
| 66 | |
| 67 | Returns: |
| 68 | Skill object, or None if loading fails |
| 69 | """ |
| 70 | try: |
| 71 | content = skill_path.read_text(encoding="utf-8") |
| 72 | |
| 73 | # Parse YAML frontmatter |
| 74 | frontmatter_match = re.match(r"^---\n(.*?)\n---\n(.*)$", content, re.DOTALL) |
| 75 | |
| 76 | if not frontmatter_match: |
| 77 | print(f"⚠️ {skill_path} missing YAML frontmatter") |
| 78 | return None |
| 79 | |
| 80 | frontmatter_text = frontmatter_match.group(1) |
| 81 | skill_content = frontmatter_match.group(2).strip() |
| 82 | |
| 83 | # Parse YAML |
| 84 | try: |
| 85 | frontmatter = yaml.safe_load(frontmatter_text) |
| 86 | except yaml.YAMLError as e: |
| 87 | print(f"❌ Failed to parse YAML frontmatter: {e}") |
| 88 | return None |
| 89 | |
| 90 | # Required fields |
| 91 | if "name" not in frontmatter or "description" not in frontmatter: |
| 92 | print(f"⚠️ {skill_path} missing required fields (name or description)") |
| 93 | return None |
| 94 | |
| 95 | # Get skill directory (parent of SKILL.md) |
| 96 | skill_dir = skill_path.parent |
| 97 | |
| 98 | # Replace relative paths in content with absolute paths |
| 99 | # This ensures scripts and resources can be found from any working directory |
| 100 | processed_content = self._process_skill_paths(skill_content, skill_dir) |
| 101 | |
| 102 | # Create Skill object |
| 103 | skill = Skill( |
| 104 | name=frontmatter["name"], |
| 105 | description=frontmatter["description"], |
| 106 | content=processed_content, |
| 107 | license=frontmatter.get("license"), |
| 108 | allowed_tools=frontmatter.get("allowed-tools"), |
| 109 | metadata=frontmatter.get("metadata"), |
| 110 | skill_path=skill_path, |
| 111 | ) |
| 112 | |
| 113 | return skill |
| 114 | |
| 115 | except Exception as e: |
| 116 | print(f"❌ Failed to load skill ({skill_path}): {e}") |
| 117 | return None |