matchesSkillConvention checks whether a relative SKILL.md path matches any recognized skill directory convention, mirroring `gh skill install` discovery logic. Paths with hidden (dot-prefixed) segments are rejected.
(relPath string)
| 1733 | // any recognized skill directory convention, mirroring `gh skill install` |
| 1734 | // discovery logic. Paths with hidden (dot-prefixed) segments are rejected. |
| 1735 | func matchesSkillConvention(relPath string) bool { |
| 1736 | parts := strings.Split(relPath, "/") |
| 1737 | |
| 1738 | // Reject any path with a hidden segment. |
| 1739 | for _, part := range parts { |
| 1740 | if strings.HasPrefix(part, ".") { |
| 1741 | return false |
| 1742 | } |
| 1743 | } |
| 1744 | |
| 1745 | // The file itself must be the manifest (SKILL.md, AGENT.md, etc.) |
| 1746 | // and the parent dir is the skill name. |
| 1747 | if len(parts) < 2 { |
| 1748 | return false |
| 1749 | } |
| 1750 | |
| 1751 | // skills/name/SKILL.md → standard flat (3 parts) |
| 1752 | if len(parts) == 3 && parts[0] == "skills" { |
| 1753 | return true |
| 1754 | } |
| 1755 | |
| 1756 | // skills/scope/name/SKILL.md → namespaced (4 parts) |
| 1757 | if len(parts) == 4 && parts[0] == "skills" { |
| 1758 | return true |
| 1759 | } |
| 1760 | |
| 1761 | // {prefix}/skills/name/SKILL.md → deeply nested skills/ dir |
| 1762 | for i, part := range parts { |
| 1763 | if part == "skills" && i < len(parts)-2 { |
| 1764 | return true |
| 1765 | } |
| 1766 | } |
| 1767 | |
| 1768 | // name/SKILL.md → root-level (2 parts, name is parent dir) |
| 1769 | if len(parts) == 2 { |
| 1770 | return true |
| 1771 | } |
| 1772 | |
| 1773 | return false |
| 1774 | } |
| 1775 | |
| 1776 | // selectItems filters discovered items based on user intent: |
| 1777 | // - skillName given → find that specific item |