Coverage for merco/skills/loader.py: 92%

37 statements  

« prev     ^ index     » next       coverage.py v7.15.0, created at 2026-07-07 14:04 +0800

1"""技能加载器 - 从文件系统加载 SKILL.md""" 

2 

3import re 

4from pathlib import Path 

5from typing import Optional 

6 

7 

8class SkillLoader: 

9 """从目录加载技能""" 

10 

11 SKILL_FILE = "SKILL.md" 

12 

13 @classmethod 

14 def load_from_path(cls, path: str) -> Optional[dict]: 

15 """从路径加载单个技能""" 

16 skill_path = Path(path).expanduser() / cls.SKILL_FILE 

17 if not skill_path.exists(): 

18 return None 

19 

20 content = skill_path.read_text() 

21 return cls._parse_skill(content) 

22 

23 @classmethod 

24 def load_from_directory(cls, directory: str) -> list[dict]: 

25 """从目录递归加载所有技能""" 

26 skills = [] 

27 base = Path(directory).expanduser() 

28 

29 if not base.exists(): 

30 return skills 

31 

32 for skill_dir in base.rglob("*"): 

33 if skill_dir.is_dir() and (skill_dir / cls.SKILL_FILE).exists(): 

34 skill = cls.load_from_path(str(skill_dir)) 

35 if skill: 

36 skills.append(skill) 

37 

38 return skills 

39 

40 @classmethod 

41 def _parse_skill(cls, content: str) -> dict: 

42 """解析技能文件(支持 frontmatter)""" 

43 # 提取 frontmatter 

44 frontmatter_match = re.match(r"^---\n(.*?)\n---\n(.*)", content, re.DOTALL) 

45 

46 if frontmatter_match: 

47 frontmatter_str = frontmatter_match.group(1) 

48 body = frontmatter_match.group(2).strip() 

49 

50 # 简单解析 YAML-like frontmatter 

51 frontmatter = {} 

52 for line in frontmatter_str.splitlines(): 

53 if ":" in line: 

54 key, value = line.split(":", 1) 

55 frontmatter[key.strip()] = value.strip() 

56 

57 return { 

58 "name": frontmatter.get("name", "unknown"), 

59 "description": frontmatter.get("description", ""), 

60 "content": body, 

61 "metadata": frontmatter, 

62 } 

63 

64 return { 

65 "name": Path(content[:50]).stem if content else "unknown", 

66 "description": "", 

67 "content": content, 

68 "metadata": {}, 

69 }