Skip to main content
Glama
arlinamid

centered-agent-memory

by arlinamid

centered-agent-memory

version CI node

One index over every AI coding tool on the machine — Claude Code, Claude Desktop / Cowork, Codex, Cursor, Gemini CLI, Antigravity, Devin — organised by project. CLI and MCP, so any agent can look up what the others already did.

Magyar · docs · docs/*.hu.md

$ cam dossier demo

# demo  (D:/work/demo)

47 session · 1820 turn · 6 subagent thread(s)

## Tools
  cursor             22 session    980 turn  2026-03-02 → 2026-08-28
  claude_code        14 session    610 turn  2026-04-11 → 2026-08-27
  codex              11 session    230 turn  2026-05-01 → 2026-08-20

## Attribution
  strong:38  medium:7  none:2

## Recent topics
  2026-08-28  cursor         Docker port 80
  2026-08-27  claude_code    recall ranking

$ cam recall "docker port"

2026-06-07 14:22  cursor  demo  · Docker port
  You moved the Docker port from 3000 to 80
  cursor:9f2a1c…#seq12-18

1 hit(s). Marks: ~ medium, ? weak, ?? unattributed project.

Reference machine: 1,643 sessions, 32,054 turns — collector check ~330 ms, cam recall 55 ms, cam dossier 8 ms, repeat cam sync ~4.6 s.

flowchart LR
  A[Claude Code] --> H[(hub.sqlite)]
  B[Codex] --> H
  C[Cursor] --> H
  D[Desktop / Cowork] --> H
  E[Gemini CLI] --> H
  F[Antigravity] --> H
  G[Devin] --> H
  H --> CLI
  H --> MCP

The index stores locators, not copies. Sources stay read-only. Nothing leaves the machine.

Rule

Meaning

Locators, not copies

A turn is a file + byte offset, or an SQLite key. Text is re-read at query time. Volatile scratchpads are the one exception (artifacts).

No guessing

Unknown project stays unattributed. Every hit names its signal and confidence (strong / medium / weak / none).

Sources are read-only

Structural: openSourceReadonly. The tool never writes another agent's store.

Optional model access

No telemetry. Dreaming, embeddings, and updates are opt-in. Embedding and dream generation report planned text volume; enabling semantic recall also hands query text to your configured embedding command.

Says how old it is

Every MCP answer ends with the index age. STALE means do not quote it as current.


Install

IMPORTANT

Node24+ (active LTS). Not on the npm registry — install from a checkout, then from the tarball.

git clone https://github.com/arlinamid/centered-agent-memory.git
cd centered-agent-memory
npm ci --ignore-scripts
npm pack
npm install -g --ignore-scripts ./centered-agent-memory-*.tgz
cam install --dry-run          # read the plan
cam install                    # wire MCP, skill, schedule

cam install registers the server with every agent tool it finds, writes a skill, picks an optional dream model from a CLI already on the machine, and schedules hourly refresh. Opt-outs and the full plan: docs/install.md.

Claude Code (and Claude Code Desktop, same folder) can take the skill alone:

npx skills add arlinamid/centered-agent-memory --skill agent-memory --agent claude-code -g -y
WARNING

Do not wire the server through npx. The cache is collected later and the entry dies silently. The installer detects that, writes nothing, and points at npm i -g. npx is fine for a one-off query — the index lives in a user data directory.

npm link and npm install -g . both link back to the checkout. Move or delete the checkout and every client you just wired breaks. The tarball is a self-contained copy.

Nothing in this dependency tree needs an install script. The SQLite binding ships a prebuilt binary, yet npm would still run node-gyp rebuild — which on Windows looks for Visual Studio to produce an empty project. --ignore-scripts skips a compiler you do not need.

%LOCALAPPDATA%\centered-agent-memory\hub.sqlite on Windows, $XDG_DATA_HOME/centered-agent-memory/hub.sqlite (or ~/.local/share/...) elsewhere. A checkout that already has .data/hub.sqlite keeps using it. cam doctor prints the paths in use.

Override with --db <path>, CAM_DB, or the config file (%APPDATA%\centered-agent-memory\config.json / $XDG_CONFIG_HOME/centered-agent-memory/config.json, moved by CAM_CONFIG):

{
  "dbPath": "D:/index/hub.sqlite",
  "roots": { "codexStateDb": "D:/codex/state_5.sqlite" }
}

Any of the ten store locations can be overridden under roots.


Related MCP server: callimachus

Quick start

cam sync                       # incremental read of every source
cam projects                   # what the index knows
cam dossier <project>          # one project, every tool
cam recall "as we discussed"   # full-text; accent-insensitive
cam get cursor:9f2a…#seq12-18  # the citation recall printed

Shared flags: --json, --since / --until, --tool <tool>, --subagents, --include-weak, --limit N, --db <path>, --quiet, --verbose. Exit 0 / 1 / 2 = ok / fail / usage. A second cam sync steps back from the first.

--quiet speaks only on failure. It never swallows the answer: cam recall --json --quiet still prints JSON.


MCP

cam install                    # register with every client on the machine
cam-mcp                        # or start by hand: stdio, JSON-RPC on stdout

Eight read-only tools: cam_dossier, cam_timeline, cam_recall, cam_get, cam_projects, cam_memory, cam_status, and cam_docs for the project's own files. Wiring: docs/mcp.md.

Every response — including errors — ends with the index age:

— index: 2026-08-29 17:37 UTC (1 min ago) · 1643 session · 32054 turn

Past 24 hours (staleAfterHours) the line says STALE, run: cam sync, and the server's instructions tell the agent to report that rather than quote old data as current. The footer is wired into tool registration, so a later tool cannot omit it.


What it reads

Tool

Source

Project key

Claude Code

~/.claude/projects/<slug>/*.jsonl + <id>/subagents/*.jsonl

cwd in the records

Codex

~/.codex/state_5.sqlite + the rollout files

threads.cwd / session_meta.cwd

Cursor

<appdata>/Cursor/User/globalStorage/state.vscdb

file paths in the conversation

Cowork

<appdata>/Claude/local-agent-mode-sessions/**

userSelectedFolders

Claude Desktop

<appdata>/Claude/claude-code-sessions/**

index + title

Cursor history

<appdata>/Cursor/User/History/*/entries.json

