Notes from building private, local memory for AI agents.
v0.5.2 · LuaJIT · SQLite · MCP · CLI
luamemo is a local memory engine for AI coding agents. It runs on your machine as an MCP server and a command line, keeps what an agent decides and learns in one SQLite file per project, and brings it back in later sessions. A new session no longer starts cold.
Chat sessions crash, grow too long and get compacted, and the decisions that mattered go with them. luamemo makes the transcript disposable. The durable part, the decisions, facts and plans, is written down as it happens and searched on demand. It works with Claude Code, VS Code, Claude Desktop and any other MCP client. It also writes a config entry for Cursor, which the project cannot test yet.
These are measurements from luamemo 0.5.x, October 2026, not promises. luamemo is built for real usage, and the benchmarks are a guard against regressions. memo usage reports how your own agent uses memory once the usage log is on. Retrieval is the lever: a miss costs about 22 KB of wrong rows on LoCoMo before the agent searches again. Methods and the other benchmarks are in the repository's eval folder.
luamemo is growing into an AI software-lifecycle tool for data manipulation and memory management. Today it writes, searches, folds, merges, exports and re-weights a project's memory, senses from corrections and praise what deserves to stay on top, and shares team memory over a LAN or VPN. Next is code review by an agent that knows why an abstraction exists and which conventions the team agreed on.
The longer direction is a memory engine for augmentative machine learning: a store that models and agents learn from, as well as read from. That is a goal, not a feature. Today the learning is small and opt-in: a learned reranker and projection that replace the current ranking only when they beat it on held-out data. Ideas are welcome in the issue tracker.
luamemo is an MCP server your agent talks to, and a command, memo, that you talk to. Setup is one command per project.
sudo apt install luajit luarocks libsqlite3-0 libsodium23 jq git sudo luarocks install luamemo
Debian, Ubuntu and WSL. Fedora and macOS with Homebrew work too, and Windows goes through WSL. luamemo runs on LuaJIT with two system libraries, SQLite and libsodium. There is no database server, no Docker and no account.
cd ~/code/my-app memo calibrate
Calibrate looks at the machine, picks the best embedder that works on it, creates the project's database, and reads what the project already knows. It registers VS Code, Cursor and Claude Desktop, and wires in Claude Code if it is installed. memo mcp --print-config prints the entry for any other client.
memo ping
Open your agent and ask what it remembers about the project. From there it searches and writes on its own, because the tool descriptions tell it when.
27 tools. These are the ones it reaches for first.
memory_briefThe project's scope, latest memories and code map, at session start.memory_searchSearch by meaning and by words, across one scope or several.memory_writeStore a decision, fact or plan. A near-duplicate merges.memory_senseLearn from your corrections, rules and praise.index_searchFind where code lives in the codebase map.secret_executeCall an API with a stored key filled in on the server.memoOutput is JSON unless a command says otherwise.
memo calibrateSet up a project: embedder, database, what it already knows.memo context QUERYSearch and current facts in one block.memo pingHealth check: libraries, database, embedder.memo agent statusWhat is wired into Claude Code, stale or missing.memo backupA consistent copy of the database while it is in use.memo config setChange a setting. Your agent can read settings, and only you change them.luamemo keeps memory in a file on your machine and sends no telemetry. This is what works the moment it is installed, what calibrate sets up, and what waits for you to ask.
Nothing to configure
hash embedder is pure Lua and instant. It matches words, not meaning, until calibrate picks a better one.memo maintain: tiers, decay, consolidation, and the review of folded memories. A search never waits on a language model.MEMO_MCP_TOOLS=core offers the 16 everyday ones for less context.Once per project
~/.local/share/luamemo, recorded in .luamemorc, which calibrate keeps out of git.ARCH: and DECISION: comments, and architectural commit messages.--no-mcp and --no-plugin skip them.~/.claude exists: hooks, a block in CLAUDE.md, and the project's file notes moved into luamemo. --no-agent skips it.One setting or command each
MEMO_FEEDBACK_ENABLED=true. It is off because it stores the text of every search. It feeds memo usage and the learned reranker.noop. Choose learned with the log on, and a model is promoted only if it beats the current ranking on held-out data.memo agent install --with-transcripts saves the conversation before Claude Code compacts it.memo index ingest indexes files, symbols and imports so the agent finds where code lives.memo calibrate --role org and memo serve. LAN or VPN only, key-paired and encrypted.MEMO_GEN_MODEL for summaries, merges and the wording of observations.Hosted embedders (OpenAI, Voyage, Cohere) are used only when you name one, with its key in the encrypted vault. Settings are yours: your agent can read them, and only memo config changes them.
Lua is Portuguese for moon. Each entry carries a phase, and the phase is the tier luamemo gives its own memories. Releases and decisions that still shape the code stay full. Lessons that mattered once settle to a crescent.
Run memo calibrate in a project and, if Claude Code is installed, luamemo wires itself in. It adds hooks to Claude Code's settings, a short block in CLAUDE.md, and moves the project's old file notes into luamemo. memo agent uninstall takes all of it out again.
When you give the agent a standing rule, such as "always run the tests before committing", luamemo now stores it as a decision. Before, a rule like that went to Claude Code's own file memory, where no search could find it.
Maintenance is on by default. Digests, summaries and the review of folded memories run in a silent background process, so a search never waits on a language model.
The macOS job in CI is allowed to fail, so the 0.5.2 run looked green while macOS was red. GitHub had retired the macos-14 runner on November 2, and the job was cancelled before it ran a single step.
After moving to macos-15, the real failures were in the tests. On macOS, /tmp is /private/tmp, and BSD wc pads its numbers with spaces. The product code was right.
From now on a release reads every job in the run, not the overall status.
When luamemo folds several memories into one summary, the originals used to be deleted. Now they are archived and hidden for 30 days. Then each one is compared with the summary that replaced it.
An original is deleted if it was never useful, or if the summary keeps at least 60% of its salient words. If it was useful and facts were lost, the missing sentences are added to the summary and the clock restarts. After two corrections the original is restored. Every deletion is written to a log next to the database first, and does not happen if the log cannot be written.
A summary can be the only copy of what it says, so it has to earn the right to replace the original.
Four features each had their own number for "near-duplicate": 0.95, 0.90, 0.80 and 0.75. On a project's own memories, measured with EmbeddingGemma 2, 0.75 matched 99.5% of unrelated memories, and 0.90 flagged 13.4% of genuinely new writes as repeats.
From 0.95 up, a distinct memory is wrongly matched 0.7% of the time and a near-duplicate is found every time. One setting, MEMO_DEDUP_THRESHOLD, now defines it. The other thresholds follow it unless they are set on their own.
PostgreSQL and pgvector are gone. Each project's memory is one SQLite file in ~/.local/share/luamemo, reached through LuaJIT's FFI, with libsodium for the encrypted vault. Search is still exact, because every vector in scope is scored.
It is also faster: 3.9 ms at 1,000 memories where it took 136 ms before, and 233 ms at 100,000. Recall moved by less than half a point on every benchmark. Nothing migrates by itself, so each project runs memo calibrate again.
luamemo now finds Homebrew's libraries on Apple silicon without being told where they are. memo calibrate found no file types in a project on macOS, because its scan used a GNU sed pattern. The test suite now runs on macOS too.