| 1 | # User Memory |
| 2 | |
| 3 | User memory gives the model a small, persistent, local store of |
| 4 | preferences and conventions that should survive across sessions — |
| 5 | "I prefer pytest over unittest", "this codebase uses 4-space |
| 6 | indentation" — without repeating them in every conversation. |
| 7 | |
| 8 | As of v0.9.4 the **native memory store** is the only memory system. |
| 9 | It is Markdown files indexed by SQLite FTS5, fully offline, scoped by |
| 10 | a hash of the repo's git origin. The legacy single-file |
| 11 | (`~/.deepseek/memory.md`) push/inject path and the planned Moraine MCP |
| 12 | backend were both removed: no Moraine server ever shipped in-repo, |
| 13 | and the native store already provides the same architecture (durable |
| 14 | Markdown source of truth plus a rebuildable search index). |
| 15 | |
| 16 | Memory is **opt-in**. When disabled (the default), nothing is loaded, |
| 17 | nothing is intercepted, and the `remember` tool isn't surfaced to the |
| 18 | model. |
| 19 | |
| 20 | ## Enabling memory |
| 21 | |
| 22 | Either set the env var: |
| 23 | |
| 24 | ```bash |
| 25 | export DEEPSEEK_MEMORY=on |
| 26 | ``` |
| 27 | |
| 28 | Accepted truthy values are `1`, `on`, `true`, `yes`, `y`, and |
| 29 | `enabled`. |
| 30 | |
| 31 | …or add to `~/.codewhale/config.toml`: |
| 32 | |
| 33 | ```toml |
| 34 | [memory] |
| 35 | enabled = true |
| 36 | ``` |
| 37 | |
| 38 | Restart the TUI after toggling. Disabling is the same in reverse. |
| 39 | |
| 40 | ## Layout |
| 41 | |
| 42 | The store lives under a `memory/` directory next to where the legacy |
| 43 | `memory_path` anchor points — by default `memory_path = "~/.codewhale/memory.md"` |
| 44 | re-roots to `~/.codewhale/memory/`: |
| 45 | |
| 46 | ```text |
| 47 | ~/.codewhale/memory/ |
| 48 | ├── global/MEMORY.md # user-scoped notes (follow you everywhere) |
| 49 | ├── workspace/<id>/MEMORY.md # repo-scoped notes (hash of git origin) |
| 50 | └── index.sqlite3 # rebuildable SQLite FTS5 cache |
| 51 | ``` |
| 52 | |
| 53 | The scope directory is `workspace` (singular) — `MemoryScope::directory`, |
| 54 | `crates/tui/src/native_memory.rs:29-33`. The index filename is |
| 55 | `index.sqlite3` (`native_memory.rs:173`). |
| 56 | |
| 57 | Markdown is the durable source of truth; `index.sqlite3` is a disposable |
| 58 | full-text cache (`/memory native reindex` rebuilds it). A configured |
| 59 | `memory_path` is an **anchor only**: the filename is discarded and its |
| 60 | parent gains the `memory/global/MEMORY.md` tree. Do not set |
| 61 | `memory_path` to the native layout path itself — that double-nests the |
| 62 | tree. The shipped example keeps `~/.codewhale/memory.md` so the store |
| 63 | lands at `~/.codewhale/memory/global/MEMORY.md`. |
| 64 | |
| 65 | ## What gets injected |
| 66 | |
| 67 | When memory is enabled, the system prompt carries a bounded, |
| 68 | provenance-bearing block of memory entries (up to 32 entries / |
| 69 | 12,000 chars, global plus current-workspace scope). The block is |
| 70 | wrapped to mark it as **untrusted user data**, not a second |
| 71 | instruction layer. For depth beyond the injected head, the model can |
| 72 | call the `memory_search` / `memory_get` tools against the FTS5 index. |
| 73 | |
| 74 | ## Three ways to add to memory |
| 75 | |
| 76 | ### 1. The `# ` composer prefix (#492) |
| 77 | |
| 78 | Type a single line that starts with `#` (but not `##` or `#!`) in |
| 79 | the composer: |
| 80 | |
| 81 | ``` |
| 82 | # remember to use 4-space indentation in this repo |
| 83 | ``` |
| 84 | |
| 85 | The TUI intercepts the input and appends the note to the **global** |
| 86 | native store via the same `NativeMemoryStore::remember` path the |
| 87 | model's tool uses. **No turn fires** — your input is consumed, the |
| 88 | status line confirms the file it wrote to, and you can keep typing |
| 89 | your real question. |
| 90 | |
| 91 | Multi-`#` prefixes deliberately fall through to normal turn |
| 92 | submission so you can paste Markdown headings without surprise. |
| 93 | |
| 94 | ### 2. The `/memory` slash command |
| 95 | |
| 96 | Inspect and maintain the native store: |
| 97 | |
| 98 | `/memory` splits in two. The bare subcommands operate on the single file at |
| 99 | `config.memory_path()`; everything about the native store lives behind |
| 100 | `/memory native …` (`crates/tui/src/commands/groups/memory/memory.rs:236-268`). |
| 101 | |
| 102 | | Subcommand | Effect | |
| 103 | |-----------------|-----------------------------------------------------------| |
| 104 | | `/memory` | Print the path and contents of the `memory_path` file | |
| 105 | | `/memory show` | Same as bare `/memory` | |
| 106 | | `/memory path` | Print the `memory_path` file location | |
| 107 | | `/memory clear` | Truncate that file | |
| 108 | | `/memory edit` | Print the `$EDITOR` invocation for it | |
| 109 | | `/memory help` | Show command-specific help | |
| 110 | |
| 111 | Anything else returns `unknown subcommand`. The native store is reached through |
| 112 | the `native` prefix (`memory.rs:221`): |
| 113 | |
| 114 | | Subcommand | Effect | |
| 115 | |-----------------------------------------|-------------------------------------| |
| 116 | | `/memory native status` | Store root, active source, index | |
| 117 | | `/memory native path` | Native store root | |
| 118 | | `/memory native remember [global\|workspace] <note>` | Append a note | |
| 119 | | `/memory native search <query>` | FTS5 search | |
| 120 | | `/memory native get <id>` | Read one entry | |
| 121 | | `/memory native reindex` | Rebuild the FTS5 index | |
| 122 | | `/memory native import` | Import the legacy single-file store | |
| 123 | | `/memory native export` | Dump entries | |
| 124 | | `/memory native delete [all\|global\|workspace]` | Delete entries | |
| 125 | |
| 126 | There is no `/memory add` and no bare `/memory reindex`; use |
| 127 | `/memory native remember` and `/memory native reindex`. |
| 128 | |
| 129 | ### 3. The `remember` tool (auto-capture, #489) |
| 130 | |
| 131 | When memory is enabled the model gets a `remember` tool: |
| 132 | |
| 133 | ```json |
| 134 | { |
| 135 | "name": "remember", |
| 136 | "input_schema": { |
| 137 | "type": "object", |
| 138 | "properties": { |
| 139 | "note": { "type": "string" }, |
| 140 | "scope": { "type": "string", "enum": ["global", "workspace"] } |
| 141 | }, |
| 142 | "required": ["note"] |
| 143 | } |
| 144 | } |
| 145 | ``` |
| 146 | |
| 147 | The model uses this when it notices a durable preference, convention, |
| 148 | or fact worth keeping across sessions. The tool is auto-approved |
| 149 | because writes are scoped to the user's own memory files — gating |
| 150 | them behind the standard write-approval flow would defeat the point |
| 151 | of automatic memory capture. Workspace scope requires a git |
| 152 | repository with an `origin` remote (the scope id is a hash of it). |
| 153 | |
| 154 | ## What stays out of memory |
| 155 | |
| 156 | Memory is for **durable** signal. Things that should NOT live there: |
| 157 | |
| 158 | - **Secrets** — no API keys, tokens, passwords. The files are plain |
| 159 | text on disk and entries are injected into the system prompt. |
| 160 | - **Transient task state** — "I'm currently working on the parser" |
| 161 | changes every session; it doesn't belong in cross-session memory. |
| 162 | - **Conversation snippets** — quote-style notes belong in the notes |
| 163 | tool (`note`), not memory. |
| 164 | - **Long instructions** — anything over a few sentences should live |
| 165 | in `AGENTS.md` (project-level) or in a skill. |
| 166 | |
| 167 | ## Privacy and scope |
| 168 | |
| 169 | The store lives entirely on your machine. It is never uploaded to any |
| 170 | cloud service — the TUI only ever includes entries inline in the |
| 171 | system prompt that the LLM provider receives, and only when memory is |
| 172 | enabled. Workspace-scoped memory is keyed by a hash of the repo's git |
| 173 | origin, so notes from one repo never leak into another repo's prompt. |
| 174 | |
| 175 | ## Configuration reference |
| 176 | |
| 177 | ```toml |
| 178 | # ~/.codewhale/config.toml |
| 179 | [memory] |
| 180 | enabled = true # default false; or set DEEPSEEK_MEMORY=on |
| 181 | # Optional explicit backend selection: |
| 182 | # backend = "native" # "native" or "off" (default: off) |
| 183 | ``` |
| 184 | |
| 185 | | Setting | Default | Override | |
| 186 | |-----------------------|-------------------------------|---------------------------------------| |
| 187 | | Memory enabled | `false` | `[memory] enabled = true` or `DEEPSEEK_MEMORY=on` | |
| 188 | | Backend | `off` | `[memory] backend = "native"` | |
| 189 | | Store root | `~/.codewhale/memory/` | derived from `memory_path` | |
| 190 | |
| 191 | ## Related |
| 192 | |
| 193 | - `docs/SUBAGENTS.md` — sub-agents inherit memory and can use the |
| 194 | `remember` tool too. |
| 195 | - `docs/CONFIGURATION.md` — full config reference. |
| 196 | - Issue [#489](https://github.com/Hmbown/CodeWhale/issues/489) |
| 197 | — phase-1 EPIC tracking the work. |
| 198 |