Skip to content

Commit aea9d0d

Browse files
committed
chore: release v0.9.0
1 parent d7da585 commit aea9d0d

File tree

5 files changed

+40
-19
lines changed

5 files changed

+40
-19
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.9.0] - 2026-01-21
9+
10+
### Changed
11+
- **Config Tests**: Fixed and updated `internal/config` tests to align with default repository configuration.
12+
- **Code Quality**: Resolved linter warnings including `io/ioutil` deprecation and unhandled errors.
13+
- **Stability**: general improvements and test coverage updates.
14+
815
## [0.8.0] - 2026-01-19
916

1017
### Added

cmd/install.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,6 @@ If no agent is specified, skills are installed to .agent/skills/ by default.`,
131131

132132
// If git discovery failed or wasn't applicable, fall back to API
133133
if err != nil || targetRepo.Type != "dir" {
134-
if err != nil && targetRepo.Type == "dir" {
135-
// Optional: log or print why git failed if verbose
136-
// fmt.Printf("Git discovery failed: %v. Falling back to API...\n", err)
137-
}
138134
repos, err = repository.FetchSkills(*targetRepo)
139135
if err != nil {
140136
fmt.Printf("Failed to fetch skills from repo '%s': %v\n", input, err)
@@ -269,13 +265,6 @@ func installSingleSkill(input string, global bool, agents []string) error {
269265
} else {
270266
// It's a URL (e.g., https://github.com/xxx.git)
271267
repoURL = input
272-
if !strings.HasSuffix(repoURL, ".git") && !strings.HasPrefix(input, "http") {
273-
// It might be a SkillHub slug (no standard user/repo format easily distinguishable from just a name,
274-
// but usually slugs are kebab-case).
275-
// We can try to resolve it via SkillHub if it doesn't look like a URL.
276-
// However, `input` here in this block is usually "http..." because of `isURL` check?
277-
// Wait, `isURL` is true for http/git prefix.
278-
}
279268
urlParts := strings.Split(strings.TrimSuffix(repoURL, ".git"), "/")
280269
skillName = urlParts[len(urlParts)-1]
281270
}

cmd/root.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ the Agent ecosystem.`,
4545
// Uncomment the following line if your bare application
4646
// has an action associated with it:
4747
// Run: func(cmd *cobra.Command, args []string) { },
48-
Version: "0.8.0",
48+
Version: "0.9.0",
4949
}
5050

5151
// Execute adds all child commands to the root command and sets flags appropriately.

internal/config/config_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ func TestDefaultConfig(t *testing.T) {
1414
if len(config.Skills) != 0 {
1515
t.Errorf("Expected empty skills list, got %d", len(config.Skills))
1616
}
17-
// We now have 8 default repos: community, anthropics, scientific, superpowers, openai, matlab, composio, vercel
18-
if len(config.Repos) != 8 {
19-
t.Errorf("Expected 8 default repos, got %d", len(config.Repos))
17+
// We now have 9 default repos: community, anthropics, scientific, superpowers, openai, matlab, composio, vercel, skillhub
18+
if len(config.Repos) != 9 {
19+
t.Errorf("Expected 9 default repos, got %d", len(config.Repos))
2020
}
2121
}
2222

@@ -35,6 +35,7 @@ func TestDefaultReposConfiguration(t *testing.T) {
3535
"matlab": {repoType: "dir", url: "matlab/skills/skills"},
3636
"composio": {repoType: "dir", url: "ComposioHQ/awesome-claude-skills"},
3737
"vercel": {repoType: "dir", url: "vercel-labs/agent-skills"},
38+
"skillhub": {repoType: "skillhub", url: "skills"},
3839
}
3940

4041
for _, repo := range config.Repos {

internal/skillhub/client.go

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package skillhub
33
import (
44
"encoding/json"
55
"fmt"
6-
"io/ioutil"
6+
"io"
77
"net/http"
88
"net/url"
99
"regexp"
@@ -67,7 +67,7 @@ func (c *Client) Search(query string) ([]Skill, error) {
6767
if err != nil {
6868
return nil, err
6969
}
70-
defer resp.Body.Close()
70+
defer func() { _ = resp.Body.Close() }()
7171

7272
if resp.StatusCode != http.StatusOK {
7373
return nil, fmt.Errorf("SkillHub API returned status: %d", resp.StatusCode)
@@ -88,13 +88,13 @@ func (c *Client) Resolve(slug string) (string, error) {
8888
if err != nil {
8989
return "", err
9090
}
91-
defer resp.Body.Close()
91+
defer func() { _ = resp.Body.Close() }()
9292

9393
if resp.StatusCode != http.StatusOK {
9494
return "", fmt.Errorf("failed to fetch skill page: %d", resp.StatusCode)
9595
}
9696

97-
body, err := ioutil.ReadAll(resp.Body)
97+
body, err := io.ReadAll(resp.Body)
9898
if err != nil {
9999
return "", err
100100
}
@@ -115,5 +115,29 @@ func (c *Client) Resolve(slug string) (string, error) {
115115
return rawURL, nil
116116
}
117117

118+
// Fallback: try to find repoUrl in Next.js hydration data or JSON
119+
// Matches: "repoUrl":"https://github.com/..."
120+
reJson := regexp.MustCompile(`"repoUrl":"(https://github\.com/[^"]+)"`)
121+
matchesJson := reJson.FindStringSubmatch(string(body))
122+
if len(matchesJson) > 1 {
123+
rawURL := matchesJson[1]
124+
// unescape backward slashes if any (though usually forward slashes are fine in JSON)
125+
// But in the hydration data we saw, it was like \"repoUrl\":\"https...\"
126+
// The string(body) should have the raw bytes.
127+
// If it's inside a JS string, it might be escaped.
128+
// The curl output showed: \"repoUrl\":\"https://github.com/MadAppGang/claude-code\"
129+
// So the regex needs to handle the escaped quotes?
130+
// Actually, if we use a broader regex, we can capture it.
131+
// Let's rely on finding https://github.com inside the quote.
132+
return rawURL, nil
133+
}
134+
135+
// Try one more pattern for escaped JSON
136+
reEscaped := regexp.MustCompile(`\\?"repoUrl\\?":\\?"(https://github\.com/[^"\\]+)\\?"`)
137+
matchesEscaped := reEscaped.FindStringSubmatch(string(body))
138+
if len(matchesEscaped) > 1 {
139+
return matchesEscaped[1], nil
140+
}
141+
118142
return "", fmt.Errorf("GitHub URL not found for skill: %s", slug)
119143
}

0 commit comments

Comments
 (0)