1
0
Files
Skill/manager/internal/repo/scanner.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
}