DiscoverSkillByPathWithOptions looks up a single skill by its exact path in the repository, applying the given options.
(client *api.Client, host, owner, repo, commitSHA, skillPath string, opts DiscoverSkillByPathOptions)
| 688 | // DiscoverSkillByPathWithOptions looks up a single skill by its exact path in |
| 689 | // the repository, applying the given options. |
| 690 | func DiscoverSkillByPathWithOptions(client *api.Client, host, owner, repo, commitSHA, skillPath string, opts DiscoverSkillByPathOptions) (*Skill, error) { |
| 691 | skillPath = strings.TrimSuffix(skillPath, "/SKILL.md") |
| 692 | skillPath = strings.TrimSuffix(skillPath, "/") |
| 693 | |
| 694 | skillName := path.Base(skillPath) |
| 695 | if !validateName(skillName) { |
| 696 | return nil, fmt.Errorf("invalid skill name %q", skillName) |
| 697 | } |
| 698 | |
| 699 | parentPath := path.Dir(skillPath) |
| 700 | apiPath := fmt.Sprintf("repos/%s/%s/contents/%s?ref=%s", url.PathEscape(owner), url.PathEscape(repo), url.PathEscape(parentPath), commitSHA) |
| 701 | |
| 702 | var contents []struct { |
| 703 | Name string `json:"name"` |
| 704 | Path string `json:"path"` |
| 705 | SHA string `json:"sha"` |
| 706 | Type string `json:"type"` |
| 707 | } |
| 708 | if err := client.REST(host, "GET", apiPath, nil, &contents); err != nil { |
| 709 | return nil, fmt.Errorf("path %q not found in %s/%s: %w", parentPath, owner, repo, err) |
| 710 | } |
| 711 | |
| 712 | var treeSHA string |
| 713 | for _, entry := range contents { |
| 714 | if entry.Name == skillName && entry.Type == "dir" { |
| 715 | treeSHA = entry.SHA |
| 716 | break |
| 717 | } |
| 718 | } |
| 719 | if treeSHA == "" { |
| 720 | return nil, fmt.Errorf("skill directory %q not found in %s/%s", skillPath, owner, repo) |
| 721 | } |
| 722 | |
| 723 | skillTreePath := fmt.Sprintf("repos/%s/%s/git/trees/%s", url.PathEscape(owner), url.PathEscape(repo), url.PathEscape(treeSHA)) |
| 724 | var skillTree treeResponse |
| 725 | if err := client.REST(host, "GET", skillTreePath, nil, &skillTree); err != nil { |
| 726 | return nil, fmt.Errorf("could not read skill directory: %w", err) |
| 727 | } |
| 728 | |
| 729 | var blobSHA string |
| 730 | for _, entry := range skillTree.Tree { |
| 731 | if entry.Path == "SKILL.md" && entry.Type == "blob" { |
| 732 | blobSHA = entry.SHA |
| 733 | break |
| 734 | } |
| 735 | } |
| 736 | if blobSHA == "" { |
| 737 | return nil, fmt.Errorf("no SKILL.md found in %s", skillPath) |
| 738 | } |
| 739 | |
| 740 | var namespace, convention string |
| 741 | parts := strings.Split(skillPath, "/") |
| 742 | for i, p := range parts { |
| 743 | if p != "skills" { |
| 744 | continue |
| 745 | } |
| 746 | |
| 747 | // Plugin convention: .../plugins/<ns>/skills/<name> |