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 = "" // 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 }