# Recursive skill discovery: one `{ name; src; }` per skill in a source tree. lib: root: let walk = dir: let subdirs = lib.filterAttrs (_: type: type == "directory") (builtins.readDir dir); in lib.concatLists ( lib.mapAttrsToList ( entryName: _: let child = dir + "/${entryName}"; in # A directory with a SKILL.md is a skill, and its subdirectories are # that skill's assets rather than nested skills, so the walk stops here. # A directory without one is a cosmetic container to recurse through. if (builtins.readDir child) ? "SKILL.md" then [ { name = entryName; src = child; } ] else walk child ) subdirs ); found = walk root; names = map (s: s.name) found; duplicates = lib.unique (lib.filter (n: lib.count (x: x == n) names > 1) names); in if duplicates != [ ] then throw '' Skill name collision: ${lib.concatStringsSep ", " duplicates}. Two or more skills under ${toString root} resolve to the same name. A skill's name is its leaf directory name and must be globally unique across the whole tree, independent of the cosmetic folders above it. Rename one of the colliding skills.'' else found