ajhahn.de
← eeco
Go 73 lines
package memory

import (
	"bytes"
	"fmt"
	"os"
)

// WriteIndex regenerates <MemoryDir>/MEMORY.md from facts. The file is
// overwritten on each call; users are not expected to edit it by hand.
// The index lists facts sorted by name, with pinned facts grouped under
// a "## pinned" heading and disabled facts under a "## disabled"
// heading at the bottom for at-a-glance separation.
func (s *Store) WriteIndex(facts []*Fact) error {
	var buf bytes.Buffer
	buf.WriteString("# memory index\n\n")
	buf.WriteString("Regenerated by eeco; do not edit by hand.\n")
	buf.WriteString("Run `eeco gc` to refresh and prune.\n\n")

	if len(facts) == 0 {
		buf.WriteString("_(empty — no facts recorded yet)_\n")
		return os.WriteFile(s.indexPath(), buf.Bytes(), 0o644)
	}

	// A disabled fact is bucketed regardless of its pin state: every
	// muted adaptation belongs in one place so an audit of "what the AI
	// has turned off" is a single glance at the "## disabled" section.
	var active, pinned, disabled []*Fact
	for _, f := range sortedFacts(facts) {
		switch {
		case f.Disabled:
			disabled = append(disabled, f)
		case f.Pin:
			pinned = append(pinned, f)
		default:
			active = append(active, f)
		}
	}

	if len(active) == 0 {
		buf.WriteString("_(no active facts)_\n")
	} else {
		for _, f := range active {
			writeIndexLine(&buf, f)
		}
	}

	if len(pinned) > 0 {
		buf.WriteString("\n## pinned\n\n")
		for _, f := range pinned {
			writeIndexLine(&buf, f)
		}
	}

	if len(disabled) > 0 {
		buf.WriteString("\n## disabled\n\n")
		for _, f := range disabled {
			writeIndexLine(&buf, f)
		}
	}

	return os.WriteFile(s.indexPath(), buf.Bytes(), 0o644)
}

func (s *Store) indexPath() string {
	return s.MemoryDir + "/" + IndexFilename
}

func writeIndexLine(buf *bytes.Buffer, f *Fact) {
	fmt.Fprintf(buf, "- **%s** (%s, last_used %s) — %s\n",
		f.Name, f.Type, f.LastUsed.UTC().Format(DateLayout), f.Description)
}