Files
2026-06-22 08:55:57 +08:00

89 lines
2.5 KiB
Go

package projectmemory
import (
"sort"
"strings"
"github.com/google/uuid"
)
// kindHeadings maps each memory kind to its AGENTS.md section heading.
var kindHeadings = map[string]string{
KindDecision: "Decisions",
KindConvention: "Conventions",
KindFileMap: "File Map",
KindGotcha: "Gotchas",
}
// agentsMDHeader is the generated-file banner. AGENTS.md is written into the
// worktree before spawn; the banner marks it generated so it is not committed or
// picked up by auto-doc diffs.
const agentsMDHeader = "<!-- GENERATED by AgentCodingWorkflow project memory. Do not edit; changes are overwritten on spawn. -->"
// renderAgentsMD produces deterministic markdown grouped by kind. Within a kind,
// entries are sorted by title then id for stable output. When wsID is set,
// workspace-scoped entries plus project-wide (workspace_id NULL) entries are
// included; otherwise only project-wide entries.
func renderAgentsMD(entries []Entry, wsID *uuid.UUID) string {
byKind := map[string][]Entry{}
for _, e := range entries {
if !includeForWorkspace(e, wsID) {
continue
}
byKind[e.Kind] = append(byKind[e.Kind], e)
}
var b strings.Builder
b.WriteString(agentsMDHeader)
b.WriteString("\n\n# AGENTS.md\n\n")
b.WriteString("Project knowledge curated by agents and maintainers. Treat as authoritative context.\n")
wrote := false
for _, kind := range validKinds {
items := byKind[kind]
if len(items) == 0 {
continue
}
sort.SliceStable(items, func(i, j int) bool {
if items[i].Title != items[j].Title {
return items[i].Title < items[j].Title
}
return items[i].ID.String() < items[j].ID.String()
})
b.WriteString("\n## ")
b.WriteString(kindHeadings[kind])
b.WriteString("\n\n")
for _, e := range items {
b.WriteString("### ")
b.WriteString(e.Title)
b.WriteString("\n")
if len(e.Tags) > 0 {
tags := append([]string(nil), e.Tags...)
sort.Strings(tags)
b.WriteString("Tags: ")
b.WriteString(strings.Join(tags, ", "))
b.WriteString("\n")
}
b.WriteString("\n")
b.WriteString(strings.TrimRight(e.Body, "\n"))
b.WriteString("\n")
}
wrote = true
}
if !wrote {
b.WriteString("\n(No project memory recorded yet.)\n")
}
return b.String()
}
// includeForWorkspace decides whether an entry belongs in a worktree's AGENTS.md.
func includeForWorkspace(e Entry, wsID *uuid.UUID) bool {
if e.WorkspaceID == nil {
return true // project-wide
}
if wsID == nil {
return false
}
return *e.WorkspaceID == *wsID
}