55 "fmt"
66 "io"
77 "net/http"
8+ "net/url"
89 "os"
910 "path/filepath"
1011 "regexp"
@@ -19,6 +20,7 @@ type RepoInfo struct {
1920 Repo string
2021 Path string // subdirectory path (for monorepo skills)
2122 Branch string
23+ TreeRef string
2224 FullURL string
2325 CloneURL string
2426}
@@ -38,14 +40,43 @@ func ParseGitHubURL(input string) (*RepoInfo, error) {
3840
3941 // Full GitHub URL
4042 if strings .HasPrefix (input , "https://github.com/" ) {
41- // Pattern: https://github.com/owner/repo/tree/branch/path
42- treePattern := regexp .MustCompile (`https://github\.com/([^/]+)/([^/]+)/tree/([^/]+)(?:/(.+))?` )
43- if matches := treePattern .FindStringSubmatch (input ); len (matches ) >= 4 {
44- info .Owner = matches [1 ]
45- info .Repo = matches [2 ]
46- info .Branch = matches [3 ]
47- if len (matches ) > 4 {
48- info .Path = matches [4 ]
43+ raw := input
44+ if idx := strings .IndexAny (raw , "?#" ); idx != - 1 {
45+ raw = raw [:idx ]
46+ }
47+
48+ if strings .Contains (raw , "/tree/" ) {
49+ u , err := url .Parse (raw )
50+ if err != nil {
51+ return nil , fmt .Errorf ("invalid GitHub URL format: %s" , input )
52+ }
53+
54+ parts := strings .Split (strings .Trim (u .Path , "/" ), "/" )
55+ if len (parts ) < 4 || parts [2 ] != "tree" {
56+ return nil , fmt .Errorf ("invalid GitHub URL format: %s" , input )
57+ }
58+
59+ info .Owner = parts [0 ]
60+ info .Repo = parts [1 ]
61+
62+ treeParts := parts [3 :]
63+ if len (treeParts ) == 0 {
64+ return nil , fmt .Errorf ("invalid GitHub URL format: %s" , input )
65+ }
66+
67+ info .TreeRef = strings .Join (treeParts , "/" )
68+ if strings .Contains (info .TreeRef , "%2F" ) || strings .Contains (info .TreeRef , "%2f" ) {
69+ decoded , err := url .PathUnescape (info .TreeRef )
70+ if err != nil {
71+ return nil , fmt .Errorf ("invalid GitHub URL format: %s" , input )
72+ }
73+ info .Branch = decoded
74+ info .Path = ""
75+ } else {
76+ info .Branch = treeParts [0 ]
77+ if len (treeParts ) > 1 {
78+ info .Path = strings .Join (treeParts [1 :], "/" )
79+ }
4980 }
5081 } else {
5182 // Pattern: https://github.com/owner/repo
@@ -93,9 +124,9 @@ func DownloadAndExtract(info *RepoInfo, targetName string) error {
93124 if err != nil {
94125 return fmt .Errorf ("failed to download: %w" , err )
95126 }
96- defer resp .Body .Close ()
97127
98128 if resp .StatusCode != http .StatusOK {
129+ resp .Body .Close ()
99130 // Try 'master' branch if 'main' fails
100131 if info .Branch == "main" {
101132 info .Branch = "master"
@@ -105,12 +136,13 @@ func DownloadAndExtract(info *RepoInfo, targetName string) error {
105136 if err != nil {
106137 return fmt .Errorf ("failed to download: %w" , err )
107138 }
108- defer resp .Body .Close ()
109- }
110- if resp .StatusCode != http .StatusOK {
111- return fmt .Errorf ("download failed with status: %s" , resp .Status )
112139 }
113140 }
141+ defer resp .Body .Close ()
142+
143+ if resp .StatusCode != http .StatusOK {
144+ return fmt .Errorf ("download failed with status: %s" , resp .Status )
145+ }
114146
115147 // Create temp file for zip
116148 tmpFile , err := os .CreateTemp ("" , "sk-*.zip" )
@@ -148,6 +180,10 @@ func DownloadAndExtract(info *RepoInfo, targetName string) error {
148180 }
149181
150182 if err != nil {
183+ if info .TreeRef != "" && strings .Contains (info .TreeRef , "/" ) &&
184+ ! strings .Contains (info .TreeRef , "%2F" ) && ! strings .Contains (info .TreeRef , "%2f" ) {
185+ return fmt .Errorf ("failed to extract: %w (if your branch contains '/', URL-encode it, e.g. feature%%2Ffoo)" , err )
186+ }
151187 return fmt .Errorf ("failed to extract: %w" , err )
152188 }
153189
0 commit comments