time-correlation input

Gemini CLI

~/.gemini/tmp/<project>/chats/session-*.json

.project_root beside the chats

Antigravity

~/.gemini/antigravity-cli/conversation_summaries.db + history.jsonl + brain/**/*.md

workspace_uris

Devin CLI

<appdata>/devin/cli/sessions.db

sessions.working_directory

Devin desktop / Windsurf

~/.codeium/windsurf/cascade/<uuid>.pb (encrypted; body on cam get)

workspace_uris from the live language server

Antigravity's conversation bodies (conversations/*.pb) are encrypted — measured at 7.998 bits of entropy per byte — so what is indexed is the summary, the typed prompts and the agent's plan documents. cam get antigravity:<id> asks the live language server for the body. Devin desktop / Windsurf Cascade is the same encrypted store without a summaries database: cam sync records the filename, and cam get devin:<id> fetches the text the same way.

Formats and traps: docs/sources.md. Schema: docs/architecture.md.


Project files

Conversations say why; the files say what. cam docs indexes a project's own files — code and prose — for keyword search, and cam note attaches a sentence to a file or folder that every hit under it carries.

cam docs add [path]                         # index a project (default: the current folder)
cam docs add --known [--dry-run]            # every project cam knows, worked on in the last 90 days
cam docs query "computeInvoiceTotal"        # BM25 keyword search, with line, snippet and notes
cam docs get <collection>/<path>            # a file's indexed text
cam note add src/billing "Money: every change needs a second reviewer."
cam note list

Search is keyword-only (BM25 via qmd): no model is downloaded or loaded, so it answers in milliseconds on any machine, CPU-only included. Ask with the words the code would use.

What a project does not count as its own stays out: whatever .gitignore (at every level, and .git/info/exclude), .ignore, .vercelignore and .cursorignore exclude, plus node_modules, dist, lock files and hidden files. The rules are read again on every refresh. .npmignore and .dockerignore are not used — they routinely list src/ and test/.

--known takes the projects from the hub whose folder still exists. A project that holds sub-projects (a monorepo with a marker such as .git or package.json) is indexed whole; a plain folder of projects is indexed one by one; hidden folders, folders past 3000 files and projects you removed with cam docs remove are left out, each with the reason. With {"docs": {"autoAdd": true}} every cam sync does the same for newly seen projects ({"autoAdd": {"sinceDays": 30, "maxFiles": 5000}} moves the limits).

cam sync (and cam docs index) re-reads only the projects that changed. In a git repository git answers that: the commit checked out plus git status, with each changed file's size and mtime — two git calls, no file read. Elsewhere the folder is walked and sizes and mtimes are compared. With 54 projects an unchanged check takes about a second and a half; cam docs index --force reads everything anyway. Files over 1 MB (offline bundles, tokenizers, data dumps) are left out; {"docs": {"maxFileBytes": …}} moves the limit. So the MCP tool cam_docs sees the files as they are. The index is docs.sqlite next to the hub; {"docs": {"enabled": false}} turns it off.


