@@ -633,34 +633,40 @@ def _fetch_skills_from_repo(self, repo: SkillRepo) -> List[Skill]:
633633 logger .warning (f"Skills path not found: { scan_dir } " )
634634 return skills
635635
636- # Scan for skill directories (those containing SKILL.md)
637- for item in scan_dir .iterdir ():
638- if not item .is_dir ():
639- continue
640-
641- skill_md = item / "SKILL.md"
642- if not skill_md .exists ():
636+ # Scan for SKILL.md files recursively
637+ for skill_md in scan_dir .rglob ("SKILL.md" ):
638+ skill_dir = skill_md .parent
639+ if not skill_dir .is_dir ():
643640 continue
644641
645642 # Parse skill metadata from SKILL.md
646643 meta = self ._parse_skill_metadata (skill_md )
647- directory = item .name
644+
645+ try :
646+ # Calculate relative path from scan_dir
647+ rel_path = skill_dir .relative_to (scan_dir )
648+ directory = str (rel_path ).replace ("\\ " , "/" )
649+
650+ # Skip if SKILL.md is at the root of scan_dir (directory == ".")
651+ # to avoid conflicts when installing to the root of skills directory
652+ if directory == "." :
653+ continue
654+ except ValueError :
655+ continue
648656
649657 # Build README URL using actual branch
650- if repo .skills_path :
651- readme_path = f"{ repo .skills_path .strip ('/' )} /{ directory } "
652- else :
653- readme_path = directory
658+ path_from_repo_root = skill_dir .relative_to (temp_dir )
659+ readme_path = str (path_from_repo_root ).replace ("\\ " , "/" )
654660
655661 skill = Skill (
656662 key = f"{ repo .owner } /{ repo .name } :{ directory } " ,
657- name = meta .get ("name" , directory ),
663+ name = meta .get ("name" , directory . split ( "/" )[ - 1 ] ),
658664 description = meta .get ("description" , "" ),
659665 directory = directory ,
660666 installed = False ,
661667 repo_owner = repo .owner ,
662668 repo_name = repo .name ,
663- repo_branch = actual_branch , # Use actual branch, not configured branch
669+ repo_branch = actual_branch ,
664670 skills_path = repo .skills_path ,
665671 readme_url = f"https://github.com/{ repo .owner } /{ repo .name } /tree/{ actual_branch } /{ readme_path } " ,
666672 )
@@ -723,12 +729,17 @@ def get_installed_skills(self, app_type: str = "claude") -> List[Skill]:
723729 installed_skills = []
724730 existing_skills = self ._load_skills ()
725731
726- for item in install_dir .iterdir ():
727- if not item .is_dir ():
732+ # Scan recursively for SKILL.md
733+ for skill_md in install_dir .rglob ("SKILL.md" ):
734+ skill_dir = skill_md .parent
735+ if not skill_dir .is_dir ():
728736 continue
729737
730- directory = item .name
731- skill_md = item / "SKILL.md"
738+ try :
739+ rel_path = skill_dir .relative_to (install_dir )
740+ directory = str (rel_path ).replace ("\\ " , "/" )
741+ except ValueError :
742+ continue
732743
733744 # Check if we have this skill in our database
734745 matching_skill = None
@@ -740,12 +751,12 @@ def get_installed_skills(self, app_type: str = "claude") -> List[Skill]:
740751 if matching_skill :
741752 matching_skill .installed = True
742753 installed_skills .append (matching_skill )
743- elif skill_md . exists () :
754+ else :
744755 # Local skill not in our database
745756 meta = self ._parse_skill_metadata (skill_md )
746757 skill = Skill (
747758 key = f"local:{ directory } " ,
748- name = meta .get ("name" , directory ),
759+ name = meta .get ("name" , directory . split ( "/" )[ - 1 ] ),
749760 description = meta .get ("description" , "" ),
750761 directory = directory ,
751762 installed = True ,
@@ -767,9 +778,14 @@ def sync_installed_status(self, app_type: str = "claude") -> None:
767778
768779 installed_dirs = set ()
769780 if install_dir .exists ():
770- installed_dirs = {
771- item .name .lower () for item in install_dir .iterdir () if item .is_dir ()
772- }
781+ # Scan recursively for SKILL.md
782+ for skill_md in install_dir .rglob ("SKILL.md" ):
783+ try :
784+ skill_dir = skill_md .parent
785+ rel_path = skill_dir .relative_to (install_dir )
786+ installed_dirs .add (str (rel_path ).replace ("\\ " , "/" ).lower ())
787+ except ValueError :
788+ continue
773789
774790 skills = self ._load_skills ()
775791 for skill in skills .values ():
0 commit comments