feat(discovery): frontmatter guard for flat agents/commands docs
A flat .md under agents/ or commands/ whose frontmatter contains none of the definition keys (name, description, mode, agent, model, argument-hint, temperature, permission) is documentation, not an item: discovery marks it Skipped with a recorded warning — it never enters a selection, inventory, availability listing or collision index, but the warning lands in the lockfile and add/update/select reports like other discovery warnings. Skills keep their folder+SKILL.md marker, MCP keeps the .yaml marker. README gains the canonical layout table and the migration note for folder-style agent repos. Closes beads: agent-lib-2n7
This commit is contained in:
@@ -20,16 +20,11 @@ type Frontmatter struct {
|
||||
func ParseFrontmatter(data []byte) (Frontmatter, []string) {
|
||||
var fm Frontmatter
|
||||
var warnings []string
|
||||
lines := strings.Split(strings.ReplaceAll(string(data), "\r\n", "\n"), "\n")
|
||||
if len(lines) == 0 || strings.TrimSpace(lines[0]) != "---" {
|
||||
lines, fenced, closed := parseFence(data)
|
||||
if !fenced {
|
||||
return fm, nil
|
||||
}
|
||||
closed := false
|
||||
for _, line := range lines[1:] {
|
||||
if strings.TrimSpace(line) == "---" {
|
||||
closed = true
|
||||
break
|
||||
}
|
||||
for _, line := range lines {
|
||||
trimmed := strings.TrimSpace(line)
|
||||
if trimmed == "" {
|
||||
continue
|
||||
@@ -58,6 +53,52 @@ func ParseFrontmatter(data []byte) (Frontmatter, []string) {
|
||||
return fm, warnings
|
||||
}
|
||||
|
||||
// definitionKeys mark a flat .md under agents/ or commands/ as an intentional
|
||||
// agent/command definition rather than dropped documentation.
|
||||
var definitionKeys = map[string]bool{
|
||||
"name": true,
|
||||
"description": true,
|
||||
"mode": true,
|
||||
"agent": true,
|
||||
"model": true,
|
||||
"argument-hint": true,
|
||||
"temperature": true,
|
||||
"permission": true,
|
||||
}
|
||||
|
||||
// HasDefinitionFrontmatter reports whether data's leading frontmatter block
|
||||
// contains any key that marks a flat .md as an agent/command definition.
|
||||
func HasDefinitionFrontmatter(data []byte) bool {
|
||||
lines, fenced, _ := parseFence(data)
|
||||
if !fenced {
|
||||
return false
|
||||
}
|
||||
for _, line := range lines {
|
||||
key, _, ok := strings.Cut(strings.TrimSpace(line), ":")
|
||||
if ok && definitionKeys[strings.ToLower(strings.TrimSpace(key))] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// parseFence returns the lines between the leading `---` fences: the block
|
||||
// lines, whether the file is fenced at all, and whether the fence closes.
|
||||
func parseFence(data []byte) (lines []string, fenced, closed bool) {
|
||||
all := strings.Split(strings.ReplaceAll(string(data), "\r\n", "\n"), "\n")
|
||||
if len(all) == 0 || strings.TrimSpace(all[0]) != "---" {
|
||||
return nil, false, false
|
||||
}
|
||||
fenced = true
|
||||
for _, line := range all[1:] {
|
||||
if strings.TrimSpace(line) == "---" {
|
||||
return lines, fenced, true
|
||||
}
|
||||
lines = append(lines, line)
|
||||
}
|
||||
return lines, fenced, false
|
||||
}
|
||||
|
||||
func splitList(v string) []string {
|
||||
if v == "" {
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user