# `ExAthena.Memory`
[🔗](https://github.com/udin-io/ex_athena/blob/v0.7.1/lib/ex_athena/memory.ex#L1)

File-based project memory.

Loads `AGENTS.md` (preferred) / `CLAUDE.md` files from a small fixed
hierarchy and turns them into messages the agent sees on every turn.

The hierarchy, in load order, is:

  1. **User-level** — `~/.config/ex_athena/AGENTS.md` (or `CLAUDE.md`).
     Cross-project preferences a user wants every agent to honour.
  2. **Project-level** — `<cwd>/AGENTS.md` (or `CLAUDE.md`).
     Repository conventions; usually committed.
  3. **Local override** — `<cwd>/AGENTS.local.md`.
     Personal scratch on top of project conventions; usually
     gitignored.

When both `AGENTS.md` and `CLAUDE.md` exist at the same level, the
`AGENTS.md` file wins (matches opencode's behaviour for cross-tool
compatibility).

## Where the messages live

Every loaded file becomes a single user-role message tagged
`name: "memory"` — placed at the front of the conversation so it
precedes the user's first prompt. The Claude Code paper notes that
Claude Code delivers memory as user-context (probabilistic compliance)
rather than as a system prompt (deterministic compliance), and we
copy that pattern.

The compactor pipeline pins these messages: see
`ExAthena.Memory.pinned_count/1`.

# `discover`

```elixir
@spec discover(
  String.t(),
  keyword()
) :: [ExAthena.Messages.Message.t()]
```

Discover and load the memory hierarchy for `cwd`. Returns a list of
`Message.t()` in load order.

Each file's contents are wrapped with a header that names the source so
the model can tell them apart. Empty / missing files are skipped.

## Options

  * `:user_dir` — override the user-level memory directory. Defaults to
    `~/.config/ex_athena/`. Useful in tests.
  * `:filenames` — override the candidate filenames (defaults to
    `["AGENTS.md", "CLAUDE.md"]`).

# `memory_message?`

```elixir
@spec memory_message?(ExAthena.Messages.Message.t() | term()) :: boolean()
```

Is `message` a memory user-context message produced by `discover/2`?

# `pinned_count`

```elixir
@spec pinned_count([ExAthena.Messages.Message.t()]) :: non_neg_integer()
```

Number of pinned-prefix slots the compactor must preserve for memory
messages already prepended to `messages`. Used by
`ExAthena.Compactors.Summary` (and the PR2 pipeline) to compute the
effective floor for the pinned prefix.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
