192 lines
4.4 KiB
Go
192 lines
4.4 KiB
Go
package repo
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"skillmgr/internal/config"
|
|
"skillmgr/internal/types"
|
|
)
|
|
|
|
// ScanSkills 扫描仓库中的 skills
|
|
func ScanSkills(repoPath string) ([]types.SkillMetadata, error) {
|
|
skillsPath := filepath.Join(repoPath, "skills")
|
|
|
|
entries, err := os.ReadDir(skillsPath)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return []types.SkillMetadata{}, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
var skills []types.SkillMetadata
|
|
for _, entry := range entries {
|
|
if !entry.IsDir() {
|
|
continue
|
|
}
|
|
|
|
// 检查是否有 SKILL.md
|
|
skillFile := filepath.Join(skillsPath, entry.Name(), "SKILL.md")
|
|
if _, err := os.Stat(skillFile); err == nil {
|
|
skills = append(skills, types.SkillMetadata{
|
|
Name: entry.Name(),
|
|
})
|
|
}
|
|
}
|
|
|
|
return skills, nil
|
|
}
|
|
|
|
// ScanCommands 扫描仓库中的 commands
|
|
func ScanCommands(repoPath string) ([]types.CommandGroup, error) {
|
|
commandsPath := filepath.Join(repoPath, "commands")
|
|
|
|
entries, err := os.ReadDir(commandsPath)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return []types.CommandGroup{}, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
var groups []types.CommandGroup
|
|
for _, entry := range entries {
|
|
if !entry.IsDir() {
|
|
continue
|
|
}
|
|
|
|
// 列出目录下的 .md 文件
|
|
files, err := filepath.Glob(filepath.Join(commandsPath, entry.Name(), "*.md"))
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "警告: 无法扫描 %s 下的 markdown 文件: %v\n", entry.Name(), err)
|
|
continue
|
|
}
|
|
|
|
var fileNames []string
|
|
for _, f := range files {
|
|
fileNames = append(fileNames, filepath.Base(f))
|
|
}
|
|
|
|
if len(fileNames) > 0 {
|
|
groups = append(groups, types.CommandGroup{
|
|
Name: entry.Name(),
|
|
Files: fileNames,
|
|
})
|
|
}
|
|
}
|
|
|
|
return groups, nil
|
|
}
|
|
|
|
// FindSkill 在所有仓库中查找 skill
|
|
func FindSkill(name string) (repoPath, skillPath string, repoName string, err error) {
|
|
cfg, err := config.LoadRepositoryConfig()
|
|
if err != nil {
|
|
return "", "", "", err
|
|
}
|
|
|
|
cachePath, err := config.GetCachePath()
|
|
if err != nil {
|
|
return "", "", "", err
|
|
}
|
|
|
|
for _, repo := range cfg.Repositories {
|
|
rp := filepath.Join(cachePath, URLToPathName(repo.URL))
|
|
sp := filepath.Join(rp, "skills", name)
|
|
|
|
if _, err := os.Stat(filepath.Join(sp, "SKILL.md")); err == nil {
|
|
return rp, sp, repo.Name, nil
|
|
}
|
|
}
|
|
|
|
return "", "", "", fmt.Errorf("skill '%s' 未在任何仓库中找到", name)
|
|
}
|
|
|
|
// FindCommand 在所有仓库中查找 command
|
|
func FindCommand(name string) (repoPath, commandPath string, repoName string, err error) {
|
|
cfg, err := config.LoadRepositoryConfig()
|
|
if err != nil {
|
|
return "", "", "", err
|
|
}
|
|
|
|
cachePath, err := config.GetCachePath()
|
|
if err != nil {
|
|
return "", "", "", err
|
|
}
|
|
|
|
for _, repo := range cfg.Repositories {
|
|
rp := filepath.Join(cachePath, URLToPathName(repo.URL))
|
|
cp := filepath.Join(rp, "commands", name)
|
|
|
|
if info, err := os.Stat(cp); err == nil && info.IsDir() {
|
|
// 检查目录是否包含 .md 文件
|
|
files, _ := filepath.Glob(filepath.Join(cp, "*.md"))
|
|
if len(files) > 0 {
|
|
return rp, cp, repo.Name, nil
|
|
}
|
|
// 目录存在但为空,返回特定错误
|
|
return "", "", "", fmt.Errorf("command group '%s' 不包含任何命令文件", name)
|
|
}
|
|
}
|
|
|
|
return "", "", "", fmt.Errorf("command '%s' 未在任何仓库中找到", name)
|
|
}
|
|
|
|
// ListAvailableSkills 列出所有可用的 skills
|
|
func ListAvailableSkills() ([]types.SkillMetadata, error) {
|
|
cfg, err := config.LoadRepositoryConfig()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cachePath, err := config.GetCachePath()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var allSkills []types.SkillMetadata
|
|
for _, repo := range cfg.Repositories {
|
|
rp := filepath.Join(cachePath, URLToPathName(repo.URL))
|
|
skills, err := ScanSkills(rp)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
for i := range skills {
|
|
skills[i].SourceRepo = repo.Name
|
|
}
|
|
allSkills = append(allSkills, skills...)
|
|
}
|
|
|
|
return allSkills, nil
|
|
}
|
|
|
|
// ListAvailableCommands 列出所有可用的 commands
|
|
func ListAvailableCommands() ([]types.CommandGroup, error) {
|
|
cfg, err := config.LoadRepositoryConfig()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cachePath, err := config.GetCachePath()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var allCommands []types.CommandGroup
|
|
for _, repo := range cfg.Repositories {
|
|
rp := filepath.Join(cachePath, URLToPathName(repo.URL))
|
|
commands, err := ScanCommands(rp)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
for i := range commands {
|
|
commands[i].SourceRepo = repo.Name
|
|
}
|
|
allCommands = append(allCommands, commands...)
|
|
}
|
|
|
|
return allCommands, nil
|
|
}
|