完成一个简易的全局skill、command管理器
This commit is contained in:
191
manager/internal/repo/scanner.go
Normal file
191
manager/internal/repo/scanner.go
Normal file
@@ -0,0 +1,191 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user