memdir

package
v0.2.2-alpha.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 21, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package memdir loads the on-disk memory files that seed the agent's system prompt at session start, and provides the write helpers the auto-memory tools call mid-session:

  • <workdir>/EVVA.md workdir memory — repo conventions (user-authored)
  • <appHome>/USER_PROFILE.md user memory — preferences, working style (auto)
  • <appHome>/projects/<projectKey(workdir)>/MEMORY.md project memory (auto)

All files are optional. Missing files yield zero-value Snapshot fields and no warning; the prompt builder skips empty sections cleanly. Any non-missing read failure (permission, oversize) is recorded in Snapshot.Warnings — Load itself never returns an error so the agent can always boot.

This package depends only on stdlib. It is not imported by the sysprompt package; the caller threads Snapshot fields into the prompt context, keeping the dependency arrow one-way.

Section parsing and merge helpers for the auto-memory files.

USER_PROFILE.md and per-project MEMORY.md both follow a "fixed set of H2 sections" shape: every save call hands us the bodies for a subset of the allowed sections, and we merge them into the existing file while preserving order, untouched sections, and any incidental whitespace.

We deliberately do NOT parse arbitrary markdown — only top-level "## " headings count as section boundaries. Nested headings, fenced code blocks containing "## ...", lists with "## " prefixes — all are treated as section body content. The trade-off: the parser is small and predictable, and the section bodies stay machine-mergeable. Real markdown ASTs would introduce a dependency and a class of bugs the tool description already rules out (sections are flat, edits are surgical).

Index

Constants

View Source
const (
	ProjectMemoryFile = "EVVA.md"
	UserProfileFile   = "USER_PROFILE.md"
)

File names. Exposed so other packages (Phase 9 user-profile background agent, future /memory slash commands) can write to the same paths without re-spelling them.

View Source
const MaxFileBytes = 64 * 1024

MaxFileBytes caps each memory file at 64 KiB. Past that the user is almost certainly using EVVA.md for the wrong thing (knowledge base, not conventions doc); we truncate and warn rather than refuse outright so a bloated file doesn't break the session.

View Source
const ProjectMemoryFileName = "MEMORY.md"

ProjectMemoryFileName is the on-disk basename of per-project memory. Lives under <APP_HOME>/projects/<key>/MEMORY.md.

View Source
const ProjectsSubdir = "projects"

ProjectsSubdir is the directory under APP_HOME that holds per-project memory keyed by ProjectKey(workdir).

Variables

View Source
var ProjectMemorySections = []string{
	"Project facts",
	"Decisions",
	"Open issues",
	"References",
}

ProjectMemorySections is the closed set of headings allowed in <APP_HOME>/projects/<key>/MEMORY.md. Adapted from the ref taxonomy (user / feedback / project / reference) — "user" lives in USER_PROFILE.md, the remaining three are restated in project-centric language.

View Source
var UserProfileSections = []string{
	"Preferences",
	"Working style",
	"Recurring topics",
}

UserProfileSections is the closed set of headings allowed in USER_PROFILE.md. Order is the canonical render order for newly-scaffolded files.

Functions

func EnsureProjectMemoryDir

func EnsureProjectMemoryDir(appHome, workdir string) error

EnsureProjectMemoryDir creates the parent directory for the per-project MEMORY.md, mirroring the ref pattern of pre-creating memory dirs at boot so the model never has to mkdir before its first write.

func IndexSummary

func IndexSummary(content string, maxBodyChars int) string

IndexSummary renders a compact one-line-per-section overview of the supplied memory content, suitable for cache-static system-prompt injection.

For each "## " section in `content`, the output contains:

## <heading> — <first non-empty line, truncated to maxBodyChars>

Sections with empty bodies render as `## <heading> — (empty)`. The result is meant to be small (a few hundred chars even for a fully-populated file) so the model sees what's recorded without paying the full file cost.

func MergeSections

func MergeSections(existing string, updates map[string]string, allowed []string) (string, error)

MergeSections updates `existing` so that each entry in `updates` replaces the body of its corresponding "## <heading>" section. Sections not in `updates` are preserved verbatim. Headings that exist in `allowed` but not in the file are appended in `allowed` order. Headings present in `updates` but not in `allowed` cause an error before any merge happens.

Body semantics: an entry's value is treated as the new section body (trimmed of leading/trailing whitespace). A value of "" clears the body — useful when the model wants to drop a section's content while keeping the heading visible. To leave a section untouched, omit the key entirely.

func ProjectKey

func ProjectKey(absPath string) string

ProjectKey turns an absolute filesystem path into a stable directory key used under <APP_HOME>/projects/. Mirrors the convention Claude Code uses at ~/.claude/projects/ — the path's separators are flattened to "-" so the result is one segment that round-trips losslessly enough for human inspection.

Examples (POSIX):

/Users/johnny/lab/evva     -> "-Users-johnny-lab-evva"
/home/alice/work/api       -> "-home-alice-work-api"

On Windows the volume colon is dropped and backslashes are flattened the same way:

C:\Users\Alice\proj        -> "C-Users-Alice-proj"

An empty input yields "". Inputs are cleaned via filepath.Clean first so "/a//b/../c" and "/a/c" produce the same key.

func ProjectMemoryPath

func ProjectMemoryPath(appHome, workdir string) string

ProjectMemoryPath returns the absolute path of MEMORY.md for the given workdir, scoped to APP_HOME. Empty inputs yield "".

func ReadProjectMemory

func ReadProjectMemory(appHome, workdir string) (string, string)

ReadProjectMemory reads the per-project MEMORY.md for the given workdir. Missing file returns ("", nil) — callers treat empty content as "no memory yet." Truncation and oversize behave like the read path in memdir.Load: cap at MaxFileBytes with a warning returned.

func UserProfilePath

func UserProfilePath(appHome string) string

UserProfilePath returns the absolute path of USER_PROFILE.md given the caller's APP_HOME. Empty appHome yields "".

func WriteProjectMemory

func WriteProjectMemory(appHome, workdir, content string) error

WriteProjectMemory writes `content` to <APP_HOME>/projects/<key>/MEMORY.md atomically. Creates the parent directory chain if missing.

func WriteUserProfile

func WriteUserProfile(appHome, content string) error

WriteUserProfile writes `content` to USER_PROFILE.md atomically (temp + rename in the same directory). Creates the parent directory if missing. Returns an error only on real I/O failure.

Types

type Snapshot

type Snapshot struct {
	WorkdirMemory      string   // raw contents of <workdir>/EVVA.md (user-authored, repo-scoped)
	UserProfile        string   // raw contents of <appHome>/USER_PROFILE.md (auto, user-scoped)
	ProjectMemory      string   // raw contents of <appHome>/projects/<key>/MEMORY.md (auto, project-scoped)
	ProjectMemoryIndex string   // compact one-line-per-section summary of ProjectMemory; empty when no sections
	Warnings           []string // non-fatal: oversize-truncation, permission errors
}

Snapshot is one session's view of the on-disk memory files. Any body field may be empty when the file is missing, empty, or unreadable; callers treat empty as "skip the section."

func Load

func Load(workdir, appHome string, loadProjectMemory bool) Snapshot

Load reads the memory files. Empty workdir or appHome silently skips the files anchored at that path. Files larger than MaxFileBytes are truncated with a warning. The function never returns an error.

loadProjectMemory gates the per-project MEMORY.md read — callers set it to cfg.EnableAutoMemory so disabled users avoid the extra stat.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL