mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-04-13 05:37:29 +08:00
274 lines
7.3 KiB
Go
274 lines
7.3 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/YspCoder/clawgo/pkg/config"
|
|
"github.com/YspCoder/clawgo/pkg/skills"
|
|
)
|
|
|
|
func skillsCmd() {
|
|
if len(os.Args) < 3 {
|
|
skillsHelp()
|
|
return
|
|
}
|
|
|
|
subcommand := os.Args[2]
|
|
|
|
cfg, err := loadConfig()
|
|
if err != nil {
|
|
fmt.Printf("Error loading config: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
workspace := cfg.WorkspacePath()
|
|
installer := skills.NewSkillInstaller(workspace)
|
|
globalDir := filepath.Dir(getConfigPath())
|
|
globalSkillsDir := filepath.Join(globalDir, "skills")
|
|
builtinSkillsDir := filepath.Join(globalDir, "clawgo", "skills")
|
|
skillsLoader := skills.NewSkillsLoader(workspace, globalSkillsDir, builtinSkillsDir)
|
|
|
|
switch subcommand {
|
|
case "list":
|
|
skillsListCmd(skillsLoader)
|
|
case "install":
|
|
skillsInstallCmd(installer)
|
|
case "remove", "uninstall":
|
|
if len(os.Args) < 4 {
|
|
fmt.Println("Usage: clawgo skills remove <skill-name>")
|
|
return
|
|
}
|
|
skillsRemoveCmd(installer, os.Args[3])
|
|
case "install-builtin":
|
|
skillsInstallBuiltinCmd(workspace)
|
|
case "list-builtin":
|
|
skillsListBuiltinCmd()
|
|
case "search":
|
|
skillsSearchCmd(installer)
|
|
case "show":
|
|
if len(os.Args) < 4 {
|
|
fmt.Println("Usage: clawgo skills show <skill-name>")
|
|
return
|
|
}
|
|
skillsShowCmd(skillsLoader, os.Args[3])
|
|
default:
|
|
fmt.Printf("Unknown skills command: %s\n", subcommand)
|
|
skillsHelp()
|
|
}
|
|
}
|
|
|
|
func skillsHelp() {
|
|
fmt.Println("\nSkills commands:")
|
|
fmt.Println(" list List installed skills")
|
|
fmt.Println(" install <repo> Install skill from GitHub")
|
|
fmt.Println(" install-builtin Install all builtin skills to workspace")
|
|
fmt.Println(" list-builtin List available builtin skills")
|
|
fmt.Println(" remove <name> Remove installed skill")
|
|
fmt.Println(" search Search available skills")
|
|
fmt.Println(" show <name> Show skill details")
|
|
fmt.Println()
|
|
fmt.Println("Examples:")
|
|
fmt.Println(" clawgo skills list")
|
|
fmt.Println(" clawgo skills install YspCoder/clawgo-skills/weather")
|
|
fmt.Println(" clawgo skills install-builtin")
|
|
fmt.Println(" clawgo skills list-builtin")
|
|
fmt.Println(" clawgo skills remove weather")
|
|
}
|
|
|
|
func skillsListCmd(loader *skills.SkillsLoader) {
|
|
allSkills := loader.ListSkills()
|
|
|
|
if len(allSkills) == 0 {
|
|
fmt.Println("No skills installed.")
|
|
return
|
|
}
|
|
|
|
fmt.Println("\nInstalled Skills:")
|
|
fmt.Println("------------------")
|
|
for _, skill := range allSkills {
|
|
fmt.Printf(" ✓ %s (%s)\n", skill.Name, skill.Source)
|
|
if skill.Description != "" {
|
|
fmt.Printf(" %s\n", skill.Description)
|
|
}
|
|
}
|
|
}
|
|
|
|
func skillsInstallCmd(installer *skills.SkillInstaller) {
|
|
if len(os.Args) < 4 {
|
|
fmt.Println("Usage: clawgo skills install <github-repo>")
|
|
fmt.Println("Example: clawgo skills install YspCoder/clawgo-skills/weather")
|
|
return
|
|
}
|
|
|
|
repo := os.Args[3]
|
|
fmt.Printf("Installing skill from %s...\n", repo)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
if err := installer.InstallFromGitHub(ctx, repo); err != nil {
|
|
fmt.Printf("✗ Failed to install skill: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
fmt.Printf("✓ Skill '%s' installed successfully!\n", filepath.Base(repo))
|
|
}
|
|
|
|
func skillsRemoveCmd(installer *skills.SkillInstaller, skillName string) {
|
|
fmt.Printf("Removing skill '%s'...\n", skillName)
|
|
|
|
if err := installer.Uninstall(skillName); err != nil {
|
|
fmt.Printf("✗ Failed to remove skill: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
fmt.Printf("✓ Skill '%s' removed successfully!\n", skillName)
|
|
}
|
|
|
|
func skillsInstallBuiltinCmd(workspace string) {
|
|
builtinSkillsDir := detectBuiltinSkillsDir(workspace)
|
|
workspaceSkillsDir := filepath.Join(workspace, "skills")
|
|
|
|
fmt.Printf("Copying builtin skills to workspace...\n")
|
|
|
|
skillsToInstall := []string{"weather", "news", "stock", "calculator"}
|
|
|
|
for _, skillName := range skillsToInstall {
|
|
builtinPath := filepath.Join(builtinSkillsDir, skillName)
|
|
workspacePath := filepath.Join(workspaceSkillsDir, skillName)
|
|
|
|
if _, err := os.Stat(builtinPath); err != nil {
|
|
fmt.Printf("⊘ Builtin skill '%s' not found: %v\n", skillName, err)
|
|
continue
|
|
}
|
|
|
|
if err := os.MkdirAll(workspacePath, 0755); err != nil {
|
|
fmt.Printf("✗ Failed to create directory for %s: %v\n", skillName, err)
|
|
continue
|
|
}
|
|
|
|
if err := copyDirectory(builtinPath, workspacePath); err != nil {
|
|
fmt.Printf("✗ Failed to copy %s: %v\n", skillName, err)
|
|
}
|
|
}
|
|
|
|
fmt.Println("\n✓ All builtin skills installed!")
|
|
fmt.Println("Now you can use them in your workspace.")
|
|
}
|
|
|
|
func skillsListBuiltinCmd() {
|
|
cfg, err := loadConfig()
|
|
if err != nil {
|
|
fmt.Printf("Error loading config: %v\n", err)
|
|
return
|
|
}
|
|
builtinSkillsDir := detectBuiltinSkillsDir(cfg.WorkspacePath())
|
|
|
|
fmt.Println("\nAvailable Builtin Skills:")
|
|
fmt.Println("-----------------------")
|
|
|
|
entries, err := os.ReadDir(builtinSkillsDir)
|
|
if err != nil {
|
|
fmt.Printf("Error reading builtin skills: %v\n", err)
|
|
return
|
|
}
|
|
|
|
if len(entries) == 0 {
|
|
fmt.Println("No builtin skills available.")
|
|
return
|
|
}
|
|
|
|
for _, entry := range entries {
|
|
if entry.IsDir() {
|
|
skillName := entry.Name()
|
|
skillFile := filepath.Join(builtinSkillsDir, skillName, "SKILL.md")
|
|
|
|
description := "No description"
|
|
if _, err := os.Stat(skillFile); err == nil {
|
|
data, err := os.ReadFile(skillFile)
|
|
if err == nil {
|
|
content := string(data)
|
|
if idx := strings.Index(content, "\n"); idx > 0 {
|
|
firstLine := content[:idx]
|
|
if strings.Contains(firstLine, "description:") {
|
|
descLine := strings.Index(content[idx:], "\n")
|
|
if descLine > 0 {
|
|
description = strings.TrimSpace(content[idx+descLine : idx+descLine])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
status := "✓"
|
|
fmt.Printf(" %s %s\n", status, entry.Name())
|
|
if description != "" {
|
|
fmt.Printf(" %s\n", description)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func detectBuiltinSkillsDir(workspace string) string {
|
|
candidates := []string{
|
|
filepath.Join(".", "skills"),
|
|
filepath.Join(filepath.Dir(workspace), "clawgo", "skills"),
|
|
filepath.Join(config.GetConfigDir(), "clawgo", "skills"),
|
|
}
|
|
for _, dir := range candidates {
|
|
if info, err := os.Stat(dir); err == nil && info.IsDir() {
|
|
return dir
|
|
}
|
|
}
|
|
return filepath.Join(".", "skills")
|
|
}
|
|
|
|
func skillsSearchCmd(installer *skills.SkillInstaller) {
|
|
fmt.Println("Searching for available skills...")
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
availableSkills, err := installer.ListAvailableSkills(ctx)
|
|
if err != nil {
|
|
fmt.Printf("✗ Failed to fetch skills list: %v\n", err)
|
|
return
|
|
}
|
|
|
|
if len(availableSkills) == 0 {
|
|
fmt.Println("No skills available.")
|
|
return
|
|
}
|
|
|
|
fmt.Printf("\nAvailable Skills (%d):\n", len(availableSkills))
|
|
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)
|
|
}
|
|
fmt.Println()
|
|
}
|
|
}
|
|
|
|
func skillsShowCmd(loader *skills.SkillsLoader, skillName string) {
|
|
content, ok := loader.LoadSkill(skillName)
|
|
if !ok {
|
|
fmt.Printf("✗ Skill '%s' not found\n", skillName)
|
|
return
|
|
}
|
|
|
|
fmt.Printf("\n📦 Skill: %s\n", skillName)
|
|
fmt.Println("----------------------")
|
|
fmt.Println(content)
|
|
}
|