Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/picoclaw/internal/skills/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func NewSkillsCommand() *cobra.Command {
newInstallBuiltinCommand(workspaceFn),
newListBuiltinCommand(),
newRemoveCommand(installerFn),
newSearchCommand(installerFn),
newSearchCommand(),
newShowCommand(loaderFn),
)

Expand Down
37 changes: 24 additions & 13 deletions cmd/picoclaw/internal/skills/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
"github.com/sipeed/picoclaw/pkg/utils"
)

const skillsSearchMaxResults = 20

func skillsListCmd(loader *skills.SkillsLoader) {
allSkills := loader.ListSkills()

Expand Down Expand Up @@ -215,34 +217,43 @@ func skillsListBuiltinCmd() {
}
}

func skillsSearchCmd(installer *skills.SkillInstaller) {
func skillsSearchCmd(query string) {
fmt.Println("Searching for available skills...")

cfg, err := internal.LoadConfig()
if err != nil {
fmt.Printf("βœ— Failed to load config: %v\n", err)
return
}

registryMgr := skills.NewRegistryManagerFromConfig(skills.RegistryConfig{
MaxConcurrentSearches: cfg.Tools.Skills.MaxConcurrentSearches,
ClawHub: skills.ClawHubConfig(cfg.Tools.Skills.Registries.ClawHub),
})

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

availableSkills, err := installer.ListAvailableSkills(ctx)
results, err := registryMgr.SearchAll(ctx, query, skillsSearchMaxResults)
if err != nil {
fmt.Printf("βœ— Failed to fetch skills list: %v\n", err)
return
}

if len(availableSkills) == 0 {
if len(results) == 0 {
fmt.Println("No skills available.")
return
}

fmt.Printf("\nAvailable Skills (%d):\n", len(availableSkills))
fmt.Printf("\nAvailable Skills (%d):\n", len(results))
fmt.Println("--------------------")
for _, skill := range availableSkills {
fmt.Printf(" πŸ“¦ %s\n", skill.Name)
fmt.Printf(" %s\n", skill.Description)
fmt.Printf(" Repo: %s\n", skill.Repository)
if skill.Author != "" {
fmt.Printf(" Author: %s\n", skill.Author)
}
if len(skill.Tags) > 0 {
fmt.Printf(" Tags: %v\n", skill.Tags)
for _, result := range results {
fmt.Printf(" πŸ“¦ %s\n", result.DisplayName)
fmt.Printf(" %s\n", result.Summary)
fmt.Printf(" Slug: %s\n", result.Slug)
fmt.Printf(" Registry: %s\n", result.RegistryName)
if result.Version != "" {
fmt.Printf(" Version: %s\n", result.Version)
}
fmt.Println()
}
Expand Down
17 changes: 8 additions & 9 deletions cmd/picoclaw/internal/skills/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@ package skills

import (
"github.com/spf13/cobra"

"github.com/sipeed/picoclaw/pkg/skills"
)

func newSearchCommand(installerFn func() (*skills.SkillInstaller, error)) *cobra.Command {
func newSearchCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "search",
Use: "search [query]",
Short: "Search available skills",
RunE: func(_ *cobra.Command, _ []string) error {
installer, err := installerFn()
if err != nil {
return err
Args: cobra.MaximumNArgs(1),
RunE: func(_ *cobra.Command, args []string) error {
query := ""
if len(args) == 1 {
query = args[0]
}
skillsSearchCmd(installer)
skillsSearchCmd(query)
return nil
},
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/picoclaw/internal/skills/search_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import (
)

func TestNewSearchSubcommand(t *testing.T) {
cmd := newSearchCommand(nil)
cmd := newSearchCommand()

require.NotNil(t, cmd)

assert.Equal(t, "search", cmd.Use)
assert.Equal(t, "search [query]", cmd.Use)
assert.Equal(t, "Search available skills", cmd.Short)

assert.Nil(t, cmd.Run)
Expand Down
41 changes: 0 additions & 41 deletions pkg/skills/installer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package skills

import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
Expand All @@ -18,14 +17,6 @@ type SkillInstaller struct {
workspace string
}

type AvailableSkill struct {
Name string `json:"name"`
Repository string `json:"repository"`
Description string `json:"description"`
Author string `json:"author"`
Tags []string `json:"tags"`
}

func NewSkillInstaller(workspace string) *SkillInstaller {
return &SkillInstaller{
workspace: workspace,
Expand Down Expand Up @@ -89,35 +80,3 @@ func (si *SkillInstaller) Uninstall(skillName string) error {

return nil
}

func (si *SkillInstaller) ListAvailableSkills(ctx context.Context) ([]AvailableSkill, error) {
url := "https://raw.githubusercontent.com/sipeed/picoclaw-skills/main/skills.json"

client := &http.Client{Timeout: 15 * time.Second}
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}

resp, err := utils.DoRequestWithRetry(client, req)
if err != nil {
return nil, fmt.Errorf("failed to fetch skills list: %w", err)
}
defer resp.Body.Close()

if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to fetch skills list: HTTP %d", resp.StatusCode)
}

body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err)
}

var skills []AvailableSkill
if err := json.Unmarshal(body, &skills); err != nil {
return nil, fmt.Errorf("failed to parse skills list: %w", err)
}

return skills, nil
}