Memory

A memory becomes long-term because it came back several times, on several days, to several different questions — not because it looked important. No model required. Gates: ≥ 3 recalls, ≥ 3 distinct queries, score ≥ 0.8.

cam memory consolidate         # fold the trace, promote what earned it
cam memory list                # the promoted memories
cam memory show <id>           # one memory with the evidence
cam memory dream [--dry-run]   # optional sentence, written by a model you configure
cam memory embed [--dry-run]   # optional vectors, using an embedding command you configure

Same database, same promotions. A promoted memory stores no text either — it references a chunk. Details: docs/memory.md.

cam memory dream is off by default, never runs from consolidate, prints what would leave the machine before it leaves, and labels every generated sentence with the model that wrote it.

Updating

cam update --check compares the installed version against the latest GitHub release; cam update --yes installs it. Both are off until the config file says {"update": {"enabled": true}}, and cam update --dry-run shows exactly what would be contacted without contacting it.

An update stops any running cam-mcp server first (the MCP client starts a fresh one on its next tool call), takes the sync lock so a scheduled run cannot collide, and — when the copy being replaced is the one doing the replacing — hands the install to a script in a temp directory that waits for the process to exit. The index is then migrated immediately by the newly installed binary, rather than at 04:00 by an unattended job. An index written by a newer version is refused, not silently stamped back.

The skill cam installed into each agent tool is a copy in that tool's own directory, so replacing the package does not reach it. After the install the new binary rewrites the skills that are already there — never adding one where there was none — and so does any full cam sync --repair. cam install --refresh-skills does it by hand.


Unattended

cam install sets these up. Recipes for Task Scheduler, launchd, systemd and cron: docs/operations.md.

cam sync --quiet                # hourly
cam memory consolidate --quiet  # nightly
cam prune --quiet               # nightly

Retention drops the old recall trace, the surplus run log, and — only if you ask — sessions whose source vanished. Evidence behind a live promotion is never pruned.

cam forget removes something from the index, not from history. The conversation files are never touched; the next sync indexes them again unless the source is gone too.


cam sync [--repair] [--tool t] # read sources (incremental, or full)
cam projects [--unattributed]  # projects, or sessions with no project
cam timeline <project>         # every tool, in time order
cam dossier <project>          # everything known about one project
cam recall "<question>"        # full-text search
cam get <tool:id[#seqN-M]>     # full text behind a citation
cam alias <folder> <project>   # merge two folders into one project
cam attribute <tool:id> <proj> # manual attribution (beats every other signal)
cam reattribute                # recompute without reading any store
cam rebuild                    # rebuild the text index from the sources
cam memory <subcommand>        # long-term memory
cam status                     # last sync, and what the index holds
cam doctor                     # status report
cam prune [--vacuum]           # retention
cam forget --project <p>       # forget one project or one session
cam backup [<file>]            # verified copy of the index
cam install [--dry-run]        # wire it in; cam uninstall undoes it

If the database is damaged, cam doctor says what is wrong. cam rebuild reconstructs the text index from the sources — cam sync --repair cannot, because a contentless FTS index cannot be rebuilt from inside the database.

Locators, a contentless FTS index (inverted index, no text), metadata (titles, timestamps, working directories), project evidence (file paths), an inline copy of volatile artifacts, and — because a promotion has to show which questions brought it up — the text of your own search queries (logQuery: false keeps only the hash).

It does not hold conversation text. Nothing is sent anywhere. Dropping hub.sqlite removes the index and touches no source.


Docs

npm test          # vitest; no test reads a real store
npx tsc --noEmit  # type check

Tests build Cursor / Codex fixtures at runtime from the real DDL. Path folding is pinned (CAM_CASE_FOLD); CI runs on Windows, macOS and Linux.

MIT — LICENSE.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    A
    maintenance
    Local index and hybrid search (SQLite FTS5 + on-device vector KNN) over your AI coding-agent conversation history across 11 tools (Claude Code, Codex, Cursor, and more). Exposes search_threads, search_current_project, recent_threads, get_thread, list_tags, and list_open_todos so any agent can recall its own past work.
    22
    39
    AGPL 3.0
  • A
    license
    Not graded
    quality
    C
    maintenance
    Provides local-first, cross-session memory for Claude Code, enabling semantic search across past sessions to retrieve procedures, decisions, or answers without exposing secrets.
    Apache 2.0