DiscoverResources walks an extracted repository tree rooted at root and returns the individual resources of the given kind. subPath optionally narrows the scan to a sub-directory (supporting the "a|b" multi-path syntax used in repo configs); when empty the whole tree is scanned. Conventions, matchi
(root, subPath string, kind entities.Kind)
| 37 | // - instruction: *.md files (each file is one instruction), excluding README/LICENSE |
| 38 | // - plugin: directories containing a plugin.json or .claude-plugin/plugin.json |
| 39 | func DiscoverResources(root, subPath string, kind entities.Kind) []DiscoveredResource { |
| 40 | scanRoots := resolveScanRoots(root, subPath) |
| 41 | var out []DiscoveredResource |
| 42 | seen := map[string]bool{} |
| 43 | |
| 44 | for _, scanRoot := range scanRoots { |
| 45 | // A directly-specified single file (e.g. an agentsPath that points at one |
| 46 | // .md) is the resource itself: walk it as one file, with no folder |
| 47 | // constraint. This is the "directly specified" subagent case. |
| 48 | if info, err := os.Stat(scanRoot); err == nil && !info.IsDir() { |
| 49 | res, ok := resourceFromFile(root, scanRoot, filepath.Base(scanRoot), kind) |
| 50 | if ok && !seen[res.Name+"|"+res.RelPath] { |
| 51 | seen[res.Name+"|"+res.RelPath] = true |
| 52 | out = append(out, res) |
| 53 | } |
| 54 | continue |
| 55 | } |
| 56 | _ = filepath.WalkDir(scanRoot, func(p string, d os.DirEntry, err error) error { |
| 57 | if err != nil { |
| 58 | return nil |
| 59 | } |
| 60 | if d.IsDir() { |
| 61 | if shouldSkipDir(d.Name()) { |
| 62 | return filepath.SkipDir |
| 63 | } |
| 64 | return nil |
| 65 | } |
| 66 | res, ok := resourceFromFile(root, p, d.Name(), kind) |
| 67 | if !ok { |
| 68 | return nil |
| 69 | } |
| 70 | // Subagents follow the convention that they live under an `agents/` |
| 71 | // folder (agents/, .claude/agents/, agents/<category>/…). A loose |
| 72 | // .md anywhere in the tree (README, CLAUDE.md, docs/*) is not a |
| 73 | // subagent. Directly-specified single files are handled above. |
| 74 | if kind == entities.KindAgent && !underAgentsFolder(root, p) { |
| 75 | return nil |
| 76 | } |
| 77 | if seen[res.Name+"|"+res.RelPath] { |
| 78 | return nil |
| 79 | } |
| 80 | seen[res.Name+"|"+res.RelPath] = true |
| 81 | out = append(out, res) |
| 82 | return nil |
| 83 | }) |
| 84 | } |
| 85 | return out |
| 86 | } |
| 87 | |
| 88 | // underAgentsFolder reports whether p (relative to the repo root) is beneath a |
| 89 | // directory named "agents" at any depth, e.g. agents/foo.md, |