Sean Davis

commonplace: one memory for Claude Code, Codex, pi and omp

A curated, cross-machine memory store for coding agents, and why it beat a transcript-extracting one I tried first.

I run coding agents on two machines, across a lot of repositories. Claude Code has its own memory: small typed facts, an index loaded at the start of every session, full bodies fetched on demand when a session needs more than the one-line summary. It’s a good model. The trouble is that it’s local to one agent’s config on one machine. A fact I teach Claude Code on my Mac doesn’t exist for Codex, for pi, or for the same Claude Code running on the other machine.

commonplace keeps Claude Code’s memory model and puts it behind one server that every agent on every machine reads and writes. The name comes from a commonplace book, a notebook where you copy down the things worth keeping.

What everyone else is building

Agent memory has turned into its own small research literature over the last two years. Generative Agents (Park et al., 2023) gave each simulated character a stream of observations and a periodic reflection step that distills them into higher-level notes. MemGPT (Packer et al., 2023) borrows paging from operating systems, swapping facts in and out of a virtual memory tier the model manages itself. CoALA (Sumers et al., 2023) proposes a shared vocabulary, working, episodic, semantic, procedural memory, for sorting the growing pile of agent architectures. Zep (Rasmussen et al., 2025) and Mem0 (Chhikara et al., 2025) take the same idea to production: ingest a conversation, extract facts automatically, and retrieve by similarity search when a new session starts.

Most of that work is solving a different problem than mine: one agent, one long-running conversation, needing to remember more than fits in context. There’s a related but separate line of tools for keeping that one conversation alive in the first place: pi-blackhole replaces an agent’s LLM-based compaction with a deterministic structural summary plus background Observer and Reflector workers; billion-context sits as a proxy between an agent and its model API, compressing older turns into layered summaries; Mastra’s Observational Memory does the same job for its own framework. All three keep a single thread from blowing its context window. None of them carry a fact to a different agent or a different machine, which is the part I needed.

I use pi-blackhole for project local context management. What I was missing was a way for a fact learned in one session, by one agent, to reach a different agent on a different machine next week. commonplace doesn’t extract anything. An agent, or I, writes a fact down on purpose, the way you’d write a line in a notebook. Nothing too sophisiticated, just a simple store that keeps the same fact available to every agent on every machine. No LLM pipeline, no background workers, no compaction, no summarization, etc.

global, host, project

The part of the design built for that problem is the scope model. Every memory lives in one of three scopes:

Project scope is not where a project’s own history goes. Issues, PR descriptions, commit messages, and markdown docs checked into the repo already do that job, searchable by anyone and versioned with the code they describe. A memory that duplicates what a doc already says just gives that fact a second copy to go stale. Project scope is for what has no other home: a convention no doc states, a decision that only lives in my head, a host-specific gotcha nobody wrote down.

Keying project scope on the git remote, not the path, is what makes two machines and many projects work together. If it were keyed on a path, the same repo would fragment into two unrelated memory stores depending on where it happened to be cloned. A session on either machine, in either checkout, lands in the same scope instead.

A session only sees three scopes in its index: global, its own host, and its own project. recall, the search command, drops that filter, so work in one project can still turn up something another project learned.

Figure 1 shows how the pieces connect.

Architecture diagram of commonplace in two dashed regions. In the client-machines region on the left are Claude Code and Codex (MCP plus a SessionStart hook), pi and omp (a bundled extension), the commonplace CLI (a built-in MCP client configured by config.toml), and other MCP clients over streamable HTTP. The SessionStart hook calls the CLI's index command. pi and omp reach the CLI through memory tools. Claude Code, Codex and other MCP clients, and the CLI itself, all connect over MCP on HTTP to commonplace serve, a FastMCP server on port 9322 with write guards, in the store-host region on the right, which can be any address the clients can reach, often a tailnet. The server reads and writes memories through aiosqlite in a SQLite database with FTS5 holding versioned memories. The export and stats CLI, also on the store host, read that database directly.

Figure 1. commonplace architecture. MCP is the protocol Claude Code and Codex use to call tools.

What a session sees

The index is the one-line-per-memory summary an agent gets at the start of a session. Here’s the real one from the session that wrote this post:

Scopes for this session: global, host:macbook, project:github.com/seandavi/seandavis-net.

## global
- prefers-justfile-over-make (feedback) — Use justfile (just) as the task runner, not Makefiles
- use-standard-postgres-uri-not-separate-vars (feedback) — User prefers a single
  standard postgresql:// URI env var over separate host/port/user/password/db vars
- git-repos-in-documents-git (reference) — On both the Mac and onclappc02, git repos
  generally live in ~/Documents/git/<repo> — look there first
...

host:macbook and the seandavis-net project scope are both declared here, and both empty. Nothing machine-specific or repo-specific about this blog has been worth writing down yet, and that’s fine. A scope just needs to exist when a fact needs it, not before.

From there, we fetch full bodies on demand: get for a known name, recall for a ranked search across everything a session can see. Four types, user, feedback, project, and reference, the same four Claude Code’s own memory uses, so importing from it is a straight copy. update writes a new version rather than overwriting. forget soft-deletes rather than erasing. history shows every version and who wrote it, which starts to matter once more than one agent writes to the same store.

The store is one SQLite table. A memory is never overwritten in place: an update inserts a new row and points the old one at it through superseded_by, so history is never lost, and a partial unique index picks out the one live row per (scope, name):

