完成一个简易的全局skill、command管理器
This commit is contained in:
90
manager/cmd/skillmgr/add.go
Normal file
90
manager/cmd/skillmgr/add.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"skillmgr/internal/config"
|
||||
"skillmgr/internal/repo"
|
||||
"skillmgr/internal/types"
|
||||
)
|
||||
|
||||
// validateGitURL 验证 Git URL 格式
|
||||
func validateGitURL(url string) error {
|
||||
if url == "" {
|
||||
return fmt.Errorf("URL 不能为空")
|
||||
}
|
||||
// 支持 https://, http://, git@, git:// 协议
|
||||
validPrefixes := []string{"https://", "http://", "git@", "git://"}
|
||||
for _, prefix := range validPrefixes {
|
||||
if strings.HasPrefix(url, prefix) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("无效的 Git URL 格式,必须以 https://, http://, git@ 或 git:// 开头")
|
||||
}
|
||||
|
||||
var addCmd = &cobra.Command{
|
||||
Use: "add <url>",
|
||||
Short: "添加源仓库",
|
||||
Long: `添加一个 git 仓库作为 skills/commands 的源。
|
||||
|
||||
示例:
|
||||
skillmgr add https://github.com/user/skills
|
||||
skillmgr add https://github.com/user/skills --name my-skills
|
||||
skillmgr add https://github.com/user/skills --branch main`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
url := args[0]
|
||||
|
||||
// 验证 URL 格式
|
||||
if err := validateGitURL(url); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name, _ := cmd.Flags().GetString("name")
|
||||
branch, _ := cmd.Flags().GetString("branch")
|
||||
|
||||
// 如果没有指定名称,从 URL 生成
|
||||
if name == "" {
|
||||
name = repo.URLToPathName(url)
|
||||
}
|
||||
|
||||
if branch == "" {
|
||||
branch = "main" // 默认分支
|
||||
}
|
||||
|
||||
// Clone 仓库
|
||||
fmt.Printf("正在克隆仓库 %s...\n", url)
|
||||
repoPath, err := repo.CloneOrPull(url, branch)
|
||||
if err != nil {
|
||||
return fmt.Errorf("克隆仓库失败: %w", err)
|
||||
}
|
||||
|
||||
// 保存到配置
|
||||
repository := types.Repository{
|
||||
Name: name,
|
||||
URL: url,
|
||||
Branch: branch,
|
||||
AddedAt: time.Now(),
|
||||
}
|
||||
|
||||
if err := config.AddRepository(repository); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("✓ 仓库 '%s' 添加成功\n", name)
|
||||
fmt.Printf(" 缓存路径: %s\n", repoPath)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
addCmd.Flags().String("name", "", "仓库别名")
|
||||
addCmd.Flags().String("branch", "main", "克隆的分支")
|
||||
rootCmd.AddCommand(addCmd)
|
||||
}
|
||||
80
manager/cmd/skillmgr/clean.go
Normal file
80
manager/cmd/skillmgr/clean.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"skillmgr/internal/config"
|
||||
"skillmgr/internal/types"
|
||||
)
|
||||
|
||||
var cleanCmd = &cobra.Command{
|
||||
Use: "clean",
|
||||
Short: "清理孤立的安装记录",
|
||||
Long: `检查并清理不存在的安装记录。
|
||||
|
||||
当文件被手动删除但安装记录仍存在时,使用此命令清理。
|
||||
|
||||
示例:
|
||||
# 检查孤立记录(不删除)
|
||||
skillmgr clean --dry-run
|
||||
|
||||
# 清理孤立记录
|
||||
skillmgr clean`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
dryRun, _ := cmd.Flags().GetBool("dry-run")
|
||||
|
||||
cfg, err := config.LoadInstallConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(cfg.Installations) == 0 {
|
||||
fmt.Println("无安装记录")
|
||||
return nil
|
||||
}
|
||||
|
||||
var orphans []types.InstallRecord
|
||||
var valid []types.InstallRecord
|
||||
|
||||
for _, r := range cfg.Installations {
|
||||
if _, err := os.Stat(r.InstallPath); os.IsNotExist(err) {
|
||||
orphans = append(orphans, r)
|
||||
} else {
|
||||
valid = append(valid, r)
|
||||
}
|
||||
}
|
||||
|
||||
if len(orphans) == 0 {
|
||||
fmt.Println("无孤立记录")
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf("发现 %d 个孤立记录:\n", len(orphans))
|
||||
for _, r := range orphans {
|
||||
fmt.Printf(" [%s] %s (%s, %s)\n", r.Type, r.Name, r.Platform, r.Scope)
|
||||
fmt.Printf(" 路径: %s\n", r.InstallPath)
|
||||
}
|
||||
|
||||
if dryRun {
|
||||
fmt.Println("\n使用 --dry-run,未执行清理")
|
||||
return nil
|
||||
}
|
||||
|
||||
cfg.Installations = valid
|
||||
if err := config.SaveInstallConfig(cfg); err != nil {
|
||||
return fmt.Errorf("保存配置失败: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("\n已清理 %d 个孤立记录\n", len(orphans))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cleanCmd.Flags().Bool("dry-run", false, "仅检查,不执行清理")
|
||||
|
||||
rootCmd.AddCommand(cleanCmd)
|
||||
}
|
||||
64
manager/cmd/skillmgr/install.go
Normal file
64
manager/cmd/skillmgr/install.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"skillmgr/internal/installer"
|
||||
"skillmgr/internal/types"
|
||||
)
|
||||
|
||||
var installCmd = &cobra.Command{
|
||||
Use: "install <type> <name>",
|
||||
Short: "安装 skill 或 command",
|
||||
Long: `将 skill 或 command 安装到目标平台。
|
||||
|
||||
类型: skill, command
|
||||
|
||||
示例:
|
||||
# 全局安装到 Claude Code
|
||||
skillmgr install skill lyxy-kb --platform claude --global
|
||||
|
||||
# 项目级安装到 OpenCode
|
||||
skillmgr install command lyxy-kb --platform opencode`,
|
||||
Args: cobra.ExactArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
itemType := args[0]
|
||||
name := args[1]
|
||||
|
||||
platformStr, _ := cmd.Flags().GetString("platform")
|
||||
global, _ := cmd.Flags().GetBool("global")
|
||||
from, _ := cmd.Flags().GetString("from")
|
||||
|
||||
platform := types.Platform(platformStr)
|
||||
scope := types.ScopeProject
|
||||
if global {
|
||||
scope = types.ScopeGlobal
|
||||
}
|
||||
|
||||
switch itemType {
|
||||
case "skill":
|
||||
if from != "" {
|
||||
return installer.InstallSkillFrom(name, platform, scope, from)
|
||||
}
|
||||
return installer.InstallSkill(name, platform, scope)
|
||||
case "command":
|
||||
if from != "" {
|
||||
return installer.InstallCommandFrom(name, platform, scope, from)
|
||||
}
|
||||
return installer.InstallCommand(name, platform, scope)
|
||||
default:
|
||||
return fmt.Errorf("无效的类型: %s(必须是 'skill' 或 'command')", itemType)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
installCmd.Flags().StringP("platform", "p", "", "目标平台 (claude|opencode)")
|
||||
installCmd.Flags().BoolP("global", "g", false, "全局安装")
|
||||
installCmd.Flags().String("from", "", "临时仓库 URL(不保存到配置)")
|
||||
installCmd.MarkFlagRequired("platform")
|
||||
|
||||
rootCmd.AddCommand(installCmd)
|
||||
}
|
||||
86
manager/cmd/skillmgr/list.go
Normal file
86
manager/cmd/skillmgr/list.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"skillmgr/internal/config"
|
||||
"skillmgr/internal/types"
|
||||
)
|
||||
|
||||
var listCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "列出已安装的 skills 和 commands",
|
||||
Long: `显示所有已安装的 skills 和 commands。
|
||||
|
||||
示例:
|
||||
skillmgr list
|
||||
skillmgr list --type skill
|
||||
skillmgr list --platform claude
|
||||
skillmgr list --global`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
itemTypeStr, _ := cmd.Flags().GetString("type")
|
||||
platformStr, _ := cmd.Flags().GetString("platform")
|
||||
global, _ := cmd.Flags().GetBool("global")
|
||||
|
||||
cfg, err := config.LoadInstallConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(cfg.Installations) == 0 {
|
||||
fmt.Println("无已安装的 skills/commands")
|
||||
return nil
|
||||
}
|
||||
|
||||
// 过滤
|
||||
var filtered []types.InstallRecord
|
||||
for _, r := range cfg.Installations {
|
||||
// 按类型过滤
|
||||
if itemTypeStr != "" && string(r.Type) != itemTypeStr {
|
||||
continue
|
||||
}
|
||||
// 按平台过滤
|
||||
if platformStr != "" && string(r.Platform) != platformStr {
|
||||
continue
|
||||
}
|
||||
// 按作用域过滤
|
||||
if global && r.Scope != types.ScopeGlobal {
|
||||
continue
|
||||
}
|
||||
if !global && cmd.Flags().Changed("global") && r.Scope != types.ScopeProject {
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, r)
|
||||
}
|
||||
|
||||
if len(filtered) == 0 {
|
||||
fmt.Println("无匹配的安装记录")
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Println("已安装:")
|
||||
for _, r := range filtered {
|
||||
fmt.Printf("\n [%s] %s\n", r.Type, r.Name)
|
||||
fmt.Printf(" 平台: %s\n", r.Platform)
|
||||
fmt.Printf(" 作用域: %s\n", r.Scope)
|
||||
fmt.Printf(" 来源: %s\n", r.SourceRepo)
|
||||
fmt.Printf(" 路径: %s\n", r.InstallPath)
|
||||
fmt.Printf(" 安装于: %s\n", r.InstalledAt.Format("2006-01-02 15:04:05"))
|
||||
if !r.UpdatedAt.Equal(r.InstalledAt) {
|
||||
fmt.Printf(" 更新于: %s\n", r.UpdatedAt.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
listCmd.Flags().String("type", "", "过滤类型 (skill|command)")
|
||||
listCmd.Flags().String("platform", "", "过滤平台 (claude|opencode)")
|
||||
listCmd.Flags().BoolP("global", "g", false, "仅显示全局安装")
|
||||
|
||||
rootCmd.AddCommand(listCmd)
|
||||
}
|
||||
44
manager/cmd/skillmgr/list_repos.go
Normal file
44
manager/cmd/skillmgr/list_repos.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"skillmgr/internal/config"
|
||||
)
|
||||
|
||||
var listReposCmd = &cobra.Command{
|
||||
Use: "list-repos",
|
||||
Short: "列出已配置的源仓库",
|
||||
Long: `显示所有已添加的源仓库及其信息。
|
||||
|
||||
示例:
|
||||
skillmgr list-repos`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg, err := config.LoadRepositoryConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(cfg.Repositories) == 0 {
|
||||
fmt.Println("无已配置的源仓库")
|
||||
fmt.Println("\n使用 'skillmgr add <url>' 添加仓库")
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Println("已配置的源仓库:")
|
||||
for _, repo := range cfg.Repositories {
|
||||
fmt.Printf("\n %s\n", repo.Name)
|
||||
fmt.Printf(" URL: %s\n", repo.URL)
|
||||
fmt.Printf(" 分支: %s\n", repo.Branch)
|
||||
fmt.Printf(" 添加于: %s\n", repo.AddedAt.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(listReposCmd)
|
||||
}
|
||||
5
manager/cmd/skillmgr/main.go
Normal file
5
manager/cmd/skillmgr/main.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
Execute()
|
||||
}
|
||||
43
manager/cmd/skillmgr/remove.go
Normal file
43
manager/cmd/skillmgr/remove.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"skillmgr/internal/config"
|
||||
)
|
||||
|
||||
var removeCmd = &cobra.Command{
|
||||
Use: "remove <name>",
|
||||
Short: "移除源仓库",
|
||||
Long: `从配置中移除已添加的源仓库。
|
||||
|
||||
示例:
|
||||
skillmgr remove my-skills`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
name := args[0]
|
||||
|
||||
// 检查仓库是否存在
|
||||
repo, err := config.FindRepository(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if repo == nil {
|
||||
fmt.Printf("仓库 '%s' 不存在\n", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := config.RemoveRepository(name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("✓ 仓库 '%s' 已移除\n", name)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(removeCmd)
|
||||
}
|
||||
55
manager/cmd/skillmgr/root.go
Normal file
55
manager/cmd/skillmgr/root.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"skillmgr/internal/config"
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "skillmgr",
|
||||
Short: "AI 编程平台 skills 和 commands 管理工具",
|
||||
Long: `skillmgr 是一个用于管理和分发 AI 编程平台 skills 和 commands 的命令行工具。
|
||||
|
||||
支持从 git 仓库拉取 skills/commands,并根据目标平台(Claude Code、OpenCode)
|
||||
将其安装到全局目录或项目目录中。
|
||||
|
||||
示例:
|
||||
# 添加源仓库
|
||||
skillmgr add https://github.com/user/skills --name my-skills
|
||||
|
||||
# 安装 skill
|
||||
skillmgr install skill lyxy-kb --platform claude --global
|
||||
|
||||
# 列出已安装
|
||||
skillmgr list
|
||||
|
||||
# 更新
|
||||
skillmgr update skill lyxy-kb --platform claude --global
|
||||
|
||||
# 卸载
|
||||
skillmgr uninstall skill lyxy-kb --platform claude --global`,
|
||||
}
|
||||
|
||||
// Execute 执行根命令
|
||||
func Execute() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
// 初始化配置目录
|
||||
cobra.OnInitialize(initConfig)
|
||||
}
|
||||
|
||||
func initConfig() {
|
||||
if err := config.EnsureConfigDirs(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "初始化配置目录失败: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
118
manager/cmd/skillmgr/search.go
Normal file
118
manager/cmd/skillmgr/search.go
Normal file
@@ -0,0 +1,118 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"skillmgr/internal/repo"
|
||||
"skillmgr/internal/types"
|
||||
)
|
||||
|
||||
var searchCmd = &cobra.Command{
|
||||
Use: "search [keyword]",
|
||||
Short: "搜索可用的 skills 和 commands",
|
||||
Long: `在已配置的仓库中搜索 skills 和 commands。
|
||||
|
||||
示例:
|
||||
# 搜索所有
|
||||
skillmgr search
|
||||
|
||||
# 按关键字搜索
|
||||
skillmgr search kb
|
||||
|
||||
# 按类型过滤
|
||||
skillmgr search --type skill
|
||||
|
||||
# 按仓库过滤
|
||||
skillmgr search --repo lyxy`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
keyword := ""
|
||||
if len(args) > 0 {
|
||||
keyword = strings.ToLower(args[0])
|
||||
}
|
||||
|
||||
itemTypeStr, _ := cmd.Flags().GetString("type")
|
||||
repoFilter, _ := cmd.Flags().GetString("repo")
|
||||
|
||||
var results []searchResult
|
||||
|
||||
// 搜索 skills
|
||||
if itemTypeStr == "" || itemTypeStr == "skill" {
|
||||
skills, err := repo.ListAvailableSkills()
|
||||
if err != nil {
|
||||
fmt.Printf("警告: 无法获取 skills: %v\n", err)
|
||||
} else {
|
||||
for _, s := range skills {
|
||||
// 按仓库过滤
|
||||
if repoFilter != "" && !strings.Contains(strings.ToLower(s.SourceRepo), strings.ToLower(repoFilter)) {
|
||||
continue
|
||||
}
|
||||
// 按关键字过滤
|
||||
if keyword == "" || strings.Contains(strings.ToLower(s.Name), keyword) {
|
||||
results = append(results, searchResult{
|
||||
Type: types.ItemTypeSkill,
|
||||
Name: s.Name,
|
||||
RepoName: s.SourceRepo,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索 commands
|
||||
if itemTypeStr == "" || itemTypeStr == "command" {
|
||||
commands, err := repo.ListAvailableCommands()
|
||||
if err != nil {
|
||||
fmt.Printf("警告: 无法获取 commands: %v\n", err)
|
||||
} else {
|
||||
for _, c := range commands {
|
||||
// 按仓库过滤
|
||||
if repoFilter != "" && !strings.Contains(strings.ToLower(c.SourceRepo), strings.ToLower(repoFilter)) {
|
||||
continue
|
||||
}
|
||||
// 按关键字过滤
|
||||
if keyword == "" || strings.Contains(strings.ToLower(c.Name), keyword) {
|
||||
results = append(results, searchResult{
|
||||
Type: types.ItemTypeCommand,
|
||||
Name: c.Name,
|
||||
RepoName: c.SourceRepo,
|
||||
Files: c.Files,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(results) == 0 {
|
||||
fmt.Println("未找到匹配项")
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf("找到 %d 个结果:\n\n", len(results))
|
||||
for _, r := range results {
|
||||
fmt.Printf(" [%s] %s\n", r.Type, r.Name)
|
||||
fmt.Printf(" 来源: %s\n", r.RepoName)
|
||||
if len(r.Files) > 0 {
|
||||
fmt.Printf(" 文件: %s\n", strings.Join(r.Files, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
type searchResult struct {
|
||||
Type types.ItemType
|
||||
Name string
|
||||
RepoName string
|
||||
Files []string
|
||||
}
|
||||
|
||||
func init() {
|
||||
searchCmd.Flags().String("type", "", "过滤类型 (skill|command)")
|
||||
searchCmd.Flags().String("repo", "", "过滤仓库")
|
||||
|
||||
rootCmd.AddCommand(searchCmd)
|
||||
}
|
||||
70
manager/cmd/skillmgr/sync.go
Normal file
70
manager/cmd/skillmgr/sync.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"skillmgr/internal/config"
|
||||
"skillmgr/internal/repo"
|
||||
)
|
||||
|
||||
var syncCmd = &cobra.Command{
|
||||
Use: "sync [name]",
|
||||
Short: "同步源仓库",
|
||||
Long: `从远程拉取最新代码,更新本地缓存。
|
||||
|
||||
示例:
|
||||
skillmgr sync # 同步所有仓库
|
||||
skillmgr sync my-skills # 同步指定仓库`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg, err := config.LoadRepositoryConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(cfg.Repositories) == 0 {
|
||||
fmt.Println("无已配置的源仓库")
|
||||
return nil
|
||||
}
|
||||
|
||||
// 如果指定了仓库名称,只同步该仓库
|
||||
if len(args) > 0 {
|
||||
name := args[0]
|
||||
for _, r := range cfg.Repositories {
|
||||
if r.Name == name {
|
||||
fmt.Printf("正在同步 %s...\n", r.Name)
|
||||
if _, err := repo.CloneOrPull(r.URL, r.Branch); err != nil {
|
||||
fmt.Printf(" ✗ 同步失败: %v\n", err)
|
||||
return err
|
||||
}
|
||||
fmt.Printf(" ✓ 同步成功\n")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("仓库 '%s' 不存在", name)
|
||||
}
|
||||
|
||||
// 同步所有仓库
|
||||
var hasError bool
|
||||
for _, r := range cfg.Repositories {
|
||||
fmt.Printf("正在同步 %s...\n", r.Name)
|
||||
if _, err := repo.CloneOrPull(r.URL, r.Branch); err != nil {
|
||||
fmt.Printf(" ✗ 同步失败: %v\n", err)
|
||||
hasError = true
|
||||
continue
|
||||
}
|
||||
fmt.Printf(" ✓ 同步成功\n")
|
||||
}
|
||||
|
||||
if hasError {
|
||||
fmt.Println("\n部分仓库同步失败")
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(syncCmd)
|
||||
}
|
||||
53
manager/cmd/skillmgr/uninstall.go
Normal file
53
manager/cmd/skillmgr/uninstall.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"skillmgr/internal/installer"
|
||||
"skillmgr/internal/types"
|
||||
)
|
||||
|
||||
var uninstallCmd = &cobra.Command{
|
||||
Use: "uninstall <type> <name>",
|
||||
Short: "卸载 skill 或 command",
|
||||
Long: `卸载已安装的 skill 或 command。
|
||||
|
||||
类型: skill, command
|
||||
|
||||
示例:
|
||||
skillmgr uninstall skill lyxy-kb --platform claude --global
|
||||
skillmgr uninstall command lyxy-kb --platform opencode`,
|
||||
Args: cobra.ExactArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
itemType := args[0]
|
||||
name := args[1]
|
||||
|
||||
platformStr, _ := cmd.Flags().GetString("platform")
|
||||
global, _ := cmd.Flags().GetBool("global")
|
||||
|
||||
platform := types.Platform(platformStr)
|
||||
scope := types.ScopeProject
|
||||
if global {
|
||||
scope = types.ScopeGlobal
|
||||
}
|
||||
|
||||
switch itemType {
|
||||
case "skill":
|
||||
return installer.UninstallSkill(name, platform, scope)
|
||||
case "command":
|
||||
return installer.UninstallCommand(name, platform, scope)
|
||||
default:
|
||||
return fmt.Errorf("无效的类型: %s(必须是 'skill' 或 'command')", itemType)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
uninstallCmd.Flags().StringP("platform", "p", "", "目标平台 (claude|opencode)")
|
||||
uninstallCmd.Flags().BoolP("global", "g", false, "全局卸载")
|
||||
uninstallCmd.MarkFlagRequired("platform")
|
||||
|
||||
rootCmd.AddCommand(uninstallCmd)
|
||||
}
|
||||
99
manager/cmd/skillmgr/update.go
Normal file
99
manager/cmd/skillmgr/update.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"skillmgr/internal/config"
|
||||
"skillmgr/internal/installer"
|
||||
"skillmgr/internal/types"
|
||||
)
|
||||
|
||||
var updateCmd = &cobra.Command{
|
||||
Use: "update [type] [name]",
|
||||
Short: "更新已安装的 skill 或 command",
|
||||
Long: `从源仓库重新安装最新版本。
|
||||
|
||||
示例:
|
||||
# 更新单个
|
||||
skillmgr update skill lyxy-kb --platform claude --global
|
||||
skillmgr update command lyxy-kb --platform opencode
|
||||
|
||||
# 更新所有
|
||||
skillmgr update --all`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
all, _ := cmd.Flags().GetBool("all")
|
||||
|
||||
if all {
|
||||
return updateAll()
|
||||
}
|
||||
|
||||
if len(args) != 2 {
|
||||
return fmt.Errorf("需要指定类型和名称,或使用 --all 更新所有")
|
||||
}
|
||||
|
||||
itemType := args[0]
|
||||
name := args[1]
|
||||
|
||||
platformStr, _ := cmd.Flags().GetString("platform")
|
||||
global, _ := cmd.Flags().GetBool("global")
|
||||
|
||||
platform := types.Platform(platformStr)
|
||||
scope := types.ScopeProject
|
||||
if global {
|
||||
scope = types.ScopeGlobal
|
||||
}
|
||||
|
||||
switch itemType {
|
||||
case "skill":
|
||||
return installer.UpdateSkill(name, platform, scope)
|
||||
case "command":
|
||||
return installer.UpdateCommand(name, platform, scope)
|
||||
default:
|
||||
return fmt.Errorf("无效的类型: %s(必须是 'skill' 或 'command')", itemType)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func updateAll() error {
|
||||
cfg, err := config.LoadInstallConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(cfg.Installations) == 0 {
|
||||
fmt.Println("无已安装的 skills/commands")
|
||||
return nil
|
||||
}
|
||||
|
||||
var hasError bool
|
||||
for _, r := range cfg.Installations {
|
||||
fmt.Printf("正在更新 [%s] %s...\n", r.Type, r.Name)
|
||||
var err error
|
||||
if r.Type == types.ItemTypeSkill {
|
||||
err = installer.UpdateSkill(r.Name, r.Platform, r.Scope)
|
||||
} else {
|
||||
err = installer.UpdateCommand(r.Name, r.Platform, r.Scope)
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Printf(" ✗ 更新失败: %v\n", err)
|
||||
hasError = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if hasError {
|
||||
fmt.Println("\n部分项目更新失败")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
updateCmd.Flags().StringP("platform", "p", "", "目标平台 (claude|opencode)")
|
||||
updateCmd.Flags().BoolP("global", "g", false, "全局更新")
|
||||
updateCmd.Flags().Bool("all", false, "更新所有已安装项")
|
||||
|
||||
rootCmd.AddCommand(updateCmd)
|
||||
}
|
||||
Reference in New Issue
Block a user