Documentation
¶
Overview ¶
Package discovery finds the skills, subagents and commands a machine actually has, and checks the ones batten.yaml claims against them.
batten.yaml names skills and agents by string: `domains.ml.skills: [pytorch-review]`. Those strings rot. A skill gets renamed, a plugin is uninstalled, a teammate clones the repo and never had the agent at all. Claude Code does not complain — the phase simply runs without the skill it was promised, and the run looks exactly like a good one. Discovery exists so that rot surfaces in `batten doctor` at 9am, not inside an unattended fan-out at 3am.
It also runs the other direction: `batten init` proposes a domain→skill mapping from what is actually installed, instead of inventing names the user does not have.
Two things the docs say and the disk does not ¶
Both were confirmed against a real ~/.claude, and both are load-bearing:
A plugin's skills live where its .claude-plugin/plugin.json says they live. The "skills" key is a string OR an array, and an array entry may name a single skill directory rather than a container. ui-ux-pro-max ships seven SKILL.md files under .claude/skills/ and declares exactly one of them; Claude Code exposes only that one. A naive walk would report six skills the user cannot invoke — inventing capability is the same sin as inventing a number.
The DIRECTORY, not the frontmatter `name`, is what a plugin skill is addressed by. claude-mem's skills/version-bump/SKILL.md declares `name: claude-code-plugin-release`, and Claude Code still loads it as `claude-mem:version-bump`. So Item.Name keeps the frontmatter (it is the human-readable label) but Item.Ref — the only string that is safe to write into batten.yaml — is built from the directory. Validate accepts either spelling, because a doctor that cries wolf gets muted.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func PluginDir ¶
Skills returns every skill visible to projectDir, project first. PluginDir returns the directory an installed plugin lives in, as Claude Code recorded it.
`batten doctor` needs it to check the copy that actually runs. The binary in this repo is not the binary the hooks invoke: hooks call ${CLAUDE_PLUGIN_ROOT}/bin/batten, and the installed tree can be stale, half-written, or line-ending mangled while the source tree is perfect.
func Suggest ¶
Suggest maps discovered skills onto the spec's domains by matching the skill's name and description against the domain name and path. Used by `batten init` to PROPOSE a mapping that the user then edits — it is a starting point, never an authority.
The scoring is deliberately blunt (token overlap; a hit in the name outweighs a hit in the description) and the threshold deliberately high: a domain with no confident match gets no key at all. An empty mapping the user must fill in beats a plausible-looking wrong one they never re-read.
Types ¶
type Item ¶
type Item struct {
Name string // from frontmatter when present, else the file/dir name
Slug string // the file/dir name — what Claude Code actually addresses (see package doc)
Description string
Path string
Source Source
Plugin string // set when Source == "plugin"
}
Item is one discovered skill, subagent or command.