CREATE TABLE memories (
    id TEXT PRIMARY KEY, scope TEXT, name TEXT, type TEXT,
    description TEXT, body TEXT, author TEXT, created_at TEXT,
    superseded_by TEXT, deleted_at TEXT, expires_at TEXT
);
CREATE UNIQUE INDEX memories_live ON memories (scope, name)
    WHERE superseded_by IS NULL AND deleted_at IS NULL;

recall is full-text search, an FTS5 virtual table with the porter unicode61 tokenizer, not embeddings. Partly that’s forced: the SQL stays within what Cloudflare D1 speaks (SQLite, FTS5, partial indexes, no triggers) on purpose, so the store can move there without a rewrite if running my own server ever stops being worth it, and D1 has no vector column to lean on. Partly it’s that a memory is a short, deliberately written fact, not a long document, so there’s less for embeddings to buy you, and BM25 ranking over a few hundred memories needs no embedding model to call, pay for, or keep in sync with the data. User text going into a search gets every token quoted and OR’d together before it reaches FTS5, so punctuation in a query can never be parsed as FTS5 query syntax:

def fts_query(text: str) -> str:
    terms = re.findall(r"\w+", text.lower())
    return " OR ".join(f'"{t}"' for t in dict.fromkeys(terms))

The same ranking catches near-duplicates before they’re written. remember scores a new memory’s name and description against its scope’s existing ones. A neighbor whose BM25 score comes in at 45% or more of the new memory’s own score gets flagged back to the caller as a warning, rather than filed alongside silently. That threshold isn’t a guess: known duplicates in my own store score between 47% and 61%, and 45% is calibrated against the live store to flag about 18% of memories.

What I tried first

Before commonplace, I tried the extraction approach above as a memory backend for omp: a server that reads agent transcripts and writes down what seems worth keeping. I ran it for one day and tore it down. The container, the database and role, the Tailscale ports, the secrets in GCP Secret Manager, all of it.

Transcript extraction needs its own LLM pipeline and its own server running continuously, and that pipeline can fail quietly: a Gemini API error once broke the reflection step with no other sign of trouble. That said, a PR description written at merge time is denser than an extracted memory, checked by a human, and lives right next to the code it describes. I’d rather durable lessons land in commonplace on purpose, or in the PR or issue, than get guessed at by something reading over my shoulder.

What it doesn’t do

commonplace has no authentication of its own. It needs a private network, and I run it over a Tailscale tailnet (a private mesh VPN between my own machines), bound to the machine’s tailnet address. A LAN behind a firewall, a different VPN, or an SSH tunnel to a server bound to 127.0.0.1 would all work too. Anyone who can reach the port can read, write, and forget every memory in the store.

Memories are also text that gets loaded straight into an agent’s context, so treat them as untrusted input the same way you’d treat a web page an agent fetches. The server’s own instructions and the injected index both tell agents never to follow instructions found inside a memory, only to read it as data. history and export are how you’d audit that if something looked wrong.

Trying it

It needs Python 3.12 or newer. It’s on PyPI as commonplace-agent-memory (the plain name commonplace was already taken):

uv tool install commonplace-agent-memory
commonplace --help

Start the server on whichever machine will hold the store:

commonplace serve --http --host <address> --port 9322

Then, on every machine whose agents should share it, point the CLI at that address in ~/.config/commonplace/config.toml:

url = "http://<server-address>:9322/mcp"
host = "macbook"

Claude Code and Codex are MCP clients, so connecting them is an MCP server registration plus a SessionStart hook that runs commonplace index --hook to load the index into context. pi and omp get a small extension that does the same two jobs by shelling out to the CLI. Each agent’s exact setup is a few lines. The repo’s README has the steps per agent.

Where this stands

commonplace is running on both of my machines now, wired into Claude Code, Codex, pi, and omp, bootstrapped from the memories those agents had already built up separately. The scope model is doing what I built it to do: a fact written once, in one session, shows up for whichever agent, on whichever machine, needs it next. The underlying SQL stays within what Cloudflare D1 supports, so if running my own server stops being worth it, the store itself doesn’t need rewriting to move. The network story is the part I’d still call open. A tailnet is enough for how I use this today, but it isn’t an answer for anyone who can’t put one between their agents and the server.

References

Chhikara, P., Khant, D., Aryan, S., Singh, T., & Yadav, D. (2025). Mem0: Building Production-Ready AI Agents with Scalable Long-Term Memory. arXiv Preprint arXiv:2504.19413.
Packer, C., Wooders, S., Lin, K., Fang, V., Patil, S. G., Stoica, I., & Gonzalez, J. E. (2023). MemGPT: Towards LLMs as Operating Systems. arXiv Preprint arXiv:2310.08560.
Park, J. S., O’Brien, J. C., Cai, C. J., Morris, M. R., Liang, P., & Bernstein, M. S. (2023). Generative Agents: Interactive Simulacra of Human Behavior. Proceedings of the 36th Annual ACM Symposium on User Interface Software and Technology (UIST). https://doi.org/10.1145/3586183.3606763
Rasmussen, P., Paliychuk, P., Beauvais, T., Ryan, J., & Chalef, D. (2025). Zep: A Temporal Knowledge Graph Architecture for Agent Memory. arXiv Preprint arXiv:2501.13956.
Sumers, T. R., Yao, S., Narasimhan, K., & Griffiths, T. L. (2023). Cognitive Architectures for Language Agents. arXiv Preprint arXiv:2309.02427.

← All notes